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

Generated on Mon Mar 19 11:30:31 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7