Wed Jan 8 2020 09:49:47

Asterisk developer's documentation


func_math.c
Go to the documentation of this file.
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 /*** MODULEINFO
32  <support_level>core</support_level>
33  ***/
34 
35 #include "asterisk.h"
36 
37 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 411313 $")
38 
39 #include <math.h>
40 
41 #include "asterisk/module.h"
42 #include "asterisk/channel.h"
43 #include "asterisk/pbx.h"
44 #include "asterisk/utils.h"
45 #include "asterisk/app.h"
46 #include "asterisk/config.h"
47 #include "asterisk/test.h"
48 
49 /*** DOCUMENTATION
50  <function name="MATH" language="en_US">
51  <synopsis>
52  Performs Mathematical Functions.
53  </synopsis>
54  <syntax>
55  <parameter name="expression" required="true">
56  <para>Is of the form:
57  <replaceable>number1</replaceable><replaceable>op</replaceable><replaceable>number2</replaceable>
58  where the possible values for <replaceable>op</replaceable>
59  are:</para>
60  <para>+,-,/,*,%,&lt;&lt;,&gt;&gt;,^,AND,OR,XOR,&lt;,&gt;,&lt;=,&gt;=,== (and behave as their C equivalents)</para>
61  </parameter>
62  <parameter name="type">
63  <para>Wanted type of result:</para>
64  <para>f, float - float(default)</para>
65  <para>i, int - integer</para>
66  <para>h, hex - hex</para>
67  <para>c, char - char</para>
68  </parameter>
69  </syntax>
70  <description>
71  <para>Performs mathematical functions based on two parameters and an operator. The returned
72  value type is <replaceable>type</replaceable></para>
73  <para>Example: Set(i=${MATH(123%16,int)}) - sets var i=11</para>
74  </description>
75  </function>
76  <function name="INC" language="en_US">
77  <synopsis>
78  Increments the value of a variable, while returning the updated value to the dialplan
79  </synopsis>
80  <syntax>
81  <parameter name="variable" required="true">
82  <para>
83  The variable name to be manipulated, without the braces.
84  </para>
85  </parameter>
86  </syntax>
87  <description>
88  <para>Increments the value of a variable, while returning the updated value to the dialplan</para>
89  <para>Example: INC(MyVAR) - Increments MyVar</para>
90  <para>Note: INC(${MyVAR}) - Is wrong, as INC expects the variable name, not its value</para>
91  </description>
92  </function>
93  <function name="DEC" language="en_US">
94  <synopsis>
95  Decrements the value of a variable, while returning the updated value to the dialplan
96  </synopsis>
97  <syntax>
98  <parameter name="variable" required="true">
99  <para>
100  The variable name to be manipulated, without the braces.
101  </para>
102  </parameter>
103  </syntax>
104  <description>
105  <para>Decrements the value of a variable, while returning the updated value to the dialplan</para>
106  <para>Example: DEC(MyVAR) - Decrements MyVar</para>
107  <para>Note: DEC(${MyVAR}) - Is wrong, as DEC expects the variable name, not its value</para>
108  </description>
109  </function>
110  ***/
111 
129 };
130 
136 };
137 
138 static int math(struct ast_channel *chan, const char *cmd, char *parse,
139  char *buf, size_t len)
140 {
141  double fnum1;
142  double fnum2;
143  double ftmp = 0;
144  char *op;
145  int iaction = -1;
146  int type_of_result = FLOAT_RESULT;
147  char *mvalue1, *mvalue2 = NULL, *mtype_of_result;
148  int negvalue1 = 0;
150  AST_APP_ARG(argv0);
151  AST_APP_ARG(argv1);
152  );
153 
154  if (ast_strlen_zero(parse)) {
155  ast_log(LOG_WARNING, "Syntax: MATH(<number1><op><number 2>[,<type_of_result>]) - missing argument!\n");
156  return -1;
157  }
158 
159  AST_STANDARD_APP_ARGS(args, parse);
160 
161  if (args.argc < 1) {
162  ast_log(LOG_WARNING, "Syntax: MATH(<number1><op><number 2>[,<type_of_result>]) - missing argument!\n");
163  return -1;
164  }
165 
166  mvalue1 = args.argv0;
167 
168  if (mvalue1[0] == '-') {
169  negvalue1 = 1;
170  mvalue1++;
171  }
172 
173  if ((op = strchr(mvalue1, '*'))) {
174  iaction = MULTIPLYFUNCTION;
175  *op = '\0';
176  } else if ((op = strchr(mvalue1, '/'))) {
177  iaction = DIVIDEFUNCTION;
178  *op = '\0';
179  } else if ((op = strchr(mvalue1, '%'))) {
180  iaction = MODULUSFUNCTION;
181  *op = '\0';
182  } else if ((op = strchr(mvalue1, '^'))) {
183  iaction = POWFUNCTION;
184  *op = '\0';
185  } else if ((op = strstr(mvalue1, "AND"))) {
186  iaction = BITWISEANDFUNCTION;
187  *op = '\0';
188  op += 2;
189  } else if ((op = strstr(mvalue1, "XOR"))) {
190  iaction = BITWISEXORFUNCTION;
191  *op = '\0';
192  op += 2;
193  } else if ((op = strstr(mvalue1, "OR"))) {
194  iaction = BITWISEORFUNCTION;
195  *op = '\0';
196  ++op;
197  } else if ((op = strchr(mvalue1, '>'))) {
198  iaction = GTFUNCTION;
199  *op = '\0';
200  if (*(op + 1) == '=') {
201  iaction = GTEFUNCTION;
202  ++op;
203  } else if (*(op + 1) == '>') {
204  iaction = SHRIGHTFUNCTION;
205  ++op;
206  }
207  } else if ((op = strchr(mvalue1, '<'))) {
208  iaction = LTFUNCTION;
209  *op = '\0';
210  if (*(op + 1) == '=') {
211  iaction = LTEFUNCTION;
212  ++op;
213  } else if (*(op + 1) == '<') {
214  iaction = SHLEFTFUNCTION;
215  ++op;
216  }
217  } else if ((op = strchr(mvalue1, '='))) {
218  *op = '\0';
219  if (*(op + 1) == '=') {
220  iaction = EQFUNCTION;
221  ++op;
222  } else
223  op = NULL;
224  } else if ((op = strchr(mvalue1, '+'))) {
225  iaction = ADDFUNCTION;
226  *op = '\0';
227  } else if ((op = strchr(mvalue1, '-'))) { /* subtraction MUST always be last, in case we have a negative second number */
228  iaction = SUBTRACTFUNCTION;
229  *op = '\0';
230  }
231 
232  if (op)
233  mvalue2 = op + 1;
234 
235  /* detect wanted type of result */
236  mtype_of_result = args.argv1;
237  if (mtype_of_result) {
238  if (!strcasecmp(mtype_of_result, "float")
239  || !strcasecmp(mtype_of_result, "f"))
240  type_of_result = FLOAT_RESULT;
241  else if (!strcasecmp(mtype_of_result, "int")
242  || !strcasecmp(mtype_of_result, "i"))
243  type_of_result = INT_RESULT;
244  else if (!strcasecmp(mtype_of_result, "hex")
245  || !strcasecmp(mtype_of_result, "h"))
246  type_of_result = HEX_RESULT;
247  else if (!strcasecmp(mtype_of_result, "char")
248  || !strcasecmp(mtype_of_result, "c"))
249  type_of_result = CHAR_RESULT;
250  else {
251  ast_log(LOG_WARNING, "Unknown type of result requested '%s'.\n",
252  mtype_of_result);
253  return -1;
254  }
255  }
256 
257  if (!mvalue2) {
259  "Supply all the parameters - just this once, please\n");
260  return -1;
261  }
262 
263  if (sscanf(mvalue1, "%30lf", &fnum1) != 1) {
264  ast_log(LOG_WARNING, "'%s' is not a valid number\n", mvalue1);
265  return -1;
266  }
267 
268  if (sscanf(mvalue2, "%30lf", &fnum2) != 1) {
269  ast_log(LOG_WARNING, "'%s' is not a valid number\n", mvalue2);
270  return -1;
271  }
272 
273  if (negvalue1)
274  fnum1 = 0 - fnum1;
275 
276  switch (iaction) {
277  case ADDFUNCTION:
278  ftmp = fnum1 + fnum2;
279  break;
280  case DIVIDEFUNCTION:
281  if (fnum2 <= 0)
282  ftmp = 0; /* can't do a divide by 0 */
283  else
284  ftmp = (fnum1 / fnum2);
285  break;
286  case MULTIPLYFUNCTION:
287  ftmp = (fnum1 * fnum2);
288  break;
289  case SUBTRACTFUNCTION:
290  ftmp = (fnum1 - fnum2);
291  break;
292  case MODULUSFUNCTION:
293  {
294  int inum1 = fnum1;
295  int inum2 = fnum2;
296 
297  if (inum2 == 0) {
298  ftmp = 0;
299  } else {
300  ftmp = (inum1 % inum2);
301  }
302 
303  break;
304  }
305  case POWFUNCTION:
306  ftmp = pow(fnum1, fnum2);
307  break;
308  case SHLEFTFUNCTION:
309  {
310  int inum1 = fnum1;
311  int inum2 = fnum2;
312 
313  ftmp = (inum1 << inum2);
314  break;
315  }
316  case SHRIGHTFUNCTION:
317  {
318  int inum1 = fnum1;
319  int inum2 = fnum2;
320 
321  ftmp = (inum1 >> inum2);
322  break;
323  }
324  case BITWISEANDFUNCTION:
325  {
326  int inum1 = fnum1;
327  int inum2 = fnum2;
328  ftmp = (inum1 & inum2);
329  break;
330  }
331  case BITWISEXORFUNCTION:
332  {
333  int inum1 = fnum1;
334  int inum2 = fnum2;
335  ftmp = (inum1 ^ inum2);
336  break;
337  }
338  case BITWISEORFUNCTION:
339  {
340  int inum1 = fnum1;
341  int inum2 = fnum2;
342  ftmp = (inum1 | inum2);
343  break;
344  }
345  case GTFUNCTION:
346  ast_copy_string(buf, (fnum1 > fnum2) ? "TRUE" : "FALSE", len);
347  break;
348  case LTFUNCTION:
349  ast_copy_string(buf, (fnum1 < fnum2) ? "TRUE" : "FALSE", len);
350  break;
351  case GTEFUNCTION:
352  ast_copy_string(buf, (fnum1 >= fnum2) ? "TRUE" : "FALSE", len);
353  break;
354  case LTEFUNCTION:
355  ast_copy_string(buf, (fnum1 <= fnum2) ? "TRUE" : "FALSE", len);
356  break;
357  case EQFUNCTION:
358  ast_copy_string(buf, (fnum1 == fnum2) ? "TRUE" : "FALSE", len);
359  break;
360  default:
362  "Something happened that neither of us should be proud of %d\n",
363  iaction);
364  return -1;
365  }
366 
367  if (iaction < GTFUNCTION || iaction > EQFUNCTION) {
368  if (type_of_result == FLOAT_RESULT)
369  snprintf(buf, len, "%f", ftmp);
370  else if (type_of_result == INT_RESULT)
371  snprintf(buf, len, "%i", (int) ftmp);
372  else if (type_of_result == HEX_RESULT)
373  snprintf(buf, len, "%x", (unsigned int) ftmp);
374  else if (type_of_result == CHAR_RESULT)
375  snprintf(buf, len, "%c", (unsigned char) ftmp);
376  }
377 
378  return 0;
379 }
380 
381 static int crement_function_read(struct ast_channel *chan, const char *cmd,
382  char *data, char *buf, size_t len)
383 {
384  int ret = -1;
385  int int_value = 0;
386  int modify_orig = 0;
387  const char *var;
388  char endchar = 0, returnvar[12]; /* If you need a variable longer than 11 digits - something is way wrong */
389 
390  if (ast_strlen_zero(data)) {
391  ast_log(LOG_WARNING, "Syntax: %s(<data>) - missing argument!\n", cmd);
392  return -1;
393  }
394 
395  if (!chan) {
396  ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
397  return -1;
398  }
399 
400  ast_channel_lock(chan);
401 
402  if (!(var = pbx_builtin_getvar_helper(chan, data))) {
403  ast_log(LOG_NOTICE, "Failed to obtain variable %s, bailing out\n", data);
404  ast_channel_unlock(chan);
405  return -1;
406  }
407 
408  if (ast_strlen_zero(var)) {
409  ast_log(LOG_NOTICE, "Variable %s doesn't exist - are you sure you wrote it correctly?\n", data);
410  ast_channel_unlock(chan);
411  return -1;
412  }
413 
414  if (sscanf(var, "%30d%1c", &int_value, &endchar) == 0 || endchar != 0) {
415  ast_log(LOG_NOTICE, "The content of ${%s} is not a numeric value - bailing out!\n", data);
416  ast_channel_unlock(chan);
417  return -1;
418  }
419 
420  /* now we'll actually do something useful */
421  if (!strcasecmp(cmd, "INC")) { /* Increment variable */
422  int_value++;
423  modify_orig = 1;
424  } else if (!strcasecmp(cmd, "DEC")) { /* Decrement variable */
425  int_value--;
426  modify_orig = 1;
427  }
428 
429  if (snprintf(returnvar, sizeof(returnvar), "%d", int_value) > 0) {
430  pbx_builtin_setvar_helper(chan, data, returnvar);
431  if (modify_orig) {
432  ast_copy_string(buf, returnvar, len);
433  }
434  ret = 0;
435  } else {
436  pbx_builtin_setvar_helper(chan, data, "0");
437  if (modify_orig) {
438  ast_copy_string(buf, "0", len);
439  }
440  ast_log(LOG_NOTICE, "Variable %s refused to be %sREMENTED, setting value to 0", data, cmd);
441  ret = 0;
442  }
443 
444  ast_channel_unlock(chan);
445 
446  return ret;
447 }
448 
449 
451  .name = "MATH",
452  .read = math
453 };
454 
456  .name = "INC",
457  .read = crement_function_read,
458 };
459 
461  .name = "DEC",
462  .read = crement_function_read,
463 };
464 
465 #ifdef TEST_FRAMEWORK
466 AST_TEST_DEFINE(test_MATH_function)
467 {
469  struct ast_str *expr, *result;
470 
471  switch (cmd) {
472  case TEST_INIT:
473  info->name = "test_MATH_function";
474  info->category = "/main/pbx/";
475  info->summary = "Test MATH function substitution";
476  info->description =
477  "Executes a series of variable substitutions using the MATH function and ensures that the expected results are received.";
478  return AST_TEST_NOT_RUN;
479  case TEST_EXECUTE:
480  break;
481  }
482 
483  ast_test_status_update(test, "Testing MATH() substitution ...\n");
484 
485  if (!(expr = ast_str_create(16)) || !(result = ast_str_create(16))) {
486  if (expr) {
487  ast_free(expr);
488  }
489  if (result) {
490  ast_free(result);
491  }
492  return AST_TEST_FAIL;
493  }
494 
495  ast_str_set(&expr, 0, "${MATH(170 AND 63,i)}");
496  ast_str_substitute_variables(&result, 0, NULL, ast_str_buffer(expr));
497  if (strcmp(ast_str_buffer(result), "42") != 0) {
498  ast_test_status_update(test, "Expected result '42' not returned! ('%s')\n",
499  ast_str_buffer(result));
500  res = AST_TEST_FAIL;
501  }
502 
503  ast_str_set(&expr, 0, "${MATH(170AND63,i)}");
504  ast_str_substitute_variables(&result, 0, NULL, ast_str_buffer(expr));
505  if (strcmp(ast_str_buffer(result), "42") != 0) {
506  ast_test_status_update(test, "Expected result '42' not returned! ('%s')\n",
507  ast_str_buffer(result));
508  res = AST_TEST_FAIL;
509  }
510 
511  ast_free(expr);
512  ast_free(result);
513 
514  return res;
515 }
516 #endif
517 
518 static int unload_module(void)
519 {
520  int res = 0;
521 
522  res |= ast_custom_function_unregister(&math_function);
523  res |= ast_custom_function_unregister(&increment_function);
524  res |= ast_custom_function_unregister(&decrement_function);
525  AST_TEST_UNREGISTER(test_MATH_function);
526 
527  return res;
528 }
529 
530 static int load_module(void)
531 {
532  int res = 0;
533 
534  res |= ast_custom_function_register(&math_function);
535  res |= ast_custom_function_register(&increment_function);
536  res |= ast_custom_function_register(&decrement_function);
537  AST_TEST_REGISTER(test_MATH_function);
538 
539  return res;
540 }
541 
542 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Mathematical dialplan function");
static struct ast_custom_function math_function
Definition: func_math.c:450
#define ast_channel_lock(chan)
Definition: channel.h:2466
Main Channel structure associated with a channel.
Definition: channel.h:742
#define AST_MODULE_INFO_STANDARD(keystr, desc)
Definition: module.h:396
Asterisk main include file. File version handling, generic pbx functions.
static struct ast_custom_function decrement_function
Definition: func_math.c:460
static int unload_module(void)
Definition: func_math.c:518
#define LOG_WARNING
Definition: logger.h:144
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:497
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application&#39;s arguments.
Definition: app.h:572
#define var
Definition: ast_expr2f.c:606
void ast_str_substitute_variables(struct ast_str **buf, ssize_t maxlen, struct ast_channel *chan, const char *templ)
Definition: pbx.c:4468
Test Framework API.
#define AST_TEST_REGISTER(cb)
Definition: test.h:127
TypeOfResult
Definition: func_math.c:131
Configuration File Parser.
struct ast_str * ast_str_create(size_t init_len)
Create a malloc&#39;ed dynamic length string.
Definition: strings.h:420
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
Definition: pbx.c:3814
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
Definition: pbx.c:10475
Utility functions.
static int crement_function_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition: func_math.c:381
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:874
static struct ast_custom_function increment_function
Definition: func_math.c:455
General Asterisk PBX channel definitions.
#define ast_test_status_update(a, b, c...)
Definition: test.h:129
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:63
Data structure associated with a custom dialplan function.
Definition: pbx.h:95
Core PBX routines and definitions.
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:364
static struct @350 args
TypeOfFunctions
Definition: func_math.c:112
#define AST_TEST_UNREGISTER(cb)
Definition: test.h:128
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
void ast_log(int level, const char *file, int line, const char *function, const char *fmt,...)
Used for sending a log message This is the standard logger function. Probably the only way you will i...
Definition: logger.c:1207
#define LOG_NOTICE
Definition: logger.h:133
#define ast_channel_unlock(chan)
Definition: channel.h:2467
static void parse(struct mgcp_request *req)
Definition: chan_mgcp.c:1858
#define ast_free(a)
Definition: astmm.h:97
static int math(struct ast_channel *chan, const char *cmd, char *parse, char *buf, size_t len)
Definition: func_math.c:138
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name...
Definition: pbx.c:10546
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:223
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
#define AST_TEST_DEFINE(hdr)
Definition: test.h:126
const char * name
Definition: pbx.h:96
#define AST_APP_ARG(name)
Define an application argument.
Definition: app.h:555
static int load_module(void)
Definition: func_math.c:530
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the &#39;standard&#39; argument separation process for an application.
Definition: app.h:604
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:38
Asterisk module definitions.
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1164
ast_test_result_state
Definition: test.h:189
#define ASTERISK_FILE_VERSION(file, version)
Register/unregister a source code file with the core.
Definition: asterisk.h:180