Merge the rest of the FullyBooted patch
[asterisk/asterisk.git] / main / xmldoc.c
index 084fdf3..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"
@@ -58,6 +60,7 @@ struct documentation_tree {
 };
 
 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
@@ -268,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;
                                }
@@ -300,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;
@@ -396,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);
 
@@ -510,7 +512,7 @@ static struct ast_xml_node *xmldoc_get_node(const char *type, const char *name,
  *  \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;
@@ -553,8 +555,8 @@ static __attribute__((format(printf,4,5))) void xmldoc_reverse_helper(int revers
  *  \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'.
+ *  \retval 1 If a 'what' element is found inside 'node'.
+ *  \retval 0 If no 'what' is found inside 'node'.
  */
 static int xmldoc_has_inside(struct ast_xml_node *fixnode, const char *what)
 {
@@ -569,6 +571,45 @@ static int xmldoc_has_inside(struct ast_xml_node *fixnode, const char *what)
 }
 
 /*! \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.
@@ -584,11 +625,10 @@ static char *xmldoc_get_syntax_fun(struct ast_xml_node *rootnode, const char *ro
 #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");
@@ -696,16 +736,13 @@ static char *xmldoc_get_syntax_fun(struct ast_xml_node *rootnode, const char *ro
                        if (argname) {
                                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 */
@@ -715,6 +752,8 @@ static char *xmldoc_get_syntax_fun(struct ast_xml_node *rootnode, const char *ro
                                ast_asprintf(&syntax, "%s%s", (printrootname ? rootname : ""), (printparenthesis ? "()" : ""));
                                return syntax;
                        }
+                       paramname = ast_strdup(paramnameattr);
+                       ast_xml_free_attr(paramnameattr);
                }
 
                /* Defaults to 'false'. */
@@ -779,11 +818,7 @@ static char *xmldoc_get_syntax_fun(struct ast_xml_node *rootnode, const char *ro
                                }
                        }
                }
-               if (paramnamemalloc) {
-                       ast_free((char *) paramname);
-               } else {
-                       ast_xml_free_attr(paramname);
-               }
+               ast_free(paramname);
 
                paramcount++;
        }
@@ -848,7 +883,7 @@ static char *xmldoc_parse_cmd_enumlist(struct ast_xml_node *fixnode)
 
        ast_str_append(&paramname, 0, "}");
 
-       ret = ast_strdup(paramname->str);
+       ret = ast_strdup(ast_str_buffer(paramname));
        ast_free(paramname);
 
        return ret;
@@ -945,7 +980,63 @@ static char *xmldoc_get_syntax_cmd(struct ast_xml_node *fixnode, const char *nam
        }
 
        /* return a common string. */
-       ret = ast_strdup(syntax->str);
+       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;
@@ -954,16 +1045,18 @@ static char *xmldoc_get_syntax_cmd(struct ast_xml_node *fixnode, const char *nam
 /*! \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. */
-struct strsyntaxtype {
+static struct strsyntaxtype {
        const char *type;
        enum syntaxtype stxtype;
 } stxtype[] = {
        { "function",           FUNCTION_SYNTAX },
        { "application",        FUNCTION_SYNTAX },
+       { "manager",            MANAGER_SYNTAX  },
        { "agi",                COMMAND_SYNTAX  }
 };
 
@@ -1001,10 +1094,18 @@ char *ast_xmldoc_build_syntax(const char *type, const char *name)
        }
 
        if (node) {
-               if (xmldoc_get_syntax_type(type) == FUNCTION_SYNTAX) {
+               switch (xmldoc_get_syntax_type(type)) {
+               case FUNCTION_SYNTAX:
                        syntax = xmldoc_get_syntax_fun(node, name, "parameter", 1, 1);
-               } else {
+                       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;
@@ -1051,9 +1152,9 @@ static int xmldoc_parse_para(struct ast_xml_node *node, const char *tabs, const
                        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_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;
@@ -1141,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) {
@@ -1207,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);
                }
@@ -1341,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;
@@ -1360,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))) {
@@ -1367,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;
 }
 
@@ -1462,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. */
@@ -1477,8 +1593,16 @@ static void xmldoc_parse_optionlist(struct ast_xml_node *fixnode, const char *ta
                        continue;
                }
 
-               optionsyntax = xmldoc_get_syntax_fun(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;
                }
 
@@ -1487,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);
        }
 }
 
@@ -1519,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;
@@ -1544,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);
 }
 
@@ -1579,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);
 
@@ -1625,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;
@@ -1656,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;
        }
 
@@ -1668,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);
 
@@ -1716,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)) {