Tue Aug 20 16:34:34 2013

Asterisk developer's documentation


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: 372628 $")
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    ast_channel_lock(chan);
00396 
00397    if (!(var = pbx_builtin_getvar_helper(chan, data))) {
00398       ast_log(LOG_NOTICE, "Failed to obtain variable %s, bailing out\n", data);
00399       ast_channel_unlock(chan);
00400       return -1;
00401    }
00402 
00403    if (ast_strlen_zero(var)) {
00404       ast_log(LOG_NOTICE, "Variable %s doesn't exist - are you sure you wrote it correctly?\n", data);
00405       ast_channel_unlock(chan);
00406       return -1;
00407    }
00408 
00409    if (sscanf(var, "%30d%1c", &int_value, &endchar) == 0 || endchar != 0) {
00410       ast_log(LOG_NOTICE, "The content of ${%s} is not a numeric value - bailing out!\n", data);
00411       ast_channel_unlock(chan);
00412       return -1;
00413    }
00414 
00415    /* now we'll actually do something useful */
00416    if (!strcasecmp(cmd, "INC")) {              /* Increment variable */
00417       int_value++;
00418       modify_orig = 1;
00419    } else if (!strcasecmp(cmd, "DEC")) {       /* Decrement variable */
00420       int_value--;
00421       modify_orig = 1;
00422    }
00423 
00424    if (snprintf(returnvar, sizeof(returnvar), "%d", int_value) > 0) {
00425       pbx_builtin_setvar_helper(chan, data, returnvar);
00426       if (modify_orig) {
00427          ast_copy_string(buf, returnvar, len);
00428       }
00429       ret = 0;
00430    } else {
00431       pbx_builtin_setvar_helper(chan, data, "0");
00432       if (modify_orig) {
00433          ast_copy_string(buf, "0", len);
00434       }
00435       ast_log(LOG_NOTICE, "Variable %s refused to be %sREMENTED, setting value to 0", data, cmd);
00436       ret = 0;
00437    }
00438 
00439    ast_channel_unlock(chan);
00440 
00441    return ret;
00442 }
00443 
00444 
00445 static struct ast_custom_function math_function = {
00446    .name = "MATH",
00447    .read = math
00448 };
00449 
00450 static struct ast_custom_function increment_function = {
00451    .name = "INC",
00452    .read = crement_function_read,
00453 };
00454 
00455 static struct ast_custom_function decrement_function = {
00456    .name = "DEC",
00457    .read = crement_function_read,
00458 };
00459 
00460 #ifdef TEST_FRAMEWORK
00461 AST_TEST_DEFINE(test_MATH_function)
00462 {
00463    enum ast_test_result_state res = AST_TEST_PASS;
00464    struct ast_str *expr, *result;
00465 
00466    switch (cmd) {
00467    case TEST_INIT:
00468       info->name = "test_MATH_function";
00469       info->category = "/main/pbx/";
00470       info->summary = "Test MATH function substitution";
00471       info->description =
00472          "Executes a series of variable substitutions using the MATH function and ensures that the expected results are received.";
00473       return AST_TEST_NOT_RUN;
00474    case TEST_EXECUTE:
00475       break;
00476    }
00477 
00478    ast_test_status_update(test, "Testing MATH() substitution ...\n");
00479 
00480    if (!(expr = ast_str_create(16)) || !(result = ast_str_create(16))) {
00481       if (expr) {
00482          ast_free(expr);
00483       }
00484       if (result) {
00485          ast_free(result);
00486       }
00487       return AST_TEST_FAIL;
00488    }
00489 
00490    ast_str_set(&expr, 0, "${MATH(170 AND 63,i)}");
00491    ast_str_substitute_variables(&result, 0, NULL, ast_str_buffer(expr));
00492    if (strcmp(ast_str_buffer(result), "42") != 0) {
00493       ast_test_status_update(test, "Expected result '42' not returned! ('%s')\n",
00494             ast_str_buffer(result));
00495       res = AST_TEST_FAIL;
00496    }
00497 
00498    ast_str_set(&expr, 0, "${MATH(170AND63,i)}");
00499    ast_str_substitute_variables(&result, 0, NULL, ast_str_buffer(expr));
00500    if (strcmp(ast_str_buffer(result), "42") != 0) {
00501       ast_test_status_update(test, "Expected result '42' not returned! ('%s')\n",
00502             ast_str_buffer(result));
00503       res = AST_TEST_FAIL;
00504    }
00505 
00506    return res;
00507 }
00508 #endif
00509 
00510 static int unload_module(void)
00511 {
00512    int res = 0;
00513 
00514    res |= ast_custom_function_unregister(&math_function);
00515    res |= ast_custom_function_unregister(&increment_function);
00516    res |= ast_custom_function_unregister(&decrement_function);
00517    AST_TEST_UNREGISTER(test_MATH_function);
00518 
00519    return res;
00520 }
00521 
00522 static int load_module(void)
00523 {
00524    int res = 0;
00525 
00526    res |= ast_custom_function_register(&math_function);
00527    res |= ast_custom_function_register(&increment_function);
00528    res |= ast_custom_function_register(&decrement_function);
00529    AST_TEST_REGISTER(test_MATH_function);
00530 
00531    return res;
00532 }
00533 
00534 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Mathematical dialplan function");

Generated on 20 Aug 2013 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1