Merge the rest of the FullyBooted patch
[asterisk/asterisk.git] / main / xmldoc.c
index c24a6b7..46be1f0 100644 (file)
@@ -19,6 +19,8 @@
  * \brief XML Documentation API
  *
  * \author Eliel C. Sardanons (LU1ALY) <eliels@gmail.com>
+ *
+ * \extref libxml2 http://www.xmlsoft.org/
  */
 
 #include "asterisk.h"
@@ -38,7 +40,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 /*! \brief Default documentation language. */
 static const char default_documentation_language[] = "en_US";
 
-/*! \brief Number of columns to print when showing the XML documentation with a 
+/*! \brief Number of columns to print when showing the XML documentation with a
  *         'core show application/function *' CLI command. Used in text wrapping.*/
 static const int xmldoc_text_columns = 74;
 
@@ -57,6 +59,9 @@ 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);
+static int xmldoc_parse_enumlist(struct ast_xml_node *fixnode, const char *tabs, struct ast_str **buffer);
+
 /*!
  * \brief Container of documentation trees
  *
@@ -266,8 +271,7 @@ static char *xmldoc_string_wrap(const char *text, int columns, int maxdiff)
                                backspace = xmldoc_foundspace_backward(text, i, maxdiff);
                                if (backspace) {
                                        needtobreak = 1;
-                                       tmp->used -= backspace;
-                                       tmp->str[tmp->used] = '\0';
+                                       ast_str_truncate(tmp, -backspace);
                                        i -= backspace + 1;
                                        continue;
                                }
@@ -298,7 +302,7 @@ static char *xmldoc_string_wrap(const char *text, int columns, int maxdiff)
                ast_str_append(&tmp, 0, "%c", text[i]);
        }
 
-       ret = ast_strdup(tmp->str);
+       ret = ast_strdup(ast_str_buffer(tmp));
        ast_free(tmp);
 
        return ret;
@@ -394,14 +398,14 @@ char *ast_xmldoc_printable(const char *bwinput, int withcolors)
        }
 
        if (withcolors) {
-               ast_term_color_code(&colorized, COLOR_BRWHITE, 0);
+               ast_str_append(&colorized, 0, "%s", term_end());
                if (!colorized) {
                        return NULL;
                }
        }
 
        /* Wrap the text, notice that string wrap will avoid cutting an ESC sequence. */
-       wrapped = xmldoc_string_wrap(colorized->str, xmldoc_text_columns, xmldoc_max_diff);
+       wrapped = xmldoc_string_wrap(ast_str_buffer(colorized), xmldoc_text_columns, xmldoc_max_diff);
 
        ast_free(colorized);
 
@@ -501,14 +505,14 @@ static struct ast_xml_node *xmldoc_get_node(const char *type, const char *name,
 
 /*! \internal
  *  \brief Helper function used to build the syntax, it allocates the needed buffer (or reallocates it),
- *         and based on the reverse value it makes use of fmt to print the parameter list inside the 
+ *         and based on the reverse value it makes use of fmt to print the parameter list inside the
  *         realloced buffer (syntax).
  *  \param reverse We are going backwards while generating the syntax?
  *  \param len Current length of 'syntax' buffer.
  *  \param syntax Output buffer for the concatenated values.
  *  \param fmt A format string that will be used in a sprintf call.
  */
-static __attribute__((format(printf,4,5))) void xmldoc_reverse_helper(int reverse, int *len, char **syntax, const char *fmt, ...)
+static void __attribute__((format(printf, 4, 5))) xmldoc_reverse_helper(int reverse, int *len, char **syntax, const char *fmt, ...)
 {
        int totlen, tmpfmtlen;
        char *tmpfmt, tmp;
@@ -548,17 +552,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.
- *  \retval 1 If a <argument> element is found inside 'node'.
- *  \retval 0 If no <argument> is found inside 'node'.
+ *  \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 'what' element is found inside 'node'.
+ *  \retval 0 If no 'what' 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;
                }
        }
