Merged revisions 51057 via svnmerge from
[asterisk/asterisk.git] / main / config.c
index ee3922a..4a986bd 100644 (file)
@@ -62,6 +62,96 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 static char *extconfig_conf = "extconfig.conf";
 
+/*! Growable string buffer */
+static char *comment_buffer;   /*!< this will be a comment collector.*/
+static int   comment_buffer_size;  /*!< the amount of storage so far alloc'd for the comment_buffer */
+
+static char *lline_buffer;    /*!< A buffer for stuff behind the ; */
+static int  lline_buffer_size;
+
+
+struct ast_comment {
+       struct ast_comment *next;
+       char cmt[0];
+};
+
+#define CB_INCR 250
+
+static void CB_INIT(void)
+{
+       if (!comment_buffer) {
+               comment_buffer = ast_malloc(CB_INCR);
+               if (!comment_buffer)
+                       return;
+               comment_buffer[0] = 0;
+               comment_buffer_size = CB_INCR;
+               lline_buffer = ast_malloc(CB_INCR);
+               if (!lline_buffer)
+                       return;
+               lline_buffer[0] = 0;
+               lline_buffer_size = CB_INCR;
+       } else {
+               comment_buffer[0] = 0;
+               lline_buffer[0] = 0;
+       }
+}
+
+static void  CB_ADD(char *str)
+{
+       int rem = comment_buffer_size - strlen(comment_buffer) - 1;
+       int siz = strlen(str);
+       if (rem < siz+1) {
+               comment_buffer = ast_realloc(comment_buffer, comment_buffer_size + CB_INCR + siz + 1);
+               if (!comment_buffer)
+                       return;
+               comment_buffer_size += CB_INCR+siz+1;
+       }
+       strcat(comment_buffer,str);
+}
+
+static void  CB_ADD_LEN(char *str, int len)
+{
+       int cbl = strlen(comment_buffer) + 1;
+       int rem = comment_buffer_size - cbl;
+       if (rem < len+1) {
+               comment_buffer = ast_realloc(comment_buffer, comment_buffer_size + CB_INCR + len + 1);
+               if (!comment_buffer)
+                       return;
+               comment_buffer_size += CB_INCR+len+1;
+       }
+       strncat(comment_buffer,str,len);
+       comment_buffer[cbl+len-1] = 0;
+}
+
+static void  LLB_ADD(char *str)
+{
+       int rem = lline_buffer_size - strlen(lline_buffer) - 1;
+       int siz = strlen(str);
+       if (rem < siz+1) {
+               lline_buffer = ast_realloc(lline_buffer, lline_buffer_size + CB_INCR + siz + 1);
+               if (!lline_buffer) 
+                       return;
+               lline_buffer_size += CB_INCR + siz + 1;
+       }
+       strcat(lline_buffer,str);
+}
+
+static void CB_RESET(void )  
+{ 
+       comment_buffer[0] = 0; 
+       lline_buffer[0] = 0;
+}
+               
+
+
+static struct ast_comment *ALLOC_COMMENT(const char *buffer)
+{ 
+       struct ast_comment *x = ast_calloc(1,sizeof(struct ast_comment)+strlen(buffer)+1);
+       strcpy(x->cmt, buffer);
+       return x;
+}
+
+
 static struct ast_config_map {
        struct ast_config_map *next;
        char *name;
@@ -76,14 +166,12 @@ static struct ast_config_engine *config_engine_list;
 
 #define MAX_INCLUDE_LEVEL 10
 
-struct ast_comment {
-       struct ast_comment *next;
-       char cmt[0];
-};
-
 struct ast_category {
        char name[80];
-       int ignored;                    /* do not let user of the config see this category */
+       int ignored;                    /*!< do not let user of the config see this category */
+       int include_level;      
+       struct ast_comment *precomments;
+       struct ast_comment *sameline;
        struct ast_variable *root;
        struct ast_variable *last;
        struct ast_category *next;
@@ -93,7 +181,7 @@ struct ast_config {
        struct ast_category *root;
        struct ast_category *last;
        struct ast_category *current;
-       struct ast_category *last_browse;               /* used to cache the last category supplied via category_browse */
+       struct ast_category *last_browse;               /*!< used to cache the last category supplied via category_browse */
        int include_level;
        int max_include_level;
 };
@@ -122,6 +210,8 @@ void ast_variable_append(struct ast_category *category, struct ast_variable *var
        else
                category->root = variable;
        category->last = variable;
+       while (category->last->next)
+               category->last = category->last->next;
 }
 
 void ast_variables_destroy(struct ast_variable *v)
@@ -147,7 +237,17 @@ struct ast_variable *ast_variable_browse(const struct ast_config *config, const
        return (cat) ? cat->root : NULL;
 }
 
-char *ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable)
+const char *ast_config_option(struct ast_config *cfg, const char *cat, const char *var)
+{
+       const char *tmp;
+       tmp = ast_variable_retrieve(cfg, cat, var);
+       if (!tmp)
+               tmp = ast_variable_retrieve(cfg, "general", var);
+       return tmp;
+}
+
+
+const char *ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable)
 {
        struct ast_variable *v;
 
@@ -242,6 +342,7 @@ void ast_category_append(struct ast_config *config, struct ast_category *categor
                config->last->next = category;
        else
                config->root = category;
+       category->include_level = config->include_level;
        config->last = category;
        config->current = category;
 }
@@ -297,6 +398,7 @@ struct ast_variable *ast_category_detach_variables(struct ast_category *cat)
 
        v = cat->root;
        cat->root = NULL;
+       cat->last = NULL;
 
        return v;
 }
