Merged revisions 114191 via svnmerge from
[asterisk-bristuff.git] / funcs / func_strings.c
blobf50ea245dc7d41ec024c2758c63774fb1f830172
1 /*
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.
19 /*! \file
21 * \brief String manipulation dialplan functions
23 * \author Tilghman Lesher
24 * \author Anothony Minessale II
25 * \ingroup functions
28 #include "asterisk.h"
30 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
32 #include <regex.h>
33 #include <ctype.h>
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;
46 int fieldcount = 0;
47 AST_DECLARE_APP_ARGS(args,
48 AST_APP_ARG(varname);
49 AST_APP_ARG(delim);
51 char delim[2] = "";
52 size_t delim_used;
54 if (chan)
55 ast_autoservice_start(chan);
57 AST_STANDARD_APP_ARGS(args, parse);
58 if (args.delim) {
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))
66 fieldcount = 0;
67 else {
68 while (strsep(&varval2, delim))
69 fieldcount++;
71 } else {
72 fieldcount = 1;
74 snprintf(buf, len, "%d", fieldcount);
76 if (chan)
77 ast_autoservice_stop(chan);
79 return 0;
82 static struct ast_custom_function fieldqty_function = {
83 .name = "FIELDQTY",
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,
90 size_t len)
92 AST_DECLARE_APP_ARGS(args,
93 AST_APP_ARG(allowed);
94 AST_APP_ARG(string);
96 char *outbuf = buf, ac;
97 char allowed[256] = "";
98 size_t allowedlen = 0;
100 AST_STANDARD_APP_ARGS(args, parse);
102 if (!args.string) {
103 ast_log(LOG_ERROR, "Usage: FILTER(<allowed-chars>,<string>)\n");
104 return -1;
107 /* Expand ranges */
108 for (; *(args.allowed) && allowedlen < sizeof(allowed); (args.allowed)++) {
109 char c1 = 0, c2 = 0;
110 size_t consumed = 0;
112 if (ast_get_encoded_char(args.allowed, &c1, &consumed))
113 return -1;
114 args.allowed += consumed;
116 if (*(args.allowed) == '-') {
117 if (ast_get_encoded_char(args.allowed + 1, &c2, &consumed))
118 c2 = -1;
119 args.allowed += consumed + 1;
121 /*!\note
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 */
132 (args.allowed)--;
133 } else
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);
143 *outbuf = '\0';
145 return 0;
148 static struct ast_custom_function filter_function = {
149 .name = "FILTER",
150 .synopsis = "Filter the string to include only the allowed characters",
151 .syntax = "FILTER(<allowed-chars>,<string>)",
152 .read = filter,
153 .desc =
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,
163 size_t len)
165 AST_DECLARE_APP_ARGS(args,
166 AST_APP_ARG(null);
167 AST_APP_ARG(reg);
168 AST_APP_ARG(str);
170 int errcode;
171 regex_t regexbuf;
173 buf[0] = '\0';
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");
179 return -1;
181 if ((*args.str == ' ') || (*args.str == '\t'))
182 args.str++;
184 ast_debug(1, "FUNCTION REGEX (%s)(%s)\n", args.reg, args.str);
186 if ((errcode = regcomp(&regexbuf, args.reg, REG_EXTENDED | REG_NOSUB))) {
187 regerror(errcode, &regexbuf, buf, len);
188 ast_log(LOG_WARNING, "Malformed input %s(%s): %s\n", cmd, parse, buf);
189 return -1;
192 strcpy(buf, regexec(&regexbuf, args.str, 0, NULL, 0) ? "0" : "1");
194 regfree(&regexbuf);
196 return 0;
199 static struct ast_custom_function regex_function = {
200 .name = "REGEX",
201 .synopsis = "Regular Expression",
202 .desc =
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>)",
208 .read = regex,
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);
228 ast_free(var);
231 AST_LIST_TRAVERSE_SAFE_END
234 static int exec_clearhash(struct ast_channel *chan, void *data)
236 char prefix[80];
237 snprintf(prefix, sizeof(prefix), HASH_PREFIX, data ? (char *)data : "null");
238 clearvar_prefix(chan, prefix);
239 return 0;
242 static int array(struct ast_channel *chan, const char *cmd, char *var,
243 const char *value)
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];
252 int i, ishash = 0;
254 value2 = ast_strdupa(value);
255 if (!var || !value2)
256 return -1;
258 if (chan)
259 ast_autoservice_start(chan);
261 if (!strcmp(cmd, "HASH")) {
262 const char *var2 = pbx_builtin_getvar_helper(chan, "~ODBCFIELDS~");
263 origvar = var;
264 if (var2)
265 var = ast_strdupa(var2);
266 else {
267 if (chan)
268 ast_autoservice_stop(chan);
269 return -1;
271 ishash = 1;
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],
287 arg2.val[i]);
288 if (i < arg2.argc) {
289 if (ishash) {
290 snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]);
291 pbx_builtin_setvar_helper(chan, varname, arg2.val[i]);
292 } else {
293 pbx_builtin_setvar_helper(chan, arg1.var[i], arg2.val[i]);
295 } else {
296 /* We could unset the variable, by passing a NULL, but due to
297 * pushvar semantics, that could create some undesired behavior. */
298 if (ishash) {
299 snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]);
300 pbx_builtin_setvar_helper(chan, varname, "");
301 } else {
302 pbx_builtin_setvar_helper(chan, arg1.var[i], "");
307 if (chan)
308 ast_autoservice_stop(chan);
310 return 0;
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;
316 int plen;
317 char prefix[80];
318 snprintf(prefix, sizeof(prefix), HASH_PREFIX, data);
319 plen = strlen(prefix);
321 memset(buf, 0, len);
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';
332 return 0;
335 static int hash_write(struct ast_channel *chan, const char *cmd, char *var, const char *value)
337 char varname[256];
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);
352 return 0;
355 static int hash_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
357 char varname[256];
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);
365 if (arg.argc == 2) {
366 snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey);
367 varvalue = pbx_builtin_getvar_helper(chan, varname);
368 if (varvalue)
369 ast_copy_string(buf, varvalue, len);
370 else
371 *buf = '\0';
372 } else if (arg.argc == 1) {
373 char colnames[4096];
374 int i;
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);
384 *buf = '\0';
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';
398 return 0;
401 static struct ast_custom_function hash_function = {
402 .name = "HASH",
403 .synopsis = "Implementation of a dialplan associative array",
404 .syntax = "HASH(hashname[,hashkey])",
405 .write = hash_write,
406 .read = hash_read,
407 .desc =
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 = {
414 .name = "HASHKEYS",
415 .synopsis = "Retrieve the keys of a HASH()",
416 .syntax = "HASHKEYS(<hashname>)",
417 .read = hashkeys_read,
418 .desc =
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 = {
426 .name = "ARRAY",
427 .synopsis = "Allows setting multiple variables at once",
428 .syntax = "ARRAY(var1[,var2[...][,varN]])",
429 .write = array,
430 .desc =
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] = "";
447 int tmpi;
448 double tmpd;
449 AST_DECLARE_APP_ARGS(arg,
450 AST_APP_ARG(format);
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++) {
458 switch (state) {
459 case SPRINTF_FLAG:
460 if (strchr("#0- +'I", arg.format[i]))
461 break;
462 state = SPRINTF_WIDTH;
463 case SPRINTF_WIDTH:
464 if (arg.format[i] >= '0' && arg.format[i] <= '9')
465 break;
467 /* Next character must be a period to go into a precision */
468 if (arg.format[i] == '.') {
469 state = SPRINTF_PRECISION;
470 } else {
471 state = SPRINTF_LENGTH;
472 i--;
474 break;
475 case SPRINTF_PRECISION:
476 if (arg.format[i] >= '0' && arg.format[i] <= '9')
477 break;
478 state = SPRINTF_LENGTH;
479 case SPRINTF_LENGTH:
480 if (strchr("hl", arg.format[i])) {
481 if (arg.format[i + 1] == arg.format[i])
482 i++;
483 state = SPRINTF_CONVERSION;
484 break;
485 } else if (strchr("Lqjzt", arg.format[i])) {
486 state = SPRINTF_CONVERSION;
487 break;
489 state = SPRINTF_CONVERSION;
490 case SPRINTF_CONVERSION:
491 if (strchr("diouxXc", arg.format[i])) {
492 /* Integer */
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);
501 goto sprintf_fail;
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])) {
510 /* Double */
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);
519 goto sprintf_fail;
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') {
528 /* String */
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];
542 } else {
543 /* Not supported */
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++]);
550 goto sprintf_fail;
552 state = -1;
553 break;
554 default:
555 if (arg.format[i] == '%') {
556 state = SPRINTF_FLAG;
557 formatstart = &arg.format[i];
558 break;
559 } else {
560 /* Literal data to copy */
561 *bufptr++ = arg.format[i];
565 return 0;
566 sprintf_fail:
567 return -1;
570 static struct ast_custom_function sprintf_function = {
571 .name = "SPRINTF",
572 .synopsis = "Format a variable according to a format string",
573 .syntax = "SPRINTF(<format>,<arg1>[,...<argN>])",
574 .read = acf_sprintf,
575 .desc =
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;
584 *bufptr++ = '"';
585 for (; bufptr < buf + len - 1; dataptr++) {
586 if (*dataptr == '\\') {
587 *bufptr++ = '\\';
588 *bufptr++ = '\\';
589 } else if (*dataptr == '"') {
590 *bufptr++ = '\\';
591 *bufptr++ = '"';
592 } else if (*dataptr == '\0') {
593 break;
594 } else {
595 *bufptr++ = *dataptr;
598 *bufptr++ = '"';
599 *bufptr = '\0';
600 return 0;
603 static struct ast_custom_function quote_function = {
604 .name = "QUOTE",
605 .synopsis = "Quotes a given string, escaping embedded quotes as necessary",
606 .syntax = "QUOTE(<string>)",
607 .read = quote,
611 static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf,
612 size_t len)
614 int length = 0;
616 if (data)
617 length = strlen(data);
619 snprintf(buf, len, "%d", length);
621 return 0;
624 static struct ast_custom_function len_function = {
625 .name = "LEN",
626 .synopsis = "Returns the length of the argument given",
627 .syntax = "LEN(<string>)",
628 .read = len,
631 static int acf_strftime(struct ast_channel *chan, const char *cmd, char *parse,
632 char *buf, size_t len)
634 AST_DECLARE_APP_ARGS(args,
635 AST_APP_ARG(epoch);
636 AST_APP_ARG(timezone);
637 AST_APP_ARG(format);
639 struct timeval tv;
640 struct ast_tm tm;
642 buf[0] = '\0';
644 AST_STANDARD_APP_ARGS(args, parse);
646 ast_get_timeval(args.epoch, &tv, ast_tvnow(), NULL);
647 ast_localtime(&tv, &tm, args.timezone);
649 if (!args.format)
650 args.format = "%c";
652 if (ast_strftime(buf, len, args.format, &tm) <= 0)
653 ast_log(LOG_WARNING, "C function strftime() output nothing?!!\n");
655 buf[len - 1] = '\0';
657 return 0;
660 static struct ast_custom_function strftime_function = {
661 .name = "STRFTIME",
662 .synopsis = "Returns the current date/time in a specified format.",
663 .syntax = "STRFTIME([<epoch>][,[timezone][,format]])",
664 .desc =
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 len)
678 AST_DECLARE_APP_ARGS(args,
679 AST_APP_ARG(timestring);
680 AST_APP_ARG(timezone);
681 AST_APP_ARG(format);
683 union {
684 struct ast_tm atm;
685 struct tm time;
686 } t = { { 0, }, };
688 buf[0] = '\0';
690 if (!data) {
691 ast_log(LOG_ERROR,
692 "Asterisk function STRPTIME() requires an argument.\n");
693 return -1;
696 AST_STANDARD_APP_ARGS(args, data);
698 if (ast_strlen_zero(args.format)) {
699 ast_log(LOG_ERROR,
700 "No format supplied to STRPTIME(<timestring>,<timezone>,<format>)");
701 return -1;
704 if (!strptime(args.timestring, args.format, &t.time)) {
705 ast_log(LOG_WARNING, "C function strptime() output nothing?!!\n");
706 } else {
707 struct timeval tv;
708 /* Since strptime(3) does not check DST, force ast_mktime() to calculate it. */
709 t.atm.tm_isdst = -1;
710 tv = ast_mktime(&t.atm, args.timezone);
711 snprintf(buf, len, "%d", (int) tv.tv_sec);
714 return 0;
717 static struct ast_custom_function strptime_function = {
718 .name = "STRPTIME",
719 .synopsis =
720 "Returns the epoch of the arbitrary date/time string structured as described in the format.",
721 .syntax = "STRPTIME(<datetime>,<timezone>,<format>)",
722 .desc =
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"
725 "date strings.\n"
726 "\n"
727 "Example:\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 len)
735 if (ast_strlen_zero(data)) {
736 ast_log(LOG_WARNING, "EVAL requires an argument: EVAL(<string>)\n");
737 return -1;
740 if (chan)
741 ast_autoservice_start(chan);
742 pbx_substitute_variables_helper(chan, data, buf, len - 1);
743 if (chan)
744 ast_autoservice_stop(chan);
746 return 0;
749 static struct ast_custom_function eval_function = {
750 .name = "EVAL",
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 len)
767 char *bufptr, *dataptr;
769 for (bufptr = buf, dataptr = data; bufptr < buf + len - 1; dataptr++) {
770 if (*dataptr == '1') {
771 *bufptr++ = '1';
772 } else if (strchr("AaBbCc2", *dataptr)) {
773 *bufptr++ = '2';
774 } else if (strchr("DdEeFf3", *dataptr)) {
775 *bufptr++ = '3';
776 } else if (strchr("GgHhIi4", *dataptr)) {
777 *bufptr++ = '4';
778 } else if (strchr("JjKkLl5", *dataptr)) {
779 *bufptr++ = '5';
780 } else if (strchr("MmNnOo6", *dataptr)) {
781 *bufptr++ = '6';
782 } else if (strchr("PpQqRrSs7", *dataptr)) {
783 *bufptr++ = '7';
784 } else if (strchr("TtUuVv8", *dataptr)) {
785 *bufptr++ = '8';
786 } else if (strchr("WwXxYyZz9", *dataptr)) {
787 *bufptr++ = '9';
788 } else if (*dataptr == '0') {
789 *bufptr++ = '0';
790 } else if (*dataptr == '\0') {
791 *bufptr++ = '\0';
792 break;
795 buf[len - 1] = '\0';
797 return 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>)",
804 .read = keypadhash,
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 len)
810 char *bufptr = buf, *dataptr = data;
812 while ((bufptr < buf + len - 1) && (*bufptr++ = toupper(*dataptr++)));
814 return 0;
817 static struct ast_custom_function toupper_function = {
818 .name = "TOUPPER",
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 len)
827 char *bufptr = buf, *dataptr = data;
829 while ((bufptr < buf + len - 1) && (*bufptr++ = tolower(*dataptr++)));
831 return 0;
834 static struct ast_custom_function tolower_function = {
835 .name = "TOLOWER",
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)
844 int res = 0;
846 res |= ast_custom_function_unregister(&fieldqty_function);
847 res |= ast_custom_function_unregister(&filter_function);
848 res |= ast_custom_function_unregister(&regex_function);
849 res |= ast_custom_function_unregister(&array_function);
850 res |= ast_custom_function_unregister(&quote_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);
863 return res;
866 static int load_module(void)
868 int res = 0;
870 res |= ast_custom_function_register(&fieldqty_function);
871 res |= ast_custom_function_register(&filter_function);
872 res |= ast_custom_function_register(&regex_function);
873 res |= ast_custom_function_register(&array_function);
874 res |= ast_custom_function_register(&quote_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);
887 return res;
890 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "String handling dialplan functions");