Add category inheritance (bug #3099)
authorMark Spencer <markster@digium.com>
Sun, 30 Jan 2005 06:26:19 +0000 (06:26 +0000)
committerMark Spencer <markster@digium.com>
Sun, 30 Jan 2005 06:26:19 +0000 (06:26 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@4925 65c4cc65-6c06-0410-ace0-fbb531ad65f3

config.c

index 92b9976..62aebe4 100755 (executable)
--- a/config.c
+++ b/config.c
@@ -62,6 +62,7 @@ struct ast_comment {
 
 struct ast_category {
        char name[80];
+       int ignored;                    /* do not let user of the config see this category */
        struct ast_variable *root;
        struct ast_variable *last;
        struct ast_category *next;
@@ -123,13 +124,59 @@ struct ast_variable *ast_variable_new(const char *name, const char *value)
        return variable;
 }
 
+static struct ast_variable *variable_get(const struct ast_category *category, const char *name)
+{
+       struct ast_variable *variable;
+
+       for (variable = category->root; variable; variable = variable->next)
+               if (!strcasecmp(variable->name, name))
+                       return variable;
+
+       return NULL;
+}
+
+static void variable_remove(struct ast_category *category, const struct ast_variable *variable)
+{
+       struct ast_variable *prev = category->root;
+
+       if (!prev)
+               return;
+
+       if (prev == variable) {
+               category->root = prev->next;
+               if (category->last == variable)
+                       category->last = NULL;
+       } else {
+               while (prev->next && (prev->next != variable)) prev = prev->next;
+               if (prev->next) {
+                       prev->next = variable->next;
+                       if (category->last == variable)
+                               category->last = prev;
+               }
+       }
+}
+
 void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
 {
-       if (category->last)
-               category->last->next = variable;
-       else
-               category->root = variable;
-       category->last = variable;
+       /* Note: this function also implements "variable replacement"... if the
+          new variable's value is empty, then existing variables of the same
+          name in the category are removed (and the new variable is destroyed)
+       */
+       if (variable->value && !ast_strlen_zero(variable->value)) {
+               if (category->last)
+                       category->last->next = variable;
+               else
+                       category->root = variable;
+               category->last = variable;
+       } else {
+               struct ast_variable *v;
+
+               while ((v = variable_get(category, variable->name))) {
+                       variable_remove(category, v);
+                       ast_variables_destroy(v);
+               }
+               ast_variables_destroy(variable);
+       }
 }
 
 void ast_variables_destroy(struct ast_variable *v)
@@ -181,6 +228,34 @@ char *ast_variable_retrieve(const struct ast_config *config, const char *categor
        return NULL;
 }
 
+static struct ast_variable *variable_clone(const struct ast_variable *old)
+{
+       struct ast_variable *new = ast_variable_new(old->name, old->value);
+
+       if (new) {
+               new->lineno = old->lineno;
+               new->object = old->object;
+               new->blanklines = old->blanklines;
+               /* TODO: clone comments? */
+       }
+
+       return new;
+}
+static void move_variables(struct ast_category *old, struct ast_category *new)
+{
+       struct ast_variable *var;
+       struct ast_variable *next;
+
+       next = old->root;
+       old->root = NULL;
+       for (var = next; var; var = next) {
+               next = var->next;
+               var->next = NULL;
+               ast_variable_append(new, var);
+       }
+}
+
 struct ast_category *ast_category_new(const char *name) 
 {
        struct ast_category *category;
@@ -194,23 +269,28 @@ struct ast_category *ast_category_new(const char *name)
        return category;
 }
 
-struct ast_category *ast_category_get(const struct ast_config *config, const char *category_name)
+static struct ast_category *category_get(const struct ast_config *config, const char *category_name, int ignored)
 {
        struct ast_category *cat;
 
        for (cat = config->root; cat; cat = cat->next) {
-               if (cat->name == category_name)
+               if (cat->name == category_name && (ignored || !cat->ignored))
                        return cat;
        }
 
        for (cat = config->root; cat; cat = cat->next) {
-               if (!strcasecmp(cat->name, category_name))
+               if (!strcasecmp(cat->name, category_name) && (ignored || !cat->ignored))
                        return cat;
        }
 
        return NULL;
 }
 
+struct ast_category *ast_category_get(const struct ast_config *config, const char *category_name)
+{
+       return category_get(config, category_name, 0);
+}
+
 int ast_category_exist(const struct ast_config *config, const char *category_name)
 {
        return !!ast_category_get(config, category_name);
@@ -232,6 +312,13 @@ void ast_category_destroy(struct ast_category *cat)
        free(cat);
 }
 
+static struct ast_category *next_available_category(struct ast_category *cat)
+{
+       for (; cat && cat->ignored; cat = cat->next);
+
+       return cat;
+}
+
 char *ast_category_browse(struct ast_config *config, const char *prev)
 {      
        struct ast_category *cat = NULL;
@@ -256,6 +343,9 @@ char *ast_category_browse(struct ast_config *config, const char *prev)
                        }
                }
        }
+       
+       if (cat)
+               cat = next_available_category(cat);
 
        config->last_browse = cat;
        if (cat)
@@ -279,6 +369,19 @@ void ast_category_rename(struct ast_category *cat, const char *name)
        strncpy(cat->name, name, sizeof(cat->name) - 1);
 }
 
