[asterisk-commits] eliel: trunk r156051 - in /trunk: doc/ include/asterisk/ main/ res/

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Tue Nov 11 18:17:44 CST 2008


Author: eliel
Date: Tue Nov 11 18:17:43 2008
New Revision: 156051

URL: http://svn.digium.com/view/asterisk?view=rev&rev=156051
Log:
Implement AGI XML documentation parsing functions.
A new <agi> element is used to describe the XML documentation.
We have the usual synopsis,syntax,description and seealso for AGI commands.
The CLI 'agi show commands' command was changed to show all the documentation se
ctions.


Modified:
    trunk/doc/appdocsxml.dtd
    trunk/include/asterisk/agi.h
    trunk/main/xmldoc.c
    trunk/res/res_agi.c

Modified: trunk/doc/appdocsxml.dtd
URL: http://svn.digium.com/view/asterisk/trunk/doc/appdocsxml.dtd?view=diff&rev=156051&r1=156050&r2=156051
==============================================================================
--- trunk/doc/appdocsxml.dtd (original)
+++ trunk/doc/appdocsxml.dtd Tue Nov 11 18:17:43 2008
@@ -1,4 +1,4 @@
-  <!ELEMENT docs (application|function)* >
+  <!ELEMENT docs (application|function|agi)* >
 
   <!ELEMENT application (synopsis?,syntax?,description?,see-also?)>
   <!ATTLIST application name CDATA #REQUIRED>
@@ -6,36 +6,42 @@
 
   <!ELEMENT function (synopsis?,syntax?,description?,see-also?)>
   <!ATTLIST function name CDATA #REQUIRED>
