func_math.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2004 - 2006, Andy Powell
00005  *
00006  * Updated by Mark Spencer <markster@digium.com>
00007  * Updated by Nir Simionovich <nirs@greenfieldtech.net>
00008  *
00009  * See http://www.asterisk.org for more information about
00010  * the Asterisk project. Please do not directly contact
00011  * any of the maintainers of this project for assistance;
00012  * the project provides a web site, mailing lists and IRC
00013  * channels for your use.
00014  *
00015  * This program is free software, distributed under the terms of
00016  * the GNU General Public License Version 2. See the LICENSE file
00017  * at the top of the source tree.
00018  */
00019 
00020 /*! \file
00021  *
00022  * \brief Math related dialplan function
00023  *
00024  * \author Andy Powell
00025  * \author Mark Spencer <markster@digium.com>
00026  * \author Nir Simionovich <nirs@greenfieldtech.net>
00027  *
00028  * \ingroup functions
00029  */
00030 
00031 /*** MODULEINFO
00032    <support_level>core</support_level>
00033  ***/
00034 
00035 #include "asterisk.h"
00036 
00037 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 434192 $")
00038 
00039 #include <math.h>
00040 
00041 #include "asterisk/module.h"
00042 #include "asterisk/channel.h"
00043 #include "asterisk/pbx.h"
00044 #include "asterisk/utils.h"
00045 #include "asterisk/app.h"
00046 #include "asterisk/config.h"
00047 #include "asterisk/test.h"
00048 
00049 /*** DOCUMENTATION
00050    <function name="MATH" language="en_US">
00051       <synopsis>
00052          Performs Mathematical Functions.
00053       </synopsis>
00054       <syntax>
00055          <parameter name="expression" required="true">
00056             <para>Is of the form:
00057             <replaceable>number1</replaceable><replaceable>op</replaceable><replaceable>number2</replaceable>
00058             where the possible values for <replaceable>op</replaceable>
00059             are:</para>
00060             <para>+,-,/,*,%,&lt;&lt;,&gt;&gt;,^,AND,OR,XOR,&lt;,&gt;,&lt;=,&gt;=,== (and behave as their C equivalents)</para>
00061          </parameter>
00062          <parameter name="type">
00063             <para>Wanted type of result:</para>
00064             <para>f, float - float(default)</para>
00065             <para>i, int - integer</para>
00066             <para>h, hex - hex</para>
00067             <para>c, char - char</para>
00068          </parameter>
00069       </syntax>
00070       <description>
00071          <para>Performs mathematical functions based on two parameters and an operator.  The returned
00072          value type is <replaceable>type</replaceable></para>
00073          <para>Example: Set(i=${MATH(123%16,int)}) - sets var i=11</para>
00074       </description>
00075    </function>
00076    <function name="INC" language="en_US">
00077       <synopsis>
00078          Increments the value of a variable, while returning the updated value to the dialplan
00079       </synopsis>
00080       <syntax>
00081          <parameter name="variable" required="true">
00082             <para>
00083             The variable name to be manipulated, without the braces.
00084             </para>
00085          </parameter>
00086       </syntax>
00087       <description>
00088          <para>Increments the value of a variable, while returning the updated value to the dialplan</para>
00089          <para>Example: INC(MyVAR) - Increments MyVar</para>
00090          <para>Note: INC(${MyVAR}) - Is wrong, as INC expects the variable name, not its value</para>
00091       </description>
00092    </function>
00093    <function name="DEC" language="en_US">
00094       <synopsis>
00095          Decrements the value of a variable, while returning the updated value to the dialplan
00096       </synopsis>
00097       <syntax>
00098          <parameter name="variable" required="true">
00099             <para>
00100             The variable name to be manipulated, without the braces.
00101             </para>
00102          </parameter>
00103       </syntax>
00104       <description>
00105          <para>Decrements the value of a variable, while returning the updated value to the dialplan</para>
00106          <para>Example: DEC(MyVAR) - Decrements MyVar</para>
00107          <para>Note: DEC(${MyVAR}) - Is wrong, as DEC expects the variable name, not its value</para>
00108       </description>
00109    </function>
00110  ***/
00111 
00112 enum TypeOfFunctions {
00113    ADDFUNCTION,
00114    DIVIDEFUNCTION,
00115    MULTIPLYFUNCTION,
00116    SUBTRACTFUNCTION,
00117    MODULUSFUNCTION,
00118    POWFUNCTION,
00119    SHLEFTFUNCTION,
00120    SHRIGHTFUNCTION,
00121    BITWISEANDFUNCTION,
00122    BITWISEXORFUNCTION,
00123    BITWISEORFUNCTION,
00124    GTFUNCTION,
00125    LTFUNCTION,
00126    GTEFUNCTION,
00127    LTEFUNCTION,
00128    EQFUNCTION
00129 };
00130 
00131 enum TypeOfResult {
00132    FLOAT_RESULT,
00133    INT_RESULT,
00134    HEX_RESULT,
00135    CHAR_RESULT
00136 };
00137 
00138 static int math(struct ast_channel *chan, const char *cmd, char *parse,
00139       char *buf, size_t len)
00140 {
00141    double fnum1;
00142    double fnum2;
00143    double ftmp = 0;
00144    char *op;
00145    int iaction = -1;
00146    int type_of_result = FLOAT_RESULT;
00147    char *mvalue1, *mvalue2 = NULL, *mtype_of_result;
00148    int negvalue1 = 0;
00149    AST_DECLARE_APP_ARGS(args,
00150               AST_APP_ARG(argv0);
00151               AST_APP_ARG(argv1);
00152    );
00153 
00154    if (ast_strlen_zero(parse)) {
00155       ast_log(LOG_WARNING, "Syntax: MATH(<number1><op><number 2>[,<type_of_result>]) - missing argument!\n");
00156       return -1;
00157    }
00158 
00159    AST_STANDARD_APP_ARGS(args, parse);
00160 
00161    if (args.argc < 1) {
00162       ast_log(LOG_WARNING, "Syntax: MATH(<number1><op><number 2>[,<type_of_result>]) - missing argument!\n");
00163       return -1;
00164    }
00165 
00166    mvalue1 = args.argv0;
00167 
00168    if (mvalue1[0] == '-') {
00169       negvalue1 = 1;
00170       mvalue1++;
00171    }
00172 
00173    if ((op = strchr(mvalue1, '*'))) {
00174       iaction = MULTIPLYFUNCTION;
00175       *op = '\0';
00176    } else if ((op = strchr(mvalue1, '/'))) {
00177       iaction = DIVIDEFUNCTION;
00178       *op = '\0';
00179    } else if ((op = strchr(mvalue1, '%'))) {
00180       iaction = MODULUSFUNCTION;
00181       *op = '\0';
00182    } else if ((op = strchr(mvalue1, '^'))) {
00183       iaction = POWFUNCTION;
00184       *op = '\0';
00185    } else if ((op = strstr(mvalue1, "AND"))) {
00186       iaction = BITWISEANDFUNCTION;
00187       *op = '\0';
00188       op += 2;
00189    } else if ((op = strstr(mvalue1, "XOR"))) {
00190       iaction = BITWISEXORFUNCTION;
00191       *op = '\0';
00192       op += 2;
00193    } else if ((op = strstr(mvalue1, "OR"))) {
00194       iaction = BITWISEORFUNCTION;
00195       *op = '\0';
00196       ++op;
00197    } else if ((op = strchr(mvalue1, '>'))) {
00198       iaction = GTFUNCTION;
00199       *op = '\0';
00200       if (*(op + 1) == '=') {
00201          iaction = GTEFUNCTION;
00202          ++op;
00203       } else if (*(op + 1) == '>') {
00204          iaction = SHRIGHTFUNCTION;
00205          ++op;
00206       }
00207    } else if ((op = strchr(mvalue1, '<'))) {
00208       iaction = LTFUNCTION;
00209       *op = '\0';
00210       if (*(op + 1) == '=') {
00211          iaction = LTEFUNCTION;
00212          ++op;
00213       } else if (*(op + 1) == '<') {
00214          iaction = SHLEFTFUNCTION;
00215          ++op;
00216       }
00217    } else if ((op = strchr(mvalue1, '='))) {
00218       *op = '\0';
00219       if (*(op + 1) == '=') {
00220          iaction = EQFUNCTION;
00221          ++op;
00222       } else
00223          op = NULL;
00224    } else if ((op = strchr(mvalue1, '+'))) {
00225       iaction = ADDFUNCTION;
00226       *op = '\0';
00227    } else if ((op = strchr(mvalue1, '-'))) { /* subtraction MUST always be last, in case we have a negative second number */
00228       iaction = SUBTRACTFUNCTION;
00229       *op = '\0';
00230    }
00231 
00232    if (op)
00233       mvalue2 = op + 1;
00234 
00235    /* detect wanted type of result */
00236    mtype_of_result = args.argv1;
00237    if (mtype_of_result) {
00238       if (!strcasecmp(mtype_of_result, "float")
00239           || !strcasecmp(mtype_of_result, "f"))
00240          type_of_result = FLOAT_RESULT;
00241       else if (!strcasecmp(mtype_of_result, "int")
00242           || !strcasecmp(mtype_of_result, "i"))
00243          type_of_result = INT_RESULT;
00244       else if (!strcasecmp(mtype_of_result, "hex")
00245           || !strcasecmp(mtype_of_result, "h"))
00246          type_of_result = HEX_RESULT;
00247       else if (!strcasecmp(mtype_of_result, "char")
00248           || !strcasecmp(mtype_of_result, "c"))
00249          type_of_result = CHAR_RESULT;
00250       else {
00251          ast_log(LOG_WARNING, "Unknown type of result requested '%s'.\n",
00252                mtype_of_result);
00253          return -1;
00254       }
00255    }
00256 
00257    if (!mvalue2) {
00258       ast_log(LOG_WARNING,
00259             "Supply all the parameters - just this once, please\n");
00260       return -1;
00261    }
00262 
00263    if (sscanf(mvalue1, "%30lf", &fnum1) != 1) {
00264       ast_log(LOG_WARNING, "'%s' is not a valid number\n", mvalue1);
00265       return -1;
00266    }
00267 
00268    if (sscanf(mvalue2, "%30lf", &fnum2) != 1) {
00269       ast_log(LOG_WARNING, "'%s' is not a valid number\n", mvalue2);
00270       return -1;
00271    }
00272 
00273    if (negvalue1)
00274       fnum1 = 0 - fnum1;
00275 
00276    switch (iaction) {
00277    case ADDFUNCTION:
00278       ftmp = fnum1 + fnum2;
00279       break;
00280    case DIVIDEFUNCTION:
00281       if (fnum2 <= 0)
00282          ftmp = 0;         /* can't do a divide by 0 */
00283       else
00284          ftmp = (fnum1 / fnum2);
00285       break;
00286    case MULTIPLYFUNCTION:
00287       ftmp = (fnum1 * fnum2);
00288       break;
00289    case SUBTRACTFUNCTION:
00290       ftmp = (fnum1 - fnum2);
00291       break;
00292    case MODULUSFUNCTION:
00293       {
00294          int inum1 = fnum1;
00295          int inum2 = fnum2;
00296 
00297          if (inum2 == 0) {
00298             ftmp = 0;
00299          } else {
00300             ftmp = (inum1 % inum2);
00301          }
00302 
00303          break;
00304       }
00305    case POWFUNCTION:
00306       ftmp = pow(fnum1, fnum2);
00307       break;
00308    case SHLEFTFUNCTION:
00309       {
00310          int inum1 = fnum1;
00311          int inum2 = fnum2;
00312 
00313          ftmp = (inum1 << inum2);
00314          break;
00315       }
00316    case SHRIGHTFUNCTION:
00317       {
00318          int inum1 = fnum1;
00319          int inum2 = fnum2;
00320 
00321          ftmp = (inum1 >> inum2);
00322          break;
00323       }
00324    case BITWISEANDFUNCTION:
00325       {
00326          int inum1 = fnum1;
00327          int inum2 = fnum2;
00328          ftmp = (inum1 & inum2);
00329          break;
00330       }
00331    case BITWISEXORFUNCTION:
00332       {
00333          int inum1 = fnum1;
00334          int inum2 = fnum2;
00335          ftmp = (inum1 ^ inum2);
00336          break;
00337       }
00338    case BITWISEORFUNCTION:
00339       {
00340          int inum1 = fnum1;
00341          int inum2 = fnum2;
00342          ftmp = (inum1 | inum2);
00343          break;
00344       }
00345    case GTFUNCTION:
00346       ast_copy_string(buf, (fnum1 > fnum2) ? "TRUE" : "FALSE", len);
00347       break;
00348    case LTFUNCTION:
00349       ast_copy_string(buf, (fnum1 < fnum2) ? "TRUE" : "FALSE", len);
00350       break;
00351    case GTEFUNCTION:
00352       ast_copy_string(buf, (fnum1 >= fnum2) ? "TRUE" : "FALSE", len);
00353       break;
00354    case LTEFUNCTION:
00355       ast_copy_string(buf, (fnum1 <= fnum2) ? "TRUE" : "FALSE", len);
00356       break;
00357    case EQFUNCTION:
00358       ast_copy_string(buf, (fnum1 == fnum2) ? "TRUE" : "FALSE", len);
00359       break;
00360    default:
00361       ast_log(LOG_WARNING,
00362             "Something happened that neither of us should be proud of %d\n",
00363             iaction);
00364       return -1;
00365    }
00366 
00367    if (iaction < GTFUNCTION || iaction > EQFUNCTION) {
00368       if (type_of_result == FLOAT_RESULT)
00369          snprintf(buf, len, "%f", ftmp);
00370       else if (type_of_result == INT_RESULT)
00371          snprintf(buf, len, "%i", (int) ftmp);
00372       else if (type_of_result == HEX_RESULT)
00373          snprintf(buf, len, "%x", (unsigned int) ftmp);
00374       else if (type_of_result == CHAR_RESULT)
00375          snprintf(buf, len, "%c", (unsigned char) ftmp);
00376    }
00377 
00378    return 0;
00379 }
00380 
00381 static int crement_function_read(struct ast_channel *chan, const char *cmd,
00382                      char *data, char *buf, size_t len)
00383 {
00384    int ret = -1;
00385    int int_value = 0;
00386    int modify_orig = 0;
00387    const char *var;
00388    char endchar = 0, returnvar[12]; /* If you need a variable longer than 11 digits - something is way wrong */
00389 
00390    if (ast_strlen_zero(data)) {
00391       ast_log(LOG_WARNING, "Syntax: %s(<data>) - missing argument!\n", cmd);
00392       return -1;
00393    }
00394 
00395    if (!chan) {
00396       ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
00397       return -1;
00398    }
00399 
00400    ast_channel_lock(chan);
00401 
00402    if (!(var = pbx_builtin_getvar_helper(chan, data))) {
00403       ast_log(LOG_NOTICE, "Failed to obtain variable %s, bailing out\n", data);
00404       ast_channel_unlock(chan);
00405       return -1;
00406    }
00407 
00408    if (ast_strlen_zero(var)) {
00409       ast_log(LOG_NOTICE, "Variable %s doesn't exist - are you sure you wrote it correctly?\n", data);
00410       ast_channel_unlock(chan);
00411       return -1;
00412    }
00413 
00414    if (sscanf(var, "%30d%1c", &int_value, &endchar) == 0 || endchar != 0) {
00415       ast_log(LOG_NOTICE, "The content of ${%s} is not a numeric value - bailing out!\n", data);
00416       ast_channel_unlock(chan);
00417       return -1;
00418    }
00419 
00420    /* now we'll actually do something useful */
00421    if (!strcasecmp(cmd, "INC")) {              /* Increment variable */
00422       int_value++;
00423       modify_orig = 1;
00424    } else if (!strcasecmp(cmd, "DEC")) {       /* Decrement variable */
00425       int_value--;
00426       modify_orig = 1;
00427    }
00428 
00429    if (snprintf(returnvar, sizeof(returnvar), "%d", int_value) > 0) {
00430       pbx_builtin_setvar_helper(chan, data, returnvar);
00431       if (modify_orig) {
00432          ast_copy_string(buf, returnvar, len);
00433       }
00434       ret = 0;
00435    } else {
00436       pbx_builtin_setvar_helper(chan, data, "0");
00437       if (modify_orig) {
00438          ast_copy_string(buf, "0", len);
00439       }
00440       ast_log(LOG_NOTICE, "Variable %s refused to be %sREMENTED, setting value to 0", data, cmd);
00441       ret = 0;
00442    }
00443 
00444    ast_channel_unlock(chan);
00445 
00446    return ret;
00447 }
00448 
00449 
00450 static struct ast_custom_function math_function = {
00451    .name = "MATH",
00452    .read = math
00453 };
00454 
00455 static struct ast_custom_function increment_function = {
00456    .name = "INC",
00457    .read = crement_function_read,
00458 };
00459 
00460 static struct ast_custom_function decrement_function = {
00461    .name = "DEC",
00462    .read = crement_function_read,
00463 };
00464 
00465 #ifdef TEST_FRAMEWORK
00466 AST_TEST_DEFINE(test_MATH_function)
00467 {
00468    enum ast_test_result_state res = AST_TEST_PASS;
00469    struct ast_str *expr, *result;
00470 
00471    switch (cmd) {
00472    case TEST_INIT:
00473       info->name = "test_MATH_function";
00474       info->category = "/main/pbx/";
00475       info->summary = "Test MATH function substitution";
00476       info->description =
00477          "Executes a series of variable substitutions using the MATH function and ensures that the expected results are received.";
00478       return AST_TEST_NOT_RUN;
00479    case TEST_EXECUTE:
00480       break;
00481    }
00482 
00483    ast_test_status_update(test, "Testing MATH() substitution ...\n");
00484 
00485    if (!(expr = ast_str_create(16))) {
00486       return AST_TEST_FAIL;
00487    }
00488    if (!(result = ast_str_create(16))) {
00489       ast_free(expr);
00490       return AST_TEST_FAIL;
00491    }
00492 
00493    ast_str_set(&expr, 0, "${MATH(170 AND 63,i)}");
00494    ast_str_substitute_variables(&result, 0, NULL, ast_str_buffer(expr));
00495    if (strcmp(ast_str_buffer(result), "42") != 0) {
00496       ast_test_status_update(test, "Expected result '42' not returned! ('%s')\n",
00497             ast_str_buffer(result));
00498       res = AST_TEST_FAIL;
00499    }
00500 
00501    ast_str_set(&expr, 0, "${MATH(170AND63,i)}");
00502    ast_str_substitute_variables(&result, 0, NULL, ast_str_buffer(expr));
00503    if (strcmp(ast_str_buffer(result), "42") != 0) {
00504       ast_test_status_update(test, "Expected result '42' not returned! ('%s')\n",
00505             ast_str_buffer(result));
00506       res = AST_TEST_FAIL;
00507    }
00508 
00509    ast_free(expr);
00510    ast_free(result);
00511 
00512    return res;
00513 }
00514 #endif
00515 
00516 static int unload_module(void)
00517 {
00518    int res = 0;
00519 
00520    res |= ast_custom_function_unregister(&math_function);
00521    res |= ast_custom_function_unregister(&increment_function);
00522    res |= ast_custom_function_unregister(&decrement_function);
00523    AST_TEST_UNREGISTER(test_MATH_function);
00524 
00525    return res;
00526 }
00527 
00528 static int load_module(void)
00529 {
00530    int res = 0;
00531 
00532    res |= ast_custom_function_register(&math_function);
00533    res |= ast_custom_function_register(&increment_function);
00534    res |= ast_custom_function_register(&decrement_function);
00535    AST_TEST_REGISTER(test_MATH_function);
00536 
00537    return res;
00538 }
00539 
00540 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Mathematical dialplan function");

Generated on Thu Apr 16 06:27:35 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6