Mon Jun 18 2012 03:55:32

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