@@ -566,6 +571,45 @@ static int xmldoc_has_arguments(struct ast_xml_node *fixnode)
 }
 
 /*! \internal
+ *  \brief Check if the passed node has at least one node inside it.
+ *  \param node Root node to search node elements.
+ *  \retval 1 If a node element is found inside 'node'.
+ *  \retval 0 If no node is found inside 'node'.
+ */
+static int xmldoc_has_nodes(struct ast_xml_node *fixnode)
+{
+       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), "text")) {
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+/*! \internal
+ *  \brief Check if the passed node has at least one specialtag.
+ *  \param node Root node to search "specialtags" elements.
+ *  \retval 1 If a "specialtag" element is found inside 'node'.
+ *  \retval 0 If no "specialtag" is found inside 'node'.
+ */
+static int xmldoc_has_specialtags(struct ast_xml_node *fixnode)
+{
+       struct ast_xml_node *node = fixnode;
+       int i;
+
+       for (node = ast_xml_node_get_children(fixnode); node; node = ast_xml_node_get_next(node)) {
+               for (i = 0; i < ARRAY_LEN(special_tags); i++) {
+                       if (!strcasecmp(ast_xml_node_get_name(node), special_tags[i].tagname)) {
+                               return 1;
+                       }
+               }
+       }
+       return 0;
+}
+
+/*! \internal
  *  \brief Build the syntax for a specified starting node.
  *  \param rootnode A pointer to the ast_xml root node.
  *  \param rootname Name of the application, function, option, etc. to build the syntax.
@@ -575,17 +619,16 @@ 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))
 #define MP(__a) ((multiple ? __a : ""))
        struct ast_xml_node *node = NULL, *firstparam = NULL, *lastparam = NULL;
-       const char *paramtype, *multipletype, *paramname, *attrargsep, *parenthesis, *argname;
+       const char *paramtype, *multipletype, *paramnameattr, *attrargsep, *parenthesis, *argname;
        int reverse, required, paramcount = 0, openbrackets = 0, len = 0, hasparams=0;
-       int reqfinode = 0, reqlanode = 0, optmidnode = 0, prnparenthesis;
-       char *syntax = NULL, *argsep;
-       int paramnamemalloc, multiple;
+       int reqfinode = 0, reqlanode = 0, optmidnode = 0, prnparenthesis, multiple;
+       char *syntax = NULL, *argsep, *paramname;
 
        if (ast_strlen_zero(rootname) || ast_strlen_zero(childname)) {
                ast_log(LOG_WARNING, "Tried to look in XML tree with faulty rootname or childname while creating a syntax.\n");
@@ -679,7 +722,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, "argument")) {
                        parenthesis = ast_xml_get_attribute(node, "hasparams");
                        prnparenthesis = 0;
                        if (parenthesis) {
@@ -691,18 +734,15 @@ 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 {
                                /* Malformed XML, print **UNKOWN** */
                                paramname = ast_strdup("**unknown**");
                        }
-                       paramnamemalloc = 1;
                } else {
-                       paramnamemalloc = 0;
-                       paramname = ast_xml_get_attribute(node, "name");
-                       if (!paramname) {
+                       paramnameattr = ast_xml_get_attribute(node, "name");
+                       if (!paramnameattr) {
                                ast_log(LOG_WARNING, "Malformed XML %s: no %s name\n", rootname, childname);
                                if (syntax) {
                                        /* Free already allocated syntax */
@@ -712,6 +752,8 @@ static char *xmldoc_get_syntax(struct ast_xml_node *rootnode, const char *rootna
                                ast_asprintf(&syntax, "%s%s", (printrootname ? rootname : ""), (printparenthesis ? "()" : ""));
                                return syntax;
                        }
+                       paramname = ast_strdup(paramnameattr);
+                       ast_xml_free_attr(paramnameattr);
                }
 
                /* Defaults to 'false'. */
@@ -776,11 +818,7 @@ static char *xmldoc_get_syntax(struct ast_xml_node *rootnode, const char *rootna
                                }
                        }
                }
-               if (paramnamemalloc) {
-                       ast_free((char *) paramname);
-               } else {
-                       ast_xml_free_attr(paramname);
-               }
+               ast_free(paramname);
 
                paramcount++;
        }
