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
30 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
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"
42 AST_THREADSTORAGE(result_buf);
45 <function name="FIELDQTY" language="en_US">
47 Count the fields with an arbitrary delimiter
50 <parameter name="varname" required="true" />
51 <parameter name="delim" required="true" />
54 <para>The delimiter may be specified as a special or extended ASCII character, by encoding it. The characters
55 <literal>\n</literal>, <literal>\r</literal>, and <literal>\t</literal> are all recognized as the newline,
56 carriage return, and tab characters, respectively. Also, octal and hexadecimal specifications are recognized
57 by the patterns <literal>\0nnn</literal> and <literal>\xHH</literal>, respectively. For example, if you wanted
58 to encode a comma as the delimiter, you could use either <literal>\054</literal> or <literal>\x2C</literal>.</para>
59 <para>Example: If ${example} contains <literal>ex-amp-le</literal>, then ${FIELDQTY(example,-)} returns 3.</para>
62 <function name="LISTFILTER" language="en_US">
63 <synopsis>Remove an item from a list, by name.</synopsis>
65 <parameter name="varname" required="true" />
66 <parameter name="delim" required="true" default="," />
67 <parameter name="value" required="true" />
70 <para>Remove <replaceable>value</replaceable> from the list contained in the <replaceable>varname</replaceable>
71 variable, where the list delimiter is specified by the <replaceable>delim</replaceable> parameter. This is
72 very useful for removing a single channel name from a list of channels, for example.</para>
75 <function name="FILTER" language="en_US">
77 Filter the string to include only the allowed characters
80 <parameter name="allowed-chars" required="true" />
81 <parameter name="string" required="true" />
84 <para>Permits all characters listed in <replaceable>allowed-chars</replaceable>,
85 filtering all others outs. In addition to literally listing the characters,
86 you may also use ranges of characters (delimited by a <literal>-</literal></para>
87 <para>Hexadecimal characters started with a <literal>\x</literal>(i.e. \x20)</para>
88 <para>Octal characters started with a <literal>\0</literal> (i.e. \040)</para>
89 <para>Also <literal>\t</literal>,<literal>\n</literal> and <literal>\r</literal> are recognized.</para>
90 <note><para>If you want the <literal>-</literal> character it needs to be prefixed with a
91 <literal>\</literal></para></note>
94 <function name="REGEX" language="en_US">
96 Check string against a regular expression.
99 <parameter name=""regular expression"" required="true" />
100 <parameter name="string" required="true" />
103 <para>Return <literal>1</literal> on regular expression match or <literal>0</literal> otherwise</para>
104 <para>Please note that the space following the double quotes separating the
105 regex from the data is optional and if present, is skipped. If a space is
106 desired at the beginning of the data, then put two spaces there; the second
107 will not be skipped.</para>
110 <application name="ClearHash" language="en_US">
112 Clear the keys from a specified hashname.
115 <parameter name="hashname" required="true" />
118 <para>Clears all keys out of the specified <replaceable>hashname</replaceable>.</para>
121 <function name="HASH" language="en_US">
123 Implementation of a dialplan associative array
126 <parameter name="hashname" required="true" />
127 <parameter name="hashkey" />
130 <para>In two arguments mode, gets and sets values to corresponding keys within
131 a named associative array. The single-argument mode will only work when assigned
132 to from a function defined by func_odbc</para>
135 <function name="HASHKEYS" language="en_US">
137 Retrieve the keys of the HASH() function.
140 <parameter name="hashname" required="true" />
143 <para>Returns a comma-delimited list of the current keys of the associative array
144 defined by the HASH() function. Note that if you iterate over the keys of
145 the result, adding keys during iteration will cause the result of the HASHKEYS()
146 function to change.</para>
149 <function name="KEYPADHASH" language="en_US">
151 Hash the letters in string into equivalent keypad numbers.
154 <parameter name="string" required="true" />
157 <para>Example: ${KEYPADHASH(Les)} returns "537"</para>
160 <function name="ARRAY" language="en_US">
162 Allows setting multiple variables at once.
165 <parameter name="var1" required="true" />
166 <parameter name="var2" required="false" multiple="true" />
167 <parameter name="varN" required="false" />
170 <para>The comma-delimited list passed as a value to which the function is set will
171 be interpreted as a set of values to which the comma-delimited list of
172 variable names in the argument should be set.</para>
173 <para>Example: Set(ARRAY(var1,var2)=1,2) will set var1 to 1 and var2 to 2</para>
176 <function name="STRPTIME" language="en_US">
178 Returns the epoch of the arbitrary date/time string structured as described by the format.
181 <parameter name="datetime" required="true" />
182 <parameter name="timezone" required="true" />
183 <parameter name="format" required="true" />
186 <para>This is useful for converting a date into <literal>EPOCH</literal> time,
187 possibly to pass to an application like SayUnixTime or to calculate the difference
188 between the two date strings</para>
189 <para>Example: ${STRPTIME(2006-03-01 07:30:35,America/Chicago,%Y-%m-%d %H:%M:%S)} returns 1141219835</para>
192 <function name="STRFTIME" language="en_US">
194 Returns the current date/time in the specified format.
197 <parameter name="epoch" />
198 <parameter name="timezone" />
199 <parameter name="format" />
202 <para>STRFTIME supports all of the same formats as the underlying C function
203 <emphasis>strftime(3)</emphasis>.
204 It also supports the following format: <literal>%[n]q</literal> - fractions of a second,
205 with leading zeros.</para>
206 <para>Example: <literal>%3q</literal> will give milliseconds and <literal>%1q</literal>
207 will give tenths of a second. The default is set at milliseconds (n=3).
208 The common case is to use it in combination with %S, as in <literal>%S.%3q</literal>.</para>
211 <ref type="manpage">strftime(3)</ref>
214 <function name="EVAL" language="en_US">
216 Evaluate stored variables
219 <parameter name="variable" required="true" />
222 <para>Using EVAL basically causes a string to be evaluated twice.
223 When a variable or expression is in the dialplan, it will be
224 evaluated at runtime. However, if the results of the evaluation
225 is in fact another variable or expression, using EVAL will have it
226 evaluated a second time.</para>
227 <para>Example: If the <variable>MYVAR</variable> contains
228 <variable>OTHERVAR</variable>, then the result of ${EVAL(
229 <variable>MYVAR</variable>)} in the dialplan will be the
230 contents of <variable>OTHERVAR</variable>. Normally just
231 putting <variable>MYVAR</variable> in the dialplan the result
232 would be <variable>OTHERVAR</variable>.</para>
235 <function name="TOUPPER" language="en_US">
237 Convert string to all uppercase letters.
240 <parameter name="string" required="true" />
243 <para>Example: ${TOUPPER(Example)} returns "EXAMPLE"</para>
246 <function name="TOLOWER" language="en_US">
248 Convert string to all lowercase letters.
251 <parameter name="string" required="true" />
254 <para>Example: ${TOLOWER(Example)} returns "example"</para>
257 <function name="LEN" language="en_US">
259 Return the length of the string given.
262 <parameter name="string" required="true" />
265 <para>Example: ${LEN(example)} returns 7</para>
268 <function name="QUOTE" language="en_US">
270 Quotes a given string, escaping embedded quotes as necessary
273 <parameter name="string" required="true" />
276 <para>Example: ${QUOTE(ab"c"de)} will return "abcde"</para>
279 <function name="SHIFT" language="en_US">
281 Removes and returns the first item off of a variable containing delimited text
284 <parameter name="varname" required="true" />
285 <parameter name="delimiter" required="false" default="," />
288 <para>Example:</para>
289 <para>exten => s,1,Set(array=one,two,three)</para>
290 <para>exten => s,n,While($["${SET(var=${SHIFT(array)})}" != ""])</para>
291 <para>exten => s,n,NoOp(var is ${var})</para>
292 <para>exten => s,n,EndWhile</para>
293 <para>This would iterate over each value in array, left to right, and
294 would result in NoOp(var is one), NoOp(var is two), and
295 NoOp(var is three) being executed.
299 <function name="POP" language="en_US">
301 Removes and returns the last item off of a variable containing delimited text
304 <parameter name="varname" required="true" />
305 <parameter name="delimiter" required="false" default="," />
308 <para>Example:</para>
309 <para>exten => s,1,Set(array=one,two,three)</para>
310 <para>exten => s,n,While($["${SET(var=${POP(array)})}" != ""])</para>
311 <para>exten => s,n,NoOp(var is ${var})</para>
312 <para>exten => s,n,EndWhile</para>
313 <para>This would iterate over each value in array, right to left, and
314 would result in NoOp(var is three), NoOp(var is two), and
315 NoOp(var is one) being executed.
319 <function name="PUSH" language="en_US">
321 Appends one or more values to the end of a variable containing delimited text
324 <parameter name="varname" required="true" />
325 <parameter name="delimiter" required="false" default="," />
328 <para>Example: Set(PUSH(array)=one,two,three) would append one,
329 two, and three to the end of the values stored in the variable
334 <function name="UNSHIFT" language="en_US">
336 Inserts one or more values to the beginning of a variable containing delimited text
339 <parameter name="varname" required="true" />
340 <parameter name="delimiter" required="false" default="," />
343 <para>Example: Set(UNSHIFT(array)=one,two,three) would insert one,
344 two, and three before the values stored in the variable
351 static int function_fieldqty_helper(struct ast_channel *chan, const char *cmd,
352 char *parse, char *buf, struct ast_str **sbuf, ssize_t len)
355 struct ast_str *str = ast_str_create(16);
357 AST_DECLARE_APP_ARGS(args,
358 AST_APP_ARG(varname);
368 AST_STANDARD_APP_ARGS(args, parse);
370 ast_get_encoded_char(args.delim, delim, &delim_used);
372 varsubst = alloca(strlen(args.varname) + 4);
374 sprintf(varsubst, "${%s}", args.varname);
375 ast_str_substitute_variables(&str, 0, chan, varsubst);
376 if (ast_str_strlen(str) == 0) {
379 char *varval = ast_str_buffer(str);
380 while (strsep(&varval, delim)) {
388 ast_str_set(sbuf, len, "%d", fieldcount);
390 snprintf(buf, len, "%d", fieldcount);
397 static int function_fieldqty(struct ast_channel *chan, const char *cmd,
398 char *parse, char *buf, size_t len)
400 return function_fieldqty_helper(chan, cmd, parse, buf, NULL, len);
403 static int function_fieldqty_str(struct ast_channel *chan, const char *cmd,
404 char *parse, struct ast_str **buf, ssize_t len)
406 return function_fieldqty_helper(chan, cmd, parse, NULL, buf, len);
409 static struct ast_custom_function fieldqty_function = {
411 .read = function_fieldqty,
412 .read2 = function_fieldqty_str,
415 static int listfilter(struct ast_channel *chan, const char *cmd, char *parse, char *buf, struct ast_str **bufstr, ssize_t len)
417 AST_DECLARE_APP_ARGS(args,
418 AST_APP_ARG(listname);
419 AST_APP_ARG(delimiter);
420 AST_APP_ARG(fieldvalue);
422 const char *orig_list, *ptr;
423 const char *begin, *cur, *next;
424 int dlen, flen, first = 1;
425 struct ast_str *result, **result_ptr = &result;
428 AST_STANDARD_APP_ARGS(args, parse);
431 result = ast_str_thread_get(&result_buf, 16);
433 /* Place the result directly into the output buffer */
438 ast_log(LOG_ERROR, "Usage: LISTFILTER(<listname>,<delimiter>,<fieldvalue>)\n");
442 /* If we don't lock the channel, the variable could disappear out from underneath us. */
444 ast_channel_lock(chan);
446 if (!(orig_list = pbx_builtin_getvar_helper(chan, args.listname))) {
447 ast_log(LOG_ERROR, "List variable '%s' not found\n", args.listname);
449 ast_channel_unlock(chan);
454 /* If the string isn't there, just copy out the string and be done with it. */
455 if (!(ptr = strstr(orig_list, args.fieldvalue))) {
457 ast_copy_string(buf, orig_list, len);
459 ast_str_set(result_ptr, len, "%s", orig_list);
462 ast_channel_unlock(chan);
467 dlen = strlen(args.delimiter);
468 delim = alloca(dlen + 1);
469 ast_get_encoded_str(args.delimiter, delim, dlen + 1);
471 if ((dlen = strlen(delim)) == 0) {
476 flen = strlen(args.fieldvalue);
478 ast_str_reset(result);
479 /* Enough space for any result */
481 ast_str_make_space(result_ptr, len ? len : strlen(orig_list) + 1);
485 next = strstr(begin, delim);
488 /* Find next boundary */
491 next = strstr(cur + dlen, delim);
493 cur = strchr(begin + dlen, '\0');
496 if (flen == cur - begin && !strncmp(begin, args.fieldvalue, flen)) {
498 begin += flen + dlen;
500 /* Copy field to output */
502 ast_str_append(result_ptr, len, "%s", delim);
505 ast_str_append_substr(result_ptr, len, begin, cur - begin + 1);
509 } while (*cur != '\0');
511 ast_channel_unlock(chan);
515 ast_copy_string(buf, ast_str_buffer(result), len);
521 static int listfilter_read(struct ast_channel *chan, const char *cmd, char *parse, char *buf, size_t len)
523 return listfilter(chan, cmd, parse, buf, NULL, len);
526 static int listfilter_read2(struct ast_channel *chan, const char *cmd, char *parse, struct ast_str **buf, ssize_t len)
528 return listfilter(chan, cmd, parse, NULL, buf, len);
531 static struct ast_custom_function listfilter_function = {
532 .name = "LISTFILTER",
533 .read = listfilter_read,
534 .read2 = listfilter_read2,
537 static int filter(struct ast_channel *chan, const char *cmd, char *parse, char *buf,
540 AST_DECLARE_APP_ARGS(args,
541 AST_APP_ARG(allowed);
544 char *outbuf = buf, ac;
545 char allowed[256] = "";
546 size_t allowedlen = 0;
548 AST_STANDARD_APP_ARGS(args, parse);
551 ast_log(LOG_ERROR, "Usage: FILTER(<allowed-chars>,<string>)\n");
556 for (; *(args.allowed) && allowedlen < sizeof(allowed); ) {
560 if (ast_get_encoded_char(args.allowed, &c1, &consumed))
562 args.allowed += consumed;
564 if (*(args.allowed) == '-') {
565 if (ast_get_encoded_char(args.allowed + 1, &c2, &consumed))
567 args.allowed += consumed + 1;
570 * Looks a little strange, until you realize that we can overflow
571 * the size of a char.
573 for (ac = c1; ac != c2 && allowedlen < sizeof(allowed) - 1; ac++)
574 allowed[allowedlen++] = ac;
575 allowed[allowedlen++] = ac;
577 ast_debug(4, "c1=%d, c2=%d\n", c1, c2);
579 /* Decrement before the loop increment */
582 allowed[allowedlen++] = c1;
585 ast_debug(1, "Allowed: %s\n", allowed);
587 for (; *(args.string) && (buf + len - 1 > outbuf); (args.string)++) {
588 if (strchr(allowed, *(args.string)))
589 *outbuf++ = *(args.string);
596 static struct ast_custom_function filter_function = {
601 static int regex(struct ast_channel *chan, const char *cmd, char *parse, char *buf,
604 AST_DECLARE_APP_ARGS(args,
614 AST_NONSTANDARD_APP_ARGS(args, parse, '"');
616 if (args.argc != 3) {
617 ast_log(LOG_ERROR, "Unexpected arguments: should have been in the form '\"<regex>\" <string>'\n");
620 if ((*args.str == ' ') || (*args.str == '\t'))
623 ast_debug(1, "FUNCTION REGEX (%s)(%s)\n", args.reg, args.str);
625 if ((errcode = regcomp(®exbuf, args.reg, REG_EXTENDED | REG_NOSUB))) {
626 regerror(errcode, ®exbuf, buf, len);
627 ast_log(LOG_WARNING, "Malformed input %s(%s): %s\n", cmd, parse, buf);
631 strcpy(buf, regexec(®exbuf, args.str, 0, NULL, 0) ? "0" : "1");
638 static struct ast_custom_function regex_function = {
643 #define HASH_PREFIX "~HASH~%s~"
644 #define HASH_FORMAT HASH_PREFIX "%s~"
646 static char *app_clearhash = "ClearHash";
648 /* This function probably should migrate to main/pbx.c, as pbx_builtin_clearvar_prefix() */
649 static void clearvar_prefix(struct ast_channel *chan, const char *prefix)
651 struct ast_var_t *var;
652 int len = strlen(prefix);
653 AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->varshead, var, entries) {
654 if (strncasecmp(prefix, ast_var_name(var), len) == 0) {
655 AST_LIST_REMOVE_CURRENT(entries);
659 AST_LIST_TRAVERSE_SAFE_END
662 static int exec_clearhash(struct ast_channel *chan, const char *data)
665 snprintf(prefix, sizeof(prefix), HASH_PREFIX, data ? (char *)data : "null");
666 clearvar_prefix(chan, prefix);
670 static int array(struct ast_channel *chan, const char *cmd, char *var,
673 AST_DECLARE_APP_ARGS(arg1,
674 AST_APP_ARG(var)[100];
676 AST_DECLARE_APP_ARGS(arg2,
677 AST_APP_ARG(val)[100];
679 char *origvar = "", *value2, varname[256];
682 value2 = ast_strdupa(value);
686 if (!strcmp(cmd, "HASH")) {
687 const char *var2 = pbx_builtin_getvar_helper(chan, "~ODBCFIELDS~");
690 var = ast_strdupa(var2);
693 ast_autoservice_stop(chan);
699 /* The functions this will generally be used with are SORT and ODBC_*, which
700 * both return comma-delimited lists. However, if somebody uses literal lists,
701 * their commas will be translated to vertical bars by the load, and I don't
702 * want them to be surprised by the result. Hence, we prefer commas as the
703 * delimiter, but we'll fall back to vertical bars if commas aren't found.
705 ast_debug(1, "array (%s=%s)\n", var, value2);
706 AST_STANDARD_APP_ARGS(arg1, var);
708 AST_STANDARD_APP_ARGS(arg2, value2);
710 for (i = 0; i < arg1.argc; i++) {
711 ast_debug(1, "array set value (%s=%s)\n", arg1.var[i],
715 snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]);
716 pbx_builtin_setvar_helper(chan, varname, arg2.val[i]);
718 pbx_builtin_setvar_helper(chan, arg1.var[i], arg2.val[i]);
721 /* We could unset the variable, by passing a NULL, but due to
722 * pushvar semantics, that could create some undesired behavior. */
724 snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]);
725 pbx_builtin_setvar_helper(chan, varname, "");
727 pbx_builtin_setvar_helper(chan, arg1.var[i], "");
735 static int hashkeys_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
737 struct ast_var_t *newvar;
738 struct ast_str *prefix = ast_str_alloca(80);
740 ast_str_set(&prefix, -1, HASH_PREFIX, data);
743 AST_LIST_TRAVERSE(&chan->varshead, newvar, entries) {
744 if (strncasecmp(ast_str_buffer(prefix), ast_var_name(newvar), ast_str_strlen(prefix)) == 0) {
745 /* Copy everything after the prefix */
746 strncat(buf, ast_var_name(newvar) + ast_str_strlen(prefix), len - strlen(buf) - 1);
747 /* Trim the trailing ~ */
748 buf[strlen(buf) - 1] = ',';
751 /* Trim the trailing comma */
752 buf[strlen(buf) - 1] = '\0';
756 static int hashkeys_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
758 struct ast_var_t *newvar;
759 struct ast_str *prefix = ast_str_alloca(80);
762 ast_str_set(&prefix, -1, HASH_PREFIX, data);
764 AST_LIST_TRAVERSE(&chan->varshead, newvar, entries) {
765 if (strncasecmp(ast_str_buffer(prefix), ast_var_name(newvar), ast_str_strlen(prefix)) == 0) {
766 /* Copy everything after the prefix */
767 ast_str_append(buf, len, "%s", ast_var_name(newvar) + ast_str_strlen(prefix));
768 /* Trim the trailing ~ */
769 tmp = ast_str_buffer(*buf);
770 tmp[ast_str_strlen(*buf) - 1] = ',';
773 /* Trim the trailing comma */
774 tmp = ast_str_buffer(*buf);
775 tmp[ast_str_strlen(*buf) - 1] = '\0';
779 static int hash_write(struct ast_channel *chan, const char *cmd, char *var, const char *value)
782 AST_DECLARE_APP_ARGS(arg,
783 AST_APP_ARG(hashname);
784 AST_APP_ARG(hashkey);
787 if (!strchr(var, ',')) {
788 /* Single argument version */
789 return array(chan, "HASH", var, value);
792 AST_STANDARD_APP_ARGS(arg, var);
793 snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey);
794 pbx_builtin_setvar_helper(chan, varname, value);
799 static int hash_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
802 const char *varvalue;
803 AST_DECLARE_APP_ARGS(arg,
804 AST_APP_ARG(hashname);
805 AST_APP_ARG(hashkey);
808 AST_STANDARD_APP_ARGS(arg, data);
810 snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey);
811 varvalue = pbx_builtin_getvar_helper(chan, varname);
813 ast_copy_string(buf, varvalue, len);
816 } else if (arg.argc == 1) {
819 AST_DECLARE_APP_ARGS(arg2,
820 AST_APP_ARG(col)[100];
823 /* Get column names, in no particular order */
824 hashkeys_read(chan, "HASHKEYS", arg.hashname, colnames, sizeof(colnames));
825 pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", colnames);
827 AST_STANDARD_APP_ARGS(arg2, colnames);
830 /* Now get the corresponding column values, in exactly the same order */
831 for (i = 0; i < arg2.argc; i++) {
832 snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg2.col[i]);
833 varvalue = pbx_builtin_getvar_helper(chan, varname);
834 strncat(buf, varvalue, len - strlen(buf) - 1);
835 strncat(buf, ",", len - strlen(buf) - 1);
838 /* Strip trailing comma */
839 buf[strlen(buf) - 1] = '\0';
845 static struct ast_custom_function hash_function = {
851 static struct ast_custom_function hashkeys_function = {
853 .read = hashkeys_read,
854 .read2 = hashkeys_read2,
857 static struct ast_custom_function array_function = {
862 static int quote(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
864 char *bufptr = buf, *dataptr = data;
866 for (; bufptr < buf + len - 1; dataptr++) {
867 if (*dataptr == '\\') {
870 } else if (*dataptr == '"') {
873 } else if (*dataptr == '\0') {
876 *bufptr++ = *dataptr;
884 static struct ast_custom_function quote_function = {
890 static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf,
896 length = strlen(data);
898 snprintf(buf, buflen, "%d", length);
903 static struct ast_custom_function len_function = {
909 static int acf_strftime(struct ast_channel *chan, const char *cmd, char *parse,
910 char *buf, size_t buflen)
912 AST_DECLARE_APP_ARGS(args,
914 AST_APP_ARG(timezone);
922 AST_STANDARD_APP_ARGS(args, parse);
924 ast_get_timeval(args.epoch, &when, ast_tvnow(), NULL);
925 ast_localtime(&when, &tm, args.timezone);
930 if (ast_strftime(buf, buflen, args.format, &tm) <= 0)
931 ast_log(LOG_WARNING, "C function strftime() output nothing?!!\n");
933 buf[buflen - 1] = '\0';
938 static struct ast_custom_function strftime_function = {
940 .read = acf_strftime,
943 static int acf_strptime(struct ast_channel *chan, const char *cmd, char *data,
944 char *buf, size_t buflen)
946 AST_DECLARE_APP_ARGS(args,
947 AST_APP_ARG(timestring);
948 AST_APP_ARG(timezone);
957 "Asterisk function STRPTIME() requires an argument.\n");
961 AST_STANDARD_APP_ARGS(args, data);
963 if (ast_strlen_zero(args.format)) {
965 "No format supplied to STRPTIME(<timestring>,<timezone>,<format>)");
969 if (!ast_strptime(args.timestring, args.format, &tm)) {
970 ast_log(LOG_WARNING, "STRPTIME() found no time specified within the string\n");
973 when = ast_mktime(&tm, args.timezone);
974 snprintf(buf, buflen, "%d", (int) when.tv_sec);
980 static struct ast_custom_function strptime_function = {
982 .read = acf_strptime,
985 static int function_eval(struct ast_channel *chan, const char *cmd, char *data,
986 char *buf, size_t buflen)
988 if (ast_strlen_zero(data)) {
989 ast_log(LOG_WARNING, "EVAL requires an argument: EVAL(<string>)\n");
993 pbx_substitute_variables_helper(chan, data, buf, buflen - 1);
998 static int function_eval2(struct ast_channel *chan, const char *cmd, char *data,
999 struct ast_str **buf, ssize_t buflen)
1001 if (ast_strlen_zero(data)) {
1002 ast_log(LOG_WARNING, "EVAL requires an argument: EVAL(<string>)\n");
1006 ast_str_substitute_variables(buf, buflen, chan, data);
1011 static struct ast_custom_function eval_function = {
1013 .read = function_eval,
1014 .read2 = function_eval2,
1017 static int keypadhash(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
1019 char *bufptr, *dataptr;
1021 for (bufptr = buf, dataptr = data; bufptr < buf + buflen - 1; dataptr++) {
1022 if (*dataptr == '\0') {
1025 } else if (*dataptr == '1') {
1027 } else if (strchr("AaBbCc2", *dataptr)) {
1029 } else if (strchr("DdEeFf3", *dataptr)) {
1031 } else if (strchr("GgHhIi4", *dataptr)) {
1033 } else if (strchr("JjKkLl5", *dataptr)) {
1035 } else if (strchr("MmNnOo6", *dataptr)) {
1037 } else if (strchr("PpQqRrSs7", *dataptr)) {
1039 } else if (strchr("TtUuVv8", *dataptr)) {
1041 } else if (strchr("WwXxYyZz9", *dataptr)) {
1043 } else if (*dataptr == '0') {
1047 buf[buflen - 1] = '\0';
1052 static struct ast_custom_function keypadhash_function = {
1053 .name = "KEYPADHASH",
1057 static int string_toupper(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
1059 char *bufptr = buf, *dataptr = data;
1061 while ((bufptr < buf + buflen - 1) && (*bufptr++ = toupper(*dataptr++)));
1066 static int string_toupper2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t buflen)
1068 char *bufptr, *dataptr = data;
1071 ast_str_make_space(buf, buflen > 0 ? buflen : strlen(data) + 1);
1073 bufptr = ast_str_buffer(*buf);
1074 while ((bufptr < ast_str_buffer(*buf) + ast_str_size(*buf) - 1) && (*bufptr++ = toupper(*dataptr++)));
1075 ast_str_update(*buf);
1080 static struct ast_custom_function toupper_function = {
1082 .read = string_toupper,
1083 .read2 = string_toupper2,
1086 static int string_tolower(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
1088 char *bufptr = buf, *dataptr = data;
1090 while ((bufptr < buf + buflen - 1) && (*bufptr++ = tolower(*dataptr++)));
1095 static int string_tolower2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t buflen)
1097 char *bufptr, *dataptr = data;
1100 ast_str_make_space(buf, buflen > 0 ? buflen : strlen(data) + 1);
1102 bufptr = ast_str_buffer(*buf);
1103 while ((bufptr < ast_str_buffer(*buf) + ast_str_size(*buf) - 1) && (*bufptr++ = tolower(*dataptr++)));
1104 ast_str_update(*buf);
1109 static struct ast_custom_function tolower_function = {
1111 .read = string_tolower,
1112 .read2 = string_tolower2,
1115 static int array_remove(struct ast_channel *chan, const char *cmd, char *var, char *buf, size_t len, int beginning)
1118 char *after, *before;
1119 char *(*search_func)(const char *s, int c) = beginning ? strchr : strrchr;
1120 AST_DECLARE_APP_ARGS(args,
1122 AST_APP_ARG(delimiter);
1126 ast_log(LOG_WARNING, "%s requires a channel\n", cmd);
1130 AST_STANDARD_APP_ARGS(args, var);
1132 if (ast_strlen_zero(args.var)) {
1133 ast_log(LOG_WARNING, "%s requires a channel variable name\n", cmd);
1137 if (args.delimiter && strlen(args.delimiter) != 1) {
1138 ast_log(LOG_WARNING, "%s delimeters should be a single character\n", cmd);
1142 ast_channel_lock(chan);
1143 if (ast_strlen_zero(tmp = pbx_builtin_getvar_helper(chan, args.var))) {
1144 ast_channel_unlock(chan);
1148 before = ast_strdupa(tmp);
1149 ast_channel_unlock(chan);
1151 /* Only one entry in array */
1152 if (!(after = search_func(before, S_OR(args.delimiter, ",")[0]))) {
1153 ast_copy_string(buf, before, len);
1154 pbx_builtin_setvar_helper(chan, args.var, "");
1157 ast_copy_string(buf, beginning ? before : after, len);
1158 pbx_builtin_setvar_helper(chan, args.var, beginning ? after : before);
1165 static int shift(struct ast_channel *chan, const char *cmd, char *var, char *buf, size_t len)
1167 return array_remove(chan, cmd, var, buf, len, 1);
1169 static struct ast_custom_function shift_function = {
1174 static int pop(struct ast_channel *chan, const char *cmd, char *var, char *buf, size_t len)
1176 return array_remove(chan, cmd, var, buf, len, 0);
1179 static struct ast_custom_function pop_function = {
1184 static int array_insert(struct ast_channel *chan, const char *cmd, char *var, const char *val, int beginning)
1187 struct ast_str *buf;
1188 AST_DECLARE_APP_ARGS(args,
1190 AST_APP_ARG(delimiter);
1194 ast_log(LOG_WARNING, "%s requires a channel\n", cmd);
1198 AST_STANDARD_APP_ARGS(args, var);
1200 if (ast_strlen_zero(args.var) || ast_strlen_zero(val)) {
1201 ast_log(LOG_WARNING, "%s requires a variable, and at least one value\n", cmd);
1205 if (args.delimiter && strlen(args.delimiter) != 1) {
1206 ast_log(LOG_WARNING, "%s delimeters should be a single character\n", cmd);
1210 if (!(buf = ast_str_create(32))) {
1211 ast_log(LOG_ERROR, "Unable to allocate memory for buffer!\n");
1215 ast_channel_lock(chan);
1216 if (!(tmp = pbx_builtin_getvar_helper(chan, args.var))) {
1217 ast_str_set(&buf, 0, "%s", val);
1219 ast_str_append(&buf, 0, "%s%s%s", beginning ? val : tmp, S_OR(args.delimiter, ","), beginning ? tmp : val);
1221 ast_channel_unlock(chan);
1223 pbx_builtin_setvar_helper(chan, args.var, ast_str_buffer(buf));
1229 static int push(struct ast_channel *chan, const char *cmd, char *var, const char *val)
1231 return array_insert(chan, cmd, var, val, 0);
1234 static struct ast_custom_function push_function = {
1239 static int unshift(struct ast_channel *chan, const char *cmd, char *var, const char *val)
1241 return array_insert(chan, cmd, var, val, 1);
1244 static struct ast_custom_function unshift_function = {
1249 static int unload_module(void)
1253 res |= ast_custom_function_unregister(&fieldqty_function);
1254 res |= ast_custom_function_unregister(&filter_function);
1255 res |= ast_custom_function_unregister(&listfilter_function);
1256 res |= ast_custom_function_unregister(®ex_function);
1257 res |= ast_custom_function_unregister(&array_function);
1258 res |= ast_custom_function_unregister("e_function);
1259 res |= ast_custom_function_unregister(&len_function);
1260 res |= ast_custom_function_unregister(&strftime_function);
1261 res |= ast_custom_function_unregister(&strptime_function);
1262 res |= ast_custom_function_unregister(&eval_function);
1263 res |= ast_custom_function_unregister(&keypadhash_function);
1264 res |= ast_custom_function_unregister(&hashkeys_function);
1265 res |= ast_custom_function_unregister(&hash_function);
1266 res |= ast_unregister_application(app_clearhash);
1267 res |= ast_custom_function_unregister(&toupper_function);
1268 res |= ast_custom_function_unregister(&tolower_function);
1269 res |= ast_custom_function_unregister(&shift_function);
1270 res |= ast_custom_function_unregister(&pop_function);
1271 res |= ast_custom_function_unregister(&push_function);
1272 res |= ast_custom_function_unregister(&unshift_function);
1277 static int load_module(void)
1281 res |= ast_custom_function_register(&fieldqty_function);
1282 res |= ast_custom_function_register(&filter_function);
1283 res |= ast_custom_function_register(&listfilter_function);
1284 res |= ast_custom_function_register(®ex_function);
1285 res |= ast_custom_function_register(&array_function);
1286 res |= ast_custom_function_register("e_function);
1287 res |= ast_custom_function_register(&len_function);
1288 res |= ast_custom_function_register(&strftime_function);
1289 res |= ast_custom_function_register(&strptime_function);
1290 res |= ast_custom_function_register(&eval_function);
1291 res |= ast_custom_function_register(&keypadhash_function);
1292 res |= ast_custom_function_register(&hashkeys_function);
1293 res |= ast_custom_function_register(&hash_function);
1294 res |= ast_register_application_xml(app_clearhash, exec_clearhash);
1295 res |= ast_custom_function_register(&toupper_function);
1296 res |= ast_custom_function_register(&tolower_function);
1297 res |= ast_custom_function_register(&shift_function);
1298 res |= ast_custom_function_register(&pop_function);
1299 res |= ast_custom_function_register(&push_function);
1300 res |= ast_custom_function_register(&unshift_function);
1305 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "String handling dialplan functions");