+static void inherit_category(struct ast_category *new, const struct ast_category *base)
+{
+       struct ast_variable *var;
+
+       for (var = base->root; var; var = var->next) {
+               struct ast_variable *v;
+               
+               v = variable_clone(var);
+               if (v)
+                       ast_variable_append(new, v);
+       }
+}
+
 struct ast_config *ast_config_new(void) 
 {
        struct ast_config *config;
@@ -323,30 +426,69 @@ void ast_config_set_current_category(struct ast_config *cfg, const struct ast_ca
 static int process_text_line(struct ast_config *cfg, struct ast_category **cat, char *buf, int lineno, const char *configfile)
 {
        char *c;
-       char *cur;
+       char *cur = buf;
        struct ast_variable *v;
        int object;
 
-       cur = ast_strip(buf);
-       if (ast_strlen_zero(cur))
-               return 0;
-
        /* Actually parse the entry */
        if (cur[0] == '[') {
+               struct ast_category *newcat = NULL;
+               char *catname;
+
                /* A category header */
                c = strchr(cur, ']');
                if (!c) {
                        ast_log(LOG_WARNING, "parse error: no closing ']', line %d of %s\n", lineno, configfile);
                        return -1;
                }
-               *c = '\0';
+               *c++ = '\0';
                cur++;
-               *cat = ast_category_new(cur);
-               if (!*cat) {
+               if (*c++ != '(')
+                       c = NULL;
+               catname = cur;
+               *cat = newcat = ast_category_new(catname);
+               if (!newcat) {
                        ast_log(LOG_WARNING, "Out of memory, line %d of %s\n", lineno, configfile);
                        return -1;
                }
-               ast_category_append(cfg, *cat);
+               /* If there are options or categories to inherit from, process them now */
+               if (c) {
+                       if (!(cur = strchr(c, ')'))) {
+                               ast_log(LOG_WARNING, "parse error: no closing ')', line %d of %s\n", lineno, configfile);
+                               return -1;
+                       }
+                       *cur = '\0';
+                       while ((cur = strsep(&c, ","))) {
+                               if (!strcasecmp(cur, "!")) {
+                                       (*cat)->ignored = 1;
+                               } else if (!strcasecmp(cur, "+")) {
+                                       *cat = category_get(cfg, catname, 1);
+                                       if (!*cat) {
+                                               ast_destroy(cfg);
+                                               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);
+                                               return -1;
+                                       }
+                                       if (newcat) {
+                                               move_variables(newcat, *cat);
+                                               ast_category_destroy(newcat);
+                                               newcat = NULL;
+                                       }
+                               } else {
+                                       struct ast_category *base;
+                               
+                                       base = category_get(cfg, cur, 1);
+                                       if (!base) {
+                                               ast_log(LOG_WARNING, "Inheritance requested, but category '%s' does not exist, line %d of %s\n", cur, lineno, configfile);
+                                               return -1;
+                                       }
+                                       inherit_category(*cat, base);
+                               }
+                       }
+               }
+               if (newcat)
+                       ast_category_append(cfg, *cat);
        } else if (cur[0] == '#') {
                /* A directive */
                cur++;
@@ -518,9 +660,13 @@ static struct ast_config *config_text_file_load(const char *database, const char
                                                        new_buf = comment_p + 1;
                                        }
                                }
-                               if (process_buf && process_text_line(cfg, &cat, process_buf, lineno, filename)) {
-                                       cfg = NULL;
-                                       break;
+                               if (process_buf) {
+                                       char *buf = ast_strip(process_buf);
+                                       if (!ast_strlen_zero(buf))
+                                               if (process_text_line(cfg, &cat, buf, lineno, filename)) {
+                                                       cfg = NULL;
+                                                       break;
+                                               }
                                }
                        }
                }