diff --git a/CHANGES b/CHANGES index ab7770cd014a7561af98f6da643ea60eccead3b0..77f1da2ef9a1cdd76cd69320c560b3bd061292d9 100644 --- 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 ------------- diff --git a/UPGRADE.txt b/UPGRADE.txt index 272db32fb9ecc9bfd94bb0784052839569a5c30f..8cd6069b41f4d89b4366a43d8668ec9d1c9de1d7 100644 --- a/UPGRADE.txt +++ b/UPGRADE.txt @@ -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. diff --git a/include/asterisk/config.h b/include/asterisk/config.h index b6c5dbd1482c9a65bc207131feed3e638b15e77b..a12d12bf62ab475a65778c27269ffbcb3a8a854a 100644 --- a/include/asterisk/config.h +++ b/include/asterisk/config.h @@ -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); diff --git a/main/config.c b/main/config.c index 62ab5dbcb85c215702ec497bfac234775e439661..11a41538c54bfad4c002e0834966a2e9b18a37c0 100644 --- a/main/config.c +++ b/main/config.c @@ -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;