Mon Mar 19 11:30:27 2012

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: 328209 $")
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;,&gt;=,&lt;=,== (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) - Increments MyVar</para>
00107          <para>Note: DEC(${MyVAR}) - Is wrong, as INC 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 (!mvalue1 || !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    ast_log(LOG_NOTICE, "The value is now: %d\n", int_value);
00425 
00426    if (snprintf(returnvar, sizeof(returnvar), "%d", int_value) > 0) {
00427       pbx_builtin_setvar_helper(chan, data, returnvar);
00428       if (modify_orig) {
00429          ast_copy_string(buf, returnvar, len);
00430       }
00431       ret = 0;
00432    } else {
00433       pbx_builtin_setvar_helper(chan, data, "0");
00434       if (modify_orig) {
00435          ast_copy_string(buf, "0", len);
00436       }
00437       ast_log(LOG_NOTICE, "Variable %s refused to be %sREMENTED, setting value to 0", data, cmd);
00438       ret = 0;
00439    }
00440 
00441    ast_channel_unlock(chan);
00442 
00443    return ret;
00444 }
00445 
00446 
00447 static struct ast_custom_function math_function = {
00448    .name = "MATH",
00449    .read = math
00450 };
00451 
00452 static struct ast_custom_function increment_function = {
00453    .name = "INC",
00454    .read = crement_function_read,
00455 };
00456 
00457 static struct ast_custom_function decrement_function = {
00458    .name = "DEC",
00459    .read = crement_function_read,
00460 };
00461 
00462 #ifdef TEST_FRAMEWORK
00463 AST_TEST_DEFINE(test_MATH_function)
00464 {
00465    enum ast_test_result_state res = AST_TEST_PASS;
00466    struct ast_str *expr, *result;
00467 
00468    switch (cmd) {
00469    case TEST_INIT:
00470       info->name = "test_MATH_function";
00471       info->category = "/main/pbx/";
00472       info->summary = "Test MATH function substitution";
00473       info->description =
00474          "Executes a series of variable substitutions using the MATH function and ensures that the expected results are received.";
00475       return AST_TEST_NOT_RUN;
00476    case TEST_EXECUTE:
00477       break;
00478    }
00479 
00480    ast_test_status_update(test, "Testing MATH() substitution ...\n");
00481 
00482    if (!(expr = ast_str_create(16)) || !(result = ast_str_create(16))) {
00483       if (expr) {
00484          ast_free(expr);
00485       }
00486       if (result) {
00487          ast_free(result);
00488       }
00489       return AST_TEST_FAIL;
00490    }
00491 
00492    ast_str_set(&expr, 0, "${MATH(170 AND 63,i)}");
00493    ast_str_substitute_variables(&result, 0, NULL, ast_str_buffer(expr));
00494    if (strcmp(ast_str_buffer(result), "42") != 0) {
00495       ast_test_status_update(test, "Expected result '42' not returned! ('%s')\n",
00496             ast_str_buffer(result));
00497       res = AST_TEST_FAIL;
00498    }
00499 
00500    ast_str_set(&expr, 0, "${MATH(170AND63,i)}");
00501    ast_str_substitute_variables(&result, 0, NULL, ast_str_buffer(expr));
00502    if (strcmp(ast_str_buffer(result), "42") != 0) {
00503       ast_test_status_update(test, "Expected result '42' not returned! ('%s')\n",
00504             ast_str_buffer(result));
00505       res = AST_TEST_FAIL;
00506    }
00507 
00508    return res;
00509 }
00510 #endif
00511 
00512 static int unload_module(void)
00513 {
00514    int res = 0;
00515 
00516    res |= ast_custom_function_unregister(&math_function);
00517    res |= ast_custom_function_unregister(&increment_function);
00518    res |= ast_custom_function_unregister(&decrement_function);
00519    AST_TEST_UNREGISTER(test_MATH_function);
00520 
00521    return res;
00522 }
00523 
00524 static int load_module(void)
00525 {
00526    int res = 0;
00527 
00528    res |= ast_custom_function_register(&math_function);
00529    res |= ast_custom_function_register(&increment_function);
00530    res |= ast_custom_function_register(&decrement_function);
00531    AST_TEST_REGISTER(test_MATH_function);
00532 
00533    return res;
00534 }
00535 
00536 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Mathematical dialplan function");

Generated on Mon Mar 19 11:30:27 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7