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/channel.h"
39 #include "asterisk/pbx.h"
40 #include "asterisk/logger.h"
41 #include "asterisk/utils.h"
42 #include "asterisk/app.h"
43 #include "asterisk/localtime.h"
45 static int function_fieldqty(struct ast_channel
*chan
, char *cmd
,
46 char *parse
, char *buf
, size_t len
)
50 AST_DECLARE_APP_ARGS(args
,
55 AST_STANDARD_APP_ARGS(args
, parse
);
57 pbx_retrieve_variable(chan
, args
.varname
, &varval
, buf
, len
, NULL
);
58 while (strsep(&varval
, args
.delim
))
63 snprintf(buf
, len
, "%d", fieldcount
);
68 static struct ast_custom_function fieldqty_function
= {
70 .synopsis
= "Count the fields, with an arbitrary delimiter",
71 .syntax
= "FIELDQTY(<varname>|<delim>)",
72 .read
= function_fieldqty
,
75 static int filter(struct ast_channel
*chan
, char *cmd
, char *parse
, char *buf
,
78 AST_DECLARE_APP_ARGS(args
,
84 AST_STANDARD_APP_ARGS(args
, parse
);
87 ast_log(LOG_ERROR
, "Usage: FILTER(<allowed-chars>|<string>)\n");
91 for (; *(args
.string
) && (buf
+ len
- 1 > outbuf
); (args
.string
)++) {
92 if (strchr(args
.allowed
, *(args
.string
)))
93 *outbuf
++ = *(args
.string
);
100 static struct ast_custom_function filter_function
= {
102 .synopsis
= "Filter the string to include only the allowed characters",
103 .syntax
= "FILTER(<allowed-chars>|<string>)",
107 static int regex(struct ast_channel
*chan
, char *cmd
, char *parse
, char *buf
,
110 AST_DECLARE_APP_ARGS(args
,
120 AST_NONSTANDARD_APP_ARGS(args
, parse
, '"');
122 if (args
.argc
!= 3) {
123 ast_log(LOG_ERROR
, "Unexpected arguments: should have been in the form '\"<regex>\" <string>'\n");
127 ast_log(LOG_DEBUG
, "FUNCTION REGEX (%s)(%s)\n", args
.reg
, args
.str
);
129 if ((errcode
= regcomp(®exbuf
, args
.reg
, REG_EXTENDED
| REG_NOSUB
))) {
130 regerror(errcode
, ®exbuf
, buf
, len
);
131 ast_log(LOG_WARNING
, "Malformed input %s(%s): %s\n", cmd
, parse
, buf
);
134 if (!regexec(®exbuf
, args
.str
, 0, NULL
, 0))
142 static struct ast_custom_function regex_function
= {
145 "Regular Expression: Returns 1 if data matches regular expression.",
146 .syntax
= "REGEX(\"<regular expression>\" <data>)",
150 static int array(struct ast_channel
*chan
, char *cmd
, char *var
,
153 AST_DECLARE_APP_ARGS(arg1
,
154 AST_APP_ARG(var
)[100];
156 AST_DECLARE_APP_ARGS(arg2
,
157 AST_APP_ARG(val
)[100];
162 value2
= ast_strdupa(value
);
166 /* The functions this will generally be used with are SORT and ODBC_*, which
167 * both return comma-delimited lists. However, if somebody uses literal lists,
168 * their commas will be translated to vertical bars by the load, and I don't
169 * want them to be surprised by the result. Hence, we prefer commas as the
170 * delimiter, but we'll fall back to vertical bars if commas aren't found.
172 ast_log(LOG_DEBUG
, "array (%s=%s)\n", var
, value2
);
173 if (strchr(var
, ','))
174 AST_NONSTANDARD_APP_ARGS(arg1
, var
, ',');
176 AST_STANDARD_APP_ARGS(arg1
, var
);
178 if (strchr(value2
, ','))
179 AST_NONSTANDARD_APP_ARGS(arg2
, value2
, ',');
181 AST_STANDARD_APP_ARGS(arg2
, value2
);
183 for (i
= 0; i
< arg1
.argc
; i
++) {
184 ast_log(LOG_DEBUG
, "array set value (%s=%s)\n", arg1
.var
[i
],
187 pbx_builtin_setvar_helper(chan
, arg1
.var
[i
], arg2
.val
[i
]);
189 /* We could unset the variable, by passing a NULL, but due to
190 * pushvar semantics, that could create some undesired behavior. */
191 pbx_builtin_setvar_helper(chan
, arg1
.var
[i
], "");
198 static struct ast_custom_function array_function
= {
200 .synopsis
= "Allows setting multiple variables at once",
201 .syntax
= "ARRAY(var1[|var2[...][|varN]])",
204 "The comma-separated list passed as a value to which the function is set will\n"
205 "be interpreted as a set of values to which the comma-separated list of\n"
206 "variable names in the argument should be set.\n"
207 "Hence, Set(ARRAY(var1|var2)=1\\,2) will set var1 to 1 and var2 to 2\n"
208 "Note: remember to either backslash your commas in extensions.conf or quote the\n"
209 "entire argument, since Set can take multiple arguments itself.\n",
212 static int acf_sprintf(struct ast_channel
*chan
, char *cmd
, char *data
, char *buf
, size_t len
)
214 #define SPRINTF_FLAG 0
215 #define SPRINTF_WIDTH 1
216 #define SPRINTF_PRECISION 2
217 #define SPRINTF_LENGTH 3
218 #define SPRINTF_CONVERSION 4
219 int i
, state
= -1, argcount
= 0;
220 char *formatstart
= NULL
, *bufptr
= buf
;
221 char formatbuf
[256] = "";
224 AST_DECLARE_APP_ARGS(arg
,
226 AST_APP_ARG(var
)[100];
229 AST_STANDARD_APP_ARGS(arg
, data
);
231 /* Scan the format, converting each argument into the requisite format type. */
232 for (i
= 0; arg
.format
[i
]; i
++) {
235 if (strchr("#0- +'I", arg
.format
[i
]))
237 state
= SPRINTF_WIDTH
;
239 if (arg
.format
[i
] >= '0' && arg
.format
[i
] <= '9')
242 /* Next character must be a period to go into a precision */
243 if (arg
.format
[i
] == '.') {
244 state
= SPRINTF_PRECISION
;
246 state
= SPRINTF_LENGTH
;
250 case SPRINTF_PRECISION
:
251 if (arg
.format
[i
] >= '0' && arg
.format
[i
] <= '9')
253 state
= SPRINTF_LENGTH
;
255 if (strchr("hl", arg
.format
[i
])) {
256 if (arg
.format
[i
+ 1] == arg
.format
[i
])
258 state
= SPRINTF_CONVERSION
;
260 } else if (strchr("Lqjzt", arg
.format
[i
]))
261 state
= SPRINTF_CONVERSION
;
263 state
= SPRINTF_CONVERSION
;
264 case SPRINTF_CONVERSION
:
265 if (strchr("diouxXc", arg
.format
[i
])) {
268 /* Isolate this format alone */
269 ast_copy_string(formatbuf
, formatstart
, sizeof(formatbuf
));
270 formatbuf
[&arg
.format
[i
] - formatstart
+ 1] = '\0';
272 /* Convert the argument into the required type */
273 if (sscanf(arg
.var
[argcount
++], "%i", &tmpi
) != 1) {
274 ast_log(LOG_ERROR
, "Argument '%s' is not an integer number for format '%s'\n", arg
.var
[argcount
- 1], formatbuf
);
278 /* Format the argument */
279 snprintf(bufptr
, buf
+ len
- bufptr
, formatbuf
, tmpi
);
281 /* Update the position of the next parameter to print */
282 bufptr
= strchr(buf
, '\0');
283 } else if (strchr("eEfFgGaA", arg
.format
[i
])) {
286 /* Isolate this format alone */
287 ast_copy_string(formatbuf
, formatstart
, sizeof(formatbuf
));
288 formatbuf
[&arg
.format
[i
] - formatstart
+ 1] = '\0';
290 /* Convert the argument into the required type */
291 if (sscanf(arg
.var
[argcount
++], "%lf", &tmpd
) != 1) {
292 ast_log(LOG_ERROR
, "Argument '%s' is not a floating point number for format '%s'\n", arg
.var
[argcount
- 1], formatbuf
);
296 /* Format the argument */
297 snprintf(bufptr
, buf
+ len
- bufptr
, formatbuf
, tmpd
);
299 /* Update the position of the next parameter to print */
300 bufptr
= strchr(buf
, '\0');
301 } else if (arg
.format
[i
] == 's') {
304 /* Isolate this format alone */
305 ast_copy_string(formatbuf
, formatstart
, sizeof(formatbuf
));
306 formatbuf
[&arg
.format
[i
] - formatstart
+ 1] = '\0';
308 /* Format the argument */
309 snprintf(bufptr
, buf
+ len
- bufptr
, formatbuf
, arg
.var
[argcount
++]);
311 /* Update the position of the next parameter to print */
312 bufptr
= strchr(buf
, '\0');
313 } else if (arg
.format
[i
] == '%') {
314 /* Literal data to copy */
315 *bufptr
++ = arg
.format
[i
];
319 /* Isolate this format alone */
320 ast_copy_string(formatbuf
, formatstart
, sizeof(formatbuf
));
321 formatbuf
[&arg
.format
[i
] - formatstart
+ 1] = '\0';
323 ast_log(LOG_ERROR
, "Format type not supported: '%s' with argument '%s'\n", formatbuf
, arg
.var
[argcount
++]);
329 if (arg
.format
[i
] == '%') {
330 state
= SPRINTF_FLAG
;
331 formatstart
= &arg
.format
[i
];
334 /* Literal data to copy */
335 *bufptr
++ = arg
.format
[i
];
344 static struct ast_custom_function sprintf_function
= {
346 .synopsis
= "Format a variable according to a format string",
347 .syntax
= "SPRINTF(<format>|<arg1>[|...<argN>])",
350 "Parses the format string specified and returns a string matching that format.\n"
351 "Supports most options supported by sprintf(3). Returns a shortened string if\n"
352 "a format specifier is not recognized.\n",
355 static int quote(struct ast_channel
*chan
, char *cmd
, char *data
, char *buf
, size_t len
)
357 char *bufptr
= buf
, *dataptr
= data
;
359 for (; bufptr
< buf
+ len
- 1; dataptr
++) {
360 if (*dataptr
== '\\') {
363 } else if (*dataptr
== '"') {
366 } else if (*dataptr
== '\0') {
369 *bufptr
++ = *dataptr
;
377 static struct ast_custom_function quote_function
= {
379 .synopsis
= "Quotes a given string, escaping embedded quotes as necessary",
380 .syntax
= "QUOTE(<string>)",
385 static int len(struct ast_channel
*chan
, char *cmd
, char *data
, char *buf
,
391 length
= strlen(data
);
393 snprintf(buf
, len
, "%d", length
);
398 static struct ast_custom_function len_function
= {
400 .synopsis
= "Returns the length of the argument given",
401 .syntax
= "LEN(<string>)",
405 static int acf_strftime(struct ast_channel
*chan
, char *cmd
, char *parse
,
406 char *buf
, size_t len
)
408 AST_DECLARE_APP_ARGS(args
,
410 AST_APP_ARG(timezone
);
418 if (ast_strlen_zero(parse
)) {
420 "Asterisk function STRFTIME() requires an argument.\n");
424 AST_STANDARD_APP_ARGS(args
, parse
);
426 ast_get_time_t(args
.epoch
, &epochi
, time(NULL
), NULL
);
427 ast_localtime(&epochi
, &tm
, args
.timezone
);
432 if (!strftime(buf
, len
, args
.format
, &tm
))
433 ast_log(LOG_WARNING
, "C function strftime() output nothing?!!\n");
440 static struct ast_custom_function strftime_function
= {
442 .synopsis
= "Returns the current date/time in a specified format.",
443 .syntax
= "STRFTIME([<epoch>][|[timezone][|format]])",
444 .read
= acf_strftime
,
447 static int acf_strptime(struct ast_channel
*chan
, char *cmd
, char *data
,
448 char *buf
, size_t len
)
450 AST_DECLARE_APP_ARGS(args
,
451 AST_APP_ARG(timestring
);
452 AST_APP_ARG(timezone
);
457 memset(&time
, 0, sizeof(struct tm
));
463 "Asterisk function STRPTIME() requires an argument.\n");
467 AST_STANDARD_APP_ARGS(args
, data
);
469 if (ast_strlen_zero(args
.format
)) {
471 "No format supplied to STRPTIME(<timestring>|<timezone>|<format>)");
475 if (!strptime(args
.timestring
, args
.format
, &time
)) {
476 ast_log(LOG_WARNING
, "C function strptime() output nothing?!!\n");
478 snprintf(buf
, len
, "%d", (int) ast_mktime(&time
, args
.timezone
));
484 static struct ast_custom_function strptime_function
= {
487 "Returns the epoch of the arbitrary date/time string structured as described in the format.",
488 .syntax
= "STRPTIME(<datetime>|<timezone>|<format>)",
490 "This is useful for converting a date into an EPOCH time, possibly to pass to\n"
491 "an application like SayUnixTime or to calculate the difference between two\n"
495 " ${STRPTIME(2006-03-01 07:30:35|America/Chicago|%Y-%m-%d %H:%M:%S)} returns 1141219835\n",
496 .read
= acf_strptime
,
499 static int function_eval(struct ast_channel
*chan
, char *cmd
, char *data
,
500 char *buf
, size_t len
)
504 if (ast_strlen_zero(data
)) {
505 ast_log(LOG_WARNING
, "EVAL requires an argument: EVAL(<string>)\n");
509 pbx_substitute_variables_helper(chan
, data
, buf
, len
- 1);
514 static struct ast_custom_function eval_function
= {
516 .synopsis
= "Evaluate stored variables.",
517 .syntax
= "EVAL(<variable>)",
518 .desc
= "Using EVAL basically causes a string to be evaluated twice.\n"
519 "When a variable or expression is in the dialplan, it will be\n"
520 "evaluated at runtime. However, if the result of the evaluation\n"
521 "is in fact a variable or expression, using EVAL will have it\n"
522 "evaluated a second time. For example, if the variable ${MYVAR}\n"
523 "contains \"${OTHERVAR}\", then the result of putting ${EVAL(${MYVAR})}\n"
524 "in the dialplan will be the contents of the variable, OTHERVAR.\n"
525 "Normally, by just putting ${MYVAR} in the dialplan, you would be\n"
526 "left with \"${OTHERVAR}\".\n",
527 .read
= function_eval
,
530 static int keypadhash(struct ast_channel
*chan
, char *cmd
, char *data
, char *buf
, size_t len
)
532 char *bufptr
, *dataptr
;
534 for (bufptr
= buf
, dataptr
= data
; bufptr
< buf
+ len
- 1; dataptr
++) {
535 if (*dataptr
== '1') {
537 } else if (strchr("AaBbCc2", *dataptr
)) {
539 } else if (strchr("DdEeFf3", *dataptr
)) {
541 } else if (strchr("GgHhIi4", *dataptr
)) {
543 } else if (strchr("JjKkLl5", *dataptr
)) {
545 } else if (strchr("MmNnOo6", *dataptr
)) {
547 } else if (strchr("PpQqRrSs7", *dataptr
)) {
549 } else if (strchr("TtUuVv8", *dataptr
)) {
551 } else if (strchr("WwXxYyZz9", *dataptr
)) {
553 } else if (*dataptr
== '0') {
555 } else if (*dataptr
== '\0') {
565 static struct ast_custom_function keypadhash_function
= {
566 .name
= "KEYPADHASH",
567 .synopsis
= "Hash the letters in the string into the equivalent keypad numbers.",
568 .syntax
= "KEYPADHASH(<string>)",
570 .desc
= "Example: ${KEYPADHASH(Les)} returns \"537\"\n",
573 static char *tdesc
= "String handling dialplan functions";
575 static int unload_module(void *mod
)
579 res
|= ast_custom_function_unregister(&fieldqty_function
);
580 res
|= ast_custom_function_unregister(&filter_function
);
581 res
|= ast_custom_function_unregister(®ex_function
);
582 res
|= ast_custom_function_unregister(&array_function
);
583 res
|= ast_custom_function_unregister("e_function
);
584 res
|= ast_custom_function_unregister(&len_function
);
585 res
|= ast_custom_function_unregister(&strftime_function
);
586 res
|= ast_custom_function_unregister(&strptime_function
);
587 res
|= ast_custom_function_unregister(&eval_function
);
588 res
|= ast_custom_function_unregister(&keypadhash_function
);
589 res
|= ast_custom_function_unregister(&sprintf_function
);
594 static int load_module(void *mod
)
598 res
|= ast_custom_function_register(&fieldqty_function
);
599 res
|= ast_custom_function_register(&filter_function
);
600 res
|= ast_custom_function_register(®ex_function
);
601 res
|= ast_custom_function_register(&array_function
);
602 res
|= ast_custom_function_register("e_function
);
603 res
|= ast_custom_function_register(&len_function
);
604 res
|= ast_custom_function_register(&strftime_function
);
605 res
|= ast_custom_function_register(&strptime_function
);
606 res
|= ast_custom_function_register(&eval_function
);
607 res
|= ast_custom_function_register(&keypadhash_function
);
608 res
|= ast_custom_function_register(&sprintf_function
);
613 static const char *description(void)
619 static const char *key(void)
621 return ASTERISK_GPL_KEY
;
624 STD_MOD(MOD_1
| NO_USECOUNT
, NULL
, NULL
, NULL
);