Add '+=' append operator to configuration files.
authorTilghman Lesher <tilghman@meg.abyt.es>
Tue, 5 Aug 2008 18:25:16 +0000 (18:25 +0000)
committerTilghman Lesher <tilghman@meg.abyt.es>
Tue, 5 Aug 2008 18:25:16 +0000 (18:25 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@135717 65c4cc65-6c06-0410-ace0-fbb531ad65f3

CHANGES
UPGRADE.txt
include/asterisk/config.h
main/config.c

diff --git a/CHANGES b/CHANGES
index ab7770c..77f1da2 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -199,6 +199,10 @@ Miscellaneous
     Skinny channels only.
   * You can now compile Asterisk against the Hoard Memory Allocator, see doc/hoard.txt
     for more information.
+  * Config file variables may now be appended to, by using the '+=' append
+    operator.  This is most helpful when working with long SQL queries in
+    func_odbc.conf, as the queries no longer need to be specified on a single
+    line.
 
 ------------------------------------------------------------------------------
 --- Functionality changes from Asterisk 1.4.X to Asterisk 1.6.0  -------------
index 272db32..8cd6069 100644 (file)
@@ -226,6 +226,13 @@ Configuration:
 * queues.conf: the queue-lessthan sound file option is no longer available, and the
   queue-round-seconds option no longer takes '1' as a valid parameter.
 
+* If you have any third party modules which use a config file variable whose
+  name ends in a '+', please note that the append capability added to this
+  version may now conflict with that variable naming scheme.  An easy
+  workaround is to ensure that a space occurs between the '+' and the '=',
+  to differentiate your variable from the append operator.  This potential
+  conflict is unlikely, but is documented here to be thorough.
+
 Manager:
 
 * Manager has been upgraded to version 1.1 with a lot of changes. 
index b6c5dbd..a12d12b 100644 (file)
@@ -339,6 +339,15 @@ void ast_include_rename(struct ast_config *conf, const char *from_file, const ch
 void ast_variable_append(struct ast_category *category, struct ast_variable *variable);
 void ast_variable_insert(struct ast_category *category, struct ast_variable *variable, const char *line);
 int ast_variable_delete(struct ast_category *category, const char *variable, const char *match, const char *line);
+
+/*! \brief Update variable value within a config
+ * \param category Category element within the config
+ * \param variable Name of the variable to change
+ * \param value New value of the variable
+ * \param match If set, previous value of the variable (if NULL or zero-length, no matching will be done)
+ * \param object Boolean of whether to make the new variable an object
+ * \return 0 on success or -1 on failure.
+ */
 int ast_variable_update(struct ast_category *category, const char *variable, 
                                                const char *value, const char *match, unsigned int object);
 
index 62ab5db..11a4153 100644 (file)
@@ -98,6 +98,14 @@ struct cache_file_mtime {
 
 static AST_LIST_HEAD_STATIC(cfmtime_head, cache_file_mtime);
 
+static int init_appendbuf(void *data)
+{
+       struct ast_str **str = data;
+       *str = ast_str_create(16);
+       return *str ? 0 : -1;
+}
+
+AST_THREADSTORAGE_CUSTOM(appendbuf, init_appendbuf, ast_free_ptr);
 
 /* comment buffers are better implemented using the ast_str_*() API */
 #define CB_SIZE 250    /* initial size of comment buffers */
@@ -752,12 +760,8 @@ int ast_variable_update(struct ast_category *category, const char *variable,
                return 0;
        }
 
-       if (prev)
-               prev->next = newer;
-       else
-               category->root = newer;
-
-       return 0;
+       /* Could not find variable to update */
+       return -1;
 }
 
 int ast_category_delete(struct ast_config *cfg, const char *category)
@@ -1041,65 +1045,93 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
                        return 0;       /* XXX is this correct ? or we should return -1 ? */
                }
 
-                               /* Strip off leading and trailing "'s and <>'s */
-                               while ((*c == '<') || (*c == '>') || (*c == '\"')) c++;
-                               /* Get rid of leading mess */
-                               cur = c;
-                               cur2 = cur;
-                               while (!ast_strlen_zero(cur)) {
-                                       c = cur + strlen(cur) - 1;
-                                       if ((*c == '>') || (*c == '<') || (*c == '\"'))
-                                               *c = '\0';
-                                       else
-                                               break;
-                               }
-                               /* #exec </path/to/executable>
-                                  We create a tmp file, then we #include it, then we delete it. */
-                               if (!do_include) {
-                                       struct timeval tv = ast_tvnow();
-                                       if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
-                                               config_cache_attribute(configfile, ATTRIBUTE_EXEC, NULL, who_asked);
-                                       snprintf(exec_file, sizeof(exec_file), "/var/tmp/exec.%d%d.%ld", (int)tv.tv_sec, (int)tv.tv_usec, (long)pthread_self());
-                                       snprintf(cmd, sizeof(cmd), "%s > %s 2>&1", cur, exec_file);
-                                       ast_safe_system(cmd);
-                                       cur = exec_file;
-                               } else {
-                                       if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
-                                               config_cache_attribute(configfile, ATTRIBUTE_INCLUDE, cur, who_asked);
-                                       exec_file[0] = '\0';
-                               }
-                               /* A #include */
-                               /* record this inclusion */
-                               inclu = ast_include_new(cfg, configfile, cur, !do_include, cur2, lineno, real_inclusion_name, sizeof(real_inclusion_name));
-
-                               do_include = ast_config_internal_load(cur, cfg, flags, real_inclusion_name, who_asked) ? 1 : 0;
-                               if (!ast_strlen_zero(exec_file))
-                                       unlink(exec_file);
-                               if (!do_include) {
-                                       ast_log(LOG_ERROR, "The file '%s' was listed as a #include but it does not exist.\n", cur);
-                                       return -1;
-                               }
-                               /* XXX otherwise what ? the default return is 0 anyways */
+               /* Strip off leading and trailing "'s and <>'s */
+               while ((*c == '<') || (*c == '>') || (*c == '\"')) c++;
+               /* Get rid of leading mess */
+               cur = c;
+               cur2 = cur;
+               while (!ast_strlen_zero(cur)) {
+                       c = cur + strlen(cur) - 1;
+                       if ((*c == '>') || (*c == '<') || (*c == '\"'))
+                               *c = '\0';
+                       else
+                               break;
+               }
+               /* #exec </path/to/executable>
+                  We create a tmp file, then we #include it, then we delete it. */
+               if (!do_include) {
+                       struct timeval tv = ast_tvnow();
+                       if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
+                               config_cache_attribute(configfile, ATTRIBUTE_EXEC, NULL, who_asked);
+                       snprintf(exec_file, sizeof(exec_file), "/var/tmp/exec.%d%d.%ld", (int)tv.tv_sec, (int)tv.tv_usec, (long)pthread_self());
+                       snprintf(cmd, sizeof(cmd), "%s > %s 2>&1", cur, exec_file);
+                       ast_safe_system(cmd);
+                       cur = exec_file;
+               } else {
+                       if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
+                               config_cache_attribute(configfile, ATTRIBUTE_INCLUDE, cur, who_asked);
+                       exec_file[0] = '\0';
+               }
+               /* A #include */
+               /* record this inclusion */
+               inclu = ast_include_new(cfg, configfile, cur, !do_include, cur2, lineno, real_inclusion_name, sizeof(real_inclusion_name));
+
+               do_include = ast_config_internal_load(cur, cfg, flags, real_inclusion_name, who_asked) ? 1 : 0;
+               if (!ast_strlen_zero(exec_file))
+                       unlink(exec_file);
+               if (!do_include) {
+                       ast_log(LOG_ERROR, "The file '%s' was listed as a #include but it does not exist.\n", cur);
+                       return -1;
+               }
+               /* XXX otherwise what ? the default return is 0 anyways */
 
        } else {
                /* Just a line (variable = value) */
+               int object = 0;
                if (!(*cat)) {
                        ast_log(LOG_WARNING,
                                "parse error: No category context for line %d of %s\n", lineno, configfile);
                        return -1;
                }
                c = strchr(cur, '=');
-               if (c) {
-                       int object;
+
+               if (c && c > cur && (*(c - 1) == '+')) {
+                       struct ast_variable *var, *replace = NULL;
+                       struct ast_str **str = ast_threadstorage_get(&appendbuf, sizeof(*str));
+
+                       if (!str || !*str) {
+                               return -1;
+                       }
+
+                       *(c - 1) = '\0';
+                       c++;
+                       cur = ast_strip(cur);
+
+                       /* Must iterate through category until we find last variable of same name (since there could be multiple) */
+                       for (var = ast_category_first(*cat); var; var = var->next) {
+                               if (!strcmp(var->name, cur)) {
+                                       replace = var;
+                               }
+                       }
+
+                       if (!replace) {
+                               /* Nothing to replace; just set a variable normally. */
+                               goto set_new_variable;
+                       }
+
+                       ast_str_set(str, 0, "%s", replace->value);
+                       ast_str_append(str, 0, "%s", c);
+                       ast_variable_update(*cat, replace->name, ast_strip((*str)->str), replace->value, object);
+               } else if (c) {
                        *c = 0;
                        c++;
                        /* Ignore > in => */
                        if (*c== '>') {
                                object = 1;
                                c++;
-                       } else
-                               object = 0;
-                       if ((v = ast_variable_new(ast_strip(cur), ast_strip(c), *suggested_include_file ? suggested_include_file : configfile))) {
+                       }
+set_new_variable:
+                       if ((v = ast_variable_new(ast_strip(cur), ast_strip(c), S_OR(suggested_include_file, configfile)))) {
                                v->lineno = lineno;
                                v->object = object;
                                *last_cat = 0;