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 (sscanf(arg
.var
[argcount
++], "%d", &tmpi
) != 1) {
304 ast_log(LOG_ERROR
, "Argument '%s' is not an integer number for format '%s'\n", arg
.var
[argcount
- 1], formatbuf
);
308 /* Format the argument */
309 snprintf(bufptr
, buf
+ len
- bufptr
, formatbuf
, tmpi
);
311 /* Update the position of the next parameter to print */
312 bufptr
= strchr(buf
, '\0');
313 } else if (strchr("eEfFgGaA", arg
.format
[i
])) {
316 /* Isolate this format alone */
317 ast_copy_string(formatbuf
, formatstart
, sizeof(formatbuf
));
318 formatbuf
[&arg
.format
[i
] - formatstart
+ 1] = '\0';
320 /* Convert the argument into the required type */
321 if (sscanf(arg
.var
[argcount
++], "%lf", &tmpd
) != 1) {
322 ast_log(LOG_ERROR
, "Argument '%s' is not a floating point number for format '%s'\n", arg
.var
[argcount
- 1], formatbuf
);
326 /* Format the argument */
327 snprintf(bufptr
, buf
+ len
- bufptr
, formatbuf
, tmpd
);
329 /* Update the position of the next parameter to print */
330 bufptr
= strchr(buf
, '\0');
331 } else if (arg
.format
[i
] == 's') {
334 /* Isolate this format alone */
335 ast_copy_string(formatbuf
, formatstart
, sizeof(formatbuf
));
336 formatbuf
[&arg
.format
[i
] - formatstart
+ 1] = '\0';
338 /* Format the argument */
339 snprintf(bufptr
, buf
+ len
- bufptr
, formatbuf
, arg
.var
[argcount
++]);
341 /* Update the position of the next parameter to print */
342 bufptr
= strchr(buf
, '\0');
343 } else if (arg
.format
[i
] == '%') {
344 /* Literal data to copy */
345 *bufptr
++ = arg
.format
[i
];
349 /* Isolate this format alone */
350 ast_copy_string(formatbuf
, formatstart
, sizeof(formatbuf
));
351 formatbuf
[&arg
.format
[i
] - formatstart
+ 1] = '\0';
353 ast_log(LOG_ERROR
, "Format type not supported: '%s' with argument '%s'\n", formatbuf
, arg
.var
[argcount
++]);
359 if (arg
.format
[i
] == '%') {
360 state
= SPRINTF_FLAG
;
361 formatstart
= &arg
.format
[i
];
364 /* Literal data to copy */
365 *bufptr
++ = arg
.format
[i
];
374 static struct ast_custom_function sprintf_function
= {
376 .synopsis
= "Format a variable according to a format string",
377 .syntax
= "SPRINTF(<format>|<arg1>[|...<argN>])",
380 "Parses the format string specified and returns a string matching that format.\n"
381 "Supports most options supported by sprintf(3). Returns a shortened string if\n"
382 "a format specifier is not recognized.\n",
385 static int quote(struct ast_channel
*chan
, char *cmd
, char *data
, char *buf
, size_t len
)
387 char *bufptr
= buf
, *dataptr
= data
;
389 for (; bufptr
< buf
+ len
- 1; dataptr
++) {
390 if (*dataptr
== '\\') {
393 } else if (*dataptr
== '"') {
396 } else if (*dataptr
== '\0') {
399 *bufptr
++ = *dataptr
;
407 static struct ast_custom_function quote_function
= {
409 .synopsis
= "Quotes a given string, escaping embedded quotes as necessary",
410 .syntax
= "QUOTE(<string>)",
415 static int len(struct ast_channel
*chan
, char *cmd
, char *data
, char *buf
,
421 length
= strlen(data
);
423 snprintf(buf
, len
, "%d", length
);
428 static struct ast_custom_function len_function
= {
430 .synopsis
= "Returns the length of the argument given",
431 .syntax
= "LEN(<string>)",
435 static int acf_strftime(struct ast_channel
*chan
, char *cmd
, char *parse
,
436 char *buf
, size_t len
)
438 AST_DECLARE_APP_ARGS(args
,
440 AST_APP_ARG(timezone
);
448 AST_STANDARD_APP_ARGS(args
, parse
);
450 ast_get_time_t(args
.epoch
, &epochi
, time(NULL
), NULL
);
451 ast_localtime(&epochi
, &tm
, args
.timezone
);
456 if (!strftime(buf
, len
, args
.format
, &tm
))
457 ast_log(LOG_WARNING
, "C function strftime() output nothing?!!\n");
464 static struct ast_custom_function strftime_function
= {
466 .synopsis
= "Returns the current date/time in a specified format.",
467 .syntax
= "STRFTIME([<epoch>][|[timezone][|format]])",
468 .read
= acf_strftime
,
471 static int acf_strptime(struct ast_channel
*chan
, char *cmd
, char *data
,
472 char *buf
, size_t len
)
474 AST_DECLARE_APP_ARGS(args
,
475 AST_APP_ARG(timestring
);
476 AST_APP_ARG(timezone
);
481 memset(&time
, 0, sizeof(struct tm
));
487 "Asterisk function STRPTIME() requires an argument.\n");
491 AST_STANDARD_APP_ARGS(args
, data
);
493 if (ast_strlen_zero(args
.format
)) {
495 "No format supplied to STRPTIME(<timestring>|<timezone>|<format>)");
499 if (!strptime(args
.timestring
, args
.format
, &time
)) {
500 ast_log(LOG_WARNING
, "C function strptime() output nothing?!!\n");
502 /* Since strptime(3) does not check DST, force ast_mktime() to calculate it. */
504 snprintf(buf
, len
, "%d", (int) ast_mktime(&time
, args
.timezone
));
510 static struct ast_custom_function strptime_function
= {
513 "Returns the epoch of the arbitrary date/time string structured as described in the format.",
514 .syntax
= "STRPTIME(<datetime>|<timezone>|<format>)",
516 "This is useful for converting a date into an EPOCH time, possibly to pass to\n"
517 "an application like SayUnixTime or to calculate the difference between two\n"
521 " ${STRPTIME(2006-03-01 07:30:35|America/Chicago|%Y-%m-%d %H:%M:%S)} returns 1141219835\n",
522 .read
= acf_strptime
,
525 static int function_eval(struct ast_channel
*chan
, char *cmd
, char *data
,
526 char *buf
, size_t len
)
530 if (ast_strlen_zero(data
)) {
531 ast_log(LOG_WARNING
, "EVAL requires an argument: EVAL(<string>)\n");
536 ast_autoservice_start(chan
);
537 pbx_substitute_variables_helper(chan
, data
, buf
, len
- 1);
539 ast_autoservice_stop(chan
);
544 static struct ast_custom_function eval_function
= {
546 .synopsis
= "Evaluate stored variables.",
547 .syntax
= "EVAL(<variable>)",
548 .desc
= "Using EVAL basically causes a string to be evaluated twice.\n"
549 "When a variable or expression is in the dialplan, it will be\n"
550 "evaluated at runtime. However, if the result of the evaluation\n"
551 "is in fact a variable or expression, using EVAL will have it\n"
552 "evaluated a second time. For example, if the variable ${MYVAR}\n"
553 "contains \"${OTHERVAR}\", then the result of putting ${EVAL(${MYVAR})}\n"
554 "in the dialplan will be the contents of the variable, OTHERVAR.\n"
555 "Normally, by just putting ${MYVAR} in the dialplan, you would be\n"
556 "left with \"${OTHERVAR}\".\n",
557 .read
= function_eval
,
560 static int keypadhash(struct ast_channel
*chan
, char *cmd
, char *data
, char *buf
, size_t len
)
562 char *bufptr
, *dataptr
;
564 for (bufptr
= buf
, dataptr
= data
; bufptr
< buf
+ len
- 1; dataptr
++) {
565 if (*dataptr
== '1') {
567 } else if (strchr("AaBbCc2", *dataptr
)) {
569 } else if (strchr("DdEeFf3", *dataptr
)) {
571 } else if (strchr("GgHhIi4", *dataptr
)) {
573 } else if (strchr("JjKkLl5", *dataptr
)) {
575 } else if (strchr("MmNnOo6", *dataptr
)) {
577 } else if (strchr("PpQqRrSs7", *dataptr
)) {
579 } else if (strchr("TtUuVv8", *dataptr
)) {
581 } else if (strchr("WwXxYyZz9", *dataptr
)) {
583 } else if (*dataptr
== '0') {
585 } else if (*dataptr
== '\0') {
595 static struct ast_custom_function keypadhash_function
= {
596 .name
= "KEYPADHASH",
597 .synopsis
= "Hash the letters in the string into the equivalent keypad numbers.",
598 .syntax
= "KEYPADHASH(<string>)",
600 .desc
= "Example: ${KEYPADHASH(Les)} returns \"537\"\n",
603 static int unload_module(void)
607 res
|= ast_custom_function_unregister(&fieldqty_function
);
608 res
|= ast_custom_function_unregister(&filter_function
);
609 res
|= ast_custom_function_unregister(®ex_function
);
610 res
|= ast_custom_function_unregister(&array_function
);
611 res
|= ast_custom_function_unregister("e_function
);
612 res
|= ast_custom_function_unregister(&len_function
);
613 res
|= ast_custom_function_unregister(&strftime_function
);
614 res
|= ast_custom_function_unregister(&strptime_function
);
615 res
|= ast_custom_function_unregister(&eval_function
);
616 res
|= ast_custom_function_unregister(&keypadhash_function
);
617 res
|= ast_custom_function_unregister(&sprintf_function
);
622 static int load_module(void)
626 res
|= ast_custom_function_register(&fieldqty_function
);
627 res
|= ast_custom_function_register(&filter_function
);
628 res
|= ast_custom_function_register(®ex_function
);
629 res
|= ast_custom_function_register(&array_function
);
630 res
|= ast_custom_function_register("e_function
);
631 res
|= ast_custom_function_register(&len_function
);
632 res
|= ast_custom_function_register(&strftime_function
);
633 res
|= ast_custom_function_register(&strptime_function
);
634 res
|= ast_custom_function_register(&eval_function
);
635 res
|= ast_custom_function_register(&keypadhash_function
);
636 res
|= ast_custom_function_register(&sprintf_function
);
641 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY
, "String handling dialplan functions");