diff --git a/CHANGES b/CHANGES index 623bd9eb385c8e801120d399e1ad9f6a26c9ae76..9da20e49ccb4a466fcec4adc89601fe9acf75d13 100644 --- a/CHANGES +++ b/CHANGES @@ -32,6 +32,9 @@ Dialplan Functions * Added debugging CLI functions to func_odbc, 'odbc read' and 'odbc write'. * func_odbc now may specify an insert query to execute, when the write query affects 0 rows (usually indicating that no such row exists). + * Added a new dialplan function, LISTFILTER, which permits removing elements + from a set list, by name. Uses the same general syntax as the existing CUT + and FIELDQTY dialplan functions, which also manage lists. Applications ------------ diff --git a/funcs/func_strings.c b/funcs/func_strings.c index 651fcede250ad483be6e1aa403d91053ef31c368..2e254ab87f3aa2ac75b260419a06014b9ddc021f 100644 --- a/funcs/func_strings.c +++ b/funcs/func_strings.c @@ -39,6 +39,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/app.h" #include "asterisk/localtime.h" +AST_THREADSTORAGE(result_buf); + /*** DOCUMENTATION <function name="FIELDQTY" language="en_US"> <synopsis> @@ -52,6 +54,19 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") <para>Example: ${FIELDQTY(ex-amp-le,-)} returns 3</para> </description> </function> + <function name="LISTFILTER" language="en_US"> + <synopsis>Remove an item from a list, by name.</synopsis> + <syntax> + <parameter name="varname" required="true" /> + <parameter name="delim" required="true" default="," /> + <parameter name="value" required="true" /> + </syntax> + <description> + <para>Remove <replaceable>value</replaceable> from the list contained in the <replaceable>varname</replaceable> + variable, where the list delimiter is specified by the <replaceable>delim</replaceable> parameter. This is + very useful for removing a single channel name from a list of channels, for example.</para> + </description> + </function> <function name="FILTER" language="en_US"> <synopsis> Filter the string to include only the allowed characters @@ -322,6 +337,105 @@ static struct ast_custom_function fieldqty_function = { .read = function_fieldqty, }; +static int listfilter(struct ast_channel *chan, const char *cmd, char *parse, char *buf, size_t len) +{ + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(listname); + AST_APP_ARG(delimiter); + AST_APP_ARG(fieldvalue); + ); + const char *orig_list, *ptr; + const char *begin, *cur, *next; + int dlen, flen; + struct ast_str *result = ast_str_thread_get(&result_buf, 16); + char *delim; + + AST_STANDARD_APP_ARGS(args, parse); + + if (args.argc < 3) { + ast_log(LOG_ERROR, "Usage: LISTFILTER(<listname>,<delimiter>,<fieldvalue>)\n"); + return -1; + } + + /* If we don't lock the channel, the variable could disappear out from underneath us. */ + if (chan) { + ast_channel_lock(chan); + } + if (!(orig_list = pbx_builtin_getvar_helper(chan, args.listname))) { + ast_log(LOG_ERROR, "List variable '%s' not found\n", args.listname); + if (chan) { + ast_channel_unlock(chan); + } + return -1; + } + + /* If the string isn't there, just copy out the string and be done with it. */ + if (!(ptr = strstr(orig_list, args.fieldvalue))) { + ast_copy_string(buf, orig_list, len); + if (chan) { + ast_channel_unlock(chan); + } + return 0; + } + + dlen = strlen(args.delimiter); + delim = alloca(dlen + 1); + ast_get_encoded_str(args.delimiter, delim, dlen + 1); + + if ((dlen = strlen(delim)) == 0) { + delim = ","; + dlen = 1; + } + + flen = strlen(args.fieldvalue); + + ast_str_reset(result); + /* Enough space for any result */ + ast_str_make_space(&result, strlen(orig_list) + 1); + + begin = orig_list; + next = strstr(begin, delim); + + do { + /* Find next boundary */ + if (next) { + cur = next; + next = strstr(cur + dlen, delim); + } else { + cur = strchr(begin + dlen, '\0'); + } + + if (flen == cur - begin && !strncmp(begin, args.fieldvalue, flen)) { + /* Skip field */ + begin += flen + dlen; + } else { + /* Copy field to output */ + if (result->used) { + ast_str_append(&result, 0, "%s", delim); + } + + /* Have to do it this way, since we're not null-terminated. */ + strncpy(result->str + result->used, begin, cur - begin); + result->used += cur - begin; + result->str[result->used] = '\0'; + + begin = cur + dlen; + } + } while (*cur != '\0'); + if (chan) { + ast_channel_unlock(chan); + } + + ast_copy_string(buf, result->str, len); + + return 0; +} + +static struct ast_custom_function listfilter_function = { + .name = "LISTFILTER", + .read = listfilter, +}; + static int filter(struct ast_channel *chan, const char *cmd, char *parse, char *buf, size_t len) { @@ -997,6 +1111,7 @@ static int unload_module(void) res |= ast_custom_function_unregister(&fieldqty_function); res |= ast_custom_function_unregister(&filter_function); + res |= ast_custom_function_unregister(&listfilter_function); res |= ast_custom_function_unregister(®ex_function); res |= ast_custom_function_unregister(&array_function); res |= ast_custom_function_unregister("e_function); @@ -1021,6 +1136,7 @@ static int load_module(void) res |= ast_custom_function_register(&fieldqty_function); res |= ast_custom_function_register(&filter_function); + res |= ast_custom_function_register(&listfilter_function); res |= ast_custom_function_register(®ex_function); res |= ast_custom_function_register(&array_function); res |= ast_custom_function_register("e_function); diff --git a/include/asterisk/app.h b/include/asterisk/app.h index 4d1bcc3ae41b78d463d5091f5376b3b8c6327229..851ad9fa650dc9e465dc4428c8e7757e6291b2f3 100644 --- a/include/asterisk/app.h +++ b/include/asterisk/app.h @@ -488,6 +488,9 @@ int ast_record_review(struct ast_channel *chan, const char *playfile, const char /*! \brief Decode an encoded control or extended ASCII character */ int ast_get_encoded_char(const char *stream, char *result, size_t *consumed); +/*! \brief Decode a stream of encoded control or extended ASCII characters */ +int ast_get_encoded_str(const char *stream, char *result, size_t result_len); + /*! \brief Common routine for child processes, to close all fds prior to exec(2) */ void ast_close_fds_above_n(int n); diff --git a/main/app.c b/main/app.c index b7429bff72c4b6a8c386d43934ca737178852574..2d1b897fb45c307cb5a10fc8811f159acb424487 100644 --- a/main/app.c +++ b/main/app.c @@ -1815,6 +1815,19 @@ int ast_get_encoded_char(const char *stream, char *result, size_t *consumed) return 0; } +int ast_get_encoded_str(const char *stream, char *result, size_t result_size) +{ + char *cur = result; + size_t consumed; + + while (cur < result + result_size - 1 && !ast_get_encoded_char(stream, cur, &consumed)) { + cur++; + stream += consumed; + } + *cur = '\0'; + return 0; +} + void ast_close_fds_above_n(int n) { int x, null;