The changes for trunk are less extensive, but include
[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         float fnum1;
74         float fnum2;
75         float 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         AST_DECLARE_APP_ARGS(args,
81                              AST_APP_ARG(argv0);
82                              AST_APP_ARG(argv1);
83         );
84
85         if (ast_strlen_zero(parse)) {
86                 ast_log(LOG_WARNING, "Syntax: Math(<number1><op><number 2>[,<type_of_result>]) - missing argument!\n");
87                 return -1;
88         }
89
90         AST_STANDARD_APP_ARGS(args, parse);
91
92         if (args.argc < 1) {
93                 ast_log(LOG_WARNING, "Syntax: Math(<number1><op><number 2>[,<type_of_result>]) - missing argument!\n");
94                 return -1;
95         }
96
97         mvalue1 = args.argv0;
98
99         if ((op = strchr(mvalue1, '+'))) {
100                 iaction = ADDFUNCTION;
101                 *op = '\0';
102         } else if ((op = strchr(mvalue1, '-'))) {
103                 iaction = SUBTRACTFUNCTION;
104                 *op = '\0';
105         } else 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         }
145
146         if (op)
147                 mvalue2 = op + 1;
148
149         /* detect wanted type of result */
150         mtype_of_result = args.argv1;
151         if (mtype_of_result) {
152                 if (!strcasecmp(mtype_of_result, "float")
153                     || !strcasecmp(mtype_of_result, "f"))
154                         type_of_result = FLOAT_RESULT;
155                 else if (!strcasecmp(mtype_of_result, "int")
156                          || !strcasecmp(mtype_of_result, "i"))
157                         type_of_result = INT_RESULT;
158                 else if (!strcasecmp(mtype_of_result, "hex")
159                          || !strcasecmp(mtype_of_result, "h"))
160                         type_of_result = HEX_RESULT;
161                 else if (!strcasecmp(mtype_of_result, "char")
162                          || !strcasecmp(mtype_of_result, "c"))
163                         type_of_result = CHAR_RESULT;
164                 else {
165                         ast_log(LOG_WARNING, "Unknown type of result requested '%s'.\n",
166                                         mtype_of_result);
167                         return -1;
168                 }
169         }
170
171         if (!mvalue1 || !mvalue2) {
172                 ast_log(LOG_WARNING,
173                                 "Supply all the parameters - just this once, please\n");
174                 return -1;
175         }
176
177         if (sscanf(mvalue1, "%f", &fnum1) != 1) {
178                 ast_log(LOG_WARNING, "'%s' is not a valid number\n", mvalue1);
179                 return -1;
180         }
181
182         if (sscanf(mvalue2, "%f", &fnum2) != 1) {
183                 ast_log(LOG_WARNING, "'%s' is not a valid number\n", mvalue2);
184                 return -1;
185         }
186
187         switch (iaction) {
188         case ADDFUNCTION:
189                 ftmp = fnum1 + fnum2;
190                 break;
191         case DIVIDEFUNCTION:
192                 if (fnum2 <= 0)
193                         ftmp = 0;                       /* can't do a divide by 0 */
194                 else
195                         ftmp = (fnum1 / fnum2);
196                 break;
197         case MULTIPLYFUNCTION:
198                 ftmp = (fnum1 * fnum2);
199                 break;
200         case SUBTRACTFUNCTION:
201                 ftmp = (fnum1 - fnum2);
202                 break;
203         case MODULUSFUNCTION:
204                 {
205                         int inum1 = fnum1;
206                         int inum2 = fnum2;
207
208                         ftmp = (inum1 % inum2);
209
210                         break;
211                 }
212         case POWFUNCTION:
213                 ftmp = pow(fnum1, fnum2);
214                 break;
215         case SHLEFTFUNCTION:
216                 {
217                         int inum1 = fnum1;
218                         int inum2 = fnum2;
219
220                         ftmp = (inum1 << inum2);
221                         break;
222                 }
223         case SHRIGHTFUNCTION:
224                 {
225                         int inum1 = fnum1;
226                         int inum2 = fnum2;
227
228                         ftmp = (inum1 >> inum2);
229                         break;
230                 }
231         case GTFUNCTION:
232                 ast_copy_string(buf, (fnum1 > fnum2) ? "TRUE" : "FALSE", len);
233                 break;
234         case LTFUNCTION:
235                 ast_copy_string(buf, (fnum1 < fnum2) ? "TRUE" : "FALSE", len);
236                 break;
237         case GTEFUNCTION:
238                 ast_copy_string(buf, (fnum1 >= fnum2) ? "TRUE" : "FALSE", len);
239                 break;
240         case LTEFUNCTION:
241                 ast_copy_string(buf, (fnum1 <= fnum2) ? "TRUE" : "FALSE", len);
242                 break;
243         case EQFUNCTION:
244                 ast_copy_string(buf, (fnum1 == fnum2) ? "TRUE" : "FALSE", len);
245                 break;
246         default:
247                 ast_log(LOG_WARNING,
248                                 "Something happened that neither of us should be proud of %d\n",
249                                 iaction);
250                 return -1;
251         }
252
253         if (iaction < GTFUNCTION || iaction > EQFUNCTION) {
254                 if (type_of_result == FLOAT_RESULT)
255                         snprintf(buf, len, "%f", ftmp);
256                 else if (type_of_result == INT_RESULT)
257                         snprintf(buf, len, "%i", (int) ftmp);
258                 else if (type_of_result == HEX_RESULT)
259                         snprintf(buf, len, "%x", (unsigned int) ftmp);
260                 else if (type_of_result == CHAR_RESULT)
261                         snprintf(buf, len, "%c", (unsigned char) ftmp);
262         }
263
264         return 0;
265 }
266
267 static struct ast_custom_function math_function = {
268         .name = "MATH",
269         .synopsis = "Performs Mathematical Functions",
270         .syntax = "MATH(<number1><op><number2>[,<type_of_result>])",
271         .desc = "Perform calculation on number1 to number2. Valid ops are: \n"
272                 "    +,-,/,*,%,<<,>>,^,<,>,>=,<=,==\n"
273                 "and behave as their C equivalents.\n"
274                 "<type_of_result> - wanted type of result:\n"
275                 "       f, float - float(default)\n"
276                 "       i, int - integer,\n"
277                 "       h, hex - hex,\n"
278                 "       c, char - char\n"
279                 "Example: Set(i=${MATH(123%16,int)}) - sets var i=11",
280         .read = math
281 };
282
283 static int unload_module(void)
284 {
285         return ast_custom_function_unregister(&math_function);
286 }
287
288 static int load_module(void)
289 {
290         return ast_custom_function_register(&math_function);
291 }
292
293 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Mathematical dialplan function");