Add a massive set of changes for converting to use the ast_debug() macro.
[asterisk/asterisk.git] / funcs / func_strings.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
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
7  *
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.
13  *
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.
17  */
18
19 /*! \file
20  *
21  * \brief String manipulation dialplan functions
22  *
23  * \author Tilghman Lesher
24  * \author Anothony Minessale II 
25  * \ingroup functions
26  */
27
28 #include "asterisk.h"
29
30 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
31
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <sys/types.h>
36 #include <regex.h>
37
38 #include "asterisk/module.h"
39 #include "asterisk/options.h"
40 #include "asterisk/channel.h"
41 #include "asterisk/pbx.h"
42 #include "asterisk/logger.h"
43 #include "asterisk/utils.h"
44 #include "asterisk/app.h"
45 #include "asterisk/localtime.h"
46 #include "asterisk/options.h"
47
48 static int function_fieldqty(struct ast_channel *chan, const char *cmd,
49                              char *parse, char *buf, size_t len)
50 {
51         char *varsubst, varval[8192] = "", *varval2 = varval;
52         int fieldcount = 0;
53         AST_DECLARE_APP_ARGS(args,
54                              AST_APP_ARG(varname);
55                              AST_APP_ARG(delim);
56                 );
57
58         AST_STANDARD_APP_ARGS(args, parse);
59         if (args.delim) {
60                 if (args.delim[0] == '\\') {
61                         if (args.delim[1] == 'n')
62                                 ast_copy_string(args.delim, "\n", 2);
63                         else if (args.delim[1] == 't')
64                                 ast_copy_string(args.delim, "\t", 2);
65                         else if (args.delim[1])
66                                 ast_copy_string(args.delim, &args.delim[1], 2);
67                         else
68                                 ast_copy_string(args.delim, "-", 2);
69                 }
70                 varsubst = alloca(strlen(args.varname) + 4);
71
72                 sprintf(varsubst, "${%s}", args.varname);
73                 pbx_substitute_variables_helper(chan, varsubst, varval, sizeof(varval) - 1);
74                 if (ast_strlen_zero(varval2))
75                         fieldcount = 0;
76                 else {
77                         while (strsep(&varval2, args.delim))
78                                 fieldcount++;
79                 }
80         } else {
81                 fieldcount = 1;
82         }
83         snprintf(buf, len, "%d", fieldcount);
84
85         return 0;
86 }
87
88 static struct ast_custom_function fieldqty_function = {
89         .name = "FIELDQTY",
90         .synopsis = "Count the fields, with an arbitrary delimiter",
91         .syntax = "FIELDQTY(<varname>|<delim>)",
92         .read = function_fieldqty,
93 };
94
95 static int filter(struct ast_channel *chan, const char *cmd, char *parse, char *buf,
96                   size_t len)
97 {
98         AST_DECLARE_APP_ARGS(args,
99                              AST_APP_ARG(allowed);
100                              AST_APP_ARG(string);
101         );
102         char *outbuf = buf;
103
104         AST_STANDARD_APP_ARGS(args, parse);
105
106         if (!args.string) {
107                 ast_log(LOG_ERROR, "Usage: FILTER(<allowed-chars>|<string>)\n");
108                 return -1;
109         }
110
111         for (; *(args.string) && (buf + len - 1 > outbuf); (args.string)++) {
112                 if (strchr(args.allowed, *(args.string)))
113                         *outbuf++ = *(args.string);
114         }
115         *outbuf = '\0';
116
117         return 0;
118 }
119
120 static struct ast_custom_function filter_function = {
121         .name = "FILTER",
122         .synopsis = "Filter the string to include only the allowed characters",
123         .syntax = "FILTER(<allowed-chars>|<string>)",
124         .read = filter,
125 };
126
127 static int regex(struct ast_channel *chan, const char *cmd, char *parse, char *buf,
128                  size_t len)
129 {
130         AST_DECLARE_APP_ARGS(args,
131                              AST_APP_ARG(null);
132                              AST_APP_ARG(reg);
133                              AST_APP_ARG(str);
134         );
135         int errcode;
136         regex_t regexbuf;
137
138         buf[0] = '\0';
139
140         AST_NONSTANDARD_APP_ARGS(args, parse, '"');
141
142         if (args.argc != 3) {
143                 ast_log(LOG_ERROR, "Unexpected arguments: should have been in the form '\"<regex>\" <string>'\n");
144                 return -1;
145         }
146         if ((*args.str == ' ') || (*args.str == '\t'))
147                 args.str++;
148
149         ast_debug(1, "FUNCTION REGEX (%s)(%s)\n", args.reg, args.str);
150
151         if ((errcode = regcomp(&regexbuf, args.reg, REG_EXTENDED | REG_NOSUB))) {
152                 regerror(errcode, &regexbuf, buf, len);
153                 ast_log(LOG_WARNING, "Malformed input %s(%s): %s\n", cmd, parse, buf);
154                 return -1;
155         }
156         
157         strcpy(buf, regexec(&regexbuf, args.str, 0, NULL, 0) ? "0" : "1");
158
159         regfree(&regexbuf);
160
161         return 0;
162 }
163
164 static struct ast_custom_function regex_function = {
165         .name = "REGEX",
166         .synopsis = "Regular Expression",
167         .desc =  
168                 "Returns 1 if data matches regular expression, or 0 otherwise.\n"
169                 "Please note that the space following the double quotes separating the regex from the data\n"
170                 "is optional and if present, is skipped. If a space is desired at the beginning of the data,\n"
171                 "then put two spaces there; the second will not be skipped.\n",
172         .syntax = "REGEX(\"<regular expression>\" <data>)",
173         .read = regex,
174 };
175
176 #define HASH_PREFIX     "~HASH~%s~"
177 #define HASH_FORMAT     HASH_PREFIX "%s~"
178
179 static char *app_clearhash = "ClearHash";
180 static char *syn_clearhash = "Clear the keys from a specified hashname";
181 static char *desc_clearhash =
182 "ClearHash(<hashname>)\n"
183 "  Clears all keys out of the specified hashname\n";
184
185 /* This function probably should migrate to main/pbx.c, as pbx_builtin_clearvar_prefix() */
186 static void clearvar_prefix(struct ast_channel *chan, const char *prefix)
187 {
188         struct ast_var_t *var;
189         int len = strlen(prefix);
190         AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->varshead, var, entries) {
191                 if (strncasecmp(prefix, ast_var_name(var), len) == 0) {
192                         AST_LIST_REMOVE_CURRENT(&chan->varshead, entries);
193                         ast_free(var);
194                 }
195         }
196         AST_LIST_TRAVERSE_SAFE_END
197 }
198
199 static int exec_clearhash(struct ast_channel *chan, void *data)
200 {
201         char prefix[80];
202         snprintf(prefix, sizeof(prefix), HASH_PREFIX, data ? (char *)data : "null");
203         clearvar_prefix(chan, prefix);
204         return 0;
205 }
206
207 static int array(struct ast_channel *chan, const char *cmd, char *var,
208                  const char *value)
209 {
210         AST_DECLARE_APP_ARGS(arg1,
211                              AST_APP_ARG(var)[100];
212         );
213         AST_DECLARE_APP_ARGS(arg2,
214                              AST_APP_ARG(val)[100];
215         );
216         char *origvar = "", *value2, varname[256];
217         int i, ishash = 0;
218
219         value2 = ast_strdupa(value);
220         if (!var || !value2)
221                 return -1;
222
223         if (!strcmp(cmd, "HASH")) {
224                 const char *var2 = pbx_builtin_getvar_helper(chan, "~ODBCFIELDS~");
225                 origvar = var;
226                 if (var2)
227                         var = ast_strdupa(var2);
228                 else
229                         return -1;
230                 ishash = 1;
231         }
232
233         /* The functions this will generally be used with are SORT and ODBC_*, which
234          * both return comma-delimited lists.  However, if somebody uses literal lists,
235          * their commas will be translated to vertical bars by the load, and I don't
236          * want them to be surprised by the result.  Hence, we prefer commas as the
237          * delimiter, but we'll fall back to vertical bars if commas aren't found.
238          */
239         ast_debug(1, "array (%s=%s)\n", var, value2);
240         if (strchr(var, ','))
241                 AST_NONSTANDARD_APP_ARGS(arg1, var, ',');
242         else
243                 AST_STANDARD_APP_ARGS(arg1, var);
244
245         if (strchr(value2, ','))
246                 AST_NONSTANDARD_APP_ARGS(arg2, value2, ',');
247         else
248                 AST_STANDARD_APP_ARGS(arg2, value2);
249
250         for (i = 0; i < arg1.argc; i++) {
251                 ast_debug(1, "array set value (%s=%s)\n", arg1.var[i],
252                                 arg2.val[i]);
253                 if (i < arg2.argc) {
254                         if (ishash) {
255                                 snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]);
256                                 pbx_builtin_setvar_helper(chan, varname, arg2.val[i]);
257                         } else {
258                                 pbx_builtin_setvar_helper(chan, arg1.var[i], arg2.val[i]);
259                         }
260                 } else {
261                         /* We could unset the variable, by passing a NULL, but due to
262                          * pushvar semantics, that could create some undesired behavior. */
263                         if (ishash) {
264                                 snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]);
265                                 pbx_builtin_setvar_helper(chan, varname, "");
266                         } else {
267                                 pbx_builtin_setvar_helper(chan, arg1.var[i], "");
268                         }
269                 }
270         }
271
272         return 0;
273 }
274
275 static int hashkeys_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
276 {
277         struct ast_var_t *newvar;
278         int plen;
279         char prefix[80];
280         snprintf(prefix, sizeof(prefix), HASH_PREFIX, data);
281         plen = strlen(prefix);
282
283         memset(buf, 0, len);
284         AST_LIST_TRAVERSE(&chan->varshead, newvar, entries) {
285                 if (strncasecmp(prefix, ast_var_name(newvar), plen) == 0) {
286                         /* Copy everything after the prefix */
287                         strncat(buf, ast_var_name(newvar) + plen, len);
288                         /* Trim the trailing ~ */
289                         buf[strlen(buf) - 1] = ',';
290                 }
291         }
292         /* Trim the trailing comma */
293         buf[strlen(buf) - 1] = '\0';
294         return 0;
295 }
296
297 static int hash_write(struct ast_channel *chan, const char *cmd, char *var, const char *value)
298 {
299         char varname[256];
300         AST_DECLARE_APP_ARGS(arg,
301                 AST_APP_ARG(hashname);
302                 AST_APP_ARG(hashkey);
303         );
304
305         if (!strchr(var, '|')) {
306                 /* Single argument version */
307                 return array(chan, "HASH", var, value);
308         }
309
310         AST_STANDARD_APP_ARGS(arg, var);
311         snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey);
312         pbx_builtin_setvar_helper(chan, varname, value);
313
314         return 0;
315 }
316
317 static int hash_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
318 {
319         char varname[256];
320         const char *varvalue;
321         AST_DECLARE_APP_ARGS(arg,
322                 AST_APP_ARG(hashname);
323                 AST_APP_ARG(hashkey);
324         );
325
326         AST_STANDARD_APP_ARGS(arg, data);
327         if (arg.argc == 2) {
328                 snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey);
329                 varvalue = pbx_builtin_getvar_helper(chan, varname);
330                 if (varvalue)
331                         ast_copy_string(buf, varvalue, len);
332                 else
333                         *buf = '\0';
334         } else if (arg.argc == 1) {
335                 char colnames[4096];
336                 int i;
337                 AST_DECLARE_APP_ARGS(arg2,
338                         AST_APP_ARG(col)[100];
339                 );
340
341                 /* Get column names, in no particular order */
342                 hashkeys_read(chan, "HASHKEYS", arg.hashname, colnames, sizeof(colnames));
343                 pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", colnames);
344
345                 AST_NONSTANDARD_APP_ARGS(arg2, colnames, ',');
346                 *buf = '\0';
347
348                 /* Now get the corresponding column values, in exactly the same order */
349                 for (i = 0; i < arg2.argc; i++) {
350                         snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg2.col[i]);
351                         varvalue = pbx_builtin_getvar_helper(chan, varname);
352                         strncat(buf, varvalue, len);
353                         strncat(buf, ",", len);
354                 }
355
356                 /* Strip trailing comma */
357                 buf[strlen(buf) - 1] = '\0';
358         }
359
360         return 0;
361 }
362
363 static struct ast_custom_function hash_function = {
364         .name = "HASH",
365         .synopsis = "Implementation of a dialplan associative array",
366         .syntax = "HASH(hashname[|hashkey])",
367         .write = hash_write,
368         .read = hash_read,
369         .desc =
370                 "In two argument mode, gets and sets values to corresponding keys within a named\n"
371                 "associative array.  The single-argument mode will only work when assigned to from\n"
372                 "a function defined by func_odbc.so.\n",
373 };
374
375 static struct ast_custom_function hashkeys_function = {
376         .name = "HASHKEYS",
377         .synopsis = "Retrieve the keys of a HASH()",
378         .syntax = "HASHKEYS(<hashname>)",
379         .read = hashkeys_read,
380         .desc =
381                 "Returns a comma-delimited list of the current keys of an associative array\n"
382                 "defined by the HASH() function.  Note that if you iterate over the keys of\n"
383                 "the result, adding keys during iteration will cause the result of the HASHKEYS\n"
384                 "function to change.\n",
385 };
386
387 static struct ast_custom_function array_function = {
388         .name = "ARRAY",
389         .synopsis = "Allows setting multiple variables at once",
390         .syntax = "ARRAY(var1[|var2[...][|varN]])",
391         .write = array,
392         .desc =
393                 "The comma-separated list passed as a value to which the function is set will\n"
394                 "be interpreted as a set of values to which the comma-separated list of\n"
395                 "variable names in the argument should be set.\n"
396                 "Hence, Set(ARRAY(var1|var2)=1\\,2) will set var1 to 1 and var2 to 2\n"
397                 "Note: remember to either backslash your commas in extensions.conf or quote the\n"
398                 "entire argument, since Set can take multiple arguments itself.\n",
399 };
400
401 static int acf_sprintf(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
402 {
403 #define SPRINTF_FLAG    0
404 #define SPRINTF_WIDTH   1
405 #define SPRINTF_PRECISION       2
406 #define SPRINTF_LENGTH  3
407 #define SPRINTF_CONVERSION      4
408         int i, state = -1, argcount = 0;
409         char *formatstart = NULL, *bufptr = buf;
410         char formatbuf[256] = "";
411         int tmpi;
412         double tmpd;
413         AST_DECLARE_APP_ARGS(arg,
414                                 AST_APP_ARG(format);
415                                 AST_APP_ARG(var)[100];
416         );
417
418         AST_STANDARD_APP_ARGS(arg, data);
419
420         /* Scan the format, converting each argument into the requisite format type. */
421         for (i = 0; arg.format[i]; i++) {
422                 switch (state) {
423                 case SPRINTF_FLAG:
424                         if (strchr("#0- +'I", arg.format[i]))
425                                 break;
426                         state = SPRINTF_WIDTH;
427                 case SPRINTF_WIDTH:
428                         if (arg.format[i] >= '0' && arg.format[i] <= '9')
429                                 break;
430
431                         /* Next character must be a period to go into a precision */
432                         if (arg.format[i] == '.') {
433                                 state = SPRINTF_PRECISION;
434                         } else {
435                                 state = SPRINTF_LENGTH;
436                                 i--;
437                         }
438                         break;
439                 case SPRINTF_PRECISION:
440                         if (arg.format[i] >= '0' && arg.format[i] <= '9')
441                                 break;
442                         state = SPRINTF_LENGTH;
443                 case SPRINTF_LENGTH:
444                         if (strchr("hl", arg.format[i])) {
445                                 if (arg.format[i + 1] == arg.format[i])
446                                         i++;
447                                 state = SPRINTF_CONVERSION;
448                                 break;
449                         } else if (strchr("Lqjzt", arg.format[i]))
450                                 state = SPRINTF_CONVERSION;
451                                 break;
452                         state = SPRINTF_CONVERSION;
453                 case SPRINTF_CONVERSION:
454                         if (strchr("diouxXc", arg.format[i])) {
455                                 /* Integer */
456
457                                 /* Isolate this format alone */
458                                 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
459                                 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
460
461                                 /* Convert the argument into the required type */
462                                 if (sscanf(arg.var[argcount++], "%d", &tmpi) != 1) {
463                                         ast_log(LOG_ERROR, "Argument '%s' is not an integer number for format '%s'\n", arg.var[argcount - 1], formatbuf);
464                                         goto sprintf_fail;
465                                 }
466
467                                 /* Format the argument */
468                                 snprintf(bufptr, buf + len - bufptr, formatbuf, tmpi);
469
470                                 /* Update the position of the next parameter to print */
471                                 bufptr = strchr(buf, '\0');
472                         } else if (strchr("eEfFgGaA", arg.format[i])) {
473                                 /* Double */
474
475                                 /* Isolate this format alone */
476                                 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
477                                 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
478
479                                 /* Convert the argument into the required type */
480                                 if (sscanf(arg.var[argcount++], "%lf", &tmpd) != 1) {
481                                         ast_log(LOG_ERROR, "Argument '%s' is not a floating point number for format '%s'\n", arg.var[argcount - 1], formatbuf);
482                                         goto sprintf_fail;
483                                 }
484
485                                 /* Format the argument */
486                                 snprintf(bufptr, buf + len - bufptr, formatbuf, tmpd);
487
488                                 /* Update the position of the next parameter to print */
489                                 bufptr = strchr(buf, '\0');
490                         } else if (arg.format[i] == 's') {
491                                 /* String */
492
493                                 /* Isolate this format alone */
494                                 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
495                                 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
496
497                                 /* Format the argument */
498                                 snprintf(bufptr, buf + len - bufptr, formatbuf, arg.var[argcount++]);
499
500                                 /* Update the position of the next parameter to print */
501                                 bufptr = strchr(buf, '\0');
502                         } else if (arg.format[i] == '%') {
503                                 /* Literal data to copy */
504                                 *bufptr++ = arg.format[i];
505                         } else {
506                                 /* Not supported */
507
508                                 /* Isolate this format alone */
509                                 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
510                                 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
511
512                                 ast_log(LOG_ERROR, "Format type not supported: '%s' with argument '%s'\n", formatbuf, arg.var[argcount++]);
513                                 goto sprintf_fail;
514                         }
515                         state = -1;
516                         break;
517                 default:
518                         if (arg.format[i] == '%') {
519                                 state = SPRINTF_FLAG;
520                                 formatstart = &arg.format[i];
521                                 break;
522                         } else {
523                                 /* Literal data to copy */
524                                 *bufptr++ = arg.format[i];
525                         }
526                 }
527         }
528         return 0;
529 sprintf_fail:
530         return -1;
531 }
532
533 static struct ast_custom_function sprintf_function = {
534         .name = "SPRINTF",
535         .synopsis = "Format a variable according to a format string",
536         .syntax = "SPRINTF(<format>|<arg1>[|...<argN>])",
537         .read = acf_sprintf,
538         .desc =
539 "Parses the format string specified and returns a string matching that format.\n"
540 "Supports most options supported by sprintf(3).  Returns a shortened string if\n"
541 "a format specifier is not recognized.\n",
542 };
543
544 static int quote(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
545 {
546         char *bufptr = buf, *dataptr = data;
547         *bufptr++ = '"';
548         for (; bufptr < buf + len - 1; dataptr++) {
549                 if (*dataptr == '\\') {
550                         *bufptr++ = '\\';
551                         *bufptr++ = '\\';
552                 } else if (*dataptr == '"') {
553                         *bufptr++ = '\\';
554                         *bufptr++ = '"';
555                 } else if (*dataptr == '\0') {
556                         break;
557                 } else {
558                         *bufptr++ = *dataptr;
559                 }
560         }
561         *bufptr++ = '"';
562         *bufptr = '\0';
563         return 0;
564 }
565
566 static struct ast_custom_function quote_function = {
567         .name = "QUOTE",
568         .synopsis = "Quotes a given string, escaping embedded quotes as necessary",
569         .syntax = "QUOTE(<string>)",
570         .read = quote,
571 };
572
573
574 static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf,
575                size_t len)
576 {
577         int length = 0;
578
579         if (data)
580                 length = strlen(data);
581
582         snprintf(buf, len, "%d", length);
583
584         return 0;
585 }
586
587 static struct ast_custom_function len_function = {
588         .name = "LEN",
589         .synopsis = "Returns the length of the argument given",
590         .syntax = "LEN(<string>)",
591         .read = len,
592 };
593
594 static int acf_strftime(struct ast_channel *chan, const char *cmd, char *parse,
595                         char *buf, size_t len)
596 {
597         AST_DECLARE_APP_ARGS(args,
598                              AST_APP_ARG(epoch);
599                              AST_APP_ARG(timezone);
600                              AST_APP_ARG(format);
601         );
602         time_t epochi;
603         struct tm tm;
604
605         buf[0] = '\0';
606
607         AST_STANDARD_APP_ARGS(args, parse);
608
609         ast_get_time_t(args.epoch, &epochi, time(NULL), NULL);
610         ast_localtime(&epochi, &tm, args.timezone);
611
612         if (!args.format)
613                 args.format = "%c";
614
615         if (!strftime(buf, len, args.format, &tm))
616                 ast_log(LOG_WARNING, "C function strftime() output nothing?!!\n");
617
618         buf[len - 1] = '\0';
619
620         return 0;
621 }
622
623 static struct ast_custom_function strftime_function = {
624         .name = "STRFTIME",
625         .synopsis = "Returns the current date/time in a specified format.",
626         .syntax = "STRFTIME([<epoch>][|[timezone][|format]])",
627         .read = acf_strftime,
628 };
629
630 static int acf_strptime(struct ast_channel *chan, const char *cmd, char *data,
631                         char *buf, size_t len)
632 {
633         AST_DECLARE_APP_ARGS(args,
634                              AST_APP_ARG(timestring);
635                              AST_APP_ARG(timezone);
636                              AST_APP_ARG(format);
637         );
638         struct tm time;
639
640         memset(&time, 0, sizeof(struct tm));
641
642         buf[0] = '\0';
643
644         if (!data) {
645                 ast_log(LOG_ERROR,
646                                 "Asterisk function STRPTIME() requires an argument.\n");
647                 return -1;
648         }
649
650         AST_STANDARD_APP_ARGS(args, data);
651
652         if (ast_strlen_zero(args.format)) {
653                 ast_log(LOG_ERROR,
654                                 "No format supplied to STRPTIME(<timestring>|<timezone>|<format>)");
655                 return -1;
656         }
657
658         if (!strptime(args.timestring, args.format, &time)) {
659                 ast_log(LOG_WARNING, "C function strptime() output nothing?!!\n");
660         } else {
661                 snprintf(buf, len, "%d", (int) ast_mktime(&time, args.timezone));
662         }
663
664         return 0;
665 }
666
667 static struct ast_custom_function strptime_function = {
668         .name = "STRPTIME",
669         .synopsis =
670                 "Returns the epoch of the arbitrary date/time string structured as described in the format.",
671         .syntax = "STRPTIME(<datetime>|<timezone>|<format>)",
672         .desc =
673                 "This is useful for converting a date into an EPOCH time, possibly to pass to\n"
674                 "an application like SayUnixTime or to calculate the difference between two\n"
675                 "date strings.\n"
676                 "\n"
677                 "Example:\n"
678                 "  ${STRPTIME(2006-03-01 07:30:35|America/Chicago|%Y-%m-%d %H:%M:%S)} returns 1141219835\n",
679         .read = acf_strptime,
680 };
681
682 static int function_eval(struct ast_channel *chan, const char *cmd, char *data,
683                          char *buf, size_t len)
684 {
685         memset(buf, 0, len);
686
687         if (ast_strlen_zero(data)) {
688                 ast_log(LOG_WARNING, "EVAL requires an argument: EVAL(<string>)\n");
689                 return -1;
690         }
691
692         pbx_substitute_variables_helper(chan, data, buf, len - 1);
693
694         return 0;
695 }
696
697 static struct ast_custom_function eval_function = {
698         .name = "EVAL",
699         .synopsis = "Evaluate stored variables.",
700         .syntax = "EVAL(<variable>)",
701         .desc = "Using EVAL basically causes a string to be evaluated twice.\n"
702                 "When a variable or expression is in the dialplan, it will be\n"
703                 "evaluated at runtime. However, if the result of the evaluation\n"
704                 "is in fact a variable or expression, using EVAL will have it\n"
705                 "evaluated a second time. For example, if the variable ${MYVAR}\n"
706                 "contains \"${OTHERVAR}\", then the result of putting ${EVAL(${MYVAR})}\n"
707                 "in the dialplan will be the contents of the variable, OTHERVAR.\n"
708                 "Normally, by just putting ${MYVAR} in the dialplan, you would be\n"
709                 "left with \"${OTHERVAR}\".\n",
710         .read = function_eval,
711 };
712
713 static int keypadhash(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
714 {
715         char *bufptr, *dataptr;
716
717         for (bufptr = buf, dataptr = data; bufptr < buf + len - 1; dataptr++) {
718                 if (*dataptr == '1') {
719                         *bufptr++ = '1';
720                 } else if (strchr("AaBbCc2", *dataptr)) {
721                         *bufptr++ = '2';
722                 } else if (strchr("DdEeFf3", *dataptr)) {
723                         *bufptr++ = '3';
724                 } else if (strchr("GgHhIi4", *dataptr)) {
725                         *bufptr++ = '4';
726                 } else if (strchr("JjKkLl5", *dataptr)) {
727                         *bufptr++ = '5';
728                 } else if (strchr("MmNnOo6", *dataptr)) {
729                         *bufptr++ = '6';
730                 } else if (strchr("PpQqRrSs7", *dataptr)) {
731                         *bufptr++ = '7';
732                 } else if (strchr("TtUuVv8", *dataptr)) {
733                         *bufptr++ = '8';
734                 } else if (strchr("WwXxYyZz9", *dataptr)) {
735                         *bufptr++ = '9';
736                 } else if (*dataptr == '0') {
737                         *bufptr++ = '0';
738                 } else if (*dataptr == '\0') {
739                         *bufptr++ = '\0';
740                         break;
741                 }
742         }
743         buf[len - 1] = '\0';
744
745         return 0;
746 }
747
748 static struct ast_custom_function keypadhash_function = {
749         .name = "KEYPADHASH",
750         .synopsis = "Hash the letters in the string into the equivalent keypad numbers.",
751         .syntax = "KEYPADHASH(<string>)",
752         .read = keypadhash,
753         .desc = "Example:  ${KEYPADHASH(Les)} returns \"537\"\n",
754 };
755
756 static int unload_module(void)
757 {
758         int res = 0;
759
760         res |= ast_custom_function_unregister(&fieldqty_function);
761         res |= ast_custom_function_unregister(&filter_function);
762         res |= ast_custom_function_unregister(&regex_function);
763         res |= ast_custom_function_unregister(&array_function);
764         res |= ast_custom_function_unregister(&quote_function);
765         res |= ast_custom_function_unregister(&len_function);
766         res |= ast_custom_function_unregister(&strftime_function);
767         res |= ast_custom_function_unregister(&strptime_function);
768         res |= ast_custom_function_unregister(&eval_function);
769         res |= ast_custom_function_unregister(&keypadhash_function);
770         res |= ast_custom_function_unregister(&sprintf_function);
771         res |= ast_custom_function_unregister(&hashkeys_function);
772         res |= ast_custom_function_unregister(&hash_function);
773         res |= ast_unregister_application(app_clearhash);
774
775         return res;
776 }
777
778 static int load_module(void)
779 {
780         int res = 0;
781
782         res |= ast_custom_function_register(&fieldqty_function);
783         res |= ast_custom_function_register(&filter_function);
784         res |= ast_custom_function_register(&regex_function);
785         res |= ast_custom_function_register(&array_function);
786         res |= ast_custom_function_register(&quote_function);
787         res |= ast_custom_function_register(&len_function);
788         res |= ast_custom_function_register(&strftime_function);
789         res |= ast_custom_function_register(&strptime_function);
790         res |= ast_custom_function_register(&eval_function);
791         res |= ast_custom_function_register(&keypadhash_function);
792         res |= ast_custom_function_register(&sprintf_function);
793         res |= ast_custom_function_register(&hashkeys_function);
794         res |= ast_custom_function_register(&hash_function);
795         res |= ast_register_application(app_clearhash, exec_clearhash, syn_clearhash, desc_clearhash);
796
797         return res;
798 }
799
800 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "String handling dialplan functions");