@@ -805,6 +843,240 @@ 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(ast_str_buffer(paramname));
+       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, *paramname;
+       const char *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_free(paramname);
+       }
+
+       /* return a common string. */
+       ret = ast_strdup(ast_str_buffer(syntax));
+       ast_free(syntax);
+
+       return ret;
+}
+
+/*! \internal
+ *  \brief Generate an AMI action syntax.
+ *  \param fixnode The manager action node pointer.
+ *  \param name The name of the manager action.
+ *  \retval The generated syntax.
+ *  \retval NULL on error.
+ */
+static char *xmldoc_get_syntax_manager(struct ast_xml_node *fixnode, const char *name)
+{
+       struct ast_str *syntax;
+       struct ast_xml_node *node = fixnode;
+       const char *paramtype, *attrname;
+       int required;
+       char *ret;
+
+       syntax = ast_str_create(128);
+       if (!syntax) {
+               return ast_strdup(name);
+       }
+
+       ast_str_append(&syntax, 0, "Action: %s", name);
+
+       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;
+               }
+
+               /* Is this parameter required? */
+               required = 0;
+               paramtype = ast_xml_get_attribute(node, "required");
+               if (paramtype) {
+                       required = ast_true(paramtype);
+                       ast_xml_free_attr(paramtype);
+               }
+
+               attrname = ast_xml_get_attribute(node, "name");
+               if (!attrname) {
+                       /* ignore this bogus parameter and continue. */
+                       continue;
+               }
+
+               ast_str_append(&syntax, 0, "\n%s%s:%s <value>",
+                       (required ? "" : "["),
+                       attrname,
+                       (required ? "" : "]"));
+
+               ast_xml_free_attr(attrname);
+       }
+
+       /* return a common string. */
+       ret = ast_strdup(ast_str_buffer(syntax));
+       ast_free(syntax);
+
+       return ret;
+}
+
+/*! \brief Types of syntax that we are able to generate. */
+enum syntaxtype {
+       FUNCTION_SYNTAX,
+       MANAGER_SYNTAX,
+       COMMAND_SYNTAX
+};
+
+/*! \brief Mapping between type of node and type of syntax to generate. */
+static struct strsyntaxtype {
+       const char *type;
+       enum syntaxtype stxtype;
+} stxtype[] = {
+       { "function",           FUNCTION_SYNTAX },
+       { "application",        FUNCTION_SYNTAX },
+       { "manager",            MANAGER_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 +1094,19 @@ char *ast_xmldoc_build_syntax(const char *type, const char *name)
        }
 
        if (node) {
-               syntax = xmldoc_get_syntax(node, name, "parameter", 1, 1);
+               switch (xmldoc_get_syntax_type(type)) {
+               case FUNCTION_SYNTAX:
+                       syntax = xmldoc_get_syntax_fun(node, name, "parameter", 1, 1);
+                       break;
+               case COMMAND_SYNTAX:
+                       syntax = xmldoc_get_syntax_cmd(node, name, 1);
+                       break;
+               case MANAGER_SYNTAX:
+                       syntax = xmldoc_get_syntax_manager(node, name);
+                       break;
+               default:
+                       syntax = xmldoc_get_syntax_fun(node, name, "parameter", 1, 1);
+               }
        }
        return syntax;
 }
