Mon Mar 19 11:30:27 2012

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 /*** MODULEINFO
00029    <support_level>core</support_level>
00030  ***/
00031 
00032 #include "asterisk.h"
00033 
00034 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 328209 $")
00035 
00036 #include <regex.h>
00037 #include <ctype.h>
00038 
00039 #include "asterisk/module.h"
00040 #include "asterisk/channel.h"
00041 #include "asterisk/pbx.h"
00042 #include "asterisk/utils.h"
00043 #include "asterisk/app.h"
00044 #include "asterisk/localtime.h"
00045 #include "asterisk/test.h"
00046 
00047 AST_THREADSTORAGE(result_buf);
00048 AST_THREADSTORAGE(tmp_buf);
00049 
00050 /*** DOCUMENTATION
00051    <function name="FIELDQTY" language="en_US">
00052       <synopsis>
00053          Count the fields with an arbitrary delimiter
00054       </synopsis>
00055       <syntax>
00056          <parameter name="varname" required="true" />
00057          <parameter name="delim" required="true" />
00058       </syntax>
00059       <description>
00060          <para>The delimiter may be specified as a special or extended ASCII character, by encoding it.  The characters
00061          <literal>\n</literal>, <literal>\r</literal>, and <literal>\t</literal> are all recognized as the newline,
00062          carriage return, and tab characters, respectively.  Also, octal and hexadecimal specifications are recognized
00063          by the patterns <literal>\0nnn</literal> and <literal>\xHH</literal>, respectively.  For example, if you wanted
00064          to encode a comma as the delimiter, you could use either <literal>\054</literal> or <literal>\x2C</literal>.</para>
00065          <para>Example: If ${example} contains <literal>ex-amp-le</literal>, then ${FIELDQTY(example,-)} returns 3.</para>
00066       </description>
00067    </function>
00068    <function name="FIELDNUM" language="en_US">
00069       <synopsis>
00070          Return the 1-based offset of a field in a list
00071       </synopsis>
00072       <syntax>
00073          <parameter name="varname" required="true" />
00074          <parameter name="delim" required="true" />
00075          <parameter name="value" required="true" />
00076       </syntax>
00077       <description>
00078          <para>Search the variable named <replaceable>varname</replaceable> for the string <replaceable>value</replaceable>
00079          delimited by <replaceable>delim</replaceable> and return a 1-based offset as to its location. If not found
00080          or an error occured, return <literal>0</literal>.</para>
00081          <para>The delimiter may be specified as a special or extended ASCII character, by encoding it.  The characters
00082          <literal>\n</literal>, <literal>\r</literal>, and <literal>\t</literal> are all recognized as the newline,
00083          carriage return, and tab characters, respectively.  Also, octal and hexadecimal specifications are recognized
00084          by the patterns <literal>\0nnn</literal> and <literal>\xHH</literal>, respectively.  For example, if you wanted
00085          to encode a comma as the delimiter, you could use either <literal>\054</literal> or <literal>\x2C</literal>.</para>
00086               <para>Example: If ${example} contains <literal>ex-amp-le</literal>, then ${FIELDNUM(example,-,amp)} returns 2.</para>
00087       </description>
00088    </function>
00089    <function name="LISTFILTER" language="en_US">
00090       <synopsis>Remove an item from a list, by name.</synopsis>
00091       <syntax>
00092          <parameter name="varname" required="true" />
00093          <parameter name="delim" required="true" default="," />
00094          <parameter name="value" required="true" />
00095       </syntax>
00096       <description>
00097          <para>Remove <replaceable>value</replaceable> from the list contained in the <replaceable>varname</replaceable>
00098          variable, where the list delimiter is specified by the <replaceable>delim</replaceable> parameter.  This is
00099          very useful for removing a single channel name from a list of channels, for example.</para>
00100       </description>
00101    </function>
00102    <function name="FILTER" language="en_US">
00103       <synopsis>
00104          Filter the string to include only the allowed characters
00105       </synopsis>
00106       <syntax>
00107          <parameter name="allowed-chars" required="true" />
00108          <parameter name="string" required="true" />
00109       </syntax>
00110       <description>
00111          <para>Permits all characters listed in <replaceable>allowed-chars</replaceable>, 
00112          filtering all others outs. In addition to literally listing the characters, 
00113          you may also use ranges of characters (delimited by a <literal>-</literal></para>
00114          <para>Hexadecimal characters started with a <literal>\x</literal>(i.e. \x20)</para>
00115          <para>Octal characters started with a <literal>\0</literal> (i.e. \040)</para>
00116          <para>Also <literal>\t</literal>,<literal>\n</literal> and <literal>\r</literal> are recognized.</para> 
00117          <note><para>If you want the <literal>-</literal> character it needs to be prefixed with a 
00118          <literal></literal></para></note>
00119       </description>
00120    </function>
00121    <function name="REPLACE" language="en_US">
00122       <synopsis>
00123          Replace a set of characters in a given string with another character.
00124       </synopsis>
00125       <syntax>
00126          <parameter name="varname" required="true" />
00127          <parameter name="find-chars" required="true" />
00128          <parameter name="replace-char" required="false" />
00129       </syntax>
00130       <description>
00131          <para>Iterates through a string replacing all the <replaceable>find-chars</replaceable> with
00132          <replaceable>replace-char</replaceable>.  <replaceable>replace-char</replaceable> may be either
00133          empty or contain one character.  If empty, all <replaceable>find-chars</replaceable> will be
00134          deleted from the output.</para>
00135          <note><para>The replacement only occurs in the output.  The original variable is not
00136          altered.</para></note>
00137       </description>
00138    </function>
00139    <function name="PASSTHRU" language="en_US">
00140       <synopsis>
00141          Pass the given argument back as a value.
00142       </synopsis>
00143       <syntax>
00144          <parameter name="string" required="false" />
00145       </syntax>
00146       <description>
00147          <para>Literally returns the given <replaceable>string</replaceable>.  The intent is to permit
00148          other dialplan functions which take a variable name as an argument to be able to take a literal
00149          string, instead.</para>
00150       </description>
00151    </function>
00152    <function name="REGEX" language="en_US">
00153       <synopsis>
00154          Check string against a regular expression.
00155       </synopsis>
00156       <syntax argsep=" ">
00157          <parameter name="&quot;regular expression&quot;" required="true" />
00158          <parameter name="string" required="true" />
00159       </syntax>
00160       <description>
00161          <para>Return <literal>1</literal> on regular expression match or <literal>0</literal> otherwise</para>
00162          <para>Please note that the space following the double quotes separating the 
00163          regex from the data is optional and if present, is skipped. If a space is 
00164          desired at the beginning of the data, then put two spaces there; the second 
00165          will not be skipped.</para>
00166       </description>
00167    </function>
00168    <application name="ClearHash" language="en_US">
00169       <synopsis>
00170          Clear the keys from a specified hashname.
00171       </synopsis>
00172       <syntax>
00173          <parameter name="hashname" required="true" />
00174       </syntax>
00175       <description>
00176          <para>Clears all keys out of the specified <replaceable>hashname</replaceable>.</para>
00177       </description>
00178    </application>
00179    <function name="HASH" language="en_US">
00180       <synopsis>
00181          Implementation of a dialplan associative array
00182       </synopsis>
00183       <syntax>
00184          <parameter name="hashname" required="true" />
00185          <parameter name="hashkey" />
00186       </syntax>
00187       <description>
00188          <para>In two arguments mode, gets and sets values to corresponding keys within
00189          a named associative array. The single-argument mode will only work when assigned
00190          to from a function defined by func_odbc</para>
00191       </description>
00192    </function>
00193    <function name="HASHKEYS" language="en_US">
00194       <synopsis>
00195          Retrieve the keys of the HASH() function.
00196       </synopsis>
00197       <syntax>
00198          <parameter name="hashname" required="true" />
00199       </syntax>
00200       <description>
00201          <para>Returns a comma-delimited list of the current keys of the associative array 
00202          defined by the HASH() function. Note that if you iterate over the keys of 
00203          the result, adding keys during iteration will cause the result of the HASHKEYS()
00204          function to change.</para>
00205       </description>
00206    </function>
00207    <function name="KEYPADHASH" language="en_US">
00208       <synopsis>
00209          Hash the letters in string into equivalent keypad numbers.
00210       </synopsis>
00211       <syntax>
00212          <parameter name="string" required="true" />
00213       </syntax>
00214       <description>
00215          <para>Example: ${KEYPADHASH(Les)} returns "537"</para>
00216       </description>
00217    </function>
00218    <function name="ARRAY" language="en_US">
00219       <synopsis>
00220          Allows setting multiple variables at once.
00221       </synopsis>
00222       <syntax>
00223          <parameter name="var1" required="true" />
00224          <parameter name="var2" required="false" multiple="true" />
00225          <parameter name="varN" required="false" />
00226       </syntax>
00227       <description>
00228          <para>The comma-delimited list passed as a value to which the function is set will 
00229          be interpreted as a set of values to which the comma-delimited list of 
00230          variable names in the argument should be set.</para>
00231          <para>Example: Set(ARRAY(var1,var2)=1,2) will set var1 to 1 and var2 to 2</para>
00232       </description>
00233    </function>
00234    <function name="STRPTIME" language="en_US">
00235       <synopsis>
00236          Returns the epoch of the arbitrary date/time string structured as described by the format.
00237       </synopsis>
00238       <syntax>
00239          <parameter name="datetime" required="true" />
00240          <parameter name="timezone" required="true" />
00241          <parameter name="format" required="true" />
00242       </syntax>
00243       <description>
00244          <para>This is useful for converting a date into <literal>EPOCH</literal> time, 
00245          possibly to pass to an application like SayUnixTime or to calculate the difference
00246          between the two date strings</para>
00247          <para>Example: ${STRPTIME(2006-03-01 07:30:35,America/Chicago,%Y-%m-%d %H:%M:%S)} returns 1141219835</para>
00248       </description>
00249    </function>
00250    <function name="STRFTIME" language="en_US">
00251       <synopsis>
00252          Returns the current date/time in the specified format.
00253       </synopsis>
00254       <syntax>
00255          <parameter name="epoch" />
00256          <parameter name="timezone" />
00257          <parameter name="format" />
00258       </syntax>
00259       <description>
00260          <para>STRFTIME supports all of the same formats as the underlying C function
00261          <emphasis>strftime(3)</emphasis>.
00262          It also supports the following format: <literal>%[n]q</literal> - fractions of a second,
00263          with leading zeros.</para>
00264          <para>Example: <literal>%3q</literal> will give milliseconds and <literal>%1q</literal>
00265          will give tenths of a second. The default is set at milliseconds (n=3).
00266          The common case is to use it in combination with %S, as in <literal>%S.%3q</literal>.</para>
00267       </description>
00268       <see-also>
00269          <ref type="manpage">strftime(3)</ref>
00270       </see-also>
00271    </function>
00272    <function name="EVAL" language="en_US">
00273       <synopsis>
00274          Evaluate stored variables
00275       </synopsis>
00276       <syntax>
00277          <parameter name="variable" required="true" />
00278       </syntax>
00279       <description>
00280          <para>Using EVAL basically causes a string to be evaluated twice.
00281          When a variable or expression is in the dialplan, it will be
00282          evaluated at runtime. However, if the results of the evaluation
00283          is in fact another variable or expression, using EVAL will have it
00284          evaluated a second time.</para>
00285          <para>Example: If the <variable>MYVAR</variable> contains
00286          <variable>OTHERVAR</variable>, then the result of ${EVAL(
00287          <variable>MYVAR</variable>)} in the dialplan will be the
00288          contents of <variable>OTHERVAR</variable>. Normally just
00289          putting <variable>MYVAR</variable> in the dialplan the result
00290          would be <variable>OTHERVAR</variable>.</para>
00291       </description>
00292    </function>
00293    <function name="TOUPPER" language="en_US">
00294       <synopsis>
00295          Convert string to all uppercase letters.
00296       </synopsis>
00297       <syntax>
00298          <parameter name="string" required="true" />
00299       </syntax>
00300       <description>
00301          <para>Example: ${TOUPPER(Example)} returns "EXAMPLE"</para>
00302       </description>
00303    </function>
00304    <function name="TOLOWER" language="en_US">
00305       <synopsis>
00306          Convert string to all lowercase letters.
00307       </synopsis>
00308       <syntax>
00309          <parameter name="string" required="true" />
00310       </syntax>
00311       <description>
00312          <para>Example: ${TOLOWER(Example)} returns "example"</para>
00313       </description>
00314    </function>
00315    <function name="LEN" language="en_US">
00316       <synopsis>
00317          Return the length of the string given.
00318       </synopsis>
00319       <syntax>
00320          <parameter name="string" required="true" />
00321       </syntax>
00322       <description>
00323          <para>Example: ${LEN(example)} returns 7</para>
00324       </description>
00325    </function>
00326    <function name="QUOTE" language="en_US">
00327       <synopsis>
00328          Quotes a given string, escaping embedded quotes as necessary
00329       </synopsis>
00330       <syntax>
00331          <parameter name="string" required="true" />
00332       </syntax>
00333       <description>
00334          <para>Example: ${QUOTE(ab"c"de)} will return "abcde"</para>
00335       </description>
00336    </function>
00337    <function name="CSV_QUOTE" language="en_US">
00338       <synopsis>
00339          Quotes a given string for use in a CSV file, escaping embedded quotes as necessary
00340       </synopsis>
00341       <syntax>
00342          <parameter name="string" required="true" />
00343       </syntax>
00344       <description>
00345          <para>Example: ${CSV_QUOTE("a,b" 123)} will return """a,b"" 123"</para>
00346       </description>
00347    </function>
00348    <function name="SHIFT" language="en_US">
00349       <synopsis>
00350          Removes and returns the first item off of a variable containing delimited text
00351       </synopsis>
00352       <syntax>
00353          <parameter name="varname" required="true" />
00354          <parameter name="delimiter" required="false" default="," />
00355       </syntax>
00356       <description>
00357          <para>Example:</para>
00358          <para>exten => s,1,Set(array=one,two,three)</para>
00359          <para>exten => s,n,While($["${SET(var=${SHIFT(array)})}" != ""])</para>
00360          <para>exten => s,n,NoOp(var is ${var})</para>
00361          <para>exten => s,n,EndWhile</para>
00362          <para>This would iterate over each value in array, left to right, and
00363             would result in NoOp(var is one), NoOp(var is two), and
00364             NoOp(var is three) being executed.
00365          </para>
00366       </description>
00367    </function> 
00368    <function name="POP" language="en_US">
00369       <synopsis>
00370          Removes and returns the last item off of a variable containing delimited text
00371       </synopsis>
00372       <syntax>
00373          <parameter name="varname" required="true" />
00374          <parameter name="delimiter" required="false" default="," />
00375       </syntax>
00376       <description>
00377          <para>Example:</para>
00378          <para>exten => s,1,Set(array=one,two,three)</para>
00379          <para>exten => s,n,While($["${SET(var=${POP(array)})}" != ""])</para>
00380          <para>exten => s,n,NoOp(var is ${var})</para>
00381          <para>exten => s,n,EndWhile</para>
00382          <para>This would iterate over each value in array, right to left, and
00383             would result in NoOp(var is three), NoOp(var is two), and
00384             NoOp(var is one) being executed.
00385          </para>
00386       </description>
00387    </function> 
00388    <function name="PUSH" language="en_US">
00389       <synopsis>
00390          Appends one or more values to the end of a variable containing delimited text
00391       </synopsis>
00392       <syntax>
00393          <parameter name="varname" required="true" />
00394          <parameter name="delimiter" required="false" default="," />
00395       </syntax>
00396       <description>
00397          <para>Example: Set(PUSH(array)=one,two,three) would append one,
00398             two, and three to the end of the values stored in the variable
00399             "array".
00400          </para>
00401       </description>
00402    </function>
00403    <function name="UNSHIFT" language="en_US">
00404       <synopsis>
00405          Inserts one or more values to the beginning of a variable containing delimited text
00406       </synopsis>
00407       <syntax>
00408          <parameter name="varname" required="true" />
00409          <parameter name="delimiter" required="false" default="," />
00410       </syntax>
00411       <description>
00412          <para>Example: Set(UNSHIFT(array)=one,two,three) would insert one,
00413             two, and three before the values stored in the variable
00414             "array".
00415          </para>
00416       </description>
00417    </function>
00418  ***/
00419 
00420 static int function_fieldqty_helper(struct ast_channel *chan, const char *cmd,
00421               char *parse, char *buf, struct ast_str **sbuf, ssize_t len)
00422 {
00423    char *varsubst;
00424    struct ast_str *str = ast_str_thread_get(&result_buf, 16);
00425    int fieldcount = 0;
00426    AST_DECLARE_APP_ARGS(args,
00427               AST_APP_ARG(varname);
00428               AST_APP_ARG(delim);
00429       );
00430    char delim[2] = "";
00431    size_t delim_used;
00432 
00433    if (!str) {
00434       return -1;
00435    }
00436 
00437    AST_STANDARD_APP_ARGS(args, parse);
00438    if (args.delim) {
00439       ast_get_encoded_char(args.delim, delim, &delim_used);
00440 
00441       varsubst = alloca(strlen(args.varname) + 4);
00442 
00443       sprintf(varsubst, "${%s}", args.varname);
00444       ast_str_substitute_variables(&str, 0, chan, varsubst);
00445       if (ast_str_strlen(str) == 0) {
00446          fieldcount = 0;
00447       } else {
00448          char *varval = ast_str_buffer(str);
00449          while (strsep(&varval, delim)) {
00450             fieldcount++;
00451          }
00452       }
00453    } else {
00454       fieldcount = 1;
00455    }
00456    if (sbuf) {
00457       ast_str_set(sbuf, len, "%d", fieldcount);
00458    } else {
00459       snprintf(buf, len, "%d", fieldcount);
00460    }
00461 
00462    return 0;
00463 }
00464 
00465 static int function_fieldqty(struct ast_channel *chan, const char *cmd,
00466               char *parse, char *buf, size_t len)
00467 {
00468    return function_fieldqty_helper(chan, cmd, parse, buf, NULL, len);
00469 }
00470 
00471 static int function_fieldqty_str(struct ast_channel *chan, const char *cmd,
00472              char *parse, struct ast_str **buf, ssize_t len)
00473 {
00474    return function_fieldqty_helper(chan, cmd, parse, NULL, buf, len);
00475 }
00476 
00477 static struct ast_custom_function fieldqty_function = {
00478    .name = "FIELDQTY",
00479    .read = function_fieldqty,
00480    .read2 = function_fieldqty_str,
00481 };
00482 
00483 static int function_fieldnum_helper(struct ast_channel *chan, const char *cmd,
00484             char *parse, char *buf, struct ast_str **sbuf, ssize_t len)
00485 {
00486    char *varsubst, *field;
00487    struct ast_str *str = ast_str_thread_get(&result_buf, 16);
00488    int fieldindex = 0, res = 0;
00489    AST_DECLARE_APP_ARGS(args,
00490       AST_APP_ARG(varname);
00491       AST_APP_ARG(delim);
00492       AST_APP_ARG(field);
00493    );
00494    char delim[2] = "";
00495    size_t delim_used;
00496 
00497    if (!str) {
00498       return -1;
00499    }
00500 
00501    AST_STANDARD_APP_ARGS(args, parse);
00502 
00503    if (args.argc < 3) {
00504       ast_log(LOG_ERROR, "Usage: FIELDNUM(<listname>,<delimiter>,<fieldvalue>)\n");
00505       res = -1;
00506    } else {
00507       varsubst = alloca(strlen(args.varname) + 4);
00508       sprintf(varsubst, "${%s}", args.varname);
00509 
00510       ast_str_substitute_variables(&str, 0, chan, varsubst);
00511 
00512       if (ast_str_strlen(str) == 0 || ast_strlen_zero(args.delim)) {
00513          fieldindex = 0;
00514       } else if (ast_get_encoded_char(args.delim, delim, &delim_used) == -1) {
00515          res = -1;
00516       } else {
00517          char *varval = ast_str_buffer(str);
00518 
00519          while ((field = strsep(&varval, delim)) != NULL) {
00520             fieldindex++;
00521 
00522             if (!strcasecmp(field, args.field)) {
00523                break;
00524             }
00525          }
00526 
00527          if (!field) {
00528             fieldindex = 0;
00529          }
00530 
00531          res = 0;
00532       }
00533    }
00534 
00535    if (sbuf) {
00536       ast_str_set(sbuf, len, "%d", fieldindex);
00537    } else {
00538       snprintf(buf, len, "%d", fieldindex);
00539    }
00540 
00541    return res;
00542 }
00543 
00544 static int function_fieldnum(struct ast_channel *chan, const char *cmd,
00545               char *parse, char *buf, size_t len)
00546 {
00547    return function_fieldnum_helper(chan, cmd, parse, buf, NULL, len);
00548 }
00549 
00550 static int function_fieldnum_str(struct ast_channel *chan, const char *cmd,
00551              char *parse, struct ast_str **buf, ssize_t len)
00552 {
00553    return function_fieldnum_helper(chan, cmd, parse, NULL, buf, len);
00554 }
00555 
00556 static struct ast_custom_function fieldnum_function = {
00557    .name = "FIELDNUM",
00558    .read = function_fieldnum,
00559    .read2 = function_fieldnum_str,
00560 };
00561 
00562 static int listfilter(struct ast_channel *chan, const char *cmd, char *parse, char *buf, struct ast_str **bufstr, ssize_t len)
00563 {
00564    AST_DECLARE_APP_ARGS(args,
00565       AST_APP_ARG(listname);
00566       AST_APP_ARG(delimiter);
00567       AST_APP_ARG(fieldvalue);
00568    );
00569    const char *ptr;
00570    struct ast_str *orig_list = ast_str_thread_get(&tmp_buf, 16);
00571    const char *begin, *cur, *next;
00572    int dlen, flen, first = 1;
00573    struct ast_str *result, **result_ptr = &result;
00574    char *delim, *varsubst;
00575 
00576    AST_STANDARD_APP_ARGS(args, parse);
00577 
00578    if (buf) {
00579       if (!(result = ast_str_thread_get(&result_buf, 16))) {
00580          return -1;
00581       }
00582    } else {
00583       /* Place the result directly into the output buffer */
00584       result_ptr = bufstr;
00585    }
00586 
00587    if (args.argc < 3) {
00588       ast_log(LOG_ERROR, "Usage: LISTFILTER(<listname>,<delimiter>,<fieldvalue>)\n");
00589       return -1;
00590    }
00591 
00592    varsubst = alloca(strlen(args.listname) + 4);
00593    sprintf(varsubst, "${%s}", args.listname);
00594 
00595    /* If we don't lock the channel, the variable could disappear out from underneath us. */
00596    if (chan) {
00597       ast_channel_lock(chan);
00598    }
00599    ast_str_substitute_variables(&orig_list, 0, chan, varsubst);
00600    if (!ast_str_strlen(orig_list)) {
00601       ast_log(LOG_ERROR, "List variable '%s' not found\n", args.listname);
00602       if (chan) {
00603          ast_channel_unlock(chan);
00604       }
00605       return -1;
00606    }
00607 
00608    /* If the string isn't there, just copy out the string and be done with it. */
00609    if (!(ptr = strstr(ast_str_buffer(orig_list), args.fieldvalue))) {
00610       if (buf) {
00611          ast_copy_string(buf, ast_str_buffer(orig_list), len);
00612       } else {
00613          ast_str_set(result_ptr, len, "%s", ast_str_buffer(orig_list));
00614       }
00615       if (chan) {
00616          ast_channel_unlock(chan);
00617       }
00618       return 0;
00619    }
00620 
00621    dlen = strlen(args.delimiter);
00622    delim = alloca(dlen + 1);
00623    ast_get_encoded_str(args.delimiter, delim, dlen + 1);
00624 
00625    if ((dlen = strlen(delim)) == 0) {
00626       delim = ",";
00627       dlen = 1;
00628    }
00629 
00630    flen = strlen(args.fieldvalue);
00631 
00632    ast_str_reset(*result_ptr);
00633    /* Enough space for any result */
00634    if (len > -1) {
00635       ast_str_make_space(result_ptr, len ? len : ast_str_strlen(orig_list) + 1);
00636    }
00637 
00638    begin = ast_str_buffer(orig_list);
00639    next = strstr(begin, delim);
00640 
00641    do {
00642       /* Find next boundary */
00643       if (next) {
00644          cur = next;
00645          next = strstr(cur + dlen, delim);
00646       } else {
00647          cur = strchr(begin + dlen, '\0');
00648       }
00649 
00650       if (flen == cur - begin && !strncmp(begin, args.fieldvalue, flen)) {
00651          /* Skip field */
00652          begin += flen + dlen;
00653       } else {
00654          /* Copy field to output */
00655          if (!first) {
00656             ast_str_append(result_ptr, len, "%s", delim);
00657          }
00658 
00659          ast_str_append_substr(result_ptr, len, begin, cur - begin);
00660          first = 0;
00661          begin = cur + dlen;
00662       }
00663    } while (*cur != '\0');
00664    if (chan) {
00665       ast_channel_unlock(chan);
00666    }
00667 
00668    if (buf) {
00669       ast_copy_string(buf, ast_str_buffer(result), len);
00670    }
00671 
00672    return 0;
00673 }
00674 
00675 static int listfilter_read(struct ast_channel *chan, const char *cmd, char *parse, char *buf, size_t len)
00676 {
00677    return listfilter(chan, cmd, parse, buf, NULL, len);
00678 }
00679 
00680 static int listfilter_read2(struct ast_channel *chan, const char *cmd, char *parse, struct ast_str **buf, ssize_t len)
00681 {
00682    return listfilter(chan, cmd, parse, NULL, buf, len);
00683 }
00684 
00685 static struct ast_custom_function listfilter_function = {
00686    .name = "LISTFILTER",
00687    .read = listfilter_read,
00688    .read2 = listfilter_read2,
00689 };
00690 
00691 static int filter(struct ast_channel *chan, const char *cmd, char *parse, char *buf,
00692         size_t len)
00693 {
00694    AST_DECLARE_APP_ARGS(args,
00695               AST_APP_ARG(allowed);
00696               AST_APP_ARG(string);
00697    );
00698    char *outbuf = buf;
00699    unsigned char ac;
00700    char allowed[256] = "";
00701    size_t allowedlen = 0;
00702    int32_t bitfield[8] = { 0, }; /* 256 bits */
00703 
00704    AST_STANDARD_RAW_ARGS(args, parse);
00705 
00706    if (!args.string) {
00707       ast_log(LOG_ERROR, "Usage: FILTER(<allowed-chars>,<string>)\n");
00708       return -1;
00709    }
00710 
00711    if (args.allowed[0] == '"' && !ast_opt_dont_warn) {
00712       ast_log(LOG_WARNING, "FILTER allowed characters includes the quote (\") character.  This may not be what you want.\n");
00713    }
00714 
00715    /* Expand ranges */
00716    for (; *(args.allowed);) {
00717       char c1 = 0, c2 = 0;
00718       size_t consumed = 0;
00719 
00720       if (ast_get_encoded_char(args.allowed, &c1, &consumed))
00721          return -1;
00722       args.allowed += consumed;
00723 
00724       if (*(args.allowed) == '-') {
00725          if (ast_get_encoded_char(args.allowed + 1, &c2, &consumed))
00726             c2 = c1;
00727          args.allowed += consumed + 1;
00728 
00729          if ((unsigned char) c2 < (unsigned char) c1 && !ast_opt_dont_warn) {
00730             ast_log(LOG_WARNING, "Range wrapping in FILTER(%s,%s).  This may not be what you want.\n", parse, args.string);
00731          }
00732 
00733          /*!\note
00734           * Looks a little strange, until you realize that we can overflow
00735           * the size of a char.
00736           */
00737          for (ac = (unsigned char) c1; ac != (unsigned char) c2; ac++) {
00738             bitfield[ac / 32] |= 1 << (ac % 32);
00739          }
00740          bitfield[ac / 32] |= 1 << (ac % 32);
00741 
00742          ast_debug(4, "c1=%d, c2=%d\n", c1, c2);
00743       } else {
00744          ac = (unsigned char) c1;
00745          ast_debug(4, "c1=%d, consumed=%d, args.allowed=%s\n", c1, (int) consumed, args.allowed - consumed);
00746          bitfield[ac / 32] |= 1 << (ac % 32);
00747       }
00748    }
00749 
00750    for (ac = 1; ac != 0; ac++) {
00751       if (bitfield[ac / 32] & (1 << (ac % 32))) {
00752          allowed[allowedlen++] = ac;
00753       }
00754    }
00755 
00756    ast_debug(1, "Allowed: %s\n", allowed);
00757 
00758    for (; *(args.string) && (buf + len - 1 > outbuf); (args.string)++) {
00759       if (strchr(allowed, *(args.string)))
00760          *outbuf++ = *(args.string);
00761    }
00762    *outbuf = '\0';
00763 
00764    return 0;
00765 }
00766 
00767 static struct ast_custom_function filter_function = {
00768    .name = "FILTER",
00769    .read = filter,
00770 };
00771 
00772 static int replace(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
00773 {
00774    AST_DECLARE_APP_ARGS(args,
00775       AST_APP_ARG(varname);
00776       AST_APP_ARG(find);
00777       AST_APP_ARG(replace);
00778    );
00779    char *strptr, *varsubst;
00780    struct ast_str *str = ast_str_thread_get(&result_buf, 16);
00781    char find[256]; /* Only 256 characters possible */
00782    char replace[2] = "";
00783    size_t unused;
00784 
00785    AST_STANDARD_APP_ARGS(args, data);
00786 
00787    if (!str) {
00788       return -1;
00789    }
00790 
00791    if (args.argc < 2) {
00792       ast_log(LOG_ERROR, "Usage: %s(<varname>,<search-chars>[,<replace-char>])\n", cmd);
00793       return -1;
00794    }
00795 
00796    /* Decode escapes */
00797    ast_get_encoded_str(args.find, find, sizeof(find));
00798    ast_get_encoded_char(args.replace, replace, &unused);
00799 
00800    if (ast_strlen_zero(find) || ast_strlen_zero(args.varname)) {
00801       ast_log(LOG_ERROR, "The characters to search for and the variable name must not be empty.\n");
00802       return -1;
00803    }
00804 
00805    varsubst = alloca(strlen(args.varname) + 4);
00806    sprintf(varsubst, "${%s}", args.varname);
00807    ast_str_substitute_variables(&str, 0, chan, varsubst);
00808 
00809    if (!ast_str_strlen(str)) {
00810       /* Blank, nothing to replace */
00811       return -1;
00812    }
00813 
00814    ast_debug(3, "String to search: (%s)\n", ast_str_buffer(str));
00815    ast_debug(3, "Characters to find: (%s)\n", find);
00816    ast_debug(3, "Character to replace with: (%s)\n", replace);
00817 
00818    for (strptr = ast_str_buffer(str); *strptr; strptr++) {
00819       /* buf is already a mutable buffer, so we construct the result
00820        * directly there */
00821       if (strchr(find, *strptr)) {
00822          if (ast_strlen_zero(replace)) {
00823             /* Remove character */
00824             strcpy(strptr, strptr + 1); /* SAFE */
00825             strptr--;
00826          } else {
00827             /* Replace character */
00828             *strptr = *replace;
00829          }
00830       }
00831    }
00832 
00833    ast_str_set(buf, len, "%s", ast_str_buffer(str));
00834    return 0;
00835 }
00836 
00837 static struct ast_custom_function replace_function = {
00838    .name = "REPLACE",
00839    .read2 = replace,
00840 };
00841 
00842 static int regex(struct ast_channel *chan, const char *cmd, char *parse, char *buf,
00843        size_t len)
00844 {
00845    AST_DECLARE_APP_ARGS(args,
00846               AST_APP_ARG(null);
00847               AST_APP_ARG(reg);
00848               AST_APP_ARG(str);
00849    );
00850    int errcode;
00851    regex_t regexbuf;
00852 
00853    buf[0] = '\0';
00854 
00855    AST_NONSTANDARD_APP_ARGS(args, parse, '"');
00856 
00857    if (args.argc != 3) {
00858       ast_log(LOG_ERROR, "Unexpected arguments: should have been in the form '\"<regex>\" <string>'\n");
00859       return -1;
00860    }
00861    if ((*args.str == ' ') || (*args.str == '\t'))
00862       args.str++;
00863 
00864    ast_debug(1, "FUNCTION REGEX (%s)(%s)\n", args.reg, args.str);
00865 
00866    if ((errcode = regcomp(&regexbuf, args.reg, REG_EXTENDED | REG_NOSUB))) {
00867       regerror(errcode, &regexbuf, buf, len);
00868       ast_log(LOG_WARNING, "Malformed input %s(%s): %s\n", cmd, parse, buf);
00869       return -1;
00870    }
00871    
00872    strcpy(buf, regexec(&regexbuf, args.str, 0, NULL, 0) ? "0" : "1");
00873 
00874    regfree(&regexbuf);
00875 
00876    return 0;
00877 }
00878 
00879 static struct ast_custom_function regex_function = {
00880    .name = "REGEX",
00881    .read = regex,
00882 };
00883 
00884 #define HASH_PREFIX  "~HASH~%s~"
00885 #define HASH_FORMAT  HASH_PREFIX "%s~"
00886 
00887 static char *app_clearhash = "ClearHash";
00888 
00889 /* This function probably should migrate to main/pbx.c, as pbx_builtin_clearvar_prefix() */
00890 static void clearvar_prefix(struct ast_channel *chan, const char *prefix)
00891 {
00892    struct ast_var_t *var;
00893    int len = strlen(prefix);
00894    AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->varshead, var, entries) {
00895       if (strncasecmp(prefix, ast_var_name(var), len) == 0) {
00896          AST_LIST_REMOVE_CURRENT(entries);
00897          ast_free(var);
00898       }
00899    }
00900    AST_LIST_TRAVERSE_SAFE_END
00901 }
00902 
00903 static int exec_clearhash(struct ast_channel *chan, const char *data)
00904 {
00905    char prefix[80];
00906    snprintf(prefix, sizeof(prefix), HASH_PREFIX, data ? (char *)data : "null");
00907    clearvar_prefix(chan, prefix);
00908    return 0;
00909 }
00910 
00911 static int array(struct ast_channel *chan, const char *cmd, char *var,
00912        const char *value)
00913 {
00914    AST_DECLARE_APP_ARGS(arg1,
00915               AST_APP_ARG(var)[100];
00916    );
00917    AST_DECLARE_APP_ARGS(arg2,
00918               AST_APP_ARG(val)[100];
00919    );
00920    char *origvar = "", *value2, varname[256];
00921    int i, ishash = 0;
00922 
00923    value2 = ast_strdupa(value);
00924    if (!var || !value2)
00925       return -1;
00926 
00927    if (!strcmp(cmd, "HASH")) {
00928       const char *var2 = pbx_builtin_getvar_helper(chan, "~ODBCFIELDS~");
00929       origvar = var;
00930       if (var2)
00931          var = ast_strdupa(var2);
00932       else {
00933          if (chan)
00934             ast_autoservice_stop(chan);
00935          return -1;
00936       }
00937       ishash = 1;
00938    }
00939 
00940    /* The functions this will generally be used with are SORT and ODBC_*, which
00941     * both return comma-delimited lists.  However, if somebody uses literal lists,
00942     * their commas will be translated to vertical bars by the load, and I don't
00943     * want them to be surprised by the result.  Hence, we prefer commas as the
00944     * delimiter, but we'll fall back to vertical bars if commas aren't found.
00945     */
00946    ast_debug(1, "array (%s=%s)\n", var, S_OR(value2, ""));
00947    AST_STANDARD_APP_ARGS(arg1, var);
00948 
00949    AST_STANDARD_APP_ARGS(arg2, value2);
00950 
00951    for (i = 0; i < arg1.argc; i++) {
00952       ast_debug(1, "array set value (%s=%s)\n", arg1.var[i],
00953             S_OR(arg2.val[i], ""));
00954       if (i < arg2.argc) {
00955          if (ishash) {
00956             if (origvar[0] == '_') {
00957                if (origvar[1] == '_') {
00958                   snprintf(varname, sizeof(varname), "__" HASH_FORMAT, origvar + 2, arg1.var[i]);
00959                } else {
00960                   snprintf(varname, sizeof(varname), "_" HASH_FORMAT, origvar + 1, arg1.var[i]);
00961                }
00962             } else {
00963                snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]);
00964             }
00965 
00966             pbx_builtin_setvar_helper(chan, varname, arg2.val[i]);
00967          } else {
00968             pbx_builtin_setvar_helper(chan, arg1.var[i], arg2.val[i]);
00969          }
00970       } else {
00971          /* We could unset the variable, by passing a NULL, but due to
00972           * pushvar semantics, that could create some undesired behavior. */
00973          if (ishash) {
00974             snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]);
00975             pbx_builtin_setvar_helper(chan, varname, "");
00976          } else {
00977             pbx_builtin_setvar_helper(chan, arg1.var[i], "");
00978          }
00979       }
00980    }
00981 
00982    return 0;
00983 }
00984 
00985 static int hashkeys_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00986 {
00987    struct ast_var_t *newvar;
00988    struct ast_str *prefix = ast_str_alloca(80);
00989 
00990    ast_str_set(&prefix, -1, HASH_PREFIX, data);
00991    memset(buf, 0, len);
00992 
00993    AST_LIST_TRAVERSE(&chan->varshead, newvar, entries) {
00994       if (strncasecmp(ast_str_buffer(prefix), ast_var_name(newvar), ast_str_strlen(prefix)) == 0) {
00995          /* Copy everything after the prefix */
00996          strncat(buf, ast_var_name(newvar) + ast_str_strlen(prefix), len - strlen(buf) - 1);
00997          /* Trim the trailing ~ */
00998          buf[strlen(buf) - 1] = ',';
00999       }
01000    }
01001    /* Trim the trailing comma */
01002    buf[strlen(buf) - 1] = '\0';
01003    return 0;
01004 }
01005 
01006 static int hashkeys_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
01007 {
01008    struct ast_var_t *newvar;
01009    struct ast_str *prefix = ast_str_alloca(80);
01010    char *tmp;
01011 
01012    ast_str_set(&prefix, -1, HASH_PREFIX, data);
01013 
01014    AST_LIST_TRAVERSE(&chan->varshead, newvar, entries) {
01015       if (strncasecmp(ast_str_buffer(prefix), ast_var_name(newvar), ast_str_strlen(prefix)) == 0) {
01016          /* Copy everything after the prefix */
01017          ast_str_append(buf, len, "%s", ast_var_name(newvar) + ast_str_strlen(prefix));
01018          /* Trim the trailing ~ */
01019          tmp = ast_str_buffer(*buf);
01020          tmp[ast_str_strlen(*buf) - 1] = ',';
01021       }
01022    }
01023    /* Trim the trailing comma */
01024    tmp = ast_str_buffer(*buf);
01025    tmp[ast_str_strlen(*buf) - 1] = '\0';
01026    return 0;
01027 }
01028 
01029 static int hash_write(struct ast_channel *chan, const char *cmd, char *var, const char *value)
01030 {
01031    char varname[256];
01032    AST_DECLARE_APP_ARGS(arg,
01033       AST_APP_ARG(hashname);
01034       AST_APP_ARG(hashkey);
01035    );
01036 
01037    if (!strchr(var, ',')) {
01038       /* Single argument version */
01039       return array(chan, "HASH", var, value);
01040    }
01041 
01042    AST_STANDARD_APP_ARGS(arg, var);
01043    if (arg.hashname[0] == '_') {
01044       if (arg.hashname[1] == '_') {
01045          snprintf(varname, sizeof(varname), "__" HASH_FORMAT, arg.hashname + 2, arg.hashkey);
01046       } else {
01047          snprintf(varname, sizeof(varname), "_" HASH_FORMAT, arg.hashname + 1, arg.hashkey);
01048       }
01049    } else {
01050       snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey);
01051    }
01052    pbx_builtin_setvar_helper(chan, varname, value);
01053 
01054    return 0;
01055 }
01056 
01057 static int hash_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
01058 {
01059    char varname[256];
01060    const char *varvalue;
01061    AST_DECLARE_APP_ARGS(arg,
01062       AST_APP_ARG(hashname);
01063       AST_APP_ARG(hashkey);
01064    );
01065 
01066    AST_STANDARD_APP_ARGS(arg, data);
01067    if (arg.argc == 2) {
01068       snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey);
01069       varvalue = pbx_builtin_getvar_helper(chan, varname);
01070       if (varvalue)
01071          ast_copy_string(buf, varvalue, len);
01072       else
01073          *buf = '\0';
01074    } else if (arg.argc == 1) {
01075       char colnames[4096];
01076       int i;
01077       AST_DECLARE_APP_ARGS(arg2,
01078          AST_APP_ARG(col)[100];
01079       );
01080 
01081       /* Get column names, in no particular order */
01082       hashkeys_read(chan, "HASHKEYS", arg.hashname, colnames, sizeof(colnames));
01083       pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", colnames);
01084 
01085       AST_STANDARD_APP_ARGS(arg2, colnames);
01086       *buf = '\0';
01087 
01088       /* Now get the corresponding column values, in exactly the same order */
01089       for (i = 0; i < arg2.argc; i++) {
01090          snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg2.col[i]);
01091          varvalue = pbx_builtin_getvar_helper(chan, varname);
01092          strncat(buf, varvalue, len - strlen(buf) - 1);
01093          strncat(buf, ",", len - strlen(buf) - 1);
01094       }
01095 
01096       /* Strip trailing comma */
01097       buf[strlen(buf) - 1] = '\0';
01098    }
01099 
01100    return 0;
01101 }
01102 
01103 static struct ast_custom_function hash_function = {
01104    .name = "HASH",
01105    .write = hash_write,
01106    .read = hash_read,
01107 };
01108 
01109 static struct ast_custom_function hashkeys_function = {
01110    .name = "HASHKEYS",
01111    .read = hashkeys_read,
01112    .read2 = hashkeys_read2,
01113 };
01114 
01115 static struct ast_custom_function array_function = {
01116    .name = "ARRAY",
01117    .write = array,
01118 };
01119 
01120 static int quote(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
01121 {
01122    char *bufptr = buf, *dataptr = data;
01123 
01124    if (len < 3){ /* at least two for quotes and one for binary zero */
01125       ast_log(LOG_ERROR, "Not enough buffer");
01126       return -1;
01127    }
01128 
01129    if (ast_strlen_zero(data)) {
01130       ast_log(LOG_WARNING, "No argument specified!\n");
01131       ast_copy_string(buf, "\"\"", len);
01132       return 0;
01133    }
01134 
01135    *bufptr++ = '"';
01136    for (; bufptr < buf + len - 3; dataptr++) {
01137       if (*dataptr == '\\') {
01138          *bufptr++ = '\\';
01139          *bufptr++ = '\\';
01140       } else if (*dataptr == '"') {
01141          *bufptr++ = '\\';
01142          *bufptr++ = '"';
01143       } else if (*dataptr == '\0') {
01144          break;
01145       } else {
01146          *bufptr++ = *dataptr;
01147       }
01148    }
01149    *bufptr++ = '"';
01150    *bufptr = '\0';
01151    return 0;
01152 }
01153 
01154 static struct ast_custom_function quote_function = {
01155    .name = "QUOTE",
01156    .read = quote,
01157 };
01158 
01159 static int csv_quote(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
01160 {
01161    char *bufptr = buf, *dataptr = data;
01162 
01163    if (len < 3) { /* at least two for quotes and one for binary zero */
01164       ast_log(LOG_ERROR, "Not enough buffer");
01165       return -1;
01166    }
01167 
01168    if (ast_strlen_zero(data)) {
01169       ast_copy_string(buf, "\"\"", len);
01170       return 0;
01171    }
01172 
01173    *bufptr++ = '"';
01174    for (; bufptr < buf + len - 3; dataptr++){
01175       if (*dataptr == '"') {
01176          *bufptr++ = '"';
01177          *bufptr++ = '"';
01178       } else if (*dataptr == '\0') {
01179          break;
01180       } else {
01181          *bufptr++ = *dataptr;
01182       }
01183    }
01184    *bufptr++ = '"';
01185    *bufptr='\0';
01186    return 0;
01187 }
01188 
01189 static struct ast_custom_function csv_quote_function = {
01190    .name = "CSV_QUOTE",
01191    .read = csv_quote,
01192 };
01193 
01194 static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
01195 {
01196    int length = 0;
01197 
01198    if (data)
01199       length = strlen(data);
01200 
01201    snprintf(buf, buflen, "%d", length);
01202 
01203    return 0;
01204 }
01205 
01206 static struct ast_custom_function len_function = {
01207    .name = "LEN",
01208    .read = len,
01209    .read_max = 12,
01210 };
01211 
01212 static int acf_strftime(struct ast_channel *chan, const char *cmd, char *parse,
01213          char *buf, size_t buflen)
01214 {
01215    AST_DECLARE_APP_ARGS(args,
01216               AST_APP_ARG(epoch);
01217               AST_APP_ARG(timezone);
01218               AST_APP_ARG(format);
01219    );
01220    struct timeval when;
01221    struct ast_tm tm;
01222 
01223    buf[0] = '\0';
01224 
01225    AST_STANDARD_APP_ARGS(args, parse);
01226 
01227    ast_get_timeval(args.epoch, &when, ast_tvnow(), NULL);
01228    ast_localtime(&when, &tm, args.timezone);
01229 
01230    if (!args.format)
01231       args.format = "%c";
01232 
01233    if (ast_strftime(buf, buflen, args.format, &tm) <= 0)
01234       ast_log(LOG_WARNING, "C function strftime() output nothing?!!\n");
01235 
01236    buf[buflen - 1] = '\0';
01237 
01238    return 0;
01239 }
01240 
01241 static struct ast_custom_function strftime_function = {
01242    .name = "STRFTIME",
01243    .read = acf_strftime,
01244 };
01245 
01246 static int acf_strptime(struct ast_channel *chan, const char *cmd, char *data,
01247          char *buf, size_t buflen)
01248 {
01249    AST_DECLARE_APP_ARGS(args,
01250               AST_APP_ARG(timestring);
01251               AST_APP_ARG(timezone);
01252               AST_APP_ARG(format);
01253    );
01254    struct ast_tm tm;
01255 
01256    buf[0] = '\0';
01257 
01258    if (!data) {
01259       ast_log(LOG_ERROR,
01260             "Asterisk function STRPTIME() requires an argument.\n");
01261       return -1;
01262    }
01263 
01264    AST_STANDARD_APP_ARGS(args, data);
01265 
01266    if (ast_strlen_zero(args.format)) {
01267       ast_log(LOG_ERROR,
01268             "No format supplied to STRPTIME(<timestring>,<timezone>,<format>)");
01269       return -1;
01270    }
01271 
01272    if (!ast_strptime(args.timestring, args.format, &tm)) {
01273       ast_log(LOG_WARNING, "STRPTIME() found no time specified within the string\n");
01274    } else {
01275       struct timeval when;
01276       when = ast_mktime(&tm, args.timezone);
01277       snprintf(buf, buflen, "%d", (int) when.tv_sec);
01278    }
01279 
01280    return 0;
01281 }
01282 
01283 static struct ast_custom_function strptime_function = {
01284    .name = "STRPTIME",
01285    .read = acf_strptime,
01286 };
01287 
01288 static int function_eval(struct ast_channel *chan, const char *cmd, char *data,
01289           char *buf, size_t buflen)
01290 {
01291    if (ast_strlen_zero(data)) {
01292       ast_log(LOG_WARNING, "EVAL requires an argument: EVAL(<string>)\n");
01293       return -1;
01294    }
01295 
01296    pbx_substitute_variables_helper(chan, data, buf, buflen - 1);
01297 
01298    return 0;
01299 }
01300 
01301 static int function_eval2(struct ast_channel *chan, const char *cmd, char *data,
01302           struct ast_str **buf, ssize_t buflen)
01303 {
01304    if (ast_strlen_zero(data)) {
01305       ast_log(LOG_WARNING, "EVAL requires an argument: EVAL(<string>)\n");
01306       return -1;
01307    }
01308 
01309    ast_str_substitute_variables(buf, buflen, chan, data);
01310 
01311    return 0;
01312 }
01313 
01314 static struct ast_custom_function eval_function = {
01315    .name = "EVAL",
01316    .read = function_eval,
01317    .read2 = function_eval2,
01318 };
01319 
01320 static int keypadhash(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
01321 {
01322    char *bufptr, *dataptr;
01323 
01324    for (bufptr = buf, dataptr = data; bufptr < buf + buflen - 1; dataptr++) {
01325       if (*dataptr == '\0') {
01326          *bufptr++ = '\0';
01327          break;
01328       } else if (*dataptr == '1') {
01329          *bufptr++ = '1';
01330       } else if (strchr("AaBbCc2", *dataptr)) {
01331          *bufptr++ = '2';
01332       } else if (strchr("DdEeFf3", *dataptr)) {
01333          *bufptr++ = '3';
01334       } else if (strchr("GgHhIi4", *dataptr)) {
01335          *bufptr++ = '4';
01336       } else if (strchr("JjKkLl5", *dataptr)) {
01337          *bufptr++ = '5';
01338       } else if (strchr("MmNnOo6", *dataptr)) {
01339          *bufptr++ = '6';
01340       } else if (strchr("PpQqRrSs7", *dataptr)) {
01341          *bufptr++ = '7';
01342       } else if (strchr("TtUuVv8", *dataptr)) {
01343          *bufptr++ = '8';
01344       } else if (strchr("WwXxYyZz9", *dataptr)) {
01345          *bufptr++ = '9';
01346       } else if (*dataptr == '0') {
01347          *bufptr++ = '0';
01348       }
01349    }
01350    buf[buflen - 1] = '\0';
01351 
01352    return 0;
01353 }
01354 
01355 static struct ast_custom_function keypadhash_function = {
01356    .name = "KEYPADHASH",
01357    .read = keypadhash,
01358 };
01359 
01360 static int string_toupper(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
01361 {
01362    char *bufptr = buf, *dataptr = data;
01363 
01364    while ((bufptr < buf + buflen - 1) && (*bufptr++ = toupper(*dataptr++)));
01365 
01366    return 0;
01367 }
01368 
01369 static int string_toupper2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t buflen)
01370 {
01371    char *bufptr, *dataptr = data;
01372 
01373    if (buflen > -1) {
01374       ast_str_make_space(buf, buflen > 0 ? buflen : strlen(data) + 1);
01375    }
01376    bufptr = ast_str_buffer(*buf);
01377    while ((bufptr < ast_str_buffer(*buf) + ast_str_size(*buf) - 1) && (*bufptr++ = toupper(*dataptr++)));
01378    ast_str_update(*buf);
01379 
01380    return 0;
01381 }
01382 
01383 static struct ast_custom_function toupper_function = {
01384    .name = "TOUPPER",
01385    .read = string_toupper,
01386    .read2 = string_toupper2,
01387 };
01388 
01389 static int string_tolower(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
01390 {
01391    char *bufptr = buf, *dataptr = data;
01392 
01393    while ((bufptr < buf + buflen - 1) && (*bufptr++ = tolower(*dataptr++)));
01394 
01395    return 0;
01396 }
01397 
01398 static int string_tolower2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t buflen)
01399 {
01400    char *bufptr, *dataptr = data;
01401 
01402    if (buflen > -1) {
01403       ast_str_make_space(buf, buflen > 0 ? buflen : strlen(data) + 1);
01404    }
01405    bufptr = ast_str_buffer(*buf);
01406    while ((bufptr < ast_str_buffer(*buf) + ast_str_size(*buf) - 1) && (*bufptr++ = tolower(*dataptr++)));
01407    ast_str_update(*buf);
01408 
01409    return 0;
01410 }
01411 
01412 static struct ast_custom_function tolower_function = {
01413    .name = "TOLOWER",
01414    .read = string_tolower,
01415    .read2 = string_tolower2,
01416 };
01417 
01418 static int shift_pop(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
01419 {
01420 #define beginning (cmd[0] == 'S') /* SHIFT */
01421    char *after, delimiter[2] = ",", *varsubst;
01422    size_t unused;
01423    struct ast_str *before = ast_str_thread_get(&result_buf, 16);
01424    char *(*search_func)(const char *s, int c) = (beginning ? strchr : strrchr);
01425    AST_DECLARE_APP_ARGS(args,
01426       AST_APP_ARG(var);
01427       AST_APP_ARG(delimiter);
01428    );
01429 
01430    if (!before) {
01431       return -1;
01432    }
01433 
01434    AST_STANDARD_APP_ARGS(args, data);
01435 
01436    if (ast_strlen_zero(args.var)) {
01437       ast_log(LOG_WARNING, "%s requires a variable name\n", cmd);
01438       return -1;
01439    }
01440 
01441    varsubst = alloca(strlen(args.var) + 4);
01442    sprintf(varsubst, "${%s}", args.var);
01443    ast_str_substitute_variables(&before, 0, chan, varsubst);
01444 
01445    if (args.argc > 1 && !ast_strlen_zero(args.delimiter)) {
01446       ast_get_encoded_char(args.delimiter, delimiter, &unused);
01447    }
01448 
01449    if (!ast_str_strlen(before)) {
01450       /* Nothing to pop */
01451       return -1;
01452    }
01453 
01454    if (!(after = search_func(ast_str_buffer(before), delimiter[0]))) {
01455       /* Only one entry in array */
01456       ast_str_set(buf, len, "%s", ast_str_buffer(before));
01457       pbx_builtin_setvar_helper(chan, args.var, "");
01458    } else {
01459       *after++ = '\0';
01460       ast_str_set(buf, len, "%s", beginning ? ast_str_buffer(before) : after);
01461       pbx_builtin_setvar_helper(chan, args.var, beginning ? after : ast_str_buffer(before));
01462    }
01463 
01464    return 0;
01465 #undef beginning
01466 }
01467 
01468 static struct ast_custom_function shift_function = {
01469    .name = "SHIFT",
01470    .read2 = shift_pop,
01471 };
01472 
01473 static struct ast_custom_function pop_function = {
01474    .name = "POP",
01475    .read2 = shift_pop,
01476 };
01477 
01478 static int unshift_push(struct ast_channel *chan, const char *cmd, char *data, const char *new_value)
01479 {
01480 #define beginning (cmd[0] == 'U') /* UNSHIFT */
01481    char delimiter[2] = ",", *varsubst;
01482    size_t unused;
01483    struct ast_str *buf, *previous_value;
01484    AST_DECLARE_APP_ARGS(args,
01485       AST_APP_ARG(var);
01486       AST_APP_ARG(delimiter);
01487    );
01488 
01489    if (!(buf = ast_str_thread_get(&result_buf, 16)) ||
01490       !(previous_value = ast_str_thread_get(&tmp_buf, 16))) {
01491       return -1;
01492    }
01493 
01494    AST_STANDARD_APP_ARGS(args, data);
01495 
01496    if (ast_strlen_zero(args.var)) {
01497       ast_log(LOG_WARNING, "%s requires a variable name\n", cmd);
01498       return -1;
01499    }
01500 
01501    if (args.argc > 1 && !ast_strlen_zero(args.delimiter)) {
01502       ast_get_encoded_char(args.delimiter, delimiter, &unused);
01503    }
01504 
01505    varsubst = alloca(strlen(args.var) + 4);
01506    sprintf(varsubst, "${%s}", args.var);
01507    ast_str_substitute_variables(&previous_value, 0, chan, varsubst);
01508 
01509    if (!ast_str_strlen(previous_value)) {
01510       ast_str_set(&buf, 0, "%s", new_value);
01511    } else {
01512       ast_str_set(&buf, 0, "%s%c%s",
01513          beginning ? new_value : ast_str_buffer(previous_value),
01514          delimiter[0],
01515          beginning ? ast_str_buffer(previous_value) : new_value);
01516    }
01517 
01518    pbx_builtin_setvar_helper(chan, args.var, ast_str_buffer(buf));
01519 
01520    return 0;
01521 #undef beginning
01522 }
01523 
01524 static struct ast_custom_function push_function = {
01525    .name = "PUSH",
01526    .write = unshift_push,
01527 };
01528 
01529 static struct ast_custom_function unshift_function = {
01530    .name = "UNSHIFT",
01531    .write = unshift_push,
01532 };
01533 
01534 static int passthru(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
01535 {
01536    ast_str_set(buf, len, "%s", data);
01537    return 0;
01538 }
01539 
01540 static struct ast_custom_function passthru_function = {
01541    .name = "PASSTHRU",
01542    .read2 = passthru,
01543 };
01544 
01545 #ifdef TEST_FRAMEWORK
01546 AST_TEST_DEFINE(test_FIELDNUM)
01547 {
01548    int i, res = AST_TEST_PASS;
01549    struct ast_channel *chan;
01550    struct ast_str *str;
01551    char expression[256];
01552    struct {
01553       const char *fields;
01554       const char *delim;
01555       const char *field;
01556       const char *expected;
01557    } test_args[] = {
01558       {"abc,def,ghi,jkl", "\\,",     "ghi", "3"},
01559       {"abc def ghi jkl", " ",       "abc", "1"},
01560       {"abc/def/ghi/jkl", "\\\\x2f", "def", "2"},
01561       {"abc$def$ghi$jkl", "",        "ghi", "0"},
01562       {"abc,def,ghi,jkl", "-",       "",    "0"},
01563       {"abc-def-ghi-jkl", "-",       "mno", "0"}
01564    };
01565 
01566    switch (cmd) {
01567    case TEST_INIT:
01568       info->name = "func_FIELDNUM_test";
01569       info->category = "/funcs/func_strings/";
01570       info->summary = "Test FIELDNUM function";
01571       info->description = "Verify FIELDNUM behavior";
01572       return AST_TEST_NOT_RUN;
01573    case TEST_EXECUTE:
01574       break;
01575    }
01576 
01577    if (!(chan = ast_dummy_channel_alloc())) {
01578       ast_test_status_update(test, "Unable to allocate dummy channel\n");
01579       return AST_TEST_FAIL;
01580    }
01581 
01582    if (!(str = ast_str_create(16))) {
01583       ast_test_status_update(test, "Unable to allocate dynamic string buffer\n");
01584       ast_channel_release(chan);
01585       return AST_TEST_FAIL;
01586    }
01587 
01588    for (i = 0; i < ARRAY_LEN(test_args); i++) {
01589       struct ast_var_t *var = ast_var_assign("FIELDS", test_args[i].fields);
01590       AST_LIST_INSERT_HEAD(&chan->varshead, var, entries);
01591 
01592       snprintf(expression, sizeof(expression), "${FIELDNUM(%s,%s,%s)}", var->name, test_args[i].delim, test_args[i].field);
01593       ast_str_substitute_variables(&str, 0, chan, expression);
01594 
01595       AST_LIST_REMOVE(&chan->varshead, var, entries);
01596       ast_var_delete(var);
01597 
01598       if (strcasecmp(ast_str_buffer(str), test_args[i].expected)) {
01599          ast_test_status_update(test, "Evaluation of '%s' returned '%s' instead of the expected value '%s'\n",
01600             expression, ast_str_buffer(str), test_args[i].expected);
01601          res = AST_TEST_FAIL;
01602          break;
01603       }
01604    }
01605 
01606    ast_free(str);
01607    ast_channel_release(chan);
01608 
01609    return res;
01610 }
01611 
01612 AST_TEST_DEFINE(test_FILTER)
01613 {
01614    int i, res = AST_TEST_PASS;
01615    const char *test_strings[][2] = {
01616       {"A-R",            "DAHDI"},
01617       {"A\\-R",          "A"},
01618       {"\\x41-R",        "DAHDI"},
01619       {"0-9A-Ca-c",      "0042133333A12212"},
01620       {"0-9a-cA-C_+\\-", "0042133333A12212"},
01621       {NULL,             NULL},
01622    };
01623 
01624    switch (cmd) {
01625    case TEST_INIT:
01626       info->name = "func_FILTER_test";
01627       info->category = "/funcs/func_strings/";
01628       info->summary = "Test FILTER function";
01629       info->description = "Verify FILTER behavior";
01630       return AST_TEST_NOT_RUN;
01631    case TEST_EXECUTE:
01632       break;
01633    }
01634 
01635    for (i = 0; test_strings[i][0]; i++) {
01636       char tmp[256], tmp2[256] = "";
01637       snprintf(tmp, sizeof(tmp), "${FILTER(%s,0042133333&DAHDI/g1/2212)}", test_strings[i][0]);
01638       pbx_substitute_variables_helper(NULL, tmp, tmp2, sizeof(tmp2) - 1);
01639       if (strcmp(test_strings[i][1], tmp2)) {
01640          ast_test_status_update(test, "Format string '%s' substituted to '%s'.  Expected '%s'.\n", test_strings[i][0], tmp2, test_strings[i][1]);
01641          res = AST_TEST_FAIL;
01642       }
01643    }
01644    return res;
01645 }
01646 #endif
01647 
01648 static int unload_module(void)
01649 {
01650    int res = 0;
01651 
01652    AST_TEST_UNREGISTER(test_FIELDNUM);
01653    AST_TEST_UNREGISTER(test_FILTER);
01654    res |= ast_custom_function_unregister(&fieldqty_function);
01655    res |= ast_custom_function_unregister(&fieldnum_function);
01656    res |= ast_custom_function_unregister(&filter_function);
01657    res |= ast_custom_function_unregister(&replace_function);
01658    res |= ast_custom_function_unregister(&listfilter_function);
01659    res |= ast_custom_function_unregister(&regex_function);
01660    res |= ast_custom_function_unregister(&array_function);
01661    res |= ast_custom_function_unregister(&quote_function);
01662    res |= ast_custom_function_unregister(&csv_quote_function);
01663    res |= ast_custom_function_unregister(&len_function);
01664    res |= ast_custom_function_unregister(&strftime_function);
01665    res |= ast_custom_function_unregister(&strptime_function);
01666    res |= ast_custom_function_unregister(&eval_function);
01667    res |= ast_custom_function_unregister(&keypadhash_function);
01668    res |= ast_custom_function_unregister(&hashkeys_function);
01669    res |= ast_custom_function_unregister(&hash_function);
01670    res |= ast_unregister_application(app_clearhash);
01671    res |= ast_custom_function_unregister(&toupper_function);
01672    res |= ast_custom_function_unregister(&tolower_function);
01673    res |= ast_custom_function_unregister(&shift_function);
01674    res |= ast_custom_function_unregister(&pop_function);
01675    res |= ast_custom_function_unregister(&push_function);
01676    res |= ast_custom_function_unregister(&unshift_function);
01677    res |= ast_custom_function_unregister(&passthru_function);
01678 
01679    return res;
01680 }
01681 
01682 static int load_module(void)
01683 {
01684    int res = 0;
01685 
01686    AST_TEST_REGISTER(test_FIELDNUM);
01687    AST_TEST_REGISTER(test_FILTER);
01688    res |= ast_custom_function_register(&fieldqty_function);
01689    res |= ast_custom_function_register(&fieldnum_function);
01690    res |= ast_custom_function_register(&filter_function);
01691    res |= ast_custom_function_register(&replace_function);
01692    res |= ast_custom_function_register(&listfilter_function);
01693    res |= ast_custom_function_register(&regex_function);
01694    res |= ast_custom_function_register(&array_function);
01695    res |= ast_custom_function_register(&quote_function);
01696    res |= ast_custom_function_register(&csv_quote_function);
01697    res |= ast_custom_function_register(&len_function);
01698    res |= ast_custom_function_register(&strftime_function);
01699    res |= ast_custom_function_register(&strptime_function);
01700    res |= ast_custom_function_register(&eval_function);
01701    res |= ast_custom_function_register(&keypadhash_function);
01702    res |= ast_custom_function_register(&hashkeys_function);
01703    res |= ast_custom_function_register(&hash_function);
01704    res |= ast_register_application_xml(app_clearhash, exec_clearhash);
01705    res |= ast_custom_function_register(&toupper_function);
01706    res |= ast_custom_function_register(&tolower_function);
01707    res |= ast_custom_function_register(&shift_function);
01708    res |= ast_custom_function_register(&pop_function);
01709    res |= ast_custom_function_register(&push_function);
01710    res |= ast_custom_function_register(&unshift_function);
01711    res |= ast_custom_function_register(&passthru_function);
01712 
01713    return res;
01714 }
01715 
01716 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "String handling dialplan functions");

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