Sat Apr 26 2014 22:01:42

Asterisk developer's documentation


xmldoc.c
Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2008, Eliel C. Sardanons (LU1ALY) <eliels@gmail.com>
00005  *
00006  * See http://www.asterisk.org for more information about
00007  * the Asterisk project. Please do not directly contact
00008  * any of the maintainers of this project for assistance;
00009  * the project provides a web site, mailing lists and IRC
00010  * channels for your use.
00011  *
00012  * This program is free software, distributed under the terms of
00013  * the GNU General Public License Version 2. See the LICENSE file
00014  * at the top of the source tree.
00015  */
00016 
00017 /*! \file
00018  *
00019  * \brief XML Documentation API
00020  *
00021  * \author Eliel C. Sardanons (LU1ALY) <eliels@gmail.com>
00022  *
00023  * \extref libxml2 http://www.xmlsoft.org/
00024  */
00025 
00026 /*** MODULEINFO
00027    <support_level>core</support_level>
00028  ***/
00029 
00030 #include "asterisk.h"
00031 
00032 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 402151 $")
00033 
00034 #include "asterisk/_private.h"
00035 #include "asterisk/paths.h"
00036 #include "asterisk/linkedlists.h"
00037 #include "asterisk/config.h"
00038 #include "asterisk/term.h"
00039 #include "asterisk/astobj2.h"
00040 #include "asterisk/xmldoc.h"
00041 
00042 #ifdef AST_XML_DOCS
00043 
00044 /*! \brief Default documentation language. */
00045 static const char default_documentation_language[] = "en_US";
00046 
00047 /*! \brief Number of columns to print when showing the XML documentation with a
00048  *         'core show application/function *' CLI command. Used in text wrapping.*/
00049 static const int xmldoc_text_columns = 74;
00050 
00051 /*! \brief This is a value that we will use to let the wrapping mechanism move the cursor
00052  *         backward and forward xmldoc_max_diff positions before cutting the middle of a
00053  *         word, trying to find a space or a \n. */
00054 static const int xmldoc_max_diff = 5;
00055 
00056 /*! \brief XML documentation language. */
00057 static char documentation_language[6];
00058 
00059 /*! \brief XML documentation tree */
00060 struct documentation_tree {
00061    char *filename;               /*!< XML document filename. */
00062    struct ast_xml_doc *doc;         /*!< Open document pointer. */
00063    AST_RWLIST_ENTRY(documentation_tree) entry;
00064 };
00065 
00066 static char *xmldoc_get_syntax_cmd(struct ast_xml_node *fixnode, const char *name, int printname);
00067 static int xmldoc_parse_enumlist(struct ast_xml_node *fixnode, const char *tabs, struct ast_str **buffer);
00068 static int xmldoc_parse_info(struct ast_xml_node *node, const char *tabs, const char *posttabs, struct ast_str **buffer);
00069 static int xmldoc_parse_para(struct ast_xml_node *node, const char *tabs, const char *posttabs, struct ast_str **buffer);
00070 static int xmldoc_parse_specialtags(struct ast_xml_node *fixnode, const char *tabs, const char *posttabs, struct ast_str **buffer);
00071 
00072 
00073 /*!
00074  * \brief Container of documentation trees
00075  *
00076  * \note A RWLIST is a sufficient container type to use here for now.
00077  *       However, some changes will need to be made to implement ref counting
00078  *       if reload support is added in the future.
00079  */
00080 static AST_RWLIST_HEAD_STATIC(xmldoc_tree, documentation_tree);
00081 
00082 static const struct strcolorized_tags {
00083    const char *init;      /*!< Replace initial tag with this string. */
00084    const char *end;       /*!< Replace end tag with this string. */
00085    const int colorfg;     /*!< Foreground color. */
00086    const char *inittag;   /*!< Initial tag description. */
00087    const char *endtag;    /*!< Ending tag description. */
00088 } colorized_tags[] = {
00089    { "<",  ">",  COLOR_GREEN,  "<replaceable>", "</replaceable>" },
00090    { "\'", "\'", COLOR_BLUE,   "<literal>",     "</literal>" },
00091    { "*",  "*",  COLOR_RED,    "<emphasis>",    "</emphasis>" },
00092    { "\"", "\"", COLOR_YELLOW, "<filename>",    "</filename>" },
00093    { "\"", "\"", COLOR_CYAN,   "<directory>",   "</directory>" },
00094    { "${", "}",  COLOR_GREEN,  "<variable>",    "</variable>" },
00095    { "",   "",   COLOR_BLUE,   "<value>",       "</value>" },
00096    { "",   "",   COLOR_BLUE,   "<enum>",        "</enum>" },
00097    { "\'", "\'", COLOR_GRAY,   "<astcli>",      "</astcli>" },
00098 
00099    /* Special tags */
00100    { "", "", COLOR_YELLOW, "<note>",   "</note>" },
00101    { "", "", COLOR_RED,   "<warning>", "</warning>" }
00102 };
00103 
00104 static const struct strspecial_tags {
00105    const char *tagname;    /*!< Special tag name. */
00106    const char *init;    /*!< Print this at the beginning. */
00107    const char *end;     /*!< Print this at the end. */
00108 } special_tags[] = {
00109    { "note",    "<note>NOTE:</note> ",             "" },
00110    { "warning", "<warning>WARNING!!!:</warning> ", "" }
00111 };
00112 
00113 /*! \internal
00114  *  \brief Calculate the space in bytes used by a format string
00115  *         that will be passed to a sprintf function.
00116  *  \param postbr The format string to use to calculate the length.
00117  *  \retval The postbr length.
00118  */
00119 static int xmldoc_postbrlen(const char *postbr)
00120 {
00121    int postbrreallen = 0, i;
00122    size_t postbrlen;
00123 
00124    if (!postbr) {
00125       return 0;
00126    }
00127    postbrlen = strlen(postbr);
00128    for (i = 0; i < postbrlen; i++) {
00129       if (postbr[i] == '\t') {
00130          postbrreallen += 8 - (postbrreallen % 8);
00131       } else {
00132          postbrreallen++;
00133       }
00134    }
00135    return postbrreallen;
00136 }
00137 
00138 /*! \internal
00139  *  \brief Setup postbr to be used while wrapping the text.
00140  *         Add to postbr array all the spaces and tabs at the beginning of text.
00141  *  \param postbr output array.
00142  *  \param len text array length.
00143  *  \param text Text with format string before the actual string.
00144  */
00145 static void xmldoc_setpostbr(char *postbr, size_t len, const char *text)
00146 {
00147    int c, postbrlen = 0;
00148 
00149    if (!text) {
00150       return;
00151    }
00152 
00153    for (c = 0; c < len; c++) {
00154       if (text[c] == '\t' || text[c] == ' ') {
00155          postbr[postbrlen++] = text[c];
00156       } else {
00157          break;
00158       }
00159    }
00160    postbr[postbrlen] = '\0';
00161 }
00162 
00163 /*! \internal
00164  *  \brief Try to find a space or a break in text starting at currentpost
00165  *         and moving at most maxdiff positions.
00166  *         Helper for xmldoc_string_wrap().
00167  *  \param text Input string where it will search.
00168  *  \param currentpos Current position within text.
00169  *  \param maxdiff Not move more than maxdiff inside text.
00170  *  \retval 1 if a space or break is found inside text while moving.
00171  *  \retval 0 if no space or break is found.
00172  */
00173 static int xmldoc_wait_nextspace(const char *text, int currentpos, int maxdiff)
00174 {
00175    int i, textlen;
00176 
00177    if (!text) {
00178       return 0;
00179    }
00180 
00181    textlen = strlen(text);
00182    for (i = currentpos; i < textlen; i++) {
00183       if (text[i] == ESC) {
00184          /* Move to the end of the escape sequence */
00185          while (i < textlen && text[i] != 'm') {
00186             i++;
00187          }
00188       } else if (text[i] == ' ' || text[i] == '\n') {
00189          /* Found the next space or linefeed */
00190          return 1;
00191       } else if (i - currentpos > maxdiff) {
00192          /* We have looked the max distance and didn't find it */
00193          return 0;
00194       }
00195    }
00196 
00197    /* Reached the end and did not find it */
00198 
00199    return 0;
00200 }
00201 
00202 /*! \internal
00203  *  \brief Helper function for xmldoc_string_wrap().
00204  *    Try to found a space or a break inside text moving backward
00205  *    not more than maxdiff positions.
00206  *  \param text The input string where to search for a space.
00207  *  \param currentpos The current cursor position.
00208  *  \param maxdiff The max number of positions to move within text.
00209  *  \retval 0 If no space is found (Notice that text[currentpos] is not a space or a break)
00210  *  \retval > 0 If a space or a break is found, and the result is the position relative to
00211  *    currentpos.
00212  */
00213 static int xmldoc_foundspace_backward(const char *text, int currentpos, int maxdiff)
00214 {
00215    int i;
00216 
00217    for (i = currentpos; i > 0; i--) {
00218       if (text[i] == ' ' || text[i] == '\n') {
00219          return (currentpos - i);
00220       } else if (text[i] == 'm' && (text[i - 1] >= '0' || text[i - 1] <= '9')) {
00221          /* give up, we found the end of a possible ESC sequence. */
00222          return 0;
00223       } else if (currentpos - i > maxdiff) {
00224          /* give up, we can't move anymore. */
00225          return 0;
00226       }
00227    }
00228 
00229    /* we found the beginning of the text */
00230 
00231    return 0;
00232 }
00233 
00234 /*! \internal
00235  *  \brief Justify a text to a number of columns.
00236  *  \param text Input text to be justified.
00237  *  \param columns Number of columns to preserve in the text.
00238  *  \param maxdiff Try to not cut a word when goinf down.
00239  *  \retval NULL on error.
00240  *  \retval The wrapped text.
00241  */
00242 static char *xmldoc_string_wrap(const char *text, int columns, int maxdiff)
00243 {
00244    struct ast_str *tmp;
00245    char *ret, postbr[160];
00246    int count = 1, i, backspace, needtobreak = 0, colmax, textlen;
00247 
00248    /* sanity check */
00249    if (!text || columns <= 0 || maxdiff < 0) {
00250       ast_log(LOG_WARNING, "Passing wrong arguments while trying to wrap the text\n");
00251       return NULL;
00252    }
00253 
00254    tmp = ast_str_create(strlen(text) * 3);
00255 
00256    if (!tmp) {
00257       return NULL;
00258    }
00259 
00260    /* Check for blanks and tabs and put them in postbr. */
00261    xmldoc_setpostbr(postbr, sizeof(postbr), text);
00262    colmax = columns - xmldoc_postbrlen(postbr);
00263 
00264    textlen = strlen(text);
00265    for (i = 0; i < textlen; i++) {
00266       if (needtobreak || !(count % colmax)) {
00267          if (text[i] == ' ') {
00268             ast_str_append(&tmp, 0, "\n%s", postbr);
00269             needtobreak = 0;
00270             count = 1;
00271          } else if (text[i] != '\n') {
00272             needtobreak = 1;
00273             if (xmldoc_wait_nextspace(text, i, maxdiff)) {
00274                /* wait for the next space */
00275                ast_str_append(&tmp, 0, "%c", text[i]);
00276                continue;
00277             }
00278             /* Try to look backwards */
00279             backspace = xmldoc_foundspace_backward(text, i, maxdiff);
00280             if (backspace) {
00281                needtobreak = 1;
00282                ast_str_truncate(tmp, -backspace);
00283                i -= backspace + 1;
00284                continue;
00285             }
00286             ast_str_append(&tmp, 0, "\n%s", postbr);
00287             needtobreak = 0;
00288             count = 1;
00289          }
00290          /* skip blanks after a \n */
00291          while (text[i] == ' ') {
00292             i++;
00293          }
00294       }
00295       if (text[i] == '\n') {
00296          xmldoc_setpostbr(postbr, sizeof(postbr), &text[i] + 1);
00297          colmax = columns - xmldoc_postbrlen(postbr);
00298          needtobreak = 0;
00299          count = 1;
00300       }
00301       if (text[i] == ESC) {
00302          /* Ignore Escape sequences. */
00303          do {
00304             ast_str_append(&tmp, 0, "%c", text[i]);
00305             i++;
00306          } while (i < textlen && text[i] != 'm');
00307       } else {
00308          count++;
00309       }
00310       ast_str_append(&tmp, 0, "%c", text[i]);
00311    }
00312 
00313    ret = ast_strdup(ast_str_buffer(tmp));
00314    ast_free(tmp);
00315 
00316    return ret;
00317 }
00318 
00319 char *ast_xmldoc_printable(const char *bwinput, int withcolors)
00320 {
00321    struct ast_str *colorized;
00322    char *wrapped = NULL;
00323    int i, c, len, colorsection;
00324    char *tmp;
00325    size_t bwinputlen;
00326    static const int base_fg = COLOR_CYAN;
00327 
00328    if (!bwinput) {
00329       return NULL;
00330    }
00331 
00332    bwinputlen = strlen(bwinput);
00333 
00334    if (!(colorized = ast_str_create(256))) {
00335       return NULL;
00336    }
00337 
00338    if (withcolors) {
00339       ast_term_color_code(&colorized, base_fg, 0);
00340       if (!colorized) {
00341          return NULL;
00342       }
00343    }
00344 
00345    for (i = 0; i < bwinputlen; i++) {
00346       colorsection = 0;
00347       /* Check if we are at the beginning of a tag to be colorized. */
00348       for (c = 0; c < ARRAY_LEN(colorized_tags); c++) {
00349          if (strncasecmp(bwinput + i, colorized_tags[c].inittag, strlen(colorized_tags[c].inittag))) {
00350             continue;
00351          }
00352 
00353          if (!(tmp = strcasestr(bwinput + i + strlen(colorized_tags[c].inittag), colorized_tags[c].endtag))) {
00354             continue;
00355          }
00356 
00357          len = tmp - (bwinput + i + strlen(colorized_tags[c].inittag));
00358 
00359          /* Setup color */
00360          if (withcolors) {
00361             if (ast_opt_light_background) {
00362                /* Turn off *bright* colors */
00363                ast_term_color_code(&colorized, colorized_tags[c].colorfg & 0x7f, 0);
00364             } else {
00365                /* Turn on *bright* colors */
00366                ast_term_color_code(&colorized, colorized_tags[c].colorfg | 0x80, 0);
00367             }
00368             if (!colorized) {
00369                return NULL;
00370             }
00371          }
00372 
00373          /* copy initial string replace */
00374          ast_str_append(&colorized, 0, "%s", colorized_tags[c].init);
00375          if (!colorized) {
00376             return NULL;
00377          }
00378          {
00379             char buf[len + 1];
00380             ast_copy_string(buf, bwinput + i + strlen(colorized_tags[c].inittag), sizeof(buf));
00381             ast_str_append(&colorized, 0, "%s", buf);
00382          }
00383          if (!colorized) {
00384             return NULL;
00385          }
00386 
00387          /* copy the ending string replace */
00388          ast_str_append(&colorized, 0, "%s", colorized_tags[c].end);
00389          if (!colorized) {
00390             return NULL;
00391          }
00392 
00393          /* Continue with the last color. */
00394          if (withcolors) {
00395             ast_term_color_code(&colorized, base_fg, 0);
00396             if (!colorized) {
00397                return NULL;
00398             }
00399          }
00400 
00401          i += len + strlen(colorized_tags[c].endtag) + strlen(colorized_tags[c].inittag) - 1;
00402          colorsection = 1;
00403          break;
00404       }
00405 
00406       if (!colorsection) {
00407          ast_str_append(&colorized, 0, "%c", bwinput[i]);
00408          if (!colorized) {
00409             return NULL;
00410          }
00411       }
00412    }
00413 
00414    if (withcolors) {
00415       ast_str_append(&colorized, 0, "%s", term_end());
00416       if (!colorized) {
00417          return NULL;
00418       }
00419    }
00420 
00421    /* Wrap the text, notice that string wrap will avoid cutting an ESC sequence. */
00422    wrapped = xmldoc_string_wrap(ast_str_buffer(colorized), xmldoc_text_columns, xmldoc_max_diff);
00423 
00424    ast_free(colorized);
00425 
00426    return wrapped;
00427 }
00428 
00429 /*! \internal
00430  *  \brief Cleanup spaces and tabs after a \n
00431  *  \param text String to be cleaned up.
00432  *  \param output buffer (not already allocated).
00433  *  \param lastspaces Remove last spaces in the string.
00434  */
00435 static void xmldoc_string_cleanup(const char *text, struct ast_str **output, int lastspaces)
00436 {
00437    int i;
00438    size_t textlen;
00439 
00440    if (!text) {
00441       *output = NULL;
00442       return;
00443    }
00444 
00445    textlen = strlen(text);
00446 
00447    *output = ast_str_create(textlen);
00448    if (!(*output)) {
00449       ast_log(LOG_ERROR, "Problem allocating output buffer\n");
00450       return;
00451    }
00452 
00453    for (i = 0; i < textlen; i++) {
00454       if (text[i] == '\n' || text[i] == '\r') {
00455          /* remove spaces/tabs/\n after a \n. */
00456          while (text[i + 1] == '\t' || text[i + 1] == '\r' || text[i + 1] == '\n') {
00457             i++;
00458          }
00459          ast_str_append(output, 0, " ");
00460          continue;
00461       } else {
00462          ast_str_append(output, 0, "%c", text[i]);
00463       }
00464    }
00465 
00466    /* remove last spaces (we don't want always to remove the trailing spaces). */
00467    if (lastspaces) {
00468       ast_str_trim_blanks(*output);
00469    }
00470 }
00471 
00472 /*! \internal
00473  * \brief Check if the given attribute on the given node matches the given value.
00474  * \param node the node to match
00475  * \param attr the name of the attribute
00476  * \param value the expected value of the attribute
00477  * \retval true if the given attribute contains the given value
00478  * \retval false if the given attribute does not exist or does not contain the given value
00479  */
00480 static int xmldoc_attribute_match(struct ast_xml_node *node, const char *attr, const char *value)
00481 {
00482    const char *attr_value = ast_xml_get_attribute(node, attr);
00483    int match = attr_value && !strcmp(attr_value, value);
00484    ast_xml_free_attr(attr_value);
00485    return match;
00486 }
00487 
00488 /*! \internal
00489  *  \brief Get the application/function node for 'name' application/function with language 'language'
00490  *         and module 'module' if we don't find any, get the first application
00491  *         with 'name' no matter which language or module.
00492  *  \param type 'application', 'function', ...
00493  *  \param name Application or Function name.
00494  *  \param module Module item is in.
00495  *  \param language Try to get this language (if not found try with en_US)
00496  *  \retval NULL on error.
00497  *  \retval A node of type ast_xml_node.
00498  */
00499 static struct ast_xml_node *xmldoc_get_node(const char *type, const char *name, const char *module, const char *language)
00500 {
00501    struct ast_xml_node *node = NULL;
00502    struct ast_xml_node *first_match = NULL;
00503    struct ast_xml_node *lang_match = NULL;
00504    struct documentation_tree *doctree;
00505 
00506    AST_RWLIST_RDLOCK(&xmldoc_tree);
00507    AST_LIST_TRAVERSE(&xmldoc_tree, doctree, entry) {
00508       /* the core xml documents have priority over thirdparty document. */
00509       node = ast_xml_get_root(doctree->doc);
00510       if (!node) {
00511          break;
00512       }
00513 
00514       node = ast_xml_node_get_children(node);
00515       while ((node = ast_xml_find_element(node, type, "name", name))) {
00516          if (!ast_xml_node_get_children(node)) {
00517             /* ignore empty nodes */
00518             node = ast_xml_node_get_next(node);
00519             continue;
00520          }
00521 
00522          if (!first_match) {
00523             first_match = node;
00524          }
00525 
00526          /* Check language */
00527          if (xmldoc_attribute_match(node, "language", language)) {
00528             if (!lang_match) {
00529                lang_match = node;
00530             }
00531 
00532             /* if module is empty we have a match */
00533             if (ast_strlen_zero(module)) {
00534                break;
00535             }
00536 
00537             /* Check module */
00538             if (xmldoc_attribute_match(node, "module", module)) {
00539                break;
00540             }
00541          }
00542 
00543          node = ast_xml_node_get_next(node);
00544       }
00545 
00546       /* if we matched lang and module return this match */
00547       if (node) {
00548          break;
00549       }
00550 
00551       /* we didn't match lang and module, just return the first
00552        * result with a matching language if we have one */
00553       if (lang_match) {
00554          node = lang_match;
00555          break;
00556       }
00557 
00558       /* we didn't match with only the language, just return the
00559        * first match */
00560       if (first_match) {
00561          node = first_match;
00562          break;
00563       }
00564    }
00565    AST_RWLIST_UNLOCK(&xmldoc_tree);
00566 
00567    return node;
00568 }
00569 
00570 /*! \internal
00571  *  \brief Helper function used to build the syntax, it allocates the needed buffer (or reallocates it),
00572  *         and based on the reverse value it makes use of fmt to print the parameter list inside the
00573  *         realloced buffer (syntax).
00574  *  \param reverse We are going backwards while generating the syntax?
00575  *  \param len Current length of 'syntax' buffer.
00576  *  \param syntax Output buffer for the concatenated values.
00577  *  \param fmt A format string that will be used in a sprintf call.
00578  */
00579 static void __attribute__((format(printf, 4, 5))) xmldoc_reverse_helper(int reverse, int *len, char **syntax, const char *fmt, ...)
00580 {
00581    int totlen;
00582    int tmpfmtlen;
00583    char *tmpfmt;
00584    char *new_syntax;
00585    char tmp;
00586    va_list ap;
00587 
00588    va_start(ap, fmt);
00589    if (ast_vasprintf(&tmpfmt, fmt, ap) < 0) {
00590       va_end(ap);
00591       return;
00592    }
00593    va_end(ap);
00594 
00595    tmpfmtlen = strlen(tmpfmt);
00596    totlen = *len + tmpfmtlen + 1;
00597 
00598    new_syntax = ast_realloc(*syntax, totlen);
00599    if (!new_syntax) {
00600       ast_free(tmpfmt);
00601       return;
00602    }
00603    *syntax = new_syntax;
00604 
00605    if (reverse) {
00606       memmove(*syntax + tmpfmtlen, *syntax, *len);
00607       /* Save this char, it will be overwritten by the \0 of strcpy. */
00608       tmp = (*syntax)[0];
00609       strcpy(*syntax, tmpfmt);
00610       /* Restore the already saved char. */
00611       (*syntax)[tmpfmtlen] = tmp;
00612       (*syntax)[totlen - 1] = '\0';
00613    } else {
00614       strcpy(*syntax + *len, tmpfmt);
00615    }
00616 
00617    *len = totlen - 1;
00618    ast_free(tmpfmt);
00619 }
00620 
00621 /*! \internal
00622  *  \brief Check if the passed node has 'what' tags inside it.
00623  *  \param node Root node to search 'what' elements.
00624  *  \param what node name to search inside node.
00625  *  \retval 1 If a 'what' element is found inside 'node'.
00626  *  \retval 0 If no 'what' is found inside 'node'.
00627  */
00628 static int xmldoc_has_inside(struct ast_xml_node *fixnode, const char *what)
00629 {
00630    struct ast_xml_node *node = fixnode;
00631 
00632    for (node = ast_xml_node_get_children(fixnode); node; node = ast_xml_node_get_next(node)) {
00633       if (!strcasecmp(ast_xml_node_get_name(node), what)) {
00634          return 1;
00635       }
00636    }
00637    return 0;
00638 }
00639 
00640 /*! \internal
00641  *  \brief Check if the passed node has at least one node inside it.
00642  *  \param node Root node to search node elements.
00643  *  \retval 1 If a node element is found inside 'node'.
00644  *  \retval 0 If no node is found inside 'node'.
00645  */
00646 static int xmldoc_has_nodes(struct ast_xml_node *fixnode)
00647 {
00648    struct ast_xml_node *node = fixnode;
00649 
00650    for (node = ast_xml_node_get_children(fixnode); node; node = ast_xml_node_get_next(node)) {
00651       if (strcasecmp(ast_xml_node_get_name(node), "text")) {
00652          return 1;
00653       }
00654    }
00655    return 0;
00656 }
00657 
00658 /*! \internal
00659  *  \brief Check if the passed node has at least one specialtag.
00660  *  \param node Root node to search "specialtags" elements.
00661  *  \retval 1 If a "specialtag" element is found inside 'node'.
00662  *  \retval 0 If no "specialtag" is found inside 'node'.
00663  */
00664 static int xmldoc_has_specialtags(struct ast_xml_node *fixnode)
00665 {
00666    struct ast_xml_node *node = fixnode;
00667    int i;
00668 
00669    for (node = ast_xml_node_get_children(fixnode); node; node = ast_xml_node_get_next(node)) {
00670       for (i = 0; i < ARRAY_LEN(special_tags); i++) {
00671          if (!strcasecmp(ast_xml_node_get_name(node), special_tags[i].tagname)) {
00672             return 1;
00673          }
00674       }
00675    }
00676    return 0;
00677 }
00678 
00679 /*! \internal
00680  *  \brief Build the syntax for a specified starting node.
00681  *  \param rootnode A pointer to the ast_xml root node.
00682  *  \param rootname Name of the application, function, option, etc. to build the syntax.
00683  *  \param childname The name of each parameter node.
00684  *  \param printparenthesis Boolean if we must print parenthesis if not parameters are found in the rootnode.
00685  *  \param printrootname Boolean if we must print the rootname before the syntax and parenthesis at the begining/end.
00686  *  \retval NULL on error.
00687  *  \retval An ast_malloc'ed string with the syntax generated.
00688  */
00689 static char *xmldoc_get_syntax_fun(struct ast_xml_node *rootnode, const char *rootname, const char *childname, int printparenthesis, int printrootname)
00690 {
00691 #define GOTONEXT(__rev, __a) (__rev ? ast_xml_node_get_prev(__a) : ast_xml_node_get_next(__a))
00692 #define ISLAST(__rev, __a)  (__rev == 1 ? (ast_xml_node_get_prev(__a) ? 0 : 1) : (ast_xml_node_get_next(__a) ? 0 : 1))
00693 #define MP(__a) ((multiple ? __a : ""))
00694    struct ast_xml_node *node = NULL, *firstparam = NULL, *lastparam = NULL;
00695    const char *paramtype, *multipletype, *paramnameattr, *attrargsep, *parenthesis, *argname;
00696    int reverse, required, paramcount = 0, openbrackets = 0, len = 0, hasparams=0;
00697    int reqfinode = 0, reqlanode = 0, optmidnode = 0, prnparenthesis, multiple;
00698    char *syntax = NULL, *argsep, *paramname;
00699 
00700    if (ast_strlen_zero(rootname) || ast_strlen_zero(childname)) {
00701       ast_log(LOG_WARNING, "Tried to look in XML tree with faulty rootname or childname while creating a syntax.\n");
00702       return NULL;
00703    }
00704 
00705    if (!rootnode || !ast_xml_node_get_children(rootnode)) {
00706       /* If the rootnode field is not found, at least print name. */
00707       if (ast_asprintf(&syntax, "%s%s", (printrootname ? rootname : ""), (printparenthesis ? "()" : "")) < 0) {
00708          syntax = NULL;
00709       }
00710       return syntax;
00711    }
00712 
00713    /* Get the argument separator from the root node attribute name 'argsep', if not found
00714    defaults to ','. */
00715    attrargsep = ast_xml_get_attribute(rootnode, "argsep");
00716    if (attrargsep) {
00717       argsep = ast_strdupa(attrargsep);
00718       ast_xml_free_attr(attrargsep);
00719    } else {
00720       argsep = ast_strdupa(",");
00721    }
00722 
00723    /* Get order of evaluation. */
00724    for (node = ast_xml_node_get_children(rootnode); node; node = ast_xml_node_get_next(node)) {
00725       if (strcasecmp(ast_xml_node_get_name(node), childname)) {
00726          continue;
00727       }
00728       required = 0;
00729       hasparams = 1;
00730       if ((paramtype = ast_xml_get_attribute(node, "required"))) {
00731          if (ast_true(paramtype)) {
00732             required = 1;
00733          }
00734          ast_xml_free_attr(paramtype);
00735       }
00736 
00737       lastparam = node;
00738       reqlanode = required;
00739 
00740       if (!firstparam) {
00741          /* first parameter node */
00742          firstparam = node;
00743          reqfinode = required;
00744       }
00745    }
00746 
00747    if (!hasparams) {
00748       /* This application, function, option, etc, doesn't have any params. */
00749       if (ast_asprintf(&syntax, "%s%s", (printrootname ? rootname : ""), (printparenthesis ? "()" : "")) < 0) {
00750          syntax = NULL;
00751       }
00752       return syntax;
00753    }
00754 
00755    if (reqfinode && reqlanode) {
00756       /* check midnode */
00757       for (node = ast_xml_node_get_children(rootnode); node; node = ast_xml_node_get_next(node)) {
00758          if (strcasecmp(ast_xml_node_get_name(node), childname)) {
00759             continue;
00760          }
00761          if (node != firstparam && node != lastparam) {
00762             if ((paramtype = ast_xml_get_attribute(node, "required"))) {
00763                if (!ast_true(paramtype)) {
00764                   optmidnode = 1;
00765                   ast_xml_free_attr(paramtype);
00766                   break;
00767                }
00768                ast_xml_free_attr(paramtype);
00769             }
00770          }
00771       }
00772    }
00773 
00774    if ((!reqfinode && reqlanode) || (reqfinode && reqlanode && optmidnode)) {
00775       reverse = 1;
00776       node = lastparam;
00777    } else {
00778       reverse = 0;
00779       node = firstparam;
00780    }
00781 
00782    /* init syntax string. */
00783    if (reverse) {
00784       xmldoc_reverse_helper(reverse, &len, &syntax,
00785          (printrootname ? (printrootname == 2 ? ")]" : ")"): ""));
00786    } else {
00787       xmldoc_reverse_helper(reverse, &len, &syntax, "%s%s", (printrootname ? rootname : ""),
00788          (printrootname ? (printrootname == 2 ? "[(" : "(") : ""));
00789    }
00790 
00791    for (; node; node = GOTONEXT(reverse, node)) {
00792       if (strcasecmp(ast_xml_node_get_name(node), childname)) {
00793          continue;
00794       }
00795 
00796       /* Get the argument name, if it is not the leaf, go inside that parameter. */
00797       if (xmldoc_has_inside(node, "argument")) {
00798          parenthesis = ast_xml_get_attribute(node, "hasparams");
00799          prnparenthesis = 0;
00800          if (parenthesis) {
00801             prnparenthesis = ast_true(parenthesis);
00802             if (!strcasecmp(parenthesis, "optional")) {
00803                prnparenthesis = 2;
00804             }
00805             ast_xml_free_attr(parenthesis);
00806          }
00807          argname = ast_xml_get_attribute(node, "name");
00808          if (argname) {
00809             paramname = xmldoc_get_syntax_fun(node, argname, "argument", prnparenthesis, prnparenthesis);
00810             ast_xml_free_attr(argname);
00811          } else {
00812             /* Malformed XML, print **UNKOWN** */
00813             paramname = ast_strdup("**unknown**");
00814          }
00815       } else {
00816          paramnameattr = ast_xml_get_attribute(node, "name");
00817          if (!paramnameattr) {
00818             ast_log(LOG_WARNING, "Malformed XML %s: no %s name\n", rootname, childname);
00819             if (syntax) {
00820                /* Free already allocated syntax */
00821                ast_free(syntax);
00822             }
00823             /* to give up is ok? */
00824             if (ast_asprintf(&syntax, "%s%s", (printrootname ? rootname : ""), (printparenthesis ? "()" : "")) < 0) {
00825                syntax = NULL;
00826             }
00827             return syntax;
00828          }
00829          paramname = ast_strdup(paramnameattr);
00830          ast_xml_free_attr(paramnameattr);
00831       }
00832 
00833       if (!paramname) {
00834          return NULL;
00835       }
00836 
00837       /* Defaults to 'false'. */
00838       multiple = 0;
00839       if ((multipletype = ast_xml_get_attribute(node, "multiple"))) {
00840          if (ast_true(multipletype)) {
00841             multiple = 1;
00842          }
00843          ast_xml_free_attr(multipletype);
00844       }
00845 
00846       required = 0;  /* Defaults to 'false'. */
00847       if ((paramtype = ast_xml_get_attribute(node, "required"))) {
00848          if (ast_true(paramtype)) {
00849             required = 1;
00850          }
00851          ast_xml_free_attr(paramtype);
00852       }
00853 
00854       /* build syntax core. */
00855 
00856       if (required) {
00857          /* First parameter */
00858          if (!paramcount) {
00859             xmldoc_reverse_helper(reverse, &len, &syntax, "%s%s%s%s", paramname, MP("["), MP(argsep), MP("...]"));
00860          } else {
00861             /* Time to close open brackets. */
00862             while (openbrackets > 0) {
00863                xmldoc_reverse_helper(reverse, &len, &syntax, (reverse ? "[" : "]"));
00864                openbrackets--;
00865             }
00866             if (reverse) {
00867                xmldoc_reverse_helper(reverse, &len, &syntax, "%s%s", paramname, argsep);
00868             } else {
00869                xmldoc_reverse_helper(reverse, &len, &syntax, "%s%s", argsep, paramname);
00870             }
00871             xmldoc_reverse_helper(reverse, &len, &syntax, "%s%s%s", MP("["), MP(argsep), MP("...]"));
00872          }
00873       } else {
00874          /* First parameter */
00875          if (!paramcount) {
00876             xmldoc_reverse_helper(reverse, &len, &syntax, "[%s%s%s%s]", paramname, MP("["), MP(argsep), MP("...]"));
00877          } else {
00878             if (ISLAST(reverse, node)) {
00879                /* This is the last parameter. */
00880                if (reverse) {
00881                   xmldoc_reverse_helper(reverse, &len, &syntax, "[%s%s%s%s]%s", paramname,
00882                            MP("["), MP(argsep), MP("...]"), argsep);
00883                } else {
00884                   xmldoc_reverse_helper(reverse, &len, &syntax, "%s[%s%s%s%s]", argsep, paramname,
00885                            MP("["), MP(argsep), MP("...]"));
00886                }
00887             } else {
00888                if (reverse) {
00889                   xmldoc_reverse_helper(reverse, &len, &syntax, "%s%s%s%s%s]", paramname, argsep,
00890                            MP("["), MP(argsep), MP("...]"));
00891                } else {
00892                   xmldoc_reverse_helper(reverse, &len, &syntax, "[%s%s%s%s%s", argsep, paramname,
00893                            MP("["), MP(argsep), MP("...]"));
00894                }
00895                openbrackets++;
00896             }
00897          }
00898       }
00899       ast_free(paramname);
00900 
00901       paramcount++;
00902    }
00903 
00904    /* Time to close open brackets. */
00905    while (openbrackets > 0) {
00906       xmldoc_reverse_helper(reverse, &len, &syntax, (reverse ? "[" : "]"));
00907       openbrackets--;
00908    }
00909 
00910    /* close syntax string. */
00911    if (reverse) {
00912       xmldoc_reverse_helper(reverse, &len, &syntax, "%s%s", (printrootname ? rootname : ""),
00913          (printrootname ? (printrootname == 2 ? "[(" : "(") : ""));
00914    } else {
00915       xmldoc_reverse_helper(reverse, &len, &syntax, (printrootname ? (printrootname == 2 ? ")]" : ")") : ""));
00916    }
00917 
00918    return syntax;
00919 #undef ISLAST
00920 #undef GOTONEXT
00921 #undef MP
00922 }
00923 
00924 /*! \internal
00925  *  \brief Parse an enumlist inside a <parameter> to generate a COMMAND
00926  *         syntax.
00927  *  \param fixnode A pointer to the <enumlist> node.
00928  *  \retval {<unknown>} on error.
00929  *  \retval A string inside brackets {} with the enum's separated by pipes |.
00930  */
00931 static char *xmldoc_parse_cmd_enumlist(struct ast_xml_node *fixnode)
00932 {
00933    struct ast_xml_node *node = fixnode;
00934    struct ast_str *paramname;
00935    char *enumname, *ret;
00936    int first = 1;
00937 
00938    paramname = ast_str_create(128);
00939    if (!paramname) {
00940       return ast_strdup("{<unkown>}");
00941    }
00942 
00943    ast_str_append(&paramname, 0, "{");
00944 
00945    for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
00946       if (strcasecmp(ast_xml_node_get_name(node), "enum")) {
00947          continue;
00948       }
00949 
00950       enumname = xmldoc_get_syntax_cmd(node, "", 0);
00951       if (!enumname) {
00952          continue;
00953       }
00954       if (!first) {
00955          ast_str_append(&paramname, 0, "|");
00956       }
00957       ast_str_append(&paramname, 0, "%s", enumname);
00958       first = 0;
00959       ast_free(enumname);
00960    }
00961 
00962    ast_str_append(&paramname, 0, "}");
00963 
00964    ret = ast_strdup(ast_str_buffer(paramname));
00965    ast_free(paramname);
00966 
00967    return ret;
00968 }
00969 
00970 /*! \internal
00971  *  \brief Generate a syntax of COMMAND type.
00972  *  \param fixnode The <syntax> node pointer.
00973  *  \param name The name of the 'command'.
00974  *  \param printname Print the name of the command before the paramters?
00975  *  \retval On error, return just 'name'.
00976  *  \retval On success return the generated syntax.
00977  */
00978 static char *xmldoc_get_syntax_cmd(struct ast_xml_node *fixnode, const char *name, int printname)
00979 {
00980    struct ast_str *syntax;
00981    struct ast_xml_node *tmpnode, *node = fixnode;
00982    char *ret, *paramname;
00983    const char *paramtype, *attrname, *literal;
00984    int required, isenum, first = 1, isliteral;
00985 
00986    syntax = ast_str_create(128);
00987    if (!syntax) {
00988       /* at least try to return something... */
00989       return ast_strdup(name);
00990    }
00991 
00992    /* append name to output string. */
00993    if (printname) {
00994       ast_str_append(&syntax, 0, "%s", name);
00995       first = 0;
00996    }
00997 
00998    for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
00999       if (strcasecmp(ast_xml_node_get_name(node), "parameter")) {
01000          continue;
01001       }
01002 
01003       if (xmldoc_has_inside(node, "parameter")) {
01004          /* is this a recursive parameter. */
01005          paramname = xmldoc_get_syntax_cmd(node, "", 0);
01006          isenum = 1;
01007       } else {
01008          for (tmpnode = ast_xml_node_get_children(node); tmpnode; tmpnode = ast_xml_node_get_next(tmpnode)) {
01009             if (!strcasecmp(ast_xml_node_get_name(tmpnode), "enumlist")) {
01010                break;
01011             }
01012          }
01013          if (tmpnode) {
01014             /* parse enumlist (note that this is a special enumlist
01015             that is used to describe a syntax like {<param1>|<param2>|...} */
01016             paramname = xmldoc_parse_cmd_enumlist(tmpnode);
01017             isenum = 1;
01018          } else {
01019             /* this is a simple parameter. */
01020             attrname = ast_xml_get_attribute(node, "name");
01021             if (!attrname) {
01022                /* ignore this bogus parameter and continue. */
01023                continue;
01024             }
01025             paramname = ast_strdup(attrname);
01026             ast_xml_free_attr(attrname);
01027             isenum = 0;
01028          }
01029       }
01030 
01031       /* Is this parameter required? */
01032       required = 0;
01033       paramtype = ast_xml_get_attribute(node, "required");
01034       if (paramtype) {
01035          required = ast_true(paramtype);
01036          ast_xml_free_attr(paramtype);
01037       }
01038 
01039       /* Is this a replaceable value or a fixed parameter value? */
01040       isliteral = 0;
01041       literal = ast_xml_get_attribute(node, "literal");
01042       if (literal) {
01043          isliteral = ast_true(literal);
01044          ast_xml_free_attr(literal);
01045       }
01046 
01047       /* if required="false" print with [...].
01048        * if literal="true" or is enum print without <..>.
01049        * if not first print a space at the beginning.
01050        */
01051       ast_str_append(&syntax, 0, "%s%s%s%s%s%s",
01052             (first ? "" : " "),
01053             (required ? "" : "["),
01054             (isenum || isliteral ? "" : "<"),
01055             paramname,
01056             (isenum || isliteral ? "" : ">"),
01057             (required ? "" : "]"));
01058       first = 0;
01059       ast_free(paramname);
01060    }
01061 
01062    /* return a common string. */
01063    ret = ast_strdup(ast_str_buffer(syntax));
01064    ast_free(syntax);
01065 
01066    return ret;
01067 }
01068 
01069 /*! \internal
01070  *  \brief Generate an AMI action/event syntax.
01071  *  \param fixnode The manager action/event node pointer.
01072  *  \param name The name of the manager action/event.
01073  *  \param manager_type "Action" or "Event"
01074  *  \retval The generated syntax.
01075  *  \retval NULL on error.
01076  */
01077 static char *xmldoc_get_syntax_manager(struct ast_xml_node *fixnode, const char *name, const char *manager_type)
01078 {
01079    struct ast_str *syntax;
01080    struct ast_xml_node *node = fixnode;
01081    const char *paramtype, *attrname;
01082    int required;
01083    char *ret;
01084 
01085    syntax = ast_str_create(128);
01086    if (!syntax) {
01087       return ast_strdup(name);
01088    }
01089 
01090    ast_str_append(&syntax, 0, "%s: %s", manager_type, name);
01091 
01092    for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
01093       if (strcasecmp(ast_xml_node_get_name(node), "parameter")) {
01094          continue;
01095       }
01096 
01097       /* Is this parameter required? */
01098       required = !strcasecmp(manager_type, "event") ? 1 : 0;
01099       paramtype = ast_xml_get_attribute(node, "required");
01100       if (paramtype) {
01101          required = ast_true(paramtype);
01102          ast_xml_free_attr(paramtype);
01103       }
01104 
01105       attrname = ast_xml_get_attribute(node, "name");
01106       if (!attrname) {
01107          /* ignore this bogus parameter and continue. */
01108          continue;
01109       }
01110 
01111       ast_str_append(&syntax, 0, "\n%s%s:%s <value>",
01112          (required ? "" : "["),
01113          attrname,
01114          (required ? "" : "]"));
01115       ast_xml_free_attr(attrname);
01116    }
01117 
01118    /* return a common string. */
01119    ret = ast_strdup(ast_str_buffer(syntax));
01120    ast_free(syntax);
01121 
01122    return ret;
01123 }
01124 
01125 /*! \brief Types of syntax that we are able to generate. */
01126 enum syntaxtype {
01127    FUNCTION_SYNTAX,
01128    MANAGER_SYNTAX,
01129    MANAGER_EVENT_SYNTAX,
01130    COMMAND_SYNTAX
01131 };
01132 
01133 /*! \brief Mapping between type of node and type of syntax to generate. */
01134 static struct strsyntaxtype {
01135    const char *type;
01136    enum syntaxtype stxtype;
01137 } stxtype[] = {
01138    { "function",     FUNCTION_SYNTAX         },
01139    { "application",  FUNCTION_SYNTAX         },
01140    { "manager",      MANAGER_SYNTAX       },
01141    { "managerEvent", MANAGER_EVENT_SYNTAX },
01142    { "agi",       COMMAND_SYNTAX       }
01143 };
01144 
01145 /*! \internal
01146  *  \brief Get syntax type based on type of node.
01147  *  \param type Type of node.
01148  *  \retval The type of syntax to generate based on the type of node.
01149  */
01150 static enum syntaxtype xmldoc_get_syntax_type(const char *type)
01151 {
01152    int i;
01153    for (i=0; i < ARRAY_LEN(stxtype); i++) {
01154       if (!strcasecmp(stxtype[i].type, type)) {
01155          return stxtype[i].stxtype;
01156       }
01157    }
01158 
01159    return FUNCTION_SYNTAX;
01160 }
01161 
01162 /*!
01163  * \internal
01164  * \brief Build syntax information for an item
01165  * \param node The syntax node to parse
01166  * \param type The source type
01167  * \param name The name of the item that the syntax describes
01168  *
01169  * \note This method exists for when you already have the node.  This
01170  * prevents having to lock the documentation tree twice
01171  *
01172  * \returns A malloc'd character pointer to the syntax of the item
01173  * \returns NULL on failure
01174  *
01175  * \since 11
01176  */
01177 static char *_ast_xmldoc_build_syntax(struct ast_xml_node *node, const char *type, const char *name)
01178 {
01179    char *syntax = NULL;
01180 
01181    for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
01182       if (!strcasecmp(ast_xml_node_get_name(node), "syntax")) {
01183          break;
01184       }
01185    }
01186 
01187    if (!node) {
01188       return syntax;
01189    }
01190 
01191    switch (xmldoc_get_syntax_type(type)) {
01192    case FUNCTION_SYNTAX:
01193       syntax = xmldoc_get_syntax_fun(node, name, "parameter", 1, 1);
01194       break;
01195    case COMMAND_SYNTAX:
01196       syntax = xmldoc_get_syntax_cmd(node, name, 1);
01197       break;
01198    case MANAGER_SYNTAX:
01199       syntax = xmldoc_get_syntax_manager(node, name, "Action");
01200       break;
01201    case MANAGER_EVENT_SYNTAX:
01202       syntax = xmldoc_get_syntax_manager(node, name, "Event");
01203       break;
01204    default:
01205       syntax = xmldoc_get_syntax_fun(node, name, "parameter", 1, 1);
01206    }
01207 
01208    return syntax;
01209 }
01210 
01211 char *ast_xmldoc_build_syntax(const char *type, const char *name, const char *module)
01212 {
01213    struct ast_xml_node *node;
01214 
01215    node = xmldoc_get_node(type, name, module, documentation_language);
01216    if (!node) {
01217       return NULL;
01218    }
01219 
01220    return _ast_xmldoc_build_syntax(node, type, name);
01221 }
01222 
01223 /*! \internal
01224  *  \brief Parse common internal elements.  This includes paragraphs, special
01225  *         tags, and information nodes.
01226  *  \param node The element to parse
01227  *  \param tabs Add this string before the content of the parsed element.
01228  *  \param posttabs Add this string after the content of the parsed element.
01229  *  \param buffer This must be an already allocated ast_str. It will be used to
01230  *                store the result (if something has already been placed in the
01231  *                buffer, the parsed elements will be appended)
01232  *  \retval 1 if any data was appended to the buffer
01233  *  \retval 2 if the data appended to the buffer contained a text paragraph
01234  *  \retval 0 if no data was appended to the buffer
01235  */
01236 static int xmldoc_parse_common_elements(struct ast_xml_node *node, const char *tabs, const char *posttabs, struct ast_str **buffer)
01237 {
01238    return (xmldoc_parse_para(node, tabs, posttabs, buffer)
01239       || xmldoc_parse_specialtags(node, tabs, posttabs, buffer)
01240       || xmldoc_parse_info(node, tabs, posttabs, buffer));
01241 }
01242 
01243 /*! \internal
01244  *  \brief Parse a <para> element.
01245  *  \param node The <para> element pointer.
01246  *  \param tabs Added this string before the content of the <para> element.
01247  *  \param posttabs Added this string after the content of the <para> element.
01248  *  \param buffer This must be an already allocated ast_str. It will be used
01249  *         to store the result (if already has something it will be appended to the current
01250  *         string).
01251  *  \retval 1 If 'node' is a named 'para'.
01252  *  \retval 2 If data is appended in buffer.
01253  *  \retval 0 on error.
01254  */
01255 static int xmldoc_parse_para(struct ast_xml_node *node, const char *tabs, const char *posttabs, struct ast_str **buffer)
01256 {
01257    const char *tmptext;
01258    struct ast_xml_node *tmp;
01259    int ret = 0;
01260    struct ast_str *tmpstr;
01261 
01262    if (!node || !ast_xml_node_get_children(node)) {
01263       return ret;
01264    }
01265 
01266    if (strcasecmp(ast_xml_node_get_name(node), "para")) {
01267       return ret;
01268    }
01269 
01270    ast_str_append(buffer, 0, "%s", tabs);
01271 
01272    ret = 1;
01273 
01274    for (tmp = ast_xml_node_get_children(node); tmp; tmp = ast_xml_node_get_next(tmp)) {
01275       /* Get the text inside the <para> element and append it to buffer. */
01276       tmptext = ast_xml_get_text(tmp);
01277       if (tmptext) {
01278          /* Strip \n etc. */
01279          xmldoc_string_cleanup(tmptext, &tmpstr, 0);
01280          ast_xml_free_text(tmptext);
01281          if (tmpstr) {
01282             if (strcasecmp(ast_xml_node_get_name(tmp), "text")) {
01283                ast_str_append(buffer, 0, "<%s>%s</%s>", ast_xml_node_get_name(tmp),
01284                      ast_str_buffer(tmpstr), ast_xml_node_get_name(tmp));
01285             } else {
01286                ast_str_append(buffer, 0, "%s", ast_str_buffer(tmpstr));
01287             }
01288             ast_free(tmpstr);
01289             ret = 2;
01290          }
01291       }
01292    }
01293 
01294    ast_str_append(buffer, 0, "%s", posttabs);
01295 
01296    return ret;
01297 }
01298 
01299 /*! \internal
01300  *  \brief Parse special elements defined in 'struct special_tags' special elements must have a <para> element inside them.
01301  *  \param fixnode special tag node pointer.
01302  *  \param tabs put tabs before printing the node content.
01303  *  \param posttabs put posttabs after printing node content.
01304  *  \param buffer Output buffer, the special tags will be appended here.
01305  *  \retval 0 if no special element is parsed.
01306  *  \retval 1 if a special element is parsed (data is appended to buffer).
01307  *  \retval 2 if a special element is parsed and also a <para> element is parsed inside the specialtag.
01308  */
01309 static int xmldoc_parse_specialtags(struct ast_xml_node *fixnode, const char *tabs, const char *posttabs, struct ast_str **buffer)
01310 {
01311    struct ast_xml_node *node = fixnode;
01312    int ret = 0, i, count = 0;
01313 
01314    if (!node || !ast_xml_node_get_children(node)) {
01315       return ret;
01316    }
01317 
01318    for (i = 0; i < ARRAY_LEN(special_tags); i++) {
01319       if (strcasecmp(ast_xml_node_get_name(node), special_tags[i].tagname)) {
01320          continue;
01321       }
01322 
01323       ret = 1;
01324       /* This is a special tag. */
01325 
01326       /* concat data */
01327       if (!ast_strlen_zero(special_tags[i].init)) {
01328          ast_str_append(buffer, 0, "%s%s", tabs, special_tags[i].init);
01329       }
01330 
01331       /* parse <para> elements inside special tags. */
01332       for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
01333          /* first <para> just print it without tabs at the begining. */
01334          if ((xmldoc_parse_para(node, (!count ? "" : tabs), posttabs, buffer) == 2)
01335             || (xmldoc_parse_info(node, (!count ? "": tabs), posttabs, buffer) == 2)) {
01336             ret = 2;
01337          }
01338       }
01339 
01340       if (!ast_strlen_zero(special_tags[i].end)) {
01341          ast_str_append(buffer, 0, "%s%s", special_tags[i].end, posttabs);
01342       }
01343 
01344       break;
01345    }
01346 
01347    return ret;
01348 }
01349 
01350 /*! \internal
01351  *  \brief Parse an 'info' tag inside an element.
01352  *  \param node A pointer to the 'info' xml node.
01353  *  \param tabs A string to be appended at the beginning of each line being printed
01354  *              inside 'buffer'
01355  *  \param posttabs Add this string after the content of the <para> element, if one exists
01356  *  \param String buffer to put values found inide the info element.
01357  *  \ret 2 if the information contained a para element, and it returned a value of 2
01358  *  \ret 1 if information was put into the buffer
01359  *  \ret 0 if no information was put into the buffer or error
01360  */
01361 static int xmldoc_parse_info(struct ast_xml_node *node, const char *tabs, const char *posttabs, struct ast_str **buffer)
01362 {
01363    const char *tech;
01364    char *internaltabs;
01365    int internal_ret;
01366    int ret = 0;
01367 
01368    if (strcasecmp(ast_xml_node_get_name(node), "info")) {
01369       return ret;
01370    }
01371 
01372    ast_asprintf(&internaltabs, "%s    ", tabs);
01373    if (!internaltabs) {
01374       return ret;
01375    }
01376 
01377    tech = ast_xml_get_attribute(node, "tech");
01378    if (tech) {
01379       ast_str_append(buffer, 0, "%s<note>Technology: %s</note>\n", internaltabs, tech);
01380       ast_xml_free_attr(tech);
01381    }
01382 
01383    ret = 1;
01384 
01385    for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
01386       if (!strcasecmp(ast_xml_node_get_name(node), "enumlist")) {
01387          xmldoc_parse_enumlist(node, internaltabs, buffer);
01388       } else if ((internal_ret = xmldoc_parse_common_elements(node, internaltabs, posttabs, buffer))) {
01389          if (internal_ret > ret) {
01390             ret = internal_ret;
01391          }
01392       }
01393    }
01394    ast_free(internaltabs);
01395 
01396    return ret;
01397 }
01398 
01399 /*! \internal
01400  *  \brief Parse an <argument> element from the xml documentation.
01401  *  \param fixnode Pointer to the 'argument' xml node.
01402  *  \param insideparameter If we are parsing an <argument> inside a <parameter>.
01403  *  \param paramtabs pre tabs if we are inside a parameter element.
01404  *  \param tabs What to be printed before the argument name.
01405  *  \param buffer Output buffer to put values found inside the <argument> element.
01406  *  \retval 1 If there is content inside the argument.
01407  *  \retval 0 If the argument element is not parsed, or there is no content inside it.
01408  */
01409 static int xmldoc_parse_argument(struct ast_xml_node *fixnode, int insideparameter, const char *paramtabs, const char *tabs, struct ast_str **buffer)
01410 {
01411    struct ast_xml_node *node = fixnode;
01412    const char *argname;
01413    int count = 0, ret = 0;
01414 
01415    if (!node || !ast_xml_node_get_children(node)) {
01416       return ret;
01417    }
01418 
01419    /* Print the argument names */
01420    argname = ast_xml_get_attribute(node, "name");
01421    if (!argname) {
01422       return 0;
01423    }
01424    if (xmldoc_has_inside(node, "para") || xmldoc_has_inside(node, "info") || xmldoc_has_specialtags(node)) {
01425       ast_str_append(buffer, 0, "%s%s%s", tabs, argname, (insideparameter ? "\n" : ""));
01426       ast_xml_free_attr(argname);
01427    } else {
01428       ast_xml_free_attr(argname);
01429       return 0;
01430    }
01431 
01432    for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
01433       if (xmldoc_parse_common_elements(node, (insideparameter ? paramtabs : (!count ? " - " : tabs)), "\n", buffer) == 2) {
01434          count++;
01435          ret = 1;
01436       }
01437    }
01438 
01439    return ret;
01440 }
01441 
01442 /*! \internal
01443  *  \brief Parse a <variable> node inside a <variablelist> node.
01444  *  \param node The variable node to parse.
01445  *  \param tabs A string to be appended at the begining of the output that will be stored
01446  *         in buffer.
01447  *  \param buffer This must be an already created ast_str. It will be used
01448  *         to store the result (if already has something it will be appended to the current
01449  *         string).
01450  *  \retval 0 if no data is appended.
01451  *  \retval 1 if data is appended.
01452  */
01453 static int xmldoc_parse_variable(struct ast_xml_node *node, const char *tabs, struct ast_str **buffer)
01454 {
01455    struct ast_xml_node *tmp;
01456    const char *valname;
01457    const char *tmptext;
01458    struct ast_str *cleanstr;
01459    int ret = 0, printedpara=0;
01460 
01461    for (tmp = ast_xml_node_get_children(node); tmp; tmp = ast_xml_node_get_next(tmp)) {
01462       if (xmldoc_parse_common_elements(tmp, (ret ? tabs : ""), "\n", buffer)) {
01463          printedpara = 1;
01464          continue;
01465       }
01466 
01467       if (strcasecmp(ast_xml_node_get_name(tmp), "value")) {
01468          continue;
01469       }
01470 
01471       /* Parse a <value> tag only. */
01472       if (!printedpara) {
01473          ast_str_append(buffer, 0, "\n");
01474          printedpara = 1;
01475       }
01476       /* Parse each <value name='valuename'>desciption</value> */
01477       valname = ast_xml_get_attribute(tmp, "name");
01478       if (valname) {
01479          ret = 1;
01480          ast_str_append(buffer, 0, "%s<value>%s</value>", tabs, valname);
01481          ast_xml_free_attr(valname);
01482       }
01483       tmptext = ast_xml_get_text(tmp);
01484       /* Check inside this node for any explanation about its meaning. */
01485       if (tmptext) {
01486          /* Cleanup text. */
01487          xmldoc_string_cleanup(tmptext, &cleanstr, 1);
01488          ast_xml_free_text(tmptext);
01489          if (cleanstr && ast_str_strlen(cleanstr) > 0) {
01490             ast_str_append(buffer, 0, ":%s", ast_str_buffer(cleanstr));
01491          }
01492          ast_free(cleanstr);
01493       }
01494       ast_str_append(buffer, 0, "\n");
01495    }
01496 
01497    return ret;
01498 }
01499 
01500 /*! \internal
01501  *  \brief Parse a <variablelist> node and put all the output inside 'buffer'.
01502  *  \param node The variablelist node pointer.
01503  *  \param tabs A string to be appended at the begining of the output that will be stored
01504  *         in buffer.
01505  *  \param buffer This must be an already created ast_str. It will be used
01506  *         to store the result (if already has something it will be appended to the current
01507  *         string).
01508  *  \retval 1 If a <variablelist> element is parsed.
01509  *  \retval 0 On error.
01510  */
01511 static int xmldoc_parse_variablelist(struct ast_xml_node *node, const char *tabs, struct ast_str **buffer)
01512 {
01513    struct ast_xml_node *tmp;
01514    const char *varname;
01515    char *vartabs;
01516    int ret = 0;
01517 
01518    if (!node || !ast_xml_node_get_children(node)) {
01519       return ret;
01520    }
01521 
01522    if (strcasecmp(ast_xml_node_get_name(node), "variablelist")) {
01523       return ret;
01524    }
01525 
01526    /* use this spacing (add 4 spaces) inside a variablelist node. */
01527    if (ast_asprintf(&vartabs, "%s    ", tabs) < 0) {
01528       return ret;
01529    }
01530    for (tmp = ast_xml_node_get_children(node); tmp; tmp = ast_xml_node_get_next(tmp)) {
01531       /* We can have a <para> element inside the variable list */
01532       if (xmldoc_parse_common_elements(tmp, (ret ? tabs : ""), "\n", buffer)) {
01533          ret = 1;
01534          continue;
01535       }
01536 
01537       if (!strcasecmp(ast_xml_node_get_name(tmp), "variable")) {
01538          /* Store the variable name in buffer. */
01539          varname = ast_xml_get_attribute(tmp, "name");
01540          if (varname) {
01541             ast_str_append(buffer, 0, "%s<variable>%s</variable>: ", tabs, varname);
01542             ast_xml_free_attr(varname);
01543             /* Parse the <variable> possible values. */
01544             xmldoc_parse_variable(tmp, vartabs, buffer);
01545             ret = 1;
01546          }
01547       }
01548    }
01549 
01550    ast_free(vartabs);
01551 
01552    return ret;
01553 }
01554 
01555 /*!
01556  * \internal
01557  * \brief Build seealso information for an item
01558  * \param node The seealso node to parse
01559  *
01560  * \note This method exists for when you already have the node.  This
01561  * prevents having to lock the documentation tree twice
01562  *
01563  * \returns A malloc'd character pointer to the seealso information of the item
01564  * \returns NULL on failure
01565  *
01566  * \since 11
01567  */
01568 static char *_ast_xmldoc_build_seealso(struct ast_xml_node *node)
01569 {
01570    char *output;
01571    struct ast_str *outputstr;
01572    const char *typename;
01573    const char *content;
01574    int first = 1;
01575 
01576    /* Find the <see-also> node. */
01577    for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
01578       if (!strcasecmp(ast_xml_node_get_name(node), "see-also")) {
01579          break;
01580       }
01581    }
01582 
01583    if (!node || !ast_xml_node_get_children(node)) {
01584       /* we couldnt find a <see-also> node. */
01585       return NULL;
01586    }
01587 
01588    /* prepare the output string. */
01589    outputstr = ast_str_create(128);
01590    if (!outputstr) {
01591       return NULL;
01592    }
01593 
01594    /* get into the <see-also> node. */
01595    for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
01596       if (strcasecmp(ast_xml_node_get_name(node), "ref")) {
01597          continue;
01598       }
01599 
01600       /* parse the <ref> node. 'type' attribute is required. */
01601       typename = ast_xml_get_attribute(node, "type");
01602       if (!typename) {
01603          continue;
01604       }
01605       content = ast_xml_get_text(node);
01606       if (!content) {
01607          ast_xml_free_attr(typename);
01608          continue;
01609       }
01610       if (!strcasecmp(typename, "application")) {
01611          ast_str_append(&outputstr, 0, "%s%s()",   (first ? "" : ", "), content);
01612       } else if (!strcasecmp(typename, "function")) {
01613          ast_str_append(&outputstr, 0, "%s%s", (first ? "" : ", "), content);
01614       } else if (!strcasecmp(typename, "astcli")) {
01615          ast_str_append(&outputstr, 0, "%s<astcli>%s</astcli>", (first ? "" : ", "), content);
01616       } else {
01617          ast_str_append(&outputstr, 0, "%s%s", (first ? "" : ", "), content);
01618       }
01619       first = 0;
01620       ast_xml_free_text(content);
01621       ast_xml_free_attr(typename);
01622    }
01623 
01624    output = ast_strdup(ast_str_buffer(outputstr));
01625    ast_free(outputstr);
01626 
01627    return output;
01628 }
01629 
01630 char *ast_xmldoc_build_seealso(const char *type, const char *name, const char *module)
01631 {
01632    char *output;
01633    struct ast_xml_node *node;
01634 
01635    if (ast_strlen_zero(type) || ast_strlen_zero(name)) {
01636       return NULL;
01637    }
01638 
01639    /* get the application/function root node. */
01640    node = xmldoc_get_node(type, name, module, documentation_language);
01641    if (!node || !ast_xml_node_get_children(node)) {
01642       return NULL;
01643    }
01644 
01645    output = _ast_xmldoc_build_seealso(node);
01646 
01647    return output;
01648 }
01649 
01650 /*! \internal
01651  *  \brief Parse a <enum> node.
01652  *  \brief fixnode An ast_xml_node pointer to the <enum> node.
01653  *  \bried buffer The output buffer.
01654  *  \retval 0 if content is not found inside the enum element (data is not appended to buffer).
01655  *  \retval 1 if content is found and data is appended to buffer.
01656  */
01657 static int xmldoc_parse_enum(struct ast_xml_node *fixnode, const char *tabs, struct ast_str **buffer)
01658 {
01659    struct ast_xml_node *node = fixnode;
01660    int ret = 0;
01661    char *optiontabs;
01662 
01663    if (ast_asprintf(&optiontabs, "%s    ", tabs) < 0) {
01664       return ret;
01665    }
01666 
01667    for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
01668       if (xmldoc_parse_common_elements(node, (ret ? tabs : " - "), "\n", buffer)) {
01669          ret = 1;
01670       }
01671 
01672       xmldoc_parse_enumlist(node, optiontabs, buffer);
01673    }
01674 
01675    ast_free(optiontabs);
01676 
01677    return ret;
01678 }
01679 
01680 /*! \internal
01681  *  \brief Parse a <enumlist> node.
01682  *  \param fixnode As ast_xml pointer to the <enumlist> node.
01683  *  \param buffer The ast_str output buffer.
01684  *  \retval 0 if no <enumlist> node was parsed.
01685  *  \retval 1 if a <enumlist> node was parsed.
01686  */
01687 static int xmldoc_parse_enumlist(struct ast_xml_node *fixnode, const char *tabs, struct ast_str **buffer)
01688 {
01689    struct ast_xml_node *node = fixnode;
01690    const char *enumname;
01691    int ret = 0;
01692 
01693    for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
01694       if (strcasecmp(ast_xml_node_get_name(node), "enum")) {
01695          continue;
01696       }
01697 
01698       enumname = ast_xml_get_attribute(node, "name");
01699       if (enumname) {
01700          ast_str_append(buffer, 0, "%s<enum>%s</enum>", tabs, enumname);
01701          ast_xml_free_attr(enumname);
01702 
01703          /* parse only enum elements inside a enumlist node. */
01704          if ((xmldoc_parse_enum(node, tabs, buffer))) {
01705             ret = 1;
01706          } else {
01707             ast_str_append(buffer, 0, "\n");
01708          }
01709       }
01710    }
01711    return ret;
01712 }
01713 
01714 /*! \internal
01715  *  \brief Parse an <option> node.
01716  *  \param fixnode An ast_xml pointer to the <option> node.
01717  *  \param tabs A string to be appended at the begining of each line being added to the
01718  *              buffer string.
01719  *  \param buffer The output buffer.
01720  *  \retval 0 if no option node is parsed.
01721  *  \retval 1 if an option node is parsed.
01722  */
01723 static int xmldoc_parse_option(struct ast_xml_node *fixnode, const char *tabs, struct ast_str **buffer)
01724 {
01725    struct ast_xml_node *node;
01726    int ret = 0;
01727    char *optiontabs;
01728 
01729    if (ast_asprintf(&optiontabs, "%s    ", tabs) < 0) {
01730       return ret;
01731    }
01732    for (node = ast_xml_node_get_children(fixnode); node; node = ast_xml_node_get_next(node)) {
01733       if (!strcasecmp(ast_xml_node_get_name(node), "argument")) {
01734          /* if this is the first data appended to buffer, print a \n*/
01735          if (!ret && ast_xml_node_get_children(node)) {
01736             /* print \n */
01737             ast_str_append(buffer, 0, "\n");
01738          }
01739          if (xmldoc_parse_argument(node, 0, NULL, optiontabs, buffer)) {
01740             ret = 1;
01741          }
01742          continue;
01743       }
01744 
01745       if (xmldoc_parse_common_elements(node, (ret ? tabs :  ""), "\n", buffer)) {
01746          ret = 1;
01747       }
01748 
01749       xmldoc_parse_variablelist(node, optiontabs, buffer);
01750 
01751       xmldoc_parse_enumlist(node, optiontabs, buffer);
01752    }
01753    ast_free(optiontabs);
01754 
01755    return ret;
01756 }
01757 
01758 /*! \internal
01759  *  \brief Parse an <optionlist> element from the xml documentation.
01760  *  \param fixnode Pointer to the optionlist xml node.
01761  *  \param tabs A string to be appended at the begining of each line being added to the
01762  *              buffer string.
01763  *  \param buffer Output buffer to put what is inside the optionlist tag.
01764  */
01765 static void xmldoc_parse_optionlist(struct ast_xml_node *fixnode, const char *tabs, struct ast_str **buffer)
01766 {
01767    struct ast_xml_node *node;
01768    const char *optname, *hasparams;
01769    char *optionsyntax;
01770    int optparams;
01771 
01772    for (node = ast_xml_node_get_children(fixnode); node; node = ast_xml_node_get_next(node)) {
01773       /* Start appending every option tag. */
01774       if (strcasecmp(ast_xml_node_get_name(node), "option")) {
01775          continue;
01776       }
01777 
01778       /* Get the option name. */
01779       optname = ast_xml_get_attribute(node, "name");
01780       if (!optname) {
01781          continue;
01782       }
01783 
01784       optparams = 1;
01785       hasparams = ast_xml_get_attribute(node, "hasparams");
01786       if (hasparams && !strcasecmp(hasparams, "optional")) {
01787          optparams = 2;
01788       }
01789 
01790       optionsyntax = xmldoc_get_syntax_fun(node, optname, "argument", 0, optparams);
01791       if (!optionsyntax) {
01792          ast_xml_free_attr(optname);
01793          ast_xml_free_attr(hasparams);
01794          continue;
01795       }
01796 
01797       ast_str_append(buffer, 0, "%s%s: ", tabs, optionsyntax);
01798 
01799       if (!xmldoc_parse_option(node, tabs, buffer)) {
01800          ast_str_append(buffer, 0, "\n");
01801       }
01802       ast_str_append(buffer, 0, "\n");
01803       ast_xml_free_attr(optname);
01804       ast_xml_free_attr(hasparams);
01805       ast_free(optionsyntax);
01806    }
01807 }
01808 
01809 /*! \internal
01810  *  \brief Parse a 'parameter' tag inside a syntax element.
01811  *  \param fixnode A pointer to the 'parameter' xml node.
01812  *  \param tabs A string to be appended at the beginning of each line being printed inside
01813  *              'buffer'.
01814  *  \param buffer String buffer to put values found inside the parameter element.
01815  */
01816 static void xmldoc_parse_parameter(struct ast_xml_node *fixnode, const char *tabs, struct ast_str **buffer)
01817 {
01818    const char *paramname;
01819    struct ast_xml_node *node = fixnode;
01820    int hasarguments, printed = 0;
01821    char *internaltabs;
01822 
01823    if (strcasecmp(ast_xml_node_get_name(node), "parameter")) {
01824       return;
01825    }
01826 
01827    hasarguments = xmldoc_has_inside(node, "argument");
01828    if (!(paramname = ast_xml_get_attribute(node, "name"))) {
01829       /* parameter MUST have an attribute name. */
01830       return;
01831    }
01832 
01833    if (ast_asprintf(&internaltabs, "%s    ", tabs) < 0) {
01834       ast_xml_free_attr(paramname);
01835       return;
01836    }
01837 
01838    if (!hasarguments && xmldoc_has_nodes(node)) {
01839       ast_str_append(buffer, 0, "%s\n", paramname);
01840       ast_xml_free_attr(paramname);
01841       printed = 1;
01842    }
01843 
01844    for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
01845       if (!strcasecmp(ast_xml_node_get_name(node), "optionlist")) {
01846          xmldoc_parse_optionlist(node, internaltabs, buffer);
01847       } else if (!strcasecmp(ast_xml_node_get_name(node), "enumlist")) {
01848          xmldoc_parse_enumlist(node, internaltabs, buffer);
01849       } else if (!strcasecmp(ast_xml_node_get_name(node), "argument")) {
01850          xmldoc_parse_argument(node, 1, internaltabs, (!hasarguments ? "        " : ""), buffer);
01851       } else if (!strcasecmp(ast_xml_node_get_name(node), "para")) {
01852          if (!printed) {
01853             ast_str_append(buffer, 0, "%s\n", paramname);
01854             ast_xml_free_attr(paramname);
01855             printed = 1;
01856          }
01857          if (xmldoc_parse_para(node, internaltabs, "\n", buffer)) {
01858             /* If anything ever goes in below this condition before the continue below,
01859              * we should probably continue immediately. */
01860             continue;
01861          }
01862          continue;
01863       } else if (!strcasecmp(ast_xml_node_get_name(node), "info")) {
01864          if (!printed) {
01865             ast_str_append(buffer, 0, "%s\n", paramname);
01866             ast_xml_free_attr(paramname);
01867             printed = 1;
01868          }
01869          if (xmldoc_parse_info(node, internaltabs, "\n", buffer)) {
01870             /* If anything ever goes in below this condition before the continue below,
01871              * we should probably continue immediately. */
01872             continue;
01873          }
01874          continue;
01875       } else if ((xmldoc_parse_specialtags(node, internaltabs, "\n", buffer))) {
01876          continue;
01877       }
01878    }
01879    if (!printed) {
01880       ast_xml_free_attr(paramname);
01881    }
01882    ast_free(internaltabs);
01883 }
01884 
01885 /*!
01886  * \internal
01887  * \brief Build the arguments for an item
01888  * \param node The arguments node to parse
01889  *
01890  * \note This method exists for when you already have the node.  This
01891  * prevents having to lock the documentation tree twice
01892  *
01893  * \returns A malloc'd character pointer to the arguments for the item
01894  * \returns NULL on failure
01895  *
01896  * \since 11
01897  */
01898 static char *_ast_xmldoc_build_arguments(struct ast_xml_node *node)
01899 {
01900    char *retstr = NULL;
01901    struct ast_str *ret;
01902 
01903    ret = ast_str_create(128);
01904    if (!ret) {
01905       return NULL;
01906    }
01907 
01908    /* Find the syntax field. */
01909    for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
01910       if (!strcasecmp(ast_xml_node_get_name(node), "syntax")) {
01911          break;
01912       }
01913    }
01914 
01915    if (!node || !ast_xml_node_get_children(node)) {
01916       /* We couldn't find the syntax node. */
01917       ast_free(ret);
01918       return NULL;
01919    }
01920 
01921    for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
01922       xmldoc_parse_parameter(node, "", &ret);
01923    }
01924 
01925    if (ast_str_strlen(ret) > 0) {
01926       /* remove last '\n' */
01927       char *buf = ast_str_buffer(ret);
01928       if (buf[ast_str_strlen(ret) - 1] == '\n') {
01929          ast_str_truncate(ret, -1);
01930       }
01931       retstr = ast_strdup(ast_str_buffer(ret));
01932    }
01933    ast_free(ret);
01934 
01935    return retstr;
01936 }
01937 
01938 char *ast_xmldoc_build_arguments(const char *type, const char *name, const char *module)
01939 {
01940    struct ast_xml_node *node;
01941 
01942    if (ast_strlen_zero(type) || ast_strlen_zero(name)) {
01943       return NULL;
01944    }
01945 
01946    node = xmldoc_get_node(type, name, module, documentation_language);
01947 
01948    if (!node || !ast_xml_node_get_children(node)) {
01949       return NULL;
01950    }
01951 
01952    return _ast_xmldoc_build_arguments(node);
01953 }
01954 
01955 /*! \internal
01956  *  \brief Return the string within a node formatted with <para> and <variablelist> elements.
01957  *  \param node Parent node where content resides.
01958  *  \param raw If set, return the node's content without further processing.
01959  *  \param raw_wrap Wrap raw text.
01960  *  \retval NULL on error
01961  *  \retval Node content on success.
01962  */
01963 static struct ast_str *xmldoc_get_formatted(struct ast_xml_node *node, int raw_output, int raw_wrap)
01964 {
01965    struct ast_xml_node *tmp;
01966    const char *notcleanret, *tmpstr;
01967    struct ast_str *ret;
01968 
01969    if (raw_output) {
01970       /* xmldoc_string_cleanup will allocate the ret object */
01971       notcleanret = ast_xml_get_text(node);
01972       tmpstr = notcleanret;
01973       xmldoc_string_cleanup(ast_skip_blanks(notcleanret), &ret, 0);
01974       ast_xml_free_text(tmpstr);
01975    } else {
01976       ret = ast_str_create(128);
01977       for (tmp = ast_xml_node_get_children(node); tmp; tmp = ast_xml_node_get_next(tmp)) {
01978          /* if found, parse a <para> element. */
01979          if (xmldoc_parse_common_elements(tmp, "", "\n", &ret)) {
01980             continue;
01981          }
01982          /* if found, parse a <variablelist> element. */
01983          xmldoc_parse_variablelist(tmp, "", &ret);
01984          xmldoc_parse_enumlist(tmp, "    ", &ret);
01985       }
01986       /* remove last '\n' */
01987       /* XXX Don't modify ast_str internals manually */
01988       tmpstr = ast_str_buffer(ret);
01989       if (tmpstr[ast_str_strlen(ret) - 1] == '\n') {
01990          ast_str_truncate(ret, -1);
01991       }
01992    }
01993    return ret;
01994 }
01995 
01996 /*!
01997  *  \brief Get the content of a field (synopsis, description, etc) from an asterisk document tree node
01998  *  \param node The node to obtain the information from
01999  *  \param var Name of field to return (synopsis, description, etc).
02000  *  \param raw Field only contains text, no other elements inside it.
02001  *  \retval NULL On error.
02002  *  \retval Field text content on success.
02003  *  \since 11
02004  */
02005 static char *_xmldoc_build_field(struct ast_xml_node *node, const char *var, int raw)
02006 {
02007    char *ret = NULL;
02008    struct ast_str *formatted;
02009 
02010    node = ast_xml_find_element(ast_xml_node_get_children(node), var, NULL, NULL);
02011 
02012    if (!node || !ast_xml_node_get_children(node)) {
02013       return ret;
02014    }
02015 
02016    formatted = xmldoc_get_formatted(node, raw, raw);
02017    if (ast_str_strlen(formatted) > 0) {
02018       ret = ast_strdup(ast_str_buffer(formatted));
02019    }
02020    ast_free(formatted);
02021 
02022    return ret;
02023 }
02024 
02025 /*!
02026  *  \brief Get the content of a field (synopsis, description, etc) from an asterisk document tree
02027  *  \param type Type of element (application, function, ...).
02028  *  \param name Name of element (Dial, Echo, Playback, ...).
02029  *  \param var Name of field to return (synopsis, description, etc).
02030  *  \param raw Field only contains text, no other elements inside it.
02031  *  \retval NULL On error.
02032  *  \retval Field text content on success.
02033  */
02034 static char *xmldoc_build_field(const char *type, const char *name, const char *module, const char *var, int raw)
02035 {
02036    struct ast_xml_node *node;
02037 
02038    if (ast_strlen_zero(type) || ast_strlen_zero(name)) {
02039       ast_log(LOG_ERROR, "Tried to look in XML tree with faulty values.\n");
02040       return NULL;
02041    }
02042 
02043    node = xmldoc_get_node(type, name, module, documentation_language);
02044 
02045    if (!node) {
02046       ast_log(LOG_WARNING, "Couldn't find %s %s in XML documentation\n", type, name);
02047       return NULL;
02048    }
02049 
02050    return _xmldoc_build_field(node, var, raw);
02051 }
02052 
02053 /* \internal
02054  * \brief Build the synopsis for an item
02055  * \param node The synopsis node
02056  *
02057  * \note This method exists for when you already have the node.  This
02058  * prevents having to lock the documentation tree twice
02059  *
02060  * \returns A malloc'd character pointer to the synopsis information
02061  * \returns NULL on failure
02062  * \since 11
02063  */
02064 static char *_ast_xmldoc_build_synopsis(struct ast_xml_node *node)
02065 {
02066    return _xmldoc_build_field(node, "synopsis", 1);
02067 }
02068 
02069 char *ast_xmldoc_build_synopsis(const char *type, const char *name, const char *module)
02070 {
02071    return xmldoc_build_field(type, name, module, "synopsis", 1);
02072 }
02073 
02074 /*!
02075  * \internal
02076  * \brief Build the descripton for an item
02077  * \param node The description node to parse
02078  *
02079  * \note This method exists for when you already have the node.  This
02080  * prevents having to lock the documentation tree twice
02081  *
02082  * \returns A malloc'd character pointer to the arguments for the item
02083  * \returns NULL on failure
02084  * \since 11
02085  */
02086 static char *_ast_xmldoc_build_description(struct ast_xml_node *node)
02087 {
02088    return _xmldoc_build_field(node, "description", 0);
02089 }
02090 
02091 char *ast_xmldoc_build_description(const char *type, const char *name, const char *module)
02092 {
02093    return xmldoc_build_field(type, name, module, "description", 0);
02094 }
02095 
02096 /*! \internal \brief ast_xml_doc_item ao2 destructor
02097  * \since 11
02098  */
02099 static void ast_xml_doc_item_destructor(void *obj)
02100 {
02101    struct ast_xml_doc_item *doc = obj;
02102 
02103    if (!doc) {
02104       return;
02105    }
02106 
02107    ast_free(doc->syntax);
02108    ast_free(doc->seealso);
02109    ast_free(doc->arguments);
02110    ast_free(doc->synopsis);
02111    ast_free(doc->description);
02112    ast_string_field_free_memory(doc);
02113 
02114    if (doc->next) {
02115       ao2_ref(doc->next, -1);
02116       doc->next = NULL;
02117    }
02118 }
02119 
02120 /*! \internal
02121  * \brief Create an ao2 ref counted ast_xml_doc_item
02122  * \param name The name of the item
02123  * \param type The item's source type
02124  * \since 11
02125  */
02126 static struct ast_xml_doc_item *ast_xml_doc_item_alloc(const char *name, const char *type)
02127 {
02128    struct ast_xml_doc_item *item;
02129 
02130    if (!(item = ao2_alloc(sizeof(*item), ast_xml_doc_item_destructor))) {
02131       ast_log(AST_LOG_ERROR, "Failed to allocate memory for ast_xml_doc_item instance\n");
02132       return NULL;
02133    }
02134 
02135    if (   !(item->syntax = ast_str_create(128))
02136       || !(item->seealso = ast_str_create(128))
02137       || !(item->arguments = ast_str_create(128))
02138       || !(item->synopsis = ast_str_create(128))
02139       || !(item->description = ast_str_create(128))) {
02140       ast_log(AST_LOG_ERROR, "Failed to allocate strings for ast_xml_doc_item instance\n");
02141       goto ast_xml_doc_item_failure;
02142    }
02143 
02144    if (ast_string_field_init(item, 64)) {
02145       ast_log(AST_LOG_ERROR, "Failed to initialize string field for ast_xml_doc_item instance\n");
02146       goto ast_xml_doc_item_failure;
02147    }
02148    ast_string_field_set(item, name, name);
02149    ast_string_field_set(item, type, type);
02150 
02151    return item;
02152 
02153 ast_xml_doc_item_failure:
02154    ao2_ref(item, -1);
02155    return NULL;
02156 }
02157 
02158 /*! \internal
02159  * \brief ao2 item hash function for ast_xml_doc_item
02160  * \since 11
02161  */
02162 static int ast_xml_doc_item_hash(const void *obj, const int flags)
02163 {
02164    const struct ast_xml_doc_item *item = obj;
02165    const char *name = (flags & OBJ_KEY) ? obj : item->name;
02166    return ast_str_case_hash(name);
02167 }
02168 
02169 /*! \internal
02170  * \brief ao2 item comparison function for ast_xml_doc_item
02171  * \since 11
02172  */
02173 static int ast_xml_doc_item_cmp(void *obj, void *arg, int flags)
02174 {
02175    struct ast_xml_doc_item *left = obj;
02176    struct ast_xml_doc_item *right = arg;
02177    const char *match = (flags & OBJ_KEY) ? arg : right->name;
02178    return strcasecmp(left->name, match) ? 0 : (CMP_MATCH | CMP_STOP);
02179 }
02180 
02181 /* \internal
02182  * \brief Build an XML documentation item
02183  * \param node The root node for the item
02184  * \param name The name of the item
02185  * \param type The item's source type
02186  *
02187  * \returns NULL on failure
02188  * \returns An ao2 ref counted object
02189  * \since 11
02190  */
02191 static struct ast_xml_doc_item *xmldoc_build_documentation_item(struct ast_xml_node *node, const char *name, const char *type)
02192 {
02193    struct ast_xml_doc_item *item;
02194    char *syntax;
02195    char *seealso;
02196    char *arguments;
02197    char *synopsis;
02198    char *description;
02199 
02200    if (!(item = ast_xml_doc_item_alloc(name, type))) {
02201       return NULL;
02202    }
02203 
02204    syntax = _ast_xmldoc_build_syntax(node, type, name);
02205    seealso = _ast_xmldoc_build_seealso(node);
02206    arguments = _ast_xmldoc_build_arguments(node);
02207    synopsis = _ast_xmldoc_build_synopsis(node);
02208    description = _ast_xmldoc_build_description(node);
02209 
02210    if (syntax) {
02211       ast_str_set(&item->syntax, 0, "%s", syntax);
02212    }
02213    if (seealso) {
02214       ast_str_set(&item->seealso, 0, "%s", seealso);
02215    }
02216    if (arguments) {
02217       ast_str_set(&item->arguments, 0, "%s", arguments);
02218    }
02219    if (synopsis) {
02220       ast_str_set(&item->synopsis, 0, "%s", synopsis);
02221    }
02222    if (description) {
02223       ast_str_set(&item->description, 0, "%s", description);
02224    }
02225 
02226    ast_free(syntax);
02227    ast_free(seealso);
02228    ast_free(arguments);
02229    ast_free(synopsis);
02230    ast_free(description);
02231 
02232    return item;
02233 }
02234 
02235 struct ao2_container *ast_xmldoc_build_documentation(const char *type)
02236 {
02237    struct ao2_container *docs;
02238    struct ast_xml_doc_item *item = NULL, *root = NULL;
02239    struct ast_xml_node *node = NULL, *instance = NULL;
02240    struct documentation_tree *doctree;
02241    const char *name;
02242 
02243    if (!(docs = ao2_container_alloc(127, ast_xml_doc_item_hash, ast_xml_doc_item_cmp))) {
02244       ast_log(AST_LOG_ERROR, "Failed to create container for xml document item instances\n");
02245       return NULL;
02246    }
02247 
02248    AST_RWLIST_RDLOCK(&xmldoc_tree);
02249    AST_LIST_TRAVERSE(&xmldoc_tree, doctree, entry) {
02250       /* the core xml documents have priority over thirdparty document. */
02251       node = ast_xml_get_root(doctree->doc);
02252       if (!node) {
02253          break;
02254       }
02255 
02256       for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
02257          /* Ignore empty nodes or nodes that aren't of the type requested */
02258          if (!ast_xml_node_get_children(node) || strcasecmp(ast_xml_node_get_name(node), type)) {
02259             continue;
02260          }
02261          name = ast_xml_get_attribute(node, "name");
02262          if (!name) {
02263             continue;
02264          }
02265 
02266          switch (xmldoc_get_syntax_type(type)) {
02267          case MANAGER_EVENT_SYNTAX:
02268             for (instance = ast_xml_node_get_children(node); instance; instance = ast_xml_node_get_next(instance)) {
02269                struct ast_xml_doc_item *temp;
02270                if (!ast_xml_node_get_children(instance) || strcasecmp(ast_xml_node_get_name(instance), "managerEventInstance")) {
02271                   continue;
02272                }
02273                temp = xmldoc_build_documentation_item(instance, name, type);
02274                if (!temp) {
02275                   break;
02276                }
02277                if (!item) {
02278                   item = temp;
02279                   root = item;
02280                } else {
02281                   item->next = temp;
02282                   item = temp;
02283                }
02284             }
02285             item = root;
02286             break;
02287          default:
02288             item = xmldoc_build_documentation_item(node, name, type);
02289          }
02290          ast_xml_free_attr(name);
02291 
02292          if (item) {
02293             ao2_link(docs, item);
02294             ao2_t_ref(item, -1, "Dispose of creation ref");
02295             item = NULL;
02296          }
02297       }
02298    }
02299    AST_RWLIST_UNLOCK(&xmldoc_tree);
02300 
02301    return docs;
02302 }
02303 
02304 #if !defined(HAVE_GLOB_NOMAGIC) || !defined(HAVE_GLOB_BRACE) || defined(DEBUG_NONGNU)
02305 static int xml_pathmatch(char *xmlpattern, int xmlpattern_maxlen, glob_t *globbuf)
02306 {
02307    int globret;
02308 
02309    snprintf(xmlpattern, xmlpattern_maxlen, "%s/documentation/11/thirdparty/*-%s.xml",
02310       ast_config_AST_DATA_DIR, documentation_language);
02311    if((globret = glob(xmlpattern, GLOB_NOCHECK, NULL, globbuf))) {
02312       return globret;
02313    }
02314 
02315    snprintf(xmlpattern, xmlpattern_maxlen, "%s/documentation/11/thirdparty/*-%.2s_??.xml",
02316       ast_config_AST_DATA_DIR, documentation_language);
02317    if((globret = glob(xmlpattern, GLOB_APPEND | GLOB_NOCHECK, NULL, globbuf))) {
02318       return globret;
02319    }
02320 
02321    snprintf(xmlpattern, xmlpattern_maxlen, "%s/documentation/11/thirdparty/*-%s.xml",
02322       ast_config_AST_DATA_DIR, default_documentation_language);
02323    if((globret = glob(xmlpattern, GLOB_APPEND | GLOB_NOCHECK, NULL, globbuf))) {
02324       return globret;
02325    }
02326 
02327    snprintf(xmlpattern, xmlpattern_maxlen, "%s/documentation/11/*-%s.xml",
02328       ast_config_AST_DATA_DIR, documentation_language);
02329    if((globret = glob(xmlpattern, GLOB_APPEND | GLOB_NOCHECK, NULL, globbuf))) {
02330       return globret;
02331    }
02332 
02333    snprintf(xmlpattern, xmlpattern_maxlen, "%s/documentation/11/*-%.2s_??.xml",
02334       ast_config_AST_DATA_DIR, documentation_language);
02335    if((globret = glob(xmlpattern, GLOB_APPEND | GLOB_NOCHECK, NULL, globbuf))) {
02336       return globret;
02337    }
02338 
02339    snprintf(xmlpattern, xmlpattern_maxlen, "%s/documentation/11/*-%s.xml",
02340       ast_config_AST_DATA_DIR, default_documentation_language);
02341    globret = glob(xmlpattern, GLOB_APPEND | GLOB_NOCHECK, NULL, globbuf);
02342 
02343    return globret;
02344 }
02345 #endif
02346 
02347 /*! \brief Close and unload XML documentation. */
02348 static void xmldoc_unload_documentation(void)
02349 {
02350    struct documentation_tree *doctree;
02351 
02352    AST_RWLIST_WRLOCK(&xmldoc_tree);
02353    while ((doctree = AST_RWLIST_REMOVE_HEAD(&xmldoc_tree, entry))) {
02354       ast_free(doctree->filename);
02355       ast_xml_close(doctree->doc);
02356       ast_free(doctree);
02357    }
02358    AST_RWLIST_UNLOCK(&xmldoc_tree);
02359 
02360    ast_xml_finish();
02361 }
02362 
02363 int ast_xmldoc_load_documentation(void)
02364 {
02365    struct ast_xml_node *root_node;
02366    struct ast_xml_doc *tmpdoc;
02367    struct documentation_tree *doc_tree;
02368    char *xmlpattern;
02369    struct ast_config *cfg = NULL;
02370    struct ast_variable *var = NULL;
02371    struct ast_flags cnfflags = { 0 };
02372    int globret, i, dup, duplicate;
02373    glob_t globbuf;
02374 #if !defined(HAVE_GLOB_NOMAGIC) || !defined(HAVE_GLOB_BRACE) || defined(DEBUG_NONGNU)
02375    int xmlpattern_maxlen;
02376 #endif
02377 
02378    /* setup default XML documentation language */
02379    snprintf(documentation_language, sizeof(documentation_language), default_documentation_language);
02380 
02381    if ((cfg = ast_config_load2("asterisk.conf", "" /* core can't reload */, cnfflags)) && cfg != CONFIG_STATUS_FILEINVALID) {
02382       for (var = ast_variable_browse(cfg, "options"); var; var = var->next) {
02383          if (!strcasecmp(var->name, "documentation_language")) {
02384             if (!ast_strlen_zero(var->value)) {
02385                snprintf(documentation_language, sizeof(documentation_language), "%s", var->value);
02386             }
02387          }
02388       }
02389       ast_config_destroy(cfg);
02390    }
02391 
02392    /* initialize the XML library. */
02393    ast_xml_init();
02394 
02395    /* register function to be run when asterisk finish. */
02396    ast_register_atexit(xmldoc_unload_documentation);
02397 
02398    globbuf.gl_offs = 0;    /* slots to reserve in gl_pathv */
02399 
02400 #if !defined(HAVE_GLOB_NOMAGIC) || !defined(HAVE_GLOB_BRACE) || defined(DEBUG_NONGNU)
02401    xmlpattern_maxlen = strlen(ast_config_AST_DATA_DIR) + strlen("/documentation/11/thirdparty") + strlen("/*-??_??.xml") + 1;
02402    xmlpattern = ast_malloc(xmlpattern_maxlen);
02403    globret = xml_pathmatch(xmlpattern, xmlpattern_maxlen, &globbuf);
02404 #else
02405    /* Get every *-LANG.xml file inside $(ASTDATADIR)/documentation */
02406    if (ast_asprintf(&xmlpattern, "%s/documentation/11{/thirdparty/,/}*-{%s,%.2s_??,%s}.xml", ast_config_AST_DATA_DIR,
02407       documentation_language, documentation_language, default_documentation_language) < 0) {
02408       return 1;
02409    }
02410    globret = glob(xmlpattern, MY_GLOB_FLAGS, NULL, &globbuf);
02411 #endif
02412 
02413    ast_debug(3, "gl_pathc %zd\n", globbuf.gl_pathc);
02414    if (globret == GLOB_NOSPACE) {
02415       ast_log(LOG_WARNING, "XML load failure, glob expansion of pattern '%s' failed: Not enough memory\n", xmlpattern);
02416       ast_free(xmlpattern);
02417       return 1;
02418    } else if (globret  == GLOB_ABORTED) {
02419       ast_log(LOG_WARNING, "XML load failure, glob expansion of pattern '%s' failed: Read error\n", xmlpattern);
02420       ast_free(xmlpattern);
02421       return 1;
02422    }
02423    ast_free(xmlpattern);
02424 
02425    AST_RWLIST_WRLOCK(&xmldoc_tree);
02426    /* loop over expanded files */
02427    for (i = 0; i < globbuf.gl_pathc; i++) {
02428       /* check for duplicates (if we already [try to] open the same file. */
02429       duplicate = 0;
02430       for (dup = 0; dup < i; dup++) {
02431          if (!strcmp(globbuf.gl_pathv[i], globbuf.gl_pathv[dup])) {
02432             duplicate = 1;
02433             break;
02434          }
02435       }
02436       if (duplicate || strchr(globbuf.gl_pathv[i], '*')) {
02437       /* skip duplicates as well as pathnames not found
02438        * (due to use of GLOB_NOCHECK in xml_pathmatch) */
02439          continue;
02440       }
02441       tmpdoc = NULL;
02442       tmpdoc = ast_xml_open(globbuf.gl_pathv[i]);
02443       if (!tmpdoc) {
02444          ast_log(LOG_ERROR, "Could not open XML documentation at '%s'\n", globbuf.gl_pathv[i]);
02445          continue;
02446       }
02447       /* Get doc root node and check if it starts with '<docs>' */
02448       root_node = ast_xml_get_root(tmpdoc);
02449       if (!root_node) {
02450          ast_log(LOG_ERROR, "Error getting documentation root node\n");
02451          ast_xml_close(tmpdoc);
02452          continue;
02453       }
02454       /* Check root node name for malformed xmls. */
02455       if (strcmp(ast_xml_node_get_name(root_node), "docs")) {
02456          ast_log(LOG_ERROR, "Documentation file is not well formed!\n");
02457          ast_xml_close(tmpdoc);
02458          continue;
02459       }
02460       doc_tree = ast_calloc(1, sizeof(*doc_tree));
02461       if (!doc_tree) {
02462          ast_log(LOG_ERROR, "Unable to allocate documentation_tree structure!\n");
02463          ast_xml_close(tmpdoc);
02464          continue;
02465       }
02466       doc_tree->doc = tmpdoc;
02467       doc_tree->filename = ast_strdup(globbuf.gl_pathv[i]);
02468       AST_RWLIST_INSERT_TAIL(&xmldoc_tree, doc_tree, entry);
02469    }
02470    AST_RWLIST_UNLOCK(&xmldoc_tree);
02471 
02472    globfree(&globbuf);
02473 
02474    return 0;
02475 }
02476 
02477 #endif /* AST_XML_DOCS */
02478 
02479