-  <!ATTLIST function language CDATA #REQUIRED>  
+  <!ATTLIST function language CDATA #REQUIRED>
+
+  <!ELEMENT agi (synopsis?,syntax?,description?,see-also?)>
+  <!ATTLIST agi name CDATA #REQUIRED>
+  <!ATTLIST agi language CDATA #REQUIRED>
 
   <!ELEMENT see-also (ref*)>
 
   <!ELEMENT ref (#PCDATA)*>
-  <!ATTLIST ref type (application|function|astcli|link|manpage|filename) #REQUIRED>
+  <!ATTLIST ref type (application|function|astcli|link|manpage|filename|agi) #REQUIRED>
 
   <!ELEMENT synopsis (#PCDATA)>
-  
+
   <!ELEMENT syntax (parameter*)>
   <!ATTLIST syntax argsep CDATA ",">
 
   <!ELEMENT description (para|note|warning|variablelist|enumlist)*>
 
-  <!ELEMENT parameter (optionlist|enumlist|argument|para|note|warning)*>
-  <!ATTLIST parameter name CDATA #REQUIRED>
+  <!ELEMENT parameter (optionlist|enumlist|argument|para|note|warning|parameter)*>
+  <!ATTLIST parameter name CDATA "">
   <!ATTLIST parameter required (yes|no|true|false) "false">
   <!ATTLIST parameter multiple (yes|no|true|false) "false">
   <!ATTLIST parameter hasparams (yes|no|true|false|optional) "false">
+  <!ATTLIST parameter literal (yes|no|true|false) "false">
+  <!ATTLIST parameter default CDATA "">
   <!ATTLIST parameter argsep CDATA ",">
 
   <!ELEMENT optionlist (option+)>
   <!ELEMENT option (argument|para|note|warning|variablelist|enumlist)*>
-  <!ATTLIST option name CDATA #REQUIRED> 
+  <!ATTLIST option name CDATA #REQUIRED>
   <!ATTLIST option argsep CDATA ",">
   <!ATTLIST option implies CDATA "">
 
   <!ELEMENT enumlist (enum+)>
   <!ELEMENT enum (para|note|warning)*>
-  <!ATTLIST enum name CDATA #REQUIRED>
+  <!ATTLIST enum name CDATA "">
 
   <!ELEMENT argument (para|note|warning|variablelist|argument)*>
   <!ATTLIST argument name CDATA #REQUIRED>
@@ -53,12 +59,11 @@
  
   <!ELEMENT note (para+)>
   <!ELEMENT warning (para+)>
- 
+
   <!ELEMENT variablelist (variable+)>
   <!ELEMENT variable (#PCDATA|value|para)*>
   <!ATTLIST variable name CDATA "">
-  
+
   <!ELEMENT value (#PCDATA)>
   <!ATTLIST value name CDATA #REQUIRED>
   <!ATTLIST value default CDATA "">
-

Modified: trunk/include/asterisk/agi.h
URL: http://svn.digium.com/view/asterisk/trunk/include/asterisk/agi.h?view=diff&rev=156051&r1=156050&r2=156051
==============================================================================
--- trunk/include/asterisk/agi.h (original)
+++ trunk/include/asterisk/agi.h Tue Nov 11 18:17:43 2008
@@ -38,20 +38,25 @@
 } AGI;
 
 typedef struct agi_command {
-	/* Null terminated list of the words of the command */
-	char *cmda[AST_MAX_CMD_LEN];
-	/* Handler for the command (channel, AGI state, # of arguments, argument list). 
+	char *cmda[AST_MAX_CMD_LEN];		/*!< Null terminated list of the words of the command */
+	/*! Handler for the command (channel, AGI state, # of arguments, argument list). 
 	    Returns RESULT_SHOWUSAGE for improper arguments */
 	int (*handler)(struct ast_channel *chan, AGI *agi, int argc, char *argv[]);
-	/* Summary of the command (< 60 characters) */
+	/*! Summary of the command (< 60 characters) */
 	char *summary;
-	/* Detailed usage information */
+	/*! Detailed usage information */
 	char *usage;
-	/* Does this application run dead */
+	/*! Does this application run dead */
 	int dead;
-	/* Pointer to module that registered the agi command */
+	/*! AGI command syntax description */
+	char *syntax;
+	/*! See also content */
+	char *seealso;
+	/*! Where the documentation come from. */
+	enum ast_doc_src docsrc;
+	/*! Pointer to module that registered the agi command */
 	struct ast_module *mod;
-	/* Linked list pointer */
+	/*! Linked list pointer */
 	AST_LIST_ENTRY(agi_command) list;
 } agi_command;
 

Modified: trunk/main/xmldoc.c
URL: http://svn.digium.com/view/asterisk/trunk/main/xmldoc.c?view=diff&rev=156051&r1=156050&r2=156051
==============================================================================
--- trunk/main/xmldoc.c (original)
+++ trunk/main/xmldoc.c Tue Nov 11 18:17:43 2008
@@ -56,6 +56,8 @@
 	struct ast_xml_doc *doc;			/*!< Open document pointer. */
 	AST_RWLIST_ENTRY(documentation_tree) entry;
 };
+
+static char *xmldoc_get_syntax_cmd(struct ast_xml_node *fixnode, const char *name, int printname);
 
 /*!
  * \brief Container of documentation trees
@@ -548,17 +550,18 @@
 }
 
 /*! \internal
- *  \brief Check if the passed node has <argument> tags inside it.
- *  \param node Root node to search argument elements.
+ *  \brief Check if the passed node has 'what' tags inside it.
+ *  \param node Root node to search 'what' elements.
+ *  \param what node name to search inside node.
  *  \retval 1 If a <argument> element is found inside 'node'.
  *  \retval 0 If no <argument> is found inside 'node'.
  */
-static int xmldoc_has_arguments(struct ast_xml_node *fixnode)
+static int xmldoc_has_inside(struct ast_xml_node *fixnode, const char *what)
 {
 	struct ast_xml_node *node = fixnode;
 
 	for (node = ast_xml_node_get_children(fixnode); node; node = ast_xml_node_get_next(node)) {
-		if (!strcasecmp(ast_xml_node_get_name(node), "argument")) {
+		if (!strcasecmp(ast_xml_node_get_name(node), what)) {
 			return 1;
 		}
 	}
@@ -575,7 +578,7 @@
  *  \retval NULL on error.
  *  \retval An ast_malloc'ed string with the syntax generated.
  */
-static char *xmldoc_get_syntax(struct ast_xml_node *rootnode, const char *rootname, const char *childname, int printparenthesis, int printrootname)
+static char *xmldoc_get_syntax_fun(struct ast_xml_node *rootnode, const char *rootname, const char *childname, int printparenthesis, int printrootname)
 {
 #define GOTONEXT(__rev, __a) (__rev ? ast_xml_node_get_prev(__a) : ast_xml_node_get_next(__a))
 #define ISLAST(__rev, __a)  (__rev == 1 ? (ast_xml_node_get_prev(__a) ? 0 : 1) : (ast_xml_node_get_next(__a) ? 0 : 1))
@@ -679,7 +682,7 @@
 		}
 
 		/* Get the argument name, if it is not the leaf, go inside that parameter. */
-		if (xmldoc_has_arguments(node)) {
+		if (xmldoc_has_inside(node, "arguments")) {
 			parenthesis = ast_xml_get_attribute(node, "hasparams");
 			prnparenthesis = 0;
 			if (parenthesis) {
@@ -691,7 +694,7 @@
 			}
 			argname = ast_xml_get_attribute(node, "name");
 			if (argname) {
-				paramname = xmldoc_get_syntax(node, argname, "argument", prnparenthesis, prnparenthesis);
+				paramname = xmldoc_get_syntax_fun(node, argname, "argument", prnparenthesis, prnparenthesis);
 				ast_xml_free_attr(argname);
 				paramnamemalloc = 1;
 			} else {
@@ -805,6 +808,182 @@
 #undef MP
 }
 
+/*! \internal 
+ *  \brief Parse an enumlist inside a <parameter> to generate a COMMAND
+ *         syntax.
+ *  \param fixnode A pointer to the <enumlist> node.
+ *  \retval {<unknown>} on error.
+ *  \retval A string inside brackets {} with the enum's separated by pipes |. 
+ */
+static char *xmldoc_parse_cmd_enumlist(struct ast_xml_node *fixnode)
+{
+	struct ast_xml_node *node = fixnode;
+	struct ast_str *paramname;
+	char *enumname, *ret;
+	int first = 1;
+
+	paramname = ast_str_create(128);
+	if (!paramname) {
+		return ast_strdup("{<unkown>}");
+	}
+
+	ast_str_append(&paramname, 0, "{");
+
+	for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
+		if (strcasecmp(ast_xml_node_get_name(node), "enum")) {
+			continue;
+		}
+
+		enumname = xmldoc_get_syntax_cmd(node, "", 0);
+		if (!enumname) {
+			continue;
+		}
+		if (!first) {
+			ast_str_append(&paramname, 0, "|");
+		}
+		ast_str_append(&paramname, 0, "%s", enumname);
+		first = 0;
+		ast_free(enumname);
+	}
+
+	ast_str_append(&paramname, 0, "}");
+
+	ret = ast_strdup(paramname->str);
+	ast_free(paramname);
+
+	return ret;
+}
+
+/*! \internal
+ *  \brief Generate a syntax of COMMAND type. 
+ *  \param fixnode The <syntax> node pointer.
+ *  \param name The name of the 'command'.
+ *  \param printname Print the name of the command before the paramters?
+ *  \retval On error, return just 'name'.
+ *  \retval On success return the generated syntax.
+ */
+static char *xmldoc_get_syntax_cmd(struct ast_xml_node *fixnode, const char *name, int printname)
+{
+	struct ast_str *syntax;
+	struct ast_xml_node *tmpnode, *node = fixnode;
+	char *ret;
+	const char *paramname, *paramtype, *attrname, *literal;
+	int required, isenum, first = 1, isliteral;
+
+	syntax = ast_str_create(128);
+	if (!syntax) {
+		/* at least try to return something... */
+		return ast_strdup(name);
+	}
+
+	/* append name to output string. */
+	if (printname) {
+		ast_str_append(&syntax, 0, "%s", name);
+		first = 0;
+	}
+
+	for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
+		if (strcasecmp(ast_xml_node_get_name(node), "parameter")) {
+			continue;
+		}
+
+		if (xmldoc_has_inside(node, "parameter")) {
+			/* is this a recursive parameter. */
+			paramname = xmldoc_get_syntax_cmd(node, "", 0);
+			isenum = 1;
+		} else if (!xmldoc_has_inside(node, "enumlist")) {
+			/* this is a simple parameter. */
+			attrname = ast_xml_get_attribute(node, "name");
+			if (!attrname) {
+				/* ignore this bogus parameter and continue. */
+				continue;
+			}
+			paramname = ast_strdup(attrname);
+			ast_xml_free_attr(attrname);
+			isenum = 0;
+		} else {
+			/* parse enumlist (note that this is a special enumlist
+			that is used to describe a syntax like {<param1>|<param2>|...} */
+			for (tmpnode = ast_xml_node_get_children(node); tmpnode; tmpnode = ast_xml_node_get_next(tmpnode)) {
+				if (!strcasecmp(ast_xml_node_get_name(tmpnode), "enumlist")) {
+					break;
+				}
+			}
+			paramname = xmldoc_parse_cmd_enumlist(tmpnode);
+			isenum = 1;
+		}
+
+		/* Is this parameter required? */
+		required = 0;
+		paramtype = ast_xml_get_attribute(node, "required");
+		if (paramtype) {
+			required = ast_true(paramtype);
+			ast_xml_free_attr(paramtype);
+		}
+
+		/* Is this a replaceable value or a fixed parameter value? */
+		isliteral = 0;
+		literal = ast_xml_get_attribute(node, "literal");
+		if (literal) {
+			isliteral = ast_true(literal);
+			ast_xml_free_attr(literal);
+		}
+
+		/* if required="false" print with [...].
+		 * if literal="true" or is enum print without <..>.
+		 * if not first print a space at the beginning.
+		 */
+		ast_str_append(&syntax, 0, "%s%s%s%s%s%s",
+				(first ? "" : " "),
+				(required ? "" : "["),
+				(isenum || isliteral ? "" : "<"),
+				paramname,
+				(isenum || isliteral ? "" : ">"),
+				(required ? "" : "]"));
+		first = 0;
+		ast_xml_free_attr(paramname);
+	}
+
+	/* return a common string. */
+	ret = ast_strdup(syntax->str);
+	ast_free(syntax);
+
+	return ret;
+}
+
+/*! \brief Types of syntax that we are able to generate. */
+enum syntaxtype {
+	FUNCTION_SYNTAX,
+	COMMAND_SYNTAX
+};
+
+/*! \brief Mapping between type of node and type of syntax to generate. */
+struct strsyntaxtype {
+	const char *type;
+	enum syntaxtype stxtype;
+} stxtype[] = {
+	{ "function",		FUNCTION_SYNTAX	},
+	{ "application",	FUNCTION_SYNTAX	},
+	{ "agi",		COMMAND_SYNTAX	}
+};
+
+/*! \internal
+ *  \brief Get syntax type based on type of node.
+ *  \param type Type of node.
+ *  \retval The type of syntax to generate based on the type of node.
+ */
+static enum syntaxtype xmldoc_get_syntax_type(const char *type)
+{
+	int i;
+	for (i=0; i < ARRAY_LEN(stxtype); i++) {
+		if (!strcasecmp(stxtype[i].type, type)) {
+			return stxtype[i].stxtype;
+		}
+	}
+
+	return FUNCTION_SYNTAX;
+}
+
 char *ast_xmldoc_build_syntax(const char *type, const char *name)
 {
 	struct ast_xml_node *node;
@@ -822,7 +1001,11 @@
 	}
 
 	if (node) {
-		syntax = xmldoc_get_syntax(node, name, "parameter", 1, 1);
+		if (xmldoc_get_syntax_type(type) == FUNCTION_SYNTAX) {
+			syntax = xmldoc_get_syntax_fun(node, name, "parameter", 1, 1);
+		} else {
+			syntax = xmldoc_get_syntax_cmd(node, name, 1);
+		}
 	}
 	return syntax;
 }
@@ -1294,7 +1477,7 @@
 			continue;
 		}
 
-		optionsyntax = xmldoc_get_syntax(node, optname, "argument", 0, 1);
+		optionsyntax = xmldoc_get_syntax_fun(node, optname, "argument", 0, 1);
 		if (!optionsyntax) {
 			continue;
 		}
@@ -1325,7 +1508,7 @@
 		return;
 	}
 
-	hasarguments = xmldoc_has_arguments(node);
+	hasarguments = xmldoc_has_inside(node, "arguments");
 	if (!(paramname = ast_xml_get_attribute(node, "name"))) {
 		/* parameter MUST have an attribute name. */
 		return;

Modified: trunk/res/res_agi.c
URL: http://svn.digium.com/view/asterisk/trunk/res/res_agi.c?view=diff&rev=156051&r1=156050&r2=156051
==============================================================================
--- trunk/res/res_agi.c (original)
+++ trunk/res/res_agi.c Tue Nov 11 18:17:43 2008
@@ -57,8 +57,66 @@
 #include "asterisk/speech.h"
 #include "asterisk/manager.h"
 #include "asterisk/features.h"
+#include "asterisk/term.h"
+#include "asterisk/xmldoc.h"
+
+/*** DOCUMENTATION
+	<agi name="answer" language="en_US">
+		<synopsis>
+			Answer channel
+		</synopsis>
+		<syntax />
+		<description>
+			<para>Answers channel if not already in answer state. Returns <literal>-1</literal> on
+			channel failure, or <literal>0</literal> if successful.</para>
+		</description>
+		<see-also>
+			<ref type="agi">hangup</ref>
+		</see-also>
+	</agi>
+	<agi name="channel status" language="en_US">
+		<synopsis>
+			Returns status of the connected channel.
+		</synopsis>
+		<syntax>
+			<parameter name="channelname" />
+		</syntax>
+		<description>
+			<para>Returns the status of the specified <replaceable>channelname</replaceable>.
+			If no channel name is given then returns the status of the current channel.</para>
+			<para>Return values:</para>
+			<enumlist>
+				<enum name="0">
+					<para>Channel is down and available.</para>
+				</enum>
+				<enum name="1">
+					<para>Channel is down, but reserved.</para>
+				</enum>
+				<enum name="2">
+					<para>Channel is off hook.</para>
+				</enum>
+				<enum name="3">
+					<para>Digits (or equivalent) have been dialed.</para>
+				</enum>
+				<enum name="4">
+					<para>Line is ringing.</para>
+				</enum>
+				<enum name="5">
+					<para>Remote end is ringing.</para>
+				</enum>
+				<enum name="6">
+					<para>Line is up.</para>
+				</enum>
+				<enum name="7">
+					<para>Line is busy.</para>
+				</enum>
+			</enumlist>
+		</description>
+	</agi>
+ ***/
 
 #define MAX_ARGS 128
+#define MAX_CMD_LEN 80
 #define AGI_NANDFS_RETRY 3
 #define AGI_BUF_LEN 2048
 
@@ -2047,20 +2105,6 @@
 static char usage_setvariable[] =
 " Usage: SET VARIABLE <variablename> <value>\n";
 
-static char usage_channelstatus[] =
-" Usage: CHANNEL STATUS [<channelname>]\n"
-"	Returns the status of the specified channel.\n"
-" If no channel name is given the returns the status of the\n"
-" current channel.  Return values:\n"
-"  0 Channel is down and available\n"
-"  1 Channel is down, but reserved\n"
-"  2 Channel is off hook\n"
-"  3 Digits (or equivalent) have been dialed\n"
-"  4 Line is ringing\n"
-"  5 Remote end is ringing\n"
-"  6 Line is up\n"
-"  7 Line is busy\n";
-
 static char usage_setcallerid[] =
 " Usage: SET CALLERID <number>\n"
 "	Changes the callerid of the current channel.\n";
@@ -2074,11 +2118,6 @@
 " Usage: HANGUP [<channelname>]\n"
 "	Hangs up the specified channel.\n"
 " If no channel name is given, hangs up the current channel\n";
-
-static char usage_answer[] =
-" Usage: ANSWER\n"
-"	Answers channel if not already in answer state. Returns -1 on\n"
-" channel failure, or 0 if successful.\n";
 
 static char usage_waitfordigit[] =
 " Usage: WAIT FOR DIGIT <timeout>\n"
@@ -2280,8 +2319,8 @@
  * \brief AGI commands list
  */
 static struct agi_command commands[] = {
-	{ { "answer", NULL }, handle_answer, "Answer channel", usage_answer , 0 },
-	{ { "channel", "status", NULL }, handle_channelstatus, "Returns status of the connected channel", usage_channelstatus , 0 },
+	{ { "answer", NULL }, handle_answer, NULL, NULL, 0 },
+	{ { "channel", "status", NULL }, handle_channelstatus, NULL, NULL, 0 },
 	{ { "database", "del", NULL }, handle_dbdel, "Removes database key/value", usage_dbdel , 1 },
 	{ { "database", "deltree", NULL }, handle_dbdeltree, "Removes database keytree/value", usage_dbdeltree , 1 },
 	{ { "database", "get", NULL }, handle_dbget, "Gets database value", usage_dbget , 1 },
@@ -2332,7 +2371,7 @@
 
 static char *help_workhorse(int fd, char *match[])
 {
-	char fullcmd[80], matchstr[80];
+	char fullcmd[MAX_CMD_LEN], matchstr[MAX_CMD_LEN];
 	struct agi_command *e;
 
 	if (match)
@@ -2358,11 +2397,21 @@
 
 int ast_agi_register(struct ast_module *mod, agi_command *cmd)
 {
-	char fullcmd[80];
+	char fullcmd[MAX_CMD_LEN];
 
 	ast_join(fullcmd, sizeof(fullcmd), cmd->cmda);
 
- 	if (!find_command(cmd->cmda,1)) {
+	if (!find_command(cmd->cmda,1)) {
+		cmd->docsrc = AST_STATIC_DOC;
+#ifdef AST_XML_DOCS
+		if (ast_strlen_zero(cmd->summary) && ast_strlen_zero(cmd->usage)) {
+			cmd->summary = ast_xmldoc_build_synopsis("agi", fullcmd);
+			cmd->usage = ast_xmldoc_build_description("agi", fullcmd);
+			cmd->syntax = ast_xmldoc_build_syntax("agi", fullcmd);
+			cmd->seealso = ast_xmldoc_build_seealso("agi", fullcmd);
+			cmd->docsrc = AST_XML_DOC;
+		}
+#endif
 		cmd->mod = mod;
 		AST_RWLIST_WRLOCK(&agi_commands);
 		AST_LIST_INSERT_TAIL(&agi_commands, cmd, list);
@@ -2381,7 +2430,7 @@
 {
 	struct agi_command *e;
 	int unregistered = 0;
-	char fullcmd[80];
+	char fullcmd[MAX_CMD_LEN];
 
 	ast_join(fullcmd, sizeof(fullcmd), cmd->cmda);
 
@@ -2391,6 +2440,16 @@
 			AST_RWLIST_REMOVE_CURRENT(list);
 			if (mod != ast_module_info->self)
 				ast_module_unref(ast_module_info->self);
+#ifdef AST_XML_DOCS
+			if (e->docsrc == AST_XML_DOC) {
+				ast_free(e->summary);
+				ast_free(e->usage);
+				ast_free(e->syntax);
+				ast_free(e->seealso);
+				e->summary = NULL, e->usage = NULL;
+				e->syntax = NULL, e->seealso = NULL;
+			}
+#endif
 			unregistered=1;
 			break;
 		}
@@ -2728,7 +2787,8 @@
 static char *handle_cli_agi_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
 	struct agi_command *command;
-	char fullcmd[80];
+	char fullcmd[MAX_CMD_LEN];
+	int error = 0;
 
 	switch (cmd) {
 	case CLI_INIT:
@@ -2746,8 +2806,73 @@
 	if (a->argc > e->args - 1) {
 		command = find_command(a->argv + e->args, 1);
 		if (command) {
-			ast_cli(a->fd, "%s", command->usage);
-			ast_cli(a->fd, " Runs Dead : %s\n", command->dead ? "Yes" : "No");
+			char *synopsis = NULL, *description = NULL, *syntax = NULL, *seealso = NULL;
+			char info[30 + MAX_CMD_LEN];					/* '-= Info about...' */
+			char infotitle[30 + MAX_CMD_LEN + AST_TERM_MAX_ESCAPE_CHARS];	/* '-= Info about...' with colors */
+			char syntitle[11 + AST_TERM_MAX_ESCAPE_CHARS];			/* [Syntax]\n with colors */
+			char desctitle[15 + AST_TERM_MAX_ESCAPE_CHARS];			/* [Description]\n with colors */
+			char deadtitle[13 + AST_TERM_MAX_ESCAPE_CHARS];			/* [Runs Dead]\n with colors */
+			char deadcontent[3 + AST_TERM_MAX_ESCAPE_CHARS];		/* 'Yes' or 'No' with colors */
+			char seealsotitle[12 + AST_TERM_MAX_ESCAPE_CHARS];		/* [See Also]\n with colors */
+			char stxtitle[10 + AST_TERM_MAX_ESCAPE_CHARS];			/* [Syntax]\n with colors */
+			size_t synlen, desclen, seealsolen, stxlen;
+
+			term_color(syntitle, "[Synopsis]\n", COLOR_MAGENTA, 0, sizeof(syntitle));
+			term_color(desctitle, "[Description]\n", COLOR_MAGENTA, 0, sizeof(desctitle));
+			term_color(deadtitle, "[Runs Dead]\n", COLOR_MAGENTA, 0, sizeof(deadtitle));
+			term_color(seealsotitle, "[See Also]\n", COLOR_MAGENTA, 0, sizeof(seealsotitle));
+			term_color(stxtitle, "[Syntax]\n", COLOR_MAGENTA, 0, sizeof(stxtitle));
+			term_color(deadcontent, command->dead ? "Yes" : "No", COLOR_CYAN, 0, sizeof(deadcontent));
+
+			ast_join(fullcmd, sizeof(fullcmd), a->argv + e->args);
+			snprintf(info, sizeof(info), "\n  -= Info about agi '%s' =- ", fullcmd);
+			term_color(infotitle, info, COLOR_CYAN, 0, sizeof(infotitle));
+#ifdef AST_XML_DOCS
+			if (command->docsrc == AST_XML_DOC) {
+				synopsis = ast_xmldoc_printable(S_OR(command->summary, "Not available"), 1);
+				description = ast_xmldoc_printable(S_OR(command->usage, "Not available"), 1);
+				seealso = ast_xmldoc_printable(S_OR(command->seealso, "Not available"), 1);
+				if (!seealso || !description || !synopsis) {
+					error = 1;
+					goto return_cleanup;
+				}
+			} else
+#endif
+			{
+				synlen = strlen(S_OR(command->summary, "Not available")) + AST_TERM_MAX_ESCAPE_CHARS;
+				synopsis = ast_malloc(synlen);
+
+				desclen = strlen(S_OR(command->usage, "Not available")) + AST_TERM_MAX_ESCAPE_CHARS;
+				description = ast_malloc(desclen);
+
+				seealsolen = strlen(S_OR(command->seealso, "Not available")) + AST_TERM_MAX_ESCAPE_CHARS;
+				seealso = ast_malloc(seealsolen);
+
+				if (!synopsis || !description || !seealso) {
+					error = 1;
+					goto return_cleanup;
+				}
+				term_color(synopsis, S_OR(command->summary, "Not available"), COLOR_CYAN, 0, synlen);
+				term_color(description, S_OR(command->usage, "Not available"), COLOR_CYAN, 0, desclen);
+				term_color(seealso, S_OR(command->seealso, "Not available"), COLOR_CYAN, 0, seealsolen);
+			}
+
+			stxlen = strlen(S_OR(command->syntax, "Not available")) + AST_TERM_MAX_ESCAPE_CHARS;
+			syntax = ast_malloc(stxlen);
+			if (!syntax) {
+				error = 1;
+				goto return_cleanup;
+			}
+			term_color(syntax, S_OR(command->syntax, "Not available"), COLOR_CYAN, 0, stxlen);
+
+			ast_cli(a->fd, "%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n", infotitle, stxtitle, syntax,
+					desctitle, description, syntitle, synopsis, deadtitle, deadcontent,
+					seealsotitle, seealso);
+return_cleanup:
+			ast_free(synopsis);
+			ast_free(description);
+			ast_free(syntax);
+			ast_free(seealso);
 		} else {
 			if (find_command(a->argv + e->args, -1)) {
 				return help_workhorse(a->fd, a->argv + e->args);
@@ -2759,7 +2884,7 @@
 	} else {
 		return help_workhorse(a->fd, NULL);
 	}
-	return CLI_SUCCESS;
+	return (error ? CLI_FAILURE : CLI_SUCCESS);
 }
 
 /*! \brief Convert string to use HTML escaped characters
@@ -2796,7 +2921,7 @@
 static int write_htmldump(char *filename)
 {
 	struct agi_command *command;
-	char fullcmd[80];
+	char fullcmd[MAX_CMD_LEN];
 	FILE *htmlfile;
 
 	if (!(htmlfile = fopen(filename, "wt")))
@@ -2808,7 +2933,10 @@
 
 	AST_RWLIST_RDLOCK(&agi_commands);
 	AST_RWLIST_TRAVERSE(&agi_commands, command, list) {
-		char *stringp, *tempstr;
+#ifdef AST_XML_DOCS
+		char *stringptmp;
+#endif
+		char *tempstr, *stringp;
 
 		if (!command->cmda[0])	/* end ? */
 			break;
@@ -2819,8 +2947,12 @@
 
 		fprintf(htmlfile, "<TR><TD><TABLE BORDER=\"1\" CELLPADDING=\"5\" WIDTH=\"100%%\">\n");
 		fprintf(htmlfile, "<TR><TH ALIGN=\"CENTER\"><B>%s - %s</B></TH></TR>\n", fullcmd, command->summary);
-
+#ifdef AST_XML_DOCS
+		stringptmp = ast_xmldoc_printable(command->usage, 0);
+		stringp = stringptmp;
+#else
 		stringp = command->usage;
+#endif
 		tempstr = strsep(&stringp, "\n");
 
 		fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">");
@@ -2834,6 +2966,9 @@
 		}
 		fprintf(htmlfile, "</TD></TR>\n");
 		fprintf(htmlfile, "</TABLE></TD></TR>\n\n");
+#ifdef AST_XML_DOCS
+		ast_free(stringptmp);
+#endif
 	}
 	AST_RWLIST_UNLOCK(&agi_commands);
 	fprintf(htmlfile, "</TABLE>\n</BODY>\n</HTML>\n");




More information about the asterisk-commits mailing list