Merged revisions 67162 via svnmerge from
[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 <stdlib.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <math.h>
37 #include <sys/types.h>
38
39 #include "asterisk/module.h"
40 #include "asterisk/channel.h"
41 #include "asterisk/pbx.h"
42 #include "asterisk/logger.h"
43 #include "asterisk/utils.h"
44 #include "asterisk/app.h"
45 #include "asterisk/config.h"
46
47 enum TypeOfFunctions {
48         ADDFUNCTION,
49         DIVIDEFUNCTION,
50         MULTIPLYFUNCTION,
51         SUBTRACTFUNCTION,
52         MODULUSFUNCTION,
53         POWFUNCTION,
54         SHLEFTFUNCTION,
55         SHRIGHTFUNCTION,
56         GTFUNCTION,
57         LTFUNCTION,
58         GTEFUNCTION,
59         LTEFUNCTION,
60         EQFUNCTION
61 };
62
63 enum TypeOfResult {
64         FLOAT_RESULT,
65         INT_RESULT,
66         HEX_RESULT,
67         CHAR_RESULT
68 };
69
70 static int math(struct ast_channel *chan, const char *cmd, char *parse,
71                 char *buf, size_t len)
72 {
73         double fnum1;
74         double fnum2;
75         double ftmp = 0;
76         char *op;
77         int iaction = -1;
78         int type_of_result = FLOAT_RESULT;
79         char *mvalue1, *mvalue2 = NULL, *mtype_of_result;
80         int negvalue1 = 0;
81         AST_DECLARE_APP_ARGS(args,
82                              AST_APP_ARG(argv0);
83                              AST_APP_ARG(argv1);
84         );
85
86         if (ast_strlen_zero(parse)) {
87                 ast_log(LOG_WARNING, "Syntax: Math(<number1><op><number 2>[,<type_of_result>]) - missing argument!\n");
88                 return -1;
89         }
90
91         AST_STANDARD_APP_ARGS(args, parse);
92
93         if (args.argc < 1) {
94                 ast_log(LOG_WARNING, "Syntax: Math(<number1><op><number 2>[,<type_of_result>]) - missing argument!\n");
95                 return -1;
96         }
97
98         mvalue1 = args.argv0;
99
100         if (mvalue1[0] == '-') {
101                 negvalue1 = 1;
102                 mvalue1++;
103         }
104
105         if ((op = strchr(mvalue1, '*'))) {
106                 iaction = MULTIPLYFUNCTION;
107                 *op = '\0';
108         } else if ((op = strchr(mvalue1, '/'))) {
109                 iaction = DIVIDEFUNCTION;
110                 *op = '\0';
111         } else if ((op = strchr(mvalue1, '%'))) {
112                 iaction = MODULUSFUNCTION;
113                 *op = '\0';
114         } else if ((op = strchr(mvalue1, '^'))) {
115                 iaction = POWFUNCTION;
116                 *op = '\0';
117         } else if ((op = strchr(mvalue1, '>'))) {
118                 iaction = GTFUNCTION;
119                 *op = '\0';
120                 if (*(op + 1) == '=') {
121                         *++op = '\0';
122                         iaction = GTEFUNCTION;
123                 } else if (*(op + 1) == '>') {
124                         *++op = '\0';
125                         iaction = SHRIGHTFUNCTION;
126                 }
127         } else if ((op = strchr(mvalue1, '<'))) {
128                 iaction = LTFUNCTION;
129                 *op = '\0';
130                 if (*(op + 1) == '=') {
131                         *++op = '\0';
132                         iaction = LTEFUNCTION;
133                 } else if (*(op + 1) == '<') {
134                         *++op = '\0';
135                         iaction = SHLEFTFUNCTION;
136                 }
137         } else if ((op = strchr(mvalue1, '='))) {
138                 *op = '\0';
139                 if (*(op + 1) == '=') {
140                         *++op = '\0';
141                         iaction = EQFUNCTION;
142                 } else
143                         op = NULL;
144         } else if ((op = strchr(mvalue1, '+'))) {
145                 iaction = ADDFUNCTION;
146                 *op = '\0';
147         } else if ((op = strchr(mvalue1, '-'))) { /* subtraction MUST always be last, in case we have a negative first number */
148                 iaction = SUBTRACTFUNCTION;
149                 *op = '\0';
150         }
151
152         if (op)
153                 mvalue2 = op + 1;
154
155         /* detect wanted type of result */
156         mtype_of_result = args.argv1;
157         if (mtype_of_result) {
158                 if (!strcasecmp(mtype_of_result, "float")
159                     || !strcasecmp(mtype_of_result, "f"))
160                         type_of_result = FLOAT_RESULT;
161                 else if (!strcasecmp(mtype_of_result, "int")
162                          || !strcasecmp(mtype_of_result, "i"))
163                         type_of_result = INT_RESULT;
164                 else if (!strcasecmp(mtype_of_result, "hex")
165                          || !strcasecmp(mtype_of_result, "h"))
166                         type_of_result = HEX_RESULT;
167                 else if (!strcasecmp(mtype_of_result, "char")
168                          || !strcasecmp(mtype_of_result, "c"))
169                         type_of_result = CHAR_RESULT;
170                 else {
171                         ast_log(LOG_WARNING, "Unknown type of result requested '%s'.\n",
172                                         mtype_of_result);
173                         return -1;
174                 }
175         }
176
177         if (!mvalue1 || !mvalue2) {
178                 ast_log(LOG_WARNING,
179                                 "Supply all the parameters - just this once, please\n");
180                 return -1;
181         }
182
183         if (sscanf(mvalue1, "%lf", &fnum1) != 1) {
184                 ast_log(LOG_WARNING, "'%s' is not a valid number\n", mvalue1);
185                 return -1;
186         }
187
188         if (sscanf(mvalue2, "%lf", &fnum2) != 1) {
189                 ast_log(LOG_WARNING, "'%s' is not a valid number\n", mvalue2);
190                 return -1;
191         }
192
193         if (negvalue1)
194                 fnum1 = 0 - fnum1;
195
196         switch (iaction) {
197         case ADDFUNCTION:
198                 ftmp = fnum1 + fnum2;
199                 break;
200         case DIVIDEFUNCTION:
201                 if (fnum2 <= 0)
202                         ftmp = 0;                       /* can't do a divide by 0 */
203                 else
204                         ftmp = (fnum1 / fnum2);
205                 break;
206         case MULTIPLYFUNCTION:
207                 ftmp = (fnum1 * fnum2);
208                 break;
209         case SUBTRACTFUNCTION:
210                 ftmp = (fnum1 - fnum2);
211                 break;
212         case MODULUSFUNCTION:
213                 {
214                         int inum1 = fnum1;
215                         int inum2 = fnum2;
216
217                         ftmp = (inum1 % inum2);
218
219                         break;
220                 }
221         case POWFUNCTION:
222                 ftmp = pow(fnum1, fnum2);
223                 break;
224         case SHLEFTFUNCTION:
225                 {
226                         int inum1 = fnum1;
227                         int inum2 = fnum2;
228
229                         ftmp = (inum1 << inum2);
230                         break;
231                 }
232         case SHRIGHTFUNCTION:
233                 {
234                         int inum1 = fnum1;
235                         int inum2 = fnum2;
236
237                         ftmp = (inum1 >> inum2);
238                         break;
239                 }
240         case GTFUNCTION:
241                 ast_copy_string(buf, (fnum1 > fnum2) ? "TRUE" : "FALSE", len);
242                 break;
243         case LTFUNCTION:
244                 ast_copy_string(buf, (fnum1 < fnum2) ? "TRUE" : "FALSE", len);
245                 break;
246         case GTEFUNCTION:
247                 ast_copy_string(buf, (fnum1 >= fnum2) ? "TRUE" : "FALSE", len);
248                 break;
249         case LTEFUNCTION:
250                 ast_copy_string(buf, (fnum1 <= fnum2) ? "TRUE" : "FALSE", len);
251                 break;
252         case EQFUNCTION:
253                 ast_copy_string(buf, (fnum1 == fnum2) ? "TRUE" : "FALSE", len);
254                 break;
255         default:
256                 ast_log(LOG_WARNING,
257                                 "Something happened that neither of us should be proud of %d\n",
258                                 iaction);
259                 return -1;
260         }
261
262         if (iaction < GTFUNCTION || iaction > EQFUNCTION) {
263                 if (type_of_result == FLOAT_RESULT)
264                         snprintf(buf, len, "%f", ftmp);
265                 else if (type_of_result == INT_RESULT)
266                         snprintf(buf, len, "%i", (int) ftmp);
267                 else if (type_of_result == HEX_RESULT)
268                         snprintf(buf, len, "%x", (unsigned int) ftmp);
269                 else if (type_of_result == CHAR_RESULT)
270                         snprintf(buf, len, "%c", (unsigned char) ftmp);
271         }
272
273         return 0;
274 }
275
276 static struct ast_custom_function math_function = {
277         .name = "MATH",
278         .synopsis = "Performs Mathematical Functions",
279         .syntax = "MATH(<number1><op><number2>[,<type_of_result>])",
280         .desc = "Perform calculation on number1 to number2. Valid ops are: \n"
281                 "    +,-,/,*,%,<<,>>,^,<,>,>=,<=,==\n"
282                 "and behave as their C equivalents.\n"
283                 "<type_of_result> - wanted type of result:\n"
284                 "       f, float - float(default)\n"
285                 "       i, int - integer,\n"
286                 "       h, hex - hex,\n"
287                 "       c, char - char\n"
288                 "Example: Set(i=${MATH(123%16,int)}) - sets var i=11",
289         .read = math
290 };
291
292 static int unload_module(void)
293 {
294         return ast_custom_function_unregister(&math_function);
295 }
296
297 static int load_module(void)
298 {
299         return ast_custom_function_register(&math_function);
300 }
301
302 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Mathematical dialplan function");