[asterisk-commits] twilson: branch twilson/config_docs r370480 - in /team/twilson/config_docs: a...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Wed Jul 25 00:04:53 CDT 2012


Author: twilson
Date: Wed Jul 25 00:04:51 2012
New Revision: 370480

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=370480
Log:
Just checking in for backup purposes

Changes:
1) Revamp to more hierarchical XML for mmichelson
2) Add a wrapper for doing XPath queries
3) Add default and regex=yes/no info to configInfo XML at runtime
4) Add runtime CLI to dump XML to file so wiki, etc. can use the runtime-added
   XML content
5) Add CLI tab completion

Things to do:
1) Make CLI output prettier
2) Add matchInfo/syntax XML docs to aco_type
3) Add more documentation
4) Things are a bit rough around the edges, do cleanup
5) Currently big WARNING messages are printed because only app_skel has xmldocs.
   Eventually, those docs will be added and instead of just warning we will
   fail to load if xmldocs are enabled and there is missing documentation.


Modified:
    team/twilson/config_docs/apps/app_skel.c
    team/twilson/config_docs/doc/appdocsxml.dtd
    team/twilson/config_docs/include/asterisk/config_options.h
    team/twilson/config_docs/include/asterisk/xml.h
    team/twilson/config_docs/include/asterisk/xmldoc.h
    team/twilson/config_docs/main/config_options.c
    team/twilson/config_docs/main/xml.c
    team/twilson/config_docs/main/xmldoc.c

Modified: team/twilson/config_docs/apps/app_skel.c
URL: http://svnview.digium.com/svn/asterisk/team/twilson/config_docs/apps/app_skel.c?view=diff&rev=370480&r1=370479&r2=370480
==============================================================================
--- team/twilson/config_docs/apps/app_skel.c (original)
+++ team/twilson/config_docs/apps/app_skel.c Wed Jul 25 00:04:51 2012
@@ -78,67 +78,49 @@
 		</description>
 	</application>
 
-	<configInfo module="app_skel" language="en_US">
-		<configObject name="globals">
-			<synopsis>Options that apply globally to app_skel</synopsis>
-			<syntax><category match="true">^general$</category></syntax>
-		</configObject>
-		<configObject name="sounds">
-			<synopsis>Prompts for SkelGuessNumber to play</synopsis>
-			<syntax><category match="true">^sounds$</category></syntax>
-		</configObject>
-		<configObject name="level">
-			<synopsis>Defined levels for the SkelGuessNumber game</synopsis>
-			<syntax><category match="false">^(general|sounds)$</category></syntax>
-		</configObject>
-
+	<configInfo name="app_skel" language="en_US">
 		<configFile name="app_skel.conf">
-			<hasObject ref="globals"/>
-			<hasObject ref="sounds"/>
-			<hasObject ref="level"/>
+			<configObject name="globals">
+				<synopsis>Options that apply globally to app_skel</synopsis>
+				<configOption name="games">
+					<synopsis>The number of games a single execution of SkelGuessNumber will play</synopsis>
+				</configOption>
+				<configOption name="cheat">
+					<synopsis>Should the computer cheat?</synopsis>
+					<description><para>If enabled, the computer will ignore winning guesses.</para></description> 
+				</configOption>
+			</configObject>
+			<configObject name="sounds">
+				<synopsis>Prompts for SkelGuessNumber to play</synopsis>
+				<configOption name="prompt" default="please-enter-your&amp;number&amp;queue-less-than">
+					<synopsis>A prompt directing the user to enter a number less than the max number</synopsis>
+				</configOption>
+				<configOption name="wrong_guess" default="vm-pls-try-again">
+					<synopsis>The sound file to play when a wrong guess is made</synopsis>
+				</configOption>
+				<configOption name="right_guess" default="auth-thankyou">
+					<synopsis>The sound file to play when a correct guess is made</synopsis>
+				</configOption>
+				<configOption name="too_low">
+					<synopsis>The sound file to play when a guess is too low</synopsis>
+				</configOption>
+				<configOption name="too_high">
+					<synopsis>The sound file to play when a guess is too high</synopsis>
+				</configOption>
+				<configOption name="lose" default="vm-goodbye">
+					<synopsis>The sound file to play when a player loses</synopsis>
+				</configOption>
+			</configObject>
+			<configObject name="level">
+				<synopsis>Defined levels for the SkelGuessNumber game</synopsis>
+				<configOption name="max_number">
+					<synopsis>The maximum in the range of numbers to guess (1 is the implied minimum)</synopsis>
+				</configOption>
+				<configOption name="max_guesses">
+					<synopsis>The maximum number of guesses before a game is considered lost</synopsis>
+				</configOption>
+			</configObject>
 		</configFile>