@@ -323,7 +425,7 @@ struct ast_config *ast_config_new(void)
        return config;
 }
 
-int ast_variable_delete(struct ast_category *category, char *variable, char *match)
+int ast_variable_delete(struct ast_category *category, const char *variable, const char *match)
 {
        struct ast_variable *cur, *prev=NULL, *curn;
        int res = -1;
@@ -372,7 +474,7 @@ int ast_variable_delete(struct ast_category *category, char *variable, char *mat
        return res;
 }
 
-int ast_variable_update(struct ast_category *category, char *variable, char *value, char *match)
+int ast_variable_update(struct ast_category *category, const char *variable, const char *value, const char *match)
 {
        struct ast_variable *cur, *prev=NULL, *newer;
        newer = ast_variable_new(variable, value);
@@ -423,7 +525,7 @@ int ast_variable_update(struct ast_category *category, char *variable, char *val
        return 0;
 }
 
-int ast_category_delete(struct ast_config *cfg, char *category)
+int ast_category_delete(struct ast_config *cfg, const char *category)
 {
        struct ast_category *prev=NULL, *cat;
        cat = cfg->root;
@@ -524,6 +626,16 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
                if (!(*cat = newcat = ast_category_new(catname))) {
                        return -1;
                }
+               /* add comments */
+               if (withcomments && comment_buffer && comment_buffer[0] ) {
+                       newcat->precomments = ALLOC_COMMENT(comment_buffer);
+               }
+               if (withcomments && lline_buffer && lline_buffer[0] ) {
+                       newcat->sameline = ALLOC_COMMENT(lline_buffer);
+               }
+               if( withcomments )
+                       CB_RESET();
+               
                /* If there are options or categories to inherit from, process them now */
                if (c) {
                        if (!(cur = strchr(c, ')'))) {
@@ -646,13 +758,22 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
                                /* Put and reset comments */
                                v->blanklines = 0;
                                ast_variable_append(*cat, v);
+                               /* add comments */
+                               if (withcomments && comment_buffer && comment_buffer[0] ) {
+                                       v->precomments = ALLOC_COMMENT(comment_buffer);
+                               }
+                               if (withcomments && lline_buffer && lline_buffer[0] ) {
+                                       v->sameline = ALLOC_COMMENT(lline_buffer);
+                               }
+                               if( withcomments )
+                                       CB_RESET();
+                               
                        } else {
                                return -1;
                        }
                } else {
                        ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
                }
-
        }
        return 0;
 }
@@ -677,6 +798,13 @@ static struct ast_config *config_text_file_load(const char *database, const char
                snprintf(fn, sizeof(fn), "%s/%s", (char *)ast_config_AST_CONFIG_DIR, filename);
        }
 
+       if (withcomments) {
+               CB_INIT();
+               if (!lline_buffer || !comment_buffer) {
+                       ast_log(LOG_ERROR, "Failed to initialize the comment buffer!\n");
+                       return NULL;
+               }
+       }
 #ifdef AST_INCLUDE_GLOB
        {
                int glob_ret;
@@ -707,30 +835,36 @@ static struct ast_config *config_text_file_load(const char *database, const char
                        ast_log(LOG_WARNING, "'%s' is not a regular file, ignoring\n", fn);
                        continue;
                }
-               if ((option_verbose > 1) && !option_debug) {
+               if (option_verbose > 1) {
                        ast_verbose(VERBOSE_PREFIX_2 "Parsing '%s': ", fn);
                        fflush(stdout);
                }
                if (!(f = fopen(fn, "r"))) {
                        if (option_debug)
                                ast_log(LOG_DEBUG, "No file to parse: %s\n", fn);
-                       else if (option_verbose > 1)
+                       if (option_verbose > 1)
                                ast_verbose( "Not found (%s)\n", strerror(errno));
                        continue;
                }
                count++;
                if (option_debug)
                        ast_log(LOG_DEBUG, "Parsing %s\n", fn);
-               else if (option_verbose > 1)
+               if (option_verbose > 1)
                        ast_verbose("Found\n");
                while(!feof(f)) {
                        lineno++;
                        if (fgets(buf, sizeof(buf), f)) {
+                               if ( withcomments ) {
+                                       CB_ADD(lline_buffer);       /* add the current lline buffer to the comment buffer */
+                                       lline_buffer[0] = 0;        /* erase the lline buffer */
+                               }
+                               
                                new_buf = buf;
-                               if (comment)
+                               if (comment) 
                                        process_buf = NULL;
                                else
                                        process_buf = buf;
+                               
                                while ((comment_p = strchr(new_buf, COMMENT_META))) {
                                        if ((comment_p > new_buf) && (*(comment_p-1) == '\\')) {
                                                /* Yuck, gotta memmove */
@@ -758,6 +892,11 @@ static struct ast_config *config_text_file_load(const char *database, const char
                                                                /* Actually have to move what's left over the top, then continue */
                                                                char *oldptr;
                                                                oldptr = process_buf + strlen(process_buf);
+                                                               if ( withcomments ) {
+                                                                       CB_ADD(";");
+                                                                       CB_ADD_LEN(oldptr+1,new_buf-oldptr-1);
+                                                               }
+                                                               
                                                                memmove(oldptr, new_buf, strlen(new_buf) + 1);
                                                                new_buf = oldptr;
                                                        } else
@@ -767,12 +906,20 @@ static struct ast_config *config_text_file_load(const char *database, const char
                                                if (!comment) {
                                                        /* If ; is found, and we are not nested in a comment, 
                                                           we immediately stop all comment processing */
+                                                       if ( withcomments ) {
+                                                               LLB_ADD(comment_p);
+                                                       }
                                                        *comment_p = '\0'; 
                                                        new_buf = comment_p;
                                                } else
                                                        new_buf = comment_p + 1;
                                        }
                                }
+                               if( withcomments && comment && !process_buf )
+                               {
+                                       CB_ADD(buf);  /* the whole line is a comment, store it */
+                               }
+                               
                                if (process_buf) {
                                        char *buf = ast_strip(process_buf);
                                        if (!ast_strlen_zero(buf)) {
@@ -797,6 +944,16 @@ static struct ast_config *config_text_file_load(const char *database, const char
                        }
                }
 #endif
+
+       if (cfg && cfg->include_level == 1 && withcomments && comment_buffer) {
+               free(comment_buffer);
+               free(lline_buffer);
+               comment_buffer = NULL;
+               lline_buffer = NULL;
+               comment_buffer_size = 0;
+               lline_buffer_size = 0;
+       }
+       
        if (count == 0)
                return NULL;
 
@@ -811,6 +968,7 @@ int config_text_file_save(const char *configfile, const struct ast_config *cfg,
        time_t t;
        struct ast_variable *var;
        struct ast_category *cat;
+       struct ast_comment *cmt;
        int blanklines = 0;
 
        if (configfile[0] == '/') {
@@ -825,22 +983,43 @@ int config_text_file_save(const char *configfile, const struct ast_config *cfg,
 #else
        if ((f = fopen(fn, "w"))) {
 #endif     
-               if ((option_verbose > 1) && !option_debug)
-                       ast_verbose(  VERBOSE_PREFIX_2 "Saving '%s': ", fn);
+               if (option_verbose > 1)
+                       ast_verbose(VERBOSE_PREFIX_2 "Saving '%s': ", fn);
                fprintf(f, ";!\n");
                fprintf(f, ";! Automatically generated configuration file\n");
-               fprintf(f, ";! Filename: %s (%s)\n", configfile, fn);
+               if (strcmp(configfile, fn))
+                       fprintf(f, ";! Filename: %s (%s)\n", configfile, fn);
+               else
+                       fprintf(f, ";! Filename: %s\n", configfile);
                fprintf(f, ";! Generator: %s\n", generator);
                fprintf(f, ";! Creation Date: %s", date);
                fprintf(f, ";!\n");
                cat = cfg->root;
                while(cat) {
                        /* Dump section with any appropriate comment */
-                       fprintf(f, "\n[%s]\n", cat->name);
+                       for (cmt = cat->precomments; cmt; cmt=cmt->next)
+                       {
+                               if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
+                                       fprintf(f,"%s", cmt->cmt);
+                       }
+                       if (!cat->precomments)
+                               fprintf(f,"\n");
+                       fprintf(f, "[%s]", cat->name);
+                       for(cmt = cat->sameline; cmt; cmt=cmt->next)
+                       {
+                               fprintf(f,"%s", cmt->cmt);
+                       }
+                       if (!cat->sameline)
+                               fprintf(f,"\n");
                        var = cat->root;
                        while(var) {
+                               for (cmt = var->precomments; cmt; cmt=cmt->next)
+                               {
+                                       if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
+                                               fprintf(f,"%s", cmt->cmt);
+                               }
                                if (var->sameline) 
-                                       fprintf(f, "%s %s %s  ; %s\n", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt);
+                                       fprintf(f, "%s %s %s  %s", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt);
                                else    
                                        fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="), var->value);
                                if (var->blanklines) {
@@ -861,9 +1040,9 @@ int config_text_file_save(const char *configfile, const struct ast_config *cfg,
                        ast_verbose("Saved\n");
        } else {
                if (option_debug)
-                       printf("Unable to open for writing: %s\n", fn);
-               else if (option_verbose > 1)
-                       printf( "Unable to write (%s)", strerror(errno));
+                       ast_log(LOG_DEBUG, "Unable to open for writing: %s\n", fn);
+               if (option_verbose > 1)
+                       ast_verbose(VERBOSE_PREFIX_2 "Unable to write (%s)", strerror(errno));
                return -1;
        }
        fclose(f);
@@ -924,7 +1103,7 @@ int read_config_maps(void)
 {
        struct ast_config *config, *configtmp;
        struct ast_variable *v;
-       char *driver, *table, *database, *stringp;
+       char *driver, *table, *database, *stringp, *tmp;
 
        clear_config_maps();
 
@@ -940,6 +1119,9 @@ int read_config_maps(void)
                stringp = v->value;
                driver = strsep(&stringp, ",");
 
+               if ((tmp = strchr(stringp, '\"')))
+                       stringp = tmp;
+
                /* check if the database text starts with a double quote */
                if (*stringp == '"') {
                        stringp++;
@@ -1071,7 +1253,7 @@ struct ast_config *ast_config_internal_load(const char *filename, struct ast_con
        char db[256];
        char table[256];
        struct ast_config_engine *loader = &text_file_engine;
-       struct ast_config *result;
+       struct ast_config *result; 
 
        if (cfg->include_level == cfg->max_include_level) {
                ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", cfg->max_include_level);
@@ -1135,23 +1317,61 @@ struct ast_config *ast_config_load_with_comments(const char *filename)
        return result;
 }
 
-struct ast_variable *ast_load_realtime(const char *family, ...)
+static struct ast_variable *ast_load_realtime_helper(const char *family, va_list ap)
 {
        struct ast_config_engine *eng;
        char db[256]="";
        char table[256]="";
        struct ast_variable *res=NULL;
-       va_list ap;
 
-       va_start(ap, family);
        eng = find_engine(family, db, sizeof(db), table, sizeof(table));
        if (eng && eng->realtime_func) 
                res = eng->realtime_func(db, table, ap);
+
+       return res;
+}
+
+struct ast_variable *ast_load_realtime_all(const char *family, ...)
+{
+       struct ast_variable *res;
+       va_list ap;
+
+       va_start(ap, family);
+       res = ast_load_realtime_helper(family, ap);
        va_end(ap);
 
        return res;
 }
 
+struct ast_variable *ast_load_realtime(const char *family, ...)
+{
+       struct ast_variable *res, *cur, *prev = NULL, *freeme = NULL;
+       va_list ap;
+
+       va_start(ap, family);
+       res = ast_load_realtime_helper(family, ap);
+       va_end(ap);
+
+       /* Eliminate blank entries */
+       for (cur = res; cur; cur = cur->next) {
+               if (freeme) {
+                       free(freeme);
+                       freeme = NULL;
+               }
+
+               if (ast_strlen_zero(cur->value)) {
+                       if (prev)
+                               prev->next = cur->next;
+                       else
+                               res = cur->next;
+                       freeme = cur;
+               } else {
+                       prev = cur;
+               }
+       }
+       return res;
+}
+
 /*! \brief Check if realtime engine is configured for family */
 int ast_check_realtime(const char *family)
 {
@@ -1222,14 +1442,17 @@ static int config_command(int fd, int argc, char **argv)
 }
 
 static char show_config_help[] =
-       "Usage: show config mappings\n"
+       "Usage: core show config mappings\n"
        "       Shows the filenames to config engines.\n";
 
-static struct ast_cli_entry config_command_struct = {
-       { "show", "config", "mappings", NULL }, config_command, "Show Config mappings (file names to config engines)", show_config_help, NULL
+static struct ast_cli_entry cli_config[] = {
+       { { "core", "show", "config", "mappings", NULL },
+       config_command, "Display config mappings (file names to config engines)",
+       show_config_help },
 };
 
 int register_config_cli() 
 {
-       return ast_cli_register(&config_command_struct);
+       ast_cli_register_multiple(cli_config, sizeof(cli_config) / sizeof(struct ast_cli_entry));
+       return 0;
 }