Add INCrement and DECrement functions
[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  * Updated by Nir Simionovich <nirs@greenfieldtech.net>
8  *
9  * See http://www.asterisk.org for more information about
10  * the Asterisk project. Please do not directly contact
11  * any of the maintainers of this project for assistance;
12  * the project provides a web site, mailing lists and IRC
13  * channels for your use.
14  *
15  * This program is free software, distributed under the terms of
16  * the GNU General Public License Version 2. See the LICENSE file
17  * at the top of the source tree.
18  */
19
20 /*! \file
21  *
22  * \brief Math related dialplan function
23  *
24  * \author Andy Powell
25  * \author Mark Spencer <markster@digium.com>
26  * \author Nir Simionovich <nirs@greenfieldtech.net>
27  *
28  * \ingroup functions
29  */
30
31 #include "asterisk.h"
32
33 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
34
35 #include <math.h>
36
37 #include "asterisk/module.h"
38 #include "asterisk/channel.h"
39 #include "asterisk/pbx.h"
40 #include "asterisk/utils.h"
41 #include "asterisk/app.h"
42 #include "asterisk/config.h"
43
44 /*** DOCUMENTATION
45         <function name="MATH" language="en_US">
46                 <synopsis>
47                         Performs Mathematical Functions.
48                 </synopsis>
49                 <syntax>
50                         <parameter name="expression" required="true">
51                                 <para>Is of the form:
52                                 <replaceable>number1</replaceable><replaceable>op</replaceable><replaceable>number2</replaceable>
53                                 where the possible values for <replaceable>op</replaceable>
54                                 are:</para>
55                                 <para>+,-,/,*,%,&lt;&lt;,&gt;&gt;,^,AND,OR,XOR,&lt;,%gt;,&gt;=,&lt;=,== (and behave as their C equivalents)</para>
56                         </parameter>
57                         <parameter name="type">
58                                 <para>Wanted type of result:</para>
59                                 <para>f, float - float(default)</para>
60                                 <para>i, int - integer</para>
61                                 <para>h, hex - hex</para>
62                                 <para>c, char - char</para>
63                         </parameter>
64                 </syntax>
65                 <description>
66                         <para>Performs mathematical functions based on two parameters and an operator.  The returned
67                         value type is <replaceable>type</replaceable></para>
68                         <para>Example: Set(i=${MATH(123%16,int)}) - sets var i=11</para>
69                 </description>
70         </function>
71         <function name="INC" language="en_US">
72                 <synopsis>
73                         Increments the value of a variable, while returning the updated value to the dialplan
74                 </synopsis>
75                 <syntax>
76                         <parameter name="variable" required="true">
77                                 <para>
78                                 The variable name to be manipulated, without the braces.
79                                 </para>
80                         </parameter>
81                 </syntax>
82                 <description>
83                         <para>Increments the value of a variable, while returning the updated value to the dialplan</para>
84                         <para>Example: INC(MyVAR) - Increments MyVar</para>
85                         <para>Note: INC(${MyVAR}) - Is wrong, as INC expects the variable name, not its value</para>
86                 </description>
87         </function>
88         <function name="DEC" language="en_US">
89                 <synopsis>
90                         Decrements the value of a variable, while returning the updated value to the dialplan
91                 </synopsis>
92                 <syntax>
93                         <parameter name="variable" required="true">
94                                 <para>
95                                 The variable name to be manipulated, without the braces.
96                                 </para>
97                         </parameter>
98                 </syntax>
99                 <description>
100                         <para>Decrements the value of a variable, while returning the updated value to the dialplan</para>
101                         <para>Example: DEC(MyVAR) - Increments MyVar</para>
102                         <para>Note: DEC(${MyVAR}) - Is wrong, as INC expects the variable name, not its value</para>
103                 </description>
104         </function>
105  ***/
106
107 enum TypeOfFunctions {
108         ADDFUNCTION,
109         DIVIDEFUNCTION,
110         MULTIPLYFUNCTION,
111         SUBTRACTFUNCTION,
112         MODULUSFUNCTION,
113         POWFUNCTION,
114         SHLEFTFUNCTION,
115         SHRIGHTFUNCTION,
116         BITWISEANDFUNCTION,
117         BITWISEXORFUNCTION,
118         BITWISEORFUNCTION,
119         GTFUNCTION,
120         LTFUNCTION,
121         GTEFUNCTION,
122         LTEFUNCTION,
123         EQFUNCTION
124 };
125
126 enum TypeOfResult {
127         FLOAT_RESULT,
128         INT_RESULT,
129         HEX_RESULT,
130         CHAR_RESULT
131 };
132
133 static int math(struct ast_channel *chan, const char *cmd, char *parse,
134                 char *buf, size_t len)
135 {
136         double fnum1;
137         double fnum2;
138         double ftmp = 0;
139         char *op;
140         int iaction = -1;
141         int type_of_result = FLOAT_RESULT;
142         char *mvalue1, *mvalue2 = NULL, *mtype_of_result;
143         int negvalue1 = 0;
144         AST_DECLARE_APP_ARGS(args,
145                              AST_APP_ARG(argv0);
146                              AST_APP_ARG(argv1);
147         );
148
149         if (ast_strlen_zero(parse)) {
150                 ast_log(LOG_WARNING, "Syntax: MATH(<number1><op><number 2>[,<type_of_result>]) - missing argument!\n");
151                 return -1;
152         }
153
154         AST_STANDARD_APP_ARGS(args, parse);
155
156         if (args.argc < 1) {
157                 ast_log(LOG_WARNING, "Syntax: MATH(<number1><op><number 2>[,<type_of_result>]) - missing argument!\n");
158                 return -1;
159         }
160
161         mvalue1 = args.argv0;
162
163         if (mvalue1[0] == '-') {
164                 negvalue1 = 1;
165                 mvalue1++;
166         }
167
168         if ((op = strchr(mvalue1, '*'))) {
169                 iaction = MULTIPLYFUNCTION;
170                 *op = '\0';
171         } else if ((op = strchr(mvalue1, '/'))) {
172                 iaction = DIVIDEFUNCTION;
173                 *op = '\0';
174         } else if ((op = strchr(mvalue1, '%'))) {
175                 iaction = MODULUSFUNCTION;
176                 *op = '\0';
177         } else if ((op = strchr(mvalue1, '^'))) {
178                 iaction = POWFUNCTION;
179                 *op = '\0';
180         } else if ((op = strstr(mvalue1, "AND"))) {
181                 iaction = BITWISEANDFUNCTION;
182                 op += 3;
183                 *op = '\0';
184         } else if ((op = strstr(mvalue1, "XOR"))) {
185                 iaction = BITWISEXORFUNCTION;
186                 op += 3;
187                 *op = '\0';
188         } else if ((op = strstr(mvalue1, "OR"))) {
189                 iaction = BITWISEORFUNCTION;
190                 op += 2;
191                 *op = '\0';
192         } else if ((op = strchr(mvalue1, '>'))) {
193                 iaction = GTFUNCTION;
194                 *op = '\0';
195                 if (*(op + 1) == '=') {
196                         *++op = '\0';
197                         iaction = GTEFUNCTION;
198                 } else if (*(op + 1) == '>') {
199                         *++op = '\0';
200                         iaction = SHRIGHTFUNCTION;
201                 }
202         } else if ((op = strchr(mvalue1, '<'))) {
203                 iaction = LTFUNCTION;
204                 *op = '\0';
205                 if (*(op + 1) == '=') {
206                         *++op = '\0';
207                         iaction = LTEFUNCTION;
208                 } else if (*(op + 1) == '<') {
209                         *++op = '\0';
210                         iaction = SHLEFTFUNCTION;
211                 }
212         } else if ((op = strchr(mvalue1, '='))) {
213                 *op = '\0';
214                 if (*(op + 1) == '=') {
215                         *++op = '\0';
216                         iaction = EQFUNCTION;
217                 } else
218                         op = NULL;
219         } else if ((op = strchr(mvalue1, '+'))) {
220                 iaction = ADDFUNCTION;
221                 *op = '\0';
222         } else if ((op = strchr(mvalue1, '-'))) { /* subtraction MUST always be last, in case we have a negative first number */
223                 iaction = SUBTRACTFUNCTION;
224                 *op = '\0';
225         }
226
227         if (op)
228                 mvalue2 = op + 1;
229
230         /* detect wanted type of result */
231         mtype_of_result = args.argv1;
232         if (mtype_of_result) {
233                 if (!strcasecmp(mtype_of_result, "float")
234                     || !strcasecmp(mtype_of_result, "f"))
235                         type_of_result = FLOAT_RESULT;
236                 else if (!strcasecmp(mtype_of_result, "int")
237                          || !strcasecmp(mtype_of_result, "i"))
238                         type_of_result = INT_RESULT;
239                 else if (!strcasecmp(mtype_of_result, "hex")
240                          || !strcasecmp(mtype_of_result, "h"))
241                         type_of_result = HEX_RESULT;
242                 else if (!strcasecmp(mtype_of_result, "char")
243                          || !strcasecmp(mtype_of_result, "c"))
244                         type_of_result = CHAR_RESULT;
245                 else {
246                         ast_log(LOG_WARNING, "Unknown type of result requested '%s'.\n",
247                                         mtype_of_result);
248                         return -1;
249                 }
250         }
251
252         if (!mvalue1 || !mvalue2) {
253                 ast_log(LOG_WARNING,
254                                 "Supply all the parameters - just this once, please\n");
255                 return -1;
256         }
257
258         if (sscanf(mvalue1, "%lf", &fnum1) != 1) {
259                 ast_log(LOG_WARNING, "'%s' is not a valid number\n", mvalue1);
260                 return -1;
261         }
262
263         if (sscanf(mvalue2, "%lf", &fnum2) != 1) {
264                 ast_log(LOG_WARNING, "'%s' is not a valid number\n", mvalue2);
265                 return -1;
266         }
267
268         if (negvalue1)
269                 fnum1 = 0 - fnum1;
270
271         switch (iaction) {
272         case ADDFUNCTION:
273                 ftmp = fnum1 + fnum2;
274                 break;
275         case DIVIDEFUNCTION:
276                 if (fnum2 <= 0)
277                         ftmp = 0;                       /* can't do a divide by 0 */
278                 else
279                         ftmp = (fnum1 / fnum2);
280                 break;
281         case MULTIPLYFUNCTION:
282                 ftmp = (fnum1 * fnum2);
283                 break;
284         case SUBTRACTFUNCTION:
285                 ftmp = (fnum1 - fnum2);
286                 break;
287         case MODULUSFUNCTION:
288                 {
289                         int inum1 = fnum1;
290                         int inum2 = fnum2;
291
292                         ftmp = (inum1 % inum2);
293
294                         break;
295                 }
296         case POWFUNCTION:
297                 ftmp = pow(fnum1, fnum2);
298                 break;
299         case SHLEFTFUNCTION:
300                 {
301                         int inum1 = fnum1;
302                         int inum2 = fnum2;
303
304                         ftmp = (inum1 << inum2);
305                         break;
306                 }
307         case SHRIGHTFUNCTION:
308                 {
309                         int inum1 = fnum1;
310                         int inum2 = fnum2;
311
312                         ftmp = (inum1 >> inum2);
313                         break;
314                 }
315         case BITWISEANDFUNCTION:
316                 {
317                         int inum1 = fnum1;
318                         int inum2 = fnum2;
319                         ftmp = (inum1 & inum2);
320                         break;
321                 }
322         case BITWISEXORFUNCTION:
323                 {
324                         int inum1 = fnum1;
325                         int inum2 = fnum2;
326                         ftmp = (inum1 ^ inum2);
327                         break;
328                 }
329         case BITWISEORFUNCTION:
330                 {
331                         int inum1 = fnum1;
332                         int inum2 = fnum2;
333                         ftmp = (inum1 | inum2);
334                         break;
335                 }
336         case GTFUNCTION:
337                 ast_copy_string(buf, (fnum1 > fnum2) ? "TRUE" : "FALSE", len);
338                 break;
339         case LTFUNCTION:
340                 ast_copy_string(buf, (fnum1 < fnum2) ? "TRUE" : "FALSE", len);
341                 break;
342         case GTEFUNCTION:
343                 ast_copy_string(buf, (fnum1 >= fnum2) ? "TRUE" : "FALSE", len);
344                 break;
345         case LTEFUNCTION:
346                 ast_copy_string(buf, (fnum1 <= fnum2) ? "TRUE" : "FALSE", len);
347                 break;
348         case EQFUNCTION:
349                 ast_copy_string(buf, (fnum1 == fnum2) ? "TRUE" : "FALSE", len);
350                 break;
351         default:
352                 ast_log(LOG_WARNING,
353                                 "Something happened that neither of us should be proud of %d\n",
354                                 iaction);
355                 return -1;
356         }
357
358         if (iaction < GTFUNCTION || iaction > EQFUNCTION) {
359                 if (type_of_result == FLOAT_RESULT)
360                         snprintf(buf, len, "%f", ftmp);
361                 else if (type_of_result == INT_RESULT)
362                         snprintf(buf, len, "%i", (int) ftmp);
363                 else if (type_of_result == HEX_RESULT)
364                         snprintf(buf, len, "%x", (unsigned int) ftmp);
365                 else if (type_of_result == CHAR_RESULT)
366                         snprintf(buf, len, "%c", (unsigned char) ftmp);
367         }
368
369         return 0;
370 }
371
372 static int crement_function_read(struct ast_channel *chan, const char *cmd,
373                      char *data, char *buf, size_t len)
374 {
375         int ret = -1;
376         int int_value = 0;
377         int modify_orig = 0;
378         const char *var;
379         char endchar = 0, returnvar[12]; /* If you need a variable longer than 11 digits - something is way wrong */
380
381         if (ast_strlen_zero(data)) {
382                 ast_log(LOG_WARNING, "Syntax: %s(<data>) - missing argument!\n", cmd);
383                 return -1;
384         }
385
386         ast_channel_lock(chan);
387
388         if (!(var = pbx_builtin_getvar_helper(chan, data))) {
389                 ast_log(LOG_NOTICE, "Failed to obtain variable %s, bailing out\n", data);
390                 ast_channel_unlock(chan);
391                 return -1;
392         }
393
394         if (ast_strlen_zero(var)) {
395                 ast_log(LOG_NOTICE, "Variable %s doesn't exist - are you sure you wrote it corrrectly?\n", data);
396                 ast_channel_unlock(chan);
397                 return -1;
398         }
399
400         if (sscanf(var, "%d%c", &int_value, &endchar) == 0 || endchar != 0) {
401                 ast_log(LOG_NOTICE, "The content of ${%s} is not a numeric value - bailing out!\n", data);
402                 ast_channel_unlock(chan);
403                 return -1;
404         }
405
406         /* now we'll actually do something useful */
407         if (!strcasecmp(cmd, "INC")) {              /* Increment variable */
408                 int_value++;
409                 modify_orig = 1;
410         } else if (!strcasecmp(cmd, "DEC")) {       /* Decrement variable */
411                 int_value--;
412                 modify_orig = 1;
413         }
414
415         ast_log(LOG_NOTICE, "The value is now: %d\n", int_value);
416
417         if (snprintf(returnvar, sizeof(returnvar), "%d", int_value) > 0) {
418                 pbx_builtin_setvar_helper(chan, data, returnvar);
419                 if (modify_orig) {
420                         ast_copy_string(buf, returnvar, len);
421                 }
422                 ret = 0;
423         } else {
424                 pbx_builtin_setvar_helper(chan, data, "0");
425                 if (modify_orig) {
426                         ast_copy_string(buf, "0", len);
427                 }
428                 ast_log(LOG_NOTICE, "Variable %s refused to be %sREMENTED, setting value to 0", data, cmd);
429                 ret = 0;
430         }
431
432         ast_channel_unlock(chan);
433
434         return ret;
435 }
436
437
438 static struct ast_custom_function math_function = {
439         .name = "MATH",
440         .read = math
441 };
442
443 static struct ast_custom_function increment_function = {
444         .name = "INC",
445         .read = crement_function_read,
446 };
447
448 static struct ast_custom_function decrement_function = {
449         .name = "DEC",
450         .read = crement_function_read,
451 };
452
453 static int unload_module(void)
454 {
455         int res = 0;
456
457         res |= ast_custom_function_unregister(&math_function);
458         res |= ast_custom_function_unregister(&increment_function);
459         res |= ast_custom_function_unregister(&decrement_function);
460
461         return res;
462 }
463
464 static int load_module(void)
465 {
466         int res = 0;
467
468         res |= ast_custom_function_register(&math_function);
469         res |= ast_custom_function_register(&increment_function);
470         res |= ast_custom_function_register(&decrement_function);
471
472         return res;
473 }
474
475 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Mathematical dialplan function");