Add category inheritance (bug #3099)
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@4925 f38db490-d61c-443f-a65b-d21fe96a405b
This commit is contained in:
parent
1addb3a944
commit
20f7932156
186
config.c
186
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;
|
||||
|
@ -257,6 +344,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)
|
||||
return cat->name;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Reference in New Issue