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

Generated on Sat Feb 11 06:33:16 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6