Wed Mar 3 22:36:51 2010

Asterisk developer's documentation


func_strings.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2005-2006, Digium, Inc.
00005  * Portions Copyright (C) 2005, Tilghman Lesher.  All rights reserved.
00006  * Portions Copyright (C) 2005, Anthony Minessale II
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief String manipulation dialplan functions
00022  *
00023  * \author Tilghman Lesher
00024  * \author Anothony Minessale II 
00025  * \ingroup functions
00026  */
00027 
00028 #include "asterisk.h"
00029 
00030 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 211569 $")
00031 
00032 #include <regex.h>
00033 #include <ctype.h>
00034 
00035 #include "asterisk/module.h"
00036 #include "asterisk/channel.h"
00037 #include "asterisk/pbx.h"
00038 #include "asterisk/utils.h"
00039 #include "asterisk/app.h"
00040 #include "asterisk/localtime.h"
00041 
00042 static int function_fieldqty(struct ast_channel *chan, const char *cmd,
00043               char *parse, char *buf, size_t len)
00044 {
00045    char *varsubst, varval[8192], *varval2 = varval;
00046    int fieldcount = 0;
00047    AST_DECLARE_APP_ARGS(args,
00048               AST_APP_ARG(varname);
00049               AST_APP_ARG(delim);
00050       );
00051    char delim[2] = "";
00052    size_t delim_used;
00053 
00054    AST_STANDARD_APP_ARGS(args, parse);
00055    if (args.delim) {
00056       ast_get_encoded_char(args.delim, delim, &delim_used);
00057 
00058       varsubst = alloca(strlen(args.varname) + 4);
00059 
00060       sprintf(varsubst, "${%s}", args.varname);
00061       pbx_substitute_variables_helper(chan, varsubst, varval, sizeof(varval) - 1);
00062       if (ast_strlen_zero(varval2))
00063          fieldcount = 0;
00064       else {
00065          while (strsep(&varval2, delim))
00066             fieldcount++;
00067       }
00068    } else {
00069       fieldcount = 1;
00070    }
00071    snprintf(buf, len, "%d", fieldcount);
00072 
00073    return 0;
00074 }
00075 
00076 static struct ast_custom_function fieldqty_function = {
00077    .name = "FIELDQTY",
00078    .synopsis = "Count the fields, with an arbitrary delimiter",
00079    .syntax = "FIELDQTY(<varname>,<delim>)",
00080    .read = function_fieldqty,
00081 };
00082 
00083 static int filter(struct ast_channel *chan, const char *cmd, char *parse, char *buf,
00084         size_t len)
00085 {
00086    AST_DECLARE_APP_ARGS(args,
00087               AST_APP_ARG(allowed);
00088               AST_APP_ARG(string);
00089    );
00090    char *outbuf = buf, ac;
00091    char allowed[256] = "";
00092    size_t allowedlen = 0;
00093 
00094    AST_STANDARD_APP_ARGS(args, parse);
00095 
00096    if (!args.string) {
00097       ast_log(LOG_ERROR, "Usage: FILTER(<allowed-chars>,<string>)\n");
00098       return -1;
00099    }
00100 
00101    /* Expand ranges */
00102    for (; *(args.allowed) && allowedlen < sizeof(allowed); ) {
00103       char c1 = 0, c2 = 0;
00104       size_t consumed = 0;
00105 
00106       if (ast_get_encoded_char(args.allowed, &c1, &consumed))
00107          return -1;
00108       args.allowed += consumed;
00109 
00110       if (*(args.allowed) == '-') {
00111          if (ast_get_encoded_char(args.allowed + 1, &c2, &consumed))
00112             c2 = -1;
00113          args.allowed += consumed + 1;
00114 
00115          /*!\note
00116           * Looks a little strange, until you realize that we can overflow
00117           * the size of a char.
00118           */
00119          for (ac = c1; ac != c2 && allowedlen < sizeof(allowed) - 1; ac++)
00120             allowed[allowedlen++] = ac;
00121          allowed[allowedlen++] = ac;
00122 
00123          ast_debug(4, "c1=%d, c2=%d\n", c1, c2);
00124 
00125          /* Decrement before the loop increment */
00126          (args.allowed)--;
00127       } else
00128          allowed[allowedlen++] = c1;
00129    }
00130 
00131    ast_debug(1, "Allowed: %s\n", allowed);
00132 
00133    for (; *(args.string) && (buf + len - 1 > outbuf); (args.string)++) {
00134       if (strchr(allowed, *(args.string)))
00135          *outbuf++ = *(args.string);
00136    }
00137    *outbuf = '\0';
00138 
00139    return 0;
00140 }
00141 
00142 static struct ast_custom_function filter_function = {
00143    .name = "FILTER",
00144    .synopsis = "Filter the string to include only the allowed characters",
00145    .syntax = "FILTER(<allowed-chars>,<string>)",
00146    .read = filter,
00147    .desc =
00148 "Permits all characters listed in <allowed-chars>, filtering all others out.\n"
00149 "In addition to literally listing the characters, you may also use ranges of\n"
00150 "characters (delimited by a '-'), as well as hexadecimal characters started\n"
00151 "with a \\x (i.e. \\x20) and octal characters started with \\0 (i.e. \\040).\n"
00152 "Also, \\t, \\n, and \\r are recognized.  If you want a literal '-' character,\n"
00153 "simply prefix it with a '\\'\n",
00154 };
00155 
00156 static int regex(struct ast_channel *chan, const char *cmd, char *parse, char *buf,
00157        size_t len)
00158 {
00159    AST_DECLARE_APP_ARGS(args,
00160               AST_APP_ARG(null);
00161               AST_APP_ARG(reg);
00162               AST_APP_ARG(str);
00163    );
00164    int errcode;
00165    regex_t regexbuf;
00166 
00167    buf[0] = '\0';
00168 
00169    AST_NONSTANDARD_APP_ARGS(args, parse, '"');
00170 
00171    if (args.argc != 3) {
00172       ast_log(LOG_ERROR, "Unexpected arguments: should have been in the form '\"<regex>\" <string>'\n");
00173       return -1;
00174    }
00175    if ((*args.str == ' ') || (*args.str == '\t'))
00176       args.str++;
00177 
00178    ast_debug(1, "FUNCTION REGEX (%s)(%s)\n", args.reg, args.str);
00179 
00180    if ((errcode = regcomp(&regexbuf, args.reg, REG_EXTENDED | REG_NOSUB))) {
00181       regerror(errcode, &regexbuf, buf, len);
00182       ast_log(LOG_WARNING, "Malformed input %s(%s): %s\n", cmd, parse, buf);
00183       return -1;
00184    }
00185    
00186    strcpy(buf, regexec(&regexbuf, args.str, 0, NULL, 0) ? "0" : "1");
00187 
00188    regfree(&regexbuf);
00189 
00190    return 0;
00191 }
00192 
00193 static struct ast_custom_function regex_function = {
00194    .name = "REGEX",
00195    .synopsis = "Regular Expression",
00196    .desc =  
00197       "Returns 1 if data matches regular expression, or 0 otherwise.\n"
00198       "Please note that the space following the double quotes separating the regex from the data\n"
00199       "is optional and if present, is skipped. If a space is desired at the beginning of the data,\n"
00200            "then put two spaces there; the second will not be skipped.\n",
00201    .syntax = "REGEX(\"<regular expression>\" <data>)",
00202    .read = regex,
00203 };
00204 
00205 #define HASH_PREFIX  "~HASH~%s~"
00206 #define HASH_FORMAT  HASH_PREFIX "%s~"
00207 
00208 static char *app_clearhash = "ClearHash";
00209 static char *syn_clearhash = "Clear the keys from a specified hashname";
00210 static char *desc_clearhash =
00211 "ClearHash(<hashname>)\n"
00212 "  Clears all keys out of the specified hashname\n";
00213 
00214 /* This function probably should migrate to main/pbx.c, as pbx_builtin_clearvar_prefix() */
00215 static void clearvar_prefix(struct ast_channel *chan, const char *prefix)
00216 {
00217    struct ast_var_t *var;
00218    int len = strlen(prefix);
00219    AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->varshead, var, entries) {
00220       if (strncasecmp(prefix, ast_var_name(var), len) == 0) {
00221          AST_LIST_REMOVE_CURRENT(entries);
00222          ast_free(var);
00223       }
00224    }
00225    AST_LIST_TRAVERSE_SAFE_END
00226 }
00227 
00228 static int exec_clearhash(struct ast_channel *chan, void *data)
00229 {
00230    char prefix[80];
00231    snprintf(prefix, sizeof(prefix), HASH_PREFIX, data ? (char *)data : "null");
00232    clearvar_prefix(chan, prefix);
00233    return 0;
00234 }
00235 
00236 static int array(struct ast_channel *chan, const char *cmd, char *var,
00237        const char *value)
00238 {
00239    AST_DECLARE_APP_ARGS(arg1,
00240               AST_APP_ARG(var)[100];
00241    );
00242    AST_DECLARE_APP_ARGS(arg2,
00243               AST_APP_ARG(val)[100];
00244    );
00245    char *origvar = "", *value2, varname[256];
00246    int i, ishash = 0;
00247 
00248    value2 = ast_strdupa(value);
00249    if (!var || !value2)
00250       return -1;
00251 
00252    if (!strcmp(cmd, "HASH")) {
00253       const char *var2 = pbx_builtin_getvar_helper(chan, "~ODBCFIELDS~");
00254       origvar = var;
00255       if (var2)
00256          var = ast_strdupa(var2);
00257       else {
00258          if (chan)
00259             ast_autoservice_stop(chan);
00260          return -1;
00261       }
00262       ishash = 1;
00263    }
00264 
00265    /* The functions this will generally be used with are SORT and ODBC_*, which
00266     * both return comma-delimited lists.  However, if somebody uses literal lists,
00267     * their commas will be translated to vertical bars by the load, and I don't
00268     * want them to be surprised by the result.  Hence, we prefer commas as the
00269     * delimiter, but we'll fall back to vertical bars if commas aren't found.
00270     */
00271    ast_debug(1, "array (%s=%s)\n", var, value2);
00272    AST_STANDARD_APP_ARGS(arg1, var);
00273 
00274    AST_STANDARD_APP_ARGS(arg2, value2);
00275 
00276    for (i = 0; i < arg1.argc; i++) {
00277       ast_debug(1, "array set value (%s=%s)\n", arg1.var[i],
00278             arg2.val[i]);
00279       if (i < arg2.argc) {
00280          if (ishash) {
00281             snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]);
00282             pbx_builtin_setvar_helper(chan, varname, arg2.val[i]);
00283          } else {
00284             pbx_builtin_setvar_helper(chan, arg1.var[i], arg2.val[i]);
00285          }
00286       } else {
00287          /* We could unset the variable, by passing a NULL, but due to
00288           * pushvar semantics, that could create some undesired behavior. */
00289          if (ishash) {
00290             snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]);
00291             pbx_builtin_setvar_helper(chan, varname, "");
00292          } else {
00293             pbx_builtin_setvar_helper(chan, arg1.var[i], "");
00294          }
00295       }
00296    }
00297 
00298    return 0;
00299 }
00300 
00301 static int hashkeys_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00302 {
00303    struct ast_var_t *newvar;
00304    int plen;
00305    char prefix[80];
00306    snprintf(prefix, sizeof(prefix), HASH_PREFIX, data);
00307    plen = strlen(prefix);
00308 
00309    memset(buf, 0, len);
00310    AST_LIST_TRAVERSE(&chan->varshead, newvar, entries) {
00311       if (strncasecmp(prefix, ast_var_name(newvar), plen) == 0) {
00312          /* Copy everything after the prefix */
00313          strncat(buf, ast_var_name(newvar) + plen, len - strlen(buf) - 1);
00314          /* Trim the trailing ~ */
00315          buf[strlen(buf) - 1] = ',';
00316       }
00317    }
00318    /* Trim the trailing comma */
00319    buf[strlen(buf) - 1] = '\0';
00320    return 0;
00321 }
00322 
00323 static int hash_write(struct ast_channel *chan, const char *cmd, char *var, const char *value)
00324 {
00325    char varname[256];
00326    AST_DECLARE_APP_ARGS(arg,
00327       AST_APP_ARG(hashname);
00328       AST_APP_ARG(hashkey);
00329    );
00330 
00331    if (!strchr(var, ',')) {
00332       /* Single argument version */
00333       return array(chan, "HASH", var, value);
00334    }
00335 
00336    AST_STANDARD_APP_ARGS(arg, var);
00337    snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey);
00338    pbx_builtin_setvar_helper(chan, varname, value);
00339 
00340    return 0;
00341 }
00342 
00343 static int hash_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00344 {
00345    char varname[256];
00346    const char *varvalue;
00347    AST_DECLARE_APP_ARGS(arg,
00348       AST_APP_ARG(hashname);
00349       AST_APP_ARG(hashkey);
00350    );
00351 
00352    AST_STANDARD_APP_ARGS(arg, data);
00353    if (arg.argc == 2) {
00354       snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey);
00355       varvalue = pbx_builtin_getvar_helper(chan, varname);
00356       if (varvalue)
00357          ast_copy_string(buf, varvalue, len);
00358       else
00359          *buf = '\0';
00360    } else if (arg.argc == 1) {
00361       char colnames[4096];
00362       int i;
00363       AST_DECLARE_APP_ARGS(arg2,
00364          AST_APP_ARG(col)[100];
00365       );
00366 
00367       /* Get column names, in no particular order */
00368       hashkeys_read(chan, "HASHKEYS", arg.hashname, colnames, sizeof(colnames));
00369       pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", colnames);
00370 
00371       AST_STANDARD_APP_ARGS(arg2, colnames);
00372       *buf = '\0';
00373 
00374       /* Now get the corresponding column values, in exactly the same order */
00375       for (i = 0; i < arg2.argc; i++) {
00376          snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg2.col[i]);
00377          varvalue = pbx_builtin_getvar_helper(chan, varname);
00378          strncat(buf, varvalue, len - strlen(buf) - 1);
00379          strncat(buf, ",", len - strlen(buf) - 1);
00380       }
00381 
00382       /* Strip trailing comma */
00383       buf[strlen(buf) - 1] = '\0';
00384    }
00385 
00386    return 0;
00387 }
00388 
00389 static struct ast_custom_function hash_function = {
00390    .name = "HASH",
00391    .synopsis = "Implementation of a dialplan associative array",
00392    .syntax = "HASH(hashname[,hashkey])",
00393    .write = hash_write,
00394    .read = hash_read,
00395    .desc =
00396       "In two argument mode, gets and sets values to corresponding keys within a named\n"
00397       "associative array.  The single-argument mode will only work when assigned to from\n"
00398       "a function defined by func_odbc.so.\n",
00399 };
00400 
00401 static struct ast_custom_function hashkeys_function = {
00402    .name = "HASHKEYS",
00403    .synopsis = "Retrieve the keys of a HASH()",
00404    .syntax = "HASHKEYS(<hashname>)",
00405    .read = hashkeys_read,
00406    .desc =
00407       "Returns a comma-delimited list of the current keys of an associative array\n"
00408          "defined by the HASH() function.  Note that if you iterate over the keys of\n"
00409       "the result, adding keys during iteration will cause the result of the HASHKEYS\n"
00410       "function to change.\n",
00411 };
00412 
00413 static struct ast_custom_function array_function = {
00414    .name = "ARRAY",
00415    .synopsis = "Allows setting multiple variables at once",
00416    .syntax = "ARRAY(var1[,var2[...][,varN]])",
00417    .write = array,
00418    .desc =
00419       "The comma-separated list passed as a value to which the function is set will\n"
00420       "be interpreted as a set of values to which the comma-separated list of\n"
00421       "variable names in the argument should be set.\n"
00422       "Hence, Set(ARRAY(var1,var2)=1,2) will set var1 to 1 and var2 to 2.\n",
00423 };
00424 
00425 static int acf_sprintf(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00426 {
00427 #define SPRINTF_FLAG 0
00428 #define SPRINTF_WIDTH   1
00429 #define SPRINTF_PRECISION  2
00430 #define SPRINTF_LENGTH  3
00431 #define SPRINTF_CONVERSION 4
00432    int i, state = -1, argcount = 0;
00433    char *formatstart = NULL, *bufptr = buf;
00434    char formatbuf[256] = "";
00435    int tmpi;
00436    double tmpd;
00437    AST_DECLARE_APP_ARGS(arg,
00438             AST_APP_ARG(format);
00439             AST_APP_ARG(var)[100];
00440    );
00441 
00442    AST_STANDARD_APP_ARGS(arg, data);
00443 
00444    /* Scan the format, converting each argument into the requisite format type. */
00445    for (i = 0; arg.format[i]; i++) {
00446       switch (state) {
00447       case SPRINTF_FLAG:
00448          if (strchr("#0- +'I", arg.format[i]))
00449             break;
00450          state = SPRINTF_WIDTH;
00451       case SPRINTF_WIDTH:
00452          if (arg.format[i] >= '0' && arg.format[i] <= '9')
00453             break;
00454 
00455          /* Next character must be a period to go into a precision */
00456          if (arg.format[i] == '.') {
00457             state = SPRINTF_PRECISION;
00458          } else {
00459             state = SPRINTF_LENGTH;
00460             i--;
00461          }
00462          break;
00463       case SPRINTF_PRECISION:
00464          if (arg.format[i] >= '0' && arg.format[i] <= '9')
00465             break;
00466          state = SPRINTF_LENGTH;
00467       case SPRINTF_LENGTH:
00468          if (strchr("hl", arg.format[i])) {
00469             if (arg.format[i + 1] == arg.format[i])
00470                i++;
00471             state = SPRINTF_CONVERSION;
00472             break;
00473          } else if (strchr("Lqjzt", arg.format[i])) {
00474             state = SPRINTF_CONVERSION;
00475             break;
00476          }
00477          state = SPRINTF_CONVERSION;
00478       case SPRINTF_CONVERSION:
00479          if (strchr("diouxXc", arg.format[i])) {
00480             /* Integer */
00481 
00482             /* Isolate this format alone */
00483             ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
00484             formatbuf[&arg.format[i] - formatstart + 1] = '\0';
00485 
00486             /* Convert the argument into the required type */
00487             if (arg.var[argcount]) {
00488                if (sscanf(arg.var[argcount++], "%30d", &tmpi) != 1) {
00489                   ast_log(LOG_ERROR, "Argument '%s' is not an integer number for format '%s'\n", arg.var[argcount - 1], formatbuf);
00490                   goto sprintf_fail;
00491                }
00492             } else {
00493                ast_log(LOG_ERROR, "SPRINTF() has more format specifiers than arguments!\n");
00494                goto sprintf_fail;
00495             }
00496 
00497             /* Format the argument */
00498             snprintf(bufptr, buf + len - bufptr, formatbuf, tmpi);
00499 
00500             /* Update the position of the next parameter to print */
00501             bufptr = strchr(buf, '\0');
00502          } else if (strchr("eEfFgGaA", arg.format[i])) {
00503             /* Double */
00504 
00505             /* Isolate this format alone */
00506             ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
00507             formatbuf[&arg.format[i] - formatstart + 1] = '\0';
00508 
00509             /* Convert the argument into the required type */
00510             if (arg.var[argcount]) {
00511                if (sscanf(arg.var[argcount++], "%30lf", &tmpd) != 1) {
00512                   ast_log(LOG_ERROR, "Argument '%s' is not a floating point number for format '%s'\n", arg.var[argcount - 1], formatbuf);
00513                   goto sprintf_fail;
00514                }
00515             } else {
00516                ast_log(LOG_ERROR, "SPRINTF() has more format specifiers than arguments!\n");
00517                goto sprintf_fail;
00518             }
00519 
00520             /* Format the argument */
00521             snprintf(bufptr, buf + len - bufptr, formatbuf, tmpd);
00522 
00523             /* Update the position of the next parameter to print */
00524             bufptr = strchr(buf, '\0');
00525          } else if (arg.format[i] == 's') {
00526             /* String */
00527 
00528             /* Isolate this format alone */
00529             ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
00530             formatbuf[&arg.format[i] - formatstart + 1] = '\0';
00531 
00532             /* Format the argument */
00533             snprintf(bufptr, buf + len - bufptr, formatbuf, arg.var[argcount++]);
00534 
00535             /* Update the position of the next parameter to print */
00536             bufptr = strchr(buf, '\0');
00537          } else if (arg.format[i] == '%') {
00538             /* Literal data to copy */
00539             *bufptr++ = arg.format[i];
00540          } else {
00541             /* Not supported */
00542 
00543             /* Isolate this format alone */
00544             ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
00545             formatbuf[&arg.format[i] - formatstart + 1] = '\0';
00546 
00547             ast_log(LOG_ERROR, "Format type not supported: '%s' with argument '%s'\n", formatbuf, arg.var[argcount++]);
00548             goto sprintf_fail;
00549          }
00550          state = -1;
00551          break;
00552       default:
00553          if (arg.format[i] == '%') {
00554             state = SPRINTF_FLAG;
00555             formatstart = &arg.format[i];
00556             break;
00557          } else {
00558             /* Literal data to copy */
00559             *bufptr++ = arg.format[i];
00560          }
00561       }
00562    }
00563    *bufptr = '\0';
00564    return 0;
00565 sprintf_fail:
00566    return -1;
00567 }
00568 
00569 static struct ast_custom_function sprintf_function = {
00570    .name = "SPRINTF",
00571    .synopsis = "Format a variable according to a format string",
00572    .syntax = "SPRINTF(<format>,<arg1>[,...<argN>])",
00573    .read = acf_sprintf,
00574    .desc =
00575 "Parses the format string specified and returns a string matching that format.\n"
00576 "Supports most options supported by sprintf(3).  Returns a shortened string if\n"
00577 "a format specifier is not recognized.\n",
00578 };
00579 
00580 static int quote(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00581 {
00582    char *bufptr = buf, *dataptr = data;
00583    if (ast_strlen_zero(data)) {
00584       ast_log(LOG_WARNING, "No argument specified!\n");
00585       ast_copy_string(buf, "\"\"", len);
00586       return 0;
00587    }
00588 
00589    *bufptr++ = '"';
00590    for (; bufptr < buf + len - 1; dataptr++) {
00591       if (*dataptr == '\\') {
00592          *bufptr++ = '\\';
00593          *bufptr++ = '\\';
00594       } else if (*dataptr == '"') {
00595          *bufptr++ = '\\';
00596          *bufptr++ = '"';
00597       } else if (*dataptr == '\0') {
00598          break;
00599       } else {
00600          *bufptr++ = *dataptr;
00601       }
00602    }
00603    *bufptr++ = '"';
00604    *bufptr = '\0';
00605    return 0;
00606 }
00607 
00608 static struct ast_custom_function quote_function = {
00609    .name = "QUOTE",
00610    .synopsis = "Quotes a given string, escaping embedded quotes as necessary",
00611    .syntax = "QUOTE(<string>)",
00612    .read = quote,
00613 };
00614 
00615 
00616 static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf,
00617           size_t buflen)
00618 {
00619    int length = 0;
00620 
00621    if (data)
00622       length = strlen(data);
00623 
00624    snprintf(buf, buflen, "%d", length);
00625 
00626    return 0;
00627 }
00628 
00629 static struct ast_custom_function len_function = {
00630    .name = "LEN",
00631    .synopsis = "Returns the length of the argument given",
00632    .syntax = "LEN(<string>)",
00633    .read = len,
00634 };
00635 
00636 static int acf_strftime(struct ast_channel *chan, const char *cmd, char *parse,
00637          char *buf, size_t buflen)
00638 {
00639    AST_DECLARE_APP_ARGS(args,
00640               AST_APP_ARG(epoch);
00641               AST_APP_ARG(timezone);
00642               AST_APP_ARG(format);
00643    );
00644    struct timeval when;
00645    struct ast_tm tm;
00646 
00647    buf[0] = '\0';
00648 
00649    AST_STANDARD_APP_ARGS(args, parse);
00650 
00651    ast_get_timeval(args.epoch, &when, ast_tvnow(), NULL);
00652    ast_localtime(&when, &tm, args.timezone);
00653 
00654    if (!args.format)
00655       args.format = "%c";
00656 
00657    if (ast_strftime(buf, buflen, args.format, &tm) <= 0)
00658       ast_log(LOG_WARNING, "C function strftime() output nothing?!!\n");
00659 
00660    buf[buflen - 1] = '\0';
00661 
00662    return 0;
00663 }
00664 
00665 static struct ast_custom_function strftime_function = {
00666    .name = "STRFTIME",
00667    .synopsis = "Returns the current date/time in a specified format.",
00668    .syntax = "STRFTIME([<epoch>][,[timezone][,format]])",
00669    .desc =
00670 "STRFTIME sports all of the same formats as the underlying C function\n"
00671 "strftime(3) - see the man page for details.  It also supports the\n"
00672 "following format:\n"
00673 " %[n]q - fractions of a second, with leading zeroes.  For example, %3q will\n"
00674 "         give milliseconds and %1q will give tenths of a second.  The default\n"
00675 "         is to output milliseconds (n=3).  The common case is to use it in\n"
00676 "         combination with %S, as in \"%S.%3q\".\n",
00677    .read = acf_strftime,
00678 };
00679 
00680 static int acf_strptime(struct ast_channel *chan, const char *cmd, char *data,
00681          char *buf, size_t buflen)
00682 {
00683    AST_DECLARE_APP_ARGS(args,
00684               AST_APP_ARG(timestring);
00685               AST_APP_ARG(timezone);
00686               AST_APP_ARG(format);
00687    );
00688    union {
00689       struct ast_tm atm;
00690       struct tm time;
00691    } t = { { 0, }, };
00692 
00693    buf[0] = '\0';
00694 
00695    if (!data) {
00696       ast_log(LOG_ERROR,
00697             "Asterisk function STRPTIME() requires an argument.\n");
00698       return -1;
00699    }
00700 
00701    AST_STANDARD_APP_ARGS(args, data);
00702 
00703    if (ast_strlen_zero(args.format)) {
00704       ast_log(LOG_ERROR,
00705             "No format supplied to STRPTIME(<timestring>,<timezone>,<format>)");
00706       return -1;
00707    }
00708 
00709    if (!strptime(args.timestring, args.format, &t.time)) {
00710       ast_log(LOG_WARNING, "C function strptime() output nothing?!!\n");
00711    } else {
00712       struct timeval when;
00713       /* Since strptime(3) does not check DST, force ast_mktime() to calculate it. */
00714       t.atm.tm_isdst = -1;
00715       when = ast_mktime(&t.atm, args.timezone);
00716       snprintf(buf, buflen, "%d", (int) when.tv_sec);
00717    }
00718 
00719    return 0;
00720 }
00721 
00722 static struct ast_custom_function strptime_function = {
00723    .name = "STRPTIME",
00724    .synopsis =
00725       "Returns the epoch of the arbitrary date/time string structured as described in the format.",
00726    .syntax = "STRPTIME(<datetime>,<timezone>,<format>)",
00727    .desc =
00728       "This is useful for converting a date into an EPOCH time, possibly to pass to\n"
00729       "an application like SayUnixTime or to calculate the difference between two\n"
00730       "date strings.\n"
00731       "\n"
00732       "Example:\n"
00733       "  ${STRPTIME(2006-03-01 07:30:35,America/Chicago,%Y-%m-%d %H:%M:%S)} returns 1141219835\n",
00734    .read = acf_strptime,
00735 };
00736 
00737 static int function_eval(struct ast_channel *chan, const char *cmd, char *data,
00738           char *buf, size_t buflen)
00739 {
00740    if (ast_strlen_zero(data)) {
00741       ast_log(LOG_WARNING, "EVAL requires an argument: EVAL(<string>)\n");
00742       return -1;
00743    }
00744 
00745    pbx_substitute_variables_helper(chan, data, buf, buflen - 1);
00746 
00747    return 0;
00748 }
00749 
00750 static struct ast_custom_function eval_function = {
00751    .name = "EVAL",
00752    .synopsis = "Evaluate stored variables.",
00753    .syntax = "EVAL(<variable>)",
00754    .desc = "Using EVAL basically causes a string to be evaluated twice.\n"
00755       "When a variable or expression is in the dialplan, it will be\n"
00756       "evaluated at runtime. However, if the result of the evaluation\n"
00757       "is in fact a variable or expression, using EVAL will have it\n"
00758       "evaluated a second time. For example, if the variable ${MYVAR}\n"
00759       "contains \"${OTHERVAR}\", then the result of putting ${EVAL(${MYVAR})}\n"
00760       "in the dialplan will be the contents of the variable, OTHERVAR.\n"
00761       "Normally, by just putting ${MYVAR} in the dialplan, you would be\n"
00762       "left with \"${OTHERVAR}\".\n",
00763    .read = function_eval,
00764 };
00765 
00766 static int keypadhash(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
00767 {
00768    char *bufptr, *dataptr;
00769 
00770    for (bufptr = buf, dataptr = data; bufptr < buf + buflen - 1; dataptr++) {
00771       if (*dataptr == '\0') {
00772          *bufptr++ = '\0';
00773          break;
00774       } else if (*dataptr == '1') {
00775          *bufptr++ = '1';
00776       } else if (strchr("AaBbCc2", *dataptr)) {
00777          *bufptr++ = '2';
00778       } else if (strchr("DdEeFf3", *dataptr)) {
00779          *bufptr++ = '3';
00780       } else if (strchr("GgHhIi4", *dataptr)) {
00781          *bufptr++ = '4';
00782       } else if (strchr("JjKkLl5", *dataptr)) {
00783          *bufptr++ = '5';
00784       } else if (strchr("MmNnOo6", *dataptr)) {
00785          *bufptr++ = '6';
00786       } else if (strchr("PpQqRrSs7", *dataptr)) {
00787          *bufptr++ = '7';
00788       } else if (strchr("TtUuVv8", *dataptr)) {
00789          *bufptr++ = '8';
00790       } else if (strchr("WwXxYyZz9", *dataptr)) {
00791          *bufptr++ = '9';
00792       } else if (*dataptr == '0') {
00793          *bufptr++ = '0';
00794       }
00795    }
00796    buf[buflen - 1] = '\0';
00797 
00798    return 0;
00799 }
00800 
00801 static struct ast_custom_function keypadhash_function = {
00802    .name = "KEYPADHASH",
00803    .synopsis = "Hash the letters in the string into the equivalent keypad numbers.",
00804    .syntax = "KEYPADHASH(<string>)",
00805    .read = keypadhash,
00806    .desc = "Example:  ${KEYPADHASH(Les)} returns \"537\"\n",
00807 };
00808 
00809 static int string_toupper(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
00810 {
00811    char *bufptr = buf, *dataptr = data;
00812 
00813    while ((bufptr < buf + buflen - 1) && (*bufptr++ = toupper(*dataptr++)));
00814 
00815    return 0;
00816 }
00817 
00818 static struct ast_custom_function toupper_function = {
00819    .name = "TOUPPER",
00820    .synopsis = "Convert the string to upper case.",
00821    .syntax = "TOUPPER(<string>)",
00822    .read = string_toupper,
00823    .desc = "Example: ${TOUPPER(Example)} returns \"EXAMPLE\"\n",
00824 };
00825 
00826 static int string_tolower(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
00827 {
00828    char *bufptr = buf, *dataptr = data;
00829 
00830    while ((bufptr < buf + buflen - 1) && (*bufptr++ = tolower(*dataptr++)));
00831 
00832    return 0;
00833 }
00834 
00835 static struct ast_custom_function tolower_function = {
00836    .name = "TOLOWER",
00837    .synopsis = "Convert the string to lower case.",
00838    .syntax = "TOLOWER(<string>)",
00839    .read = string_tolower,
00840    .desc = "Example: ${TOLOWER(Example)} returns \"example\"\n",
00841 };
00842 
00843 static int unload_module(void)
00844 {
00845    int res = 0;
00846 
00847    res |= ast_custom_function_unregister(&fieldqty_function);
00848    res |= ast_custom_function_unregister(&filter_function);
00849    res |= ast_custom_function_unregister(&regex_function);
00850    res |= ast_custom_function_unregister(&array_function);
00851    res |= ast_custom_function_unregister(&quote_function);
00852    res |= ast_custom_function_unregister(&len_function);
00853    res |= ast_custom_function_unregister(&strftime_function);
00854    res |= ast_custom_function_unregister(&strptime_function);
00855    res |= ast_custom_function_unregister(&eval_function);
00856    res |= ast_custom_function_unregister(&keypadhash_function);
00857    res |= ast_custom_function_unregister(&sprintf_function);
00858    res |= ast_custom_function_unregister(&hashkeys_function);
00859    res |= ast_custom_function_unregister(&hash_function);
00860    res |= ast_unregister_application(app_clearhash);
00861    res |= ast_custom_function_unregister(&toupper_function);
00862    res |= ast_custom_function_unregister(&tolower_function);
00863 
00864    return res;
00865 }
00866 
00867 static int load_module(void)
00868 {
00869    int res = 0;
00870 
00871    res |= ast_custom_function_register(&fieldqty_function);
00872    res |= ast_custom_function_register(&filter_function);
00873    res |= ast_custom_function_register(&regex_function);
00874    res |= ast_custom_function_register(&array_function);
00875    res |= ast_custom_function_register(&quote_function);
00876    res |= ast_custom_function_register(&len_function);
00877    res |= ast_custom_function_register(&strftime_function);
00878    res |= ast_custom_function_register(&strptime_function);
00879    res |= ast_custom_function_register(&eval_function);
00880    res |= ast_custom_function_register(&keypadhash_function);
00881    res |= ast_custom_function_register(&sprintf_function);
00882    res |= ast_custom_function_register(&hashkeys_function);
00883    res |= ast_custom_function_register(&hash_function);
00884    res |= ast_register_application(app_clearhash, exec_clearhash, syn_clearhash, desc_clearhash);
00885    res |= ast_custom_function_register(&toupper_function);
00886    res |= ast_custom_function_register(&tolower_function);
00887 
00888    return res;
00889 }
00890 
00891 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "String handling dialplan functions");

Generated on 3 Mar 2010 for Asterisk - the Open Source PBX by  doxygen 1.6.1