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
29 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
34 #include <sys/types.h>
37 #include "asterisk/module.h"
38 #include "asterisk/options.h"
39 #include "asterisk/channel.h"
40 #include "asterisk/pbx.h"
41 #include "asterisk/logger.h"
42 #include "asterisk/utils.h"
43 #include "asterisk/app.h"
44 #include "asterisk/localtime.h"
46 static int function_fieldqty(struct ast_channel
*chan
, char *cmd
,
47 char *parse
, char *buf
, size_t len
)
49 char *varsubst
, varval
[8192] = "", *varval2
= varval
;
51 AST_DECLARE_APP_ARGS(args
,
57 ast_autoservice_start(chan
);
59 AST_STANDARD_APP_ARGS(args
, parse
);
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
, args
.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
, char *cmd
, char *parse
, char *buf
,
92 AST_DECLARE_APP_ARGS(args
,
98 AST_STANDARD_APP_ARGS(args
, parse
);
101 ast_log(LOG_ERROR
, "Usage: FILTER(<allowed-chars>|<string>)\n");
105 for (; *(args
.string
) && (buf
+ len
- 1 > outbuf
); (args
.string
)++) {
106 if (strchr(args
.allowed
, *(args
.string
)))
107 *outbuf
++ = *(args
.string
);
114 static struct ast_custom_function filter_function
= {
116 .synopsis
= "Filter the string to include only the allowed characters",
117 .syntax
= "FILTER(<allowed-chars>|<string>)",
121 static int regex(struct ast_channel
*chan
, char *cmd
, char *parse
, char *buf
,
124 AST_DECLARE_APP_ARGS(args
,
134 AST_NONSTANDARD_APP_ARGS(args
, parse
, '"');
136 if (args
.argc
!= 3) {
137 ast_log(LOG_ERROR
, "Unexpected arguments: should have been in the form '\"<regex>\" <string>'\n");
140 if ((*args
.str
== ' ') || (*args
.str
== '\t'))
144 ast_log(LOG_DEBUG
, "FUNCTION REGEX (%s)(%s)\n", args
.reg
, args
.str
);
146 if ((errcode
= regcomp(®exbuf
, args
.reg
, REG_EXTENDED
| REG_NOSUB
))) {
147 regerror(errcode
, ®exbuf
, buf
, len
);
148 ast_log(LOG_WARNING
, "Malformed input %s(%s): %s\n", cmd
, parse
, buf
);
152 strcpy(buf
, regexec(®exbuf
, args
.str
, 0, NULL
, 0) ? "0" : "1");
159 static struct ast_custom_function regex_function
= {
161 .synopsis
= "Regular Expression",
163 "Returns 1 if data matches regular expression, or 0 otherwise.\n"
164 "Please note that the space following the double quotes separating the regex from the data\n"
165 "is optional and if present, is skipped. If a space is desired at the beginning of the data,\n"
166 "then put two spaces there; the second will not be skipped.\n",
167 .syntax
= "REGEX(\"<regular expression>\" <data>)",
171 static int array(struct ast_channel
*chan
, char *cmd
, char *var
,
174 AST_DECLARE_APP_ARGS(arg1
,
175 AST_APP_ARG(var
)[100];
177 AST_DECLARE_APP_ARGS(arg2
,
178 AST_APP_ARG(val
)[100];
183 value2
= ast_strdupa(value
);
188 ast_autoservice_start(chan
);
190 /* The functions this will generally be used with are SORT and ODBC_*, which
191 * both return comma-delimited lists. However, if somebody uses literal lists,
192 * their commas will be translated to vertical bars by the load, and I don't
193 * want them to be surprised by the result. Hence, we prefer commas as the
194 * delimiter, but we'll fall back to vertical bars if commas aren't found.
197 ast_log(LOG_DEBUG
, "array (%s=%s)\n", var
, value2
);
198 if (strchr(var
, ','))
199 AST_NONSTANDARD_APP_ARGS(arg1
, var
, ',');
201 AST_STANDARD_APP_ARGS(arg1
, var
);
203 if (strchr(value2
, ','))
204 AST_NONSTANDARD_APP_ARGS(arg2
, value2
, ',');
206 AST_STANDARD_APP_ARGS(arg2
, value2
);
208 for (i
= 0; i
< arg1
.argc
; i
++) {
210 ast_log(LOG_DEBUG
, "array set value (%s=%s)\n", arg1
.var
[i
],
213 pbx_builtin_setvar_helper(chan
, arg1
.var
[i
], arg2
.val
[i
]);
215 /* We could unset the variable, by passing a NULL, but due to
216 * pushvar semantics, that could create some undesired behavior. */
217 pbx_builtin_setvar_helper(chan
, arg1
.var
[i
], "");
222 ast_autoservice_stop(chan
);
227 static struct ast_custom_function array_function
= {
229 .synopsis
= "Allows setting multiple variables at once",
230 .syntax
= "ARRAY(var1[|var2[...][|varN]])",
233 "The comma-separated list passed as a value to which the function is set will\n"
234 "be interpreted as a set of values to which the comma-separated list of\n"
235 "variable names in the argument should be set.\n"
236 "Hence, Set(ARRAY(var1|var2)=1\\,2) will set var1 to 1 and var2 to 2\n"
237 "Note: remember to either backslash your commas in extensions.conf or quote the\n"
238 "entire argument, since Set can take multiple arguments itself.\n",
241 static int acf_sprintf(struct ast_channel
*chan
, char *cmd
, char *data
, char *buf
, size_t len
)
243 #define SPRINTF_FLAG 0
244 #define SPRINTF_WIDTH 1
245 #define SPRINTF_PRECISION 2
246 #define SPRINTF_LENGTH 3
247 #define SPRINTF_CONVERSION 4
248 int i
, state
= -1, argcount
= 0;
249 char *formatstart
= NULL
, *bufptr
= buf
;
250 char formatbuf
[256] = "";
253 AST_DECLARE_APP_ARGS(arg
,
255 AST_APP_ARG(var
)[100];
258 AST_STANDARD_APP_ARGS(arg
, data
);
260 /* Scan the format, converting each argument into the requisite format type. */
261 for (i
= 0; arg
.format
[i
]; i
++) {
264 if (strchr("#0- +'I", arg
.format
[i
]))
266 state
= SPRINTF_WIDTH
;
268 if (arg
.format
[i
] >= '0' && arg
.format
[i
] <= '9')
271 /* Next character must be a period to go into a precision */
272 if (arg
.format
[i
] == '.') {
273 state
= SPRINTF_PRECISION
;
275 state
= SPRINTF_LENGTH
;
279 case SPRINTF_PRECISION
:
280 if (arg
.format
[i
] >= '0' && arg
.format
[i
] <= '9')
282 state
= SPRINTF_LENGTH
;
284 if (strchr("hl", arg
.format
[i
])) {
285 if (arg
.format
[i
+ 1] == arg
.format
[i
])
287 state
= SPRINTF_CONVERSION
;
289 } else if (strchr("Lqjzt", arg
.format
[i
])) {
290 state
= SPRINTF_CONVERSION
;
293 state
= SPRINTF_CONVERSION
;
294 case SPRINTF_CONVERSION
:
295 if (strchr("diouxXc", arg
.format
[i
])) {
298 /* Isolate this format alone */
299 ast_copy_string(formatbuf
, formatstart
, sizeof(formatbuf
));
300 formatbuf
[&arg
.format
[i
] - formatstart
+ 1] = '\0';
302 /* Convert the argument into the required type */
303 if (arg
.var
[argcount
]) {
304 if (sscanf(arg
.var
[argcount
++], "%d", &tmpi
) != 1) {
305 ast_log(LOG_ERROR
, "Argument '%s' is not an integer number for format '%s'\n", arg
.var
[argcount
- 1], formatbuf
);
309 ast_log(LOG_ERROR
, "SPRINTF() has more format specifiers than arguments!\n");
313 /* Format the argument */
314 snprintf(bufptr
, buf
+ len
- bufptr
, formatbuf
, tmpi
);
316 /* Update the position of the next parameter to print */
317 bufptr
= strchr(buf
, '\0');
318 } else if (strchr("eEfFgGaA", arg
.format
[i
])) {
321 /* Isolate this format alone */
322 ast_copy_string(formatbuf
, formatstart
, sizeof(formatbuf
));
323 formatbuf
[&arg
.format
[i
] - formatstart
+ 1] = '\0';
325 /* Convert the argument into the required type */
326 if (arg
.var
[argcount
]) {
327 if (sscanf(arg
.var
[argcount
++], "%lf", &tmpd
) != 1) {
328 ast_log(LOG_ERROR
, "Argument '%s' is not a floating point number for format '%s'\n", arg
.var
[argcount
- 1], formatbuf
);
332 ast_log(LOG_ERROR
, "SPRINTF() has more format specifiers than arguments!\n");
336 /* Format the argument */
337 snprintf(bufptr
, buf
+ len
- bufptr
, formatbuf
, tmpd
);
339 /* Update the position of the next parameter to print */
340 bufptr
= strchr(buf
, '\0');
341 } else if (arg
.format
[i
] == 's') {
344 /* Isolate this format alone */
345 ast_copy_string(formatbuf
, formatstart
, sizeof(formatbuf
));
346 formatbuf
[&arg
.format
[i
] - formatstart
+ 1] = '\0';
348 /* Format the argument */
349 snprintf(bufptr
, buf
+ len
- bufptr
, formatbuf
, arg
.var
[argcount
++]);
351 /* Update the position of the next parameter to print */
352 bufptr
= strchr(buf
, '\0');
353 } else if (arg
.format
[i
] == '%') {
354 /* Literal data to copy */
355 *bufptr
++ = arg
.format
[i
];
359 /* Isolate this format alone */
360 ast_copy_string(formatbuf
, formatstart
, sizeof(formatbuf
));
361 formatbuf
[&arg
.format
[i
] - formatstart
+ 1] = '\0';
363 ast_log(LOG_ERROR
, "Format type not supported: '%s' with argument '%s'\n", formatbuf
, arg
.var
[argcount
++]);
369 if (arg
.format
[i
] == '%') {
370 state
= SPRINTF_FLAG
;
371 formatstart
= &arg
.format
[i
];
374 /* Literal data to copy */
375 *bufptr
++ = arg
.format
[i
];
385 static struct ast_custom_function sprintf_function
= {
387 .synopsis
= "Format a variable according to a format string",
388 .syntax
= "SPRINTF(<format>|<arg1>[|...<argN>])",
391 "Parses the format string specified and returns a string matching that format.\n"
392 "Supports most options supported by sprintf(3). Returns a shortened string if\n"
393 "a format specifier is not recognized.\n",
396 static int quote(struct ast_channel
*chan
, char *cmd
, char *data
, char *buf
, size_t len
)
398 char *bufptr
= buf
, *dataptr
= data
;
400 for (; bufptr
< buf
+ len
- 1; dataptr
++) {
401 if (*dataptr
== '\\') {
404 } else if (*dataptr
== '"') {
407 } else if (*dataptr
== '\0') {
410 *bufptr
++ = *dataptr
;
418 static struct ast_custom_function quote_function
= {
420 .synopsis
= "Quotes a given string, escaping embedded quotes as necessary",
421 .syntax
= "QUOTE(<string>)",
426 static int len(struct ast_channel
*chan
, char *cmd
, char *data
, char *buf
,
432 length
= strlen(data
);
434 snprintf(buf
, len
, "%d", length
);
439 static struct ast_custom_function len_function
= {
441 .synopsis
= "Returns the length of the argument given",
442 .syntax
= "LEN(<string>)",
446 static int acf_strftime(struct ast_channel
*chan
, char *cmd
, char *parse
,
447 char *buf
, size_t len
)
449 AST_DECLARE_APP_ARGS(args
,
451 AST_APP_ARG(timezone
);
459 AST_STANDARD_APP_ARGS(args
, parse
);
461 ast_get_time_t(args
.epoch
, &epochi
, time(NULL
), NULL
);
462 ast_localtime(&epochi
, &tm
, args
.timezone
);
467 if (!strftime(buf
, len
, args
.format
, &tm
))
468 ast_log(LOG_WARNING
, "C function strftime() output nothing?!!\n");
475 static struct ast_custom_function strftime_function
= {
477 .synopsis
= "Returns the current date/time in a specified format.",
478 .syntax
= "STRFTIME([<epoch>][|[timezone][|format]])",
479 .read
= acf_strftime
,
482 static int acf_strptime(struct ast_channel
*chan
, char *cmd
, char *data
,
483 char *buf
, size_t len
)
485 AST_DECLARE_APP_ARGS(args
,
486 AST_APP_ARG(timestring
);
487 AST_APP_ARG(timezone
);
492 memset(&time
, 0, sizeof(struct tm
));
498 "Asterisk function STRPTIME() requires an argument.\n");
502 AST_STANDARD_APP_ARGS(args
, data
);
504 if (ast_strlen_zero(args
.format
)) {
506 "No format supplied to STRPTIME(<timestring>|<timezone>|<format>)");
510 if (!strptime(args
.timestring
, args
.format
, &time
)) {
511 ast_log(LOG_WARNING
, "C function strptime() output nothing?!!\n");
513 /* Since strptime(3) does not check DST, force ast_mktime() to calculate it. */
515 snprintf(buf
, len
, "%d", (int) ast_mktime(&time
, args
.timezone
));
521 static struct ast_custom_function strptime_function
= {
524 "Returns the epoch of the arbitrary date/time string structured as described in the format.",
525 .syntax
= "STRPTIME(<datetime>|<timezone>|<format>)",
527 "This is useful for converting a date into an EPOCH time, possibly to pass to\n"
528 "an application like SayUnixTime or to calculate the difference between two\n"
532 " ${STRPTIME(2006-03-01 07:30:35|America/Chicago|%Y-%m-%d %H:%M:%S)} returns 1141219835\n",
533 .read
= acf_strptime
,
536 static int function_eval(struct ast_channel
*chan
, char *cmd
, char *data
,
537 char *buf
, size_t len
)
541 if (ast_strlen_zero(data
)) {
542 ast_log(LOG_WARNING
, "EVAL requires an argument: EVAL(<string>)\n");
547 ast_autoservice_start(chan
);
548 pbx_substitute_variables_helper(chan
, data
, buf
, len
- 1);
550 ast_autoservice_stop(chan
);
555 static struct ast_custom_function eval_function
= {
557 .synopsis
= "Evaluate stored variables.",
558 .syntax
= "EVAL(<variable>)",
559 .desc
= "Using EVAL basically causes a string to be evaluated twice.\n"
560 "When a variable or expression is in the dialplan, it will be\n"
561 "evaluated at runtime. However, if the result of the evaluation\n"
562 "is in fact a variable or expression, using EVAL will have it\n"
563 "evaluated a second time. For example, if the variable ${MYVAR}\n"
564 "contains \"${OTHERVAR}\", then the result of putting ${EVAL(${MYVAR})}\n"
565 "in the dialplan will be the contents of the variable, OTHERVAR.\n"
566 "Normally, by just putting ${MYVAR} in the dialplan, you would be\n"
567 "left with \"${OTHERVAR}\".\n",
568 .read
= function_eval
,
571 static int keypadhash(struct ast_channel
*chan
, char *cmd
, char *data
, char *buf
, size_t len
)
573 char *bufptr
, *dataptr
;
575 for (bufptr
= buf
, dataptr
= data
; bufptr
< buf
+ len
- 1; dataptr
++) {
576 if (*dataptr
== '1') {
578 } else if (strchr("AaBbCc2", *dataptr
)) {
580 } else if (strchr("DdEeFf3", *dataptr
)) {
582 } else if (strchr("GgHhIi4", *dataptr
)) {
584 } else if (strchr("JjKkLl5", *dataptr
)) {
586 } else if (strchr("MmNnOo6", *dataptr
)) {
588 } else if (strchr("PpQqRrSs7", *dataptr
)) {
590 } else if (strchr("TtUuVv8", *dataptr
)) {
592 } else if (strchr("WwXxYyZz9", *dataptr
)) {
594 } else if (*dataptr
== '0') {
596 } else if (*dataptr
== '\0') {
606 static struct ast_custom_function keypadhash_function
= {
607 .name
= "KEYPADHASH",
608 .synopsis
= "Hash the letters in the string into the equivalent keypad numbers.",
609 .syntax
= "KEYPADHASH(<string>)",
611 .desc
= "Example: ${KEYPADHASH(Les)} returns \"537\"\n",
614 static int unload_module(void)
618 res
|= ast_custom_function_unregister(&fieldqty_function
);
619 res
|= ast_custom_function_unregister(&filter_function
);
620 res
|= ast_custom_function_unregister(®ex_function
);
621 res
|= ast_custom_function_unregister(&array_function
);
622 res
|= ast_custom_function_unregister("e_function
);
623 res
|= ast_custom_function_unregister(&len_function
);
624 res
|= ast_custom_function_unregister(&strftime_function
);
625 res
|= ast_custom_function_unregister(&strptime_function
);
626 res
|= ast_custom_function_unregister(&eval_function
);
627 res
|= ast_custom_function_unregister(&keypadhash_function
);
628 res
|= ast_custom_function_unregister(&sprintf_function
);
633 static int load_module(void)
637 res
|= ast_custom_function_register(&fieldqty_function
);
638 res
|= ast_custom_function_register(&filter_function
);
639 res
|= ast_custom_function_register(®ex_function
);
640 res
|= ast_custom_function_register(&array_function
);
641 res
|= ast_custom_function_register("e_function
);
642 res
|= ast_custom_function_register(&len_function
);
643 res
|= ast_custom_function_register(&strftime_function
);
644 res
|= ast_custom_function_register(&strptime_function
);
645 res
|= ast_custom_function_register(&eval_function
);
646 res
|= ast_custom_function_register(&keypadhash_function
);
647 res
|= ast_custom_function_register(&sprintf_function
);
652 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY
, "String handling dialplan functions");