Mon Jun 27 16:50:54 2011

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

Generated on Mon Jun 27 16:50:54 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7