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 (sscanf(arg.var[argcount++], "%d", &tmpi) != 1) {
500 ast_log(LOG_ERROR, "Argument '%s' is not an integer number for format '%s'\n", arg.var[argcount - 1], formatbuf);
504 /* Format the argument */
505 snprintf(bufptr, buf + len - bufptr, formatbuf, tmpi);
507 /* Update the position of the next parameter to print */
508 bufptr = strchr(buf, '\0');
509 } else if (strchr("eEfFgGaA", arg.format[i])) {
512 /* Isolate this format alone */
513 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
514 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
516 /* Convert the argument into the required type */
517 if (sscanf(arg.var[argcount++], "%lf", &tmpd) != 1) {
518 ast_log(LOG_ERROR, "Argument '%s' is not a floating point number for format '%s'\n", arg.var[argcount - 1], formatbuf);
522 /* Format the argument */
523 snprintf(bufptr, buf + len - bufptr, formatbuf, tmpd);
525 /* Update the position of the next parameter to print */
526 bufptr = strchr(buf, '\0');
527 } else if (arg.format[i] == 's') {
530 /* Isolate this format alone */
531 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
532 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
534 /* Format the argument */
535 snprintf(bufptr, buf + len - bufptr, formatbuf, arg.var[argcount++]);
537 /* Update the position of the next parameter to print */
538 bufptr = strchr(buf, '\0');
539 } else if (arg.format[i] == '%') {
540 /* Literal data to copy */
541 *bufptr++ = arg.format[i];
545 /* Isolate this format alone */
546 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
547 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
549 ast_log(LOG_ERROR, "Format type not supported: '%s' with argument '%s'\n", formatbuf, arg.var[argcount++]);
555 if (arg.format[i] == '%') {
556 state = SPRINTF_FLAG;
557 formatstart = &arg.format[i];
560 /* Literal data to copy */
561 *bufptr++ = arg.format[i];
570 static struct ast_custom_function sprintf_function = {
572 .synopsis = "Format a variable according to a format string",
573 .syntax = "SPRINTF(<format>,<arg1>[,...<argN>])",
576 "Parses the format string specified and returns a string matching that format.\n"
577 "Supports most options supported by sprintf(3). Returns a shortened string if\n"
578 "a format specifier is not recognized.\n",
581 static int quote(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
583 char *bufptr = buf, *dataptr = data;
585 for (; bufptr < buf + len - 1; dataptr++) {
586 if (*dataptr == '\\') {
589 } else if (*dataptr == '"') {
592 } else if (*dataptr == '\0') {
595 *bufptr++ = *dataptr;
603 static struct ast_custom_function quote_function = {
605 .synopsis = "Quotes a given string, escaping embedded quotes as necessary",
606 .syntax = "QUOTE(<string>)",
611 static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf,
617 length = strlen(data);
619 snprintf(buf, buflen, "%d", length);
624 static struct ast_custom_function len_function = {
626 .synopsis = "Returns the length of the argument given",
627 .syntax = "LEN(<string>)",
631 static int acf_strftime(struct ast_channel *chan, const char *cmd, char *parse,
632 char *buf, size_t buflen)
634 AST_DECLARE_APP_ARGS(args,
636 AST_APP_ARG(timezone);
644 AST_STANDARD_APP_ARGS(args, parse);
646 ast_get_timeval(args.epoch, &when, ast_tvnow(), NULL);
647 ast_localtime(&when, &tm, args.timezone);
652 if (ast_strftime(buf, buflen, args.format, &tm) <= 0)
653 ast_log(LOG_WARNING, "C function strftime() output nothing?!!\n");
655 buf[buflen - 1] = '\0';
660 static struct ast_custom_function strftime_function = {
662 .synopsis = "Returns the current date/time in a specified format.",
663 .syntax = "STRFTIME([<epoch>][,[timezone][,format]])",
665 "STRFTIME sports all of the same formats as the underlying C function\n"
666 "strftime(3) - see the man page for details. It also supports the\n"
667 "following format:\n"
668 " %[n]q - fractions of a second, with leading zeroes. For example, %3q will\n"
669 " give milliseconds and %1q will give tenths of a second. The default\n"
670 " is to output milliseconds (n=3). The common case is to use it in\n"
671 " combination with %S, as in \"%S.%3q\".\n",
672 .read = acf_strftime,
675 static int acf_strptime(struct ast_channel *chan, const char *cmd, char *data,
676 char *buf, size_t buflen)
678 AST_DECLARE_APP_ARGS(args,
679 AST_APP_ARG(timestring);
680 AST_APP_ARG(timezone);
692 "Asterisk function STRPTIME() requires an argument.\n");
696 AST_STANDARD_APP_ARGS(args, data);
698 if (ast_strlen_zero(args.format)) {
700 "No format supplied to STRPTIME(<timestring>,<timezone>,<format>)");
704 if (!strptime(args.timestring, args.format, &t.time)) {
705 ast_log(LOG_WARNING, "C function strptime() output nothing?!!\n");
708 /* Since strptime(3) does not check DST, force ast_mktime() to calculate it. */
710 when = ast_mktime(&t.atm, args.timezone);
711 snprintf(buf, buflen, "%d", (int) when.tv_sec);
717 static struct ast_custom_function strptime_function = {
720 "Returns the epoch of the arbitrary date/time string structured as described in the format.",
721 .syntax = "STRPTIME(<datetime>,<timezone>,<format>)",
723 "This is useful for converting a date into an EPOCH time, possibly to pass to\n"
724 "an application like SayUnixTime or to calculate the difference between two\n"
728 " ${STRPTIME(2006-03-01 07:30:35,America/Chicago,%Y-%m-%d %H:%M:%S)} returns 1141219835\n",
729 .read = acf_strptime,
732 static int function_eval(struct ast_channel *chan, const char *cmd, char *data,
733 char *buf, size_t buflen)
735 if (ast_strlen_zero(data)) {
736 ast_log(LOG_WARNING, "EVAL requires an argument: EVAL(<string>)\n");
741 ast_autoservice_start(chan);
742 pbx_substitute_variables_helper(chan, data, buf, buflen - 1);
744 ast_autoservice_stop(chan);
749 static struct ast_custom_function eval_function = {
751 .synopsis = "Evaluate stored variables.",
752 .syntax = "EVAL(<variable>)",
753 .desc = "Using EVAL basically causes a string to be evaluated twice.\n"
754 "When a variable or expression is in the dialplan, it will be\n"
755 "evaluated at runtime. However, if the result of the evaluation\n"
756 "is in fact a variable or expression, using EVAL will have it\n"
757 "evaluated a second time. For example, if the variable ${MYVAR}\n"
758 "contains \"${OTHERVAR}\", then the result of putting ${EVAL(${MYVAR})}\n"
759 "in the dialplan will be the contents of the variable, OTHERVAR.\n"
760 "Normally, by just putting ${MYVAR} in the dialplan, you would be\n"
761 "left with \"${OTHERVAR}\".\n",
762 .read = function_eval,
765 static int keypadhash(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
767 char *bufptr, *dataptr;
769 for (bufptr = buf, dataptr = data; bufptr < buf + buflen - 1; dataptr++) {
770 if (*dataptr == '1') {
772 } else if (strchr("AaBbCc2", *dataptr)) {
774 } else if (strchr("DdEeFf3", *dataptr)) {
776 } else if (strchr("GgHhIi4", *dataptr)) {
778 } else if (strchr("JjKkLl5", *dataptr)) {
780 } else if (strchr("MmNnOo6", *dataptr)) {
782 } else if (strchr("PpQqRrSs7", *dataptr)) {
784 } else if (strchr("TtUuVv8", *dataptr)) {
786 } else if (strchr("WwXxYyZz9", *dataptr)) {
788 } else if (*dataptr == '0') {
790 } else if (*dataptr == '\0') {
795 buf[buflen - 1] = '\0';
800 static struct ast_custom_function keypadhash_function = {
801 .name = "KEYPADHASH",
802 .synopsis = "Hash the letters in the string into the equivalent keypad numbers.",
803 .syntax = "KEYPADHASH(<string>)",
805 .desc = "Example: ${KEYPADHASH(Les)} returns \"537\"\n",
808 static int string_toupper(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
810 char *bufptr = buf, *dataptr = data;
812 while ((bufptr < buf + buflen - 1) && (*bufptr++ = toupper(*dataptr++)));
817 static struct ast_custom_function toupper_function = {
819 .synopsis = "Convert the string to upper case.",
820 .syntax = "TOUPPER(<string>)",
821 .read = string_toupper,
822 .desc = "Example: ${TOUPPER(Example)} returns \"EXAMPLE\"\n",
825 static int string_tolower(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
827 char *bufptr = buf, *dataptr = data;
829 while ((bufptr < buf + buflen - 1) && (*bufptr++ = tolower(*dataptr++)));
834 static struct ast_custom_function tolower_function = {
836 .synopsis = "Convert the string to lower case.",
837 .syntax = "TOLOWER(<string>)",
838 .read = string_tolower,
839 .desc = "Example: ${TOLOWER(Example)} returns \"example\"\n",
842 static int unload_module(void)
846 res |= ast_custom_function_unregister(&fieldqty_function);
847 res |= ast_custom_function_unregister(&filter_function);
848 res |= ast_custom_function_unregister(®ex_function);
849 res |= ast_custom_function_unregister(&array_function);
850 res |= ast_custom_function_unregister("e_function);
851 res |= ast_custom_function_unregister(&len_function);
852 res |= ast_custom_function_unregister(&strftime_function);
853 res |= ast_custom_function_unregister(&strptime_function);
854 res |= ast_custom_function_unregister(&eval_function);
855 res |= ast_custom_function_unregister(&keypadhash_function);
856 res |= ast_custom_function_unregister(&sprintf_function);
857 res |= ast_custom_function_unregister(&hashkeys_function);
858 res |= ast_custom_function_unregister(&hash_function);
859 res |= ast_unregister_application(app_clearhash);
860 res |= ast_custom_function_unregister(&toupper_function);
861 res |= ast_custom_function_unregister(&tolower_function);
866 static int load_module(void)
870 res |= ast_custom_function_register(&fieldqty_function);
871 res |= ast_custom_function_register(&filter_function);
872 res |= ast_custom_function_register(®ex_function);
873 res |= ast_custom_function_register(&array_function);
874 res |= ast_custom_function_register("e_function);
875 res |= ast_custom_function_register(&len_function);
876 res |= ast_custom_function_register(&strftime_function);
877 res |= ast_custom_function_register(&strptime_function);
878 res |= ast_custom_function_register(&eval_function);
879 res |= ast_custom_function_register(&keypadhash_function);
880 res |= ast_custom_function_register(&sprintf_function);
881 res |= ast_custom_function_register(&hashkeys_function);
882 res |= ast_custom_function_register(&hash_function);
883 res |= ast_register_application(app_clearhash, exec_clearhash, syn_clearhash, desc_clearhash);
884 res |= ast_custom_function_register(&toupper_function);
885 res |= ast_custom_function_register(&tolower_function);
890 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "String handling dialplan functions");