Adds STRREPLACE function
[asterisk/asterisk.git] / funcs / func_strings.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2005-2006, Digium, Inc.
5  * Portions Copyright (C) 2005, Tilghman Lesher.  All rights reserved.
6  * Portions Copyright (C) 2005, Anthony Minessale II
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief String manipulation dialplan functions
22  *
23  * \author Tilghman Lesher
24  * \author Anothony Minessale II 
25  * \ingroup functions
26  */
27
28 #include "asterisk.h"
29
30 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
31
32 #include <regex.h>
33 #include <ctype.h>
34
35 #include "asterisk/module.h"
36 #include "asterisk/channel.h"
37 #include "asterisk/pbx.h"
38 #include "asterisk/utils.h"
39 #include "asterisk/app.h"
40 #include "asterisk/localtime.h"
41 #include "asterisk/test.h"
42
43 AST_THREADSTORAGE(result_buf);
44 AST_THREADSTORAGE(tmp_buf);
45
46 /*** DOCUMENTATION
47         <function name="FIELDQTY" language="en_US">
48                 <synopsis>
49                         Count the fields with an arbitrary delimiter
50                 </synopsis>
51                 <syntax>
52                         <parameter name="varname" required="true" />
53                         <parameter name="delim" required="true" />
54                 </syntax>
55                 <description>
56                         <para>The delimiter may be specified as a special or extended ASCII character, by encoding it.  The characters
57                         <literal>\n</literal>, <literal>\r</literal>, and <literal>\t</literal> are all recognized as the newline,
58                         carriage return, and tab characters, respectively.  Also, octal and hexadecimal specifications are recognized
59                         by the patterns <literal>\0nnn</literal> and <literal>\xHH</literal>, respectively.  For example, if you wanted
60                         to encode a comma as the delimiter, you could use either <literal>\054</literal> or <literal>\x2C</literal>.</para>
61                         <para>Example: If ${example} contains <literal>ex-amp-le</literal>, then ${FIELDQTY(example,-)} returns 3.</para>
62                 </description>
63         </function>
64         <function name="FIELDNUM" language="en_US">
65                 <synopsis>
66                         Return the 1-based offset of a field in a list
67                 </synopsis>
68                 <syntax>
69                         <parameter name="varname" required="true" />
70                         <parameter name="delim" required="true" />
71                         <parameter name="value" required="true" />
72                 </syntax>
73                 <description>
74                         <para>Search the variable named <replaceable>varname</replaceable> for the string <replaceable>value</replaceable>
75                         delimited by <replaceable>delim</replaceable> and return a 1-based offset as to its location. If not found
76                         or an error occured, return <literal>0</literal>.</para>
77                         <para>The delimiter may be specified as a special or extended ASCII character, by encoding it.  The characters
78                         <literal>\n</literal>, <literal>\r</literal>, and <literal>\t</literal> are all recognized as the newline,
79                         carriage return, and tab characters, respectively.  Also, octal and hexadecimal specifications are recognized
80                         by the patterns <literal>\0nnn</literal> and <literal>\xHH</literal>, respectively.  For example, if you wanted
81                         to encode a comma as the delimiter, you could use either <literal>\054</literal> or <literal>\x2C</literal>.</para>
82                         <para>Example: If ${example} contains <literal>ex-amp-le</literal>, then ${FIELDNUM(example,-,amp)} returns 2.</para>
83                 </description>
84         </function>
85         <function name="LISTFILTER" language="en_US">
86                 <synopsis>Remove an item from a list, by name.</synopsis>
87                 <syntax>
88                         <parameter name="varname" required="true" />
89                         <parameter name="delim" required="true" default="," />
90                         <parameter name="value" required="true" />
91                 </syntax>
92                 <description>
93                         <para>Remove <replaceable>value</replaceable> from the list contained in the <replaceable>varname</replaceable>
94                         variable, where the list delimiter is specified by the <replaceable>delim</replaceable> parameter.  This is
95                         very useful for removing a single channel name from a list of channels, for example.</para>
96                 </description>
97         </function>
98         <function name="FILTER" language="en_US">
99                 <synopsis>
100                         Filter the string to include only the allowed characters
101                 </synopsis>
102                 <syntax>
103                         <parameter name="allowed-chars" required="true" />
104                         <parameter name="string" required="true" />
105                 </syntax>
106                 <description>
107                         <para>Permits all characters listed in <replaceable>allowed-chars</replaceable>, 
108                         filtering all others outs. In addition to literally listing the characters, 
109                         you may also use ranges of characters (delimited by a <literal>-</literal></para>
110                         <para>Hexadecimal characters started with a <literal>\x</literal>(i.e. \x20)</para>
111                         <para>Octal characters started with a <literal>\0</literal> (i.e. \040)</para>
112                         <para>Also <literal>\t</literal>,<literal>\n</literal> and <literal>\r</literal> are recognized.</para> 
113                         <note><para>If you want the <literal>-</literal> character it needs to be prefixed with a 
114                         <literal>\</literal></para></note>
115                 </description>
116         </function>
117         <function name="REPLACE" language="en_US">
118                 <synopsis>
119                         Replace a set of characters in a given string with another character.
120                 </synopsis>
121                 <syntax>
122                         <parameter name="varname" required="true" />
123                         <parameter name="find-chars" required="true" />
124                         <parameter name="replace-char" required="false" />
125                 </syntax>
126                 <description>
127                         <para>Iterates through a string replacing all the <replaceable>find-chars</replaceable> with
128                         <replaceable>replace-char</replaceable>.  <replaceable>replace-char</replaceable> may be either
129                         empty or contain one character.  If empty, all <replaceable>find-chars</replaceable> will be
130                         deleted from the output.</para>
131                         <note><para>The replacement only occurs in the output.  The original variable is not
132                         altered.</para></note>
133                 </description>
134         </function>
135         <function name="STRREPLACE" language="en_US">
136                 <synopsis>
137                         Replace instances of a substring within a string with another string.
138                 </synopsis>
139                 <syntax>
140                         <parameter name="varname" required="true" />
141                         <parameter name="find-string" required="true" />
142                         <parameter name="replace-string" required="false" />
143                         <parameter name="max-replacements" required="false" />
144                 </syntax>
145                 <description>
146                         <para>Searches for all instances of the <replaceable>find-string</replaceable> in provided variable and
147                         replaces them with <replaceable>replace-string</replaceable>.  If <replaceable>replace-string</replaceable>
148                         is an empty string, this will effecively delete that substring.  If <replaceable>max-replacements</replaceable>
149                         is specified, this function will stop after performing replacements <replaceable>max-replacements</replaceable> times.</para>
150                         <note><para>The replacement only occurs in the output.  The original variable is not altered.</para></note>
151                 </description>
152         </function>
153         <function name="PASSTHRU" language="en_US">
154                 <synopsis>
155                         Pass the given argument back as a value.
156                 </synopsis>
157                 <syntax>
158                         <parameter name="string" required="false" />
159                 </syntax>
160                 <description>
161                         <para>Literally returns the given <replaceable>string</replaceable>.  The intent is to permit
162                         other dialplan functions which take a variable name as an argument to be able to take a literal
163                         string, instead.</para>
164                 </description>
165         </function>
166         <function name="REGEX" language="en_US">
167                 <synopsis>
168                         Check string against a regular expression.
169                 </synopsis>
170                 <syntax argsep=" ">
171                         <parameter name="&quot;regular expression&quot;" required="true" />
172                         <parameter name="string" required="true" />
173                 </syntax>
174                 <description>
175                         <para>Return <literal>1</literal> on regular expression match or <literal>0</literal> otherwise</para>
176                         <para>Please note that the space following the double quotes separating the 
177                         regex from the data is optional and if present, is skipped. If a space is 
178                         desired at the beginning of the data, then put two spaces there; the second 
179                         will not be skipped.</para>
180                 </description>
181         </function>
182         <application name="ClearHash" language="en_US">
183                 <synopsis>
184                         Clear the keys from a specified hashname.
185                 </synopsis>
186                 <syntax>
187                         <parameter name="hashname" required="true" />
188                 </syntax>
189                 <description>
190                         <para>Clears all keys out of the specified <replaceable>hashname</replaceable>.</para>
191                 </description>
192         </application>
193         <function name="HASH" language="en_US">
194                 <synopsis>
195                         Implementation of a dialplan associative array
196                 </synopsis>
197                 <syntax>
198                         <parameter name="hashname" required="true" />
199                         <parameter name="hashkey" />
200                 </syntax>
201                 <description>
202                         <para>In two arguments mode, gets and sets values to corresponding keys within
203                         a named associative array. The single-argument mode will only work when assigned
204                         to from a function defined by func_odbc</para>
205                 </description>
206         </function>
207         <function name="HASHKEYS" language="en_US">
208                 <synopsis>
209                         Retrieve the keys of the HASH() function.
210                 </synopsis>
211                 <syntax>
212                         <parameter name="hashname" required="true" />
213                 </syntax>
214                 <description>
215                         <para>Returns a comma-delimited list of the current keys of the associative array 
216                         defined by the HASH() function. Note that if you iterate over the keys of 
217                         the result, adding keys during iteration will cause the result of the HASHKEYS()
218                         function to change.</para>
219                 </description>
220         </function>
221         <function name="KEYPADHASH" language="en_US">
222                 <synopsis>
223                         Hash the letters in string into equivalent keypad numbers.
224                 </synopsis>
225                 <syntax>
226                         <parameter name="string" required="true" />
227                 </syntax>
228                 <description>
229                         <para>Example: ${KEYPADHASH(Les)} returns "537"</para>
230                 </description>
231         </function>
232         <function name="ARRAY" language="en_US">
233                 <synopsis>
234                         Allows setting multiple variables at once.
235                 </synopsis>
236                 <syntax>
237                         <parameter name="var1" required="true" />
238                         <parameter name="var2" required="false" multiple="true" />
239                         <parameter name="varN" required="false" />
240                 </syntax>
241                 <description>
242                         <para>The comma-delimited list passed as a value to which the function is set will 
243                         be interpreted as a set of values to which the comma-delimited list of 
244                         variable names in the argument should be set.</para>
245                         <para>Example: Set(ARRAY(var1,var2)=1,2) will set var1 to 1 and var2 to 2</para>
246                 </description>
247         </function>
248         <function name="STRPTIME" language="en_US">
249                 <synopsis>
250                         Returns the epoch of the arbitrary date/time string structured as described by the format.
251                 </synopsis>
252                 <syntax>
253                         <parameter name="datetime" required="true" />
254                         <parameter name="timezone" required="true" />
255                         <parameter name="format" required="true" />
256                 </syntax>
257                 <description>
258                         <para>This is useful for converting a date into <literal>EPOCH</literal> time, 
259                         possibly to pass to an application like SayUnixTime or to calculate the difference
260                         between the two date strings</para>
261                         <para>Example: ${STRPTIME(2006-03-01 07:30:35,America/Chicago,%Y-%m-%d %H:%M:%S)} returns 1141219835</para>
262                 </description>
263         </function>
264         <function name="STRFTIME" language="en_US">
265                 <synopsis>
266                         Returns the current date/time in the specified format.
267                 </synopsis>
268                 <syntax>
269                         <parameter name="epoch" />
270                         <parameter name="timezone" />
271                         <parameter name="format" />
272                 </syntax>
273                 <description>
274                         <para>STRFTIME supports all of the same formats as the underlying C function
275                         <emphasis>strftime(3)</emphasis>.
276                         It also supports the following format: <literal>%[n]q</literal> - fractions of a second,
277                         with leading zeros.</para>
278                         <para>Example: <literal>%3q</literal> will give milliseconds and <literal>%1q</literal>
279                         will give tenths of a second. The default is set at milliseconds (n=3).
280                         The common case is to use it in combination with %S, as in <literal>%S.%3q</literal>.</para>
281                 </description>
282                 <see-also>
283                         <ref type="manpage">strftime(3)</ref>
284                 </see-also>
285         </function>
286         <function name="EVAL" language="en_US">
287                 <synopsis>
288                         Evaluate stored variables
289                 </synopsis>
290                 <syntax>
291                         <parameter name="variable" required="true" />
292                 </syntax>
293                 <description>
294                         <para>Using EVAL basically causes a string to be evaluated twice.
295                         When a variable or expression is in the dialplan, it will be
296                         evaluated at runtime. However, if the results of the evaluation
297                         is in fact another variable or expression, using EVAL will have it
298                         evaluated a second time.</para>
299                         <para>Example: If the <variable>MYVAR</variable> contains
300                         <variable>OTHERVAR</variable>, then the result of ${EVAL(
301                         <variable>MYVAR</variable>)} in the dialplan will be the
302                         contents of <variable>OTHERVAR</variable>. Normally just
303                         putting <variable>MYVAR</variable> in the dialplan the result
304                         would be <variable>OTHERVAR</variable>.</para>
305                 </description>
306         </function>
307         <function name="TOUPPER" language="en_US">
308                 <synopsis>
309                         Convert string to all uppercase letters.
310                 </synopsis>
311                 <syntax>
312                         <parameter name="string" required="true" />
313                 </syntax>
314                 <description>
315                         <para>Example: ${TOUPPER(Example)} returns "EXAMPLE"</para>
316                 </description>
317         </function>
318         <function name="TOLOWER" language="en_US">
319                 <synopsis>
320                         Convert string to all lowercase letters.
321                 </synopsis>
322                 <syntax>
323                         <parameter name="string" required="true" />
324                 </syntax>
325                 <description>
326                         <para>Example: ${TOLOWER(Example)} returns "example"</para>
327                 </description>
328         </function>
329         <function name="LEN" language="en_US">
330                 <synopsis>
331                         Return the length of the string given.
332                 </synopsis>
333                 <syntax>
334                         <parameter name="string" required="true" />
335                 </syntax>
336                 <description>
337                         <para>Example: ${LEN(example)} returns 7</para>
338                 </description>
339         </function>
340         <function name="QUOTE" language="en_US">
341                 <synopsis>
342                         Quotes a given string, escaping embedded quotes as necessary
343                 </synopsis>
344                 <syntax>
345                         <parameter name="string" required="true" />
346                 </syntax>
347                 <description>
348                         <para>Example: ${QUOTE(ab"c"de)} will return "abcde"</para>
349                 </description>
350         </function>
351         <function name="CSV_QUOTE" language="en_US">
352                 <synopsis>
353                         Quotes a given string for use in a CSV file, escaping embedded quotes as necessary
354                 </synopsis>
355                 <syntax>
356                         <parameter name="string" required="true" />
357                 </syntax>
358                 <description>
359                         <para>Example: ${CSV_QUOTE("a,b" 123)} will return """a,b"" 123"</para>
360                 </description>
361         </function>
362         <function name="SHIFT" language="en_US">
363                 <synopsis>
364                         Removes and returns the first item off of a variable containing delimited text
365                 </synopsis>
366                 <syntax>
367                         <parameter name="varname" required="true" />
368                         <parameter name="delimiter" required="false" default="," />
369                 </syntax>
370                 <description>
371                         <para>Example:</para>
372                         <para>exten => s,1,Set(array=one,two,three)</para>
373                         <para>exten => s,n,While($["${SET(var=${SHIFT(array)})}" != ""])</para>
374                         <para>exten => s,n,NoOp(var is ${var})</para>
375                         <para>exten => s,n,EndWhile</para>
376                         <para>This would iterate over each value in array, left to right, and
377                                 would result in NoOp(var is one), NoOp(var is two), and
378                                 NoOp(var is three) being executed.
379                         </para>
380                 </description>
381         </function>     
382         <function name="POP" language="en_US">
383                 <synopsis>
384                         Removes and returns the last item off of a variable containing delimited text
385                 </synopsis>
386                 <syntax>
387                         <parameter name="varname" required="true" />
388                         <parameter name="delimiter" required="false" default="," />
389                 </syntax>
390                 <description>
391                         <para>Example:</para>
392                         <para>exten => s,1,Set(array=one,two,three)</para>
393                         <para>exten => s,n,While($["${SET(var=${POP(array)})}" != ""])</para>
394                         <para>exten => s,n,NoOp(var is ${var})</para>
395                         <para>exten => s,n,EndWhile</para>
396                         <para>This would iterate over each value in array, right to left, and
397                                 would result in NoOp(var is three), NoOp(var is two), and
398                                 NoOp(var is one) being executed.
399                         </para>
400                 </description>
401         </function>     
402         <function name="PUSH" language="en_US">
403                 <synopsis>
404                         Appends one or more values to the end of a variable containing delimited text
405                 </synopsis>
406                 <syntax>
407                         <parameter name="varname" required="true" />
408                         <parameter name="delimiter" required="false" default="," />
409                 </syntax>
410                 <description>
411                         <para>Example: Set(PUSH(array)=one,two,three) would append one,
412                                 two, and three to the end of the values stored in the variable
413                                 "array".
414                         </para>
415                 </description>
416         </function>
417         <function name="UNSHIFT" language="en_US">
418                 <synopsis>
419                         Inserts one or more values to the beginning of a variable containing delimited text
420                 </synopsis>
421                 <syntax>
422                         <parameter name="varname" required="true" />
423                         <parameter name="delimiter" required="false" default="," />
424                 </syntax>
425                 <description>
426                         <para>Example: Set(UNSHIFT(array)=one,two,three) would insert one,
427                                 two, and three before the values stored in the variable
428                                 "array".
429                         </para>
430                 </description>
431         </function>
432  ***/
433
434 static int function_fieldqty_helper(struct ast_channel *chan, const char *cmd,
435                              char *parse, char *buf, struct ast_str **sbuf, ssize_t len)
436 {
437         char *varsubst;
438         struct ast_str *str = ast_str_thread_get(&result_buf, 16);
439         int fieldcount = 0;
440         AST_DECLARE_APP_ARGS(args,
441                              AST_APP_ARG(varname);
442                              AST_APP_ARG(delim);
443                 );
444         char delim[2] = "";
445         size_t delim_used;
446
447         if (!str) {
448                 return -1;
449         }
450
451         AST_STANDARD_APP_ARGS(args, parse);
452         if (args.delim) {
453                 ast_get_encoded_char(args.delim, delim, &delim_used);
454
455                 varsubst = alloca(strlen(args.varname) + 4);
456
457                 sprintf(varsubst, "${%s}", args.varname);
458                 ast_str_substitute_variables(&str, 0, chan, varsubst);
459                 if (ast_str_strlen(str) == 0) {
460                         fieldcount = 0;
461                 } else {
462                         char *varval = ast_str_buffer(str);
463                         while (strsep(&varval, delim)) {
464                                 fieldcount++;
465                         }
466                 }
467         } else {
468                 fieldcount = 1;
469         }
470         if (sbuf) {
471                 ast_str_set(sbuf, len, "%d", fieldcount);
472         } else {
473                 snprintf(buf, len, "%d", fieldcount);
474         }
475
476         return 0;
477 }
478
479 static int function_fieldqty(struct ast_channel *chan, const char *cmd,
480                              char *parse, char *buf, size_t len)
481 {
482         return function_fieldqty_helper(chan, cmd, parse, buf, NULL, len);
483 }
484
485 static int function_fieldqty_str(struct ast_channel *chan, const char *cmd,
486                                  char *parse, struct ast_str **buf, ssize_t len)
487 {
488         return function_fieldqty_helper(chan, cmd, parse, NULL, buf, len);
489 }
490
491 static struct ast_custom_function fieldqty_function = {
492         .name = "FIELDQTY",
493         .read = function_fieldqty,
494         .read2 = function_fieldqty_str,
495 };
496
497 static int function_fieldnum_helper(struct ast_channel *chan, const char *cmd,
498                                 char *parse, char *buf, struct ast_str **sbuf, ssize_t len)
499 {
500         char *varsubst, *field;
501         struct ast_str *str = ast_str_thread_get(&result_buf, 16);
502         int fieldindex = 0, res = 0;
503         AST_DECLARE_APP_ARGS(args,
504                 AST_APP_ARG(varname);
505                 AST_APP_ARG(delim);
506                 AST_APP_ARG(field);
507         );
508         char delim[2] = "";
509         size_t delim_used;
510
511         if (!str) {
512                 return -1;
513         }
514
515         AST_STANDARD_APP_ARGS(args, parse);
516
517         if (args.argc < 3) {
518                 ast_log(LOG_ERROR, "Usage: FIELDNUM(<listname>,<delimiter>,<fieldvalue>)\n");
519                 res = -1;
520         } else {
521                 varsubst = alloca(strlen(args.varname) + 4);
522                 sprintf(varsubst, "${%s}", args.varname);
523
524                 ast_str_substitute_variables(&str, 0, chan, varsubst);
525
526                 if (ast_str_strlen(str) == 0 || ast_strlen_zero(args.delim)) {
527                         fieldindex = 0;
528                 } else if (ast_get_encoded_char(args.delim, delim, &delim_used) == -1) {
529                         res = -1;
530                 } else {
531                         char *varval = ast_str_buffer(str);
532
533                         while ((field = strsep(&varval, delim)) != NULL) {
534                                 fieldindex++;
535
536                                 if (!strcasecmp(field, args.field)) {
537                                         break;
538                                 }
539                         }
540
541                         if (!field) {
542                                 fieldindex = 0;
543                         }
544
545                         res = 0;
546                 }
547         }
548
549         if (sbuf) {
550                 ast_str_set(sbuf, len, "%d", fieldindex);
551         } else {
552                 snprintf(buf, len, "%d", fieldindex);
553         }
554
555         return res;
556 }
557
558 static int function_fieldnum(struct ast_channel *chan, const char *cmd,
559                              char *parse, char *buf, size_t len)
560 {
561         return function_fieldnum_helper(chan, cmd, parse, buf, NULL, len);
562 }
563
564 static int function_fieldnum_str(struct ast_channel *chan, const char *cmd,
565                                  char *parse, struct ast_str **buf, ssize_t len)
566 {
567         return function_fieldnum_helper(chan, cmd, parse, NULL, buf, len);
568 }
569
570 static struct ast_custom_function fieldnum_function = {
571         .name = "FIELDNUM",
572         .read = function_fieldnum,
573         .read2 = function_fieldnum_str,
574 };
575
576 static int listfilter(struct ast_channel *chan, const char *cmd, char *parse, char *buf, struct ast_str **bufstr, ssize_t len)
577 {
578         AST_DECLARE_APP_ARGS(args,
579                 AST_APP_ARG(listname);
580                 AST_APP_ARG(delimiter);
581                 AST_APP_ARG(fieldvalue);
582         );
583         const char *ptr;
584         struct ast_str *orig_list = ast_str_thread_get(&tmp_buf, 16);
585         const char *begin, *cur, *next;
586         int dlen, flen, first = 1;
587         struct ast_str *result, **result_ptr = &result;
588         char *delim, *varsubst;
589
590         AST_STANDARD_APP_ARGS(args, parse);
591
592         if (buf) {
593                 if (!(result = ast_str_thread_get(&result_buf, 16))) {
594                         return -1;
595                 }
596         } else {
597                 /* Place the result directly into the output buffer */
598                 result_ptr = bufstr;
599         }
600
601         if (args.argc < 3) {
602                 ast_log(LOG_ERROR, "Usage: LISTFILTER(<listname>,<delimiter>,<fieldvalue>)\n");
603                 return -1;
604         }
605
606         varsubst = alloca(strlen(args.listname) + 4);
607         sprintf(varsubst, "${%s}", args.listname);
608
609         /* If we don't lock the channel, the variable could disappear out from underneath us. */
610         if (chan) {
611                 ast_channel_lock(chan);
612         }
613         ast_str_substitute_variables(&orig_list, 0, chan, varsubst);
614         if (!ast_str_strlen(orig_list)) {
615                 ast_log(LOG_ERROR, "List variable '%s' not found\n", args.listname);
616                 if (chan) {
617                         ast_channel_unlock(chan);
618                 }
619                 return -1;
620         }
621
622         /* If the string isn't there, just copy out the string and be done with it. */
623         if (!(ptr = strstr(ast_str_buffer(orig_list), args.fieldvalue))) {
624                 if (buf) {
625                         ast_copy_string(buf, ast_str_buffer(orig_list), len);
626                 } else {
627                         ast_str_set(result_ptr, len, "%s", ast_str_buffer(orig_list));
628                 }
629                 if (chan) {
630                         ast_channel_unlock(chan);
631                 }
632                 return 0;
633         }
634
635         dlen = strlen(args.delimiter);
636         delim = alloca(dlen + 1);
637         ast_get_encoded_str(args.delimiter, delim, dlen + 1);
638
639         if ((dlen = strlen(delim)) == 0) {
640                 delim = ",";
641                 dlen = 1;
642         }
643
644         flen = strlen(args.fieldvalue);
645
646         ast_str_reset(*result_ptr);
647         /* Enough space for any result */
648         if (len > -1) {
649                 ast_str_make_space(result_ptr, len ? len : ast_str_strlen(orig_list) + 1);
650         }
651
652         begin = ast_str_buffer(orig_list);
653         next = strstr(begin, delim);
654
655         do {
656                 /* Find next boundary */
657                 if (next) {
658                         cur = next;
659                         next = strstr(cur + dlen, delim);
660                 } else {
661                         cur = strchr(begin + dlen, '\0');
662                 }
663
664                 if (flen == cur - begin && !strncmp(begin, args.fieldvalue, flen)) {
665                         /* Skip field */
666                         begin += flen + dlen;
667                 } else {
668                         /* Copy field to output */
669                         if (!first) {
670                                 ast_str_append(result_ptr, len, "%s", delim);
671                         }
672
673                         ast_str_append_substr(result_ptr, len, begin, cur - begin);
674                         first = 0;
675                         begin = cur + dlen;
676                 }
677         } while (*cur != '\0');
678         if (chan) {
679                 ast_channel_unlock(chan);
680         }
681
682         if (buf) {
683                 ast_copy_string(buf, ast_str_buffer(result), len);
684         }
685
686         return 0;
687 }
688
689 static int listfilter_read(struct ast_channel *chan, const char *cmd, char *parse, char *buf, size_t len)
690 {
691         return listfilter(chan, cmd, parse, buf, NULL, len);
692 }
693
694 static int listfilter_read2(struct ast_channel *chan, const char *cmd, char *parse, struct ast_str **buf, ssize_t len)
695 {
696         return listfilter(chan, cmd, parse, NULL, buf, len);
697 }
698
699 static struct ast_custom_function listfilter_function = {
700         .name = "LISTFILTER",
701         .read = listfilter_read,
702         .read2 = listfilter_read2,
703 };
704
705 static int filter(struct ast_channel *chan, const char *cmd, char *parse, char *buf,
706                   size_t len)
707 {
708         AST_DECLARE_APP_ARGS(args,
709                              AST_APP_ARG(allowed);
710                              AST_APP_ARG(string);
711         );
712         char *outbuf = buf;
713         unsigned char ac;
714         char allowed[256] = "";
715         size_t allowedlen = 0;
716         int32_t bitfield[8] = { 0, }; /* 256 bits */
717
718         AST_STANDARD_RAW_ARGS(args, parse);
719
720         if (!args.string) {
721                 ast_log(LOG_ERROR, "Usage: FILTER(<allowed-chars>,<string>)\n");
722                 return -1;
723         }
724
725         if (args.allowed[0] == '"' && !ast_opt_dont_warn) {
726                 ast_log(LOG_WARNING, "FILTER allowed characters includes the quote (\") character.  This may not be what you want.\n");
727         }
728
729         /* Expand ranges */
730         for (; *(args.allowed);) {
731                 char c1 = 0, c2 = 0;
732                 size_t consumed = 0;
733
734                 if (ast_get_encoded_char(args.allowed, &c1, &consumed))
735                         return -1;
736                 args.allowed += consumed;
737
738                 if (*(args.allowed) == '-') {
739                         if (ast_get_encoded_char(args.allowed + 1, &c2, &consumed))
740                                 c2 = c1;
741                         args.allowed += consumed + 1;
742
743                         if ((unsigned char) c2 < (unsigned char) c1 && !ast_opt_dont_warn) {
744                                 ast_log(LOG_WARNING, "Range wrapping in FILTER(%s,%s).  This may not be what you want.\n", parse, args.string);
745                         }
746
747                         /*!\note
748                          * Looks a little strange, until you realize that we can overflow
749                          * the size of a char.
750                          */
751                         for (ac = (unsigned char) c1; ac != (unsigned char) c2; ac++) {
752                                 bitfield[ac / 32] |= 1 << (ac % 32);
753                         }
754                         bitfield[ac / 32] |= 1 << (ac % 32);
755
756                         ast_debug(4, "c1=%d, c2=%d\n", c1, c2);
757                 } else {
758                         ac = (unsigned char) c1;
759                         ast_debug(4, "c1=%d, consumed=%d, args.allowed=%s\n", c1, (int) consumed, args.allowed - consumed);
760                         bitfield[ac / 32] |= 1 << (ac % 32);
761                 }
762         }
763
764         for (ac = 1; ac != 0; ac++) {
765                 if (bitfield[ac / 32] & (1 << (ac % 32))) {
766                         allowed[allowedlen++] = ac;
767                 }
768         }
769
770         ast_debug(1, "Allowed: %s\n", allowed);
771
772         for (; *(args.string) && (buf + len - 1 > outbuf); (args.string)++) {
773                 if (strchr(allowed, *(args.string)))
774                         *outbuf++ = *(args.string);
775         }
776         *outbuf = '\0';
777
778         return 0;
779 }
780
781 static struct ast_custom_function filter_function = {
782         .name = "FILTER",
783         .read = filter,
784 };
785
786 static int replace(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
787 {
788         AST_DECLARE_APP_ARGS(args,
789                 AST_APP_ARG(varname);
790                 AST_APP_ARG(find);
791                 AST_APP_ARG(replace);
792         );
793         char *strptr, *varsubst;
794         struct ast_str *str = ast_str_thread_get(&result_buf, 16);
795         char find[256]; /* Only 256 characters possible */
796         char replace[2] = "";
797         size_t unused;
798
799         AST_STANDARD_APP_ARGS(args, data);
800
801         if (!str) {
802                 return -1;
803         }
804
805         if (args.argc < 2) {
806                 ast_log(LOG_ERROR, "Usage: %s(<varname>,<search-chars>[,<replace-char>])\n", cmd);
807                 return -1;
808         }
809
810         /* Decode escapes */
811         ast_get_encoded_str(args.find, find, sizeof(find));
812         ast_get_encoded_char(args.replace, replace, &unused);
813
814         if (ast_strlen_zero(find) || ast_strlen_zero(args.varname)) {
815                 ast_log(LOG_ERROR, "The characters to search for and the variable name must not be empty.\n");
816                 return -1;
817         }
818
819         varsubst = alloca(strlen(args.varname) + 4);
820         sprintf(varsubst, "${%s}", args.varname);
821         ast_str_substitute_variables(&str, 0, chan, varsubst);
822
823         if (!ast_str_strlen(str)) {
824                 /* Blank, nothing to replace */
825                 return -1;
826         }
827
828         ast_debug(3, "String to search: (%s)\n", ast_str_buffer(str));
829         ast_debug(3, "Characters to find: (%s)\n", find);
830         ast_debug(3, "Character to replace with: (%s)\n", replace);
831
832         for (strptr = ast_str_buffer(str); *strptr; strptr++) {
833                 /* buf is already a mutable buffer, so we construct the result
834                  * directly there */
835                 if (strchr(find, *strptr)) {
836                         if (ast_strlen_zero(replace)) {
837                                 /* Remove character */
838                                 strcpy(strptr, strptr + 1); /* SAFE */
839                                 strptr--;
840                         } else {
841                                 /* Replace character */
842                                 *strptr = *replace;
843                         }
844                 }
845         }
846
847         ast_str_set(buf, len, "%s", ast_str_buffer(str));
848         return 0;
849 }
850
851 static struct ast_custom_function replace_function = {
852         .name = "REPLACE",
853         .read2 = replace,
854 };
855
856 static int strreplace(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
857 {
858         char *starts[len]; /* marks starts of substrings */
859         char *varsubstr; /* substring for input var */
860         int count_len = 0; /* counter for starts */
861         char *p; /* tracks position of cursor for search and replace */
862         int find_size; /* length of given find-string */
863         int max_matches; /* number of matches we find before terminating search */
864
865         int x; /* loop counter */
866         struct ast_str *str = ast_str_thread_get(&result_buf, 16); /* Holds the data obtained from varname */
867
868         AST_DECLARE_APP_ARGS(args,
869                 AST_APP_ARG(varname);
870                 AST_APP_ARG(find_string);
871                 AST_APP_ARG(replace_string);
872                 AST_APP_ARG(max_replacements);
873         );
874
875         AST_STANDARD_APP_ARGS(args, data);
876
877         if (!str) { /* If we failed to allocate str, forget it.  We failed. */
878                 return -1;
879         }
880
881         if (args.argc < 2) { /* Didn't receive enough arguments to do anything */
882                 ast_log(LOG_ERROR, "Usage: %s(<varname>,<find-string>[,<replace-string>, <max-replacements>])\n", cmd);
883                 return -1;
884         }
885
886         /* No var name specified. Return failure, string is already empty. */
887         if (ast_strlen_zero(args.varname)) {
888                 return -1;
889         }
890
891         /* set varsubstr to the matching variable */
892         varsubstr = alloca(strlen(args.varname) + 4);
893         sprintf(varsubstr, "${%s}", args.varname);
894         ast_str_substitute_variables(&str, 0, chan, varsubstr);
895
896         p = ast_str_buffer(str);
897
898         /* Zero length find strings are a no-no. Kill the function if we run into one. */
899         if (ast_strlen_zero(args.find_string)) {
900                 ast_log(LOG_ERROR, "The <find-string> must have length > 0\n");
901                 return -1;
902         }
903         find_size = strlen(args.find_string);
904
905         /* If the replace string is a null pointer, set it to an empty string */
906         if (!args.replace_string) {
907                 args.replace_string = "";
908         }
909
910         /*
911          * If max_replacements specified and is a number, max_matches will become that.
912          * otherwise, just go the length of the input string
913          */
914         if (!(args.max_replacements && (max_matches = atoi(args.max_replacements)))) {
915                 max_matches = strlen(p);
916         }
917
918         /* Iterate through string finding matches until it is exhausted or we reach max_matches */
919         for (x = 0; x < max_matches; x++) {
920                 if ((p = strstr(p, args.find_string))) {
921                         starts[count_len++] = p;
922                         *p++ = '\0';
923                 } else {
924                         break;
925                 }
926         }
927
928         p = ast_str_buffer(str);
929
930         /* here we rebuild the string with the replaced words by using fancy ast_string_append on the buffer */
931         for (x = 0; x < count_len; x++) {
932                 ast_str_append(buf, 0, "%s", p);
933                 p = starts[x];
934                 p += find_size;
935                 ast_str_append(buf, 0, "%s", args.replace_string);
936         }
937         ast_str_append(buf, 0, "%s", p);
938
939         return 0;
940 }
941
942 static struct ast_custom_function strreplace_function = {
943         .name = "STRREPLACE",
944         .read2 = strreplace,
945 };
946
947 static int regex(struct ast_channel *chan, const char *cmd, char *parse, char *buf,
948                  size_t len)
949 {
950         AST_DECLARE_APP_ARGS(args,
951                              AST_APP_ARG(null);
952                              AST_APP_ARG(reg);
953                              AST_APP_ARG(str);
954         );
955         int errcode;
956         regex_t regexbuf;
957
958         buf[0] = '\0';
959
960         AST_NONSTANDARD_APP_ARGS(args, parse, '"');
961
962         if (args.argc != 3) {
963                 ast_log(LOG_ERROR, "Unexpected arguments: should have been in the form '\"<regex>\" <string>'\n");
964                 return -1;
965         }
966         if ((*args.str == ' ') || (*args.str == '\t'))
967                 args.str++;
968
969         ast_debug(1, "FUNCTION REGEX (%s)(%s)\n", args.reg, args.str);
970
971         if ((errcode = regcomp(&regexbuf, args.reg, REG_EXTENDED | REG_NOSUB))) {
972                 regerror(errcode, &regexbuf, buf, len);
973                 ast_log(LOG_WARNING, "Malformed input %s(%s): %s\n", cmd, parse, buf);
974                 return -1;
975         }
976         
977         strcpy(buf, regexec(&regexbuf, args.str, 0, NULL, 0) ? "0" : "1");
978
979         regfree(&regexbuf);
980
981         return 0;
982 }
983
984 static struct ast_custom_function regex_function = {
985         .name = "REGEX",
986         .read = regex,
987 };
988
989 #define HASH_PREFIX     "~HASH~%s~"
990 #define HASH_FORMAT     HASH_PREFIX "%s~"
991
992 static char *app_clearhash = "ClearHash";
993
994 /* This function probably should migrate to main/pbx.c, as pbx_builtin_clearvar_prefix() */
995 static void clearvar_prefix(struct ast_channel *chan, const char *prefix)
996 {
997         struct ast_var_t *var;
998         int len = strlen(prefix);
999         AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->varshead, var, entries) {
1000                 if (strncasecmp(prefix, ast_var_name(var), len) == 0) {
1001                         AST_LIST_REMOVE_CURRENT(entries);
1002                         ast_free(var);
1003                 }
1004         }
1005         AST_LIST_TRAVERSE_SAFE_END
1006 }
1007
1008 static int exec_clearhash(struct ast_channel *chan, const char *data)
1009 {
1010         char prefix[80];
1011         snprintf(prefix, sizeof(prefix), HASH_PREFIX, data ? (char *)data : "null");
1012         clearvar_prefix(chan, prefix);
1013         return 0;
1014 }
1015
1016 static int array(struct ast_channel *chan, const char *cmd, char *var,
1017                  const char *value)
1018 {
1019         AST_DECLARE_APP_ARGS(arg1,
1020                              AST_APP_ARG(var)[100];
1021         );
1022         AST_DECLARE_APP_ARGS(arg2,
1023                              AST_APP_ARG(val)[100];
1024         );
1025         char *origvar = "", *value2, varname[256];
1026         int i, ishash = 0;
1027
1028         value2 = ast_strdupa(value);
1029         if (!var || !value2)
1030                 return -1;
1031
1032         if (!strcmp(cmd, "HASH")) {
1033                 const char *var2 = pbx_builtin_getvar_helper(chan, "~ODBCFIELDS~");
1034                 origvar = var;
1035                 if (var2)
1036                         var = ast_strdupa(var2);
1037                 else {
1038                         if (chan)
1039                                 ast_autoservice_stop(chan);
1040                         return -1;
1041                 }
1042                 ishash = 1;
1043         }
1044
1045         /* The functions this will generally be used with are SORT and ODBC_*, which
1046          * both return comma-delimited lists.  However, if somebody uses literal lists,
1047          * their commas will be translated to vertical bars by the load, and I don't
1048          * want them to be surprised by the result.  Hence, we prefer commas as the
1049          * delimiter, but we'll fall back to vertical bars if commas aren't found.
1050          */
1051         ast_debug(1, "array (%s=%s)\n", var, S_OR(value2, ""));
1052         AST_STANDARD_APP_ARGS(arg1, var);
1053
1054         AST_STANDARD_APP_ARGS(arg2, value2);
1055
1056         for (i = 0; i < arg1.argc; i++) {
1057                 ast_debug(1, "array set value (%s=%s)\n", arg1.var[i],
1058                                 S_OR(arg2.val[i], ""));
1059                 if (i < arg2.argc) {
1060                         if (ishash) {
1061                                 if (origvar[0] == '_') {
1062                                         if (origvar[1] == '_') {
1063                                                 snprintf(varname, sizeof(varname), "__" HASH_FORMAT, origvar + 2, arg1.var[i]);
1064                                         } else {
1065                                                 snprintf(varname, sizeof(varname), "_" HASH_FORMAT, origvar + 1, arg1.var[i]);
1066                                         }
1067                                 } else {
1068                                         snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]);
1069                                 }
1070
1071                                 pbx_builtin_setvar_helper(chan, varname, arg2.val[i]);
1072                         } else {
1073                                 pbx_builtin_setvar_helper(chan, arg1.var[i], arg2.val[i]);
1074                         }
1075                 } else {
1076                         /* We could unset the variable, by passing a NULL, but due to
1077                          * pushvar semantics, that could create some undesired behavior. */
1078                         if (ishash) {
1079                                 snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]);
1080                                 pbx_builtin_setvar_helper(chan, varname, "");
1081                         } else {
1082                                 pbx_builtin_setvar_helper(chan, arg1.var[i], "");
1083                         }
1084                 }
1085         }
1086
1087         return 0;
1088 }
1089
1090 static int hashkeys_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1091 {
1092         struct ast_var_t *newvar;
1093         struct ast_str *prefix = ast_str_alloca(80);
1094
1095         ast_str_set(&prefix, -1, HASH_PREFIX, data);
1096         memset(buf, 0, len);
1097
1098         AST_LIST_TRAVERSE(&chan->varshead, newvar, entries) {
1099                 if (strncasecmp(ast_str_buffer(prefix), ast_var_name(newvar), ast_str_strlen(prefix)) == 0) {
1100                         /* Copy everything after the prefix */
1101                         strncat(buf, ast_var_name(newvar) + ast_str_strlen(prefix), len - strlen(buf) - 1);
1102                         /* Trim the trailing ~ */
1103                         buf[strlen(buf) - 1] = ',';
1104                 }
1105         }
1106         /* Trim the trailing comma */
1107         buf[strlen(buf) - 1] = '\0';
1108         return 0;
1109 }
1110
1111 static int hashkeys_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
1112 {
1113         struct ast_var_t *newvar;
1114         struct ast_str *prefix = ast_str_alloca(80);
1115         char *tmp;
1116
1117         ast_str_set(&prefix, -1, HASH_PREFIX, data);
1118
1119         AST_LIST_TRAVERSE(&chan->varshead, newvar, entries) {
1120                 if (strncasecmp(ast_str_buffer(prefix), ast_var_name(newvar), ast_str_strlen(prefix)) == 0) {
1121                         /* Copy everything after the prefix */
1122                         ast_str_append(buf, len, "%s", ast_var_name(newvar) + ast_str_strlen(prefix));
1123                         /* Trim the trailing ~ */
1124                         tmp = ast_str_buffer(*buf);
1125                         tmp[ast_str_strlen(*buf) - 1] = ',';
1126                 }
1127         }
1128         /* Trim the trailing comma */
1129         tmp = ast_str_buffer(*buf);
1130         tmp[ast_str_strlen(*buf) - 1] = '\0';
1131         return 0;
1132 }
1133
1134 static int hash_write(struct ast_channel *chan, const char *cmd, char *var, const char *value)
1135 {
1136         char varname[256];
1137         AST_DECLARE_APP_ARGS(arg,
1138                 AST_APP_ARG(hashname);
1139                 AST_APP_ARG(hashkey);
1140         );
1141
1142         if (!strchr(var, ',')) {
1143                 /* Single argument version */
1144                 return array(chan, "HASH", var, value);
1145         }
1146
1147         AST_STANDARD_APP_ARGS(arg, var);
1148         if (arg.hashname[0] == '_') {
1149                 if (arg.hashname[1] == '_') {
1150                         snprintf(varname, sizeof(varname), "__" HASH_FORMAT, arg.hashname + 2, arg.hashkey);
1151                 } else {
1152                         snprintf(varname, sizeof(varname), "_" HASH_FORMAT, arg.hashname + 1, arg.hashkey);
1153                 }
1154         } else {
1155                 snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey);
1156         }
1157         pbx_builtin_setvar_helper(chan, varname, value);
1158
1159         return 0;
1160 }
1161
1162 static int hash_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1163 {
1164         char varname[256];
1165         const char *varvalue;
1166         AST_DECLARE_APP_ARGS(arg,
1167                 AST_APP_ARG(hashname);
1168                 AST_APP_ARG(hashkey);
1169         );
1170
1171         AST_STANDARD_APP_ARGS(arg, data);
1172         if (arg.argc == 2) {
1173                 snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey);
1174                 varvalue = pbx_builtin_getvar_helper(chan, varname);
1175                 if (varvalue)
1176                         ast_copy_string(buf, varvalue, len);
1177                 else
1178                         *buf = '\0';
1179         } else if (arg.argc == 1) {
1180                 char colnames[4096];
1181                 int i;
1182                 AST_DECLARE_APP_ARGS(arg2,
1183                         AST_APP_ARG(col)[100];
1184                 );
1185
1186                 /* Get column names, in no particular order */
1187                 hashkeys_read(chan, "HASHKEYS", arg.hashname, colnames, sizeof(colnames));
1188                 pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", colnames);
1189
1190                 AST_STANDARD_APP_ARGS(arg2, colnames);
1191                 *buf = '\0';
1192
1193                 /* Now get the corresponding column values, in exactly the same order */
1194                 for (i = 0; i < arg2.argc; i++) {
1195                         snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg2.col[i]);
1196                         varvalue = pbx_builtin_getvar_helper(chan, varname);
1197                         strncat(buf, varvalue, len - strlen(buf) - 1);
1198                         strncat(buf, ",", len - strlen(buf) - 1);
1199                 }
1200
1201                 /* Strip trailing comma */
1202                 buf[strlen(buf) - 1] = '\0';
1203         }
1204
1205         return 0;
1206 }
1207
1208 static struct ast_custom_function hash_function = {
1209         .name = "HASH",
1210         .write = hash_write,
1211         .read = hash_read,
1212 };
1213
1214 static struct ast_custom_function hashkeys_function = {
1215         .name = "HASHKEYS",
1216         .read = hashkeys_read,
1217         .read2 = hashkeys_read2,
1218 };
1219
1220 static struct ast_custom_function array_function = {
1221         .name = "ARRAY",
1222         .write = array,
1223 };
1224
1225 static int quote(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1226 {
1227         char *bufptr = buf, *dataptr = data;
1228
1229         if (len < 3){ /* at least two for quotes and one for binary zero */
1230                 ast_log(LOG_ERROR, "Not enough buffer");
1231                 return -1;
1232         }
1233
1234         if (ast_strlen_zero(data)) {
1235                 ast_log(LOG_WARNING, "No argument specified!\n");
1236                 ast_copy_string(buf, "\"\"", len);
1237                 return 0;
1238         }
1239
1240         *bufptr++ = '"';
1241         for (; bufptr < buf + len - 3; dataptr++) {
1242                 if (*dataptr == '\\') {
1243                         *bufptr++ = '\\';
1244                         *bufptr++ = '\\';
1245                 } else if (*dataptr == '"') {
1246                         *bufptr++ = '\\';
1247                         *bufptr++ = '"';
1248                 } else if (*dataptr == '\0') {
1249                         break;
1250                 } else {
1251                         *bufptr++ = *dataptr;
1252                 }
1253         }
1254         *bufptr++ = '"';
1255         *bufptr = '\0';
1256         return 0;
1257 }
1258
1259 static struct ast_custom_function quote_function = {
1260         .name = "QUOTE",
1261         .read = quote,
1262 };
1263
1264 static int csv_quote(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1265 {
1266         char *bufptr = buf, *dataptr = data;
1267
1268         if (len < 3) { /* at least two for quotes and one for binary zero */
1269                 ast_log(LOG_ERROR, "Not enough buffer");
1270                 return -1;
1271         }
1272
1273         if (ast_strlen_zero(data)) {
1274                 ast_copy_string(buf, "\"\"", len);
1275                 return 0;
1276         }
1277
1278         *bufptr++ = '"';
1279         for (; bufptr < buf + len - 3; dataptr++){
1280                 if (*dataptr == '"') {
1281                         *bufptr++ = '"';
1282                         *bufptr++ = '"';
1283                 } else if (*dataptr == '\0') {
1284                         break;
1285                 } else {
1286                         *bufptr++ = *dataptr;
1287                 }
1288         }
1289         *bufptr++ = '"';
1290         *bufptr='\0';
1291         return 0;
1292 }
1293
1294 static struct ast_custom_function csv_quote_function = {
1295         .name = "CSV_QUOTE",
1296         .read = csv_quote,
1297 };
1298
1299 static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
1300 {
1301         int length = 0;
1302
1303         if (data)
1304                 length = strlen(data);
1305
1306         snprintf(buf, buflen, "%d", length);
1307
1308         return 0;
1309 }
1310
1311 static struct ast_custom_function len_function = {
1312         .name = "LEN",
1313         .read = len,
1314         .read_max = 12,
1315 };
1316
1317 static int acf_strftime(struct ast_channel *chan, const char *cmd, char *parse,
1318                         char *buf, size_t buflen)
1319 {
1320         AST_DECLARE_APP_ARGS(args,
1321                              AST_APP_ARG(epoch);
1322                              AST_APP_ARG(timezone);
1323                              AST_APP_ARG(format);
1324         );
1325         struct timeval when;
1326         struct ast_tm tm;
1327
1328         buf[0] = '\0';
1329
1330         AST_STANDARD_APP_ARGS(args, parse);
1331
1332         ast_get_timeval(args.epoch, &when, ast_tvnow(), NULL);
1333         ast_localtime(&when, &tm, args.timezone);
1334
1335         if (!args.format)
1336                 args.format = "%c";
1337
1338         if (ast_strftime(buf, buflen, args.format, &tm) <= 0)
1339                 ast_log(LOG_WARNING, "C function strftime() output nothing?!!\n");
1340
1341         buf[buflen - 1] = '\0';
1342
1343         return 0;
1344 }
1345
1346 static struct ast_custom_function strftime_function = {
1347         .name = "STRFTIME",
1348         .read = acf_strftime,
1349 };
1350
1351 static int acf_strptime(struct ast_channel *chan, const char *cmd, char *data,
1352                         char *buf, size_t buflen)
1353 {
1354         AST_DECLARE_APP_ARGS(args,
1355                              AST_APP_ARG(timestring);
1356                              AST_APP_ARG(timezone);
1357                              AST_APP_ARG(format);
1358         );
1359         struct ast_tm tm;
1360
1361         buf[0] = '\0';
1362
1363         if (!data) {
1364                 ast_log(LOG_ERROR,
1365                                 "Asterisk function STRPTIME() requires an argument.\n");
1366                 return -1;
1367         }
1368
1369         AST_STANDARD_APP_ARGS(args, data);
1370
1371         if (ast_strlen_zero(args.format)) {
1372                 ast_log(LOG_ERROR,
1373                                 "No format supplied to STRPTIME(<timestring>,<timezone>,<format>)");
1374                 return -1;
1375         }
1376
1377         if (!ast_strptime(args.timestring, args.format, &tm)) {
1378                 ast_log(LOG_WARNING, "STRPTIME() found no time specified within the string\n");
1379         } else {
1380                 struct timeval when;
1381                 when = ast_mktime(&tm, args.timezone);
1382                 snprintf(buf, buflen, "%d", (int) when.tv_sec);
1383         }
1384
1385         return 0;
1386 }
1387
1388 static struct ast_custom_function strptime_function = {
1389         .name = "STRPTIME",
1390         .read = acf_strptime,
1391 };
1392
1393 static int function_eval(struct ast_channel *chan, const char *cmd, char *data,
1394                          char *buf, size_t buflen)
1395 {
1396         if (ast_strlen_zero(data)) {
1397                 ast_log(LOG_WARNING, "EVAL requires an argument: EVAL(<string>)\n");
1398                 return -1;
1399         }
1400
1401         pbx_substitute_variables_helper(chan, data, buf, buflen - 1);
1402
1403         return 0;
1404 }
1405
1406 static int function_eval2(struct ast_channel *chan, const char *cmd, char *data,
1407                          struct ast_str **buf, ssize_t buflen)
1408 {
1409         if (ast_strlen_zero(data)) {
1410                 ast_log(LOG_WARNING, "EVAL requires an argument: EVAL(<string>)\n");
1411                 return -1;
1412         }
1413
1414         ast_str_substitute_variables(buf, buflen, chan, data);
1415
1416         return 0;
1417 }
1418
1419 static struct ast_custom_function eval_function = {
1420         .name = "EVAL",
1421         .read = function_eval,
1422         .read2 = function_eval2,
1423 };
1424
1425 static int keypadhash(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
1426 {
1427         char *bufptr, *dataptr;
1428
1429         for (bufptr = buf, dataptr = data; bufptr < buf + buflen - 1; dataptr++) {
1430                 if (*dataptr == '\0') {
1431                         *bufptr++ = '\0';
1432                         break;
1433                 } else if (*dataptr == '1') {
1434                         *bufptr++ = '1';
1435                 } else if (strchr("AaBbCc2", *dataptr)) {
1436                         *bufptr++ = '2';
1437                 } else if (strchr("DdEeFf3", *dataptr)) {
1438                         *bufptr++ = '3';
1439                 } else if (strchr("GgHhIi4", *dataptr)) {
1440                         *bufptr++ = '4';
1441                 } else if (strchr("JjKkLl5", *dataptr)) {
1442                         *bufptr++ = '5';
1443                 } else if (strchr("MmNnOo6", *dataptr)) {
1444                         *bufptr++ = '6';
1445                 } else if (strchr("PpQqRrSs7", *dataptr)) {
1446                         *bufptr++ = '7';
1447                 } else if (strchr("TtUuVv8", *dataptr)) {
1448                         *bufptr++ = '8';
1449                 } else if (strchr("WwXxYyZz9", *dataptr)) {
1450                         *bufptr++ = '9';
1451                 } else if (*dataptr == '0') {
1452                         *bufptr++ = '0';
1453                 }
1454         }
1455         buf[buflen - 1] = '\0';
1456
1457         return 0;
1458 }
1459
1460 static struct ast_custom_function keypadhash_function = {
1461         .name = "KEYPADHASH",
1462         .read = keypadhash,
1463 };
1464
1465 static int string_toupper(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
1466 {
1467         char *bufptr = buf, *dataptr = data;
1468
1469         while ((bufptr < buf + buflen - 1) && (*bufptr++ = toupper(*dataptr++)));
1470
1471         return 0;
1472 }
1473
1474 static int string_toupper2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t buflen)
1475 {
1476         char *bufptr, *dataptr = data;
1477
1478         if (buflen > -1) {
1479                 ast_str_make_space(buf, buflen > 0 ? buflen : strlen(data) + 1);
1480         }
1481         bufptr = ast_str_buffer(*buf);
1482         while ((bufptr < ast_str_buffer(*buf) + ast_str_size(*buf) - 1) && (*bufptr++ = toupper(*dataptr++)));
1483         ast_str_update(*buf);
1484
1485         return 0;
1486 }
1487
1488 static struct ast_custom_function toupper_function = {
1489         .name = "TOUPPER",
1490         .read = string_toupper,
1491         .read2 = string_toupper2,
1492 };
1493
1494 static int string_tolower(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
1495 {
1496         char *bufptr = buf, *dataptr = data;
1497
1498         while ((bufptr < buf + buflen - 1) && (*bufptr++ = tolower(*dataptr++)));
1499
1500         return 0;
1501 }
1502
1503 static int string_tolower2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t buflen)
1504 {
1505         char *bufptr, *dataptr = data;
1506
1507         if (buflen > -1) {
1508                 ast_str_make_space(buf, buflen > 0 ? buflen : strlen(data) + 1);
1509         }
1510         bufptr = ast_str_buffer(*buf);
1511         while ((bufptr < ast_str_buffer(*buf) + ast_str_size(*buf) - 1) && (*bufptr++ = tolower(*dataptr++)));
1512         ast_str_update(*buf);
1513
1514         return 0;
1515 }
1516
1517 static struct ast_custom_function tolower_function = {
1518         .name = "TOLOWER",
1519         .read = string_tolower,
1520         .read2 = string_tolower2,
1521 };
1522
1523 static int shift_pop(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
1524 {
1525 #define beginning       (cmd[0] == 'S') /* SHIFT */
1526         char *after, delimiter[2] = ",", *varsubst;
1527         size_t unused;
1528         struct ast_str *before = ast_str_thread_get(&result_buf, 16);
1529         char *(*search_func)(const char *s, int c) = (beginning ? strchr : strrchr);
1530         AST_DECLARE_APP_ARGS(args,
1531                 AST_APP_ARG(var);
1532                 AST_APP_ARG(delimiter);
1533         );
1534
1535         if (!before) {
1536                 return -1;
1537         }
1538
1539         AST_STANDARD_APP_ARGS(args, data);
1540
1541         if (ast_strlen_zero(args.var)) {
1542                 ast_log(LOG_WARNING, "%s requires a variable name\n", cmd);
1543                 return -1;
1544         }
1545
1546         varsubst = alloca(strlen(args.var) + 4);
1547         sprintf(varsubst, "${%s}", args.var);
1548         ast_str_substitute_variables(&before, 0, chan, varsubst);
1549
1550         if (args.argc > 1 && !ast_strlen_zero(args.delimiter)) {
1551                 ast_get_encoded_char(args.delimiter, delimiter, &unused);
1552         }
1553
1554         if (!ast_str_strlen(before)) {
1555                 /* Nothing to pop */
1556                 return -1;
1557         }
1558
1559         if (!(after = search_func(ast_str_buffer(before), delimiter[0]))) {
1560                 /* Only one entry in array */
1561                 ast_str_set(buf, len, "%s", ast_str_buffer(before));
1562                 pbx_builtin_setvar_helper(chan, args.var, "");
1563         } else {
1564                 *after++ = '\0';
1565                 ast_str_set(buf, len, "%s", beginning ? ast_str_buffer(before) : after);
1566                 pbx_builtin_setvar_helper(chan, args.var, beginning ? after : ast_str_buffer(before));
1567         }
1568
1569         return 0;
1570 #undef beginning
1571 }
1572
1573 static struct ast_custom_function shift_function = {
1574         .name = "SHIFT",
1575         .read2 = shift_pop,
1576 };
1577
1578 static struct ast_custom_function pop_function = {
1579         .name = "POP",
1580         .read2 = shift_pop,
1581 };
1582
1583 static int unshift_push(struct ast_channel *chan, const char *cmd, char *data, const char *new_value)
1584 {
1585 #define beginning       (cmd[0] == 'U') /* UNSHIFT */
1586         char delimiter[2] = ",", *varsubst;
1587         size_t unused;
1588         struct ast_str *buf, *previous_value;
1589         AST_DECLARE_APP_ARGS(args,
1590                 AST_APP_ARG(var);
1591                 AST_APP_ARG(delimiter);
1592         );
1593
1594         if (!(buf = ast_str_thread_get(&result_buf, 16)) ||
1595                 !(previous_value = ast_str_thread_get(&tmp_buf, 16))) {
1596                 return -1;
1597         }
1598
1599         AST_STANDARD_APP_ARGS(args, data);
1600
1601         if (ast_strlen_zero(args.var)) {
1602                 ast_log(LOG_WARNING, "%s requires a variable name\n", cmd);
1603                 return -1;
1604         }
1605
1606         if (args.argc > 1 && !ast_strlen_zero(args.delimiter)) {
1607                 ast_get_encoded_char(args.delimiter, delimiter, &unused);
1608         }
1609
1610         varsubst = alloca(strlen(args.var) + 4);
1611         sprintf(varsubst, "${%s}", args.var);
1612         ast_str_substitute_variables(&previous_value, 0, chan, varsubst);
1613
1614         if (!ast_str_strlen(previous_value)) {
1615                 ast_str_set(&buf, 0, "%s", new_value);
1616         } else {
1617                 ast_str_set(&buf, 0, "%s%c%s",
1618                         beginning ? new_value : ast_str_buffer(previous_value),
1619                         delimiter[0],
1620                         beginning ? ast_str_buffer(previous_value) : new_value);
1621         }
1622
1623         pbx_builtin_setvar_helper(chan, args.var, ast_str_buffer(buf));
1624
1625         return 0;
1626 #undef beginning
1627 }
1628
1629 static struct ast_custom_function push_function = {
1630         .name = "PUSH",
1631         .write = unshift_push,
1632 };
1633
1634 static struct ast_custom_function unshift_function = {
1635         .name = "UNSHIFT",
1636         .write = unshift_push,
1637 };
1638
1639 static int passthru(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
1640 {
1641         ast_str_set(buf, len, "%s", data);
1642         return 0;
1643 }
1644
1645 static struct ast_custom_function passthru_function = {
1646         .name = "PASSTHRU",
1647         .read2 = passthru,
1648 };
1649
1650 #ifdef TEST_FRAMEWORK
1651 AST_TEST_DEFINE(test_FIELDNUM)
1652 {
1653         int i, res = AST_TEST_PASS;
1654         struct ast_channel *chan;
1655         struct ast_str *str;
1656         char expression[256];
1657         struct {
1658                 const char *fields;
1659                 const char *delim;
1660                 const char *field;
1661                 const char *expected;
1662         } test_args[] = {
1663                 {"abc,def,ghi,jkl", "\\,",     "ghi", "3"},
1664                 {"abc def ghi jkl", " ",       "abc", "1"},
1665                 {"abc/def/ghi/jkl", "\\\\x2f", "def", "2"},
1666                 {"abc$def$ghi$jkl", "",        "ghi", "0"},
1667                 {"abc,def,ghi,jkl", "-",       "",    "0"},
1668                 {"abc-def-ghi-jkl", "-",       "mno", "0"}
1669         };
1670
1671         switch (cmd) {
1672         case TEST_INIT:
1673                 info->name = "func_FIELDNUM_test";
1674                 info->category = "/funcs/func_strings/";
1675                 info->summary = "Test FIELDNUM function";
1676                 info->description = "Verify FIELDNUM behavior";
1677                 return AST_TEST_NOT_RUN;
1678         case TEST_EXECUTE:
1679                 break;
1680         }
1681
1682         if (!(chan = ast_dummy_channel_alloc())) {
1683                 ast_test_status_update(test, "Unable to allocate dummy channel\n");
1684                 return AST_TEST_FAIL;
1685         }
1686
1687         if (!(str = ast_str_create(16))) {
1688                 ast_test_status_update(test, "Unable to allocate dynamic string buffer\n");
1689                 ast_channel_release(chan);
1690                 return AST_TEST_FAIL;
1691         }
1692
1693         for (i = 0; i < ARRAY_LEN(test_args); i++) {
1694                 struct ast_var_t *var = ast_var_assign("FIELDS", test_args[i].fields);
1695                 AST_LIST_INSERT_HEAD(&chan->varshead, var, entries);
1696
1697                 snprintf(expression, sizeof(expression), "${FIELDNUM(%s,%s,%s)}", var->name, test_args[i].delim, test_args[i].field);
1698                 ast_str_substitute_variables(&str, 0, chan, expression);
1699
1700                 AST_LIST_REMOVE(&chan->varshead, var, entries);
1701                 ast_var_delete(var);
1702
1703                 if (strcasecmp(ast_str_buffer(str), test_args[i].expected)) {
1704                         ast_test_status_update(test, "Evaluation of '%s' returned '%s' instead of the expected value '%s'\n",
1705                                 expression, ast_str_buffer(str), test_args[i].expected);
1706                         res = AST_TEST_FAIL;
1707                         break;
1708                 }
1709         }
1710
1711         ast_free(str);
1712         ast_channel_release(chan);
1713
1714         return res;
1715 }
1716
1717 AST_TEST_DEFINE(test_FILTER)
1718 {
1719         int i, res = AST_TEST_PASS;
1720         const char *test_strings[][2] = {
1721                 {"A-R",            "DAHDI"},
1722                 {"A\\-R",          "A"},
1723                 {"\\x41-R",        "DAHDI"},
1724                 {"0-9A-Ca-c",      "0042133333A12212"},
1725                 {"0-9a-cA-C_+\\-", "0042133333A12212"},
1726                 {NULL,             NULL},
1727         };
1728
1729         switch (cmd) {
1730         case TEST_INIT:
1731                 info->name = "func_FILTER_test";
1732                 info->category = "/funcs/func_strings/";
1733                 info->summary = "Test FILTER function";
1734                 info->description = "Verify FILTER behavior";
1735                 return AST_TEST_NOT_RUN;
1736         case TEST_EXECUTE:
1737                 break;
1738         }
1739
1740         for (i = 0; test_strings[i][0]; i++) {
1741                 char tmp[256], tmp2[256] = "";
1742                 snprintf(tmp, sizeof(tmp), "${FILTER(%s,0042133333&DAHDI/g1/2212)}", test_strings[i][0]);
1743                 pbx_substitute_variables_helper(NULL, tmp, tmp2, sizeof(tmp2) - 1);
1744                 if (strcmp(test_strings[i][1], tmp2)) {
1745                         ast_test_status_update(test, "Format string '%s' substituted to '%s'.  Expected '%s'.\n", test_strings[i][0], tmp2, test_strings[i][1]);
1746                         res = AST_TEST_FAIL;
1747                 }
1748         }
1749         return res;
1750 }
1751
1752 AST_TEST_DEFINE(test_STRREPLACE)
1753 {
1754         int i, res = AST_TEST_PASS;
1755         struct ast_channel *chan; /* dummy channel */
1756         struct ast_str *str; /* fancy string for holding comparing value */
1757
1758         const char *test_strings[][5] = {
1759                 {"Weasels have eaten my telephone system", "have eaten my", "are eating our", "", "Weasels are eating our telephone system"}, /*Test normal conditions */
1760                 {"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 */
1761                 {"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."},
1762                 {"One and one and one is three", "and", "plus", "1", "One plus one and one is three"}, /* Test <max-replacements> = 1*/
1763                 {"", "fhqwagads", "spelunker", NULL, ""}, /* Empty primary string */
1764                 {"Part of this string is missing.", "missing", NULL, NULL, "Part of this string is ."}, /* Empty replace string */
1765                 {"'Accidentally' left off a bunch of stuff.", NULL, NULL, NULL, ""}, /* Deliberate error test from too few args */
1766                 {"This test will also error.", "", "", "", ""}, /* Deliberate error test from blank find string */
1767                 {"This is an \"escape character\" test.", "\\\"escape character\\\"", "evil", NULL, "This is an evil test."}
1768         };
1769
1770         switch (cmd) {
1771         case TEST_INIT:
1772                 info->name = "func_STRREPLACE_test";
1773                 info->category = "/funcs/func_strings/";
1774                 info->summary = "Test STRREPLACE function";
1775                 info->description = "Verify STRREPLACE behavior";
1776                 return AST_TEST_NOT_RUN;
1777         case TEST_EXECUTE:
1778                 break;
1779         }
1780
1781         if (!(chan = ast_dummy_channel_alloc())) {
1782                 ast_test_status_update(test, "Unable to allocate dummy channel\n");
1783                 return AST_TEST_FAIL;
1784         }
1785
1786         if (!(str = ast_str_create(64))) {
1787                 ast_test_status_update(test, "Unable to allocate dynamic string buffer\n");
1788                 ast_channel_release(chan);
1789                 return AST_TEST_FAIL;
1790         }
1791
1792         for (i = 0; i < ARRAY_LEN(test_strings); i++) {
1793                 char tmp[512], tmp2[512] = "";
1794
1795                 struct ast_var_t *var = ast_var_assign("test_string", test_strings[i][0]);
1796                 AST_LIST_INSERT_HEAD(&chan->varshead, var, entries);
1797
1798                 if (test_strings[i][3]) {
1799                         snprintf(tmp, sizeof(tmp), "${STRREPLACE(%s,%s,%s,%s)}", "test_string", test_strings[i][1], test_strings[i][2], test_strings[i][3]);
1800                 } else if (test_strings[i][2]) {
1801                         snprintf(tmp, sizeof(tmp), "${STRREPLACE(%s,%s,%s)}", "test_string", test_strings[i][1], test_strings[i][2]);
1802                 } else if (test_strings[i][1]) {
1803                         snprintf(tmp, sizeof(tmp), "${STRREPLACE(%s,%s)}", "test_string", test_strings[i][1]);
1804                 } else {
1805                         snprintf(tmp, sizeof(tmp), "${STRREPLACE(%s)}", "test_string");
1806                 }
1807                 ast_str_substitute_variables(&str, 0, chan, tmp);
1808                 if (strcmp(test_strings[i][4], ast_str_buffer(str))) {
1809                         ast_test_status_update(test, "Format string '%s' substituted to '%s'.  Expected '%s'.\n", test_strings[i][0], tmp2, test_strings[i][4]);
1810                         res = AST_TEST_FAIL;
1811                 }
1812         }
1813
1814         ast_free(str);
1815         ast_channel_release(chan);
1816
1817         return res;
1818 }
1819 #endif
1820
1821 static int unload_module(void)
1822 {
1823         int res = 0;
1824
1825         AST_TEST_UNREGISTER(test_FIELDNUM);
1826         AST_TEST_UNREGISTER(test_FILTER);
1827         AST_TEST_UNREGISTER(test_STRREPLACE);
1828         res |= ast_custom_function_unregister(&fieldqty_function);
1829         res |= ast_custom_function_unregister(&fieldnum_function);
1830         res |= ast_custom_function_unregister(&filter_function);
1831         res |= ast_custom_function_unregister(&replace_function);
1832         res |= ast_custom_function_unregister(&strreplace_function);
1833         res |= ast_custom_function_unregister(&listfilter_function);
1834         res |= ast_custom_function_unregister(&regex_function);
1835         res |= ast_custom_function_unregister(&array_function);
1836         res |= ast_custom_function_unregister(&quote_function);
1837         res |= ast_custom_function_unregister(&csv_quote_function);
1838         res |= ast_custom_function_unregister(&len_function);
1839         res |= ast_custom_function_unregister(&strftime_function);
1840         res |= ast_custom_function_unregister(&strptime_function);
1841         res |= ast_custom_function_unregister(&eval_function);
1842         res |= ast_custom_function_unregister(&keypadhash_function);
1843         res |= ast_custom_function_unregister(&hashkeys_function);
1844         res |= ast_custom_function_unregister(&hash_function);
1845         res |= ast_unregister_application(app_clearhash);
1846         res |= ast_custom_function_unregister(&toupper_function);
1847         res |= ast_custom_function_unregister(&tolower_function);
1848         res |= ast_custom_function_unregister(&shift_function);
1849         res |= ast_custom_function_unregister(&pop_function);
1850         res |= ast_custom_function_unregister(&push_function);
1851         res |= ast_custom_function_unregister(&unshift_function);
1852         res |= ast_custom_function_unregister(&passthru_function);
1853
1854         return res;
1855 }
1856
1857 static int load_module(void)
1858 {
1859         int res = 0;
1860
1861         AST_TEST_REGISTER(test_FIELDNUM);
1862         AST_TEST_REGISTER(test_FILTER);
1863         AST_TEST_REGISTER(test_STRREPLACE);
1864         res |= ast_custom_function_register(&fieldqty_function);
1865         res |= ast_custom_function_register(&fieldnum_function);
1866         res |= ast_custom_function_register(&filter_function);
1867         res |= ast_custom_function_register(&replace_function);
1868         res |= ast_custom_function_register(&strreplace_function);
1869         res |= ast_custom_function_register(&listfilter_function);
1870         res |= ast_custom_function_register(&regex_function);
1871         res |= ast_custom_function_register(&array_function);
1872         res |= ast_custom_function_register(&quote_function);
1873         res |= ast_custom_function_register(&csv_quote_function);
1874         res |= ast_custom_function_register(&len_function);
1875         res |= ast_custom_function_register(&strftime_function);
1876         res |= ast_custom_function_register(&strptime_function);
1877         res |= ast_custom_function_register(&eval_function);
1878         res |= ast_custom_function_register(&keypadhash_function);
1879         res |= ast_custom_function_register(&hashkeys_function);
1880         res |= ast_custom_function_register(&hash_function);
1881         res |= ast_register_application_xml(app_clearhash, exec_clearhash);
1882         res |= ast_custom_function_register(&toupper_function);
1883         res |= ast_custom_function_register(&tolower_function);
1884         res |= ast_custom_function_register(&shift_function);
1885         res |= ast_custom_function_register(&pop_function);
1886         res |= ast_custom_function_register(&push_function);
1887         res |= ast_custom_function_register(&unshift_function);
1888         res |= ast_custom_function_register(&passthru_function);
1889
1890         return res;
1891 }
1892
1893 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "String handling dialplan functions");