[asterisk-commits] gtjoseph: branch 13 r425384 - in /branches/13: ./ apps/ include/asterisk/ mai...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Mon Oct 13 11:10:13 CDT 2014


Author: gtjoseph
Date: Mon Oct 13 11:10:06 2014
New Revision: 425384

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=425384
Log:
manager/config: Support templates and non-unique category names via AMI

This patch provides the capability to manipulate templates and categories
with non-unique names via AMI.

Summary of changes:

GetConfig and GetConfigJSON: Added "Filter" parameter:  A comma separated list
of name_regex=value_regex expressions which will cause only categories whose
variables match all expressions to be considered.  The special variable name
TEMPLATES can be used to control whether templates are included.  Passing
'include' as the value will include templates along with normal categories.
Passing 'restrict' as the value will restrict the operation to ONLY templates.
Not specifying a TEMPLATES expression results in the current default behavior
which is to not include templates.

UpdateConfig: NewCat now includes options for allowing duplicate category
names, indicating if the category should be created as a template, and
specifying templates the category should inherit from.  The rest of the
actions now accept a filter string as defined above.  If there are non-unique
category names, you can now update specific ones based on variable values.

To facilitate the new capabilities in manager, corresponding changes had to be
made to config, most notably the addition of filter criteria to many of the
APIs.  In some cases it was easy to change the references to use the new
prototype but others would have required touching too many files for this
patch so a wrapper with the original prototype was created.  Macros couldn't
be used in this case because it would break binary compatibility with modules
such as res_digium_phone that are linked to real symbols.

Tested-by: George Joseph

Review: https://reviewboard.asterisk.org/r/4033/
........

Merged revisions 425383 from http://svn.asterisk.org/svn/asterisk/branches/12

Modified:
    branches/13/   (props changed)
    branches/13/apps/app_directory.c
    branches/13/apps/app_voicemail.c
    branches/13/include/asterisk/config.h
    branches/13/main/config.c
    branches/13/main/manager.c
    branches/13/pbx/pbx_realtime.c
    branches/13/res/res_sorcery_config.c
    branches/13/res/res_sorcery_realtime.c
    branches/13/tests/test_config.c
    branches/13/tests/test_sorcery.c
    branches/13/tests/test_sorcery_realtime.c

Propchange: branches/13/
------------------------------------------------------------------------------
Binary property 'branch-12-merged' - no diff available.

Modified: branches/13/apps/app_directory.c
URL: http://svnview.digium.com/svn/asterisk/branches/13/apps/app_directory.c?view=diff&rev=425384&r1=425383&r2=425384
==============================================================================
--- branches/13/apps/app_directory.c (original)
+++ branches/13/apps/app_directory.c Mon Oct 13 11:10:06 2014
@@ -528,7 +528,7 @@
 		}
 
 		/* Does the context exist within the config file? If not, make one */
