--- funcs/func_strings.c.orig 2010-03-10 17:48:33.000000000 -0300 +++ funcs/func_strings.c 2010-07-08 12:50:11.000000000 -0300 @@ -40,6 +40,7 @@ #include "asterisk/localtime.h" AST_THREADSTORAGE(result_buf); +AST_THREADSTORAGE(tmp_buf); /*** DOCUMENTATION @@ -91,6 +92,37 @@ \ + + + Replace a set of characters in a given string with another character. + + + + + + + + Iterates through a string replacing all the find-chars with + replace-char. replace-char may be either + empty or contain one character. If empty, all find-chars will be + deleted from the output. + The replacement only occurs in the output. The original variable is not + altered. + + + + + Pass the given argument back as a value. + + + + + + Literally returns the given string. The intent is to permit + other dialplan functions which take a variable name as an argument to be able to take a literal + string, instead. + + Check string against a regular expression. @@ -287,6 +319,76 @@ Example: ${CSV_QUOTE("a,b" 123)} will return """a,b"" 123" + + + Removes and returns the first item off of a variable containing delimited text + + + + + + + Example: + exten => s,1,Set(array=one,two,three) + exten => s,n,While($["${SET(var=${SHIFT(array)})}" != ""]) + exten => s,n,NoOp(var is ${var}) + exten => s,n,EndWhile + This would iterate over each value in array, left to right, and + would result in NoOp(var is one), NoOp(var is two), and + NoOp(var is three) being executed. + + + + + + Removes and returns the last item off of a variable containing delimited text + + + + + + + Example: + exten => s,1,Set(array=one,two,three) + exten => s,n,While($["${SET(var=${POP(array)})}" != ""]) + exten => s,n,NoOp(var is ${var}) + exten => s,n,EndWhile + This would iterate over each value in array, right to left, and + would result in NoOp(var is three), NoOp(var is two), and + NoOp(var is one) being executed. + + + + + + Appends one or more values to the end of a variable containing delimited text + + + + + + + Example: Set(PUSH(array)=one,two,three) would append one, + two, and three to the end of the values stored in the variable + "array". + + + + + + Inserts one or more values to the beginning of a variable containing delimited text + + + + + + + Example: Set(UNSHIFT(array)=one,two,three) would insert one, + two, and three before the values stored in the variable + "array". + + + ***/ static int function_fieldqty(struct ast_channel *chan, const char *cmd, @@ -505,8 +607,77 @@ .read = filter, }; -static int regex(struct ast_channel *chan, const char *cmd, char *parse, char *buf, - size_t len) +static int replace(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) +{ + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(varname); + AST_APP_ARG(find); + AST_APP_ARG(replace); + ); + char *strptr, *varsubst; + struct ast_str *str = ast_str_thread_get(&result_buf, 16); + char find[256]; /* Only 256 characters possible */ + char replace[2] = ""; + size_t unused; + + AST_STANDARD_APP_ARGS(args, data); + + if (!str) { + return -1; + } + + if (args.argc < 2) { + ast_log(LOG_ERROR, "Usage: %s(,[,])\n", cmd); + return -1; + } + + /* Decode escapes */ + ast_get_encoded_str(args.find, find, sizeof(find)); + ast_get_encoded_char(args.replace, replace, &unused); + + if (ast_strlen_zero(find) || ast_strlen_zero(args.varname)) { + ast_log(LOG_ERROR, "The characters to search for and the variable name must not be empty.\n"); + return -1; + } + + varsubst = alloca(strlen(args.varname) + 4); + sprintf(varsubst, "${%s}", args.varname); + ast_str_substitute_variables(&str, 0, chan, varsubst); + + if (!ast_str_strlen(str)) { + /* Blank, nothing to replace */ + return -1; + } + + ast_debug(3, "String to search: (%s)\n", ast_str_buffer(str)); + ast_debug(3, "Characters to find: (%s)\n", find); + ast_debug(3, "Character to replace with: (%s)\n", replace); + + for (strptr = ast_str_buffer(str); *strptr; strptr++) { + /* buf is already a mutable buffer, so we construct the result + * directly there */ + if (strchr(find, *strptr)) { + if (ast_strlen_zero(replace)) { + /* Remove character */ + strcpy(strptr, strptr + 1); /* SAFE */ + strptr--; + } else { + /* Replace character */ + *strptr = *replace; + } + } + } + + snprintf(buf, len, "%s", ast_str_buffer(str)); + return 0; +} + +static struct ast_custom_function replace_function = { + .name = "REPLACE", + .read = replace, +}; + +static int regex(struct ast_channel *chan, const char *cmd, char *parse, char *buf, size_t len) { AST_DECLARE_APP_ARGS(args, AST_APP_ARG(null); @@ -997,12 +1168,140 @@ .read = string_tolower, }; +static int shift_pop(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) +{ +#define beginning (cmd[0] == 'S') /* SHIFT */ + char *after, delimiter[2] = ",", *varsubst; + size_t unused; + struct ast_str *before = ast_str_thread_get(&result_buf, 16); + char *(*search_func)(const char *s, int c) = (beginning ? strchr : strrchr); + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(var); + AST_APP_ARG(delimiter); + ); + + if (!before) { + return -1; + } + + AST_STANDARD_APP_ARGS(args, data); + + if (ast_strlen_zero(args.var)) { + ast_log(LOG_WARNING, "%s requires a variable name\n", cmd); + return -1; + } + + varsubst = alloca(strlen(args.var) + 4); + sprintf(varsubst, "${%s}", args.var); + ast_str_substitute_variables(&before, 0, chan, varsubst); + + if (args.argc > 1 && !ast_strlen_zero(args.delimiter)) { + ast_get_encoded_char(args.delimiter, delimiter, &unused); + } + + if (!ast_str_strlen(before)) { + /* Nothing to pop */ + return -1; + } + + if (!(after = search_func(ast_str_buffer(before), delimiter[0]))) { + /* Only one entry in array */ + snprintf(buf, len, "%s", ast_str_buffer(before)); + pbx_builtin_setvar_helper(chan, args.var, ""); + } else { + *after++ = '\0'; + snprintf(buf, len, "%s", beginning ? ast_str_buffer(before) : after); + pbx_builtin_setvar_helper(chan, args.var, beginning ? after : ast_str_buffer(before)); + } + + return 0; +#undef beginning +} + +static struct ast_custom_function shift_function = { + .name = "SHIFT", + .read = shift_pop, +}; + +static struct ast_custom_function pop_function = { + .name = "POP", + .read = shift_pop, +}; + +static int unshift_push(struct ast_channel *chan, const char *cmd, char *data, const char *new_value) +{ +#define beginning (cmd[0] == 'U') /* UNSHIFT */ + char delimiter[2] = ",", *varsubst; + size_t unused; + struct ast_str *buf, *previous_value; + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(var); + AST_APP_ARG(delimiter); + ); + + if (!(buf = ast_str_thread_get(&result_buf, 16)) || + !(previous_value = ast_str_thread_get(&tmp_buf, 16))) { + return -1; + } + + AST_STANDARD_APP_ARGS(args, data); + + if (ast_strlen_zero(args.var)) { + ast_log(LOG_WARNING, "%s requires a variable name\n", cmd); + return -1; + } + + if (args.argc > 1 && !ast_strlen_zero(args.delimiter)) { + ast_get_encoded_char(args.delimiter, delimiter, &unused); + } + + varsubst = alloca(strlen(args.var) + 4); + sprintf(varsubst, "${%s}", args.var); + ast_str_substitute_variables(&previous_value, 0, chan, varsubst); + + if (!ast_str_strlen(previous_value)) { + ast_str_set(&buf, 0, "%s", new_value); + } else { + ast_str_set(&buf, 0, "%s%c%s", + beginning ? new_value : ast_str_buffer(previous_value), + delimiter[0], + beginning ? ast_str_buffer(previous_value) : new_value); + } + + pbx_builtin_setvar_helper(chan, args.var, ast_str_buffer(buf)); + + return 0; +#undef beginning +} + +static struct ast_custom_function push_function = { + .name = "PUSH", + .write = unshift_push, +}; + +static struct ast_custom_function unshift_function = { + .name = "UNSHIFT", + .write = unshift_push, +}; + +static int passthru(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) +{ + snprintf(buf, len, "%s", data); + return 0; +} + +static struct ast_custom_function passthru_function = { + .name = "PASSTHRU", + .read = passthru, +}; + static int unload_module(void) { int res = 0; res |= ast_custom_function_unregister(&fieldqty_function); res |= ast_custom_function_unregister(&filter_function); + res |= ast_custom_function_unregister(&replace_function); res |= ast_custom_function_unregister(&listfilter_function); res |= ast_custom_function_unregister(®ex_function); res |= ast_custom_function_unregister(&array_function); @@ -1018,6 +1317,11 @@ res |= ast_unregister_application(app_clearhash); res |= ast_custom_function_unregister(&toupper_function); res |= ast_custom_function_unregister(&tolower_function); + res |= ast_custom_function_unregister(&shift_function); + res |= ast_custom_function_unregister(&pop_function); + res |= ast_custom_function_unregister(&push_function); + res |= ast_custom_function_unregister(&unshift_function); + res |= ast_custom_function_unregister(&passthru_function); return res; } @@ -1028,6 +1332,7 @@ res |= ast_custom_function_register(&fieldqty_function); res |= ast_custom_function_register(&filter_function); + res |= ast_custom_function_register(&replace_function); res |= ast_custom_function_register(&listfilter_function); res |= ast_custom_function_register(®ex_function); res |= ast_custom_function_register(&array_function); @@ -1043,6 +1348,11 @@ res |= ast_register_application_xml(app_clearhash, exec_clearhash); res |= ast_custom_function_register(&toupper_function); res |= ast_custom_function_register(&tolower_function); + res |= ast_custom_function_register(&shift_function); + res |= ast_custom_function_register(&pop_function); + res |= ast_custom_function_register(&push_function); + res |= ast_custom_function_register(&unshift_function); + res |= ast_custom_function_register(&passthru_function); return res; }