2 * Asterisk -- An open source telephony toolkit.
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
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.
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.
21 * \brief String manipulation dialplan functions
23 * \author Tilghman Lesher
24 * \author Anothony Minessale II
29 <support_level>core</support_level>
34 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
39 #include "asterisk/module.h"
40 #include "asterisk/channel.h"
41 #include "asterisk/pbx.h"
42 #include "asterisk/utils.h"
43 #include "asterisk/app.h"
44 #include "asterisk/localtime.h"
45 #include "asterisk/test.h"
47 AST_THREADSTORAGE(result_buf);
48 AST_THREADSTORAGE(tmp_buf);
51 <function name="FIELDQTY" language="en_US">
53 Count the fields with an arbitrary delimiter
56 <parameter name="varname" required="true" />
57 <parameter name="delim" required="true" />
60 <para>The delimiter may be specified as a special or extended ASCII character, by encoding it. The characters
61 <literal>\n</literal>, <literal>\r</literal>, and <literal>\t</literal> are all recognized as the newline,
62 carriage return, and tab characters, respectively. Also, octal and hexadecimal specifications are recognized
63 by the patterns <literal>\0nnn</literal> and <literal>\xHH</literal>, respectively. For example, if you wanted
64 to encode a comma as the delimiter, you could use either <literal>\054</literal> or <literal>\x2C</literal>.</para>
65 <para>Example: If ${example} contains <literal>ex-amp-le</literal>, then ${FIELDQTY(example,-)} returns 3.</para>
68 <function name="FIELDNUM" language="en_US">
70 Return the 1-based offset of a field in a list
73 <parameter name="varname" required="true" />
74 <parameter name="delim" required="true" />
75 <parameter name="value" required="true" />
78 <para>Search the variable named <replaceable>varname</replaceable> for the string <replaceable>value</replaceable>
79 delimited by <replaceable>delim</replaceable> and return a 1-based offset as to its location. If not found
80 or an error occured, return <literal>0</literal>.</para>
81 <para>The delimiter may be specified as a special or extended ASCII character, by encoding it. The characters
82 <literal>\n</literal>, <literal>\r</literal>, and <literal>\t</literal> are all recognized as the newline,
83 carriage return, and tab characters, respectively. Also, octal and hexadecimal specifications are recognized
84 by the patterns <literal>\0nnn</literal> and <literal>\xHH</literal>, respectively. For example, if you wanted
85 to encode a comma as the delimiter, you could use either <literal>\054</literal> or <literal>\x2C</literal>.</para>
86 <para>Example: If ${example} contains <literal>ex-amp-le</literal>, then ${FIELDNUM(example,-,amp)} returns 2.</para>
89 <function name="LISTFILTER" language="en_US">
90 <synopsis>Remove an item from a list, by name.</synopsis>
92 <parameter name="varname" required="true" />
93 <parameter name="delim" required="true" default="," />
94 <parameter name="value" required="true" />
97 <para>Remove <replaceable>value</replaceable> from the list contained in the <replaceable>varname</replaceable>
98 variable, where the list delimiter is specified by the <replaceable>delim</replaceable> parameter. This is
99 very useful for removing a single channel name from a list of channels, for example.</para>
102 <function name="FILTER" language="en_US">
104 Filter the string to include only the allowed characters
107 <parameter name="allowed-chars" required="true" />
108 <parameter name="string" required="true" />
111 <para>Permits all characters listed in <replaceable>allowed-chars</replaceable>,
112 filtering all others outs. In addition to literally listing the characters,
113 you may also use ranges of characters (delimited by a <literal>-</literal></para>
114 <para>Hexadecimal characters started with a <literal>\x</literal>(i.e. \x20)</para>
115 <para>Octal characters started with a <literal>\0</literal> (i.e. \040)</para>
116 <para>Also <literal>\t</literal>,<literal>\n</literal> and <literal>\r</literal> are recognized.</para>
117 <note><para>If you want the <literal>-</literal> character it needs to be prefixed with a
118 <literal>\</literal></para></note>
121 <function name="REPLACE" language="en_US">
123 Replace a set of characters in a given string with another character.
126 <parameter name="varname" required="true" />
127 <parameter name="find-chars" required="true" />
128 <parameter name="replace-char" required="false" />
131 <para>Iterates through a string replacing all the <replaceable>find-chars</replaceable> with
132 <replaceable>replace-char</replaceable>. <replaceable>replace-char</replaceable> may be either
133 empty or contain one character. If empty, all <replaceable>find-chars</replaceable> will be
134 deleted from the output.</para>
135 <note><para>The replacement only occurs in the output. The original variable is not
136 altered.</para></note>
139 <function name="STRREPLACE" language="en_US">
141 Replace instances of a substring within a string with another string.
144 <parameter name="varname" required="true" />
145 <parameter name="find-string" required="true" />
146 <parameter name="replace-string" required="false" />
147 <parameter name="max-replacements" required="false" />
150 <para>Searches for all instances of the <replaceable>find-string</replaceable> in provided variable and
151 replaces them with <replaceable>replace-string</replaceable>. If <replaceable>replace-string</replaceable>
152 is an empty string, this will effecively delete that substring. If <replaceable>max-replacements</replaceable>
153 is specified, this function will stop after performing replacements <replaceable>max-replacements</replaceable> times.</para>
154 <note><para>The replacement only occurs in the output. The original variable is not altered.</para></note>
157 <function name="PASSTHRU" language="en_US">
159 Pass the given argument back as a value.
162 <parameter name="string" required="false" />
165 <para>Literally returns the given <replaceable>string</replaceable>. The intent is to permit
166 other dialplan functions which take a variable name as an argument to be able to take a literal
167 string, instead.</para>
168 <note><para>The functions which take a variable name need to be passed var and not
169 ${var}. Similarly, use PASSTHRU() and not ${PASSTHRU()}.</para></note>
170 <para>Example: ${CHANNEL} contains SIP/321-1</para>
171 <para> ${CUT(PASSTHRU(${CUT(CHANNEL,-,1)}),/,2)}) will return 321</para>
174 <function name="REGEX" language="en_US">
176 Check string against a regular expression.
179 <parameter name=""regular expression"" required="true" />
180 <parameter name="string" required="true" />
183 <para>Return <literal>1</literal> on regular expression match or <literal>0</literal> otherwise</para>
184 <para>Please note that the space following the double quotes separating the
185 regex from the data is optional and if present, is skipped. If a space is
186 desired at the beginning of the data, then put two spaces there; the second
187 will not be skipped.</para>
190 <application name="ClearHash" language="en_US">
192 Clear the keys from a specified hashname.
195 <parameter name="hashname" required="true" />
198 <para>Clears all keys out of the specified <replaceable>hashname</replaceable>.</para>
201 <function name="HASH" language="en_US">
203 Implementation of a dialplan associative array
206 <parameter name="hashname" required="true" />
207 <parameter name="hashkey" />
210 <para>In two arguments mode, gets and sets values to corresponding keys within
211 a named associative array. The single-argument mode will only work when assigned
212 to from a function defined by func_odbc</para>
215 <function name="HASHKEYS" language="en_US">
217 Retrieve the keys of the HASH() function.
220 <parameter name="hashname" required="true" />
223 <para>Returns a comma-delimited list of the current keys of the associative array
224 defined by the HASH() function. Note that if you iterate over the keys of
225 the result, adding keys during iteration will cause the result of the HASHKEYS()
226 function to change.</para>
229 <function name="KEYPADHASH" language="en_US">
231 Hash the letters in string into equivalent keypad numbers.
234 <parameter name="string" required="true" />
237 <para>Example: ${KEYPADHASH(Les)} returns "537"</para>
240 <function name="ARRAY" language="en_US">
242 Allows setting multiple variables at once.
245 <parameter name="var1" required="true" />
246 <parameter name="var2" required="false" multiple="true" />
247 <parameter name="varN" required="false" />
250 <para>The comma-delimited list passed as a value to which the function is set will
251 be interpreted as a set of values to which the comma-delimited list of
252 variable names in the argument should be set.</para>
253 <para>Example: Set(ARRAY(var1,var2)=1,2) will set var1 to 1 and var2 to 2</para>
256 <function name="STRPTIME" language="en_US">
258 Returns the epoch of the arbitrary date/time string structured as described by the format.
261 <parameter name="datetime" required="true" />
262 <parameter name="timezone" required="true" />
263 <parameter name="format" required="true" />
266 <para>This is useful for converting a date into <literal>EPOCH</literal> time,
267 possibly to pass to an application like SayUnixTime or to calculate the difference
268 between the two date strings</para>
269 <para>Example: ${STRPTIME(2006-03-01 07:30:35,America/Chicago,%Y-%m-%d %H:%M:%S)} returns 1141219835</para>
272 <function name="STRFTIME" language="en_US">
274 Returns the current date/time in the specified format.
277 <parameter name="epoch" />
278 <parameter name="timezone" />
279 <parameter name="format" />
282 <para>STRFTIME supports all of the same formats as the underlying C function
283 <emphasis>strftime(3)</emphasis>.
284 It also supports the following format: <literal>%[n]q</literal> - fractions of a second,
285 with leading zeros.</para>
286 <para>Example: <literal>%3q</literal> will give milliseconds and <literal>%1q</literal>
287 will give tenths of a second. The default is set at milliseconds (n=3).
288 The common case is to use it in combination with %S, as in <literal>%S.%3q</literal>.</para>
291 <ref type="manpage">strftime(3)</ref>
294 <function name="EVAL" language="en_US">
296 Evaluate stored variables
299 <parameter name="variable" required="true" />
302 <para>Using EVAL basically causes a string to be evaluated twice.
303 When a variable or expression is in the dialplan, it will be
304 evaluated at runtime. However, if the results of the evaluation
305 is in fact another variable or expression, using EVAL will have it
306 evaluated a second time.</para>
307 <para>Example: If the <variable>MYVAR</variable> contains
308 <variable>OTHERVAR</variable>, then the result of ${EVAL(
309 <variable>MYVAR</variable>)} in the dialplan will be the
310 contents of <variable>OTHERVAR</variable>. Normally just
311 putting <variable>MYVAR</variable> in the dialplan the result
312 would be <variable>OTHERVAR</variable>.</para>
315 <function name="TOUPPER" language="en_US">
317 Convert string to all uppercase letters.
320 <parameter name="string" required="true" />
323 <para>Example: ${TOUPPER(Example)} returns "EXAMPLE"</para>
326 <function name="TOLOWER" language="en_US">
328 Convert string to all lowercase letters.
331 <parameter name="string" required="true" />
334 <para>Example: ${TOLOWER(Example)} returns "example"</para>
337 <function name="LEN" language="en_US">
339 Return the length of the string given.
342 <parameter name="string" required="true" />
345 <para>Example: ${LEN(example)} returns 7</para>
348 <function name="QUOTE" language="en_US">
350 Quotes a given string, escaping embedded quotes as necessary
353 <parameter name="string" required="true" />
356 <para>Example: ${QUOTE(ab"c"de)} will return ""ab\"c\"de""</para>
359 <function name="CSV_QUOTE" language="en_US">
361 Quotes a given string for use in a CSV file, escaping embedded quotes as necessary
364 <parameter name="string" required="true" />
367 <para>Example: ${CSV_QUOTE("a,b" 123)} will return """a,b"" 123"</para>
370 <function name="SHIFT" language="en_US">
372 Removes and returns the first item off of a variable containing delimited text
375 <parameter name="varname" required="true" />
376 <parameter name="delimiter" required="false" default="," />
379 <para>Example:</para>
380 <para>exten => s,1,Set(array=one,two,three)</para>
381 <para>exten => s,n,While($["${SET(var=${SHIFT(array)})}" != ""])</para>
382 <para>exten => s,n,NoOp(var is ${var})</para>
383 <para>exten => s,n,EndWhile</para>
384 <para>This would iterate over each value in array, left to right, and
385 would result in NoOp(var is one), NoOp(var is two), and
386 NoOp(var is three) being executed.
390 <function name="POP" language="en_US">
392 Removes and returns the last item off of a variable containing delimited text
395 <parameter name="varname" required="true" />
396 <parameter name="delimiter" required="false" default="," />
399 <para>Example:</para>
400 <para>exten => s,1,Set(array=one,two,three)</para>
401 <para>exten => s,n,While($["${SET(var=${POP(array)})}" != ""])</para>
402 <para>exten => s,n,NoOp(var is ${var})</para>
403 <para>exten => s,n,EndWhile</para>
404 <para>This would iterate over each value in array, right to left, and
405 would result in NoOp(var is three), NoOp(var is two), and
406 NoOp(var is one) being executed.
410 <function name="PUSH" language="en_US">
412 Appends one or more values to the end of a variable containing delimited text
415 <parameter name="varname" required="true" />
416 <parameter name="delimiter" required="false" default="," />
419 <para>Example: Set(PUSH(array)=one,two,three) would append one,
420 two, and three to the end of the values stored in the variable
425 <function name="UNSHIFT" language="en_US">
427 Inserts one or more values to the beginning of a variable containing delimited text
430 <parameter name="varname" required="true" />
431 <parameter name="delimiter" required="false" default="," />
434 <para>Example: Set(UNSHIFT(array)=one,two,three) would insert one,
435 two, and three before the values stored in the variable
442 static int function_fieldqty_helper(struct ast_channel *chan, const char *cmd,
443 char *parse, char *buf, struct ast_str **sbuf, ssize_t len)
446 struct ast_str *str = ast_str_thread_get(&result_buf, 16);
448 AST_DECLARE_APP_ARGS(args,
449 AST_APP_ARG(varname);
459 AST_STANDARD_APP_ARGS(args, parse);
461 ast_get_encoded_char(args.delim, delim, &delim_used);
463 varsubst = ast_alloca(strlen(args.varname) + 4);
465 sprintf(varsubst, "${%s}", args.varname);
466 ast_str_substitute_variables(&str, 0, chan, varsubst);
467 if (ast_str_strlen(str) == 0) {
470 char *varval = ast_str_buffer(str);
471 while (strsep(&varval, delim)) {
479 ast_str_set(sbuf, len, "%d", fieldcount);
481 snprintf(buf, len, "%d", fieldcount);
487 static int function_fieldqty(struct ast_channel *chan, const char *cmd,
488 char *parse, char *buf, size_t len)
490 return function_fieldqty_helper(chan, cmd, parse, buf, NULL, len);
493 static int function_fieldqty_str(struct ast_channel *chan, const char *cmd,
494 char *parse, struct ast_str **buf, ssize_t len)
496 return function_fieldqty_helper(chan, cmd, parse, NULL, buf, len);
499 static struct ast_custom_function fieldqty_function = {
501 .read = function_fieldqty,
502 .read2 = function_fieldqty_str,
505 static int function_fieldnum_helper(struct ast_channel *chan, const char *cmd,
506 char *parse, char *buf, struct ast_str **sbuf, ssize_t len)
508 char *varsubst, *field;
509 struct ast_str *str = ast_str_thread_get(&result_buf, 16);
510 int fieldindex = 0, res = 0;
511 AST_DECLARE_APP_ARGS(args,
512 AST_APP_ARG(varname);
523 AST_STANDARD_APP_ARGS(args, parse);
526 ast_log(LOG_ERROR, "Usage: FIELDNUM(<listname>,<delimiter>,<fieldvalue>)\n");
529 varsubst = ast_alloca(strlen(args.varname) + 4);
530 sprintf(varsubst, "${%s}", args.varname);
532 ast_str_substitute_variables(&str, 0, chan, varsubst);
534 if (ast_str_strlen(str) == 0 || ast_strlen_zero(args.delim)) {
536 } else if (ast_get_encoded_char(args.delim, delim, &delim_used) == -1) {
539 char *varval = ast_str_buffer(str);
541 while ((field = strsep(&varval, delim)) != NULL) {
544 if (!strcasecmp(field, args.field)) {
558 ast_str_set(sbuf, len, "%d", fieldindex);
560 snprintf(buf, len, "%d", fieldindex);
566 static int function_fieldnum(struct ast_channel *chan, const char *cmd,
567 char *parse, char *buf, size_t len)
569 return function_fieldnum_helper(chan, cmd, parse, buf, NULL, len);
572 static int function_fieldnum_str(struct ast_channel *chan, const char *cmd,
573 char *parse, struct ast_str **buf, ssize_t len)
575 return function_fieldnum_helper(chan, cmd, parse, NULL, buf, len);
578 static struct ast_custom_function fieldnum_function = {
580 .read = function_fieldnum,
581 .read2 = function_fieldnum_str,
584 static int listfilter(struct ast_channel *chan, const char *cmd, char *parse, char *buf, struct ast_str **bufstr, ssize_t len)
586 AST_DECLARE_APP_ARGS(args,
587 AST_APP_ARG(listname);
588 AST_APP_ARG(delimiter);
589 AST_APP_ARG(fieldvalue);
591 struct ast_str *orig_list = ast_str_thread_get(&tmp_buf, 16);
592 const char *begin, *cur, *next;
593 int dlen, flen, first = 1;
594 struct ast_str *result, **result_ptr = &result;
595 char *delim, *varsubst;
597 AST_STANDARD_APP_ARGS(args, parse);
600 if (!(result = ast_str_thread_get(&result_buf, 16))) {
604 /* Place the result directly into the output buffer */
609 ast_log(LOG_ERROR, "Usage: LISTFILTER(<listname>,<delimiter>,<fieldvalue>)\n");
613 varsubst = ast_alloca(strlen(args.listname) + 4);
614 sprintf(varsubst, "${%s}", args.listname);
616 /* If we don't lock the channel, the variable could disappear out from underneath us. */
618 ast_channel_lock(chan);
620 ast_str_substitute_variables(&orig_list, 0, chan, varsubst);
621 if (!ast_str_strlen(orig_list)) {
622 ast_log(LOG_ERROR, "List variable '%s' not found\n", args.listname);
624 ast_channel_unlock(chan);
629 /* If the string isn't there, just copy out the string and be done with it. */
630 if (!strstr(ast_str_buffer(orig_list), args.fieldvalue)) {
632 ast_copy_string(buf, ast_str_buffer(orig_list), len);
634 ast_str_set(result_ptr, len, "%s", ast_str_buffer(orig_list));
637 ast_channel_unlock(chan);
642 dlen = strlen(args.delimiter);
643 delim = ast_alloca(dlen + 1);
644 ast_get_encoded_str(args.delimiter, delim, dlen + 1);
646 if ((dlen = strlen(delim)) == 0) {
651 flen = strlen(args.fieldvalue);
653 ast_str_reset(*result_ptr);
654 /* Enough space for any result */
656 ast_str_make_space(result_ptr, len ? len : ast_str_strlen(orig_list) + 1);
659 begin = ast_str_buffer(orig_list);
660 next = strstr(begin, delim);
663 /* Find next boundary */
666 next = strstr(cur + dlen, delim);
668 cur = strchr(begin + dlen, '\0');
671 if (flen == cur - begin && !strncmp(begin, args.fieldvalue, flen)) {
673 begin += flen + dlen;
675 /* Copy field to output */
677 ast_str_append(result_ptr, len, "%s", delim);
680 ast_str_append_substr(result_ptr, len, begin, cur - begin);
684 } while (*cur != '\0');
686 ast_channel_unlock(chan);
690 ast_copy_string(buf, ast_str_buffer(result), len);
696 static int listfilter_read(struct ast_channel *chan, const char *cmd, char *parse, char *buf, size_t len)
698 return listfilter(chan, cmd, parse, buf, NULL, len);
701 static int listfilter_read2(struct ast_channel *chan, const char *cmd, char *parse, struct ast_str **buf, ssize_t len)
703 return listfilter(chan, cmd, parse, NULL, buf, len);
706 static struct ast_custom_function listfilter_function = {
707 .name = "LISTFILTER",
708 .read = listfilter_read,
709 .read2 = listfilter_read2,
712 static int filter(struct ast_channel *chan, const char *cmd, char *parse, char *buf,
715 AST_DECLARE_APP_ARGS(args,
716 AST_APP_ARG(allowed);
721 char allowed[256] = "";
722 size_t allowedlen = 0;
723 int32_t bitfield[8] = { 0, }; /* 256 bits */
725 AST_STANDARD_RAW_ARGS(args, parse);
728 ast_log(LOG_ERROR, "Usage: FILTER(<allowed-chars>,<string>)\n");
732 if (args.allowed[0] == '"' && !ast_opt_dont_warn) {
733 ast_log(LOG_WARNING, "FILTER allowed characters includes the quote (\") character. This may not be what you want.\n");
737 for (; *(args.allowed);) {
741 if (ast_get_encoded_char(args.allowed, &c1, &consumed))
743 args.allowed += consumed;
745 if (*(args.allowed) == '-') {
746 if (ast_get_encoded_char(args.allowed + 1, &c2, &consumed))
748 args.allowed += consumed + 1;
750 if ((unsigned char) c2 < (unsigned char) c1 && !ast_opt_dont_warn) {
751 ast_log(LOG_WARNING, "Range wrapping in FILTER(%s,%s). This may not be what you want.\n", parse, args.string);
755 * Looks a little strange, until you realize that we can overflow
756 * the size of a char.
758 for (ac = (unsigned char) c1; ac != (unsigned char) c2; ac++) {
759 bitfield[ac / 32] |= 1 << (ac % 32);
761 bitfield[ac / 32] |= 1 << (ac % 32);
763 ast_debug(4, "c1=%d, c2=%d\n", c1, c2);
765 ac = (unsigned char) c1;
766 ast_debug(4, "c1=%d, consumed=%d, args.allowed=%s\n", c1, (int) consumed, args.allowed - consumed);
767 bitfield[ac / 32] |= 1 << (ac % 32);
771 for (ac = 1; ac != 0; ac++) {
772 if (bitfield[ac / 32] & (1 << (ac % 32))) {
773 allowed[allowedlen++] = ac;
777 ast_debug(1, "Allowed: %s\n", allowed);
779 for (; *(args.string) && (buf + len - 1 > outbuf); (args.string)++) {
780 if (strchr(allowed, *(args.string)))
781 *outbuf++ = *(args.string);
788 static struct ast_custom_function filter_function = {
793 static int replace(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
795 AST_DECLARE_APP_ARGS(args,
796 AST_APP_ARG(varname);
798 AST_APP_ARG(replace);
800 char *strptr, *varsubst;
801 struct ast_str *str = ast_str_thread_get(&result_buf, 16);
802 char find[256]; /* Only 256 characters possible */
803 char replace[2] = "";
806 AST_STANDARD_APP_ARGS(args, data);
813 ast_log(LOG_ERROR, "Usage: %s(<varname>,<search-chars>[,<replace-char>])\n", cmd);
818 ast_get_encoded_str(args.find, find, sizeof(find));
819 ast_get_encoded_char(args.replace, replace, &unused);
821 if (ast_strlen_zero(find) || ast_strlen_zero(args.varname)) {
822 ast_log(LOG_ERROR, "The characters to search for and the variable name must not be empty.\n");
826 varsubst = ast_alloca(strlen(args.varname) + 4);
827 sprintf(varsubst, "${%s}", args.varname);
828 ast_str_substitute_variables(&str, 0, chan, varsubst);
830 if (!ast_str_strlen(str)) {
831 /* Blank, nothing to replace */
835 ast_debug(3, "String to search: (%s)\n", ast_str_buffer(str));
836 ast_debug(3, "Characters to find: (%s)\n", find);
837 ast_debug(3, "Character to replace with: (%s)\n", replace);
839 for (strptr = ast_str_buffer(str); *strptr; strptr++) {
840 /* buf is already a mutable buffer, so we construct the result
842 if (strchr(find, *strptr)) {
843 if (ast_strlen_zero(replace)) {
844 memmove(strptr, strptr + 1, strlen(strptr + 1) + 1);
847 /* Replace character */
853 ast_str_set(buf, len, "%s", ast_str_buffer(str));
857 static struct ast_custom_function replace_function = {
862 static int strreplace(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
864 char *varsubstr; /* substring for input var */
865 char *start; /* Starting pos of substring search. */
866 char *end; /* Ending pos of substring search. */
867 int find_size; /* length of given find-string */
868 unsigned max_matches; /* number of matches we find before terminating search */
869 unsigned count; /* loop counter */
870 struct ast_str *str = ast_str_thread_get(&result_buf, 16); /* Holds the data obtained from varname */
872 AST_DECLARE_APP_ARGS(args,
873 AST_APP_ARG(varname);
874 AST_APP_ARG(find_string);
875 AST_APP_ARG(replace_string);
876 AST_APP_ARG(max_replacements);
877 AST_APP_ARG(other); /* Any remining unused arguments */
880 /* Guarantee output string is empty to start with. */
884 /* We failed to allocate str, forget it. We failed. */
888 /* Parse the arguments. */
889 AST_STANDARD_APP_ARGS(args, data);
892 /* Didn't receive enough arguments to do anything */
894 "Usage: %s(<varname>,<find-string>[,<replace-string>,[<max-replacements>]])\n",
899 /* No var name specified. Return failure, string is already empty. */
900 if (ast_strlen_zero(args.varname)) {
904 /* Zero length find strings are a no-no. Kill the function if we run into one. */
905 if (ast_strlen_zero(args.find_string)) {
906 ast_log(LOG_ERROR, "No <find-string> specified\n");
909 find_size = strlen(args.find_string);
911 /* set varsubstr to the matching variable */
912 varsubstr = ast_alloca(strlen(args.varname) + 4);
913 sprintf(varsubstr, "${%s}", args.varname);
914 ast_str_substitute_variables(&str, 0, chan, varsubstr);
916 /* Determine how many replacements are allowed. */
917 if (!args.max_replacements
918 || (max_matches = atoi(args.max_replacements)) <= 0) {
919 /* Unlimited replacements are allowed. */
923 /* Generate the search and replaced string. */
924 start = ast_str_buffer(str);
925 for (count = 0; count < max_matches; ++count) {
926 end = strstr(start, args.find_string);
928 /* Did not find a matching substring in the remainder. */
932 /* Replace the found substring. */
934 ast_str_append(buf, len, "%s", start);
935 if (args.replace_string) {
936 /* Append the replacement string */
937 ast_str_append(buf, len, "%s", args.replace_string);
939 start = end + find_size;
941 ast_str_append(buf, len, "%s", start);
946 static struct ast_custom_function strreplace_function = {
947 .name = "STRREPLACE",
951 static int regex(struct ast_channel *chan, const char *cmd, char *parse, char *buf,
954 AST_DECLARE_APP_ARGS(args,
964 AST_NONSTANDARD_APP_ARGS(args, parse, '"');
966 if (args.argc != 3) {
967 ast_log(LOG_ERROR, "Unexpected arguments: should have been in the form '\"<regex>\" <string>'\n");
970 if ((*args.str == ' ') || (*args.str == '\t'))
973 ast_debug(1, "FUNCTION REGEX (%s)(%s)\n", args.reg, args.str);
975 if ((errcode = regcomp(®exbuf, args.reg, REG_EXTENDED | REG_NOSUB))) {
976 regerror(errcode, ®exbuf, buf, len);
977 ast_log(LOG_WARNING, "Malformed input %s(%s): %s\n", cmd, parse, buf);
981 strcpy(buf, regexec(®exbuf, args.str, 0, NULL, 0) ? "0" : "1");
988 static struct ast_custom_function regex_function = {
993 #define HASH_PREFIX "~HASH~%s~"
994 #define HASH_FORMAT HASH_PREFIX "%s~"
996 static char *app_clearhash = "ClearHash";
998 /* This function probably should migrate to main/pbx.c, as pbx_builtin_clearvar_prefix() */
999 static void clearvar_prefix(struct ast_channel *chan, const char *prefix)
1001 struct ast_var_t *var;
1002 int len = strlen(prefix);
1003 AST_LIST_TRAVERSE_SAFE_BEGIN(ast_channel_varshead(chan), var, entries) {
1004 if (strncmp(prefix, ast_var_name(var), len) == 0) {
1005 AST_LIST_REMOVE_CURRENT(entries);
1009 AST_LIST_TRAVERSE_SAFE_END
1012 static int exec_clearhash(struct ast_channel *chan, const char *data)
1015 snprintf(prefix, sizeof(prefix), HASH_PREFIX, data ? (char *)data : "null");
1016 clearvar_prefix(chan, prefix);
1020 static int array(struct ast_channel *chan, const char *cmd, char *var,
1023 AST_DECLARE_APP_ARGS(arg1,
1024 AST_APP_ARG(var)[100];
1026 AST_DECLARE_APP_ARGS(arg2,
1027 AST_APP_ARG(val)[100];
1029 char *origvar = "", *value2, varname[256];
1035 value2 = ast_strdupa(value);
1037 if (!strcmp(cmd, "HASH")) {
1038 const char *var2 = pbx_builtin_getvar_helper(chan, "~ODBCFIELDS~");
1041 var = ast_strdupa(var2);
1044 ast_autoservice_stop(chan);
1050 /* The functions this will generally be used with are SORT and ODBC_*, which
1051 * both return comma-delimited lists. However, if somebody uses literal lists,
1052 * their commas will be translated to vertical bars by the load, and I don't
1053 * want them to be surprised by the result. Hence, we prefer commas as the
1054 * delimiter, but we'll fall back to vertical bars if commas aren't found.
1056 ast_debug(1, "array (%s=%s)\n", var, S_OR(value2, ""));
1057 AST_STANDARD_APP_ARGS(arg1, var);
1059 AST_STANDARD_APP_ARGS(arg2, value2);
1061 for (i = 0; i < arg1.argc; i++) {
1062 ast_debug(1, "array set value (%s=%s)\n", arg1.var[i],
1063 S_OR(arg2.val[i], ""));
1064 if (i < arg2.argc) {
1066 if (origvar[0] == '_') {
1067 if (origvar[1] == '_') {
1068 snprintf(varname, sizeof(varname), "__" HASH_FORMAT, origvar + 2, arg1.var[i]);
1070 snprintf(varname, sizeof(varname), "_" HASH_FORMAT, origvar + 1, arg1.var[i]);
1073 snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]);
1076 pbx_builtin_setvar_helper(chan, varname, arg2.val[i]);
1078 pbx_builtin_setvar_helper(chan, arg1.var[i], arg2.val[i]);
1081 /* We could unset the variable, by passing a NULL, but due to
1082 * pushvar semantics, that could create some undesired behavior. */
1084 snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]);
1085 pbx_builtin_setvar_helper(chan, varname, "");
1087 pbx_builtin_setvar_helper(chan, arg1.var[i], "");
1095 static int hashkeys_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1097 struct ast_var_t *newvar;
1098 struct ast_str *prefix = ast_str_alloca(80);
1100 ast_str_set(&prefix, -1, HASH_PREFIX, data);
1101 memset(buf, 0, len);
1103 AST_LIST_TRAVERSE(ast_channel_varshead(chan), newvar, entries) {
1104 if (strncmp(ast_str_buffer(prefix), ast_var_name(newvar), ast_str_strlen(prefix)) == 0) {
1105 /* Copy everything after the prefix */
1106 strncat(buf, ast_var_name(newvar) + ast_str_strlen(prefix), len - strlen(buf) - 1);
1107 /* Trim the trailing ~ */
1108 buf[strlen(buf) - 1] = ',';
1111 /* Trim the trailing comma */
1112 buf[strlen(buf) - 1] = '\0';
1116 static int hashkeys_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
1118 struct ast_var_t *newvar;
1119 struct ast_str *prefix = ast_str_alloca(80);
1122 ast_str_set(&prefix, -1, HASH_PREFIX, data);
1124 AST_LIST_TRAVERSE(ast_channel_varshead(chan), newvar, entries) {
1125 if (strncmp(ast_str_buffer(prefix), ast_var_name(newvar), ast_str_strlen(prefix)) == 0) {
1126 /* Copy everything after the prefix */
1127 ast_str_append(buf, len, "%s", ast_var_name(newvar) + ast_str_strlen(prefix));
1128 /* Trim the trailing ~ */
1129 tmp = ast_str_buffer(*buf);
1130 tmp[ast_str_strlen(*buf) - 1] = ',';
1133 /* Trim the trailing comma */
1134 tmp = ast_str_buffer(*buf);
1135 tmp[ast_str_strlen(*buf) - 1] = '\0';
1139 static int hash_write(struct ast_channel *chan, const char *cmd, char *var, const char *value)
1142 AST_DECLARE_APP_ARGS(arg,
1143 AST_APP_ARG(hashname);
1144 AST_APP_ARG(hashkey);
1147 if (!strchr(var, ',')) {
1148 /* Single argument version */
1149 return array(chan, "HASH", var, value);
1152 AST_STANDARD_APP_ARGS(arg, var);
1153 if (arg.hashname[0] == '_') {
1154 if (arg.hashname[1] == '_') {
1155 snprintf(varname, sizeof(varname), "__" HASH_FORMAT, arg.hashname + 2, arg.hashkey);
1157 snprintf(varname, sizeof(varname), "_" HASH_FORMAT, arg.hashname + 1, arg.hashkey);
1160 snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey);
1162 pbx_builtin_setvar_helper(chan, varname, value);
1167 static int hash_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1170 const char *varvalue;
1171 AST_DECLARE_APP_ARGS(arg,
1172 AST_APP_ARG(hashname);
1173 AST_APP_ARG(hashkey);
1176 AST_STANDARD_APP_ARGS(arg, data);
1177 if (arg.argc == 2) {
1178 snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey);
1179 varvalue = pbx_builtin_getvar_helper(chan, varname);
1181 ast_copy_string(buf, varvalue, len);
1184 } else if (arg.argc == 1) {
1185 char colnames[4096];
1187 AST_DECLARE_APP_ARGS(arg2,
1188 AST_APP_ARG(col)[100];
1191 /* Get column names, in no particular order */
1192 hashkeys_read(chan, "HASHKEYS", arg.hashname, colnames, sizeof(colnames));
1193 pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", colnames);
1195 AST_STANDARD_APP_ARGS(arg2, colnames);
1198 /* Now get the corresponding column values, in exactly the same order */
1199 for (i = 0; i < arg2.argc; i++) {
1200 snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg2.col[i]);
1201 varvalue = pbx_builtin_getvar_helper(chan, varname);
1202 strncat(buf, varvalue, len - strlen(buf) - 1);
1203 strncat(buf, ",", len - strlen(buf) - 1);
1206 /* Strip trailing comma */
1207 buf[strlen(buf) - 1] = '\0';
1213 static struct ast_custom_function hash_function = {
1215 .write = hash_write,
1219 static struct ast_custom_function hashkeys_function = {
1221 .read = hashkeys_read,
1222 .read2 = hashkeys_read2,
1225 static struct ast_custom_function array_function = {
1230 static int quote(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1232 char *bufptr = buf, *dataptr = data;
1234 if (len < 3){ /* at least two for quotes and one for binary zero */
1235 ast_log(LOG_ERROR, "Not enough buffer\n");
1239 if (ast_strlen_zero(data)) {
1240 ast_log(LOG_WARNING, "No argument specified!\n");
1241 ast_copy_string(buf, "\"\"", len);
1246 for (; bufptr < buf + len - 3; dataptr++) {
1247 if (*dataptr == '\\') {
1250 } else if (*dataptr == '"') {
1253 } else if (*dataptr == '\0') {
1256 *bufptr++ = *dataptr;
1264 static struct ast_custom_function quote_function = {
1269 static int csv_quote(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1271 char *bufptr = buf, *dataptr = data;
1273 if (len < 3) { /* at least two for quotes and one for binary zero */
1274 ast_log(LOG_ERROR, "Not enough buffer\n");
1278 if (ast_strlen_zero(data)) {
1279 ast_copy_string(buf, "\"\"", len);
1284 for (; bufptr < buf + len - 3; dataptr++){
1285 if (*dataptr == '"') {
1288 } else if (*dataptr == '\0') {
1291 *bufptr++ = *dataptr;
1299 static struct ast_custom_function csv_quote_function = {
1300 .name = "CSV_QUOTE",
1304 static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
1309 length = strlen(data);
1311 snprintf(buf, buflen, "%d", length);
1316 static struct ast_custom_function len_function = {
1322 static int acf_strftime(struct ast_channel *chan, const char *cmd, char *parse,
1323 char *buf, size_t buflen)
1325 AST_DECLARE_APP_ARGS(args,
1327 AST_APP_ARG(timezone);
1328 AST_APP_ARG(format);
1330 struct timeval when;
1335 AST_STANDARD_APP_ARGS(args, parse);
1337 ast_get_timeval(args.epoch, &when, ast_tvnow(), NULL);
1338 ast_localtime(&when, &tm, args.timezone);
1343 if (ast_strftime(buf, buflen, args.format, &tm) <= 0)
1344 ast_log(LOG_WARNING, "C function strftime() output nothing?!!\n");
1346 buf[buflen - 1] = '\0';
1351 static struct ast_custom_function strftime_function = {
1353 .read = acf_strftime,
1356 static int acf_strptime(struct ast_channel *chan, const char *cmd, char *data,
1357 char *buf, size_t buflen)
1359 AST_DECLARE_APP_ARGS(args,
1360 AST_APP_ARG(timestring);
1361 AST_APP_ARG(timezone);
1362 AST_APP_ARG(format);
1370 "Asterisk function STRPTIME() requires an argument.\n");
1374 AST_STANDARD_APP_ARGS(args, data);
1376 if (ast_strlen_zero(args.format)) {
1378 "No format supplied to STRPTIME(<timestring>,<timezone>,<format>)");
1382 if (!ast_strptime(args.timestring, args.format, &tm)) {
1383 ast_log(LOG_WARNING, "STRPTIME() found no time specified within the string\n");
1385 struct timeval when;
1386 when = ast_mktime(&tm, args.timezone);
1387 snprintf(buf, buflen, "%d", (int) when.tv_sec);
1393 static struct ast_custom_function strptime_function = {
1395 .read = acf_strptime,
1398 static int function_eval(struct ast_channel *chan, const char *cmd, char *data,
1399 char *buf, size_t buflen)
1401 if (ast_strlen_zero(data)) {
1402 ast_log(LOG_WARNING, "EVAL requires an argument: EVAL(<string>)\n");
1406 pbx_substitute_variables_helper(chan, data, buf, buflen - 1);
1411 static int function_eval2(struct ast_channel *chan, const char *cmd, char *data,
1412 struct ast_str **buf, ssize_t buflen)
1414 if (ast_strlen_zero(data)) {
1415 ast_log(LOG_WARNING, "EVAL requires an argument: EVAL(<string>)\n");
1419 ast_str_substitute_variables(buf, buflen, chan, data);
1424 static struct ast_custom_function eval_function = {
1426 .read = function_eval,
1427 .read2 = function_eval2,
1430 static int keypadhash(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
1432 char *bufptr, *dataptr;
1434 for (bufptr = buf, dataptr = data; bufptr < buf + buflen - 1; dataptr++) {
1435 if (*dataptr == '\0') {
1438 } else if (*dataptr == '1') {
1440 } else if (strchr("AaBbCc2", *dataptr)) {
1442 } else if (strchr("DdEeFf3", *dataptr)) {
1444 } else if (strchr("GgHhIi4", *dataptr)) {
1446 } else if (strchr("JjKkLl5", *dataptr)) {
1448 } else if (strchr("MmNnOo6", *dataptr)) {
1450 } else if (strchr("PpQqRrSs7", *dataptr)) {
1452 } else if (strchr("TtUuVv8", *dataptr)) {
1454 } else if (strchr("WwXxYyZz9", *dataptr)) {
1456 } else if (*dataptr == '0') {
1460 buf[buflen - 1] = '\0';
1465 static struct ast_custom_function keypadhash_function = {
1466 .name = "KEYPADHASH",
1470 static int string_toupper(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
1472 char *bufptr = buf, *dataptr = data;
1474 while ((bufptr < buf + buflen - 1) && (*bufptr++ = toupper(*dataptr++)));
1479 static int string_toupper2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t buflen)
1481 char *bufptr, *dataptr = data;
1484 ast_str_make_space(buf, buflen > 0 ? buflen : strlen(data) + 1);
1486 bufptr = ast_str_buffer(*buf);
1487 while ((bufptr < ast_str_buffer(*buf) + ast_str_size(*buf) - 1) && (*bufptr++ = toupper(*dataptr++)));
1488 ast_str_update(*buf);
1493 static struct ast_custom_function toupper_function = {
1495 .read = string_toupper,
1496 .read2 = string_toupper2,
1499 static int string_tolower(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
1501 char *bufptr = buf, *dataptr = data;
1503 while ((bufptr < buf + buflen - 1) && (*bufptr++ = tolower(*dataptr++)));
1508 static int string_tolower2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t buflen)
1510 char *bufptr, *dataptr = data;
1513 ast_str_make_space(buf, buflen > 0 ? buflen : strlen(data) + 1);
1515 bufptr = ast_str_buffer(*buf);
1516 while ((bufptr < ast_str_buffer(*buf) + ast_str_size(*buf) - 1) && (*bufptr++ = tolower(*dataptr++)));
1517 ast_str_update(*buf);
1522 static struct ast_custom_function tolower_function = {
1524 .read = string_tolower,
1525 .read2 = string_tolower2,
1528 static int shift_pop(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
1530 #define beginning (cmd[0] == 'S') /* SHIFT */
1531 char *after, delimiter[2] = ",", *varsubst;
1533 struct ast_str *before = ast_str_thread_get(&result_buf, 16);
1534 char *(*search_func)(const char *s, int c) = (beginning ? strchr : strrchr);
1535 AST_DECLARE_APP_ARGS(args,
1537 AST_APP_ARG(delimiter);
1544 AST_STANDARD_APP_ARGS(args, data);
1546 if (ast_strlen_zero(args.var)) {
1547 ast_log(LOG_WARNING, "%s requires a variable name\n", cmd);
1551 varsubst = ast_alloca(strlen(args.var) + 4);
1552 sprintf(varsubst, "${%s}", args.var);
1553 ast_str_substitute_variables(&before, 0, chan, varsubst);
1555 if (args.argc > 1 && !ast_strlen_zero(args.delimiter)) {
1556 ast_get_encoded_char(args.delimiter, delimiter, &unused);
1559 if (!ast_str_strlen(before)) {
1560 /* Nothing to pop */
1564 if (!(after = search_func(ast_str_buffer(before), delimiter[0]))) {
1565 /* Only one entry in array */
1566 ast_str_set(buf, len, "%s", ast_str_buffer(before));
1567 pbx_builtin_setvar_helper(chan, args.var, "");
1570 ast_str_set(buf, len, "%s", beginning ? ast_str_buffer(before) : after);
1571 pbx_builtin_setvar_helper(chan, args.var, beginning ? after : ast_str_buffer(before));
1578 static struct ast_custom_function shift_function = {
1583 static struct ast_custom_function pop_function = {
1588 static int unshift_push(struct ast_channel *chan, const char *cmd, char *data, const char *new_value)
1590 #define beginning (cmd[0] == 'U') /* UNSHIFT */
1591 char delimiter[2] = ",", *varsubst;
1593 struct ast_str *buf, *previous_value;
1594 AST_DECLARE_APP_ARGS(args,
1596 AST_APP_ARG(delimiter);
1599 if (!(buf = ast_str_thread_get(&result_buf, 16)) ||
1600 !(previous_value = ast_str_thread_get(&tmp_buf, 16))) {
1604 AST_STANDARD_APP_ARGS(args, data);
1606 if (ast_strlen_zero(args.var)) {
1607 ast_log(LOG_WARNING, "%s requires a variable name\n", cmd);
1611 if (args.argc > 1 && !ast_strlen_zero(args.delimiter)) {
1612 ast_get_encoded_char(args.delimiter, delimiter, &unused);
1615 varsubst = ast_alloca(strlen(args.var) + 4);
1616 sprintf(varsubst, "${%s}", args.var);
1617 ast_str_substitute_variables(&previous_value, 0, chan, varsubst);
1619 if (!ast_str_strlen(previous_value)) {
1620 ast_str_set(&buf, 0, "%s", new_value);
1622 ast_str_set(&buf, 0, "%s%c%s",
1623 beginning ? new_value : ast_str_buffer(previous_value),
1625 beginning ? ast_str_buffer(previous_value) : new_value);
1628 pbx_builtin_setvar_helper(chan, args.var, ast_str_buffer(buf));
1634 static struct ast_custom_function push_function = {
1636 .write = unshift_push,
1639 static struct ast_custom_function unshift_function = {
1641 .write = unshift_push,
1644 static int passthru(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
1646 ast_str_set(buf, len, "%s", data);
1650 static struct ast_custom_function passthru_function = {
1655 #ifdef TEST_FRAMEWORK
1656 AST_TEST_DEFINE(test_FIELDNUM)
1658 int i, res = AST_TEST_PASS;
1659 struct ast_channel *chan;
1660 struct ast_str *str;
1661 char expression[256];
1666 const char *expected;
1668 {"abc,def,ghi,jkl", "\\,", "ghi", "3"},
1669 {"abc def ghi jkl", " ", "abc", "1"},
1670 {"abc/def/ghi/jkl", "\\\\x2f", "def", "2"},
1671 {"abc$def$ghi$jkl", "", "ghi", "0"},
1672 {"abc,def,ghi,jkl", "-", "", "0"},
1673 {"abc-def-ghi-jkl", "-", "mno", "0"}
1678 info->name = "func_FIELDNUM_test";
1679 info->category = "/funcs/func_strings/";
1680 info->summary = "Test FIELDNUM function";
1681 info->description = "Verify FIELDNUM behavior";
1682 return AST_TEST_NOT_RUN;
1687 if (!(chan = ast_dummy_channel_alloc())) {
1688 ast_test_status_update(test, "Unable to allocate dummy channel\n");
1689 return AST_TEST_FAIL;
1692 if (!(str = ast_str_create(16))) {
1693 ast_test_status_update(test, "Unable to allocate dynamic string buffer\n");
1694 ast_channel_release(chan);
1695 return AST_TEST_FAIL;
1698 for (i = 0; i < ARRAY_LEN(test_args); i++) {
1699 struct ast_var_t *var = ast_var_assign("FIELDS", test_args[i].fields);
1701 ast_test_status_update(test, "Out of memory\n");
1702 res = AST_TEST_FAIL;
1706 AST_LIST_INSERT_HEAD(ast_channel_varshead(chan), var, entries);
1708 snprintf(expression, sizeof(expression), "${FIELDNUM(%s,%s,%s)}", var->name, test_args[i].delim, test_args[i].field);
1709 ast_str_substitute_variables(&str, 0, chan, expression);
1711 AST_LIST_REMOVE(ast_channel_varshead(chan), var, entries);
1712 ast_var_delete(var);
1714 if (strcasecmp(ast_str_buffer(str), test_args[i].expected)) {
1715 ast_test_status_update(test, "Evaluation of '%s' returned '%s' instead of the expected value '%s'\n",
1716 expression, ast_str_buffer(str), test_args[i].expected);
1717 res = AST_TEST_FAIL;
1723 ast_channel_release(chan);
1728 AST_TEST_DEFINE(test_REPLACE)
1730 int i, res = AST_TEST_PASS;
1731 struct ast_channel *chan;
1732 struct ast_str *str;
1733 char expression[256];
1735 const char *test_string;
1736 const char *find_chars;
1737 const char *replace_char;
1738 const char *expected;
1740 {"abc,def", "\\,", "-", "abc-def"},
1741 {"abc,abc", "bc", "a", "aaa,aaa"},
1742 {"abc,def", "x", "?", "abc,def"},
1743 {"abc,def", "\\,", "", "abcdef"}
1748 info->name = "func_REPLACE_test";
1749 info->category = "/funcs/func_strings/";
1750 info->summary = "Test REPLACE function";
1751 info->description = "Verify REPLACE behavior";
1752 return AST_TEST_NOT_RUN;
1757 if (!(chan = ast_dummy_channel_alloc())) {
1758 ast_test_status_update(test, "Unable to allocate dummy channel\n");
1759 return AST_TEST_FAIL;
1762 if (!(str = ast_str_create(16))) {
1763 ast_test_status_update(test, "Unable to allocate dynamic string buffer\n");
1764 ast_channel_release(chan);
1765 return AST_TEST_FAIL;
1768 for (i = 0; i < ARRAY_LEN(test_args); i++) {
1769 struct ast_var_t *var = ast_var_assign("TEST_STRING", test_args[i].test_string);
1771 ast_test_status_update(test, "Out of memory\n");
1772 res = AST_TEST_FAIL;
1776 AST_LIST_INSERT_HEAD(ast_channel_varshead(chan), var, entries);
1778 snprintf(expression, sizeof(expression), "${REPLACE(%s,%s,%s)}", var->name, test_args[i].find_chars, test_args[i].replace_char);
1779 ast_str_substitute_variables(&str, 0, chan, expression);
1781 AST_LIST_REMOVE(ast_channel_varshead(chan), var, entries);
1782 ast_var_delete(var);
1784 if (strcasecmp(ast_str_buffer(str), test_args[i].expected)) {
1785 ast_test_status_update(test, "Evaluation of '%s' returned '%s' instead of the expected value '%s'\n",
1786 expression, ast_str_buffer(str), test_args[i].expected);
1787 res = AST_TEST_FAIL;
1793 ast_channel_release(chan);
1798 AST_TEST_DEFINE(test_FILTER)
1800 int i, res = AST_TEST_PASS;
1801 const char *test_strings[][2] = {
1804 {"\\x41-R", "DAHDI"},
1805 {"0-9A-Ca-c", "0042133333A12212"},
1806 {"0-9a-cA-C_+\\-", "0042133333A12212"},
1812 info->name = "func_FILTER_test";
1813 info->category = "/funcs/func_strings/";
1814 info->summary = "Test FILTER function";
1815 info->description = "Verify FILTER behavior";
1816 return AST_TEST_NOT_RUN;
1821 for (i = 0; test_strings[i][0]; i++) {
1822 char tmp[256], tmp2[256] = "";
1823 snprintf(tmp, sizeof(tmp), "${FILTER(%s,0042133333&DAHDI/g1/2212)}", test_strings[i][0]);
1824 pbx_substitute_variables_helper(NULL, tmp, tmp2, sizeof(tmp2) - 1);
1825 if (strcmp(test_strings[i][1], tmp2)) {
1826 ast_test_status_update(test, "Format string '%s' substituted to '%s'. Expected '%s'.\n", test_strings[i][0], tmp2, test_strings[i][1]);
1827 res = AST_TEST_FAIL;
1833 AST_TEST_DEFINE(test_STRREPLACE)
1835 int i, res = AST_TEST_PASS;
1836 struct ast_channel *chan; /* dummy channel */
1837 struct ast_str *str; /* fancy string for holding comparing value */
1839 const char *test_strings[][5] = {
1840 {"Weasels have eaten my telephone system", "have eaten my", "are eating our", "", "Weasels are eating our telephone system"}, /*Test normal conditions */
1841 {"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 */
1842 {"foofoofoofoofoofoofoo", "foofoo", "bar", NULL, "barbarbarfoo"}, /* Found string within previous match */
1843 {"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."},
1844 {"One and one and one is three", "and", "plus", "1", "One plus one and one is three"}, /* Test <max-replacements> = 1*/
1845 {"", "fhqwagads", "spelunker", NULL, ""}, /* Empty primary string */
1846 {"Part of this string is missing.", "missing", NULL, NULL, "Part of this string is ."}, /* Empty replace string */
1847 {"'Accidentally' left off a bunch of stuff.", NULL, NULL, NULL, ""}, /* Deliberate error test from too few args */
1848 {"This test will also error.", "", "", "", ""}, /* Deliberate error test from blank find string */
1849 {"This is an \"escape character\" test.", "\\\"escape character\\\"", "evil", NULL, "This is an evil test."}
1854 info->name = "func_STRREPLACE_test";
1855 info->category = "/funcs/func_strings/";
1856 info->summary = "Test STRREPLACE function";
1857 info->description = "Verify STRREPLACE behavior";
1858 return AST_TEST_NOT_RUN;
1863 if (!(chan = ast_dummy_channel_alloc())) {
1864 ast_test_status_update(test, "Unable to allocate dummy channel\n");
1865 return AST_TEST_FAIL;
1868 if (!(str = ast_str_create(64))) {
1869 ast_test_status_update(test, "Unable to allocate dynamic string buffer\n");
1870 ast_channel_release(chan);
1871 return AST_TEST_FAIL;
1874 for (i = 0; i < ARRAY_LEN(test_strings); i++) {
1875 char tmp[512], tmp2[512] = "";
1877 struct ast_var_t *var = ast_var_assign("test_string", test_strings[i][0]);
1879 ast_test_status_update(test, "Unable to allocate variable\n");
1881 ast_channel_release(chan);
1882 return AST_TEST_FAIL;
1885 AST_LIST_INSERT_HEAD(ast_channel_varshead(chan), var, entries);
1887 if (test_strings[i][3]) {
1888 snprintf(tmp, sizeof(tmp), "${STRREPLACE(%s,%s,%s,%s)}", "test_string", test_strings[i][1], test_strings[i][2], test_strings[i][3]);
1889 } else if (test_strings[i][2]) {
1890 snprintf(tmp, sizeof(tmp), "${STRREPLACE(%s,%s,%s)}", "test_string", test_strings[i][1], test_strings[i][2]);
1891 } else if (test_strings[i][1]) {
1892 snprintf(tmp, sizeof(tmp), "${STRREPLACE(%s,%s)}", "test_string", test_strings[i][1]);
1894 snprintf(tmp, sizeof(tmp), "${STRREPLACE(%s)}", "test_string");
1896 ast_str_substitute_variables(&str, 0, chan, tmp);
1897 if (strcmp(test_strings[i][4], ast_str_buffer(str))) {
1898 ast_test_status_update(test, "Format string '%s' substituted to '%s'. Expected '%s'.\n", test_strings[i][0], tmp2, test_strings[i][4]);
1899 res = AST_TEST_FAIL;
1904 ast_channel_release(chan);
1910 static int unload_module(void)
1914 AST_TEST_UNREGISTER(test_FIELDNUM);
1915 AST_TEST_UNREGISTER(test_REPLACE);
1916 AST_TEST_UNREGISTER(test_FILTER);
1917 AST_TEST_UNREGISTER(test_STRREPLACE);
1918 res |= ast_custom_function_unregister(&fieldqty_function);
1919 res |= ast_custom_function_unregister(&fieldnum_function);
1920 res |= ast_custom_function_unregister(&filter_function);
1921 res |= ast_custom_function_unregister(&replace_function);
1922 res |= ast_custom_function_unregister(&strreplace_function);
1923 res |= ast_custom_function_unregister(&listfilter_function);
1924 res |= ast_custom_function_unregister(®ex_function);
1925 res |= ast_custom_function_unregister(&array_function);
1926 res |= ast_custom_function_unregister("e_function);
1927 res |= ast_custom_function_unregister(&csv_quote_function);
1928 res |= ast_custom_function_unregister(&len_function);
1929 res |= ast_custom_function_unregister(&strftime_function);
1930 res |= ast_custom_function_unregister(&strptime_function);
1931 res |= ast_custom_function_unregister(&eval_function);
1932 res |= ast_custom_function_unregister(&keypadhash_function);
1933 res |= ast_custom_function_unregister(&hashkeys_function);
1934 res |= ast_custom_function_unregister(&hash_function);
1935 res |= ast_unregister_application(app_clearhash);
1936 res |= ast_custom_function_unregister(&toupper_function);
1937 res |= ast_custom_function_unregister(&tolower_function);
1938 res |= ast_custom_function_unregister(&shift_function);
1939 res |= ast_custom_function_unregister(&pop_function);
1940 res |= ast_custom_function_unregister(&push_function);
1941 res |= ast_custom_function_unregister(&unshift_function);
1942 res |= ast_custom_function_unregister(&passthru_function);
1947 static int load_module(void)
1951 AST_TEST_REGISTER(test_FIELDNUM);
1952 AST_TEST_REGISTER(test_REPLACE);
1953 AST_TEST_REGISTER(test_FILTER);
1954 AST_TEST_REGISTER(test_STRREPLACE);
1955 res |= ast_custom_function_register(&fieldqty_function);
1956 res |= ast_custom_function_register(&fieldnum_function);
1957 res |= ast_custom_function_register(&filter_function);
1958 res |= ast_custom_function_register(&replace_function);
1959 res |= ast_custom_function_register(&strreplace_function);
1960 res |= ast_custom_function_register(&listfilter_function);
1961 res |= ast_custom_function_register(®ex_function);
1962 res |= ast_custom_function_register(&array_function);
1963 res |= ast_custom_function_register("e_function);
1964 res |= ast_custom_function_register(&csv_quote_function);
1965 res |= ast_custom_function_register(&len_function);
1966 res |= ast_custom_function_register(&strftime_function);
1967 res |= ast_custom_function_register(&strptime_function);
1968 res |= ast_custom_function_register(&eval_function);
1969 res |= ast_custom_function_register(&keypadhash_function);
1970 res |= ast_custom_function_register(&hashkeys_function);
1971 res |= ast_custom_function_register(&hash_function);
1972 res |= ast_register_application_xml(app_clearhash, exec_clearhash);
1973 res |= ast_custom_function_register(&toupper_function);
1974 res |= ast_custom_function_register(&tolower_function);
1975 res |= ast_custom_function_register(&shift_function);
1976 res |= ast_custom_function_register(&pop_function);
1977 res |= ast_custom_function_register(&push_function);
1978 res |= ast_custom_function_register(&unshift_function);
1979 res |= ast_custom_function_register(&passthru_function);
1984 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "String handling dialplan functions");