Mon Apr 28 2014 10:05:41

Asterisk developer's documentation


config.c
Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2010, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Configuration File Parser
00022  *
00023  * \author Mark Spencer <markster@digium.com>
00024  *
00025  * Includes the Asterisk Realtime API - ARA
00026  * See http://wiki.asterisk.org
00027  */
00028 
00029 /*** MODULEINFO
00030    <support_level>core</support_level>
00031  ***/
00032 
00033 #include "asterisk.h"
00034 
00035 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 409916 $")
00036 
00037 #include "asterisk/paths.h"   /* use ast_config_AST_CONFIG_DIR */
00038 #include "asterisk/network.h" /* we do some sockaddr manipulation here */
00039 #include <time.h>
00040 #include <sys/stat.h>
00041 
00042 #include <math.h> /* HUGE_VAL */
00043 
00044 #define AST_INCLUDE_GLOB 1
00045 
00046 #include "asterisk/config.h"
00047 #include "asterisk/cli.h"
00048 #include "asterisk/lock.h"
00049 #include "asterisk/utils.h"
00050 #include "asterisk/channel.h"
00051 #include "asterisk/app.h"
00052 #include "asterisk/astobj2.h"
00053 #include "asterisk/strings.h" /* for the ast_str_*() API */
00054 #include "asterisk/netsock2.h"
00055 
00056 #define MAX_NESTED_COMMENTS 128
00057 #define COMMENT_START ";--"
00058 #define COMMENT_END "--;"
00059 #define COMMENT_META ';'
00060 #define COMMENT_TAG '-'
00061 
00062 /*!
00063  * Define the minimum filename space to reserve for each
00064  * ast_variable in case the filename is renamed later by
00065  * ast_include_rename().
00066  */
00067 #define MIN_VARIABLE_FNAME_SPACE 40
00068 
00069 static char *extconfig_conf = "extconfig.conf";
00070 
00071 
00072 /*! \brief Structure to keep comments for rewriting configuration files */
00073 struct ast_comment {
00074    struct ast_comment *next;
00075    /*! Comment body allocated after struct. */
00076    char cmt[0];
00077 };
00078 
00079 /*! \brief Hold the mtime for config files, so if we don't need to reread our config, don't. */
00080 struct cache_file_include {
00081    AST_LIST_ENTRY(cache_file_include) list;
00082    char include[0];
00083 };
00084 
00085 struct cache_file_mtime {
00086    AST_LIST_ENTRY(cache_file_mtime) list;
00087    AST_LIST_HEAD_NOLOCK(includes, cache_file_include) includes;
00088    unsigned int has_exec:1;
00089    /*! stat() file size */
00090    unsigned long stat_size;
00091    /*! stat() file modtime nanoseconds */
00092    unsigned long stat_mtime_nsec;
00093    /*! stat() file modtime seconds since epoc */
00094    time_t stat_mtime;
00095 
00096    /*! String stuffed in filename[] after the filename string. */
00097    const char *who_asked;
00098    /*! Filename and who_asked stuffed after it. */
00099    char filename[0];
00100 };
00101 
00102 /*! Cached file mtime list. */
00103 static AST_LIST_HEAD_STATIC(cfmtime_head, cache_file_mtime);
00104 
00105 static int init_appendbuf(void *data)
00106 {
00107    struct ast_str **str = data;
00108    *str = ast_str_create(16);
00109    return *str ? 0 : -1;
00110 }
00111 
00112 AST_THREADSTORAGE_CUSTOM(appendbuf, init_appendbuf, ast_free_ptr);
00113 
00114 /* comment buffers are better implemented using the ast_str_*() API */
00115 #define CB_SIZE 250  /* initial size of comment buffers */
00116 
00117 static void  CB_ADD(struct ast_str **cb, const char *str)
00118 {
00119    ast_str_append(cb, 0, "%s", str);
00120 }
00121 
00122 static void  CB_ADD_LEN(struct ast_str **cb, const char *str, int len)
00123 {
00124    char *s = ast_alloca(len + 1);
00125    ast_copy_string(s, str, len);
00126    ast_str_append(cb, 0, "%s", str);
00127 }
00128 
00129 static void CB_RESET(struct ast_str *cb, struct ast_str *llb)  
00130 { 
00131    if (cb) {
00132       ast_str_reset(cb);
00133    }
00134    if (llb) {
00135       ast_str_reset(llb);
00136    }
00137 }
00138 
00139 static struct ast_comment *ALLOC_COMMENT(struct ast_str *buffer)
00140 { 
00141    struct ast_comment *x = NULL;
00142    if (!buffer || !ast_str_strlen(buffer)) {
00143       return NULL;
00144    }
00145    if ((x = ast_calloc(1, sizeof(*x) + ast_str_strlen(buffer) + 1))) {
00146       strcpy(x->cmt, ast_str_buffer(buffer)); /* SAFE */
00147    }
00148    return x;
00149 }
00150 
00151 /* I need to keep track of each config file, and all its inclusions,
00152    so that we can track blank lines in each */
00153 
00154 struct inclfile {
00155    char *fname;
00156    int lineno;
00157 };
00158 
00159 static int hash_string(const void *obj, const int flags)
00160 {
00161    char *str = ((struct inclfile *) obj)->fname;
00162    int total;
00163 
00164    for (total = 0; *str; str++) {
00165       unsigned int tmp = total;
00166       total <<= 1; /* multiply by 2 */
00167       total += tmp; /* multiply by 3 */
00168       total <<= 2; /* multiply by 12 */
00169       total += tmp; /* multiply by 13 */
00170 
00171       total += ((unsigned int) (*str));
00172    }
00173    if (total < 0) {
00174       total = -total;
00175    }
00176    return total;
00177 }
00178 
00179 static int hashtab_compare_strings(void *a, void *b, int flags)
00180 {
00181    const struct inclfile *ae = a, *be = b;
00182    return !strcmp(ae->fname, be->fname) ? CMP_MATCH | CMP_STOP : 0;
00183 }
00184 
00185 static struct ast_config_map {
00186    struct ast_config_map *next;
00187    int priority;
00188    /*! Stored in stuff[] at struct end. */
00189    const char *name;
00190    /*! Stored in stuff[] at struct end. */
00191    const char *driver;
00192    /*! Stored in stuff[] at struct end. */
00193    const char *database;
00194    /*! Stored in stuff[] at struct end. */
00195    const char *table;
00196    /*! Contents of name, driver, database, and table in that order stuffed here. */
00197    char stuff[0];
00198 } *config_maps = NULL;
00199 
00200 AST_MUTEX_DEFINE_STATIC(config_lock);
00201 static struct ast_config_engine *config_engine_list;
00202 
00203 #define MAX_INCLUDE_LEVEL 10
00204 
00205 struct ast_category_template_instance {
00206    char name[80]; /* redundant? */
00207    const struct ast_category *inst;
00208    AST_LIST_ENTRY(ast_category_template_instance) next;
00209 };
00210 
00211 struct ast_category {
00212    char name[80];
00213    int ignored;         /*!< do not let user of the config see this category -- set by (!) after the category decl; a template */
00214    int include_level;
00215    /*!
00216     * \brief The file name from whence this declaration was read
00217     * \note Will never be NULL
00218     */
00219    char *file;
00220    int lineno;
00221    AST_LIST_HEAD_NOLOCK(template_instance_list, ast_category_template_instance) template_instances;
00222    struct ast_comment *precomments;
00223    struct ast_comment *sameline;
00224    struct ast_comment *trailing; /*!< the last object in the list will get assigned any trailing comments when EOF is hit */
00225    /*! First category variable in the list. */
00226    struct ast_variable *root;
00227    /*! Last category variable in the list. */
00228    struct ast_variable *last;
00229    /*! Next node in the list. */
00230    struct ast_category *next;
00231 };
00232 
00233 struct ast_config {
00234    /*! First config category in the list. */
00235    struct ast_category *root;
00236    /*! Last config category in the list. */
00237    struct ast_category *last;
00238    struct ast_category *current;
00239    struct ast_category *last_browse;     /*!< used to cache the last category supplied via category_browse */
00240    int include_level;
00241    int max_include_level;
00242    struct ast_config_include *includes;  /*!< a list of inclusions, which should describe the entire tree */
00243 };
00244 
00245 struct ast_config_include {
00246    /*!
00247     * \brief file name in which the include occurs
00248     * \note Will never be NULL
00249     */
00250    char *include_location_file;
00251    int  include_location_lineno;    /*!< lineno where include occurred */
00252    int  exec;                       /*!< set to non-zero if its a #exec statement */
00253    /*!
00254     * \brief if it's an exec, you'll have both the /var/tmp to read, and the original script
00255     * \note Will never be NULL if exec is non-zero
00256     */
00257    char *exec_file;
00258    /*!
00259     * \brief file name included
00260     * \note Will never be NULL
00261     */
00262    char *included_file;
00263    int inclusion_count;             /*!< if the file is included more than once, a running count thereof -- but, worry not,
00264                                          we explode the instances and will include those-- so all entries will be unique */
00265    int output;                      /*!< a flag to indicate if the inclusion has been output */
00266    struct ast_config_include *next; /*!< ptr to next inclusion in the list */
00267 };
00268 
00269 static void ast_variable_destroy(struct ast_variable *doomed);
00270 static void ast_includes_destroy(struct ast_config_include *incls);
00271 
00272 #ifdef MALLOC_DEBUG
00273 struct ast_variable *_ast_variable_new(const char *name, const char *value, const char *filename, const char *file, const char *func, int lineno)
00274 #else
00275 struct ast_variable *ast_variable_new(const char *name, const char *value, const char *filename)
00276 #endif
00277 {
00278    struct ast_variable *variable;
00279    int name_len = strlen(name) + 1;
00280    int val_len = strlen(value) + 1;
00281    int fn_len = strlen(filename) + 1;
00282 
00283    /* Ensure a minimum length in case the filename is changed later. */
00284    if (fn_len < MIN_VARIABLE_FNAME_SPACE) {
00285       fn_len = MIN_VARIABLE_FNAME_SPACE;
00286    }
00287 
00288    if (
00289 #ifdef MALLOC_DEBUG
00290       (variable = __ast_calloc(1, fn_len + name_len + val_len + sizeof(*variable), file, lineno, func))
00291 #else
00292       (variable = ast_calloc(1, fn_len + name_len + val_len + sizeof(*variable)))
00293 #endif
00294       ) {
00295       char *dst = variable->stuff;  /* writable space starts here */
00296 
00297       /* Put file first so ast_include_rename() can calculate space available. */
00298       variable->file = strcpy(dst, filename);
00299       dst += fn_len;
00300       variable->name = strcpy(dst, name);
00301       dst += name_len;
00302       variable->value = strcpy(dst, value);
00303    }
00304    return variable;
00305 }
00306 
00307 /*!
00308  * \internal
00309  * \brief Move the contents from the source to the destination variable.
00310  *
00311  * \param dst_var Destination variable node
00312  * \param src_var Source variable node
00313  *
00314  * \return Nothing
00315  */
00316 static void ast_variable_move(struct ast_variable *dst_var, struct ast_variable *src_var)
00317 {
00318    dst_var->lineno = src_var->lineno;
00319    dst_var->object = src_var->object;
00320    dst_var->blanklines = src_var->blanklines;
00321    dst_var->precomments = src_var->precomments;
00322    src_var->precomments = NULL;
00323    dst_var->sameline = src_var->sameline;
00324    src_var->sameline = NULL;
00325    dst_var->trailing = src_var->trailing;
00326    src_var->trailing = NULL;
00327 }
00328 
00329 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)
00330 {
00331    /* a file should be included ONCE. Otherwise, if one of the instances is changed,
00332     * then all be changed. -- how do we know to include it? -- Handling modified 
00333     * instances is possible, I'd have
00334     * to create a new master for each instance. */
00335    struct ast_config_include *inc;
00336    struct stat statbuf;
00337    
00338    inc = ast_include_find(conf, included_file);
00339    if (inc) {
00340       do {
00341          inc->inclusion_count++;
00342          snprintf(real_included_file_name, real_included_file_name_size, "%s~~%d", included_file, inc->inclusion_count);
00343       } while (stat(real_included_file_name, &statbuf) == 0);
00344       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);
00345    } else
00346       *real_included_file_name = 0;
00347    
00348    inc = ast_calloc(1,sizeof(struct ast_config_include));
00349    if (!inc) {
00350       return NULL;
00351    }
00352    inc->include_location_file = ast_strdup(from_file);
00353    inc->include_location_lineno = from_lineno;
00354    if (!ast_strlen_zero(real_included_file_name))
00355       inc->included_file = ast_strdup(real_included_file_name);
00356    else
00357       inc->included_file = ast_strdup(included_file);
00358    
00359    inc->exec = is_exec;
00360    if (is_exec)
00361       inc->exec_file = ast_strdup(exec_file);
00362 
00363    if (!inc->include_location_file
00364       || !inc->included_file
00365       || (is_exec && !inc->exec_file)) {
00366       ast_includes_destroy(inc);
00367       return NULL;
00368    }
00369 
00370    /* attach this new struct to the conf struct */
00371    inc->next = conf->includes;
00372    conf->includes = inc;
00373    
00374    return inc;
00375 }
00376 
00377 void ast_include_rename(struct ast_config *conf, const char *from_file, const char *to_file)
00378 {
00379    struct ast_config_include *incl;
00380    struct ast_category *cat;
00381    char *str;
00382 
00383    int from_len = strlen(from_file);
00384    int to_len = strlen(to_file);
00385    
00386    if (strcmp(from_file, to_file) == 0) /* no use wasting time if the name is the same */
00387       return;
00388    
00389    /* the manager code allows you to read in one config file, then
00390     * write it back out under a different name. But, the new arrangement
00391     * ties output lines to the file name. So, before you try to write
00392     * the config file to disk, better riffle thru the data and make sure
00393     * the file names are changed.
00394     */
00395    /* file names are on categories, includes (of course), and on variables. So,
00396     * traverse all this and swap names */
00397 
00398    for (incl = conf->includes; incl; incl=incl->next) {
00399       if (strcmp(incl->include_location_file,from_file) == 0) {
00400          if (from_len >= to_len)
00401             strcpy(incl->include_location_file, to_file);
00402          else {
00403             /* Keep the old filename if the allocation fails. */
00404             str = ast_strdup(to_file);
00405             if (str) {
00406                ast_free(incl->include_location_file);
00407                incl->include_location_file = str;
00408             }
00409          }
00410       }
00411    }
00412    for (cat = conf->root; cat; cat = cat->next) {
00413       struct ast_variable **prev;
00414       struct ast_variable *v;
00415       struct ast_variable *new_var;
00416 
00417       if (strcmp(cat->file,from_file) == 0) {
00418          if (from_len >= to_len)
00419             strcpy(cat->file, to_file);
00420          else {
00421             /* Keep the old filename if the allocation fails. */
00422             str = ast_strdup(to_file);
00423             if (str) {
00424                ast_free(cat->file);
00425                cat->file = str;
00426             }
00427          }
00428       }
00429       for (prev = &cat->root, v = cat->root; v; prev = &v->next, v = v->next) {
00430          if (strcmp(v->file, from_file)) {
00431             continue;
00432          }
00433 
00434          /*
00435           * Calculate actual space available.  The file string is
00436           * intentionally stuffed before the name string just so we can
00437           * do this.
00438           */
00439          if (to_len < v->name - v->file) {
00440             /* The new name will fit in the available space. */
00441             str = (char *) v->file;/* Stupid compiler complains about discarding qualifiers even though I used a cast. */
00442             strcpy(str, to_file);/* SAFE */
00443             continue;
00444          }
00445 
00446          /* Keep the old filename if the allocation fails. */
00447          new_var = ast_variable_new(v->name, v->value, to_file);
00448          if (!new_var) {
00449             continue;
00450          }
00451 
00452          /* Move items from the old list node to the replacement node. */
00453          ast_variable_move(new_var, v);
00454 
00455          /* Replace the old node in the list with the new node. */
00456          new_var->next = v->next;
00457          if (cat->last == v) {
00458             cat->last = new_var;
00459          }
00460          *prev = new_var;
00461 
00462          ast_variable_destroy(v);
00463 
00464          v = new_var;
00465       }
00466    }
00467 }
00468 
00469 struct ast_config_include *ast_include_find(struct ast_config *conf, const char *included_file)
00470 {
00471    struct ast_config_include *x;
00472    for (x=conf->includes;x;x=x->next) {
00473       if (strcmp(x->included_file,included_file) == 0)
00474          return x;
00475    }
00476    return 0;
00477 }
00478 
00479 
00480 void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
00481 {
00482    if (!variable)
00483       return;
00484    if (category->last)
00485       category->last->next = variable;
00486    else
00487       category->root = variable;
00488    category->last = variable;
00489    while (category->last->next)
00490       category->last = category->last->next;
00491 }
00492 
00493 void ast_variable_insert(struct ast_category *category, struct ast_variable *variable, const char *line)
00494 {
00495    struct ast_variable *cur = category->root;
00496    int lineno;
00497    int insertline;
00498 
00499    if (!variable || sscanf(line, "%30d", &insertline) != 1) {
00500       return;
00501    }
00502    if (!insertline) {
00503       variable->next = category->root;
00504       category->root = variable;
00505    } else {
00506       for (lineno = 1; lineno < insertline; lineno++) {
00507          cur = cur->next;
00508          if (!cur->next) {
00509             break;
00510          }
00511       }
00512       variable->next = cur->next;
00513       cur->next = variable;
00514    }
00515 }
00516 
00517 static void ast_comment_destroy(struct ast_comment **comment)
00518 {
00519    struct ast_comment *n, *p;
00520 
00521    for (p = *comment; p; p = n) {
00522       n = p->next;
00523       ast_free(p);
00524    }
00525 
00526    *comment = NULL;
00527 }
00528 
00529 static void ast_variable_destroy(struct ast_variable *doomed)
00530 {
00531    ast_comment_destroy(&doomed->precomments);
00532    ast_comment_destroy(&doomed->sameline);
00533    ast_comment_destroy(&doomed->trailing);
00534    ast_free(doomed);
00535 }
00536 
00537 struct ast_variable *ast_variables_dup(struct ast_variable *var)
00538 {
00539    struct ast_variable *cloned;
00540    struct ast_variable *tmp;
00541 
00542    if (!(cloned = ast_variable_new(var->name, var->value, var->file))) {
00543       return NULL;
00544    }
00545 
00546    tmp = cloned;
00547 
00548    while ((var = var->next)) {
00549       if (!(tmp->next = ast_variable_new(var->name, var->value, var->file))) {
00550          ast_variables_destroy(cloned);
00551          return NULL;
00552       }
00553       tmp = tmp->next;
00554    }
00555 
00556    return cloned;
00557 }
00558 
00559 void ast_variables_destroy(struct ast_variable *v)
00560 {
00561    struct ast_variable *vn;
00562 
00563    while (v) {
00564       vn = v;
00565       v = v->next;
00566       ast_variable_destroy(vn);
00567    }
00568 }
00569 
00570 struct ast_variable *ast_variable_browse(const struct ast_config *config, const char *category)
00571 {
00572    struct ast_category *cat = NULL;
00573 
00574    if (!category) {
00575       return NULL;
00576    }
00577 
00578    if (config->last_browse && (config->last_browse->name == category)) {
00579       cat = config->last_browse;
00580    } else {
00581       cat = ast_category_get(config, category);
00582    }
00583 
00584    return (cat) ? cat->root : NULL;
00585 }
00586 
00587 const char *ast_config_option(struct ast_config *cfg, const char *cat, const char *var)
00588 {
00589    const char *tmp;
00590    tmp = ast_variable_retrieve(cfg, cat, var);
00591    if (!tmp) {
00592       tmp = ast_variable_retrieve(cfg, "general", var);
00593    }
00594    return tmp;
00595 }
00596 
00597 
00598 const char *ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable)
00599 {
00600    struct ast_variable *v;
00601 
00602    if (category) {
00603       for (v = ast_variable_browse(config, category); v; v = v->next) {
00604          if (!strcasecmp(variable, v->name)) {
00605             return v->value;
00606          }
00607       }
00608    } else {
00609       struct ast_category *cat;
00610 
00611       for (cat = config->root; cat; cat = cat->next) {
00612          for (v = cat->root; v; v = v->next) {
00613             if (!strcasecmp(variable, v->name)) {
00614                return v->value;
00615             }
00616          }
00617       }
00618    }
00619 
00620    return NULL;
00621 }
00622 
00623 static struct ast_variable *variable_clone(const struct ast_variable *old)
00624 {
00625    struct ast_variable *new = ast_variable_new(old->name, old->value, old->file);
00626 
00627    if (new) {
00628       new->lineno = old->lineno;
00629       new->object = old->object;
00630       new->blanklines = old->blanklines;
00631       /* TODO: clone comments? */
00632    }
00633 
00634    return new;
00635 }
00636  
00637 static void move_variables(struct ast_category *old, struct ast_category *new)
00638 {
00639    struct ast_variable *var = old->root;
00640 
00641    old->root = NULL;
00642    /* we can just move the entire list in a single op */
00643    ast_variable_append(new, var);
00644 }
00645 
00646 struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno) 
00647 {
00648    struct ast_category *category;
00649 
00650    category = ast_calloc(1, sizeof(*category));
00651    if (!category) {
00652       return NULL;
00653    }
00654    category->file = ast_strdup(in_file);
00655    if (!category->file) {
00656       ast_category_destroy(category);
00657       return NULL;
00658    }
00659    ast_copy_string(category->name, name, sizeof(category->name));
00660    category->lineno = lineno; /* if you don't know the lineno, set it to 999999 or something real big */
00661    return category;
00662 }
00663 
00664 static struct ast_category *category_get(const struct ast_config *config, const char *category_name, int ignored)
00665 {
00666    struct ast_category *cat;
00667 
00668    /* try exact match first, then case-insensitive match */
00669    for (cat = config->root; cat; cat = cat->next) {
00670       if (cat->name == category_name && (ignored || !cat->ignored))
00671          return cat;
00672    }
00673 
00674    for (cat = config->root; cat; cat = cat->next) {
00675       if (!strcasecmp(cat->name, category_name) && (ignored || !cat->ignored))
00676          return cat;
00677    }
00678 
00679    return NULL;
00680 }
00681 
00682 struct ast_category *ast_category_get(const struct ast_config *config, const char *category_name)
00683 {
00684    return category_get(config, category_name, 0);
00685 }
00686 
00687 int ast_category_exist(const struct ast_config *config, const char *category_name)
00688 {
00689    return !!ast_category_get(config, category_name);
00690 }
00691 
00692 void ast_category_append(struct ast_config *config, struct ast_category *category)
00693 {
00694    if (config->last)
00695       config->last->next = category;
00696    else
00697       config->root = category;
00698    category->include_level = config->include_level;
00699    config->last = category;
00700    config->current = category;
00701 }
00702 
00703 void ast_category_insert(struct ast_config *config, struct ast_category *cat, const char *match)
00704 {
00705    struct ast_category *cur_category;
00706 
00707    if (!cat || !match)
00708       return;
00709    if (!strcasecmp(config->root->name, match)) {
00710       cat->next = config->root;
00711       config->root = cat;
00712       return;
00713    } 
00714    for (cur_category = config->root; cur_category; cur_category = cur_category->next) {
00715       if (!strcasecmp(cur_category->next->name, match)) {
00716          cat->next = cur_category->next;
00717          cur_category->next = cat;
00718          break;
00719       }
00720    }
00721 }
00722 
00723 static void ast_destroy_template_list(struct ast_category *cat)
00724 {
00725    struct ast_category_template_instance *x;
00726 
00727    while ((x = AST_LIST_REMOVE_HEAD(&cat->template_instances, next)))
00728       ast_free(x);
00729 }
00730 
00731 void ast_category_destroy(struct ast_category *cat)
00732 {
00733    ast_variables_destroy(cat->root);
00734    cat->root = NULL;
00735    cat->last = NULL;
00736    ast_comment_destroy(&cat->precomments);
00737    ast_comment_destroy(&cat->sameline);
00738    ast_comment_destroy(&cat->trailing);
00739    ast_destroy_template_list(cat);
00740    ast_free(cat->file);
00741    ast_free(cat);
00742 }
00743 
00744 static void ast_includes_destroy(struct ast_config_include *incls)
00745 {
00746    struct ast_config_include *incl,*inclnext;
00747    
00748    for (incl=incls; incl; incl = inclnext) {
00749       inclnext = incl->next;
00750       ast_free(incl->include_location_file);
00751       ast_free(incl->exec_file);
00752       ast_free(incl->included_file);
00753       ast_free(incl);
00754    }
00755 }
00756 
00757 static struct ast_category *next_available_category(struct ast_category *cat)
00758 {
00759    for (; cat && cat->ignored; cat = cat->next);
00760 
00761    return cat;
00762 }
00763 
00764 /*! return the first var of a category */
00765 struct ast_variable *ast_category_first(struct ast_category *cat)
00766 {
00767    return (cat) ? cat->root : NULL;
00768 }
00769 
00770 struct ast_variable *ast_category_root(struct ast_config *config, char *cat)
00771 {
00772    struct ast_category *category = ast_category_get(config, cat);
00773 
00774    if (category)
00775       return category->root;
00776    return NULL;
00777 }
00778 
00779 char *ast_category_browse(struct ast_config *config, const char *prev)
00780 {  
00781    struct ast_category *cat;
00782 
00783    if (!prev) {
00784       /* First time browse. */
00785       cat = config->root;
00786    } else if (config->last_browse && (config->last_browse->name == prev)) {
00787       /* Simple last browse found. */
00788       cat = config->last_browse->next;
00789    } else {
00790       /*
00791        * Config changed since last browse.
00792        *
00793        * First try cheap last browse search. (Rebrowsing a different
00794        * previous category?)
00795        */
00796       for (cat = config->root; cat; cat = cat->next) {
00797          if (cat->name == prev) {
00798             /* Found it. */
00799             cat = cat->next;
00800             break;
00801          }
00802       }
00803       if (!cat) {
00804          /*
00805           * Have to do it the hard way. (Last category was deleted and
00806           * re-added?)
00807           */
00808          for (cat = config->root; cat; cat = cat->next) {
00809             if (!strcasecmp(cat->name, prev)) {
00810                /* Found it. */
00811                cat = cat->next;
00812                break;
00813             }
00814          }
00815       }
00816    }
00817    
00818    if (cat)
00819       cat = next_available_category(cat);
00820 
00821    config->last_browse = cat;
00822    return (cat) ? cat->name : NULL;
00823 }
00824 
00825 struct ast_variable *ast_category_detach_variables(struct ast_category *cat)
00826 {
00827    struct ast_variable *v;
00828 
00829    v = cat->root;
00830    cat->root = NULL;
00831    cat->last = NULL;
00832 
00833    return v;
00834 }
00835 
00836 void ast_category_rename(struct ast_category *cat, const char *name)
00837 {
00838    ast_copy_string(cat->name, name, sizeof(cat->name));
00839 }
00840 
00841 static void inherit_category(struct ast_category *new, const struct ast_category *base)
00842 {
00843    struct ast_variable *var;
00844    struct ast_category_template_instance *x;
00845 
00846    x = ast_calloc(1, sizeof(*x));
00847    if (!x) {
00848       return;
00849    }
00850    strcpy(x->name, base->name);
00851    x->inst = base;
00852    AST_LIST_INSERT_TAIL(&new->template_instances, x, next);
00853    for (var = base->root; var; var = var->next)
00854       ast_variable_append(new, variable_clone(var));
00855 }
00856 
00857 struct ast_config *ast_config_new(void) 
00858 {
00859    struct ast_config *config;
00860 
00861    if ((config = ast_calloc(1, sizeof(*config))))
00862       config->max_include_level = MAX_INCLUDE_LEVEL;
00863    return config;
00864 }
00865 
00866 int ast_variable_delete(struct ast_category *category, const char *variable, const char *match, const char *line)
00867 {
00868    struct ast_variable *cur, *prev=NULL, *curn;
00869    int res = -1;
00870    int num_item = 0;
00871    int req_item;
00872 
00873    req_item = -1;
00874    if (!ast_strlen_zero(line)) {
00875       /* Requesting to delete by item number. */
00876       if (sscanf(line, "%30d", &req_item) != 1
00877          || req_item < 0) {
00878          /* Invalid item number to delete. */
00879          return -1;
00880       }
00881    }
00882 
00883    prev = NULL;
00884    cur = category->root;
00885    while (cur) {
00886       curn = cur->next;
00887       /* Delete by item number or by variable name with optional value. */
00888       if ((0 <= req_item && num_item == req_item)
00889          || (req_item < 0 && !strcasecmp(cur->name, variable)
00890             && (ast_strlen_zero(match) || !strcasecmp(cur->value, match)))) {
00891          if (prev) {
00892             prev->next = cur->next;
00893             if (cur == category->last)
00894                category->last = prev;
00895          } else {
00896             category->root = cur->next;
00897             if (cur == category->last)
00898                category->last = NULL;
00899          }
00900          ast_variable_destroy(cur);
00901          res = 0;
00902       } else
00903          prev = cur;
00904 
00905       cur = curn;
00906       ++num_item;
00907    }
00908    return res;
00909 }
00910 
00911 int ast_variable_update(struct ast_category *category, const char *variable, 
00912                   const char *value, const char *match, unsigned int object)
00913 {
00914    struct ast_variable *cur, *prev=NULL, *newer=NULL;
00915 
00916    for (cur = category->root; cur; prev = cur, cur = cur->next) {
00917       if (strcasecmp(cur->name, variable) ||
00918          (!ast_strlen_zero(match) && strcasecmp(cur->value, match)))
00919          continue;
00920 
00921       if (!(newer = ast_variable_new(variable, value, cur->file)))
00922          return -1;
00923 
00924       ast_variable_move(newer, cur);
00925       newer->object = newer->object || object;
00926 
00927       /* Replace the old node in the list with the new node. */
00928       newer->next = cur->next;
00929       if (prev)
00930          prev->next = newer;
00931       else
00932          category->root = newer;
00933       if (category->last == cur)
00934          category->last = newer;
00935 
00936       ast_variable_destroy(cur);
00937 
00938       return 0;
00939    }
00940 
00941    /* Could not find variable to update */
00942    return -1;
00943 }
00944 
00945 int ast_category_delete(struct ast_config *cfg, const char *category)
00946 {
00947    struct ast_category *prev=NULL, *cat;
00948 
00949    cat = cfg->root;
00950    while (cat) {
00951       if (cat->name == category) {
00952          if (prev) {
00953             prev->next = cat->next;
00954             if (cat == cfg->last)
00955                cfg->last = prev;
00956          } else {
00957             cfg->root = cat->next;
00958             if (cat == cfg->last)
00959                cfg->last = NULL;
00960          }
00961          ast_category_destroy(cat);
00962          return 0;
00963       }
00964       prev = cat;
00965       cat = cat->next;
00966    }
00967 
00968    prev = NULL;
00969    cat = cfg->root;
00970    while (cat) {
00971       if (!strcasecmp(cat->name, category)) {
00972          if (prev) {
00973             prev->next = cat->next;
00974             if (cat == cfg->last)
00975                cfg->last = prev;
00976          } else {
00977             cfg->root = cat->next;
00978             if (cat == cfg->last)
00979                cfg->last = NULL;
00980          }
00981          ast_category_destroy(cat);
00982          return 0;
00983       }
00984       prev = cat;
00985       cat = cat->next;
00986    }
00987    return -1;
00988 }
00989 
00990 int ast_category_empty(struct ast_config *cfg, const char *category)
00991 {
00992    struct ast_category *cat;
00993 
00994    for (cat = cfg->root; cat; cat = cat->next) {
00995       if (!strcasecmp(cat->name, category))
00996          continue;
00997       ast_variables_destroy(cat->root);
00998       cat->root = NULL;
00999       cat->last = NULL;
01000       return 0;
01001    }
01002 
01003    return -1;
01004 }
01005 
01006 void ast_config_destroy(struct ast_config *cfg)
01007 {
01008    struct ast_category *cat, *catn;
01009 
01010    if (!cfg)
01011       return;
01012 
01013    ast_includes_destroy(cfg->includes);
01014 
01015    cat = cfg->root;
01016    while (cat) {
01017       catn = cat;
01018       cat = cat->next;
01019       ast_category_destroy(catn);
01020    }
01021    ast_free(cfg);
01022 }
01023 
01024 struct ast_category *ast_config_get_current_category(const struct ast_config *cfg)
01025 {
01026    return cfg->current;
01027 }
01028 
01029 void ast_config_set_current_category(struct ast_config *cfg, const struct ast_category *cat)
01030 {
01031    /* cast below is just to silence compiler warning about dropping "const" */
01032    cfg->current = (struct ast_category *) cat;
01033 }
01034 
01035 /*!
01036  * \internal
01037  * \brief Create a new cfmtime list node.
01038  *
01039  * \param filename Config filename caching.
01040  * \param who_asked Who wanted to know.
01041  *
01042  * \retval cfmtime New node on success.
01043  * \retval NULL on error.
01044  */
01045 static struct cache_file_mtime *cfmtime_new(const char *filename, const char *who_asked)
01046 {
01047    struct cache_file_mtime *cfmtime;
01048    char *dst;
01049 
01050    cfmtime = ast_calloc(1,
01051       sizeof(*cfmtime) + strlen(filename) + 1 + strlen(who_asked) + 1);
01052    if (!cfmtime) {
01053       return NULL;
01054    }
01055    dst = cfmtime->filename;   /* writable space starts here */
01056    strcpy(dst, filename);
01057    dst += strlen(dst) + 1;
01058    cfmtime->who_asked = strcpy(dst, who_asked);
01059 
01060    return cfmtime;
01061 }
01062 
01063 enum config_cache_attribute_enum {
01064    ATTRIBUTE_INCLUDE = 0,
01065    ATTRIBUTE_EXEC = 1,
01066 };
01067 
01068 /*!
01069  * \internal
01070  * \brief Clear the stat() data in the cached file modtime struct.
01071  *
01072  * \param cfmtime Cached file modtime.
01073  *
01074  * \return Nothing
01075  */
01076 static void cfmstat_clear(struct cache_file_mtime *cfmtime)
01077 {
01078    cfmtime->stat_size = 0;
01079    cfmtime->stat_mtime_nsec = 0;
01080    cfmtime->stat_mtime = 0;
01081 }
01082 
01083 /*!
01084  * \internal
01085  * \brief Save the stat() data to the cached file modtime struct.
01086  *
01087  * \param cfmtime Cached file modtime.
01088  * \param statbuf Buffer filled in by stat().
01089  *
01090  * \return Nothing
01091  */
01092 static void cfmstat_save(struct cache_file_mtime *cfmtime, struct stat *statbuf)
01093 {
01094    cfmtime->stat_size = statbuf->st_size;
01095 #if defined(HAVE_STRUCT_STAT_ST_MTIM)
01096    cfmtime->stat_mtime_nsec = statbuf->st_mtim.tv_nsec;
01097 #elif defined(HAVE_STRUCT_STAT_ST_MTIMENSEC)
01098    cfmtime->stat_mtime_nsec = statbuf->st_mtimensec;
01099 #elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
01100    cfmtime->stat_mtime_nsec = statbuf->st_mtimespec.tv_nsec;
01101 #else
01102    cfmtime->stat_mtime_nsec = 0;
01103 #endif
01104    cfmtime->stat_mtime = statbuf->st_mtime;
01105 }
01106 
01107 /*!
01108  * \internal
01109  * \brief Compare the stat() data with the cached file modtime struct.
01110  *
01111  * \param cfmtime Cached file modtime.
01112  * \param statbuf Buffer filled in by stat().
01113  *
01114  * \retval non-zero if different.
01115  */
01116 static int cfmstat_cmp(struct cache_file_mtime *cfmtime, struct stat *statbuf)
01117 {
01118    struct cache_file_mtime cfm_buf;
01119 
01120    cfmstat_save(&cfm_buf, statbuf);
01121 
01122    return cfmtime->stat_size != cfm_buf.stat_size
01123       || cfmtime->stat_mtime != cfm_buf.stat_mtime
01124       || cfmtime->stat_mtime_nsec != cfm_buf.stat_mtime_nsec;
01125 }
01126 
01127 static void config_cache_attribute(const char *configfile, enum config_cache_attribute_enum attrtype, const char *filename, const char *who_asked)
01128 {
01129    struct cache_file_mtime *cfmtime;
01130    struct cache_file_include *cfinclude;
01131    struct stat statbuf = { 0, };
01132 
01133    /* Find our cached entry for this configuration file */
01134    AST_LIST_LOCK(&cfmtime_head);
01135    AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
01136       if (!strcmp(cfmtime->filename, configfile) && !strcmp(cfmtime->who_asked, who_asked))
01137          break;
01138    }
01139    if (!cfmtime) {
01140       cfmtime = cfmtime_new(configfile, who_asked);
01141       if (!cfmtime) {
01142          AST_LIST_UNLOCK(&cfmtime_head);
01143          return;
01144       }
01145       /* Note that the file mtime is initialized to 0, i.e. 1970 */
01146       AST_LIST_INSERT_SORTALPHA(&cfmtime_head, cfmtime, list, filename);
01147    }
01148 
01149    if (stat(configfile, &statbuf)) {
01150       cfmstat_clear(cfmtime);
01151    } else {
01152       cfmstat_save(cfmtime, &statbuf);
01153    }
01154 
01155    switch (attrtype) {
01156    case ATTRIBUTE_INCLUDE:
01157       AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
01158          if (!strcmp(cfinclude->include, filename)) {
01159             AST_LIST_UNLOCK(&cfmtime_head);
01160             return;
01161          }
01162       }
01163       cfinclude = ast_calloc(1, sizeof(*cfinclude) + strlen(filename) + 1);
01164       if (!cfinclude) {
01165          AST_LIST_UNLOCK(&cfmtime_head);
01166          return;
01167       }
01168       strcpy(cfinclude->include, filename);
01169       AST_LIST_INSERT_TAIL(&cfmtime->includes, cfinclude, list);
01170       break;
01171    case ATTRIBUTE_EXEC:
01172       cfmtime->has_exec = 1;
01173       break;
01174    }
01175    AST_LIST_UNLOCK(&cfmtime_head);
01176 }
01177 
01178 /*! \brief parse one line in the configuration.
01179  * \verbatim
01180  * We can have a category header [foo](...)
01181  * a directive          #include / #exec
01182  * or a regular line       name = value
01183  * \endverbatim
01184  */
01185 static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
01186    char *buf, int lineno, const char *configfile, struct ast_flags flags,
01187    struct ast_str *comment_buffer,
01188    struct ast_str *lline_buffer,
01189    const char *suggested_include_file,
01190    struct ast_category **last_cat, struct ast_variable **last_var, const char *who_asked)
01191 {
01192    char *c;
01193    char *cur = buf;
01194    struct ast_variable *v;
01195    char cmd[512], exec_file[512];
01196 
01197    /* Actually parse the entry */
01198    if (cur[0] == '[') { /* A category header */
01199       /* format is one of the following:
01200        * [foo] define a new category named 'foo'
01201        * [foo](!) define a new template category named 'foo'
01202        * [foo](+) append to category 'foo', error if foo does not exist.
01203        * [foo](a) define a new category and inherit from template a.
01204        *    You can put a comma-separated list of templates and '!' and '+'
01205        *    between parentheses, with obvious meaning.
01206        */
01207       struct ast_category *newcat = NULL;
01208       char *catname;
01209 
01210       c = strchr(cur, ']');
01211       if (!c) {
01212          ast_log(LOG_WARNING, "parse error: no closing ']', line %d of %s\n", lineno, configfile);
01213          return -1;
01214       }
01215       *c++ = '\0';
01216       cur++;
01217       if (*c++ != '(')
01218          c = NULL;
01219       catname = cur;
01220       if (!(*cat = newcat = ast_category_new(catname,
01221             S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile),
01222             lineno))) {
01223          return -1;
01224       }
01225       (*cat)->lineno = lineno;
01226       *last_var = 0;
01227       *last_cat = newcat;
01228       
01229       /* add comments */
01230       if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
01231          newcat->precomments = ALLOC_COMMENT(comment_buffer);
01232       if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
01233          newcat->sameline = ALLOC_COMMENT(lline_buffer);
01234       if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
01235          CB_RESET(comment_buffer, lline_buffer);
01236       
01237       /* If there are options or categories to inherit from, process them now */
01238       if (c) {
01239          if (!(cur = strchr(c, ')'))) {
01240             ast_log(LOG_WARNING, "parse error: no closing ')', line %d of %s\n", lineno, configfile);
01241             return -1;
01242          }
01243          *cur = '\0';
01244          while ((cur = strsep(&c, ","))) {
01245             if (!strcasecmp(cur, "!")) {
01246                (*cat)->ignored = 1;
01247             } else if (!strcasecmp(cur, "+")) {
01248                *cat = category_get(cfg, catname, 1);
01249                if (!(*cat)) {
01250                   if (newcat)
01251                      ast_category_destroy(newcat);
01252                   ast_log(LOG_WARNING, "Category addition requested, but category '%s' does not exist, line %d of %s\n", catname, lineno, configfile);
01253                   return -1;
01254                }
01255                if (newcat) {
01256                   move_variables(newcat, *cat);
01257                   ast_category_destroy(newcat);
01258                   newcat = NULL;
01259                }
01260             } else {
01261                struct ast_category *base;
01262             
01263                base = category_get(cfg, cur, 1);
01264                if (!base) {
01265                   ast_log(LOG_WARNING, "Inheritance requested, but category '%s' does not exist, line %d of %s\n", cur, lineno, configfile);
01266                   return -1;
01267                }
01268                inherit_category(*cat, base);
01269             }
01270          }
01271       }
01272       if (newcat)
01273          ast_category_append(cfg, *cat);
01274    } else if (cur[0] == '#') { /* A directive - #include or #exec */
01275       char *cur2;
01276       char real_inclusion_name[256];
01277       int do_include = 0;  /* otherwise, it is exec */
01278 
01279       cur++;
01280       c = cur;
01281       while (*c && (*c > 32)) {
01282          c++;
01283       }
01284 
01285       if (*c) {
01286          *c = '\0';
01287          /* Find real argument */
01288          c = ast_strip(c + 1);
01289          if (!(*c)) {
01290             c = NULL;
01291          }
01292       } else {
01293          c = NULL;
01294       }
01295       if (!strcasecmp(cur, "include")) {
01296          do_include = 1;
01297       } else if (!strcasecmp(cur, "exec")) {
01298          if (!ast_opt_exec_includes) {
01299             ast_log(LOG_WARNING, "Cannot perform #exec unless execincludes option is enabled in asterisk.conf (options section)!\n");
01300             return 0;   /* XXX is this correct ? or we should return -1 ? */
01301          }
01302       } else {
01303          ast_log(LOG_WARNING, "Unknown directive '#%s' at line %d of %s\n", cur, lineno, configfile);
01304          return 0;   /* XXX is this correct ? or we should return -1 ? */
01305       }
01306 
01307       if (c == NULL) {
01308          ast_log(LOG_WARNING, "Directive '#%s' needs an argument (%s) at line %d of %s\n", 
01309                do_include ? "include" : "exec",
01310                do_include ? "filename" : "/path/to/executable",
01311                lineno,
01312                configfile);
01313          return 0;   /* XXX is this correct ? or we should return -1 ? */
01314       }
01315 
01316       cur = c;
01317       /* Strip off leading and trailing "'s and <>'s */
01318       /* Dequote */
01319       if ((*c == '"') || (*c == '<')) {
01320          char quote_char = *c;
01321          if (quote_char == '<') {
01322             quote_char = '>';
01323          }
01324 
01325          if (*(c + strlen(c) - 1) == quote_char) {
01326             cur++;
01327             *(c + strlen(c) - 1) = '\0';
01328          }
01329       }
01330       cur2 = cur;
01331 
01332       /* #exec </path/to/executable>
01333          We create a tmp file, then we #include it, then we delete it. */
01334       if (!do_include) {
01335          struct timeval now = ast_tvnow();
01336          if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
01337             config_cache_attribute(configfile, ATTRIBUTE_EXEC, NULL, who_asked);
01338          snprintf(exec_file, sizeof(exec_file), "/var/tmp/exec.%d%d.%ld", (int)now.tv_sec, (int)now.tv_usec, (long)pthread_self());
01339          snprintf(cmd, sizeof(cmd), "%s > %s 2>&1", cur, exec_file);
01340          ast_safe_system(cmd);
01341          cur = exec_file;
01342       } else {
01343          if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
01344             config_cache_attribute(configfile, ATTRIBUTE_INCLUDE, cur, who_asked);
01345          exec_file[0] = '\0';
01346       }
01347       /* A #include */
01348       /* record this inclusion */
01349       ast_include_new(cfg, cfg->include_level == 1 ? "" : configfile, cur, !do_include, cur2, lineno, real_inclusion_name, sizeof(real_inclusion_name));
01350 
01351       do_include = ast_config_internal_load(cur, cfg, flags, real_inclusion_name, who_asked) ? 1 : 0;
01352       if (!ast_strlen_zero(exec_file))
01353          unlink(exec_file);
01354       if (!do_include) {
01355          ast_log(LOG_ERROR, "The file '%s' was listed as a #include but it does not exist.\n", cur);
01356          return -1;
01357       }
01358       /* XXX otherwise what ? the default return is 0 anyways */
01359 
01360    } else {
01361       /* Just a line (variable = value) */
01362       int object = 0;
01363       if (!(*cat)) {
01364          ast_log(LOG_WARNING,
01365             "parse error: No category context for line %d of %s\n", lineno, configfile);
01366          return -1;
01367       }
01368       c = strchr(cur, '=');
01369 
01370       if (c && c > cur && (*(c - 1) == '+')) {
01371          struct ast_variable *var, *replace = NULL;
01372          struct ast_str **str = ast_threadstorage_get(&appendbuf, sizeof(*str));
01373 
01374          if (!str || !*str) {
01375             return -1;
01376          }
01377 
01378          *(c - 1) = '\0';
01379          c++;
01380          cur = ast_strip(cur);
01381 
01382          /* Must iterate through category until we find last variable of same name (since there could be multiple) */
01383          for (var = ast_category_first(*cat); var; var = var->next) {
01384             if (!strcmp(var->name, cur)) {
01385                replace = var;
01386             }
01387          }
01388 
01389          if (!replace) {
01390             /* Nothing to replace; just set a variable normally. */
01391             goto set_new_variable;
01392          }
01393 
01394          ast_str_set(str, 0, "%s", replace->value);
01395          ast_str_append(str, 0, "%s", c);
01396          ast_str_trim_blanks(*str);
01397          ast_variable_update(*cat, replace->name, ast_skip_blanks(ast_str_buffer(*str)), replace->value, object);
01398       } else if (c) {
01399          *c = 0;
01400          c++;
01401          /* Ignore > in => */
01402          if (*c== '>') {
01403             object = 1;
01404             c++;
01405          }
01406 set_new_variable:
01407          if ((v = ast_variable_new(ast_strip(cur), ast_strip(c), S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile)))) {
01408             v->lineno = lineno;
01409             v->object = object;
01410             *last_cat = 0;
01411             *last_var = v;
01412             /* Put and reset comments */
01413             v->blanklines = 0;
01414             ast_variable_append(*cat, v);
01415             /* add comments */
01416             if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
01417                v->precomments = ALLOC_COMMENT(comment_buffer);
01418             if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
01419                v->sameline = ALLOC_COMMENT(lline_buffer);
01420             if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
01421                CB_RESET(comment_buffer, lline_buffer);
01422             
01423          } else {
01424             return -1;
01425          }
01426       } else {
01427          ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
01428       }
01429    }
01430    return 0;
01431 }
01432 
01433 static struct ast_config *config_text_file_load(const char *database, const char *table, const char *filename, struct ast_config *cfg, struct ast_flags flags, const char *suggested_include_file, const char *who_asked)
01434 {
01435    char fn[256];
01436 #if defined(LOW_MEMORY)
01437    char buf[512];
01438 #else
01439    char buf[8192];
01440 #endif
01441    char *new_buf, *comment_p, *process_buf;
01442    FILE *f;
01443    int lineno=0;
01444    int comment = 0, nest[MAX_NESTED_COMMENTS];
01445    struct ast_category *cat = NULL;
01446    int count = 0;
01447    struct stat statbuf;
01448    struct cache_file_mtime *cfmtime = NULL;
01449    struct cache_file_include *cfinclude;
01450    struct ast_variable *last_var = 0;
01451    struct ast_category *last_cat = 0;
01452    /*! Growable string buffer */
01453    struct ast_str *comment_buffer = NULL; /*!< this will be a comment collector.*/
01454    struct ast_str *lline_buffer = NULL;   /*!< A buffer for stuff behind the ; */
01455 
01456    if (cfg)
01457       cat = ast_config_get_current_category(cfg);
01458 
01459    if (filename[0] == '/') {
01460       ast_copy_string(fn, filename, sizeof(fn));
01461    } else {
01462       snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_CONFIG_DIR, filename);
01463    }
01464 
01465    if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
01466       comment_buffer = ast_str_create(CB_SIZE);
01467       if (comment_buffer)
01468          lline_buffer = ast_str_create(CB_SIZE);
01469       if (!lline_buffer) {
01470          ast_free(comment_buffer);
01471          ast_log(LOG_ERROR, "Failed to initialize the comment buffer!\n");
01472          return NULL;
01473       }
01474    }
01475 #ifdef AST_INCLUDE_GLOB
01476    {
01477       int glob_ret;
01478       glob_t globbuf;
01479       globbuf.gl_offs = 0; /* initialize it to silence gcc */
01480       glob_ret = glob(fn, MY_GLOB_FLAGS, NULL, &globbuf);
01481       if (glob_ret == GLOB_NOSPACE)
01482          ast_log(LOG_WARNING,
01483             "Glob Expansion of pattern '%s' failed: Not enough memory\n", fn);
01484       else if (glob_ret  == GLOB_ABORTED)
01485          ast_log(LOG_WARNING,
01486             "Glob Expansion of pattern '%s' failed: Read error\n", fn);
01487       else  {
01488          /* loop over expanded files */
01489          int i;
01490          for (i=0; i<globbuf.gl_pathc; i++) {
01491             ast_copy_string(fn, globbuf.gl_pathv[i], sizeof(fn));
01492 #endif
01493    /*
01494     * The following is not a loop, but just a convenient way to define a block
01495     * (using do { } while(0) ), and be able to exit from it with 'continue'
01496     * or 'break' in case of errors. Nice trick.
01497     */
01498    do {
01499       if (stat(fn, &statbuf))
01500          continue;
01501 
01502       if (!S_ISREG(statbuf.st_mode)) {
01503          ast_log(LOG_WARNING, "'%s' is not a regular file, ignoring\n", fn);
01504          continue;
01505       }
01506 
01507       if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
01508          /* Find our cached entry for this configuration file */
01509          AST_LIST_LOCK(&cfmtime_head);
01510          AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
01511             if (!strcmp(cfmtime->filename, fn) && !strcmp(cfmtime->who_asked, who_asked))
01512                break;
01513          }
01514          if (!cfmtime) {
01515             cfmtime = cfmtime_new(fn, who_asked);
01516             if (!cfmtime) {
01517                AST_LIST_UNLOCK(&cfmtime_head);
01518                continue;
01519             }
01520             /* Note that the file mtime is initialized to 0, i.e. 1970 */
01521             AST_LIST_INSERT_SORTALPHA(&cfmtime_head, cfmtime, list, filename);
01522          }
01523       }
01524 
01525       if (cfmtime
01526          && !cfmtime->has_exec
01527          && !cfmstat_cmp(cfmtime, &statbuf)
01528          && ast_test_flag(&flags, CONFIG_FLAG_FILEUNCHANGED)) {
01529          /* File is unchanged, what about the (cached) includes (if any)? */
01530          int unchanged = 1;
01531          AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
01532             /* We must glob here, because if we did not, then adding a file to globbed directory would
01533              * incorrectly cause no reload to be necessary. */
01534             char fn2[256];
01535 #ifdef AST_INCLUDE_GLOB
01536             int glob_return;
01537             glob_t glob_buf = { .gl_offs = 0 };
01538             glob_return = glob(cfinclude->include, MY_GLOB_FLAGS, NULL, &glob_buf);
01539             /* On error, we reparse */
01540             if (glob_return == GLOB_NOSPACE || glob_return  == GLOB_ABORTED)
01541                unchanged = 0;
01542             else  {
01543                /* loop over expanded files */
01544                int j;
01545                for (j = 0; j < glob_buf.gl_pathc; j++) {
01546                   ast_copy_string(fn2, glob_buf.gl_pathv[j], sizeof(fn2));
01547 #else
01548                   ast_copy_string(fn2, cfinclude->include);
01549 #endif
01550                   if (config_text_file_load(NULL, NULL, fn2, NULL, flags, "", who_asked) == NULL) {
01551                      /* that second-to-last field needs to be looked at in this case... TODO */
01552                      unchanged = 0;
01553                      /* One change is enough to short-circuit and reload the whole shebang */
01554                      break;
01555                   }
01556 #ifdef AST_INCLUDE_GLOB
01557                }
01558             }
01559 #endif
01560          }
01561 
01562          if (unchanged) {
01563             AST_LIST_UNLOCK(&cfmtime_head);
01564             ast_free(comment_buffer);
01565             ast_free(lline_buffer);
01566 #ifdef AST_INCLUDE_GLOB
01567             globfree(&globbuf);
01568 #endif
01569             return CONFIG_STATUS_FILEUNCHANGED;
01570          }
01571       }
01572       if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
01573          AST_LIST_UNLOCK(&cfmtime_head);
01574 
01575       /* If cfg is NULL, then we just want an answer */
01576       if (cfg == NULL) {
01577          ast_free(comment_buffer);
01578          ast_free(lline_buffer);
01579 #ifdef AST_INCLUDE_GLOB
01580             globfree(&globbuf);
01581 #endif
01582          return NULL;
01583       }
01584 
01585       if (cfmtime) {
01586          cfmstat_save(cfmtime, &statbuf);
01587       }
01588 
01589       ast_verb(2, "Parsing '%s': ", fn);
01590          fflush(stdout);
01591       if (!(f = fopen(fn, "r"))) {
01592          ast_debug(1, "No file to parse: %s\n", fn);
01593          ast_verb(2, "Not found (%s)\n", strerror(errno));
01594          continue;
01595       }
01596       count++;
01597       /* If we get to this point, then we're loading regardless */
01598       ast_clear_flag(&flags, CONFIG_FLAG_FILEUNCHANGED);
01599       ast_debug(1, "Parsing %s\n", fn);
01600       ast_verb(2, "Found\n");
01601       while (!feof(f)) {
01602          lineno++;
01603          if (fgets(buf, sizeof(buf), f)) {
01604             /* Skip lines that are too long */
01605             if (strlen(buf) == sizeof(buf) - 1 && buf[sizeof(buf) - 1] != '\n') {
01606                ast_log(LOG_WARNING, "Line %d too long, skipping. It begins with: %.32s...\n", lineno, buf);
01607                while (fgets(buf, sizeof(buf), f)) {
01608                   if (strlen(buf) != sizeof(buf) - 1 || buf[sizeof(buf) - 1] == '\n') {
01609                      break;
01610                   }
01611                }
01612                continue;
01613             }
01614 
01615             if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && lline_buffer && ast_str_strlen(lline_buffer)) {
01616                CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer));       /* add the current lline buffer to the comment buffer */
01617                ast_str_reset(lline_buffer);        /* erase the lline buffer */
01618             }
01619             
01620             new_buf = buf;
01621             if (comment) 
01622                process_buf = NULL;
01623             else
01624                process_buf = buf;
01625             
01626             if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer) && (ast_strlen_zero(buf) || strlen(buf) == strspn(buf," \t\n\r"))) {
01627                /* blank line? really? Can we add it to an existing comment and maybe preserve inter- and post- comment spacing? */
01628                CB_ADD(&comment_buffer, "\n");       /* add a newline to the comment buffer */
01629                continue; /* go get a new line, then */
01630             }
01631             
01632             while ((comment_p = strchr(new_buf, COMMENT_META))) {
01633                if ((comment_p > new_buf) && (*(comment_p - 1) == '\\')) {
01634                   /* Escaped semicolons aren't comments. */
01635                   new_buf = comment_p;
01636                   /* write over the \ and bring the null terminator with us */
01637                   memmove(comment_p - 1, comment_p, strlen(comment_p) + 1);
01638                } else if (comment_p[1] == COMMENT_TAG && comment_p[2] == COMMENT_TAG && (comment_p[3] != '-')) {
01639                   /* Meta-Comment start detected ";--" */
01640                   if (comment < MAX_NESTED_COMMENTS) {
01641                      *comment_p = '\0';
01642                      new_buf = comment_p + 3;
01643                      comment++;
01644                      nest[comment-1] = lineno;
01645                   } else {
01646                      ast_log(LOG_ERROR, "Maximum nest limit of %d reached.\n", MAX_NESTED_COMMENTS);
01647                   }
01648                } else if ((comment_p >= new_buf + 2) &&
01649                      (*(comment_p - 1) == COMMENT_TAG) &&
01650                      (*(comment_p - 2) == COMMENT_TAG)) {
01651                   /* Meta-Comment end detected */
01652                   comment--;
01653                   new_buf = comment_p + 1;
01654                   if (!comment) {
01655                      /* Back to non-comment now */
01656                      if (process_buf) {
01657                         /* Actually have to move what's left over the top, then continue */
01658                         char *oldptr;
01659                         oldptr = process_buf + strlen(process_buf);
01660                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
01661                            CB_ADD(&comment_buffer, ";");
01662                            CB_ADD_LEN(&comment_buffer, oldptr+1, new_buf-oldptr-1);
01663                         }
01664                         
01665                         memmove(oldptr, new_buf, strlen(new_buf) + 1);
01666                         new_buf = oldptr;
01667                      } else
01668                         process_buf = new_buf;
01669                   }
01670                } else {
01671                   if (!comment) {
01672                      /* If ; is found, and we are not nested in a comment, 
01673                         we immediately stop all comment processing */
01674                      if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
01675                         CB_ADD(&lline_buffer, comment_p);
01676                      }
01677                      *comment_p = '\0'; 
01678                      new_buf = comment_p;
01679                   } else
01680                      new_buf = comment_p + 1;
01681                }
01682             }
01683             if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment && !process_buf ) {
01684                CB_ADD(&comment_buffer, buf);  /* the whole line is a comment, store it */
01685             }
01686             
01687             if (process_buf) {
01688                char *buffer = ast_strip(process_buf);
01689                if (!ast_strlen_zero(buffer)) {
01690                   if (process_text_line(cfg, &cat, buffer, lineno, fn, flags, comment_buffer, lline_buffer, suggested_include_file, &last_cat, &last_var, who_asked)) {
01691                      cfg = CONFIG_STATUS_FILEINVALID;
01692                      break;
01693                   }
01694                }
01695             }
01696          }
01697       }
01698       /* end of file-- anything in a comment buffer? */
01699       if (last_cat) {
01700          if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
01701             if (lline_buffer && ast_str_strlen(lline_buffer)) {
01702                CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer));       /* add the current lline buffer to the comment buffer */
01703                ast_str_reset(lline_buffer);        /* erase the lline buffer */
01704             }
01705             last_cat->trailing = ALLOC_COMMENT(comment_buffer);
01706          }
01707       } else if (last_var) {
01708          if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
01709             if (lline_buffer && ast_str_strlen(lline_buffer)) {
01710                CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer));       /* add the current lline buffer to the comment buffer */
01711                ast_str_reset(lline_buffer);        /* erase the lline buffer */
01712             }
01713             last_var->trailing = ALLOC_COMMENT(comment_buffer);
01714          }
01715       } else {
01716          if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
01717             ast_debug(1, "Nothing to attach comments to, discarded: %s\n", ast_str_buffer(comment_buffer));
01718          }
01719       }
01720       if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
01721          CB_RESET(comment_buffer, lline_buffer);
01722 
01723       fclose(f);
01724    } while (0);
01725    if (comment) {
01726       ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment - 1]);
01727    }
01728 #ifdef AST_INCLUDE_GLOB
01729                if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
01730                   break;
01731                }
01732             }
01733             globfree(&globbuf);
01734          }
01735       }
01736 #endif
01737 
01738    if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
01739       ast_free(comment_buffer);
01740       ast_free(lline_buffer);
01741       comment_buffer = NULL;
01742       lline_buffer = NULL;
01743    }
01744 
01745    if (count == 0)
01746       return NULL;
01747 
01748    return cfg;
01749 }
01750 
01751 
01752 /* NOTE: categories and variables each have a file and lineno attribute. On a save operation, these are used to determine
01753    which file and line number to write out to. Thus, an entire hierarchy of config files (via #include statements) can be
01754    recreated. BUT, care must be taken to make sure that every cat and var has the proper file name stored, or you may
01755    be shocked and mystified as to why things are not showing up in the files! 
01756 
01757    Also, All #include/#exec statements are recorded in the "includes" LL in the ast_config structure. The file name
01758    and line number are stored for each include, plus the name of the file included, so that these statements may be
01759    included in the output files on a file_save operation. 
01760 
01761    The lineno's are really just for relative placement in the file. There is no attempt to make sure that blank lines
01762    are included to keep the lineno's the same between input and output. The lineno fields are used mainly to determine
01763    the position of the #include and #exec directives. So, blank lines tend to disappear from a read/rewrite operation,
01764    and a header gets added.
01765 
01766    vars and category heads are output in the order they are stored in the config file. So, if the software
01767    shuffles these at all, then the placement of #include directives might get a little mixed up, because the
01768    file/lineno data probably won't get changed.
01769 
01770 */
01771 
01772 static void gen_header(FILE *f1, const char *configfile, const char *fn, const char *generator)
01773 {
01774    char date[256]="";
01775    time_t t;
01776 
01777    time(&t);
01778    ast_copy_string(date, ctime(&t), sizeof(date));
01779 
01780    fprintf(f1, ";!\n");
01781    fprintf(f1, ";! Automatically generated configuration file\n");
01782    if (strcmp(configfile, fn))
01783       fprintf(f1, ";! Filename: %s (%s)\n", configfile, fn);
01784    else
01785       fprintf(f1, ";! Filename: %s\n", configfile);
01786    fprintf(f1, ";! Generator: %s\n", generator);
01787    fprintf(f1, ";! Creation Date: %s", date);
01788    fprintf(f1, ";!\n");
01789 }
01790 
01791 static void inclfile_destroy(void *obj)
01792 {
01793    const struct inclfile *o = obj;
01794 
01795    ast_free(o->fname);
01796 }
01797 
01798 
01799 static struct inclfile *set_fn(char *fn, int fn_size, const char *file, const char *configfile, struct ao2_container *fileset)
01800 {
01801    struct inclfile lookup;
01802    struct inclfile *fi;
01803 
01804    if (ast_strlen_zero(file)) {
01805       if (configfile[0] == '/')
01806          ast_copy_string(fn, configfile, fn_size);
01807       else
01808          snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, configfile);
01809    } else if (file[0] == '/')
01810       ast_copy_string(fn, file, fn_size);
01811    else
01812       snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, file);
01813    lookup.fname = fn;
01814    fi = ao2_find(fileset, &lookup, OBJ_POINTER);
01815    if (fi) {
01816       /* Found existing include file scratch pad. */
01817       return fi;
01818    }
01819 
01820    /* set up a file scratch pad */
01821    fi = ao2_alloc(sizeof(struct inclfile), inclfile_destroy);
01822    if (!fi) {
01823       /* Scratch pad creation failed. */
01824       return NULL;
01825    }
01826    fi->fname = ast_strdup(fn);
01827    if (!fi->fname) {
01828       /* Scratch pad creation failed. */
01829       ao2_ref(fi, -1);
01830       return NULL;
01831    }
01832    fi->lineno = 1;
01833 
01834    ao2_link(fileset, fi);
01835 
01836    return fi;
01837 }
01838 
01839 static int count_linefeeds(char *str)
01840 {
01841    int count = 0;
01842 
01843    while (*str) {
01844       if (*str =='\n')
01845          count++;
01846       str++;
01847    }
01848    return count;
01849 }
01850 
01851 static int count_linefeeds_in_comments(struct ast_comment *x)
01852 {
01853    int count = 0;
01854 
01855    while (x) {
01856       count += count_linefeeds(x->cmt);
01857       x = x->next;
01858    }
01859    return count;
01860 }
01861 
01862 static void insert_leading_blank_lines(FILE *fp, struct inclfile *fi, struct ast_comment *precomments, int lineno)
01863 {
01864    int precomment_lines;
01865    int i;
01866 
01867    if (!fi) {
01868       /* No file scratch pad object so insert no blank lines. */
01869       return;
01870    }
01871 
01872    precomment_lines = count_linefeeds_in_comments(precomments);
01873 
01874    /* I don't have to worry about those ;! comments, they are
01875       stored in the precomments, but not printed back out.
01876       I did have to make sure that comments following
01877       the ;! header comments were not also deleted in the process */
01878    if (lineno - precomment_lines - fi->lineno < 0) { /* insertions can mess up the line numbering and produce negative numbers that mess things up */
01879       return;
01880    } else if (lineno == 0) {
01881       /* Line replacements also mess things up */
01882       return;
01883    } else if (lineno - precomment_lines - fi->lineno < 5) {
01884       /* Only insert less than 5 blank lines; if anything more occurs,
01885        * it's probably due to context deletion. */
01886       for (i = fi->lineno; i < lineno - precomment_lines; i++) {
01887          fprintf(fp, "\n");
01888       }
01889    } else {
01890       /* Deletion occurred - insert a single blank line, for separation of
01891        * contexts. */
01892       fprintf(fp, "\n");
01893    }
01894  
01895    fi->lineno = lineno + 1; /* Advance the file lineno */
01896 }
01897 
01898 int config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
01899 {
01900    return ast_config_text_file_save(configfile, cfg, generator);
01901 }
01902 
01903 int ast_config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
01904 {
01905    FILE *f;
01906    char fn[PATH_MAX];
01907    struct ast_variable *var;
01908    struct ast_category *cat;
01909    struct ast_comment *cmt;
01910    struct ast_config_include *incl;
01911    int blanklines = 0;
01912    struct ao2_container *fileset;
01913    struct inclfile *fi;
01914 
01915    fileset = ao2_container_alloc(1023, hash_string, hashtab_compare_strings);
01916    if (!fileset) {
01917       /* Container creation failed. */
01918       return -1;
01919    }
01920 
01921    /* reset all the output flags, in case this isn't our first time saving this data */
01922    for (incl = cfg->includes; incl; incl = incl->next) {
01923       incl->output = 0;
01924    }
01925 
01926    /* go thru all the inclusions and make sure all the files involved (configfile plus all its inclusions)
01927       are all truncated to zero bytes and have that nice header*/
01928    for (incl = cfg->includes; incl; incl = incl->next) {
01929       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*/
01930          /* normally, fn is just set to incl->included_file, prepended with config dir if relative */
01931          fi = set_fn(fn, sizeof(fn), incl->included_file, configfile, fileset);
01932          f = fopen(fn, "w");
01933          if (f) {
01934             gen_header(f, configfile, fn, generator);
01935             fclose(f); /* this should zero out the file */
01936          } else {
01937             ast_debug(1, "Unable to open for writing: %s\n", fn);
01938             ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
01939          }
01940          if (fi) {
01941             ao2_ref(fi, -1);
01942          }
01943       }
01944    }
01945 
01946    /* just set fn to absolute ver of configfile */
01947    fi = set_fn(fn, sizeof(fn), 0, configfile, fileset);
01948    if (
01949 #ifdef __CYGWIN__
01950       (f = fopen(fn, "w+"))
01951 #else
01952       (f = fopen(fn, "w"))
01953 #endif
01954       ) {
01955       ast_verb(2, "Saving '%s': ", fn);
01956       gen_header(f, configfile, fn, generator);
01957       cat = cfg->root;
01958       fclose(f);
01959       if (fi) {
01960          ao2_ref(fi, -1);
01961       }
01962 
01963       /* from here out, we open each involved file and concat the stuff we need to add to the end and immediately close... */
01964       /* since each var, cat, and associated comments can come from any file, we have to be
01965          mobile, and open each file, print, and close it on an entry-by-entry basis */
01966 
01967       while (cat) {
01968          fi = set_fn(fn, sizeof(fn), cat->file, configfile, fileset);
01969          f = fopen(fn, "a");
01970          if (!f) {
01971             ast_debug(1, "Unable to open for writing: %s\n", fn);
01972             ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
01973             if (fi) {
01974                ao2_ref(fi, -1);
01975             }
01976             ao2_ref(fileset, -1);
01977             return -1;
01978          }
01979 
01980          /* dump any includes that happen before this category header */
01981          for (incl=cfg->includes; incl; incl = incl->next) {
01982             if (strcmp(incl->include_location_file, cat->file) == 0){
01983                if (cat->lineno > incl->include_location_lineno && !incl->output) {
01984                   if (incl->exec)
01985                      fprintf(f,"#exec \"%s\"\n", incl->exec_file);
01986                   else
01987                      fprintf(f,"#include \"%s\"\n", incl->included_file);
01988                   incl->output = 1;
01989                }
01990             }
01991          }
01992 
01993          insert_leading_blank_lines(f, fi, cat->precomments, cat->lineno);
01994          /* Dump section with any appropriate comment */
01995          for (cmt = cat->precomments; cmt; cmt=cmt->next) {
01996             char *cmtp = cmt->cmt;
01997             while (cmtp && *cmtp == ';' && *(cmtp+1) == '!') {
01998                char *cmtp2 = strchr(cmtp+1, '\n');
01999                if (cmtp2)
02000                   cmtp = cmtp2+1;
02001                else cmtp = 0;
02002             }
02003             if (cmtp)
02004                fprintf(f,"%s", cmtp);
02005          }
02006          fprintf(f, "[%s]", cat->name);
02007          if (cat->ignored || !AST_LIST_EMPTY(&cat->template_instances)) {
02008             fprintf(f, "(");
02009             if (cat->ignored) {
02010                fprintf(f, "!");
02011             }
02012             if (cat->ignored && !AST_LIST_EMPTY(&cat->template_instances)) {
02013                fprintf(f, ",");
02014             }
02015             if (!AST_LIST_EMPTY(&cat->template_instances)) {
02016                struct ast_category_template_instance *x;
02017                AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
02018                   fprintf(f,"%s",x->name);
02019                   if (x != AST_LIST_LAST(&cat->template_instances))
02020                      fprintf(f,",");
02021                }
02022             }
02023             fprintf(f, ")");
02024          }
02025          for(cmt = cat->sameline; cmt; cmt=cmt->next)
02026          {
02027             fprintf(f,"%s", cmt->cmt);
02028          }
02029          if (!cat->sameline)
02030             fprintf(f,"\n");
02031          for (cmt = cat->trailing; cmt; cmt=cmt->next) {
02032             if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
02033                fprintf(f,"%s", cmt->cmt);
02034          }
02035          fclose(f);
02036          if (fi) {
02037             ao2_ref(fi, -1);
02038          }
02039 
02040          var = cat->root;
02041          while (var) {
02042             struct ast_category_template_instance *x;
02043             int found = 0;
02044             AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
02045                struct ast_variable *v;
02046                for (v = x->inst->root; v; v = v->next) {
02047                   if (!strcasecmp(var->name, v->name) && !strcmp(var->value, v->value)) {
02048                      found = 1;
02049                      break;
02050                   }
02051                }
02052                if (found)
02053                   break;
02054             }
02055             if (found) {
02056                var = var->next;
02057                continue;
02058             }
02059             fi = set_fn(fn, sizeof(fn), var->file, configfile, fileset);
02060             f = fopen(fn, "a");
02061             if (!f) {
02062                ast_debug(1, "Unable to open for writing: %s\n", fn);
02063                ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
02064                if (fi) {
02065                   ao2_ref(fi, -1);
02066                }
02067                ao2_ref(fileset, -1);
02068                return -1;
02069             }
02070 
02071             /* dump any includes that happen before this category header */
02072             for (incl=cfg->includes; incl; incl = incl->next) {
02073                if (strcmp(incl->include_location_file, var->file) == 0){
02074                   if (var->lineno > incl->include_location_lineno && !incl->output) {
02075                      if (incl->exec)
02076                         fprintf(f,"#exec \"%s\"\n", incl->exec_file);
02077                      else
02078                         fprintf(f,"#include \"%s\"\n", incl->included_file);
02079                      incl->output = 1;
02080                   }
02081                }
02082             }
02083 
02084             insert_leading_blank_lines(f, fi, var->precomments, var->lineno);
02085             for (cmt = var->precomments; cmt; cmt=cmt->next) {
02086                if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
02087                   fprintf(f,"%s", cmt->cmt);
02088             }
02089             if (var->sameline)
02090                fprintf(f, "%s %s %s  %s", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt);
02091             else
02092                fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="), var->value);
02093             for (cmt = var->trailing; cmt; cmt=cmt->next) {
02094                if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
02095                   fprintf(f,"%s", cmt->cmt);
02096             }
02097             if (var->blanklines) {
02098                blanklines = var->blanklines;
02099                while (blanklines--)
02100                   fprintf(f, "\n");
02101             }
02102 
02103             fclose(f);
02104             if (fi) {
02105                ao2_ref(fi, -1);
02106             }
02107 
02108             var = var->next;
02109          }
02110          cat = cat->next;
02111       }
02112       if (!option_debug)
02113          ast_verb(2, "Saved\n");
02114    } else {
02115       ast_debug(1, "Unable to open for writing: %s\n", fn);
02116       ast_verb(2, "Unable to write (%s)", strerror(errno));
02117       if (fi) {
02118          ao2_ref(fi, -1);
02119       }
02120       ao2_ref(fileset, -1);
02121       return -1;
02122    }
02123 
02124    /* Now, for files with trailing #include/#exec statements,
02125       we have to make sure every entry is output */
02126    for (incl=cfg->includes; incl; incl = incl->next) {
02127       if (!incl->output) {
02128          /* open the respective file */
02129          fi = set_fn(fn, sizeof(fn), incl->include_location_file, configfile, fileset);
02130          f = fopen(fn, "a");
02131          if (!f) {
02132             ast_debug(1, "Unable to open for writing: %s\n", fn);
02133             ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
02134             if (fi) {
02135                ao2_ref(fi, -1);
02136             }
02137             ao2_ref(fileset, -1);
02138             return -1;
02139          }
02140 
02141          /* output the respective include */
02142          if (incl->exec)
02143             fprintf(f,"#exec \"%s\"\n", incl->exec_file);
02144          else
02145             fprintf(f,"#include \"%s\"\n", incl->included_file);
02146          fclose(f);
02147          incl->output = 1;
02148          if (fi) {
02149             ao2_ref(fi, -1);
02150          }
02151       }
02152    }
02153    ao2_ref(fileset, -1); /* this should destroy the hash container */
02154 
02155    return 0;
02156 }
02157 
02158 static void clear_config_maps(void) 
02159 {
02160    struct ast_config_map *map;
02161 
02162    ast_mutex_lock(&config_lock);
02163 
02164    while (config_maps) {
02165       map = config_maps;
02166       config_maps = config_maps->next;
02167       ast_free(map);
02168    }
02169       
02170    ast_mutex_unlock(&config_lock);
02171 }
02172 
02173 static int append_mapping(const char *name, const char *driver, const char *database, const char *table, int priority)
02174 {
02175    struct ast_config_map *map;
02176    char *dst;
02177    int length;
02178 
02179    length = sizeof(*map);
02180    length += strlen(name) + 1;
02181    length += strlen(driver) + 1;
02182    length += strlen(database) + 1;
02183    if (table)
02184       length += strlen(table) + 1;
02185 
02186    if (!(map = ast_calloc(1, length)))
02187       return -1;
02188 
02189    dst = map->stuff; /* writable space starts here */
02190    map->name = strcpy(dst, name);
02191    dst += strlen(dst) + 1;
02192    map->driver = strcpy(dst, driver);
02193    dst += strlen(dst) + 1;
02194    map->database = strcpy(dst, database);
02195    if (table) {
02196       dst += strlen(dst) + 1;
02197       map->table = strcpy(dst, table);
02198    }
02199    map->priority = priority;
02200    map->next = config_maps;
02201    config_maps = map;
02202 
02203    ast_verb(2, "Binding %s to %s/%s/%s\n", map->name, map->driver, map->database, map->table ? map->table : map->name);
02204 
02205    return 0;
02206 }
02207 
02208 int read_config_maps(void) 
02209 {
02210    struct ast_config *config, *configtmp;
02211    struct ast_variable *v;
02212    char *driver, *table, *database, *textpri, *stringp, *tmp;
02213    struct ast_flags flags = { CONFIG_FLAG_NOREALTIME };
02214    int pri;
02215 
02216    clear_config_maps();
02217 
02218    configtmp = ast_config_new();
02219    if (!configtmp) {
02220       ast_log(LOG_ERROR, "Unable to allocate memory for new config\n");
02221       return -1;
02222    }
02223    config = ast_config_internal_load(extconfig_conf, configtmp, flags, "", "extconfig");
02224    if (config == CONFIG_STATUS_FILEINVALID) {
02225       return -1;
02226    } else if (!config) {
02227       ast_config_destroy(configtmp);
02228       return 0;
02229    }
02230 
02231    for (v = ast_variable_browse(config, "settings"); v; v = v->next) {
02232       char buf[512];
02233       ast_copy_string(buf, v->value, sizeof(buf));
02234       stringp = buf;
02235       driver = strsep(&stringp, ",");
02236 
02237       if ((tmp = strchr(stringp, '\"')))
02238          stringp = tmp;
02239 
02240       /* check if the database text starts with a double quote */
02241       if (*stringp == '"') {
02242          stringp++;
02243          database = strsep(&stringp, "\"");
02244          strsep(&stringp, ",");
02245       } else {
02246          /* apparently this text has no quotes */
02247          database = strsep(&stringp, ",");
02248       }
02249 
02250       table = strsep(&stringp, ",");
02251       textpri = strsep(&stringp, ",");
02252       if (!textpri || !(pri = atoi(textpri))) {
02253          pri = 1;
02254       }
02255 
02256       if (!strcmp(v->name, extconfig_conf)) {
02257          ast_log(LOG_WARNING, "Cannot bind '%s'!\n", extconfig_conf);
02258          continue;
02259       }
02260 
02261       if (!strcmp(v->name, "asterisk.conf")) {
02262          ast_log(LOG_WARNING, "Cannot bind 'asterisk.conf'!\n");
02263          continue;
02264       }
02265 
02266       if (!strcmp(v->name, "logger.conf")) {
02267          ast_log(LOG_WARNING, "Cannot bind 'logger.conf'!\n");
02268          continue;
02269       }
02270 
02271       if (!driver || !database)
02272          continue;
02273       if (!strcasecmp(v->name, "sipfriends")) {
02274          ast_log(LOG_WARNING, "The 'sipfriends' table is obsolete, update your config to use sippeers instead.\n");
02275          append_mapping("sippeers", driver, database, table ? table : "sipfriends", pri);
02276       } else if (!strcasecmp(v->name, "iaxfriends")) {
02277          ast_log(LOG_WARNING, "The 'iaxfriends' table is obsolete, update your config to use iaxusers and iaxpeers, though they can point to the same table.\n");
02278          append_mapping("iaxusers", driver, database, table ? table : "iaxfriends", pri);
02279          append_mapping("iaxpeers", driver, database, table ? table : "iaxfriends", pri);
02280       } else 
02281          append_mapping(v->name, driver, database, table, pri);
02282    }
02283       
02284    ast_config_destroy(config);
02285    return 0;
02286 }
02287 
02288 int ast_config_engine_register(struct ast_config_engine *new) 
02289 {
02290    struct ast_config_engine *ptr;
02291 
02292    ast_mutex_lock(&config_lock);
02293 
02294    if (!config_engine_list) {
02295       config_engine_list = new;
02296    } else {
02297       for (ptr = config_engine_list; ptr->next; ptr=ptr->next);
02298       ptr->next = new;
02299    }
02300 
02301    ast_mutex_unlock(&config_lock);
02302    ast_log(LOG_NOTICE,"Registered Config Engine %s\n", new->name);
02303 
02304    return 1;
02305 }
02306 
02307 int ast_config_engine_deregister(struct ast_config_engine *del) 
02308 {
02309    struct ast_config_engine *ptr, *last=NULL;
02310 
02311    ast_mutex_lock(&config_lock);
02312 
02313    for (ptr = config_engine_list; ptr; ptr=ptr->next) {
02314       if (ptr == del) {
02315          if (last)
02316             last->next = ptr->next;
02317          else
02318             config_engine_list = ptr->next;
02319          break;
02320       }
02321       last = ptr;
02322    }
02323 
02324    ast_mutex_unlock(&config_lock);
02325 
02326    return 0;
02327 }
02328 
02329 /*! \brief Find realtime engine for realtime family */
02330 static struct ast_config_engine *find_engine(const char *family, int priority, char *database, int dbsiz, char *table, int tabsiz) 
02331 {
02332    struct ast_config_engine *eng, *ret = NULL;
02333    struct ast_config_map *map;
02334 
02335    ast_mutex_lock(&config_lock);
02336 
02337    for (map = config_maps; map; map = map->next) {
02338       if (!strcasecmp(family, map->name) && (priority == map->priority)) {
02339          if (database)
02340             ast_copy_string(database, map->database, dbsiz);
02341          if (table)
02342             ast_copy_string(table, map->table ? map->table : family, tabsiz);
02343          break;
02344       }
02345    }
02346 
02347    /* Check if the required driver (engine) exist */
02348    if (map) {
02349       for (eng = config_engine_list; !ret && eng; eng = eng->next) {
02350          if (!strcasecmp(eng->name, map->driver))
02351             ret = eng;
02352       }
02353    }
02354 
02355    ast_mutex_unlock(&config_lock);
02356    
02357    /* if we found a mapping, but the engine is not available, then issue a warning */
02358    if (map && !ret)
02359       ast_log(LOG_WARNING, "Realtime mapping for '%s' found to engine '%s', but the engine is not available\n", map->name, map->driver);
02360 
02361    return ret;
02362 }
02363 
02364 static struct ast_config_engine text_file_engine = {
02365    .name = "text",
02366    .load_func = config_text_file_load,
02367 };
02368 
02369 struct ast_config *ast_config_internal_load(const char *filename, struct ast_config *cfg, struct ast_flags flags, const char *suggested_include_file, const char *who_asked)
02370 {
02371    char db[256];
02372    char table[256];
02373    struct ast_config_engine *loader = &text_file_engine;
02374    struct ast_config *result; 
02375 
02376    /* The config file itself bumps include_level by 1 */
02377    if (cfg->max_include_level > 0 && cfg->include_level == cfg->max_include_level + 1) {
02378       ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", cfg->max_include_level);
02379       return NULL;
02380    }
02381 
02382    cfg->include_level++;
02383 
02384    if (!ast_test_flag(&flags, CONFIG_FLAG_NOREALTIME) && config_engine_list) {
02385       struct ast_config_engine *eng;
02386 
02387       eng = find_engine(filename, 1, db, sizeof(db), table, sizeof(table));
02388 
02389 
02390       if (eng && eng->load_func) {
02391          loader = eng;
02392       } else {
02393          eng = find_engine("global", 1, db, sizeof(db), table, sizeof(table));
02394          if (eng && eng->load_func)
02395             loader = eng;
02396       }
02397    }
02398 
02399    result = loader->load_func(db, table, filename, cfg, flags, suggested_include_file, who_asked);
02400 
02401    if (result && result != CONFIG_STATUS_FILEINVALID && result != CONFIG_STATUS_FILEUNCHANGED)
02402       result->include_level--;
02403    else if (result != CONFIG_STATUS_FILEINVALID)
02404       cfg->include_level--;
02405 
02406    return result;
02407 }
02408 
02409 struct ast_config *ast_config_load2(const char *filename, const char *who_asked, struct ast_flags flags)
02410 {
02411    struct ast_config *cfg;
02412    struct ast_config *result;
02413 
02414    cfg = ast_config_new();
02415    if (!cfg)
02416       return NULL;
02417 
02418    result = ast_config_internal_load(filename, cfg, flags, "", who_asked);
02419    if (!result || result == CONFIG_STATUS_FILEUNCHANGED || result == CONFIG_STATUS_FILEINVALID)
02420       ast_config_destroy(cfg);
02421 
02422    return result;
02423 }
02424 
02425 static struct ast_variable *ast_load_realtime_helper(const char *family, va_list ap)
02426 {
02427    struct ast_config_engine *eng;
02428    char db[256];
02429    char table[256];
02430    struct ast_variable *res=NULL;
02431    int i;
02432 
02433    for (i = 1; ; i++) {
02434       if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
02435          if (eng->realtime_func && (res = eng->realtime_func(db, table, ap))) {
02436             return res;
02437          }
02438       } else {
02439          return NULL;
02440       }
02441    }
02442 
02443    return res;
02444 }
02445 
02446 struct ast_variable *ast_load_realtime_all(const char *family, ...)
02447 {
02448    struct ast_variable *res;
02449    va_list ap;
02450 
02451    va_start(ap, family);
02452    res = ast_load_realtime_helper(family, ap);
02453    va_end(ap);
02454 
02455    return res;
02456 }
02457 
02458 struct ast_variable *ast_load_realtime(const char *family, ...)
02459 {
02460    struct ast_variable *res;
02461    struct ast_variable *cur;
02462    struct ast_variable **prev;
02463    va_list ap;
02464 
02465    va_start(ap, family);
02466    res = ast_load_realtime_helper(family, ap);
02467    va_end(ap);
02468 
02469    /* Filter the list. */
02470    prev = &res;
02471    cur = res;
02472    while (cur) {
02473       if (ast_strlen_zero(cur->value)) {
02474          /* Eliminate empty entries */
02475          struct ast_variable *next;
02476 
02477          next = cur->next;
02478          *prev = next;
02479          ast_variable_destroy(cur);
02480          cur = next;
02481       } else {
02482          /* Make blank entries empty and keep them. */
02483          if (cur->value[0] == ' ' && cur->value[1] == '\0') {
02484             char *vptr = (char *) cur->value;
02485 
02486             vptr[0] = '\0';
02487          }
02488 
02489          prev = &cur->next;
02490          cur = cur->next;
02491       }
02492    }
02493    return res;
02494 }
02495 
02496 /*! \brief Check if realtime engine is configured for family */
02497 int ast_check_realtime(const char *family)
02498 {
02499    struct ast_config_engine *eng;
02500    if (!ast_realtime_enabled()) {
02501       return 0;   /* There are no engines at all so fail early */
02502    }
02503 
02504    eng = find_engine(family, 1, NULL, 0, NULL, 0);
02505    if (eng)
02506       return 1;
02507    return 0;
02508 }
02509 
02510 /*! \brief Check if there's any realtime engines loaded */
02511 int ast_realtime_enabled(void)
02512 {
02513    return config_maps ? 1 : 0;
02514 }
02515 
02516 int ast_realtime_require_field(const char *family, ...)
02517 {
02518    struct ast_config_engine *eng;
02519    char db[256];
02520    char table[256];
02521    va_list ap;
02522    int res = -1, i;
02523 
02524    va_start(ap, family);
02525    for (i = 1; ; i++) {
02526       if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
02527          /* If the require succeeds, it returns 0. */
02528          if (eng->require_func && !(res = eng->require_func(db, table, ap))) {
02529             break;
02530          }
02531       } else {
02532          break;
02533       }
02534    }
02535    va_end(ap);
02536 
02537    return res;
02538 }
02539 
02540 int ast_unload_realtime(const char *family)
02541 {
02542    struct ast_config_engine *eng;
02543    char db[256];
02544    char table[256];
02545    int res = -1, i;
02546 
02547    for (i = 1; ; i++) {
02548       if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
02549          if (eng->unload_func) {
02550             /* Do this for ALL engines */
02551             res = eng->unload_func(db, table);
02552          }
02553       } else {
02554          break;
02555       }
02556    }
02557    return res;
02558 }
02559 
02560 struct ast_config *ast_load_realtime_multientry(const char *family, ...)
02561 {
02562    struct ast_config_engine *eng;
02563    char db[256];
02564    char table[256];
02565    struct ast_config *res = NULL;
02566    va_list ap;
02567    int i;
02568 
02569    va_start(ap, family);
02570    for (i = 1; ; i++) {
02571       if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
02572          if (eng->realtime_multi_func && (res = eng->realtime_multi_func(db, table, ap))) {
02573             /* If we were returned an empty cfg, destroy it and return NULL */
02574             if (!res->root) {
02575                ast_config_destroy(res);
02576                res = NULL;
02577             }
02578             break;
02579          }
02580       } else {
02581          break;
02582       }
02583    }
02584    va_end(ap);
02585 
02586    return res;
02587 }
02588 
02589 int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...)
02590 {
02591    struct ast_config_engine *eng;
02592    int res = -1, i;
02593    char db[256];
02594    char table[256];
02595    va_list ap;
02596 
02597    va_start(ap, lookup);
02598    for (i = 1; ; i++) {
02599       if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
02600          /* If the update succeeds, it returns 0. */
02601          if (eng->update_func && !(res = eng->update_func(db, table, keyfield, lookup, ap))) {
02602             break;
02603          }
02604       } else {
02605          break;
02606       }
02607    }
02608    va_end(ap);
02609 
02610    return res;
02611 }
02612 
02613 int ast_update2_realtime(const char *family, ...)
02614 {
02615    struct ast_config_engine *eng;
02616    int res = -1, i;
02617    char db[256];
02618    char table[256];
02619    va_list ap;
02620 
02621    va_start(ap, family);
02622    for (i = 1; ; i++) {
02623       if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
02624          if (eng->update2_func && !(res = eng->update2_func(db, table, ap))) {
02625             break;
02626          }
02627       } else {
02628          break;
02629       }
02630    }
02631    va_end(ap);
02632 
02633    return res;
02634 }
02635 
02636 int ast_store_realtime(const char *family, ...)
02637 {
02638    struct ast_config_engine *eng;
02639    int res = -1, i;
02640    char db[256];
02641    char table[256];
02642    va_list ap;
02643 
02644    va_start(ap, family);
02645    for (i = 1; ; i++) {
02646       if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
02647          /* If the store succeeds, it returns 0. */
02648          if (eng->store_func && !(res = eng->store_func(db, table, ap))) {
02649             break;
02650          }
02651       } else {
02652          break;
02653       }
02654    }
02655    va_end(ap);
02656 
02657    return res;
02658 }
02659 
02660 int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup, ...)
02661 {
02662    struct ast_config_engine *eng;
02663    int res = -1, i;
02664    char db[256];
02665    char table[256];
02666    va_list ap;
02667 
02668    va_start(ap, lookup);
02669    for (i = 1; ; i++) {
02670       if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
02671          if (eng->destroy_func && !(res = eng->destroy_func(db, table, keyfield, lookup, ap))) {
02672             break;
02673          }
02674       } else {
02675          break;
02676       }
02677    }
02678    va_end(ap);
02679 
02680    return res;
02681 }
02682 
02683 char *ast_realtime_decode_chunk(char *chunk)
02684 {
02685    char *orig = chunk;
02686    for (; *chunk; chunk++) {
02687       if (*chunk == '^' && strchr("0123456789ABCDEFabcdef", chunk[1]) && strchr("0123456789ABCDEFabcdef", chunk[2])) {
02688          sscanf(chunk + 1, "%02hhX", chunk);
02689          memmove(chunk + 1, chunk + 3, strlen(chunk + 3) + 1);
02690       }
02691    }
02692    return orig;
02693 }
02694 
02695 char *ast_realtime_encode_chunk(struct ast_str **dest, ssize_t maxlen, const char *chunk)
02696 {
02697    if (!strchr(chunk, ';') && !strchr(chunk, '^')) {
02698       ast_str_set(dest, maxlen, "%s", chunk);
02699    } else {
02700       ast_str_reset(*dest);
02701       for (; *chunk; chunk++) {
02702          if (strchr(";^", *chunk)) {
02703             ast_str_append(dest, maxlen, "^%02hhX", *chunk);
02704          } else {
02705             ast_str_append(dest, maxlen, "%c", *chunk);
02706          }
02707       }
02708    }
02709    return ast_str_buffer(*dest);
02710 }
02711 
02712 /*! \brief Helper function to parse arguments
02713  * See documentation in config.h
02714  */
02715 int ast_parse_arg(const char *arg, enum ast_parse_flags flags,
02716    void *p_result, ...)
02717 {
02718    va_list ap;
02719    int error = 0;
02720 
02721    va_start(ap, p_result);
02722    switch (flags & PARSE_TYPE) {
02723    case PARSE_INT32:
02724    {
02725       long int x = 0;
02726       int32_t *result = p_result;
02727       int32_t def = result ? *result : 0, high = INT32_MAX, low = INT32_MIN;
02728       char *endptr = NULL;
02729 
02730       /* optional arguments: default value and/or (low, high) */
02731       if (flags & PARSE_DEFAULT) {
02732          def = va_arg(ap, int32_t);
02733       }
02734       if (flags & (PARSE_IN_RANGE | PARSE_OUT_RANGE)) {
02735          low = va_arg(ap, int32_t);
02736          high = va_arg(ap, int32_t);
02737       }
02738       if (ast_strlen_zero(arg)) {
02739          error = 1;
02740          goto int32_done;
02741       }
02742       errno = 0;
02743       x = strtol(arg, &endptr, 0);
02744       if (*endptr || errno || x < INT32_MIN || x > INT32_MAX) {
02745          /* Parse error, or type out of int32_t bounds */
02746          error = 1;
02747          goto int32_done;
02748       }
02749       error = (x < low) || (x > high);
02750       if (flags & PARSE_OUT_RANGE) {
02751          error = !error;
02752       }
02753 int32_done:
02754       if (result) {
02755          *result  = error ? def : x;
02756       }
02757 
02758       ast_debug(3, "extract int from [%s] in [%d, %d] gives [%ld](%d)\n",
02759             arg, low, high, result ? *result : x, error);
02760       break;
02761    }
02762 
02763    case PARSE_UINT32:
02764    {
02765       unsigned long int x = 0;
02766       uint32_t *result = p_result;
02767       uint32_t def = result ? *result : 0, low = 0, high = UINT32_MAX;
02768       char *endptr = NULL;
02769 
02770       /* optional argument: first default value, then range */
02771       if (flags & PARSE_DEFAULT) {
02772          def = va_arg(ap, uint32_t);
02773       }
02774       if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
02775          /* range requested, update bounds */
02776          low = va_arg(ap, uint32_t);
02777          high = va_arg(ap, uint32_t);
02778       }
02779 
02780       if (ast_strlen_zero(arg)) {
02781          error = 1;
02782          goto uint32_done;
02783       }
02784       /* strtoul will happilly and silently negate negative numbers */
02785       arg = ast_skip_blanks(arg);
02786       if (*arg == '-') {
02787          error = 1;
02788          goto uint32_done;
02789       }
02790       errno = 0;
02791       x = strtoul(arg, &endptr, 0);
02792       if (*endptr || errno || x > UINT32_MAX) {
02793          error = 1;
02794          goto uint32_done;
02795       }
02796       error = (x < low) || (x > high);
02797       if (flags & PARSE_OUT_RANGE) {
02798          error = !error;
02799       }
02800 uint32_done:
02801       if (result) {
02802          *result  = error ? def : x;
02803       }
02804       ast_debug(3, "extract uint from [%s] in [%u, %u] gives [%lu](%d)\n",
02805             arg, low, high, result ? *result : x, error);
02806       break;
02807    }
02808 
02809    case PARSE_DOUBLE:
02810    {
02811       double *result = p_result;
02812       double x = 0, def = result ? *result : 0, low = -HUGE_VAL, high = HUGE_VAL;
02813       char *endptr = NULL;
02814 
02815       /* optional argument: first default value, then range */
02816       if (flags & PARSE_DEFAULT) {
02817          def = va_arg(ap, double);
02818       }
02819       if (flags & (PARSE_IN_RANGE | PARSE_OUT_RANGE)) {
02820          /* range requested, update bounds */
02821          low = va_arg(ap, double);
02822          high = va_arg(ap, double);
02823       }
02824       if (ast_strlen_zero(arg)) {
02825          error = 1;
02826          goto double_done;
02827       }
02828       errno = 0;
02829       x = strtod(arg, &endptr);
02830       if (*endptr || errno == ERANGE) {
02831          error = 1;
02832          goto double_done;
02833       }
02834       error = (x < low) || (x > high);
02835       if (flags & PARSE_OUT_RANGE) {
02836          error = !error;
02837       }
02838 double_done:
02839       if (result) {
02840          *result = error ? def : x;
02841       }
02842       ast_debug(3, "extract double from [%s] in [%f, %f] gives [%f](%d)\n",
02843             arg, low, high, result ? *result : x, error);
02844       break;
02845    }
02846    case PARSE_ADDR:
02847        {
02848       struct ast_sockaddr *addr = (struct ast_sockaddr *)p_result;
02849 
02850       if (!ast_sockaddr_parse(addr, arg, flags & PARSE_PORT_MASK)) {
02851          error = 1;
02852       }
02853 
02854       ast_debug(3, "extract addr from %s gives %s(%d)\n",
02855            arg, ast_sockaddr_stringify(addr), error);
02856 
02857       break;
02858        }
02859    case PARSE_INADDR:   /* TODO Remove this (use PARSE_ADDR instead). */
02860        {
02861       char *port, *buf;
02862       struct sockaddr_in _sa_buf;   /* buffer for the result */
02863       struct sockaddr_in *sa = p_result ?
02864          (struct sockaddr_in *)p_result : &_sa_buf;
02865       /* default is either the supplied value or the result itself */
02866       struct sockaddr_in *def = (flags & PARSE_DEFAULT) ?
02867          va_arg(ap, struct sockaddr_in *) : sa;
02868       struct hostent *hp;
02869       struct ast_hostent ahp;
02870 
02871       memset(&_sa_buf, '\0', sizeof(_sa_buf)); /* clear buffer */
02872       /* duplicate the string to strip away the :port */
02873       port = ast_strdupa(arg);
02874       buf = strsep(&port, ":");
02875       sa->sin_family = AF_INET;  /* assign family */
02876       /*
02877        * honor the ports flag setting, assign default value
02878        * in case of errors or field unset.
02879        */
02880       flags &= PARSE_PORT_MASK; /* the only flags left to process */
02881       if (port) {
02882          if (flags == PARSE_PORT_FORBID) {
02883             error = 1;  /* port was forbidden */
02884             sa->sin_port = def->sin_port;
02885          } else if (flags == PARSE_PORT_IGNORE)
02886             sa->sin_port = def->sin_port;
02887          else /* accept or require */
02888             sa->sin_port = htons(strtol(port, NULL, 0));
02889       } else {
02890          sa->sin_port = def->sin_port;
02891          if (flags == PARSE_PORT_REQUIRE)
02892             error = 1;
02893       }
02894       /* Now deal with host part, even if we have errors before. */
02895       hp = ast_gethostbyname(buf, &ahp);
02896       if (hp)  /* resolved successfully */
02897          memcpy(&sa->sin_addr, hp->h_addr, sizeof(sa->sin_addr));
02898       else {
02899          error = 1;
02900          sa->sin_addr = def->sin_addr;
02901       }
02902       ast_debug(3,
02903          "extract inaddr from [%s] gives [%s:%d](%d)\n",
02904          arg, ast_inet_ntoa(sa->sin_addr),
02905          ntohs(sa->sin_port), error);
02906          break;
02907        }
02908    }
02909    va_end(ap);
02910    return error;
02911 }
02912 
02913 static char *handle_cli_core_show_config_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02914 {
02915    struct ast_config_engine *eng;
02916    struct ast_config_map *map;
02917 
02918    switch (cmd) {
02919    case CLI_INIT:
02920       e->command = "core show config mappings";
02921       e->usage =
02922          "Usage: core show config mappings\n"
02923          "  Shows the filenames to config engines.\n";
02924       return NULL;
02925    case CLI_GENERATE:
02926       return NULL;
02927    }
02928    
02929    ast_mutex_lock(&config_lock);
02930 
02931    if (!config_engine_list) {
02932       ast_cli(a->fd, "No config mappings found.\n");
02933    } else {
02934       for (eng = config_engine_list; eng; eng = eng->next) {
02935          ast_cli(a->fd, "Config Engine: %s\n", eng->name);
02936          for (map = config_maps; map; map = map->next) {
02937             if (!strcasecmp(map->driver, eng->name)) {
02938                ast_cli(a->fd, "===> %s (db=%s, table=%s)\n", map->name, map->database,
02939                      map->table ? map->table : map->name);
02940             }
02941          }
02942       }
02943    }
02944    
02945    ast_mutex_unlock(&config_lock);
02946 
02947    return CLI_SUCCESS;
02948 }
02949 
02950 static char *handle_cli_config_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02951 {
02952    struct cache_file_mtime *cfmtime;
02953    char *prev = "", *completion_value = NULL;
02954    int wordlen, which = 0;
02955 
02956    switch (cmd) {
02957    case CLI_INIT:
02958       e->command = "config reload";
02959       e->usage =
02960          "Usage: config reload <filename.conf>\n"
02961          "   Reloads all modules that reference <filename.conf>\n";
02962       return NULL;
02963    case CLI_GENERATE:
02964       if (a->pos > 2) {
02965          return NULL;
02966       }
02967 
02968       wordlen = strlen(a->word);
02969 
02970       AST_LIST_LOCK(&cfmtime_head);
02971       AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
02972          /* Skip duplicates - this only works because the list is sorted by filename */
02973          if (strcmp(cfmtime->filename, prev) == 0) {
02974             continue;
02975          }
02976 
02977          /* Core configs cannot be reloaded */
02978          if (ast_strlen_zero(cfmtime->who_asked)) {
02979             continue;
02980          }
02981 
02982          if (++which > a->n && strncmp(cfmtime->filename, a->word, wordlen) == 0) {
02983             completion_value = ast_strdup(cfmtime->filename);
02984             break;
02985          }
02986 
02987          /* Otherwise save that we've seen this filename */
02988          prev = cfmtime->filename;
02989       }
02990       AST_LIST_UNLOCK(&cfmtime_head);
02991 
02992       return completion_value;
02993    }
02994 
02995    if (a->argc != 3) {
02996       return CLI_SHOWUSAGE;
02997    }
02998 
02999    AST_LIST_LOCK(&cfmtime_head);
03000    AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
03001       if (!strcmp(cfmtime->filename, a->argv[2])) {
03002          char *buf = ast_alloca(strlen("module reload ") + strlen(cfmtime->who_asked) + 1);
03003          sprintf(buf, "module reload %s", cfmtime->who_asked);
03004          ast_cli_command(a->fd, buf);
03005       }
03006    }
03007    AST_LIST_UNLOCK(&cfmtime_head);
03008 
03009    return CLI_SUCCESS;
03010 }
03011 
03012 static char *handle_cli_config_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
03013 {
03014    struct cache_file_mtime *cfmtime;
03015 
03016    switch (cmd) {
03017    case CLI_INIT:
03018       e->command = "config list";
03019       e->usage =
03020          "Usage: config list\n"
03021          "   Show all modules that have loaded a configuration file\n";
03022       return NULL;
03023    case CLI_GENERATE:
03024       return NULL;
03025    }
03026 
03027    AST_LIST_LOCK(&cfmtime_head);
03028    AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
03029       ast_cli(a->fd, "%-20.20s %-50s\n", S_OR(cfmtime->who_asked, "core"), cfmtime->filename);
03030    }
03031    AST_LIST_UNLOCK(&cfmtime_head);
03032 
03033    return CLI_SUCCESS;
03034 }
03035 
03036 static struct ast_cli_entry cli_config[] = {
03037    AST_CLI_DEFINE(handle_cli_core_show_config_mappings, "Display config mappings (file names to config engines)"),
03038    AST_CLI_DEFINE(handle_cli_config_reload, "Force a reload on modules using a particular configuration file"),
03039    AST_CLI_DEFINE(handle_cli_config_list, "Show all files that have loaded a configuration file"),
03040 };
03041 
03042 static void config_shutdown(void)
03043 {
03044    struct cache_file_mtime *cfmtime;
03045 
03046    AST_LIST_LOCK(&cfmtime_head);
03047    while ((cfmtime = AST_LIST_REMOVE_HEAD(&cfmtime_head, list))) {
03048       struct cache_file_include *cfinclude;
03049       while ((cfinclude = AST_LIST_REMOVE_HEAD(&cfmtime->includes, list))) {
03050          ast_free(cfinclude);
03051       }
03052       ast_free(cfmtime);
03053    }
03054    AST_LIST_UNLOCK(&cfmtime_head);
03055 
03056    ast_cli_unregister_multiple(cli_config, ARRAY_LEN(cli_config));
03057 }
03058 
03059 int register_config_cli(void)
03060 {
03061    ast_cli_register_multiple(cli_config, ARRAY_LEN(cli_config));
03062    ast_register_atexit(config_shutdown);
03063    return 0;
03064 }