-		if (!(cat = ast_category_get(cfg, ctx))) {
+		if (!(cat = ast_category_get(cfg, ctx, NULL))) {
 			if (!(cat = ast_category_new(ctx, "", 99999))) {
 				ast_log(LOG_WARNING, "Out of memory\n");
 				ast_config_destroy(cfg);

Modified: branches/13/apps/app_voicemail.c
URL: http://svnview.digium.com/svn/asterisk/branches/13/apps/app_voicemail.c?view=diff&rev=425384&r1=425383&r2=425384
==============================================================================
--- branches/13/apps/app_voicemail.c (original)
+++ branches/13/apps/app_voicemail.c Mon Oct 13 11:10:06 2014
@@ -1821,7 +1821,7 @@
 						new = ast_alloca((strlen(value) + strlen(newpassword) + 1));
 						sprintf(new, "%s%s", newpassword, value);
 					}
-					if (!(cat = ast_category_get(cfg, category))) {
+					if (!(cat = ast_category_get(cfg, category, NULL))) {
 						ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
 						break;
 					}
@@ -1858,7 +1858,7 @@
 					}
 					new = ast_alloca(strlen(newpassword) + 1);
 					sprintf(new, "%s", newpassword);
-					if (!(cat = ast_category_get(cfg, category))) {
+					if (!(cat = ast_category_get(cfg, category, NULL))) {
 						ast_debug(4, "failed to get category!\n");
 						ast_free(var);
 						break;
@@ -7791,7 +7791,7 @@
 				char duration_buf[12];
 
 				*duration += prepend_duration;
-				msg_cat = ast_category_get(msg_cfg, "message");
+				msg_cat = ast_category_get(msg_cfg, "message", NULL);
 				snprintf(duration_buf, 11, "%ld", *duration);
 				if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
 					ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
@@ -11930,7 +11930,7 @@
 		return -1;
 	}
 
-	cat = ast_category_get(msg_cfg, "message");
+	cat = ast_category_get(msg_cfg, "message", NULL);
 	if (!cat) {
 		ast_log(LOG_ERROR, "Voicemail data file %s/%d.txt has no [message] category?\n", dir, msg);
 		ast_variables_destroy(var);

Modified: branches/13/include/asterisk/config.h
URL: http://svnview.digium.com/svn/asterisk/branches/13/include/asterisk/config.h?view=diff&rev=425384&r1=425383&r2=425384
==============================================================================
--- branches/13/include/asterisk/config.h (original)
+++ branches/13/include/asterisk/config.h Mon Oct 13 11:10:06 2014
@@ -203,10 +203,30 @@
 								int (*comparator)(struct ast_category *p, struct ast_category *q));
 
 /*!
- * \brief Goes through categories
+ * \brief Browse categories with filters
  *
  * \param config Which config structure you wish to "browse"
- * \param prev A pointer to a previous category.
+ * \param category_name An optional category name.
+ * Pass NULL to not restrict by category name.
+ * \param prev A pointer to the starting category structure.
+ * Pass NULL to start at the beginning.
+ * \param filter An optional comma-separated list of <name_regex>=<value_regex>
+ * pairs.  Only categories with matching variables will be returned.
+ * The special name 'TEMPLATES' can be used with the special values
+ * 'include' or 'restrict' to include templates in the result or
+ * restrict the result to only templates.
+ *
+ * \retval a category on success
+ * \retval NULL on failure/no-more-categories
+ */
+struct ast_category *ast_category_browse_filtered(struct ast_config *config,
+	const char *category_name, struct ast_category *prev, const char *filter);
+
+/*!
+ * \brief Browse categories
+ *
+ * \param config Which config structure you wish to "browse"
+ * \param prev_name A pointer to a previous category name.
  *
  * \details
  * This function is kind of non-intuitive in it's use.
@@ -216,13 +236,20 @@
  * as the second pointer, and it will return a pointer to the category name
  * afterwards.
  *
- * \retval a category on success
+ * \retval a category name on success
  * \retval NULL on failure/no-more-categories
  */
-char *ast_category_browse(struct ast_config *config, const char *prev);
-
-/*!
- * \brief Goes through variables
+char *ast_category_browse(struct ast_config *config, const char *prev_name);
+
+/*!
+ * \brief Browse variables
+ * \param config Which config structure you wish to "browse"
+ * \param category_name Which category to "browse"
+ * \param filter an optional comma-separated list of <name_regex>=<value_regex>
+ * pairs.  Only categories with matching variables will be browsed.
+ * The special name 'TEMPLATES' can be used with the special values
+ * 'include' or 'restrict' to include templates in the result or
+ * restrict the result to only templates.
  *
  * \details
  * Somewhat similar in intent as the ast_category_browse.
@@ -231,7 +258,10 @@
  * \retval ast_variable list on success
  * \retval NULL on failure
  */
-struct ast_variable *ast_variable_browse(const struct ast_config *config, const char *category);
+struct ast_variable *ast_variable_browse_filtered(const struct ast_config *config,
+	const char *category_name, const char *filter);
+struct ast_variable *ast_variable_browse(const struct ast_config *config,
+	const char *category_name);
 
 /*!
  * \brief given a pointer to a category, return the root variable.
@@ -243,25 +273,50 @@
 struct ast_variable *ast_category_first(struct ast_category *cat);
 
 /*!
- * \brief Gets a variable
+ * \brief Gets a variable by context and variable names
  *
  * \param config which (opened) config to use
  * \param category category under which the variable lies
  * \param variable which variable you wish to get the data for
- *
- * \details
- * Goes through a given config file in the given category and searches for the given variable
+ * \param filter an optional comma-separated list of <name_regex>=<value_regex>
+ * pairs.  Only categories with matching variables will be searched.
+ * The special name 'TEMPLATES' can be used with the special values
+ * 'include' or 'restrict' to include templates in the result or
+ * restrict the result to only templates.
  *
  * \retval The variable value on success
  * \retval NULL if unable to find it.
  */
-const char *ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable);
+const char *ast_variable_retrieve_filtered(struct ast_config *config,
+	const char *category, const char *variable, const char *filter);
+const char *ast_variable_retrieve(struct ast_config *config,
+	const char *category, const char *variable);
+
+/*!
+ * \brief Gets a variable from a specific category structure
+ *
+ * \param category category structure under which the variable lies
+ * \param variable which variable you wish to get the data for
+ *
+ * \details
+ * Goes through a given category and searches for the given variable
+ *
+ * \retval The variable value on success
+ * \retval NULL if unable to find it.
+ */
+const char *ast_variable_find(const struct ast_category *category, const char *variable);
 
 /*!
  * \brief Retrieve a category if it exists
  *
  * \param config which config to use
  * \param category_name name of the category you're looking for
+ * \param filter If a config contains more than 1 category with the same name,
+ * you can specify a filter to narrow the search.  The filter is a comma-separated
+ * list of <name_regex>=<value_regex> pairs.  Only a category with matching
+ * variables will be returned. The special name 'TEMPLATES' can be used with the
+ * special values 'include' or 'restrict' to include templates in the result or
+ * restrict the result to only templates.
  *
  * \details
  * This will search through the categories within a given config file for a match.
@@ -269,20 +324,57 @@
  * \retval pointer to category if found
  * \retval NULL if not.
  */
-struct ast_category *ast_category_get(const struct ast_config *config, const char *category_name);
+struct ast_category *ast_category_get(const struct ast_config *config,
+	const char *category_name, const char *filter);
+
+/*!
+ * \brief Return the name of the category
+ *
+ * \param category category structure
+ *
+ * \retval pointer to category name if found
+ * \retval NULL if not.
+ */
+const char *ast_category_get_name(const struct ast_category *category);
+
+/*!
+ * \brief Check if category is a template
+ *
+ * \param category category structure
+ *
+ * \retval 1 if a template.
+ * \retval 0 if not.
+ */
+int ast_category_is_template(const struct ast_category *category);
+
+/*!
+ * \brief Return the template names this category inherits from
+ *
+ * \param category category structure
+ *
+ * \return an ast_str (which must be freed after use) with a comma
+ * separated list of templates names or NULL if there were no templates.
+ */
+struct ast_str *ast_category_get_templates(const struct ast_category *category);
 
 /*!
  * \brief Check for category duplicates
  *
  * \param config which config to use
  * \param category_name name of the category you're looking for
+ * \param filter an optional comma-separated list of <name_regex>=<value_regex>
+ * pairs.  Only categories with matching variables will be returned.
+ * The special name 'TEMPLATES' can be used with the special values
+ * 'include' or 'restrict' to include templates in the result or
+ * restrict the result to only templates.
  *
  * \details
  * This will search through the categories within a given config file for a match.
  *
  * \return non-zero if found
  */
-int ast_category_exist(const struct ast_config *config, const char *category_name);
+int ast_category_exist(const struct ast_config *config, const char *category_name,
+	const char *filter);
 
 /*!
  * \brief Retrieve realtime configuration
@@ -661,9 +753,23 @@
  */
 const char *ast_config_option(struct ast_config *cfg, const char *cat, const char *var);
 
-/*! \brief Create a category structure */
+/*!
+ * \brief Create a category
+ *
+ * \param name name of new category
+ * \param in_file filename which contained the new config
+ * \param lineno line number
+ */
 struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno);
-void ast_category_append(struct ast_config *config, struct ast_category *cat);
+
+/*!
+ * \brief Create a category making it a template
+ *
+ * \param name name of new template
+ * \param in_file filename which contained the new config
+ * \param lineno line number
+ */
+struct ast_category *ast_category_new_template(const char *name, const char *in_file, int lineno);
 
 /*!
  * \brief Inserts new category
@@ -677,17 +783,49 @@
  * matching the match parameter.
  *
  * \retval 0 if succeeded
- * \retval -1 if NULL parameters or match category was not found
+ * \retval -1 if the specified match category wasn't found
  */
 int ast_category_insert(struct ast_config *config, struct ast_category *cat, const char *match);
-int ast_category_delete(struct ast_config *cfg, const char *category);
-
-/*!
- * \brief Removes and destroys all variables within a category
- * \retval 0 if the category was found and emptied
- * \retval -1 if the category was not found
- */
-int ast_category_empty(struct ast_config *cfg, const char *category);
+
+/*!
+ * \brief Delete a category
+ *
+ * \param config which config to use
+ * \param category category to delete
+ *
+ * \return the category after the deleted one which could be NULL.
+ */
+struct ast_category *ast_category_delete(struct ast_config *cfg, struct ast_category *category);
+
+/*!
+ * \brief Appends a category to a config
+ *
+ * \param config which config to use
+ * \param cat category to insert
+ */
+void ast_category_append(struct ast_config *config, struct ast_category *cat);
+
+/*!
+ * \brief Applies base (template) to category.
+ *
+ * \param existing existing category
+ * \param base base category
+ *
+ * \details
+ * This function is used to apply a base (template) to an existing category
+ */
+void ast_category_inherit(struct ast_category *existing, const struct ast_category *base);
+
+/*!
+ * \brief Removes and destroys all variables in a category
+ *
+ * \param category category to empty
+ *
+ * \retval 0 if succeeded
+ * \retval -1 if categopry is NULL
+ */
+int ast_category_empty(struct ast_category *category);
+
 void ast_category_destroy(struct ast_category *cat);
 struct ast_variable *ast_category_detach_variables(struct ast_category *cat);
 void ast_category_rename(struct ast_category *cat, const char *name);

Modified: branches/13/main/config.c
URL: http://svnview.digium.com/svn/asterisk/branches/13/main/config.c?view=diff&rev=425384&r1=425383&r2=425384
==============================================================================
--- branches/13/main/config.c (original)
+++ branches/13/main/config.c Mon Oct 13 11:10:06 2014
@@ -40,6 +40,7 @@
 #include <sys/stat.h>
 
 #include <math.h>	/* HUGE_VAL */
+#include <regex.h>
 
 #define AST_INCLUDE_GLOB 1
 
@@ -71,6 +72,7 @@
 static struct ao2_container *cfg_hooks;
 static void config_hook_exec(const char *filename, const char *module, const struct ast_config *cfg);
 inline struct ast_variable *variable_list_switch(struct ast_variable *l1, struct ast_variable *l2);
+static int does_category_match(struct ast_category *cat, const char *category_name, const char *match);
 
 /*! \brief Structure to keep comments for rewriting configuration files */
 struct ast_comment {
@@ -232,6 +234,8 @@
 	struct ast_variable *root;
 	/*! Last category variable in the list. */
 	struct ast_variable *last;
+	/*! Previous node in the list. */
+	struct ast_category *prev;
 	/*! Next node in the list. */
 	struct ast_category *next;
 };
@@ -599,16 +603,12 @@
 
 struct ast_variable *ast_variable_browse(const struct ast_config *config, const char *category)
 {
-	struct ast_category *cat = NULL;
-
-	if (!category) {
-		return NULL;
-	}
+	struct ast_category *cat;
 
 	if (config->last_browse && (config->last_browse->name == category)) {
 		cat = config->last_browse;
 	} else {
-		cat = ast_category_get(config, category);
+		cat = ast_category_get(config, category, NULL);
 	}
 
 	return (cat) ? cat->root : NULL;
@@ -677,29 +677,37 @@
 	return tmp;
 }
 
-
-const char *ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable)
+const char *ast_variable_retrieve(struct ast_config *config,
+	const char *category, const char *variable)
+{
+	return ast_variable_retrieve_filtered(config, category, variable, NULL);
+}
+
+const char *ast_variable_retrieve_filtered(struct ast_config *config,
+	const char *category, const char *variable, const char *filter)
+{
+	struct ast_category *cat = NULL;
+	const char *value;
+
+	while ((cat = ast_category_browse_filtered(config, category, cat, filter))) {
+		value = ast_variable_find(cat, variable);
+		if (value) {
+			return value;
+		}
+	}
+
+	return NULL;
+}
+
+const char *ast_variable_find(const struct ast_category *category, const char *variable)
 {
 	struct ast_variable *v;
 
-	if (category) {
-		for (v = ast_variable_browse(config, category); v; v = v->next) {
-			if (!strcasecmp(variable, v->name)) {
-				return v->value;
-			}
-		}
-	} else {
-		struct ast_category *cat;
-
-		for (cat = config->root; cat; cat = cat->next) {
-			for (v = cat->root; v; v = v->next) {
-				if (!strcasecmp(variable, v->name)) {
-					return v->value;
-				}
-			}
-		}
-	}
-
+	for (v = category->root; v; v = v->next) {
+		if (!strcasecmp(variable, v->name)) {
+			return v->value;
+		}
+	}
 	return NULL;
 }
 
