diff --git a/include/asterisk/config.h b/include/asterisk/config.h index 44ba4e4877..954508b718 100644 --- a/include/asterisk/config.h +++ b/include/asterisk/config.h @@ -914,7 +914,7 @@ void ast_category_rename(struct ast_category *cat, const char *name); struct ast_variable *_ast_variable_new(const char *name, const char *value, const char *filename, const char *file, const char *function, int lineno); #define ast_variable_new(name, value, filename) _ast_variable_new(name, value, filename, __FILE__, __PRETTY_FUNCTION__, __LINE__) -struct ast_config_include *ast_include_new(struct ast_config *conf, const char *from_file, const char *included_file, int is_exec, const char *exec_file, int from_lineno, char *real_included_file_name, int real_included_file_name_size); +struct ast_config_include *ast_include_new(struct ast_config *conf, const char *from_file, const char *included_file, int include_type, const char *exec_file, int from_lineno, char *real_included_file_name, int real_included_file_name_size); struct ast_config_include *ast_include_find(struct ast_config *conf, const char *included_file); void ast_include_rename(struct ast_config *conf, const char *from_file, const char *to_file); void ast_variable_append(struct ast_category *category, struct ast_variable *variable); diff --git a/main/config.c b/main/config.c index 7f5503d111..71a51224a1 100644 --- a/main/config.c +++ b/main/config.c @@ -256,7 +256,28 @@ struct ast_config { struct ast_category *last_browse; /*!< used to cache the last category supplied via category_browse */ int include_level; int max_include_level; - struct ast_config_include *includes; /*!< a list of inclusions, which should describe the entire tree */ + /*! First inclusion in the list. */ + struct ast_config_include *includes_root; + /*! Last inclusion in the list. */ + struct ast_config_include *includes_last; +}; + +struct config_included_file { + AST_LIST_ENTRY(config_included_file) next; + /*! #include path or matching #tryinclude paths */ + char path[]; +}; + +/*! + * \brief Types used for ast_include_new() include_type + */ +enum include_statement_type { + /*! #include statements */ + CONFIG_STATEMENT_INCLUDE = 0, + /*! #exec statements */ + CONFIG_STATEMENT_EXEC = 1, + /*! #tryinclude statements */ + CONFIG_STATEMENT_TRYINCLUDE = 2, }; struct ast_config_include { @@ -266,7 +287,7 @@ struct ast_config_include { */ char *include_location_file; int include_location_lineno; /*!< lineno where include occurred */ - int exec; /*!< set to non-zero if its a #exec statement */ + enum include_statement_type include_type; /*!< #include, #exec, #tryinclude */ /*! * \brief if it's an exec, you'll have both the /var/tmp to read, and the original script * \note Will never be NULL if exec is non-zero @@ -277,9 +298,13 @@ struct ast_config_include { * \note Will never be NULL */ char *included_file; + AST_LIST_HEAD_NOLOCK(included_files_list, config_included_file) included_files; /*!< list of #include/#tryinclude files */ int inclusion_count; /*!< if the file is included more than once, a running count thereof -- but, worry not, we explode the instances and will include those-- so all entries will be unique */ int output; /*!< a flag to indicate if the inclusion has been output */ + struct ast_comment *precomments; + struct ast_comment *sameline; + struct ast_comment *trailing; /*!< the last object in the list will get assigned any trailing comments when EOF is hit */ struct ast_config_include *next; /*!< ptr to next inclusion in the list */ }; @@ -333,14 +358,18 @@ static void ast_variable_move(struct ast_variable *dst_var, struct ast_variable src_var->trailing = NULL; } -struct ast_config_include *ast_include_new(struct ast_config *conf, const char *from_file, const char *included_file, int is_exec, const char *exec_file, int from_lineno, char *real_included_file_name, int real_included_file_name_size) +static void make_fn(char *fn, size_t fn_size, const char *file, const char *configfile); + +struct ast_config_include *ast_include_new(struct ast_config *conf, const char *from_file, const char *included_file, int include_type, const char *exec_file, int from_lineno, char *real_included_file_name, int real_included_file_name_size) { /* a file should be included ONCE. Otherwise, if one of the instances is changed, * then all be changed. -- how do we know to include it? -- Handling modified * instances is possible, I'd have * to create a new master for each instance. */ struct ast_config_include *inc; + struct config_included_file *inc_file; struct stat statbuf; + char fn[PATH_MAX]; inc = ast_include_find(conf, included_file); if (inc) { @@ -348,7 +377,7 @@ struct ast_config_include *ast_include_new(struct ast_config *conf, const char * inc->inclusion_count++; snprintf(real_included_file_name, real_included_file_name_size, "%s~~%d", included_file, inc->inclusion_count); } while (stat(real_included_file_name, &statbuf) == 0); - ast_log(LOG_WARNING,"'%s', line %d: Same File included more than once! This data will be saved in %s if saved back to disk.\n", from_file, from_lineno, real_included_file_name); + ast_log(LOG_WARNING, "'%s', line %d: Same File included more than once! This data will be saved in %s if saved back to disk.\n", from_file, from_lineno, real_included_file_name); } else *real_included_file_name = 0; @@ -357,26 +386,82 @@ struct ast_config_include *ast_include_new(struct ast_config *conf, const char * return NULL; } inc->include_location_file = ast_strdup(from_file); + if (!inc->include_location_file) { + ast_includes_destroy(inc); + return NULL; + } inc->include_location_lineno = from_lineno; - if (!ast_strlen_zero(real_included_file_name)) + if (!ast_strlen_zero(real_included_file_name)) { inc->included_file = ast_strdup(real_included_file_name); - else + } else { inc->included_file = ast_strdup(included_file); - - inc->exec = is_exec; - if (is_exec) - inc->exec_file = ast_strdup(exec_file); - - if (!inc->include_location_file - || !inc->included_file - || (is_exec && !inc->exec_file)) { + } + if (!inc->included_file) { ast_includes_destroy(inc); return NULL; } + inc->include_type = include_type; + + make_fn(fn, sizeof(fn), inc->included_file, from_file); + switch (inc->include_type) { + case CONFIG_STATEMENT_INCLUDE: + inc_file = ast_calloc(1, sizeof(*inc_file) + strlen(fn) + 1); + if (!inc_file) { + ast_includes_destroy(inc); + return NULL; + } + strcpy(inc_file->path, fn); /* safe */ + AST_LIST_INSERT_TAIL(&inc->included_files, inc_file, next); + break; + case CONFIG_STATEMENT_TRYINCLUDE: + { + int glob_ret; + glob_t globbuf; + + /* add files that match the pattern */ + globbuf.gl_offs = 0; + glob_ret = glob(fn, MY_GLOB_FLAGS, NULL, &globbuf); + if (glob_ret == 0) { + int i; + + for (i = 0; i < globbuf.gl_pathc; i++) { + char *matched_path = globbuf.gl_pathv[i]; + + if (access(matched_path, F_OK) != 0) { + /* skip #tryinclude paths that do not already exist. */ + continue; + } + + inc_file = ast_calloc(1, sizeof(*inc_file) + strlen(matched_path) + 1); + if (!inc_file) { + ast_includes_destroy(inc); + globfree(&globbuf); + return NULL; + } + strcpy(inc_file->path, matched_path); /* safe */ + AST_LIST_INSERT_TAIL(&inc->included_files, inc_file, next); + } + } + globfree(&globbuf); + break; + } + case CONFIG_STATEMENT_EXEC: + inc->exec_file = ast_strdup(exec_file); + if (!inc->exec_file) { + ast_includes_destroy(inc); + return NULL; + } + break; + } + /* attach this new struct to the conf struct */ - inc->next = conf->includes; - conf->includes = inc; + if (conf->includes_root && conf->includes_last) { + conf->includes_last->next = inc; + } else { + conf->includes_root = inc; + } + conf->includes_last = inc; return inc; } @@ -402,8 +487,8 @@ void ast_include_rename(struct ast_config *conf, const char *from_file, const ch /* file names are on categories, includes (of course), and on variables. So, * traverse all this and swap names */ - for (incl = conf->includes; incl; incl=incl->next) { - if (strcmp(incl->include_location_file,from_file) == 0) { + for (incl = conf->includes_root; incl; incl = incl->next) { + if (strcmp(incl->include_location_file, from_file) == 0) { if (from_len >= to_len) strcpy(incl->include_location_file, to_file); else { @@ -421,7 +506,7 @@ void ast_include_rename(struct ast_config *conf, const char *from_file, const ch struct ast_variable *v; struct ast_variable *new_var; - if (strcmp(cat->file,from_file) == 0) { + if (strcmp(cat->file, from_file) == 0) { if (from_len >= to_len) strcpy(cat->file, to_file); else { @@ -476,8 +561,8 @@ void ast_include_rename(struct ast_config *conf, const char *from_file, const ch struct ast_config_include *ast_include_find(struct ast_config *conf, const char *included_file) { struct ast_config_include *x; - for (x=conf->includes;x;x=x->next) { - if (strcmp(x->included_file,included_file) == 0) + for (x = conf->includes_root; x; x = x->next) { + if (strcmp(x->included_file, included_file) == 0) return x; } return 0; @@ -1230,16 +1315,34 @@ void ast_category_destroy(struct ast_category *cat) ast_free(cat); } +static void config_included_files_destroy(struct ast_config_include *incl) +{ + struct config_included_file *x; + + while ((x = AST_LIST_REMOVE_HEAD(&incl->included_files, next))) { + ast_free(x); + } +} + +static void ast_include_destroy(struct ast_config_include *incl) +{ + ast_free(incl->include_location_file); + ast_free(incl->exec_file); + ast_free(incl->included_file); + config_included_files_destroy(incl); + ast_comment_destroy(&incl->precomments); + ast_comment_destroy(&incl->sameline); + ast_comment_destroy(&incl->trailing); + ast_free(incl); +} + static void ast_includes_destroy(struct ast_config_include *incls) { - struct ast_config_include *incl,*inclnext; + struct ast_config_include *incl, *inclnext; - for (incl=incls; incl; incl = inclnext) { + for (incl = incls; incl; incl = inclnext) { inclnext = incl->next; - ast_free(incl->include_location_file); - ast_free(incl->exec_file); - ast_free(incl->included_file); - ast_free(incl); + ast_include_destroy(incl); } } @@ -1346,7 +1449,7 @@ void ast_config_sort_categories(struct ast_config *config, int descending, } else if (qsize == 0 || !q) { /* q is empty; e must come from p. */ e = p; p = p->next; psize--; - } else if ((comparator(p,q) * descending) <= 0) { + } else if ((comparator(p, q) * descending) <= 0) { /* First element of p is lower (or same) e must come from p. */ e = p; p = p->next; @@ -1631,7 +1734,7 @@ void ast_config_destroy(struct ast_config *cfg) if (!cfg) return; - ast_includes_destroy(cfg->includes); + ast_includes_destroy(cfg->includes_root); cat = cfg->root; while (cat) { @@ -1923,7 +2026,7 @@ static int handle_include_exec(const char *command, const char *output_file) /*! \brief parse one line in the configuration. * \verbatim * We can have a category header [foo](...) - * a directive #include / #exec + * a directive #include, #tryinclude, #exec * or a regular line name = value * \endverbatim */ @@ -1932,7 +2035,10 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat, struct ast_str *comment_buffer, struct ast_str *lline_buffer, const char *suggested_include_file, - struct ast_category **last_cat, struct ast_variable **last_var, const char *who_asked) + struct ast_category **last_cat, + struct ast_variable **last_var, + struct ast_config_include **last_inc, + const char *who_asked) { char *c; char *cur = buf; @@ -1971,12 +2077,11 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat, (*cat)->lineno = lineno; /* add comments */ - if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) + if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) { newcat->precomments = ALLOC_COMMENT(comment_buffer); - if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) newcat->sameline = ALLOC_COMMENT(lline_buffer); - if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) CB_RESET(comment_buffer, lline_buffer); + } /* If there are options or categories to inherit from, process them now */ if (c) { @@ -2039,16 +2144,18 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat, * may be in another file or it already has trailing comments * that we would then leak. */ - *last_var = NULL; *last_cat = newcat; + *last_var = NULL; + *last_inc = NULL; if (newcat) { ast_category_append(cfg, newcat); } - } else if (cur[0] == '#') { /* A directive - #include or #exec */ + } else if (cur[0] == '#') { /* A directive - #include, #tryinclude, or #exec */ char *cur2; char real_inclusion_name[256]; - int do_include = 0; /* otherwise, it is exec */ - int try_include = 0; + int include_type; + struct ast_config_include *newinclude; + struct ast_config *result; cur++; c = cur; @@ -2067,15 +2174,15 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat, c = NULL; } if (!strcasecmp(cur, "include")) { - do_include = 1; + include_type = CONFIG_STATEMENT_INCLUDE; } else if (!strcasecmp(cur, "tryinclude")) { - do_include = 1; - try_include = 1; + include_type = CONFIG_STATEMENT_TRYINCLUDE; } else if (!strcasecmp(cur, "exec")) { if (!ast_opt_exec_includes) { ast_log(LOG_WARNING, "Cannot perform #exec unless execincludes option is enabled in asterisk.conf (options section)!\n"); return 0; /* XXX is this correct ? or we should return -1 ? */ } + include_type = CONFIG_STATEMENT_EXEC; } else { ast_log(LOG_WARNING, "Unknown directive '#%s' at line %d of %s\n", cur, lineno, configfile); return 0; /* XXX is this correct ? or we should return -1 ? */ @@ -2083,8 +2190,8 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat, if (c == NULL) { ast_log(LOG_WARNING, "Directive '#%s' needs an argument (%s) at line %d of %s\n", - do_include ? "include / tryinclude" : "exec", - do_include ? "filename" : "/path/to/executable", + cur, + (include_type != CONFIG_STATEMENT_EXEC) ? "filename" : "/path/to/executable", lineno, configfile); return 0; /* XXX is this correct ? or we should return -1 ? */ @@ -2106,9 +2213,9 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat, } cur2 = cur; - /* #exec - We create a tmp file, then we #include it, then we delete it. */ - if (!do_include) { + if (include_type == CONFIG_STATEMENT_EXEC) { + /* #exec + We create a tmp file, then we #include it, then we delete it. */ struct timeval now = ast_tvnow(); if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) @@ -2125,17 +2232,35 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat, } /* A #include */ /* record this inclusion */ - ast_include_new(cfg, cfg->include_level == 1 ? "" : configfile, cur, !do_include, cur2, lineno, real_inclusion_name, sizeof(real_inclusion_name)); + newinclude = ast_include_new(cfg, cfg->include_level == 1 ? "" : configfile, cur, include_type, cur2, lineno, real_inclusion_name, sizeof(real_inclusion_name)); + if (!newinclude) { + if (!ast_strlen_zero(exec_file)) { + unlink(exec_file); + } + return -1; + } - do_include = ast_config_internal_load(cur, cfg, flags, real_inclusion_name, who_asked) ? 1 : 0; - if (!ast_strlen_zero(exec_file)) + result = ast_config_internal_load(cur, cfg, flags, real_inclusion_name, who_asked); + if (!ast_strlen_zero(exec_file)) { unlink(exec_file); - if (!do_include && !try_include) { + } + if (!result && (include_type != CONFIG_STATEMENT_TRYINCLUDE)) { ast_log(LOG_ERROR, "The file '%s' was listed as a #include but it does not exist.\n", cur); + ast_include_destroy(newinclude); return -1; } - /* XXX otherwise what ? the default return is 0 anyways */ + /* add comments */ + if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) { + newinclude->precomments = ALLOC_COMMENT(comment_buffer); + newinclude->sameline = ALLOC_COMMENT(lline_buffer); + CB_RESET(comment_buffer, lline_buffer); + } + + /* for now, any end-of-file (trailing) comments are after this #include */ + *last_cat = NULL; + *last_var = NULL; + *last_inc = newinclude; } else { /* Just a line (variable = value) */ int object = 0; @@ -2203,17 +2328,16 @@ set_new_variable: v->object = object; *last_cat = NULL; *last_var = v; + *last_inc = NULL; /* Put and reset comments */ v->blanklines = 0; ast_variable_append(*cat, v); /* add comments */ - if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) + if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) { v->precomments = ALLOC_COMMENT(comment_buffer); - if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) v->sameline = ALLOC_COMMENT(lline_buffer); - if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) CB_RESET(comment_buffer, lline_buffer); - + } } else { return -1; } @@ -2241,8 +2365,9 @@ static struct ast_config *config_text_file_load(const char *database, const char struct stat statbuf; struct cache_file_mtime *cfmtime = NULL; struct cache_file_include *cfinclude; - struct ast_variable *last_var = NULL; struct ast_category *last_cat = NULL; + struct ast_variable *last_var = NULL; + struct ast_config_include *last_inc = NULL; /*! Growable string buffer */ struct ast_str *comment_buffer = NULL; /*!< this will be a comment collector.*/ struct ast_str *lline_buffer = NULL; /*!< A buffer for stuff behind the ; */ @@ -2296,7 +2421,7 @@ static struct ast_config *config_text_file_load(const char *database, const char ast_free(lline_buffer); return NULL; } - for (i=0; itrailing = ALLOC_COMMENT(comment_buffer); } + } else if (last_inc) { + if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) { + if (lline_buffer && ast_str_strlen(lline_buffer)) { + CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer)); /* add the current lline buffer to the comment buffer */ + ast_str_reset(lline_buffer); /* erase the lline buffer */ + } + last_inc->trailing = ALLOC_COMMENT(comment_buffer); + } } else { if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) { ast_debug(1, "Nothing to attach comments to, discarded: %s\n", ast_str_buffer(comment_buffer)); @@ -2553,7 +2684,7 @@ static struct ast_config *config_text_file_load(const char *database, const char fclose(f); } while (0); if (comment) { - ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment - 1]); + ast_log(LOG_WARNING, "Unterminated comment detected beginning on line %d\n", nest[comment - 1]); } if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) { break; @@ -2749,6 +2880,41 @@ static int is_writable(const char *fn) return 1; } +static void print_comment(FILE *f, struct ast_comment *comment) +{ + struct ast_comment *cmt; + + for (cmt = comment; cmt; cmt = cmt->next) { + const char *line = cmt->cmt; + char *nl; + + while ((nl = strchr(line, '\n')) != NULL) { + *nl = '\0'; + if (line[0] != ';' || line[1] != '!') { + /* if not one of our generated header comments */ + fprintf(f, "%s\n", line); + } + + line = nl + 1; + } + } +} + +static void print_include(FILE *f, struct ast_config_include *incl) +{ + switch (incl->include_type) { + case CONFIG_STATEMENT_INCLUDE: + fprintf(f, "#include \"%s\"\n", incl->included_file); + break; + case CONFIG_STATEMENT_TRYINCLUDE: + fprintf(f, "#tryinclude \"%s\"\n", incl->included_file); + break; + case CONFIG_STATEMENT_EXEC: + fprintf(f, "#exec \"%s\"\n", incl->exec_file); + break; + } +} + int ast_config_text_file_save2(const char *configfile, const struct ast_config *cfg, const char *generator, uint32_t flags) { FILE *f; @@ -2762,15 +2928,30 @@ int ast_config_text_file_save2(const char *configfile, const struct ast_config * struct inclfile *fi; /* Check all the files for write access before attempting to modify any of them */ - for (incl = cfg->includes; incl; incl = incl->next) { + for (incl = cfg->includes_root; incl; incl = incl->next) { /* reset all the output flags in case this isn't our first time saving this data */ incl->output = 0; - if (!incl->exec) { - /* now make sure we have write access to the include file or its parent directory */ - make_fn(fn, sizeof(fn), incl->included_file, configfile); - /* If the file itself doesn't exist, make sure we have write access to the directory */ - if (!is_writable(fn)) { + if (incl->include_type != CONFIG_STATEMENT_EXEC) { + struct config_included_file *included_file; + int ok = 1; + + /* Not a #exec. */ + /* For #include, make sure we have write access to the include file + file or its parent directory. If the file itself doesn't exist, + make sure we have write access to the directory. */ + /* For #tryinclude, we check the file(s) that match the pattern. */ + + AST_LIST_TRAVERSE(&incl->included_files, included_file, next) { + if (!is_writable(included_file->path)) { + /* the #tryinclude path exists but is not writable */ + ok = 0; + break; + } + } + + if (!ok) { + /* at least one include file is not writable */ return -1; } } @@ -2793,19 +2974,24 @@ int ast_config_text_file_save2(const char *configfile, const struct ast_config * /* go thru all the inclusions and make sure all the files involved (configfile plus all its inclusions) are all truncated to zero bytes and have that nice header*/ - for (incl = cfg->includes; incl; incl = incl->next) { - if (!incl->exec) { /* leave the execs alone -- we'll write out the #exec directives, but won't zero out the include files or exec files*/ - /* normally, fn is just set to incl->included_file, prepended with config dir if relative */ - fi = set_fn(fn, sizeof(fn), incl->included_file, configfile, fileset); - f = fopen(fn, "w"); - if (f) { - gen_header(f, configfile, fn, generator); - fclose(f); /* this should zero out the file */ - } else { - ast_log(LOG_ERROR, "Unable to write %s (%s)\n", fn, strerror(errno)); - } - if (fi) { - ao2_ref(fi, -1); + /* Note: for #exec, we write out the directives but don't zero out the exec files */ + for (incl = cfg->includes_root; incl; incl = incl->next) { + if (incl->include_type != CONFIG_STATEMENT_EXEC) { + struct config_included_file *included_file; + + AST_LIST_TRAVERSE(&incl->included_files, included_file, next) { + fi = set_fn(fn, sizeof(fn), included_file->path, configfile, fileset); + f = fopen(fn, "w"); + if (f) { + ast_verb(2, "Saving tryinclude '%s'\n", fn); + gen_header(f, configfile, fn, generator); + fclose(f); + } else { + ast_log(LOG_ERROR, "Unable to write %s (%s)\n", fn, strerror(errno)); + } + if (fi) { + ao2_ref(fi, -1); + } } } } @@ -2844,31 +3030,24 @@ int ast_config_text_file_save2(const char *configfile, const struct ast_config * } /* dump any includes that happen before this category header */ - for (incl=cfg->includes; incl; incl = incl->next) { - if (strcmp(incl->include_location_file, cat->file) == 0){ + for (incl = cfg->includes_root; incl; incl = incl->next) { + if (strcmp(incl->include_location_file, cat->file) == 0) { if (cat->lineno > incl->include_location_lineno && !incl->output) { - if (incl->exec) - fprintf(f,"#exec \"%s\"\n", incl->exec_file); - else - fprintf(f,"#include \"%s\"\n", incl->included_file); + /* add the precomments */ + insert_leading_blank_lines(f, fi, incl->precomments, incl->include_location_lineno); + print_comment(f, incl->precomments); + /* and the include */ + print_include(f, incl); incl->output = 1; } } } + /* dump any comments that happen before this category header */ insert_leading_blank_lines(f, fi, cat->precomments, cat->lineno); - /* Dump section with any appropriate comment */ - for (cmt = cat->precomments; cmt; cmt=cmt->next) { - char *cmtp = cmt->cmt; - while (cmtp && *cmtp == ';' && *(cmtp+1) == '!') { - char *cmtp2 = strchr(cmtp+1, '\n'); - if (cmtp2) - cmtp = cmtp2+1; - else cmtp = 0; - } - if (cmtp) - fprintf(f,"%s", cmtp); - } + print_comment(f, cat->precomments); + + /* dump the category header */ fprintf(f, "[%s]", cat->name); if (cat->ignored || !AST_LIST_EMPTY(&cat->template_instances)) { fprintf(f, "("); @@ -2881,23 +3060,24 @@ int ast_config_text_file_save2(const char *configfile, const struct ast_config * if (!AST_LIST_EMPTY(&cat->template_instances)) { struct ast_category_template_instance *x; AST_LIST_TRAVERSE(&cat->template_instances, x, next) { - fprintf(f,"%s",x->name); + fprintf(f, "%s",x->name); if (x != AST_LIST_LAST(&cat->template_instances)) - fprintf(f,","); + fprintf(f, ","); } } fprintf(f, ")"); } - for(cmt = cat->sameline; cmt; cmt=cmt->next) + for(cmt = cat->sameline; cmt; cmt = cmt->next) { - fprintf(f,"%s", cmt->cmt); + fprintf(f, "%s", cmt->cmt); } if (!cat->sameline) - fprintf(f,"\n"); - for (cmt = cat->trailing; cmt; cmt=cmt->next) { - if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!') - fprintf(f,"%s", cmt->cmt); - } + fprintf(f, "\n"); + + /* dump any trailing comments */ + print_comment(f, cat->trailing); + + /* and we're all done */ fclose(f); if (fi) { ao2_ref(fi, -1); @@ -2949,24 +3129,23 @@ int ast_config_text_file_save2(const char *configfile, const struct ast_config * return -1; } - /* dump any includes that happen before this category header */ - for (incl=cfg->includes; incl; incl = incl->next) { - if (strcmp(incl->include_location_file, var->file) == 0){ + /* dump any includes that happen before this variable */ + for (incl = cfg->includes_root; incl; incl = incl->next) { + if (strcmp(incl->include_location_file, var->file) == 0) { if (var->lineno > incl->include_location_lineno && !incl->output) { - if (incl->exec) - fprintf(f,"#exec \"%s\"\n", incl->exec_file); - else - fprintf(f,"#include \"%s\"\n", incl->included_file); + /* add the precomments */ + insert_leading_blank_lines(f, fi, incl->precomments, incl->include_location_lineno); + print_comment(f, incl->precomments); + /* and the include */ + print_include(f, incl); incl->output = 1; } } } + /* dump any comments that happen before this variable */ insert_leading_blank_lines(f, fi, var->precomments, var->lineno); - for (cmt = var->precomments; cmt; cmt=cmt->next) { - if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!') - fprintf(f,"%s", cmt->cmt); - } + print_comment(f, var->precomments); { /* Block for 'escaped' scope */ int escaped_len = 2 * strlen(var->value) + 1; @@ -2983,10 +3162,8 @@ int ast_config_text_file_save2(const char *configfile, const struct ast_config * } } - for (cmt = var->trailing; cmt; cmt=cmt->next) { - if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!') - fprintf(f,"%s", cmt->cmt); - } + /* dump any trailing comments */ + print_comment(f, var->trailing); if (var->blanklines) { blanklines = var->blanklines; while (blanklines--) @@ -3002,6 +3179,42 @@ int ast_config_text_file_save2(const char *configfile, const struct ast_config * } cat = cat->next; } + + + /* Now, for files with trailing #include/#exec statements, + we have to make sure every entry is output */ + for (incl = cfg->includes_root; incl; incl = incl->next) { + if (!incl->output) { + /* open the respective file */ + fi = set_fn(fn, sizeof(fn), incl->include_location_file, configfile, fileset); + f = fopen(fn, "a"); + if (!f) { + ast_debug(1, "Unable to open for writing: %s\n", fn); + ast_verb(2, "Unable to write %s (%s)\n", fn, strerror(errno)); + if (fi) { + ao2_ref(fi, -1); + } + ao2_ref(fileset, -1); + return -1; + } + + /* add the precomments */ + insert_leading_blank_lines(f, fi, incl->precomments, incl->include_location_lineno); + print_comment(f, incl->precomments); + /* and the include */ + print_include(f, incl); + /* and any trailing comments */ + print_comment(f, incl->trailing); + + incl->output = 1; + fclose(f); + if (fi) { + ao2_ref(fi, -1); + } + } + } + + ast_verb(2, "Saving '%s': saved\n", fn); } else { ast_debug(1, "Unable to open for writing: %s\n", fn); @@ -3013,35 +3226,6 @@ int ast_config_text_file_save2(const char *configfile, const struct ast_config * return -1; } - /* Now, for files with trailing #include/#exec statements, - we have to make sure every entry is output */ - for (incl=cfg->includes; incl; incl = incl->next) { - if (!incl->output) { - /* open the respective file */ - fi = set_fn(fn, sizeof(fn), incl->include_location_file, configfile, fileset); - f = fopen(fn, "a"); - if (!f) { - ast_debug(1, "Unable to open for writing: %s\n", fn); - ast_verb(2, "Unable to write %s (%s)\n", fn, strerror(errno)); - if (fi) { - ao2_ref(fi, -1); - } - ao2_ref(fileset, -1); - return -1; - } - - /* output the respective include */ - if (incl->exec) - fprintf(f,"#exec \"%s\"\n", incl->exec_file); - else - fprintf(f,"#include \"%s\"\n", incl->included_file); - fclose(f); - incl->output = 1; - if (fi) { - ao2_ref(fi, -1); - } - } - } ao2_ref(fileset, -1); /* this should destroy the hash container */ /* pass new configuration to any config hooks */ @@ -3393,7 +3577,7 @@ static int realtime_arguments_to_fields2(va_list ap, int skip, struct ast_variab * While this works on generic amd64 machines (2014), it doesn't on the * raspberry PI. The va_arg() manpage says: * - * If ap is passed to a function that uses va_arg(ap,type) then + * If ap is passed to a function that uses va_arg(ap, type) then * the value of ap is undefined after the return of that function. * * On the raspberry, ap seems to get reset after the call: the contents