Add LISTFILTER dialplan function, along with supporting documentation. See
authorTilghman Lesher <tilghman@meg.abyt.es>
Wed, 5 Nov 2008 21:58:48 +0000 (21:58 +0000)
committerTilghman Lesher <tilghman@meg.abyt.es>
Wed, 5 Nov 2008 21:58:48 +0000 (21:58 +0000)
documentation for more information on how to use it.

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@154915 65c4cc65-6c06-0410-ace0-fbb531ad65f3

CHANGES
funcs/func_strings.c
include/asterisk/app.h
main/app.c

diff --git a/CHANGES b/CHANGES
index 623bd9e..9da20e4 100644 (file)
--- 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
 ------------
index 651fced..2e254ab 100644 (file)
@@ -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(&regex_function);
        res |= ast_custom_function_unregister(&array_function);
        res |= ast_custom_function_unregister(&quote_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(&regex_function);
        res |= ast_custom_function_register(&array_function);
        res |= ast_custom_function_register(&quote_function);
index 4d1bcc3..851ad9f 100644 (file)
@@ -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);
 
index b7429bf..2d1b897 100644 (file)
@@ -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;