-
-		<configOption name="games" objectType="globals" default="3">
-			<synopsis>The number of games a single execution of SkelGuessNumber will play</synopsis>
-			<syntax><dataType type="uint" min="1" max="1000"/></syntax>
-		</configOption>
-		<configOption name="cheat" objectType="globals" default="no">
-			<synopsis>Should the computer cheat?</synopsis>
-			<description><para>If enabled, the computer will ignore winning guesses.</para></description> 
-			<syntax><dataType type="boolean"/></syntax>
-		</configOption>
-		<configOption name="prompt" objectType="sounds" default="please-enter-your&amp;number&amp;queue-less-than">
-			<synopsis>A prompt directing the user to enter a number less than the max number</synopsis>
-			<syntax><dataType type="string"/></syntax>
-		</configOption>
-		<configOption name="wrong_guess" objectType="sounds" default="vm-pls-try-again">
-			<synopsis>The sound file to play when a wrong guess is made</synopsis>
-			<syntax><dataType type="string"/></syntax>
-		</configOption>
-		<configOption name="right_guess" objectType="sounds" default="auth-thankyou">
-			<synopsis>The sound file to play when a correct guess is made</synopsis>
-			<syntax><dataType type="string"/></syntax>
-		</configOption>
-		<configOption name="too_low" objectType="sounds" default="low">
-			<synopsis>The sound file to play when a guess is too low</synopsis>
-			<syntax><dataType type="string"/></syntax>
-		</configOption>
-		<configOption name="too_high" objectType="sounds" default="high">
-			<synopsis>The sound file to play when a guess is too high</synopsis>
-			<syntax><dataType type="string"/></syntax>
-		</configOption>
-		<configOption name="lose" objectType="sounds" default="vm-goodbye">
-			<synopsis>The sound file to play when a player loses</synopsis>
-			<syntax><dataType type="string"/></syntax>
-		</configOption>
-		<configOption name="max_number" objectType="level">
-			<synopsis>The maximum in the range of numbers to guess (1 is the implied minimum)</synopsis>
-			<syntax><dataType type="uint"/></syntax>
-		</configOption>
-		<configOption name="max_guesses" objectType="level">
-			<synopsis>The maximum number of guesses before a game is considered lost</synopsis>
-			<syntax><dataType type="uint"/></syntax>
-		</configOption>
 	</configInfo>
  ***/
 

Modified: team/twilson/config_docs/doc/appdocsxml.dtd
URL: http://svnview.digium.com/svn/asterisk/team/twilson/config_docs/doc/appdocsxml.dtd?view=diff&rev=370480&r1=370479&r2=370480
==============================================================================
--- team/twilson/config_docs/doc/appdocsxml.dtd (original)
+++ team/twilson/config_docs/doc/appdocsxml.dtd Wed Jul 25 00:04:51 2012
@@ -36,23 +36,34 @@
   <!ELEMENT managerEventInstance (synopsis?,syntax?,description?,see-also?)*>
   <!ATTLIST managerEventInstance class CDATA #REQUIRED>
 
-  <!ELEMENT configInfo (configObject*,configFile*,configOption*)>
-  <!ATTLIST configInfo module CDATA #REQUIRED>
+  <!ELEMENT configInfo (configFile+)>
+  <!ATTLIST configInfo name CDATA #REQUIRED>
   <!ATTLIST configInfo language CDATA #REQUIRED>
 
