Mon Oct 8 12:39:06 2012

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

Generated on Mon Oct 8 12:39:06 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7