Mon May 14 04:42:58 2007

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

Generated on Mon May 14 04:42:58 2007 for Asterisk - the Open Source PBX by  doxygen 1.5.1