Add a massive set of changes for converting to use the ast_debug() macro.
[asterisk/asterisk.git] / main / config.c
index d145f5b..ad71384 100644 (file)
@@ -62,6 +62,91 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 static char *extconfig_conf = "extconfig.conf";
 
+
+/*! \brief Structure to keep comments for rewriting configuration files */
+/*! \brief Structure to keep comments for rewriting configuration files */
+struct ast_comment {
+       struct ast_comment *next;
+       char cmt[0];
+};
+
+#define CB_INCR 250
+
+static void CB_INIT(char **comment_buffer, int *comment_buffer_size, char **lline_buffer, int *lline_buffer_size)
+{
+       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 **comment_buffer, int *comment_buffer_size, 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 **comment_buffer, int *comment_buffer_size, 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 **lline_buffer, int *lline_buffer_size, 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(char **comment_buffer, char **lline_buffer)  
+{ 
+       (*comment_buffer)[0] = 0; 
+       (*lline_buffer)[0] = 0;
+}
+
+
+static struct ast_comment *ALLOC_COMMENT(const char *buffer)
+{ 
+       struct ast_comment *x;
+       x = ast_calloc(1, sizeof(*x)+strlen(buffer)+1);
+       strcpy(x->cmt, buffer);
+       return x;
+}
+
+
 static struct ast_config_map {
        struct ast_config_map *next;
        char *name;
@@ -76,14 +161,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 +176,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,16 +205,18 @@ 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)
 {
        struct ast_variable *vn;
 
-       while(v) {
+       while (v) {
                vn = v;
                v = v->next;
-               free(vn);
+               ast_free(vn);
        }
 }
 
@@ -252,6 +337,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;
 }
@@ -259,7 +345,7 @@ void ast_category_append(struct ast_config *config, struct ast_category *categor
 void ast_category_destroy(struct ast_category *cat)
 {
        ast_variables_destroy(cat->root);
-       free(cat);
+       ast_free(cat);
 }
 
 static struct ast_category *next_available_category(struct ast_category *cat)
@@ -307,6 +393,7 @@ struct ast_variable *ast_category_detach_variables(struct ast_category *cat)
 
        v = cat->root;
        cat->root = NULL;
+       cat->last = NULL;
 
        return v;
 }
@@ -333,7 +420,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;
@@ -382,62 +469,49 @@ 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, unsigned int object)
 {
        struct ast_variable *cur, *prev=NULL, *newer;
-       newer = ast_variable_new(variable, value);
-       if (!newer)
+
+       if (!(newer = ast_variable_new(variable, value)))
                return -1;
-       cur = category->root;
-       while (cur) {
-               if (cur->name == variable) {
-                       newer->next = cur->next;
-                       newer->object = cur->object;
-                       if (prev)
-                               prev->next = newer;
-                       else
-                               category->root = newer;
-                       if (category->last == cur)
-                               category->last = newer;
-                       cur->next = NULL;
-                       ast_variables_destroy(cur);
-                       return 0;
-               }
-               prev = cur;
-               cur = cur->next;
-       }
+       
+       newer->object = object;
 
-       prev = NULL;
-       cur = category->root;
-       while (cur) {
-               if (!strcasecmp(cur->name, variable) && (ast_strlen_zero(match) || !strcasecmp(cur->value, match))) {
-                       newer->next = cur->next;
-                       newer->object = cur->object;
-                       if (prev)
-                               prev->next = newer;
-                       else
-                               category->root = newer;
-                       if (category->last == cur)
-                               category->last = newer;
-                       cur->next = NULL;
-                       ast_variables_destroy(cur);
-                       return 0;
-               }
-               prev = cur;
-               cur = cur->next;
+       for (cur = category->root; cur; prev = cur, cur = cur->next) {
+               if (strcasecmp(cur->name, variable) ||
+                       (!ast_strlen_zero(match) && strcasecmp(cur->value, match)))
+                       continue;
+
+               newer->next = cur->next;
+               newer->object = cur->object || object;
+               if (prev)
+                       prev->next = newer;
+               else
+                       category->root = newer;
+               if (category->last == cur)
+                       category->last = newer;
+
+               cur->next = NULL;
+               ast_variables_destroy(cur);
+
+               return 0;
        }
+
        if (prev)
                prev->next = newer;
        else
                category->root = newer;
+
        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;
-       while(cat) {
+       while (cat) {
                if (cat->name == category) {
                        ast_variables_destroy(cat->root);
                        if (prev) {
@@ -449,7 +523,7 @@ int ast_category_delete(struct ast_config *cfg, char *category)
                                if (cat == cfg->last)
                                        cfg->last = NULL;
                        }
-                       free(cat);
+                       ast_free(cat);
                        return 0;
                }
                prev = cat;
@@ -458,7 +532,7 @@ int ast_category_delete(struct ast_config *cfg, char *category)
 
        prev = NULL;
        cat = cfg->root;
-       while(cat) {
+       while (cat) {
                if (!strcasecmp(cat->name, category)) {
                        ast_variables_destroy(cat->root);
                        if (prev) {
@@ -470,7 +544,7 @@ int ast_category_delete(struct ast_config *cfg, char *category)
                                if (cat == cfg->last)
                                        cfg->last = NULL;
                        }
-                       free(cat);
+                       ast_free(cat);
                        return 0;
                }
                prev = cat;
@@ -487,13 +561,13 @@ void ast_config_destroy(struct ast_config *cfg)
                return;
 
        cat = cfg->root;
-       while(cat) {
+       while (cat) {
                ast_variables_destroy(cat->root);
                catn = cat;
                cat = cat->next;
-               free(catn);
+               ast_free(catn);
        }
-       free(cfg);
+       ast_free(cfg);
 }
 
 struct ast_category *ast_config_get_current_category(const struct ast_config *cfg)
@@ -507,7 +581,8 @@ void ast_config_set_current_category(struct ast_config *cfg, const struct ast_ca
        cfg->current = (struct ast_category *) cat;
 }
 
-static int process_text_line(struct ast_config *cfg, struct ast_category **cat, char *buf, int lineno, const char *configfile, int withcomments)
+static int process_text_line(struct ast_config *cfg, struct ast_category **cat, char *buf, int lineno, const char *configfile, int withcomments,
+                               char **comment_buffer, int *comment_buffer_size, char **lline_buffer, int *lline_buffer_size)
 {
        char *c;
        char *cur = buf;
@@ -534,6 +609,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(comment_buffer, lline_buffer);
+               
                /* If there are options or categories to inherit from, process them now */
                if (c) {
                        if (!(cur = strchr(c, ')'))) {
@@ -546,8 +631,7 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
                                        (*cat)->ignored = 1;
                                } else if (!strcasecmp(cur, "+")) {
                                        *cat = category_get(cfg, catname, 1);
-                                       if (!*cat) {
-                                               ast_config_destroy(cfg);
+                                       if (!(*cat)) {
                                                if (newcat)
                                                        ast_category_destroy(newcat);
                                                ast_log(LOG_WARNING, "Category addition requested, but category '%s' does not exist, line %d of %s\n", catname, lineno, configfile);
@@ -576,17 +660,17 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
                /* A directive */
                cur++;
                c = cur;
-               while(*c && (*c > 32)) c++;
+               while (*c && (*c > 32)) c++;
                if (*c) {
                        *c = '\0';
                        /* Find real argument */
                        c = ast_skip_blanks(c + 1);
-                       if (!*c)
+                       if (!(*c))
                                c = NULL;
                } else 
                        c = NULL;
                do_include = !strcasecmp(cur, "include");
-               if(!do_include)
+               if (!do_include)
                        do_exec = !strcasecmp(cur, "exec");
                else
                        do_exec = 0;
@@ -597,7 +681,7 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
                if (do_include || do_exec) {
                        if (c) {
                                /* Strip off leading and trailing "'s and <>'s */
-                               while((*c == '<') || (*c == '>') || (*c == '\"')) c++;
+                               while ((*c == '<') || (*c == '>') || (*c == '\"')) c++;
                                /* Get rid of leading mess */
                                cur = c;
                                while (!ast_strlen_zero(cur)) {
@@ -618,9 +702,9 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
                                        exec_file[0] = '\0';
                                /* A #include */
                                do_include = ast_config_internal_load(cur, cfg, withcomments) ? 1 : 0;
-                               if(!ast_strlen_zero(exec_file))
+                               if (!ast_strlen_zero(exec_file))
                                        unlink(exec_file);
-                               if(!do_include)
+                               if (!do_include)
                                        return 0;
 
                        } else {
@@ -635,7 +719,7 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
                        ast_log(LOG_WARNING, "Unknown directive '%s' at line %d of %s\n", cur, lineno, configfile);
        } else {
                /* Just a line (variable = value) */
-               if (!*cat) {
+               if (!(*cat)) {
                        ast_log(LOG_WARNING,
                                "parse error: No category context for line %d of %s\n", lineno, configfile);
                        return -1;
@@ -656,13 +740,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(comment_buffer, lline_buffer);
+                               
                        } else {
                                return -1;
                        }
                } else {
                        ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
                }
-
        }
        return 0;
 }
@@ -678,6 +771,13 @@ static struct ast_config *config_text_file_load(const char *database, const char
        struct ast_category *cat = NULL;
        int count = 0;
        struct stat statbuf;
+       /*! Growable string buffer */
+       char *comment_buffer=0;   /*!< this will be a comment collector.*/
+       int   comment_buffer_size=0;  /*!< the amount of storage so far alloc'd for the comment_buffer */
+
+       char *lline_buffer=0;    /*!< A buffer for stuff behind the ; */
+       int  lline_buffer_size=0;
+
        
        cat = ast_config_get_current_category(cfg);
 
@@ -687,6 +787,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(&comment_buffer, &comment_buffer_size, &lline_buffer, &lline_buffer_size);
+               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;
@@ -722,31 +829,35 @@ static struct ast_config *config_text_file_load(const char *database, const char
                        fflush(stdout);
                }
                if (!(f = fopen(fn, "r"))) {
-                       if (option_debug)
-                               ast_log(LOG_DEBUG, "No file to parse: %s\n", fn);
+                       ast_debug(1, "No file to parse: %s\n", fn);
                        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);
+               ast_debug(1, "Parsing %s\n", fn);
                if (option_verbose > 1)
                        ast_verbose("Found\n");
-               while(!feof(f)) {
+               while (!feof(f)) {
                        lineno++;
                        if (fgets(buf, sizeof(buf), f)) {
+                               if ( withcomments ) {
+                                       CB_ADD(&comment_buffer, &comment_buffer_size, 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 */
                                                memmove(comment_p - 1, comment_p, strlen(comment_p) + 1);
                                                new_buf = comment_p;
-                                       } else if(comment_p[1] == COMMENT_TAG && comment_p[2] == COMMENT_TAG && (comment_p[3] != '-')) {
+                                       } else if (comment_p[1] == COMMENT_TAG && comment_p[2] == COMMENT_TAG && (comment_p[3] != '-')) {
                                                /* Meta-Comment start detected ";--" */
                                                if (comment < MAX_NESTED_COMMENTS) {
                                                        *comment_p = '\0';
@@ -768,6 +879,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(&comment_buffer, &comment_buffer_size, ";");
+                                                                       CB_ADD_LEN(&comment_buffer, &comment_buffer_size, oldptr+1, new_buf-oldptr-1);
+                                                               }
+                                                               
                                                                memmove(oldptr, new_buf, strlen(new_buf) + 1);
                                                                new_buf = oldptr;
                                                        } else
@@ -777,16 +893,24 @@ 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(&lline_buffer, &lline_buffer_size, comment_p);
+                                                       }
                                                        *comment_p = '\0'; 
                                                        new_buf = comment_p;
                                                } else
                                                        new_buf = comment_p + 1;
                                        }
                                }
+                               if ( withcomments && comment && !process_buf )
+                               {
+                                       CB_ADD(&comment_buffer, &comment_buffer_size, buf);  /* the whole line is a comment, store it */
+                               }
+                               
                                if (process_buf) {
                                        char *buf = ast_strip(process_buf);
                                        if (!ast_strlen_zero(buf)) {
-                                               if (process_text_line(cfg, &cat, buf, lineno, filename, withcomments)) {
+                                               if (process_text_line(cfg, &cat, buf, lineno, fn, withcomments, &comment_buffer, &comment_buffer_size, &lline_buffer, &lline_buffer_size)) {
                                                        cfg = NULL;
                                                        break;
                                                }
@@ -795,7 +919,7 @@ static struct ast_config *config_text_file_load(const char *database, const char
                        }
                }
                fclose(f);              
-       } while(0);
+       } while (0);
        if (comment) {
                ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment]);
        }
@@ -807,6 +931,16 @@ static struct ast_config *config_text_file_load(const char *database, const char
                        }
                }
 #endif
+
+       if (cfg && cfg->include_level == 1 && withcomments && comment_buffer) {
+               ast_free(comment_buffer);
+               ast_free(lline_buffer);
+               comment_buffer = NULL;
+               lline_buffer = NULL;
+               comment_buffer_size = 0;
+               lline_buffer_size = 0;
+       }
+       
        if (count == 0)
                return NULL;
 
@@ -821,6 +955,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] == '/') {
@@ -836,21 +971,42 @@ int config_text_file_save(const char *configfile, const struct ast_config *cfg,
        if ((f = fopen(fn, "w"))) {
 #endif     
                if (option_verbose > 1)
-                       ast_verbose(  VERBOSE_PREFIX_2 "Saving '%s': ", fn);
+                       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) {
+               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) {
+                       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) {
@@ -870,10 +1026,9 @@ int config_text_file_save(const char *configfile, const struct ast_config *cfg,
                if ((option_verbose > 1) && !option_debug)
                        ast_verbose("Saved\n");
        } else {
-               if (option_debug)
-                       printf("Unable to open for writing: %s\n", fn);
+               ast_debug(1, "Unable to open for writing: %s\n", fn);
                if (option_verbose > 1)
-                       printf( "Unable to write (%s)", strerror(errno));
+                       ast_verbose(VERBOSE_PREFIX_2 "Unable to write (%s)", strerror(errno));
                return -1;
        }
        fclose(f);
@@ -889,7 +1044,7 @@ static void clear_config_maps(void)
        while (config_maps) {
                map = config_maps;
                config_maps = config_maps->next;
-               free(map);
+               ast_free(map);
        }
                
        ast_mutex_unlock(&config_lock);
@@ -934,7 +1089,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();
 
@@ -950,6 +1105,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++;
@@ -1081,7 +1239,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);
@@ -1109,6 +1267,8 @@ struct ast_config *ast_config_internal_load(const char *filename, struct ast_con
 
        if (result)
                result->include_level--;
+       else
+               cfg->include_level--;
 
        return result;
 }
@@ -1145,23 +1305,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) {
+                       ast_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)
 {
@@ -1174,6 +1372,12 @@ int ast_check_realtime(const char *family)
 
 }
 
+/*! \brief Check if there's any realtime engines loaded */
+int ast_realtime_enabled()
+{
+       return config_maps ? 1 : 0;
+}
+
 struct ast_config *ast_load_realtime_multientry(const char *family, ...)
 {
        struct ast_config_engine *eng;
@@ -1208,6 +1412,38 @@ int ast_update_realtime(const char *family, const char *keyfield, const char *lo
        return res;
 }
 
+int ast_store_realtime(const char *family, ...) {
+       struct ast_config_engine *eng;
+       int res = -1;
+       char db[256]="";
+       char table[256]="";
+       va_list ap;
+
+       va_start(ap, family);
+       eng = find_engine(family, db, sizeof(db), table, sizeof(table));
+       if (eng && eng->store_func) 
+               res = eng->store_func(db, table, ap);
+       va_end(ap);
+
+       return res;
+}
+
+int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup, ...) {
+       struct ast_config_engine *eng;
+       int res = -1;
+       char db[256]="";
+       char table[256]="";
+       va_list ap;
+
+       va_start(ap, lookup);
+       eng = find_engine(family, db, sizeof(db), table, sizeof(table));
+       if (eng && eng->destroy_func) 
+               res = eng->destroy_func(db, table, keyfield, lookup, ap);
+       va_end(ap);
+
+       return res;
+}
+
 static int config_command(int fd, int argc, char **argv) 
 {
        struct ast_config_engine *eng;
@@ -1232,11 +1468,11 @@ static int config_command(int fd, int argc, char **argv)
 }
 
 static char show_config_help[] =
-       "Usage: core list config mappings\n"
+       "Usage: core show config mappings\n"
        "       Shows the filenames to config engines.\n";
 
 static struct ast_cli_entry cli_config[] = {
-       { { "core", "list", "config", "mappings", NULL },
+       { { "core", "show", "config", "mappings", NULL },
        config_command, "Display config mappings (file names to config engines)",
        show_config_help },
 };