[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&number&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&number&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