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

Generated on Thu Apr 16 06:27:36 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6