Implement AGI XML documentation parsing functions.
authorEliel C. Sardanons <eliels@gmail.com>
Wed, 12 Nov 2008 00:17:43 +0000 (00:17 +0000)
committerEliel C. Sardanons <eliels@gmail.com>
Wed, 12 Nov 2008 00:17:43 +0000 (00:17 +0000)
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.

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@156051 65c4cc65-6c06-0410-ace0-fbb531ad65f3

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

index 057bf09..0c0b825 100644 (file)
@@ -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>
  
   <!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 "">
-
index 609e988..48de8c9 100644 (file)
@@ -38,20 +38,25 @@ typedef struct agi_state {
 } 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;
 
index c24a6b7..3a68b6c 100644 (file)
@@ -57,6 +57,8 @@ struct documentation_tree {
        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 @@ static __attribute__((format(printf,4,5))) void xmldoc_reverse_helper(int revers
 }
 
 /*! \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 @@ static int xmldoc_has_arguments(struct ast_xml_node *fixnode)
  *  \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 @@ static char *xmldoc_get_syntax(struct ast_xml_node *rootnode, const char *rootna
                }
 
                /* 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 @@ static char *xmldoc_get_syntax(struct ast_xml_node *rootnode, const char *rootna
                        }
                        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 @@ static char *xmldoc_get_syntax(struct ast_xml_node *rootnode, const char *rootna
 #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 @@ char *ast_xmldoc_build_syntax(const char *type, const char *name)
        }
 
        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 @@ static void xmldoc_parse_optionlist(struct ast_xml_node *fixnode, const char *ta
                        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 @@ static void xmldoc_parse_parameter(struct ast_xml_node *fixnode, const char *tab
                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;
index eba9ddb..45799ae 100644 (file)
@@ -57,8 +57,66 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #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_getvariablefull[] =
 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";
@@ -2075,11 +2119,6 @@ static char usage_hangup[] =
 "      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"
 "      Waits up to 'timeout' milliseconds for channel to receive a DTMF digit.\n"
@@ -2280,8 +2319,8 @@ static char usage_speechrecognize[] =
  * \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 AST_RWLIST_HEAD_STATIC(agi_commands, agi_command);
 
 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 @@ static char *help_workhorse(int fd, char *match[])
 
 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 @@ int ast_agi_unregister(struct ast_module *mod, agi_command *cmd)
 {
        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 @@ int ast_agi_unregister(struct ast_module *mod, agi_command *cmd)
                        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 enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi
 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 @@ static char *handle_cli_agi_show(struct ast_cli_entry *e, int cmd, struct ast_cl
        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 @@ static char *handle_cli_agi_show(struct ast_cli_entry *e, int cmd, struct ast_cl
        } 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 void write_html_escaped(FILE *htmlfile, char *str)
 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 @@ static int write_htmldump(char *filename)
 
        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 @@ static int write_htmldump(char *filename)
 
                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 @@ static int write_htmldump(char *filename)
                }
                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");