-  <!ELEMENT configObject (synopsis?,syntax?)*>
+  <!ELEMENT configFile (configObject+)>
+  <!ATTLIST configFile name CDATA #REQUIRED>
+
+  <!ELEMENT configObject (synopsis?,description?,syntax?,configOption+)>
   <!ATTLIST configObject name ID #REQUIRED>
 
   <!ELEMENT configOption (synopsis,description?,syntax?)*>
   <!ATTLIST configOption name CDATA #REQUIRED>
-  <!ATTLIST configOption objectType IDREF #REQUIRED>
+  <!ATTLIST configOption regex (yes|no|true|false) "false">
   <!ATTLIST configOption default CDATA #IMPLIED>
 
-  <!ELEMENT configFile (hasObject?)*>
-  <!ATTLIST configFile name CDATA #REQUIRED>
+  <!ELEMENT matchInfo (category,field?)>
 
-  <!ELEMENT hasObject EMPTY>
-  <!ATTLIST hasObject ref IDREF #REQUIRED>
+  <!ELEMENT category (#PCDATA)>
+  <!ATTLIST category regex CDATA #REQUIRED>
+  <!ATTLIST category match (yes|no|true|false) #REQUIRED>
+
+  <!ELEMENT field (#PCDATA)>
+  <!ATTLIST field name CDATA #REQUIRED>
+
+  <!ELEMENT dataType EMPTY>
+  <!ATTLIST dataType type (string|uint|integer|boolean|float|enum) #REQUIRED>
+  <!ATTLIST dataType max CDATA #IMPLIED>
+  <!ATTLIST dataType min CDATA #IMPLIED>
 
   <!ELEMENT info (para|note|warning|variablelist|enumlist|info|xi:include)*>
   <!ATTLIST info name CDATA #REQUIRED>
@@ -65,15 +76,6 @@
   <!ATTLIST ref type (application|function|astcli|link|manpage|filename|agi|manager|managerEvent) #REQUIRED>
 
   <!ELEMENT synopsis (#PCDATA)>
-
-  <!ELEMENT category (#PCDATA)>
-  <!ATTLIST category match CDATA #IMPLIED>
-  <!ATTLIST category exact CDATA #IMPLIED>
-
-  <!ELEMENT dataType EMPTY>
-  <!ATTLIST dataType type (string|uint|integer|boolean|float|enum) #REQUIRED>
-  <!ATTLIST dataType max CDATA #IMPLIED>
-  <!ATTLIST dataType min CDATA #IMPLIED>
 
   <!ELEMENT syntax (parameter|dataType|category|xi:include)*>
   <!ATTLIST syntax argsep CDATA ",">

Modified: team/twilson/config_docs/include/asterisk/config_options.h
URL: http://svnview.digium.com/svn/asterisk/team/twilson/config_docs/include/asterisk/config_options.h?view=diff&rev=370480&r1=370479&r2=370480
==============================================================================
--- team/twilson/config_docs/include/asterisk/config_options.h (original)
+++ team/twilson/config_docs/include/asterisk/config_options.h Wed Jul 25 00:04:51 2012
@@ -512,6 +512,7 @@
  *
  * \param info The aco_info holding this module's config information
  * \param name The name of the option
+ * \param match_type Whether the field is matched exactly or by regex
  * \param types An array of valid option types for matching categories to the correct struct type
  * \param default_val The default value of the option in the same format as defined in a config file
  * \param type The option type (only for default handlers)

Modified: team/twilson/config_docs/include/asterisk/xml.h
URL: http://svnview.digium.com/svn/asterisk/team/twilson/config_docs/include/asterisk/xml.h?view=diff&rev=370480&r1=370479&r2=370480
==============================================================================
--- team/twilson/config_docs/include/asterisk/xml.h (original)
+++ team/twilson/config_docs/include/asterisk/xml.h Wed Jul 25 00:04:51 2012
@@ -207,6 +207,13 @@
  * \brief Dump the specified document to a file. */
 int ast_xml_doc_dump_file(FILE *output, struct ast_xml_doc *doc);
 
+struct ast_xml_xpath_results;
+void ast_xml_xpath_results_free(struct ast_xml_xpath_results *results);
+int ast_xml_xpath_num_results(struct ast_xml_xpath_results *results);
+struct ast_xml_node *ast_xml_xpath_results(struct ast_xml_xpath_results *results);
+/*!
+ * \brief Execute an XPath query on an XML document */
+struct ast_xml_xpath_results *ast_xml_node_find_xpath(struct ast_xml_doc *doc, const char *xpath_str);
 /* Features using ast_xml_ */
 #ifdef HAVE_LIBXML2
 #define AST_XML_DOCS

Modified: team/twilson/config_docs/include/asterisk/xmldoc.h
URL: http://svnview.digium.com/svn/asterisk/team/twilson/config_docs/include/asterisk/xmldoc.h?view=diff&rev=370480&r1=370479&r2=370480
==============================================================================
--- team/twilson/config_docs/include/asterisk/xmldoc.h (original)
+++ team/twilson/config_docs/include/asterisk/xmldoc.h Wed Jul 25 00:04:51 2012
@@ -63,10 +63,15 @@
 		AST_STRING_FIELD(type);
 		/*! Reference to another field */
 		AST_STRING_FIELD(ref);
+		/*! Default value */
+		AST_STRING_FIELD(default_value);
 	);
 	/*! The next XML documentation item that matches the same name/item type */
 	struct ast_xml_doc_item *next;
+	unsigned int regex;
 };
+
+void ast_xml_doc_update_config_option(const char *module, const char *name, const char *object_name, const char *default_value, unsigned int regex);
 
 /*!
  *  \brief Get the syntax for a specified application or function.

Modified: team/twilson/config_docs/main/config_options.c
URL: http://svnview.digium.com/svn/asterisk/team/twilson/config_docs/main/config_options.c?view=diff&rev=370480&r1=370479&r2=370480
==============================================================================
--- team/twilson/config_docs/main/config_options.c (original)
+++ team/twilson/config_docs/main/config_options.c Wed Jul 25 00:04:51 2012
@@ -159,6 +159,9 @@
 			}
 			return -1;
 		}
+#ifdef AST_XML_DOCS
+		ast_xml_doc_update_config_option(AST_MODULE, opt->name, type->name, opt->default_val, opt->match_type == ACO_REGEX);
+#endif
 	}
 	return 0;
 }
@@ -188,31 +191,32 @@
 	return 0;
 }
 
-static int docs_exist(const char *module, struct aco_type **types, const char *name)
-{
 #ifdef AST_XML_DOCS
-	RAII_VAR(struct ast_xml_doc_item *, item, ao2_find(xmldocs, module, OBJ_KEY), ao2_cleanup);
-	struct ast_xml_doc_item *iter = item;
-	if (!item) {
-		return 0;
-	}
-
+/*! \brief Find a particular ast_xml_doc_item by module, types, and name
+ */
+static struct ast_xml_doc_item *find_docs(struct ast_xml_doc_item *config_info, struct aco_type **types, const char *name)
+{
+	struct ast_xml_doc_item *iter = config_info;
+
+	if (!iter) {
+		return NULL;
+	}
 	/* First is just the configInfo, we can skip it */
 	while ((iter = iter->next)) {
-		if (!strcasecmp(iter->name, name)) {
-			size_t x;
-			for (x = 0; types[x]; x++) {
-				if (!strcasecmp(types[x]->name, iter->ref)) {
-					return 1;
-				}
+		size_t x;
+		if (strcasecmp(iter->name, name)) {
+			continue;
+		}
+		for (x = 0; types[x]; x++) {
+			/* All we care about is that at least one type has the option */
+			if (!strcasecmp(types[x]->name, iter->ref)) {
+				return iter;
 			}
 		}
 	}
-	return 0;
-#else
-	return 1;
+	return NULL;
+}
 #endif
-}
 
 int __aco_option_register(struct aco_info *info, const char *name, enum aco_matchtype matchtype, struct aco_type **types,
 	const char *default_val, enum aco_option_type kind, aco_option_handler handler, unsigned int flags, size_t argc, ...)
@@ -220,14 +224,6 @@
 	struct aco_option *opt;
 	va_list ap;
 	int tmp;
-
-#ifdef AST_XML_DOCS
-	/* XXX If no documentation has been registered, fail with warning */
-	if (!docs_exist(info->module, types, name)) {
-		ast_log(LOG_ERROR, "XML Documentation for option '%s' in modules '%s' not found!\n", name, info->module);
-		/* return -1; */
-	}
-#endif
 
 	/* Custom option types require a handler */
 	if (!handler && kind == OPT_CUSTOM_T) {
@@ -272,6 +268,23 @@
 		ao2_ref(opt, -1);
 		return -1;
 	}
+
+#ifdef AST_XML_DOCS
+	/* XXX If no documentation has been registered, fail with warning */
+	{
+		RAII_VAR(struct ast_xml_doc_item *, config_info, ao2_find(xmldocs, info->module, OBJ_KEY), ao2_cleanup);
+		struct ast_xml_doc_item *config_option;
+
+		if (!config_info || !(config_option = find_docs(config_info, types, name))) {
+			ast_log(LOG_ERROR, "XML Documentation for option '%s' in modules '%s' not found!\n", name, info->module);
+			/* ao2_ref(opt, -1);
+			 * return -1; */
+			return 0;
+		}
+		config_option->regex = opt->match_type == ACO_REGEX;
+		ast_string_field_set(config_option, default_value, opt->default_val);
+	}
+#endif
 
 	return 0;
 }
@@ -724,6 +737,82 @@
 	return 0;
 }
 
+static char *complete_config_module(const char *word, int pos, int state)
+{
+	char *c = NULL;
+	size_t wordlen = strlen(word);
+	int which = 0;
+	struct ao2_iterator i;
+	struct ast_xml_doc_item *cur;
+
+	if (pos != 3) {
+		return NULL;
+	}
+
+	i = ao2_iterator_init(xmldocs, 0);
+	while ((cur = ao2_iterator_next(&i))) {
+		if (!strncasecmp(word, cur->name, wordlen) && ++which > state) {
+			c = ast_strdup(cur->name);
+			ao2_ref(cur, -1);
+			break;
+		}
+		ao2_ref(cur, -1);
+	}
+	ao2_iterator_destroy(&i);
+
+	return c;
+}
+
+static char *complete_config_type(const char *module, const char *word, int pos, int state)
+{
+	char *c = NULL;
+	size_t wordlen = strlen(word);
+	int which = 0;
+	RAII_VAR(struct ast_xml_doc_item *, info, NULL, ao2_cleanup);
+	struct ast_xml_doc_item *cur = info;
+
+	if (pos != 4) {
+		return NULL;
+	}
+
+	if (!(cur = ao2_find(xmldocs, module, OBJ_KEY))) {
+		return NULL;
+	}
+
+	while ((cur = cur->next)) {
+		if (!strcasecmp(cur->type, "configObject") && !strncasecmp(word, cur->name, wordlen) && ++which > state) {
+			c = ast_strdup(cur->name);
+			break;
+		}
+	}
+	return c;
+}
+
+static char *complete_config_option(const char *module, const char *option, const char *word, int pos, int state)
+{
+	char *c = NULL;
+	size_t wordlen = strlen(word);
+	int which = 0;
+	RAII_VAR(struct ast_xml_doc_item *, info, NULL, ao2_cleanup);
+	struct ast_xml_doc_item *cur = info;
+
+	if (pos != 5) {
+		return NULL;
+	}
+
+	if (!(cur = ao2_find(xmldocs, module, OBJ_KEY))) {
+		return NULL;
+	}
+
+	while ((cur = cur->next)) {
+		if (!strcasecmp(cur->type, "configOption") && !strcasecmp(cur->ref, option) && !strncasecmp(word, cur->name, wordlen) && ++which > state) {
+			c = ast_strdup(cur->name);
+			break;
+		}
+	}
+	return c;
+}
+
 static char *cli_show_types(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
 	RAII_VAR(struct ast_xml_doc_item *, item, NULL, ao2_cleanup);
@@ -737,7 +826,7 @@
 			"   Lists a module's different config object types.\n";
 		return NULL;
 	case CLI_GENERATE:
-		return NULL;
+		return complete_config_module(a->word, a->pos, a->n);
 	}
 
 	if (a->argc != 4) {
@@ -769,7 +858,11 @@
 			"   Display detailed information about a config object type.\n";
 		return NULL;
 	case CLI_GENERATE:
-		return NULL;
+		switch(a->pos) {
+		case 3: return complete_config_module(a->word, a->pos, a->n);
+		case 4: return complete_config_type(a->argv[3], a->word, a->pos, a->n);
+		default: return NULL;
+		}
 	}
 
 	if (a->argc != 5) {
@@ -805,7 +898,11 @@
 			"   Lists a module's different config options.\n";
 		return NULL;
 	case CLI_GENERATE:
-		return NULL;
+		switch(a->pos) {
+		case 3: return complete_config_module(a->word, a->pos, a->n);
+		case 4: return complete_config_type(a->argv[3], a->word, a->pos, a->n);
+		default: return NULL;
+		}
 	}
 
 	if (a->argc < 4 || a->argc > 5) {
@@ -838,7 +935,12 @@
 			"   Display detailed information about a config option.\n";
 		return NULL;
 	case CLI_GENERATE:
-		return NULL;
+		switch(a->pos) {
+		case 3: return complete_config_module(a->word, a->pos, a->n);
+		case 4: return complete_config_type(a->argv[3], a->word, a->pos, a->n);
+		case 5: return complete_config_option(a->argv[3], a->argv[4], a->word, a->pos, a->n);
+		default: return NULL;
+		}
 	}
 
 	if (a->argc != 6) {
@@ -852,6 +954,7 @@
 	while ((tmp = tmp->next)) {
 		if (!strcasecmp(tmp->type, "configOption") && !strcasecmp(tmp->ref, a->argv[4]) && !strcasecmp(tmp->name, a->argv[5])) {
 		   	ast_cli(a->fd, "<%s> %s\n", tmp->ref, tmp->name);
+			ast_cli(a->fd, "Default: %s\n", S_OR(tmp->default_value, "n/a"));
 			ast_cli(a->fd, "Synopsis: %s\n", AS_OR(tmp->synopsis, "n/a"));
 			ast_cli(a->fd, "Description:\n\t%s\n", AS_OR(tmp->description, "n/a"));
 			ast_cli(a->fd, "Syntax:\n\t%s\n", AS_OR(tmp->syntax, "n/a"));

Modified: team/twilson/config_docs/main/xml.c
URL: http://svnview.digium.com/svn/asterisk/team/twilson/config_docs/main/xml.c?view=diff&rev=370480&r1=370479&r2=370480
==============================================================================
--- team/twilson/config_docs/main/xml.c (original)
+++ team/twilson/config_docs/main/xml.c Wed Jul 25 00:04:51 2012
@@ -35,6 +35,7 @@
 #include <libxml/parser.h>
 #include <libxml/tree.h>
 #include <libxml/xinclude.h>
+#include <libxml/xpath.h>
 /* libxml2 ast_xml implementation. */
 
 
@@ -316,5 +317,42 @@
 	return (struct ast_xml_node *) ((xmlNode *) node)->parent;
 }
 
+struct ast_xml_node *ast_xml_xpath_results(struct ast_xml_xpath_results *results)
+{
+	return (struct ast_xml_node *) ((xmlXPathObjectPtr) results)->nodesetval->nodeTab[0];
+}
+
+void ast_xml_xpath_results_free(struct ast_xml_xpath_results *results)
+{
+	xmlXPathFreeObject((xmlXPathObjectPtr) results);
+}
+
+int ast_xml_xpath_num_results(struct ast_xml_xpath_results *results)
+{
+	return ((xmlXPathObjectPtr) results)->nodesetval->nodeNr;
+}
+
+struct ast_xml_xpath_results *ast_xml_node_find_xpath(struct ast_xml_doc *doc, const char *xpath_str)
+{
+	xmlXPathContextPtr context;
+	xmlXPathObjectPtr result;
+	if (!(context = xmlXPathNewContext((xmlDoc *) doc))) {
+		ast_log(LOG_ERROR, "Could not create XPath context!\n");
+		return NULL;
+	}
+	result = xmlXPathEvalExpression((xmlChar *) xpath_str, context);
+	xmlXPathFreeContext(context);
+	if (!result) {
+		ast_log(LOG_WARNING, "Error for query: %s\n", xpath_str);
+		return NULL;
+	}
+	if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
+		xmlXPathFreeObject(result);
+		ast_log(LOG_WARNING, "No results for query: %s\n", xpath_str);
+		return NULL;
+	}
+	return (struct ast_xml_xpath_results *) result;
+}
+
 #endif /* defined(HAVE_LIBXML2) */
 

Modified: team/twilson/config_docs/main/xmldoc.c
URL: http://svnview.digium.com/svn/asterisk/team/twilson/config_docs/main/xmldoc.c?view=diff&rev=370480&r1=370479&r2=370480
==============================================================================
--- team/twilson/config_docs/main/xmldoc.c (original)
+++ team/twilson/config_docs/main/xmldoc.c Wed Jul 25 00:04:51 2012
@@ -38,6 +38,7 @@
 #include "asterisk/term.h"
 #include "asterisk/astobj2.h"
 #include "asterisk/xmldoc.h"
+#include "asterisk/cli.h"
 
 #ifdef AST_XML_DOCS
 
@@ -2262,6 +2263,66 @@
 	return item;
 }
 
+static struct ast_xml_xpath_results *find_config_option(const char *module, const char * object_name, const char *name)
+{
+	struct ast_xml_xpath_results *results = NULL;
+	struct documentation_tree *doctree;
+	RAII_VAR(struct ast_str *, xpath_str, ast_str_create(128), ast_free);
+
+	ast_str_set(&xpath_str, 0, "//configObject[@name='%s']/configOption[@name='%s']", object_name, name);
+
+	AST_RWLIST_RDLOCK(&xmldoc_tree);
+	AST_LIST_TRAVERSE(&xmldoc_tree, doctree, entry) {
+		if (!(results = ast_xml_node_find_xpath(doctree->doc, ast_str_buffer(xpath_str)))) {
+			continue;
+		}
+		break;
+	}
+	AST_RWLIST_UNLOCK(&xmldoc_tree);
+
+	return results;
+}
+
+void ast_xml_doc_update_config_option(const char *module, const char *name, const char *object_name, const char *default_value, unsigned int regex)
+{
+	RAII_VAR(struct ast_xml_xpath_results *, results, find_config_option(module, object_name, name), ast_xml_xpath_results_free);
+	struct ast_xml_node *option;
+
+
+	if (!results) {
+		return;
+	}
+
+	if (!(option = ast_xml_xpath_results(results))) {
+		return;
+	}
+	ast_xml_set_attribute(option, "regex", regex ? "true" : "false");
+	ast_xml_set_attribute(option, "default", default_value);
+}
+
+static void build_config_docs(struct ast_xml_node *cur, struct ast_xml_doc_item **tail)
+{
+	struct ast_xml_node *iter;
+	struct ast_xml_doc_item *item;
+	
+	for (iter = ast_xml_node_get_children(cur); iter; iter = ast_xml_node_get_next(iter)) {
+		if (strncasecmp(ast_xml_node_get_name(iter), "config", 6)) {
+			continue;
+		}
+		/* Now add all of the child config-related items to the list */
+		if (!(item = xmldoc_build_documentation_item(iter, ast_xml_get_attribute(iter, "name"), ast_xml_node_get_name(iter)))) {
+			ast_log(LOG_ERROR, "Could not build documentation for '%s:%s'\n", ast_xml_node_get_name(iter), ast_xml_get_attribute(iter, "name"));
+			break;
+		}
+		if (!strcasecmp(ast_xml_node_get_name(iter), "configOption")) {
+			ast_string_field_set(item, ref, ast_xml_get_attribute(cur, "name"));
+		}
+		(*tail)->next = item;
+		*tail = (*tail)->next;
+		build_config_docs(iter, tail);
+	}
+}
+
 struct ao2_container *ast_xmldoc_build_documentation(const char *type)
 {
 	struct ao2_container *docs;
@@ -2313,38 +2374,15 @@
 				break;
 			case CONFIG_INFO_SYNTAX:
 			{
-				struct ast_xml_doc_item *temp;
-				if (item) {
+				struct ast_xml_doc_item *tail;
+				if (item || !ast_xml_node_get_children(node) || strcasecmp(ast_xml_node_get_name(node), "configInfo")) {
 					break;
 				}
-				/* Each configInfo is unique and contains other config-related elements. The first item is the one that
-				 * is hashed off of for the container, so the first item we create based on the configInfo and the module
-				 * name. */
-				/* Ignore configInfos without any information */
-				if (!ast_xml_node_get_children(node) || strcasecmp(ast_xml_node_get_name(node), "configInfo")) {
-					continue;
-				}
-				if (!(temp = xmldoc_build_documentation_item(node, ast_xml_get_attribute(node, "module"), "configInfo"))) {
+				if (!(item = xmldoc_build_documentation_item(node, ast_xml_get_attribute(node, "name"), "configInfo"))) {
 					break;
 				}
-				item = temp;
-				root = item;
-
-				for (instance = ast_xml_node_get_children(node); instance; instance = ast_xml_node_get_next(instance)) {
-					if (strncasecmp(ast_xml_node_get_name(instance), "config", 6)) {
-						continue;
-					}
-					/* Now add all of the child config-related items to the list */
-					if (!(temp = xmldoc_build_documentation_item(instance, ast_xml_get_attribute(instance, "name"), ast_xml_node_get_name(instance)))) {
-						break;
-					}
-					if (!strcasecmp(ast_xml_node_get_name(instance), "configOption")) {
-						ast_string_field_set(temp, ref, ast_xml_get_attribute(instance, "objectType"));
-					}
-					item->next = temp;
-					item = temp;
-				}
-				item = root;
+				tail = item;
+				build_config_docs(node, &tail);
 				break;
 			}
 			default:
@@ -2405,10 +2443,47 @@
 }
 #endif
 
+
+static char *handle_dump_docs(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	struct documentation_tree *doctree;
+	FILE *f;
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "xmldoc dump";
+		e->usage =
+			"Usage: xmldoc dump <filename>\n"
+			"  Dump XML documentation to a file\n";
+		return NULL;
+	case CLI_GENERATE:
+		return NULL;
+	}
+
+	if (a->argc != 3) {
+		return CLI_SHOWUSAGE;
+	}
+	if (!(f = fopen(a->argv[2], "w"))) {
+		ast_log(LOG_ERROR, "Could not open file '%s': %s\n", a->argv[2], strerror(errno));
+		return CLI_FAILURE;
+	}
+	AST_RWLIST_RDLOCK(&xmldoc_tree);
+	AST_LIST_TRAVERSE(&xmldoc_tree, doctree, entry) {
+		ast_xml_doc_dump_file(f, doctree->doc);
+	}
+	AST_RWLIST_UNLOCK(&xmldoc_tree);
+	fclose(f);
+	return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_dump_xmldocs = AST_CLI_DEFINE(handle_dump_docs, "Dump the XML docs to the screen");
+
 /*! \brief Close and unload XML documentation. */
 static void xmldoc_unload_documentation(void)
 {
-        struct documentation_tree *doctree;
+	struct documentation_tree *doctree;
+
+	ast_cli_unregister(&cli_dump_xmldocs);
 
 	AST_RWLIST_WRLOCK(&xmldoc_tree);
 	while ((doctree = AST_RWLIST_REMOVE_HEAD(&xmldoc_tree, entry))) {
@@ -2452,6 +2527,7 @@
 	/* initialize the XML library. */
 	ast_xml_init();
 
+	ast_cli_register(&cli_dump_xmldocs);
 	/* register function to be run when asterisk finish. */
 	ast_register_atexit(xmldoc_unload_documentation);
 




More information about the asterisk-commits mailing list