@@ -726,7 +734,99 @@
 	ast_variable_append(new, var);
 }
 
-struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno)
+/*! \brief Returns true if ALL of the regex expressions and category name match.
+ * Both can be NULL (I.E. no predicate) which results in a true return;
+ */
+static int does_category_match(struct ast_category *cat, const char *category_name, const char *match)
+{
+	char *dupmatch;
+	char *nvp = NULL;
+	int match_found = 0, match_expressions = 0;
+	int template_ok = 0;
+
+	/* Only match on category name if it's not a NULL or empty string */
+	if (!ast_strlen_zero(category_name) && strcasecmp(cat->name, category_name)) {
+		return 0;
+	}
+
+	/* If match is NULL or empty, automatically match if not a template */
+	if (ast_strlen_zero(match)) {
+		return !cat->ignored;
+	}
+
+	dupmatch = ast_strdupa(match);
+
+	while ((nvp = ast_strsep(&dupmatch, ',', AST_STRSEP_STRIP))) {
+		struct ast_variable *v;
+		char *match_name;
+		char *match_value = NULL;
+		char *regerr;
+		int rc;
+		regex_t r_name, r_value;
+
+		match_expressions++;
+
+		match_name = ast_strsep(&nvp, '=', AST_STRSEP_STRIP);
+		match_value = ast_strsep(&nvp, '=', AST_STRSEP_STRIP);
+
+		/* an empty match value is OK.  A NULL match value (no =) is NOT. */
+		if (match_value == NULL) {
+			break;
+		}
+
+		if (!strcmp("TEMPLATES", match_name)) {
+			if (!strcasecmp("include", match_value)) {
+				if (cat->ignored) {
+					template_ok = 1;
+				}
+				match_found++;
+			} else if (!strcasecmp("restrict", match_value)) {
+				if (cat->ignored) {
+					match_found++;
+					template_ok = 1;
+				} else {
+					break;
+				}
+			}
+			continue;
+		}
+
+		if ((rc = regcomp(&r_name, match_name, REG_EXTENDED | REG_NOSUB))) {
+			regerr = ast_alloca(128);
+			regerror(rc, &r_name, regerr, 128);
+			ast_log(LOG_ERROR, "Regular expression '%s' failed to compile: %s\n",
+				match_name, regerr);
+			regfree(&r_name);
+			return 0;
+		}
+		if ((rc = regcomp(&r_value, match_value, REG_EXTENDED | REG_NOSUB))) {
+			regerr = ast_alloca(128);
+			regerror(rc, &r_value, regerr, 128);
+			ast_log(LOG_ERROR, "Regular expression '%s' failed to compile: %s\n",
+				match_value, regerr);
+			regfree(&r_name);
+			regfree(&r_value);
+			return 0;
+		}
+
+		for (v = cat->root; v; v = v->next) {
+			if (!regexec(&r_name, v->name, 0, NULL, 0)
+				&& !regexec(&r_value, v->value, 0, NULL, 0)) {
+				match_found++;
+				break;
+			}
+		}
+		regfree(&r_name);
+		regfree(&r_value);
+	}
+	if (match_found == match_expressions && (!cat->ignored || template_ok)) {
+		return 1;
+	}
+	return 0;
+}
+
+
+static struct ast_category *new_category(const char *name, const char *in_file, int lineno, int template)
 {
 	struct ast_category *category;
 
@@ -741,44 +841,85 @@
 	}
 	ast_copy_string(category->name, name, sizeof(category->name));
 	category->lineno = lineno; /* if you don't know the lineno, set it to 999999 or something real big */
