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 static int function_fieldqty(struct ast_channel *chan, const char *cmd,
43 char *parse, char *buf, size_t len)
45 char *varsubst, varval[8192], *varval2 = varval;
47 AST_DECLARE_APP_ARGS(args,
55 ast_autoservice_start(chan);
57 AST_STANDARD_APP_ARGS(args, parse);
59 ast_get_encoded_char(args.delim, delim, &delim_used);
61 varsubst = alloca(strlen(args.varname) + 4);
63 sprintf(varsubst, "${%s}", args.varname);
64 pbx_substitute_variables_helper(chan, varsubst, varval, sizeof(varval) - 1);
65 if (ast_strlen_zero(varval2))
68 while (strsep(&varval2, delim))
74 snprintf(buf, len, "%d", fieldcount);
77 ast_autoservice_stop(chan);
82 static struct ast_custom_function fieldqty_function = {
84 .synopsis = "Count the fields, with an arbitrary delimiter",
85 .syntax = "FIELDQTY(<varname>,<delim>)",
86 .read = function_fieldqty,
89 static int filter(struct ast_channel *chan, const char *cmd, char *parse, char *buf,
92 AST_DECLARE_APP_ARGS(args,
96 char *outbuf = buf, ac;
97 char allowed[256] = "";
98 size_t allowedlen = 0;
100 AST_STANDARD_APP_ARGS(args, parse);
103 ast_log(LOG_ERROR, "Usage: FILTER(<allowed-chars>,<string>)\n");
108 for (; *(args.allowed) && allowedlen < sizeof(allowed); (args.allowed)++) {
112 if (ast_get_encoded_char(args.allowed, &c1, &consumed))
114 args.allowed += consumed;
116 if (*(args.allowed) == '-') {
117 if (ast_get_encoded_char(args.allowed + 1, &c2, &consumed))
119 args.allowed += consumed + 1;
122 * Looks a little strange, until you realize that we can overflow
123 * the size of a char.
125 for (ac = c1; ac != c2 && allowedlen < sizeof(allowed) - 1; ac++)
126 allowed[allowedlen++] = ac;
127 allowed[allowedlen++] = ac;
129 ast_debug(4, "c1=%d, c2=%d\n", c1, c2);
131 /* Decrement before the loop increment */
134 allowed[allowedlen++] = c1;
137 ast_debug(1, "Allowed: %s\n", allowed);
139 for (; *(args.string) && (buf + len - 1 > outbuf); (args.string)++) {
140 if (strchr(allowed, *(args.string)))
141 *outbuf++ = *(args.string);
148 static struct ast_custom_function filter_function = {
150 .synopsis = "Filter the string to include only the allowed characters",
151 .syntax = "FILTER(<allowed-chars>,<string>)",
154 "Permits all characters listed in <allowed-chars>, filtering all others out.\n"
155 "In addition to literally listing the characters, you may also use ranges of\n"
156 "characters (delimited by a '-'), as well as hexadecimal characters started\n"
157 "with a \\x (i.e. \\x20) and octal characters started with \\0 (i.e. \\040).\n"
158 "Also, \\t, \\n, and \\r are recognized. If you want a literal '-' character,\n"
159 "simply prefix it with a '\\'\n",
162 static int regex(struct ast_channel *chan, const char *cmd, char *parse, char *buf,
165 AST_DECLARE_APP_ARGS(args,
175 AST_NONSTANDARD_APP_ARGS(args, parse, '"');
177 if (args.argc != 3) {
178 ast_log(LOG_ERROR, "Unexpected arguments: should have been in the form '\"<regex>\" <string>'\n");
181 if ((*args.str == ' ') || (*args.str == '\t'))
184 ast_debug(1, "FUNCTION REGEX (%s)(%s)\n", args.reg, args.str);
186 if ((errcode = regcomp(®exbuf, args.reg, REG_EXTENDED | REG_NOSUB))) {
187 regerror(errcode, ®exbuf, buf, len);
188 ast_log(LOG_WARNING, "Malformed input %s(%s): %s\n", cmd, parse, buf);
192 strcpy(buf, regexec(®exbuf, args.str, 0, NULL, 0) ? "0" : "1");
199 static struct ast_custom_function regex_function = {
201 .synopsis = "Regular Expression",
203 "Returns 1 if data matches regular expression, or 0 otherwise.\n"
204 "Please note that the space following the double quotes separating the regex from the data\n"
205 "is optional and if present, is skipped. If a space is desired at the beginning of the data,\n"
206 "then put two spaces there; the second will not be skipped.\n",
207 .syntax = "REGEX(\"<regular expression>\" <data>)",
211 #define HASH_PREFIX "~HASH~%s~"
212 #define HASH_FORMAT HASH_PREFIX "%s~"
214 static char *app_clearhash = "ClearHash";
215 static char *syn_clearhash = "Clear the keys from a specified hashname";
216 static char *desc_clearhash =
217 "ClearHash(<hashname>)\n"
218 " Clears all keys out of the specified hashname\n";
220 /* This function probably should migrate to main/pbx.c, as pbx_builtin_clearvar_prefix() */
221 static void clearvar_prefix(struct ast_channel *chan, const char *prefix)
223 struct ast_var_t *var;
224 int len = strlen(prefix);
225 AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->varshead, var, entries) {
226 if (strncasecmp(prefix, ast_var_name(var), len) == 0) {
227 AST_LIST_REMOVE_CURRENT(entries);
231 AST_LIST_TRAVERSE_SAFE_END
234 static int exec_clearhash(struct ast_channel *chan, void *data)
237 snprintf(prefix, sizeof(prefix), HASH_PREFIX, data ? (char *)data : "null");
238 clearvar_prefix(chan, prefix);
242 static int array(struct ast_channel *chan, const char *cmd, char *var,
245 AST_DECLARE_APP_ARGS(arg1,
246 AST_APP_ARG(var)[100];
248 AST_DECLARE_APP_ARGS(arg2,
249 AST_APP_ARG(val)[100];
251 char *origvar = "", *value2, varname[256];
254 value2 = ast_strdupa(value);
259 ast_autoservice_start(chan);
261 if (!strcmp(cmd, "HASH")) {
262 const char *var2 = pbx_builtin_getvar_helper(chan, "~ODBCFIELDS~");
265 var = ast_strdupa(var2);
268 ast_autoservice_stop(chan);
274 /* The functions this will generally be used with are SORT and ODBC_*, which
275 * both return comma-delimited lists. However, if somebody uses literal lists,
276 * their commas will be translated to vertical bars by the load, and I don't
277 * want them to be surprised by the result. Hence, we prefer commas as the
278 * delimiter, but we'll fall back to vertical bars if commas aren't found.
280 ast_debug(1, "array (%s=%s)\n", var, value2);
281 AST_STANDARD_APP_ARGS(arg1, var);
283 AST_STANDARD_APP_ARGS(arg2, value2);
285 for (i = 0; i < arg1.argc; i++) {
286 ast_debug(1, "array set value (%s=%s)\n", arg1.var[i],
290 snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]);
291 pbx_builtin_setvar_helper(chan, varname, arg2.val[i]);
293 pbx_builtin_setvar_helper(chan, arg1.var[i], arg2.val[i]);
296 /* We could unset the variable, by passing a NULL, but due to
297 * pushvar semantics, that could create some undesired behavior. */
299 snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]);
300 pbx_builtin_setvar_helper(chan, varname, "");
302 pbx_builtin_setvar_helper(chan, arg1.var[i], "");
308 ast_autoservice_stop(chan);
313 static int hashkeys_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
315 struct ast_var_t *newvar;
318 snprintf(prefix, sizeof(prefix), HASH_PREFIX, data);
319 plen = strlen(prefix);
322 AST_LIST_TRAVERSE(&chan->varshead, newvar, entries) {
323 if (strncasecmp(prefix, ast_var_name(newvar), plen) == 0) {
324 /* Copy everything after the prefix */
325 strncat(buf, ast_var_name(newvar) + plen, len - strlen(buf) - 1);
326 /* Trim the trailing ~ */
327 buf[strlen(buf) - 1] = ',';
330 /* Trim the trailing comma */
331 buf[strlen(buf) - 1] = '\0';
335 static int hash_write(struct ast_channel *chan, const char *cmd, char *var, const char *value)
338 AST_DECLARE_APP_ARGS(arg,
339 AST_APP_ARG(hashname);
340 AST_APP_ARG(hashkey);
343 if (!strchr(var, ',')) {
344 /* Single argument version */
345 return array(chan, "HASH", var, value);
348 AST_STANDARD_APP_ARGS(arg, var);
349 snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey);
350 pbx_builtin_setvar_helper(chan, varname, value);
355 static int hash_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
358 const char *varvalue;
359 AST_DECLARE_APP_ARGS(arg,
360 AST_APP_ARG(hashname);
361 AST_APP_ARG(hashkey);
364 AST_STANDARD_APP_ARGS(arg, data);
366 snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey);
367 varvalue = pbx_builtin_getvar_helper(chan, varname);
369 ast_copy_string(buf, varvalue, len);
372 } else if (arg.argc == 1) {
375 AST_DECLARE_APP_ARGS(arg2,
376 AST_APP_ARG(col)[100];
379 /* Get column names, in no particular order */
380 hashkeys_read(chan, "HASHKEYS", arg.hashname, colnames, sizeof(colnames));
381 pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", colnames);
383 AST_STANDARD_APP_ARGS(arg2, colnames);
386 /* Now get the corresponding column values, in exactly the same order */
387 for (i = 0; i < arg2.argc; i++) {
388 snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg2.col[i]);
389 varvalue = pbx_builtin_getvar_helper(chan, varname);
390 strncat(buf, varvalue, len - strlen(buf) - 1);
391 strncat(buf, ",", len - strlen(buf) - 1);
394 /* Strip trailing comma */
395 buf[strlen(buf) - 1] = '\0';
401 static struct ast_custom_function hash_function = {
403 .synopsis = "Implementation of a dialplan associative array",
404 .syntax = "HASH(hashname[,hashkey])",
408 "In two argument mode, gets and sets values to corresponding keys within a named\n"
409 "associative array. The single-argument mode will only work when assigned to from\n"
410 "a function defined by func_odbc.so.\n",
413 static struct ast_custom_function hashkeys_function = {
415 .synopsis = "Retrieve the keys of a HASH()",
416 .syntax = "HASHKEYS(<hashname>)",
417 .read = hashkeys_read,
419 "Returns a comma-delimited list of the current keys of an associative array\n"
420 "defined by the HASH() function. Note that if you iterate over the keys of\n"
421 "the result, adding keys during iteration will cause the result of the HASHKEYS\n"
422 "function to change.\n",
425 static struct ast_custom_function array_function = {
427 .synopsis = "Allows setting multiple variables at once",
428 .syntax = "ARRAY(var1[,var2[...][,varN]])",
431 "The comma-separated list passed as a value to which the function is set will\n"
432 "be interpreted as a set of values to which the comma-separated list of\n"
433 "variable names in the argument should be set.\n"
434 "Hence, Set(ARRAY(var1,var2)=1,2) will set var1 to 1 and var2 to 2.\n",
437 static int acf_sprintf(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
439 #define SPRINTF_FLAG 0
440 #define SPRINTF_WIDTH 1
441 #define SPRINTF_PRECISION 2
442 #define SPRINTF_LENGTH 3
443 #define SPRINTF_CONVERSION 4
444 int i, state = -1, argcount = 0;
445 char *formatstart = NULL, *bufptr = buf;
446 char formatbuf[256] = "";
449 AST_DECLARE_APP_ARGS(arg,
451 AST_APP_ARG(var)[100];
454 AST_STANDARD_APP_ARGS(arg, data);
456 /* Scan the format, converting each argument into the requisite format type. */
457 for (i = 0; arg.format[i]; i++) {
460 if (strchr("#0- +'I", arg.format[i]))
462 state = SPRINTF_WIDTH;
464 if (arg.format[i] >= '0' && arg.format[i] <= '9')
467 /* Next character must be a period to go into a precision */
468 if (arg.format[i] == '.') {
469 state = SPRINTF_PRECISION;
471 state = SPRINTF_LENGTH;
475 case SPRINTF_PRECISION:
476 if (arg.format[i] >= '0' && arg.format[i] <= '9')
478 state = SPRINTF_LENGTH;
480 if (strchr("hl", arg.format[i])) {
481 if (arg.format[i + 1] == arg.format[i])
483 state = SPRINTF_CONVERSION;
485 } else if (strchr("Lqjzt", arg.format[i])) {
486 state = SPRINTF_CONVERSION;
489 state = SPRINTF_CONVERSION;
490 case SPRINTF_CONVERSION:
491 if (strchr("diouxXc", arg.format[i])) {
494 /* Isolate this format alone */
495 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
496 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
498 /* Convert the argument into the required type */
499 if (arg.var[argcount]) {
500 if (sscanf(arg.var[argcount++], "%d", &tmpi) != 1) {
501 ast_log(LOG_ERROR, "Argument '%s' is not an integer number for format '%s'\n", arg.var[argcount - 1], formatbuf);
505 ast_log(LOG_ERROR, "SPRINTF() has more format specifiers than arguments!\n");
509 /* Format the argument */
510 snprintf(bufptr, buf + len - bufptr, formatbuf, tmpi);
512 /* Update the position of the next parameter to print */
513 bufptr = strchr(buf, '\0');
514 } else if (strchr("eEfFgGaA", arg.format[i])) {
517 /* Isolate this format alone */
518 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
519 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
521 /* Convert the argument into the required type */
522 if (arg.var[argcount]) {
523 if (sscanf(arg.var[argcount++], "%lf", &tmpd) != 1) {
524 ast_log(LOG_ERROR, "Argument '%s' is not a floating point number for format '%s'\n", arg.var[argcount - 1], formatbuf);
528 ast_log(LOG_ERROR, "SPRINTF() has more format specifiers than arguments!\n");
532 /* Format the argument */
533 snprintf(bufptr, buf + len - bufptr, formatbuf, tmpd);
535 /* Update the position of the next parameter to print */
536 bufptr = strchr(buf, '\0');
537 } else if (arg.format[i] == 's') {
540 /* Isolate this format alone */
541 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
542 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
544 /* Format the argument */
545 snprintf(bufptr, buf + len - bufptr, formatbuf, arg.var[argcount++]);
547 /* Update the position of the next parameter to print */
548 bufptr = strchr(buf, '\0');
549 } else if (arg.format[i] == '%') {
550 /* Literal data to copy */
551 *bufptr++ = arg.format[i];
555 /* Isolate this format alone */
556 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
557 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
559 ast_log(LOG_ERROR, "Format type not supported: '%s' with argument '%s'\n", formatbuf, arg.var[argcount++]);
565 if (arg.format[i] == '%') {
566 state = SPRINTF_FLAG;
567 formatstart = &arg.format[i];
570 /* Literal data to copy */
571 *bufptr++ = arg.format[i];
581 static struct ast_custom_function sprintf_function = {
583 .synopsis = "Format a variable according to a format string",
584 .syntax = "SPRINTF(<format>,<arg1>[,...<argN>])",
587 "Parses the format string specified and returns a string matching that format.\n"
588 "Supports most options supported by sprintf(3). Returns a shortened string if\n"
589 "a format specifier is not recognized.\n",
592 static int quote(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
594 char *bufptr = buf, *dataptr = data;
596 for (; bufptr < buf + len - 1; dataptr++) {
597 if (*dataptr == '\\') {
600 } else if (*dataptr == '"') {
603 } else if (*dataptr == '\0') {
606 *bufptr++ = *dataptr;
614 static struct ast_custom_function quote_function = {
616 .synopsis = "Quotes a given string, escaping embedded quotes as necessary",
617 .syntax = "QUOTE(<string>)",
622 static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf,
628 length = strlen(data);
630 snprintf(buf, buflen, "%d", length);
635 static struct ast_custom_function len_function = {
637 .synopsis = "Returns the length of the argument given",
638 .syntax = "LEN(<string>)",
642 static int acf_strftime(struct ast_channel *chan, const char *cmd, char *parse,
643 char *buf, size_t buflen)
645 AST_DECLARE_APP_ARGS(args,
647 AST_APP_ARG(timezone);
655 AST_STANDARD_APP_ARGS(args, parse);
657 ast_get_timeval(args.epoch, &when, ast_tvnow(), NULL);
658 ast_localtime(&when, &tm, args.timezone);
663 if (ast_strftime(buf, buflen, args.format, &tm) <= 0)
664 ast_log(LOG_WARNING, "C function strftime() output nothing?!!\n");
666 buf[buflen - 1] = '\0';
671 static struct ast_custom_function strftime_function = {
673 .synopsis = "Returns the current date/time in a specified format.",
674 .syntax = "STRFTIME([<epoch>][,[timezone][,format]])",
676 "STRFTIME sports all of the same formats as the underlying C function\n"
677 "strftime(3) - see the man page for details. It also supports the\n"
678 "following format:\n"
679 " %[n]q - fractions of a second, with leading zeroes. For example, %3q will\n"
680 " give milliseconds and %1q will give tenths of a second. The default\n"
681 " is to output milliseconds (n=3). The common case is to use it in\n"
682 " combination with %S, as in \"%S.%3q\".\n",
683 .read = acf_strftime,
686 static int acf_strptime(struct ast_channel *chan, const char *cmd, char *data,
687 char *buf, size_t buflen)
689 AST_DECLARE_APP_ARGS(args,
690 AST_APP_ARG(timestring);
691 AST_APP_ARG(timezone);
703 "Asterisk function STRPTIME() requires an argument.\n");
707 AST_STANDARD_APP_ARGS(args, data);
709 if (ast_strlen_zero(args.format)) {
711 "No format supplied to STRPTIME(<timestring>,<timezone>,<format>)");
715 if (!strptime(args.timestring, args.format, &t.time)) {
716 ast_log(LOG_WARNING, "C function strptime() output nothing?!!\n");
719 /* Since strptime(3) does not check DST, force ast_mktime() to calculate it. */
721 when = ast_mktime(&t.atm, args.timezone);
722 snprintf(buf, buflen, "%d", (int) when.tv_sec);
728 static struct ast_custom_function strptime_function = {
731 "Returns the epoch of the arbitrary date/time string structured as described in the format.",
732 .syntax = "STRPTIME(<datetime>,<timezone>,<format>)",
734 "This is useful for converting a date into an EPOCH time, possibly to pass to\n"
735 "an application like SayUnixTime or to calculate the difference between two\n"
739 " ${STRPTIME(2006-03-01 07:30:35,America/Chicago,%Y-%m-%d %H:%M:%S)} returns 1141219835\n",
740 .read = acf_strptime,
743 static int function_eval(struct ast_channel *chan, const char *cmd, char *data,
744 char *buf, size_t buflen)
746 if (ast_strlen_zero(data)) {
747 ast_log(LOG_WARNING, "EVAL requires an argument: EVAL(<string>)\n");
752 ast_autoservice_start(chan);
753 pbx_substitute_variables_helper(chan, data, buf, buflen - 1);
755 ast_autoservice_stop(chan);
760 static struct ast_custom_function eval_function = {
762 .synopsis = "Evaluate stored variables.",
763 .syntax = "EVAL(<variable>)",
764 .desc = "Using EVAL basically causes a string to be evaluated twice.\n"
765 "When a variable or expression is in the dialplan, it will be\n"
766 "evaluated at runtime. However, if the result of the evaluation\n"
767 "is in fact a variable or expression, using EVAL will have it\n"
768 "evaluated a second time. For example, if the variable ${MYVAR}\n"
769 "contains \"${OTHERVAR}\", then the result of putting ${EVAL(${MYVAR})}\n"
770 "in the dialplan will be the contents of the variable, OTHERVAR.\n"
771 "Normally, by just putting ${MYVAR} in the dialplan, you would be\n"
772 "left with \"${OTHERVAR}\".\n",
773 .read = function_eval,
776 static int keypadhash(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
778 char *bufptr, *dataptr;
780 for (bufptr = buf, dataptr = data; bufptr < buf + buflen - 1; dataptr++) {
781 if (*dataptr == '1') {
783 } else if (strchr("AaBbCc2", *dataptr)) {
785 } else if (strchr("DdEeFf3", *dataptr)) {
787 } else if (strchr("GgHhIi4", *dataptr)) {
789 } else if (strchr("JjKkLl5", *dataptr)) {
791 } else if (strchr("MmNnOo6", *dataptr)) {
793 } else if (strchr("PpQqRrSs7", *dataptr)) {
795 } else if (strchr("TtUuVv8", *dataptr)) {
797 } else if (strchr("WwXxYyZz9", *dataptr)) {
799 } else if (*dataptr == '0') {
801 } else if (*dataptr == '\0') {
806 buf[buflen - 1] = '\0';
811 static struct ast_custom_function keypadhash_function = {
812 .name = "KEYPADHASH",
813 .synopsis = "Hash the letters in the string into the equivalent keypad numbers.",
814 .syntax = "KEYPADHASH(<string>)",
816 .desc = "Example: ${KEYPADHASH(Les)} returns \"537\"\n",
819 static int string_toupper(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
821 char *bufptr = buf, *dataptr = data;
823 while ((bufptr < buf + buflen - 1) && (*bufptr++ = toupper(*dataptr++)));
828 static struct ast_custom_function toupper_function = {
830 .synopsis = "Convert the string to upper case.",
831 .syntax = "TOUPPER(<string>)",
832 .read = string_toupper,
833 .desc = "Example: ${TOUPPER(Example)} returns \"EXAMPLE\"\n",
836 static int string_tolower(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
838 char *bufptr = buf, *dataptr = data;
840 while ((bufptr < buf + buflen - 1) && (*bufptr++ = tolower(*dataptr++)));
845 static struct ast_custom_function tolower_function = {
847 .synopsis = "Convert the string to lower case.",
848 .syntax = "TOLOWER(<string>)",
849 .read = string_tolower,
850 .desc = "Example: ${TOLOWER(Example)} returns \"example\"\n",
853 static int unload_module(void)
857 res |= ast_custom_function_unregister(&fieldqty_function);
858 res |= ast_custom_function_unregister(&filter_function);
859 res |= ast_custom_function_unregister(®ex_function);
860 res |= ast_custom_function_unregister(&array_function);
861 res |= ast_custom_function_unregister("e_function);
862 res |= ast_custom_function_unregister(&len_function);
863 res |= ast_custom_function_unregister(&strftime_function);
864 res |= ast_custom_function_unregister(&strptime_function);
865 res |= ast_custom_function_unregister(&eval_function);
866 res |= ast_custom_function_unregister(&keypadhash_function);
867 res |= ast_custom_function_unregister(&sprintf_function);
868 res |= ast_custom_function_unregister(&hashkeys_function);
869 res |= ast_custom_function_unregister(&hash_function);
870 res |= ast_unregister_application(app_clearhash);
871 res |= ast_custom_function_unregister(&toupper_function);
872 res |= ast_custom_function_unregister(&tolower_function);
877 static int load_module(void)
881 res |= ast_custom_function_register(&fieldqty_function);
882 res |= ast_custom_function_register(&filter_function);
883 res |= ast_custom_function_register(®ex_function);
884 res |= ast_custom_function_register(&array_function);
885 res |= ast_custom_function_register("e_function);
886 res |= ast_custom_function_register(&len_function);
887 res |= ast_custom_function_register(&strftime_function);
888 res |= ast_custom_function_register(&strptime_function);
889 res |= ast_custom_function_register(&eval_function);
890 res |= ast_custom_function_register(&keypadhash_function);
891 res |= ast_custom_function_register(&sprintf_function);
892 res |= ast_custom_function_register(&hashkeys_function);
893 res |= ast_custom_function_register(&hash_function);
894 res |= ast_register_application(app_clearhash, exec_clearhash, syn_clearhash, desc_clearhash);
895 res |= ast_custom_function_register(&toupper_function);
896 res |= ast_custom_function_register(&tolower_function);
901 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "String handling dialplan functions");