@@ -867,10 +1151,10 @@ static int xmldoc_parse_para(struct ast_xml_node *node, const char *tabs, const
                        ast_xml_free_text(tmptext);
                        if (tmpstr) {
                                if (strcasecmp(ast_xml_node_get_name(tmp), "text")) {
-                                       ast_str_append(buffer, 0, "<%s>%s</%s>", ast_xml_node_get_name(tmp), 
-                                                       tmpstr->str, ast_xml_node_get_name(tmp));
+                                       ast_str_append(buffer, 0, "<%s>%s</%s>", ast_xml_node_get_name(tmp),
+                                                       ast_str_buffer(tmpstr), ast_xml_node_get_name(tmp));
                                } else {
-                                       ast_str_append(buffer, 0, "%s", tmpstr->str);
+                                       ast_str_append(buffer, 0, "%s", ast_str_buffer(tmpstr));
                                }
                                ast_free(tmpstr);
                                ret = 2;
@@ -958,8 +1242,13 @@ static int xmldoc_parse_argument(struct ast_xml_node *fixnode, int insideparamet
        if (!argname) {
                return 0;
        }
-       ast_str_append(buffer, 0, "%s%s%s", tabs, argname, (insideparameter ? "\n" : ""));
-       ast_xml_free_attr(argname);
+       if (xmldoc_has_inside(node, "para") || xmldoc_has_specialtags(node)) {
+               ast_str_append(buffer, 0, "%s%s%s", tabs, argname, (insideparameter ? "\n" : ""));
+               ast_xml_free_attr(argname);
+       } else {
+               ast_xml_free_attr(argname);
+               return 0;
+       }
 
        for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
                if (xmldoc_parse_para(node, (insideparameter ? paramtabs : (!count ? " - " : tabs)), "\n", buffer) == 2) {
@@ -1024,8 +1313,8 @@ static int xmldoc_parse_variable(struct ast_xml_node *node, const char *tabs, st
                        /* Cleanup text. */
                        xmldoc_string_cleanup(tmptext, &cleanstr, 1);
                        ast_xml_free_text(tmptext);
-                       if (cleanstr && cleanstr->used > 0) {
-                               ast_str_append(buffer, 0, ":%s", cleanstr->str);
+                       if (cleanstr && ast_str_strlen(cleanstr) > 0) {
+                               ast_str_append(buffer, 0, ":%s", ast_str_buffer(cleanstr));
                        }
                        ast_free(cleanstr);
                }
@@ -1158,9 +1447,10 @@ char *ast_xmldoc_build_seealso(const char *type, const char *name)
                }
                first = 0;
                ast_xml_free_text(content);
+               ast_xml_free_attr(typename);
        }
 
-       output = ast_strdup(outputstr->str);
+       output = ast_strdup(ast_str_buffer(outputstr));
        ast_free(outputstr);
 
        return output;
@@ -1177,6 +1467,9 @@ static int xmldoc_parse_enum(struct ast_xml_node *fixnode, const char *tabs, str
 {
        struct ast_xml_node *node = fixnode;
        int ret = 0;
+       char *optiontabs;
+
+       ast_asprintf(&optiontabs, "%s    ", tabs);
 
        for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
                if ((xmldoc_parse_para(node, (ret ? tabs : " - "), "\n", buffer))) {
@@ -1184,7 +1477,12 @@ static int xmldoc_parse_enum(struct ast_xml_node *fixnode, const char *tabs, str
                } else if ((xmldoc_parse_specialtags(node, (ret ? tabs : " - "), "\n", buffer))) {
                        ret = 1;
                }
+
+               xmldoc_parse_enumlist(node, optiontabs, buffer);
        }
+
+       ast_free(optiontabs);
+
        return ret;
 }
 
@@ -1279,8 +1577,9 @@ static int xmldoc_parse_option(struct ast_xml_node *fixnode, const char *tabs, s
 static void xmldoc_parse_optionlist(struct ast_xml_node *fixnode, const char *tabs, struct ast_str **buffer)
 {
        struct ast_xml_node *node;
-       const char *optname;
+       const char *optname, *hasparams;
        char *optionsyntax;
+       int optparams;
 
        for (node = ast_xml_node_get_children(fixnode); node; node = ast_xml_node_get_next(node)) {
                /* Start appending every option tag. */
@@ -1294,8 +1593,16 @@ static void xmldoc_parse_optionlist(struct ast_xml_node *fixnode, const char *ta
                        continue;
                }
 
-               optionsyntax = xmldoc_get_syntax(node, optname, "argument", 0, 1);
+               optparams = 1;
+               hasparams = ast_xml_get_attribute(node, "hasparams");
+               if (hasparams && !strcasecmp(hasparams, "optional")) {
+                       optparams = 2;
+               }
+
+               optionsyntax = xmldoc_get_syntax_fun(node, optname, "argument", 0, optparams);
                if (!optionsyntax) {
+                       ast_xml_free_attr(optname);
+                       ast_xml_free_attr(hasparams);
                        continue;
                }
 
@@ -1304,6 +1611,9 @@ static void xmldoc_parse_optionlist(struct ast_xml_node *fixnode, const char *ta
                if (!xmldoc_parse_option(node, tabs, buffer)) {
                        ast_str_append(buffer, 0, "\n");
                }
+               ast_str_append(buffer, 0, "\n");
+               ast_xml_free_attr(optname);
+               ast_xml_free_attr(hasparams);
        }
 }
 
@@ -1325,7 +1635,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, "argument");
        if (!(paramname = ast_xml_get_attribute(node, "name"))) {
                /* parameter MUST have an attribute name. */
                return;
@@ -1336,7 +1646,7 @@ static void xmldoc_parse_parameter(struct ast_xml_node *fixnode, const char *tab
                return;
        }
 
-       if (!hasarguments) {
+       if (!hasarguments && xmldoc_has_nodes(node)) {
                ast_str_append(buffer, 0, "%s\n", paramname);
                ast_xml_free_attr(paramname);
                printed = 1;
@@ -1361,6 +1671,9 @@ static void xmldoc_parse_parameter(struct ast_xml_node *fixnode, const char *tab
                        continue;
                }
        }
+       if (!printed) {
+               ast_xml_free_attr(paramname);
+       }
        ast_free(internaltabs);
 }
 
@@ -1396,13 +1709,13 @@ char *ast_xmldoc_build_arguments(const char *type, const char *name)
                xmldoc_parse_parameter(node, "", &ret);
        }
 
-       if (ret->used > 0) {
+       if (ast_str_strlen(ret) > 0) {
                /* remove last '\n' */
-               if (ret->str[ret->used - 1] == '\n') {
-                       ret->str[ret->used - 1] = '\0';
-                       ret->used--;
+               char *buf = ast_str_buffer(ret);
+               if (buf[ast_str_strlen(ret) - 1] == '\n') {
+                       ast_str_truncate(ret, -1);
                }
-               retstr = ast_strdup(ret->str);
+               retstr = ast_strdup(ast_str_buffer(ret));
        }
        ast_free(ret);
 
@@ -1442,9 +1755,9 @@ static struct ast_str *xmldoc_get_formatted(struct ast_xml_node *node, int raw_o
                }
                /* remove last '\n' */
                /* XXX Don't modify ast_str internals manually */
-               if (ret->str[ret->used-1] == '\n') {
-                       ret->str[ret->used-1] = '\0';
-                       ret->used--;
+               tmpstr = ast_str_buffer(ret);
+               if (tmpstr[ast_str_strlen(ret) - 1] == '\n') {
+                       ast_str_truncate(ret, -1);
                }
        }
        return ret;
@@ -1473,7 +1786,7 @@ static char *xmldoc_build_field(const char *type, const char *name, const char *
        node = xmldoc_get_node(type, name, documentation_language);
 
        if (!node) {
-               ast_log(LOG_WARNING, "Counldn't find %s %s in XML documentation\n", type, name);
+               ast_log(LOG_WARNING, "Couldn't find %s %s in XML documentation\n", type, name);
                return ret;
        }
 
@@ -1485,8 +1798,8 @@ static char *xmldoc_build_field(const char *type, const char *name, const char *
        }
 
        formatted = xmldoc_get_formatted(node, raw, raw);
-       if (formatted->used > 0) {
-               ret = ast_strdup(formatted->str);
+       if (ast_str_strlen(formatted) > 0) {
+               ret = ast_strdup(ast_str_buffer(formatted));
        }
        ast_free(formatted);
 
@@ -1533,7 +1846,7 @@ int ast_xmldoc_load_documentation(void)
        /* setup default XML documentation language */
        snprintf(documentation_language, sizeof(documentation_language), default_documentation_language);
 
-       if ((cfg = ast_config_load2("asterisk.conf", "" /* core can't reload */, cnfflags))) {
+       if ((cfg = ast_config_load2("asterisk.conf", "" /* core can't reload */, cnfflags)) && cfg != CONFIG_STATUS_FILEINVALID) {
                for (var = ast_variable_browse(cfg, "options"); var; var = var->next) {
                        if (!strcasecmp(var->name, "documentation_language")) {
                                if (!ast_strlen_zero(var->value)) {