+	category->ignored = template;
 	return category;
 }
 
-static struct ast_category *category_get(const struct ast_config *config, const char *category_name, int ignored)
+struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno)
+{
+	return new_category(name, in_file, lineno, 0);
+}
+
+struct ast_category *ast_category_new_template(const char *name, const char *in_file, int lineno)
+{
+	return new_category(name, in_file, lineno, 1);
+}
+
+struct ast_category *ast_category_get(const struct ast_config *config,
+	const char *category_name, const char *filter)
 {
 	struct ast_category *cat;
 
-	/* try exact match first, then case-insensitive match */
 	for (cat = config->root; cat; cat = cat->next) {
-		if (cat->name == category_name && (ignored || !cat->ignored))
+		if (does_category_match(cat, category_name, filter)) {
 			return cat;
-	}
-
-	for (cat = config->root; cat; cat = cat->next) {
-		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);
+const char *ast_category_get_name(const struct ast_category *category)
+{
+	return category->name;
+}
+
+int ast_category_is_template(const struct ast_category *category)
+{
+	return category->ignored;
+}
+
+struct ast_str *ast_category_get_templates(const struct ast_category *category)
+{
+	struct ast_category_template_instance *template;
+	struct ast_str *str;
+	int first = 1;
+
+	if (AST_LIST_EMPTY(&category->template_instances)) {
+		return NULL;
+	}
+
+	str = ast_str_create(128);
+	if (!str) {
+		return NULL;
+	}
+
+	AST_LIST_TRAVERSE(&category->template_instances, template, next) {
+		ast_str_append(&str, 0, "%s%s", first ? "" : ",", template->name);
+		first = 0;
+	}
+
+	return str;
+}
+
+int ast_category_exist(const struct ast_config *config, const char *category_name,
+	const char *filter)
+{
+	return !!ast_category_get(config, category_name, filter);
 }
 
 void ast_category_append(struct ast_config *config, struct ast_category *category)
 {
-	if (config->last)
+	if (config->last) {
 		config->last->next = category;
-	else
+		category->prev = config->last;
+	} else {
 		config->root = category;
+		category->prev = NULL;
+	}
+	category->next = NULL;
 	category->include_level = config->include_level;
+
 	config->last = category;
 	config->current = category;
 }
@@ -787,19 +928,26 @@
 {
 	struct ast_category *cur_category;
 
-	if (!config || !cat || !match) {
+	if (!config || !config->root || !cat || !match) {
 		return -1;
 	}
+
 	if (!strcasecmp(config->root->name, match)) {
 		cat->next = config->root;
+		cat->prev = NULL;
+		config->root->prev = cat;
 		config->root = cat;
 		return 0;
 	}
-	for (cur_category = config->root; cur_category && cur_category->next;
-		cur_category = cur_category->next) {
-		if (!strcasecmp(cur_category->next->name, match)) {
-			cat->next = cur_category->next;
-			cur_category->next = cat;
+
+	for (cur_category = config->root->next; cur_category; cur_category = cur_category->next) {
+		if (!strcasecmp(cur_category->name, match)) {
+			cat->prev = cur_category->prev;
+			cat->prev->next = cat;
+
+			cat->next = cur_category;
+			cur_category->prev = cat;
+
 			return 0;
 		}
 	}
@@ -841,9 +989,10 @@
 	}
 }
 
-static struct ast_category *next_available_category(struct ast_category *cat)
-{
-	for (; cat && cat->ignored; cat = cat->next);
+static struct ast_category *next_available_category(struct ast_category *cat,
+	const char *name, const char *filter)
+{
+	for (; cat && !does_category_match(cat, name, filter); cat = cat->next);
 
 	return cat;
 }
@@ -856,7 +1005,7 @@
 
 struct ast_variable *ast_category_root(struct ast_config *config, char *cat)
 {
-	struct ast_category *category = ast_category_get(config, cat);
+	struct ast_category *category = ast_category_get(config, cat, NULL);
 
 	if (category)
 		return category->root;
@@ -1021,10 +1170,27 @@
 	}
 
 	if (cat)
-		cat = next_available_category(cat);
+		cat = next_available_category(cat, NULL, NULL);
 
 	config->last_browse = cat;
 	return (cat) ? cat->name : NULL;
+}
+
+struct ast_category *ast_category_browse_filtered(struct ast_config *config,
+	const char *category_name, struct ast_category *prev, const char *filter)
+{
+	struct ast_category *cat;
+
+	if (!prev) {
+		prev = config->root;
+	} else {
+		prev = prev->next;
+	}
+
+	cat = next_available_category(prev, category_name, filter);
+	config->last_browse = cat;
+
+	return cat;
 }
 
 struct ast_variable *ast_category_detach_variables(struct ast_category *cat)
@@ -1043,7 +1209,7 @@
 	ast_copy_string(cat->name, name, sizeof(cat->name));
 }
 
-static void inherit_category(struct ast_category *new, const struct ast_category *base)
+void ast_category_inherit(struct ast_category *new, const struct ast_category *base)
 {
 	struct ast_variable *var;
 	struct ast_category_template_instance *x;
@@ -1147,65 +1313,45 @@
 	return -1;
 }
 
-int ast_category_delete(struct ast_config *cfg, const char *category)
-{
-	struct ast_category *prev=NULL, *cat;
-
-	cat = cfg->root;
-	while (cat) {
-		if (cat->name == category) {
-			if (prev) {
-				prev->next = cat->next;
-				if (cat == cfg->last)
-					cfg->last = prev;
-			} else {
-				cfg->root = cat->next;
-				if (cat == cfg->last)
-					cfg->last = NULL;
-			}
-			ast_category_destroy(cat);
-			return 0;
-		}
-		prev = cat;
-		cat = cat->next;
-	}
-
-	prev = NULL;
-	cat = cfg->root;
-	while (cat) {
-		if (!strcasecmp(cat->name, category)) {
-			if (prev) {
-				prev->next = cat->next;
-				if (cat == cfg->last)
-					cfg->last = prev;
-			} else {
-				cfg->root = cat->next;
-				if (cat == cfg->last)
-					cfg->last = NULL;
-			}
-			ast_category_destroy(cat);
-			return 0;
-		}
-		prev = cat;
-		cat = cat->next;
-	}
-	return -1;
-}
-
-int ast_category_empty(struct ast_config *cfg, const char *category)
-{
-	struct ast_category *cat;
-
-	for (cat = cfg->root; cat; cat = cat->next) {
-		if (strcasecmp(cat->name, category))
-			continue;
-		ast_variables_destroy(cat->root);
-		cat->root = NULL;
-		cat->last = NULL;
-		return 0;
-	}
-
-	return -1;
+struct ast_category *ast_category_delete(struct ast_config *config,
+	struct ast_category *category)
+{
+	struct ast_category *prev;
+
+	if (!config || !category) {
+		return NULL;
+	}
+
+	if (category->prev) {
+		category->prev->next = category->next;
+	} else {
+		config->root = category->next;
+	}
+
+	if (category->next) {
+		category->next->prev = category->prev;
+	} else {
+		config->last = category->prev;
+	}
+
+	prev = category->prev;
+
+	ast_category_destroy(category);
+
+	return prev;
+}
+
+int ast_category_empty(struct ast_category *category)
+{
+	if (!category) {
+		return -1;
+	}
+
+	ast_variables_destroy(category->root);
+	category->root = NULL;
+	category->last = NULL;
+
+	return 0;
 }
 
 void ast_config_destroy(struct ast_config *cfg)
@@ -1489,7 +1635,7 @@
 				if (!strcasecmp(cur, "!")) {
 					(*cat)->ignored = 1;
 				} else if (!strcasecmp(cur, "+")) {
-					*cat = category_get(cfg, catname, 1);
+					*cat = ast_category_get(cfg, catname, NULL);
 					if (!(*cat)) {
 						if (newcat)
 							ast_category_destroy(newcat);
@@ -1504,12 +1650,12 @@
 				} else {
 					struct ast_category *base;
 
-					base = category_get(cfg, cur, 1);
+					base = ast_category_get(cfg, cur, "TEMPLATES=restrict");
 					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);
+					ast_category_inherit(*cat, base);
 				}
 			}
 		}

Modified: branches/13/main/manager.c
URL: http://svnview.digium.com/svn/asterisk/branches/13/main/manager.c?view=diff&rev=425384&r1=425383&r2=425384
==============================================================================
--- branches/13/main/manager.c (original)
+++ branches/13/main/manager.c Mon Oct 13 11:10:06 2014
@@ -354,10 +354,24 @@
 			<parameter name="Category">
 				<para>Category in configuration file.</para>
 			</parameter>
+			<parameter name="Filter">
+				<para>A comma separated list of
+				<replaceable>name_regex</replaceable>=<replaceable>value_regex</replaceable>
+				expressions which will cause only categories whose variables match all expressions
+				to be considered.  The special variable name <literal>TEMPLATES</literal>
+				can be used to control whether templates are included.  Passing
+				<literal>include</literal> as the value will include templates
+				along with normal categories. Passing
+				<literal>restrict</literal> as the value will restrict the operation to
+				ONLY templates.  Not specifying a <literal>TEMPLATES</literal> expression
+				results in the default behavior which is to not include templates.</para>
+			</parameter>
 		</syntax>
 		<description>
 			<para>This action will dump the contents of a configuration
-			file by category and contents or optionally by specified category only.</para>
+			file by category and contents or optionally by specified category only.
+			In the case where a category name is non-unique, a filter may be specified
+			to match only categories with matching variable values.</para>
 		</description>
 	</manager>
 	<manager name="GetConfigJSON" language="en_US">
@@ -369,11 +383,19 @@
 			<parameter name="Filename" required="true">
 				<para>Configuration filename (e.g. <filename>foo.conf</filename>).</para>
 			</parameter>
+			<parameter name="Category">
+				<para>Category in configuration file.</para>
+			</parameter>
+			<parameter name="Filter">
+				<xi:include xpointer="xpointer(/docs/manager[@name='GetConfig']/syntax/parameter[@name='Filter']/para[1])" />
+			</parameter>
 		</syntax>
 		<description>
 			<para>This action will dump the contents of a configuration file by category
-			and contents in JSON format. This only makes sense to be used using rawman over
-			the HTTP interface.</para>
+			and contents in JSON format or optionally by specified category only.
+			This only makes sense to be used using rawman over the HTTP interface.
+			In the case where a category name is non-unique, a filter may be specified
+			to match only categories with matching variable values.</para>
 		</description>
 	</manager>
 	<manager name="UpdateConfig" language="en_US">
@@ -391,9 +413,9 @@
 			<parameter name="Reload">
 				<para>Whether or not a reload should take place (or name of specific module).</para>
 			</parameter>
-			<parameter name="Action-XXXXXX">
+			<parameter name="Action-000000">
 				<para>Action to take.</para>
-				<para>X's represent 6 digit number beginning with 000000.</para>
+				<para>0's represent 6 digit number beginning with 000000.</para>
 				<enumlist>
 					<enum name="NewCat" />
 					<enum name="RenameCat" />
@@ -405,25 +427,58 @@
 					<enum name="Insert" />
 				</enumlist>
 			</parameter>
-			<parameter name="Cat-XXXXXX">
+			<parameter name="Cat-000000">
 				<para>Category to operate on.</para>
-				<xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-XXXXXX']/para[2])" />
-			</parameter>
-			<parameter name="Var-XXXXXX">
+				<xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-000000']/para[2])" />
+			</parameter>
+			<parameter name="Var-000000">
 				<para>Variable to work on.</para>
-				<xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-XXXXXX']/para[2])" />
-			</parameter>
-			<parameter name="Value-XXXXXX">
+				<xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-000000']/para[2])" />
+			</parameter>
+			<parameter name="Value-000000">
 				<para>Value to work on.</para>
-				<xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-XXXXXX']/para[2])" />
-			</parameter>
-			<parameter name="Match-XXXXXX">
+				<xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-000000']/para[2])" />
+			</parameter>
+			<parameter name="Match-000000">
 				<para>Extra match required to match line.</para>
-				<xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-XXXXXX']/para[2])" />
-			</parameter>
-			<parameter name="Line-XXXXXX">
+				<xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-000000']/para[2])" />
+			</parameter>
+			<parameter name="Line-000000">
 				<para>Line in category to operate on (used with delete and insert actions).</para>
-				<xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-XXXXXX']/para[2])" />
+				<xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-000000']/para[2])" />
+			</parameter>
+			<parameter name="Options-000000">
+				<para>A comma separated list of action-specific options.</para>
+					<enumlist>
+						<enum name="NewCat"><para>One or more of the following... </para>
+							<enumlist>
+								<enum name="allowdups"><para>Allow duplicate category names.</para></enum>
+								<enum name="template"><para>This category is a template.</para></enum>
+								<enum name="inherit="template[,...]""><para>Templates from which to inherit.</para></enum>
+							</enumlist>
+						</enum>
+					</enumlist>
+					<para> </para>
+						<para>The following actions share the same options...</para>
+					<enumlist>
+						<enum name="RenameCat"/>
+						<enum name="DelCat"/>
+						<enum name="EmptyCat"/>
+						<enum name="Update"/>
+						<enum name="Delete"/>
+						<enum name="Append"/>
+						<enum name="Insert"><para> </para>
+							<enumlist>
+								<enum name="catfilter="<expression>[,...]""><para> </para>
+									<xi:include xpointer="xpointer(/docs/manager[@name='GetConfig']/syntax/parameter[@name='Filter']/para[1])" />
+									<para><literal>catfilter</literal> is most useful when a file
+									contains multiple categories with the same name and you wish to
+									operate on specific ones instead of all of them.</para>
+								</enum>
+							</enumlist>
+						</enum>
+					</enumlist>
+				<xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-000000']/para[2])" />
 			</parameter>
 		</syntax>
 		<description>
@@ -1175,7 +1230,8 @@
 	FAILURE_EMPTYCAT,
 	FAILURE_UPDATE,
 	FAILURE_DELETE,
-	FAILURE_APPEND
+	FAILURE_APPEND,
+	FAILURE_TEMPLATE
 };
 
 enum add_filter_result {
@@ -3157,9 +3213,11 @@
 	struct ast_config *cfg;
 	const char *fn = astman_get_header(m, "Filename");
 	const char *category = astman_get_header(m, "Category");
+	const char *filter = astman_get_header(m, "Filter");
+	const char *category_name;
 	int catcount = 0;
 	int lineno = 0;
-	char *cur_category = NULL;
+	struct ast_category *cur_category = NULL;
 	struct ast_variable *v;
 	struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
 
@@ -3167,6 +3225,7 @@
 		astman_send_error(s, m, "Filename not specified");
 		return 0;
 	}
+
 	cfg = ast_config_load2(fn, "manager", config_flags);
 	if (cfg == CONFIG_STATUS_FILEMISSING) {
 		astman_send_error(s, m, "Config file not found");
@@ -3177,19 +3236,34 @@
 	}
 
 	astman_start_ack(s, m);
-	while ((cur_category = ast_category_browse(cfg, cur_category))) {
-		if (ast_strlen_zero(category) || (!ast_strlen_zero(category) && !strcmp(category, cur_category))) {
-			lineno = 0;
-			astman_append(s, "Category-%06d: %s\r\n", catcount, cur_category);
-			for (v = ast_variable_browse(cfg, cur_category); v; v = v->next) {
-				astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
-			}
-			catcount++;
-		}
-	}
+	while ((cur_category = ast_category_browse_filtered(cfg, category, cur_category, filter))) {
+		struct ast_str *templates;
+
+		category_name = ast_category_get_name(cur_category);
+		lineno = 0;
+		astman_append(s, "Category-%06d: %s\r\n", catcount, category_name);
+
+		if (ast_category_is_template(cur_category)) {
+			astman_append(s, "IsTemplate-%06d: %d\r\n", catcount, 1);
+		}
+
+		if ((templates = ast_category_get_templates(cur_category))
+			&& ast_str_strlen(templates) > 0) {
+			astman_append(s, "Templates-%06d: %s\r\n", catcount, ast_str_buffer(templates));
+			ast_free(templates);
+		}
+
+		for (v = ast_category_first(cur_category); v; v = v->next) {
+			astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
+		}
+
+		catcount++;
+	}
+
 	if (!ast_strlen_zero(category) && catcount == 0) { /* TODO: actually, a config with no categories doesn't even get loaded */
 		astman_append(s, "No categories found\r\n");
 	}
+
 	ast_config_destroy(cfg);
 	astman_append(s, "\r\n");
 
@@ -3200,7 +3274,8 @@
 {
 	struct ast_config *cfg;
 	const char *fn = astman_get_header(m, "Filename");
-	char *category = NULL;
+	const char *match = astman_get_header(m, "Match");
+	struct ast_category *category = NULL;
 	struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
 	int catcount = 0;
 
@@ -3208,6 +3283,7 @@
 		astman_send_error(s, m, "Filename not specified");
 		return 0;
 	}
+
 	if (!(cfg = ast_config_load2(fn, "manager", config_flags))) {
 		astman_send_error(s, m, "Config file not found");
 		return 0;
@@ -3215,22 +3291,22 @@
 		astman_send_error(s, m, "Config file has invalid format");
 		return 0;
 	}
+
 	astman_start_ack(s, m);
-	while ((category = ast_category_browse(cfg, category))) {
-		astman_append(s, "Category-%06d: %s\r\n", catcount, category);
+	while ((category = ast_category_browse_filtered(cfg, NULL, category, match))) {
+		astman_append(s, "Category-%06d: %s\r\n", catcount, ast_category_get_name(category));
 		catcount++;
 	}
+
 	if (catcount == 0) { /* TODO: actually, a config with no categories doesn't even get loaded */
 		astman_append(s, "Error: no categories found\r\n");
 	}
+
 	ast_config_destroy(cfg);
 	astman_append(s, "\r\n");
 
 	return 0;
 }
-
-
-
 
 /*! The amount of space in out must be at least ( 2 * strlen(in) + 1 ) */
 static void json_escape(char *out, const char *in)
@@ -3266,7 +3342,10 @@
 {
 	struct ast_config *cfg;
 	const char *fn = astman_get_header(m, "Filename");
-	char *category = NULL;
+	const char *filter = astman_get_header(m, "Filter");

[... 1070 lines stripped ...]



More information about the asterisk-commits mailing list