Asterisk - The Open Source Telephony Project  21.4.1
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  * Updated by Naveen Albert <asterisk@phreaknet.org>
9  *
10  * See http://www.asterisk.org for more information about
11  * the Asterisk project. Please do not directly contact
12  * any of the maintainers of this project for assistance;
13  * the project provides a web site, mailing lists and IRC
14  * channels for your use.
15  *
16  * This program is free software, distributed under the terms of
17  * the GNU General Public License Version 2. See the LICENSE file
18  * at the top of the source tree.
19  */
20 
21 /*! \file
22  *
23  * \brief Math related dialplan function
24  *
25  * \author Andy Powell
26  * \author Mark Spencer <markster@digium.com>
27  * \author Nir Simionovich <nirs@greenfieldtech.net>
28  * \author Naveen Albert <asterisk@phreaknet.org>
29  *
30  * \ingroup functions
31  */
32 
33 /*** MODULEINFO
34  <support_level>core</support_level>
35  ***/
36 
37 #include "asterisk.h"
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/conversions.h"
46 #include "asterisk/app.h"
47 #include "asterisk/config.h"
48 #include "asterisk/test.h"
49 
50 /*** DOCUMENTATION
51  <function name="MATH" language="en_US">
52  <synopsis>
53  Performs Mathematical Functions.
54  </synopsis>
55  <syntax>
56  <parameter name="expression" required="true">
57  <para>Is of the form:
58  <replaceable>number1</replaceable><replaceable>op</replaceable><replaceable>number2</replaceable>
59  where the possible values for <replaceable>op</replaceable>
60  are:</para>
61  <para>+,-,/,*,%,&lt;&lt;,&gt;&gt;,^,AND,OR,XOR,&lt;,&gt;,&lt;=,&gt;=,== (and behave as their C equivalents)</para>
62  </parameter>
63  <parameter name="type">
64  <para>Wanted type of result:</para>
65  <para>f, float - float(default)</para>
66  <para>i, int - integer</para>
67  <para>h, hex - hex</para>
68  <para>c, char - char</para>
69  </parameter>
70  </syntax>
71  <description>
72  <para>Performs mathematical functions based on two parameters and an operator. The returned
73  value type is <replaceable>type</replaceable></para>
74  <example title="Sets var i to 11">
75  same => n,Set(i=${MATH(123%16,int)})
76  </example>
77  </description>
78  </function>
79  <function name="INC" language="en_US">
80  <synopsis>
81  Increments the value of a variable, while returning the updated value to the dialplan
82  </synopsis>
83  <syntax>
84  <parameter name="variable" required="true">
85  <para>
86  The variable name to be manipulated, without the braces.
87  </para>
88  </parameter>
89  </syntax>
90  <description>
91  <para>Increments the value of a variable, while returning the updated value to the dialplan</para>
92  <para>Example: INC(MyVAR) - Increments MyVar</para>
93  <para>Note: INC(${MyVAR}) - Is wrong, as INC expects the variable name, not its value</para>
94  </description>
95  </function>
96  <function name="DEC" language="en_US">
97  <synopsis>
98  Decrements the value of a variable, while returning the updated value to the dialplan
99  </synopsis>
100  <syntax>
101  <parameter name="variable" required="true">
102  <para>
103  The variable name to be manipulated, without the braces.
104  </para>
105  </parameter>
106  </syntax>
107  <description>
108  <para>Decrements the value of a variable, while returning the updated value to the dialplan</para>
109  <example title="Decrements MyVAR">
110  same => n,NoOp(${DEC(MyVAR)})
111  </example>
112  <note><para>DEC(${MyVAR}) is wrong, as DEC expects the variable name, not its value</para></note>
113  </description>
114  </function>
115  <function name="MIN" language="en_US">
116  <since>
117  <version>16.19.0</version>
118  <version>18.5.0</version>
119  <version>19.0.0</version>
120  </since>
121  <synopsis>
122  Returns the minimum of two numbers.
123  </synopsis>
124  <syntax>
125  <parameter name="num1" />
126  <parameter name="num2" />
127  </syntax>
128  <description>
129  <para>Returns the minimum of two numbers <replaceable>num1</replaceable> and <replaceable>num2</replaceable>.</para>
130  <example title="Sets the min variable equal to 4">
131  same => n,Set(min=${MIN(7,4)})
132  </example>
133  </description>
134  </function>
135  <function name="MAX" language="en_US">
136  <since>
137  <version>16.19.0</version>
138  <version>18.5.0</version>
139  <version>19.0.0</version>
140  </since>
141  <synopsis>
142  Returns the maximum of two numbers.
143  </synopsis>
144  <syntax>
145  <parameter name="num1" />
146  <parameter name="num2" />
147  </syntax>
148  <description>
149  <para>Returns the maximum of two numbers <replaceable>num1</replaceable> and <replaceable>num2</replaceable>.</para>
150  <example title="Sets the max variable equal to 13">
151  same => n,Set(max=${MAX(4,7)})
152  </example>
153  </description>
154  </function>
155  <function name="ABS" language="en_US">
156  <since>
157  <version>16.19.0</version>
158  <version>18.5.0</version>
159  <version>19.0.0</version>
160  </since>
161  <synopsis>
162  Returns absolute value of a number.
163  </synopsis>
164  <syntax>
165  <parameter name="num" />
166  </syntax>
167  <description>
168  <para>Returns the absolute value of a number <replaceable>num</replaceable>.</para>
169  <example title="Sets the absval variable equal to 13">
170  same => n,Set(absval=${ABS(-13)})
171  </example>
172  </description>
173  </function>
174  ***/
175 
176 enum TypeOfFunctions {
177  ADDFUNCTION,
178  DIVIDEFUNCTION,
179  MULTIPLYFUNCTION,
180  SUBTRACTFUNCTION,
181  MODULUSFUNCTION,
182  POWFUNCTION,
183  SHLEFTFUNCTION,
184  SHRIGHTFUNCTION,
185  BITWISEANDFUNCTION,
186  BITWISEXORFUNCTION,
187  BITWISEORFUNCTION,
188  GTFUNCTION,
189  LTFUNCTION,
190  GTEFUNCTION,
191  LTEFUNCTION,
192  EQFUNCTION
193 };
194 
195 enum TypeOfResult {
196  FLOAT_RESULT,
197  INT_RESULT,
198  HEX_RESULT,
199  CHAR_RESULT
200 };
201 
202 static int math(struct ast_channel *chan, const char *cmd, char *parse,
203  char *buf, size_t len)
204 {
205  double fnum1;
206  double fnum2;
207  double ftmp = 0;
208  char *op;
209  int iaction = -1;
210  int type_of_result = FLOAT_RESULT;
211  char *mvalue1, *mvalue2 = NULL, *mtype_of_result;
212  int negvalue1 = 0;
214  AST_APP_ARG(argv0);
215  AST_APP_ARG(argv1);
216  );
217 
218  if (ast_strlen_zero(parse)) {
219  ast_log(LOG_WARNING, "Syntax: MATH(<number1><op><number 2>[,<type_of_result>]) - missing argument!\n");
220  return -1;
221  }
222 
223  AST_STANDARD_APP_ARGS(args, parse);
224 
225  if (args.argc < 1) {
226  ast_log(LOG_WARNING, "Syntax: MATH(<number1><op><number 2>[,<type_of_result>]) - missing argument!\n");
227  return -1;
228  }
229 
230  mvalue1 = args.argv0;
231 
232  if (mvalue1[0] == '-') {
233  negvalue1 = 1;
234  mvalue1++;
235  }
236 
237  if ((op = strchr(mvalue1, '*'))) {
238  iaction = MULTIPLYFUNCTION;
239  *op = '\0';
240  } else if ((op = strchr(mvalue1, '/'))) {
241  iaction = DIVIDEFUNCTION;
242  *op = '\0';
243  } else if ((op = strchr(mvalue1, '%'))) {
244  iaction = MODULUSFUNCTION;
245  *op = '\0';
246  } else if ((op = strchr(mvalue1, '^'))) {
247  iaction = POWFUNCTION;
248  *op = '\0';
249  } else if ((op = strstr(mvalue1, "AND"))) {
250  iaction = BITWISEANDFUNCTION;
251  *op = '\0';
252  op += 2;
253  } else if ((op = strstr(mvalue1, "XOR"))) {
254  iaction = BITWISEXORFUNCTION;
255  *op = '\0';
256  op += 2;
257  } else if ((op = strstr(mvalue1, "OR"))) {
258  iaction = BITWISEORFUNCTION;
259  *op = '\0';
260  ++op;
261  } else if ((op = strchr(mvalue1, '>'))) {
262  iaction = GTFUNCTION;
263  *op = '\0';
264  if (*(op + 1) == '=') {
265  iaction = GTEFUNCTION;
266  ++op;
267  } else if (*(op + 1) == '>') {
268  iaction = SHRIGHTFUNCTION;
269  ++op;
270  }
271  } else if ((op = strchr(mvalue1, '<'))) {
272  iaction = LTFUNCTION;
273  *op = '\0';
274  if (*(op + 1) == '=') {
275  iaction = LTEFUNCTION;
276  ++op;
277  } else if (*(op + 1) == '<') {
278  iaction = SHLEFTFUNCTION;
279  ++op;
280  }
281  } else if ((op = strchr(mvalue1, '='))) {
282  *op = '\0';
283  if (*(op + 1) == '=') {
284  iaction = EQFUNCTION;
285  ++op;
286  } else
287  op = NULL;
288  } else if ((op = strchr(mvalue1, '+'))) {
289  iaction = ADDFUNCTION;
290  *op = '\0';
291  } else if ((op = strchr(mvalue1, '-'))) { /* subtraction MUST always be last, in case we have a negative second number */
292  iaction = SUBTRACTFUNCTION;
293  *op = '\0';
294  }
295 
296  if (op)
297  mvalue2 = op + 1;
298 
299  /* detect wanted type of result */
300  mtype_of_result = args.argv1;
301  if (mtype_of_result) {
302  if (!strcasecmp(mtype_of_result, "float")
303  || !strcasecmp(mtype_of_result, "f"))
304  type_of_result = FLOAT_RESULT;
305  else if (!strcasecmp(mtype_of_result, "int")
306  || !strcasecmp(mtype_of_result, "i"))
307  type_of_result = INT_RESULT;
308  else if (!strcasecmp(mtype_of_result, "hex")
309  || !strcasecmp(mtype_of_result, "h"))
310  type_of_result = HEX_RESULT;
311  else if (!strcasecmp(mtype_of_result, "char")
312  || !strcasecmp(mtype_of_result, "c"))
313  type_of_result = CHAR_RESULT;
314  else {
315  ast_log(LOG_WARNING, "Unknown type of result requested '%s'.\n",
316  mtype_of_result);
317  return -1;
318  }
319  }
320 
321  if (!mvalue2) {
322  ast_log(LOG_WARNING,
323  "Supply all the parameters - just this once, please\n");
324  return -1;
325  }
326 
327  if (sscanf(mvalue1, "%30lf", &fnum1) != 1) {
328  ast_log(LOG_WARNING, "'%s' is not a valid number\n", mvalue1);
329  return -1;
330  }
331 
332  if (sscanf(mvalue2, "%30lf", &fnum2) != 1) {
333  ast_log(LOG_WARNING, "'%s' is not a valid number\n", mvalue2);
334  return -1;
335  }
336 
337  if (negvalue1)
338  fnum1 = 0 - fnum1;
339 
340  switch (iaction) {
341  case ADDFUNCTION:
342  ftmp = fnum1 + fnum2;
343  break;
344  case DIVIDEFUNCTION:
345  if (fnum2 <= 0)
346  ftmp = 0; /* can't do a divide by 0 */
347  else
348  ftmp = (fnum1 / fnum2);
349  break;
350  case MULTIPLYFUNCTION:
351  ftmp = (fnum1 * fnum2);
352  break;
353  case SUBTRACTFUNCTION:
354  ftmp = (fnum1 - fnum2);
355  break;
356  case MODULUSFUNCTION:
357  {
358  int inum1 = fnum1;
359  int inum2 = fnum2;
360 
361  if (inum2 == 0) {
362  ftmp = 0;
363  } else {
364  ftmp = (inum1 % inum2);
365  }
366 
367  break;
368  }
369  case POWFUNCTION:
370  ftmp = pow(fnum1, fnum2);
371  break;
372  case SHLEFTFUNCTION:
373  {
374  int inum1 = fnum1;
375  int inum2 = fnum2;
376 
377  ftmp = (inum1 << inum2);
378  break;
379  }
380  case SHRIGHTFUNCTION:
381  {
382  int inum1 = fnum1;
383  int inum2 = fnum2;
384 
385  ftmp = (inum1 >> inum2);
386  break;
387  }
388  case BITWISEANDFUNCTION:
389  {
390  int inum1 = fnum1;
391  int inum2 = fnum2;
392  ftmp = (inum1 & inum2);
393  break;
394  }
395  case BITWISEXORFUNCTION:
396  {
397  int inum1 = fnum1;
398  int inum2 = fnum2;
399  ftmp = (inum1 ^ inum2);
400  break;
401  }
402  case BITWISEORFUNCTION:
403  {
404  int inum1 = fnum1;
405  int inum2 = fnum2;
406  ftmp = (inum1 | inum2);
407  break;
408  }
409  case GTFUNCTION:
410  ast_copy_string(buf, (fnum1 > fnum2) ? "TRUE" : "FALSE", len);
411  break;
412  case LTFUNCTION:
413  ast_copy_string(buf, (fnum1 < fnum2) ? "TRUE" : "FALSE", len);
414  break;
415  case GTEFUNCTION:
416  ast_copy_string(buf, (fnum1 >= fnum2) ? "TRUE" : "FALSE", len);
417  break;
418  case LTEFUNCTION:
419  ast_copy_string(buf, (fnum1 <= fnum2) ? "TRUE" : "FALSE", len);
420  break;
421  case EQFUNCTION:
422  ast_copy_string(buf, (fnum1 == fnum2) ? "TRUE" : "FALSE", len);
423  break;
424  default:
425  ast_log(LOG_WARNING,
426  "Something happened that neither of us should be proud of %d\n",
427  iaction);
428  return -1;
429  }
430 
431  if (iaction < GTFUNCTION || iaction > EQFUNCTION) {
432  if (type_of_result == FLOAT_RESULT)
433  snprintf(buf, len, "%f", ftmp);
434  else if (type_of_result == INT_RESULT)
435  snprintf(buf, len, "%i", (int) ftmp);
436  else if (type_of_result == HEX_RESULT)
437  snprintf(buf, len, "%x", (unsigned int) ftmp);
438  else if (type_of_result == CHAR_RESULT)
439  snprintf(buf, len, "%c", (unsigned char) ftmp);
440  }
441 
442  return 0;
443 }
444 
445 static int crement_function_read(struct ast_channel *chan, const char *cmd,
446  char *data, char *buf, size_t len)
447 {
448  int ret = -1;
449  int int_value = 0;
450  int modify_orig = 0;
451  const char *var;
452  char endchar = 0, returnvar[12]; /* If you need a variable longer than 11 digits - something is way wrong */
453 
454  if (ast_strlen_zero(data)) {
455  ast_log(LOG_WARNING, "Syntax: %s(<data>) - missing argument!\n", cmd);
456  return -1;
457  }
458 
459  if (!chan) {
460  ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
461  return -1;
462  }
463 
464  ast_channel_lock(chan);
465 
466  if (!(var = pbx_builtin_getvar_helper(chan, data))) {
467  ast_log(LOG_NOTICE, "Failed to obtain variable %s, bailing out\n", data);
468  ast_channel_unlock(chan);
469  return -1;
470  }
471 
472  if (ast_strlen_zero(var)) {
473  ast_log(LOG_NOTICE, "Variable %s doesn't exist - are you sure you wrote it correctly?\n", data);
474  ast_channel_unlock(chan);
475  return -1;
476  }
477 
478  if (sscanf(var, "%30d%1c", &int_value, &endchar) == 0 || endchar != 0) {
479  ast_log(LOG_NOTICE, "The content of ${%s} is not a numeric value - bailing out!\n", data);
480  ast_channel_unlock(chan);
481  return -1;
482  }
483 
484  /* now we'll actually do something useful */
485  if (!strcasecmp(cmd, "INC")) { /* Increment variable */
486  int_value++;
487  modify_orig = 1;
488  } else if (!strcasecmp(cmd, "DEC")) { /* Decrement variable */
489  int_value--;
490  modify_orig = 1;
491  }
492 
493  if (snprintf(returnvar, sizeof(returnvar), "%d", int_value) > 0) {
494  pbx_builtin_setvar_helper(chan, data, returnvar);
495  if (modify_orig) {
496  ast_copy_string(buf, returnvar, len);
497  }
498  ret = 0;
499  } else {
500  pbx_builtin_setvar_helper(chan, data, "0");
501  if (modify_orig) {
502  ast_copy_string(buf, "0", len);
503  }
504  ast_log(LOG_NOTICE, "Variable %s refused to be %sREMENTED, setting value to 0", data, cmd);
505  ret = 0;
506  }
507 
508  ast_channel_unlock(chan);
509 
510  return ret;
511 }
512 
513 static int acf_min_exec(struct ast_channel *chan, const char *cmd,
514  char *parse, char *buffer, size_t buflen)
515 {
516  double num1, num2, response_num = 0;
518  AST_APP_ARG(num1);
519  AST_APP_ARG(num2);
520  );
521 
522  AST_STANDARD_APP_ARGS(args, parse);
523 
524  if (ast_strlen_zero(args.num1) && ast_strlen_zero(args.num2)) {
525  ast_log(LOG_ERROR, "Missing argument for number(s).");
526  return -1;
527  }
528 
529  if (ast_strlen_zero(args.num1)) {
530  response_num = -1; /* couldn't read num1 successfully */
531  } else if (sscanf(args.num1, "%30lf", &num1) != 1) {
532  ast_log(LOG_WARNING, "'%s' is not a valid number\n", args.num1);
533  return -1;
534  }
535 
536  if (ast_strlen_zero(args.num2)) {
537  num2 = num1; /* num1 must be a valid integer here */
538  } else if (sscanf(args.num2, "%30lf", &num2) != 1) {
539  ast_log(LOG_WARNING, "'%s' is not a valid number\n", args.num2);
540  return -1;
541  }
542 
543  if (response_num == -1) { /* could only read num2 */
544  response_num = num2;
545  } else {
546  response_num = (num1 > num2) ? num2 : num1;
547  }
548 
549  ast_debug(1, "%f is the minimum of [%f,%f]\n", response_num, num1, num2);
550  if ((int) response_num == response_num) {
551  snprintf(buffer, buflen, "%d", (int) response_num);
552  } else {
553  snprintf(buffer, buflen, "%f", response_num);
554  }
555 
556  return 0;
557 }
558 
559 static int acf_max_exec(struct ast_channel *chan, const char *cmd,
560  char *parse, char *buffer, size_t buflen)
561 {
562  double num1, num2, response_num = 0;
564  AST_APP_ARG(num1);
565  AST_APP_ARG(num2);
566  );
567 
568  AST_STANDARD_APP_ARGS(args, parse);
569 
570  if (ast_strlen_zero(args.num1) && ast_strlen_zero(args.num2)) {
571  ast_log(LOG_ERROR, "Missing argument for number(s).");
572  return -1;
573  }
574 
575  if (ast_strlen_zero(args.num1)) {
576  response_num = -1; /* couldn't read num1 successfully */
577  } else if (sscanf(args.num1, "%30lf", &num1) != 1) {
578  ast_log(LOG_WARNING, "'%s' is not a valid number\n", args.num1);
579  return -1;
580  }
581 
582  if (ast_strlen_zero(args.num2)) {
583  num2 = num1; /* num1 must be a valid integer here */
584  } else if (sscanf(args.num2, "%30lf", &num2) != 1) {
585  ast_log(LOG_WARNING, "'%s' is not a valid number\n", args.num2);
586  return -1;
587  }
588 
589  if (response_num == -1) { /* could only read num2 */
590  response_num = num2;
591  } else {
592  response_num = (num1 < num2) ? num2 : num1;
593  }
594 
595  ast_debug(1, "%f is the maximum of [%f,%f]\n", response_num, num1, num2);
596  if ((int) response_num == response_num) {
597  snprintf(buffer, buflen, "%d", (int) response_num);
598  } else {
599  snprintf(buffer, buflen, "%f", response_num);
600  }
601 
602  return 0;
603 }
604 
605 static int acf_abs_exec(struct ast_channel *chan, const char *cmd,
606  char *parse, char *buffer, size_t buflen)
607 {
608  double num1, response_num;
610  AST_APP_ARG(num1);
611  );
612 
613  AST_STANDARD_APP_ARGS(args, parse);
614 
615  if (ast_strlen_zero(args.num1) || sscanf(args.num1, "%30lf", &num1) != 1) {
616  ast_log(LOG_WARNING, "Bad or missing argument for number: %s", args.num1);
617  return -1;
618  }
619 
620  response_num = fabs(num1);
621  ast_debug(1, "%f is the absolute value of %f\n", response_num, num1);
622  if ((int) response_num == response_num) {
623  snprintf(buffer, buflen, "%d", (int) response_num);
624  } else {
625  snprintf(buffer, buflen, "%f", response_num);
626  }
627 
628  return 0;
629 }
630 
631 static struct ast_custom_function math_function = {
632  .name = "MATH",
633  .read = math
634 };
635 
636 static struct ast_custom_function increment_function = {
637  .name = "INC",
638  .read = crement_function_read,
639 };
640 
641 static struct ast_custom_function decrement_function = {
642  .name = "DEC",
643  .read = crement_function_read,
644 };
645 
646 static struct ast_custom_function acf_min = {
647  .name = "MIN",
648  .read = acf_min_exec,
649  .read_max = 12,
650 };
651 
652 static struct ast_custom_function acf_max = {
653  .name = "MAX",
654  .read = acf_max_exec,
655  .read_max = 12,
656 };
657 
658 static struct ast_custom_function acf_abs = {
659  .name = "ABS",
660  .read = acf_abs_exec,
661  .read_max = 12,
662 };
663 
664 #ifdef TEST_FRAMEWORK
665 AST_TEST_DEFINE(test_MATH_function)
666 {
667  enum ast_test_result_state res = AST_TEST_PASS;
668  struct ast_str *expr, *result;
669 
670  switch (cmd) {
671  case TEST_INIT:
672  info->name = "test_MATH_function";
673  info->category = "/main/pbx/";
674  info->summary = "Test MATH function substitution";
675  info->description =
676  "Executes a series of variable substitutions using the MATH function and ensures that the expected results are received.";
677  return AST_TEST_NOT_RUN;
678  case TEST_EXECUTE:
679  break;
680  }
681 
682  ast_test_status_update(test, "Testing MATH() substitution ...\n");
683 
684  if (!(expr = ast_str_create(16))) {
685  return AST_TEST_FAIL;
686  }
687  if (!(result = ast_str_create(16))) {
688  ast_free(expr);
689  return AST_TEST_FAIL;
690  }
691 
692  ast_str_set(&expr, 0, "${MATH(170 AND 63,i)}");
693  ast_str_substitute_variables(&result, 0, NULL, ast_str_buffer(expr));
694  if (strcmp(ast_str_buffer(result), "42") != 0) {
695  ast_test_status_update(test, "Expected result '42' not returned! ('%s')\n",
696  ast_str_buffer(result));
697  res = AST_TEST_FAIL;
698  }
699 
700  ast_str_set(&expr, 0, "${MATH(170AND63,i)}");
701  ast_str_substitute_variables(&result, 0, NULL, ast_str_buffer(expr));
702  if (strcmp(ast_str_buffer(result), "42") != 0) {
703  ast_test_status_update(test, "Expected result '42' not returned! ('%s')\n",
704  ast_str_buffer(result));
705  res = AST_TEST_FAIL;
706  }
707 
708  ast_free(expr);
709  ast_free(result);
710 
711  return res;
712 }
713 #endif
714 
715 static int unload_module(void)
716 {
717  int res = 0;
718 
719  res |= ast_custom_function_unregister(&math_function);
720  res |= ast_custom_function_unregister(&increment_function);
721  res |= ast_custom_function_unregister(&decrement_function);
722  res |= ast_custom_function_unregister(&acf_min);
723  res |= ast_custom_function_unregister(&acf_max);
724  res |= ast_custom_function_unregister(&acf_abs);
725  AST_TEST_UNREGISTER(test_MATH_function);
726 
727  return res;
728 }
729 
730 static int load_module(void)
731 {
732  int res = 0;
733 
734  res |= ast_custom_function_register(&math_function);
735  res |= ast_custom_function_register(&increment_function);
736  res |= ast_custom_function_register(&decrement_function);
737  res |= ast_custom_function_register(&acf_min);
738  res |= ast_custom_function_register(&acf_max);
739  res |= ast_custom_function_register(&acf_abs);
740  AST_TEST_REGISTER(test_MATH_function);
741 
742  return res;
743 }
744 
745 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Mathematical dialplan function");
const char * name
Definition: pbx.h:119
Main Channel structure associated with a channel.
Asterisk main include file. File version handling, generic pbx functions.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
void ast_str_substitute_variables(struct ast_str **buf, ssize_t maxlen, struct ast_channel *chan, const char *templ)
Test Framework API.
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
Utility functions.
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:1113
Configuration File Parser.
General Asterisk PBX channel definitions.
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
Conversion utility functions.
#define ast_debug(level,...)
Log a DEBUG message.
Core PBX routines and definitions.
Support for dynamic strings.
Definition: strings.h:623
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...
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
#define AST_TEST_DEFINE(hdr)
Definition: test.h:126
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1558
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659
#define AST_APP_ARG(name)
Define an application argument.