3d737ef5f6460b43476bc97d758030c8eb7cbf5d
[asterisk/asterisk.git] / funcs / func_math.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2004 - 2006, Andy Powell 
5  *
6  * Updated by Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Math related dialplan function
22  *
23  * \author Andy Powell
24  * \author Mark Spencer <markster@digium.com>
25  *
26  * \ingroup functions
27  */
28
29 #include "asterisk.h"
30
31 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
32
33 #include <math.h>
34
35 #include "asterisk/module.h"
36 #include "asterisk/channel.h"
37 #include "asterisk/pbx.h"
38 #include "asterisk/utils.h"
39 #include "asterisk/app.h"
40 #include "asterisk/config.h"
41
42 /*** DOCUMENTATION
43         <function name="MATH" language="en_US">
44                 <synopsis>
45                         Performs Mathematical Functions.
46                 </synopsis>
47                 <syntax>
48                         <parameter name="expression" required="true">
49                                 <para>Is of the form:
50                                 <replaceable>number1</replaceable><replaceable>op</replaceable><replaceable>number2</replaceable>
51                                 where the possible values for <replaceable>op</replaceable>
52                                 are:</para>
53                                 <para>+,-,/,*,%,&lt;&lt;,&gt;&gt;,^,AND,OR,XOR,&lt;,%gt;,&gt;=,&lt;=,== (and behave as their C equivalents)</para>
54                         </parameter>
55                         <parameter name="type">
56                                 <para>Wanted type of result:</para>
57                                 <para>f, float - float(default)</para>
58                                 <para>i, int - integer</para>
59                                 <para>h, hex - hex</para>
60                                 <para>c, char - char</para>
61                         </parameter>
62                 </syntax>
63                 <description>
64                         <para>Performs mathematical functions based on two parameters and an operator.  The returned
65                         value type is <replaceable>type</replaceable></para>
66                         <para>Example: Set(i=${MATH(123%16,int)}) - sets var i=11</para>
67                 </description>
68         </function>
69  ***/
70
71 enum TypeOfFunctions {
72         ADDFUNCTION,
73         DIVIDEFUNCTION,
74         MULTIPLYFUNCTION,
75         SUBTRACTFUNCTION,
76         MODULUSFUNCTION,
77         POWFUNCTION,
78         SHLEFTFUNCTION,
79         SHRIGHTFUNCTION,
80         BITWISEANDFUNCTION,
81         BITWISEXORFUNCTION,
82         BITWISEORFUNCTION,
83         GTFUNCTION,
84         LTFUNCTION,
85         GTEFUNCTION,
86         LTEFUNCTION,
87         EQFUNCTION
88 };
89
90 enum TypeOfResult {
91         FLOAT_RESULT,
92         INT_RESULT,
93         HEX_RESULT,
94         CHAR_RESULT
95 };
96
97 static int math(struct ast_channel *chan, const char *cmd, char *parse,
98                 char *buf, size_t len)
99 {
100         double fnum1;
101         double fnum2;
102         double ftmp = 0;
103         char *op;
104         int iaction = -1;
105         int type_of_result = FLOAT_RESULT;
106         char *mvalue1, *mvalue2 = NULL, *mtype_of_result;
107         int negvalue1 = 0;
108         AST_DECLARE_APP_ARGS(args,
109                              AST_APP_ARG(argv0);
110                              AST_APP_ARG(argv1);
111         );
112
113         if (ast_strlen_zero(parse)) {
114                 ast_log(LOG_WARNING, "Syntax: MATH(<number1><op><number 2>[,<type_of_result>]) - missing argument!\n");
115                 return -1;
116         }
117
118         AST_STANDARD_APP_ARGS(args, parse);
119
120         if (args.argc < 1) {
121                 ast_log(LOG_WARNING, "Syntax: MATH(<number1><op><number 2>[,<type_of_result>]) - missing argument!\n");
122                 return -1;
123         }
124
125         mvalue1 = args.argv0;
126
127         if (mvalue1[0] == '-') {
128                 negvalue1 = 1;
129                 mvalue1++;
130         }
131
132         if ((op = strchr(mvalue1, '*'))) {
133                 iaction = MULTIPLYFUNCTION;
134                 *op = '\0';
135         } else if ((op = strchr(mvalue1, '/'))) {
136                 iaction = DIVIDEFUNCTION;
137                 *op = '\0';
138         } else if ((op = strchr(mvalue1, '%'))) {
139                 iaction = MODULUSFUNCTION;
140                 *op = '\0';
141         } else if ((op = strchr(mvalue1, '^'))) {
142                 iaction = POWFUNCTION;
143                 *op = '\0';
144         } else if ((op = strstr(mvalue1, "AND"))) {
145                 iaction = BITWISEANDFUNCTION;
146                 op += 3;
147                 *op = '\0';
148         } else if ((op = strstr(mvalue1, "XOR"))) {
149                 iaction = BITWISEXORFUNCTION;
150                 op += 3;
151                 *op = '\0';
152         } else if ((op = strstr(mvalue1, "OR"))) {
153                 iaction = BITWISEORFUNCTION;
154                 op += 2;
155                 *op = '\0';
156         } else if ((op = strchr(mvalue1, '>'))) {
157                 iaction = GTFUNCTION;
158                 *op = '\0';
159                 if (*(op + 1) == '=') {
160                         *++op = '\0';
161                         iaction = GTEFUNCTION;
162                 } else if (*(op + 1) == '>') {
163                         *++op = '\0';
164                         iaction = SHRIGHTFUNCTION;
165                 }
166         } else if ((op = strchr(mvalue1, '<'))) {
167                 iaction = LTFUNCTION;
168                 *op = '\0';
169                 if (*(op + 1) == '=') {
170                         *++op = '\0';
171                         iaction = LTEFUNCTION;
172                 } else if (*(op + 1) == '<') {
173                         *++op = '\0';
174                         iaction = SHLEFTFUNCTION;
175                 }
176         } else if ((op = strchr(mvalue1, '='))) {
177                 *op = '\0';
178                 if (*(op + 1) == '=') {
179                         *++op = '\0';
180                         iaction = EQFUNCTION;
181                 } else
182                         op = NULL;
183         } else if ((op = strchr(mvalue1, '+'))) {
184                 iaction = ADDFUNCTION;
185                 *op = '\0';
186         } else if ((op = strchr(mvalue1, '-'))) { /* subtraction MUST always be last, in case we have a negative first number */
187                 iaction = SUBTRACTFUNCTION;
188                 *op = '\0';
189         }
190
191         if (op)
192                 mvalue2 = op + 1;
193
194         /* detect wanted type of result */
195         mtype_of_result = args.argv1;
196         if (mtype_of_result) {
197                 if (!strcasecmp(mtype_of_result, "float")
198                     || !strcasecmp(mtype_of_result, "f"))
199                         type_of_result = FLOAT_RESULT;
200                 else if (!strcasecmp(mtype_of_result, "int")
201                          || !strcasecmp(mtype_of_result, "i"))
202                         type_of_result = INT_RESULT;
203                 else if (!strcasecmp(mtype_of_result, "hex")
204                          || !strcasecmp(mtype_of_result, "h"))
205                         type_of_result = HEX_RESULT;
206                 else if (!strcasecmp(mtype_of_result, "char")
207                          || !strcasecmp(mtype_of_result, "c"))
208                         type_of_result = CHAR_RESULT;
209                 else {
210                         ast_log(LOG_WARNING, "Unknown type of result requested '%s'.\n",
211                                         mtype_of_result);
212                         return -1;
213                 }
214         }
215
216         if (!mvalue1 || !mvalue2) {
217                 ast_log(LOG_WARNING,
218                                 "Supply all the parameters - just this once, please\n");
219                 return -1;
220         }
221
222         if (sscanf(mvalue1, "%lf", &fnum1) != 1) {
223                 ast_log(LOG_WARNING, "'%s' is not a valid number\n", mvalue1);
224                 return -1;
225         }
226
227         if (sscanf(mvalue2, "%lf", &fnum2) != 1) {
228                 ast_log(LOG_WARNING, "'%s' is not a valid number\n", mvalue2);
229                 return -1;
230         }
231
232         if (negvalue1)
233                 fnum1 = 0 - fnum1;
234
235         switch (iaction) {
236         case ADDFUNCTION:
237                 ftmp = fnum1 + fnum2;
238                 break;
239         case DIVIDEFUNCTION:
240                 if (fnum2 <= 0)
241                         ftmp = 0;                       /* can't do a divide by 0 */
242                 else
243                         ftmp = (fnum1 / fnum2);
244                 break;
245         case MULTIPLYFUNCTION:
246                 ftmp = (fnum1 * fnum2);
247                 break;
248         case SUBTRACTFUNCTION:
249                 ftmp = (fnum1 - fnum2);
250                 break;
251         case MODULUSFUNCTION:
252                 {
253                         int inum1 = fnum1;
254                         int inum2 = fnum2;
255
256                         ftmp = (inum1 % inum2);
257
258                         break;
259                 }
260         case POWFUNCTION:
261                 ftmp = pow(fnum1, fnum2);
262                 break;
263         case SHLEFTFUNCTION:
264                 {
265                         int inum1 = fnum1;
266                         int inum2 = fnum2;
267
268                         ftmp = (inum1 << inum2);
269                         break;
270                 }
271         case SHRIGHTFUNCTION:
272                 {
273                         int inum1 = fnum1;
274                         int inum2 = fnum2;
275
276                         ftmp = (inum1 >> inum2);
277                         break;
278                 }
279         case BITWISEANDFUNCTION:
280                 {
281                         int inum1 = fnum1;
282                         int inum2 = fnum2;
283                         ftmp = (inum1 & inum2);
284                         break;
285                 }
286         case BITWISEXORFUNCTION:
287                 {
288                         int inum1 = fnum1;
289                         int inum2 = fnum2;
290                         ftmp = (inum1 ^ inum2);
291                         break;
292                 }
293         case BITWISEORFUNCTION:
294                 {
295                         int inum1 = fnum1;
296                         int inum2 = fnum2;
297                         ftmp = (inum1 | inum2);
298                         break;
299                 }
300         case GTFUNCTION:
301                 ast_copy_string(buf, (fnum1 > fnum2) ? "TRUE" : "FALSE", len);
302                 break;
303         case LTFUNCTION:
304                 ast_copy_string(buf, (fnum1 < fnum2) ? "TRUE" : "FALSE", len);
305                 break;
306         case GTEFUNCTION:
307                 ast_copy_string(buf, (fnum1 >= fnum2) ? "TRUE" : "FALSE", len);
308                 break;
309         case LTEFUNCTION:
310                 ast_copy_string(buf, (fnum1 <= fnum2) ? "TRUE" : "FALSE", len);
311                 break;
312         case EQFUNCTION:
313                 ast_copy_string(buf, (fnum1 == fnum2) ? "TRUE" : "FALSE", len);
314                 break;
315         default:
316                 ast_log(LOG_WARNING,
317                                 "Something happened that neither of us should be proud of %d\n",
318                                 iaction);
319                 return -1;
320         }
321
322         if (iaction < GTFUNCTION || iaction > EQFUNCTION) {
323                 if (type_of_result == FLOAT_RESULT)
324                         snprintf(buf, len, "%f", ftmp);
325                 else if (type_of_result == INT_RESULT)
326                         snprintf(buf, len, "%i", (int) ftmp);
327                 else if (type_of_result == HEX_RESULT)
328                         snprintf(buf, len, "%x", (unsigned int) ftmp);
329                 else if (type_of_result == CHAR_RESULT)
330                         snprintf(buf, len, "%c", (unsigned char) ftmp);
331         }
332
333         return 0;
334 }
335
336 static struct ast_custom_function math_function = {
337         .name = "MATH",
338         .read = math
339 };
340
341 static int unload_module(void)
342 {
343         return ast_custom_function_unregister(&math_function);
344 }
345
346 static int load_module(void)
347 {
348         return ast_custom_function_register(&math_function);
349 }
350
351 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Mathematical dialplan function");