Wed Apr 6 11:29:48 2011

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: 300521 $")
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 Get the application/function node for 'name' application/function with language 'language'
00460  *         if we don't find any, get the first application with 'name' no matter which language with.
00461  *  \param type 'application', 'function', ...
00462  *  \param name Application or Function name.
00463  *  \param language Try to get this language (if not found try with en_US)
00464  *  \retval NULL on error.
00465  *  \retval A node of type ast_xml_node.
00466  */
00467 static struct ast_xml_node *xmldoc_get_node(const char *type, const char *name, const char *language)
00468 {
00469    struct ast_xml_node *node = NULL;
00470    struct documentation_tree *doctree;
00471    const char *lang;
00472 
00473    AST_RWLIST_RDLOCK(&xmldoc_tree);
00474    AST_LIST_TRAVERSE(&xmldoc_tree, doctree, entry) {
00475       /* the core xml documents have priority over thirdparty document. */
00476       node = ast_xml_get_root(doctree->doc);
00477       while ((node = ast_xml_find_element(node, type, "name", name))) {
00478          /* Check language */
00479          lang = ast_xml_get_attribute(node, "language");
00480          if (lang && !strcmp(lang, language)) {
00481             ast_xml_free_attr(lang);
00482             break;
00483          } else if (lang) {
00484             ast_xml_free_attr(lang);
00485          }
00486       }
00487 
00488       if (node && ast_xml_node_get_children(node)) {
00489          break;
00490       }
00491 
00492       /* We didn't find the application documentation for the specified language,
00493       so, try to load documentation for any language */
00494       node = ast_xml_get_root(doctree->doc);
00495       if (ast_xml_node_get_children(node)) {
00496          if ((node = ast_xml_find_element(ast_xml_node_get_children(node), type, "name", name))) {
00497             break;
00498          }
00499       }
00500    }
00501    AST_RWLIST_UNLOCK(&xmldoc_tree);
00502 
00503    return node;
00504 }
00505 
00506 /*! \internal
00507  *  \brief Helper function used to build the syntax, it allocates the needed buffer (or reallocates it),
00508  *         and based on the reverse value it makes use of fmt to print the parameter list inside the
00509  *         realloced buffer (syntax).
00510  *  \param reverse We are going backwards while generating the syntax?
00511  *  \param len Current length of 'syntax' buffer.
00512  *  \param syntax Output buffer for the concatenated values.
00513  *  \param fmt A format string that will be used in a sprintf call.
00514  */
00515 static void __attribute__((format(printf, 4, 5))) xmldoc_reverse_helper(int reverse, int *len, char **syntax, const char *fmt, ...)
00516 {
00517    int totlen, tmpfmtlen;
00518    char *tmpfmt, tmp;
00519    va_list ap;
00520 
00521    va_start(ap, fmt);
00522    if (ast_vasprintf(&tmpfmt, fmt, ap) < 0) {
00523       va_end(ap);
00524       return;
00525    }
00526    va_end(ap);
00527 
00528    tmpfmtlen = strlen(tmpfmt);
00529    totlen = *len + tmpfmtlen + 1;
00530 
00531    *syntax = ast_realloc(*syntax, totlen);
00532 
00533    if (!*syntax) {
00534       ast_free(tmpfmt);
00535       return;
00536    }
00537 
00538    if (reverse) {
00539       memmove(*syntax + tmpfmtlen, *syntax, *len);
00540       /* Save this char, it will be overwritten by the \0 of strcpy. */
00541       tmp = (*syntax)[0];
00542       strcpy(*syntax, tmpfmt);
00543       /* Restore the already saved char. */
00544       (*syntax)[tmpfmtlen] = tmp;
00545       (*syntax)[totlen - 1] = '\0';
00546    } else {
00547       strcpy(*syntax + *len, tmpfmt);
00548    }
00549 
00550    *len = totlen - 1;
00551    ast_free(tmpfmt);
00552 }
00553 
00554 /*! \internal
00555  *  \brief Check if the passed node has 'what' tags inside it.
00556  *  \param node Root node to search 'what' elements.
00557  *  \param what node name to search inside node.
00558  *  \retval 1 If a 'what' element is found inside 'node'.
00559  *  \retval 0 If no 'what' is found inside 'node'.
00560  */
00561 static int xmldoc_has_inside(struct ast_xml_node *fixnode, const char *what)
00562 {
00563    struct ast_xml_node *node = fixnode;
00564 
00565    for (node = ast_xml_node_get_children(fixnode); node; node = ast_xml_node_get_next(node)) {
00566       if (!strcasecmp(ast_xml_node_get_name(node), what)) {
00567          return 1;
00568       }
00569    }
00570    return 0;
00571 }
00572 
00573 /*! \internal
00574  *  \brief Check if the passed node has at least one node inside it.
00575  *  \param node Root node to search node elements.
00576  *  \retval 1 If a node element is found inside 'node'.
00577  *  \retval 0 If no node is found inside 'node'.
00578  */
00579 static int xmldoc_has_nodes(struct ast_xml_node *fixnode)
00580 {
00581    struct ast_xml_node *node = fixnode;
00582 
00583    for (node = ast_xml_node_get_children(fixnode); node; node = ast_xml_node_get_next(node)) {
00584       if (strcasecmp(ast_xml_node_get_name(node), "text")) {
00585          return 1;
00586       }
00587    }
00588    return 0;
00589 }
00590 
00591 /*! \internal
00592  *  \brief Check if the passed node has at least one specialtag.
00593  *  \param node Root node to search "specialtags" elements.
00594  *  \retval 1 If a "specialtag" element is found inside 'node'.
00595  *  \retval 0 If no "specialtag" is found inside 'node'.
00596  */
00597 static int xmldoc_has_specialtags(struct ast_xml_node *fixnode)
00598 {
00599    struct ast_xml_node *node = fixnode;
00600    int i;
00601 
00602    for (node = ast_xml_node_get_children(fixnode); node; node = ast_xml_node_get_next(node)) {
00603       for (i = 0; i < ARRAY_LEN(special_tags); i++) {
00604          if (!strcasecmp(ast_xml_node_get_name(node), special_tags[i].tagname)) {
00605             return 1;
00606          }
00607       }
00608    }
00609    return 0;
00610 }
00611 
00612 /*! \internal
00613  *  \brief Build the syntax for a specified starting node.
00614  *  \param rootnode A pointer to the ast_xml root node.
00615  *  \param rootname Name of the application, function, option, etc. to build the syntax.
00616  *  \param childname The name of each parameter node.
00617  *  \param printparenthesis Boolean if we must print parenthesis if not parameters are found in the rootnode.
00618  *  \param printrootname Boolean if we must print the rootname before the syntax and parenthesis at the begining/end.
00619  *  \retval NULL on error.
00620  *  \retval An ast_malloc'ed string with the syntax generated.
00621  */
00622 static char *xmldoc_get_syntax_fun(struct ast_xml_node *rootnode, const char *rootname, const char *childname, int printparenthesis, int printrootname)
00623 {
00624 #define GOTONEXT(__rev, __a) (__rev ? ast_xml_node_get_prev(__a) : ast_xml_node_get_next(__a))
00625 #define ISLAST(__rev, __a)  (__rev == 1 ? (ast_xml_node_get_prev(__a) ? 0 : 1) : (ast_xml_node_get_next(__a) ? 0 : 1))
00626 #define MP(__a) ((multiple ? __a : ""))
00627    struct ast_xml_node *node = NULL, *firstparam = NULL, *lastparam = NULL;
00628    const char *paramtype, *multipletype, *paramnameattr, *attrargsep, *parenthesis, *argname;
00629    int reverse, required, paramcount = 0, openbrackets = 0, len = 0, hasparams=0;
00630    int reqfinode = 0, reqlanode = 0, optmidnode = 0, prnparenthesis, multiple;
00631    char *syntax = NULL, *argsep, *paramname;
00632 
00633    if (ast_strlen_zero(rootname) || ast_strlen_zero(childname)) {
00634       ast_log(LOG_WARNING, "Tried to look in XML tree with faulty rootname or childname while creating a syntax.\n");
00635       return NULL;
00636    }
00637 
00638    if (!rootnode || !ast_xml_node_get_children(rootnode)) {
00639       /* If the rootnode field is not found, at least print name. */
00640       ast_asprintf(&syntax, "%s%s", (printrootname ? rootname : ""), (printparenthesis ? "()" : ""));
00641       return syntax;
00642    }
00643 
00644    /* Get the argument separator from the root node attribute name 'argsep', if not found
00645    defaults to ','. */
00646    attrargsep = ast_xml_get_attribute(rootnode, "argsep");
00647    if (attrargsep) {
00648       argsep = ast_strdupa(attrargsep);
00649       ast_xml_free_attr(attrargsep);
00650    } else {
00651       argsep = ast_strdupa(",");
00652    }
00653 
00654    /* Get order of evaluation. */
00655    for (node = ast_xml_node_get_children(rootnode); node; node = ast_xml_node_get_next(node)) {
00656       if (strcasecmp(ast_xml_node_get_name(node), childname)) {
00657          continue;
00658       }
00659       required = 0;
00660       hasparams = 1;
00661       if ((paramtype = ast_xml_get_attribute(node, "required"))) {
00662          if (ast_true(paramtype)) {
00663             required = 1;
00664          }
00665          ast_xml_free_attr(paramtype);
00666       }
00667 
00668       lastparam = node;
00669       reqlanode = required;
00670 
00671       if (!firstparam) {
00672          /* first parameter node */
00673          firstparam = node;
00674          reqfinode = required;
00675       }
00676    }
00677 
00678    if (!hasparams) {
00679       /* This application, function, option, etc, doesn't have any params. */
00680       ast_asprintf(&syntax, "%s%s", (printrootname ? rootname : ""), (printparenthesis ? "()" : ""));
00681       return syntax;
00682    }
00683 
00684    if (reqfinode && reqlanode) {
00685       /* check midnode */
00686       for (node = ast_xml_node_get_children(rootnode); node; node = ast_xml_node_get_next(node)) {
00687          if (strcasecmp(ast_xml_node_get_name(node), childname)) {
00688             continue;
00689          }
00690          if (node != firstparam && node != lastparam) {
00691             if ((paramtype = ast_xml_get_attribute(node, "required"))) {
00692                if (!ast_true(paramtype)) {
00693                   optmidnode = 1;
00694                   break;
00695                }
00696                ast_xml_free_attr(paramtype);
00697             }
00698          }
00699       }
00700    }
00701 
00702    if ((!reqfinode && reqlanode) || (reqfinode && reqlanode && optmidnode)) {
00703       reverse = 1;
00704       node = lastparam;
00705    } else {
00706       reverse = 0;
00707       node = firstparam;
00708    }
00709 
00710    /* init syntax string. */
00711    if (reverse) {
00712       xmldoc_reverse_helper(reverse, &len, &syntax,
00713          (printrootname ? (printrootname == 2 ? ")]" : ")"): ""));
00714    } else {
00715       xmldoc_reverse_helper(reverse, &len, &syntax, "%s%s", (printrootname ? rootname : ""),
00716          (printrootname ? (printrootname == 2 ? "[(" : "(") : ""));
00717    }
00718 
00719    for (; node; node = GOTONEXT(reverse, node)) {
00720       if (strcasecmp(ast_xml_node_get_name(node), childname)) {
00721          continue;
00722       }
00723 
00724       /* Get the argument name, if it is not the leaf, go inside that parameter. */
00725       if (xmldoc_has_inside(node, "argument")) {
00726          parenthesis = ast_xml_get_attribute(node, "hasparams");
00727          prnparenthesis = 0;
00728          if (parenthesis) {
00729             prnparenthesis = ast_true(parenthesis);
00730             if (!strcasecmp(parenthesis, "optional")) {
00731                prnparenthesis = 2;
00732             }
00733             ast_xml_free_attr(parenthesis);
00734          }
00735          argname = ast_xml_get_attribute(node, "name");
00736          if (argname) {
00737             paramname = xmldoc_get_syntax_fun(node, argname, "argument", prnparenthesis, prnparenthesis);
00738             ast_xml_free_attr(argname);
00739          } else {
00740             /* Malformed XML, print **UNKOWN** */
00741             paramname = ast_strdup("**unknown**");
00742          }
00743       } else {
00744          paramnameattr = ast_xml_get_attribute(node, "name");
00745          if (!paramnameattr) {
00746             ast_log(LOG_WARNING, "Malformed XML %s: no %s name\n", rootname, childname);
00747             if (syntax) {
00748                /* Free already allocated syntax */
00749                ast_free(syntax);
00750             }
00751             /* to give up is ok? */
00752             ast_asprintf(&syntax, "%s%s", (printrootname ? rootname : ""), (printparenthesis ? "()" : ""));
00753             return syntax;
00754          }
00755          paramname = ast_strdup(paramnameattr);
00756          ast_xml_free_attr(paramnameattr);
00757       }
00758 
00759       /* Defaults to 'false'. */
00760       multiple = 0;
00761       if ((multipletype = ast_xml_get_attribute(node, "multiple"))) {
00762          if (ast_true(multipletype)) {
00763             multiple = 1;
00764          }
00765          ast_xml_free_attr(multipletype);
00766       }
00767 
00768       required = 0;  /* Defaults to 'false'. */
00769       if ((paramtype = ast_xml_get_attribute(node, "required"))) {
00770          if (ast_true(paramtype)) {
00771             required = 1;
00772          }
00773          ast_xml_free_attr(paramtype);
00774       }
00775 
00776       /* build syntax core. */
00777 
00778       if (required) {
00779          /* First parameter */
00780          if (!paramcount) {
00781             xmldoc_reverse_helper(reverse, &len, &syntax, "%s%s%s%s", paramname, MP("["), MP(argsep), MP("...]"));
00782          } else {
00783             /* Time to close open brackets. */
00784             while (openbrackets > 0) {
00785                xmldoc_reverse_helper(reverse, &len, &syntax, (reverse ? "[" : "]"));
00786                openbrackets--;
00787             }
00788             if (reverse) {
00789                xmldoc_reverse_helper(reverse, &len, &syntax, "%s%s", paramname, argsep);
00790             } else {
00791                xmldoc_reverse_helper(reverse, &len, &syntax, "%s%s", argsep, paramname);
00792             }
00793             xmldoc_reverse_helper(reverse, &len, &syntax, "%s%s%s", MP("["), MP(argsep), MP("...]"));
00794          }
00795       } else {
00796          /* First parameter */
00797          if (!paramcount) {
00798             xmldoc_reverse_helper(reverse, &len, &syntax, "[%s%s%s%s]", paramname, MP("["), MP(argsep), MP("...]"));
00799          } else {
00800             if (ISLAST(reverse, node)) {
00801                /* This is the last parameter. */
00802                if (reverse) {
00803                   xmldoc_reverse_helper(reverse, &len, &syntax, "[%s%s%s%s]%s", paramname,
00804                            MP("["), MP(argsep), MP("...]"), argsep);
00805                } else {
00806                   xmldoc_reverse_helper(reverse, &len, &syntax, "%s[%s%s%s%s]", argsep, paramname,
00807                            MP("["), MP(argsep), MP("...]"));
00808                }
00809             } else {
00810                if (reverse) {
00811                   xmldoc_reverse_helper(reverse, &len, &syntax, "%s%s%s%s%s]", paramname, argsep,
00812                            MP("["), MP(argsep), MP("...]"));
00813                } else {
00814                   xmldoc_reverse_helper(reverse, &len, &syntax, "[%s%s%s%s%s", argsep, paramname,
00815                            MP("["), MP(argsep), MP("...]"));
00816                }
00817                openbrackets++;
00818             }
00819          }
00820       }
00821       ast_free(paramname);
00822 
00823       paramcount++;
00824    }
00825 
00826    /* Time to close open brackets. */
00827    while (openbrackets > 0) {
00828       xmldoc_reverse_helper(reverse, &len, &syntax, (reverse ? "[" : "]"));
00829       openbrackets--;
00830    }
00831 
00832    /* close syntax string. */
00833    if (reverse) {
00834       xmldoc_reverse_helper(reverse, &len, &syntax, "%s%s", (printrootname ? rootname : ""),
00835          (printrootname ? (printrootname == 2 ? "[(" : "(") : ""));
00836    } else {
00837       xmldoc_reverse_helper(reverse, &len, &syntax, (printrootname ? (printrootname == 2 ? ")]" : ")") : ""));
00838    }
00839 
00840    return syntax;
00841 #undef ISLAST
00842 #undef GOTONEXT
00843 #undef MP
00844 }
00845 
00846 /*! \internal
00847  *  \brief Parse an enumlist inside a <parameter> to generate a COMMAND
00848  *         syntax.
00849  *  \param fixnode A pointer to the <enumlist> node.
00850  *  \retval {<unknown>} on error.
00851  *  \retval A string inside brackets {} with the enum's separated by pipes |.
00852  */
00853 static char *xmldoc_parse_cmd_enumlist(struct ast_xml_node *fixnode)
00854 {
00855    struct ast_xml_node *node = fixnode;
00856    struct ast_str *paramname;
00857    char *enumname, *ret;
00858    int first = 1;
00859 
00860    paramname = ast_str_create(128);
00861    if (!paramname) {
00862       return ast_strdup("{<unkown>}");
00863    }
00864 
00865    ast_str_append(&paramname, 0, "{");
00866 
00867    for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
00868       if (strcasecmp(ast_xml_node_get_name(node), "enum")) {
00869          continue;
00870       }
00871 
00872       enumname = xmldoc_get_syntax_cmd(node, "", 0);
00873       if (!enumname) {
00874          continue;
00875       }
00876       if (!first) {
00877          ast_str_append(&paramname, 0, "|");
00878       }
00879       ast_str_append(&paramname, 0, "%s", enumname);
00880       first = 0;
00881       ast_free(enumname);
00882    }
00883 
00884    ast_str_append(&paramname, 0, "}");
00885 
00886    ret = ast_strdup(ast_str_buffer(paramname));
00887    ast_free(paramname);
00888 
00889    return ret;
00890 }
00891 
00892 /*! \internal
00893  *  \brief Generate a syntax of COMMAND type.
00894  *  \param fixnode The <syntax> node pointer.
00895  *  \param name The name of the 'command'.
00896  *  \param printname Print the name of the command before the paramters?
00897  *  \retval On error, return just 'name'.
00898  *  \retval On success return the generated syntax.
00899  */
00900 static char *xmldoc_get_syntax_cmd(struct ast_xml_node *fixnode, const char *name, int printname)
00901 {
00902    struct ast_str *syntax;
00903    struct ast_xml_node *tmpnode, *node = fixnode;
00904    char *ret, *paramname;
00905    const char *paramtype, *attrname, *literal;
00906    int required, isenum, first = 1, isliteral;
00907 
00908    syntax = ast_str_create(128);
00909    if (!syntax) {
00910       /* at least try to return something... */
00911       return ast_strdup(name);
00912    }
00913 
00914    /* append name to output string. */
00915    if (printname) {
00916       ast_str_append(&syntax, 0, "%s", name);
00917       first = 0;
00918    }
00919 
00920    for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
00921       if (strcasecmp(ast_xml_node_get_name(node), "parameter")) {
00922          continue;
00923       }
00924 
00925       if (xmldoc_has_inside(node, "parameter")) {
00926          /* is this a recursive parameter. */
00927          paramname = xmldoc_get_syntax_cmd(node, "", 0);
00928          isenum = 1;
00929       } else if (!xmldoc_has_inside(node, "enumlist")) {
00930          /* this is a simple parameter. */
00931          attrname = ast_xml_get_attribute(node, "name");
00932          if (!attrname) {
00933             /* ignore this bogus parameter and continue. */
00934             continue;
00935          }
00936          paramname = ast_strdup(attrname);
00937          ast_xml_free_attr(attrname);
00938          isenum = 0;
00939       } else {
00940          /* parse enumlist (note that this is a special enumlist
00941          that is used to describe a syntax like {<param1>|<param2>|...} */
00942          for (tmpnode = ast_xml_node_get_children(node); tmpnode; tmpnode = ast_xml_node_get_next(tmpnode)) {
00943             if (!strcasecmp(ast_xml_node_get_name(tmpnode), "enumlist")) {
00944                break;
00945             }
00946          }
00947          paramname = xmldoc_parse_cmd_enumlist(tmpnode);
00948          isenum = 1;
00949       }
00950 
00951       /* Is this parameter required? */
00952       required = 0;
00953       paramtype = ast_xml_get_attribute(node, "required");
00954       if (paramtype) {
00955          required = ast_true(paramtype);
00956          ast_xml_free_attr(paramtype);
00957       }
00958 
00959       /* Is this a replaceable value or a fixed parameter value? */
00960       isliteral = 0;
00961       literal = ast_xml_get_attribute(node, "literal");
00962       if (literal) {
00963          isliteral = ast_true(literal);
00964          ast_xml_free_attr(literal);
00965       }
00966 
00967       /* if required="false" print with [...].
00968        * if literal="true" or is enum print without <..>.
00969        * if not first print a space at the beginning.
00970        */
00971       ast_str_append(&syntax, 0, "%s%s%s%s%s%s",
00972             (first ? "" : " "),
00973             (required ? "" : "["),
00974             (isenum || isliteral ? "" : "<"),
00975             paramname,
00976             (isenum || isliteral ? "" : ">"),
00977             (required ? "" : "]"));
00978       first = 0;
00979       ast_free(paramname);
00980    }
00981 
00982    /* return a common string. */
00983    ret = ast_strdup(ast_str_buffer(syntax));
00984    ast_free(syntax);
00985 
00986    return ret;
00987 }
00988 
00989 /*! \internal
00990  *  \brief Generate an AMI action syntax.
00991  *  \param fixnode The manager action node pointer.
00992  *  \param name The name of the manager action.
00993  *  \retval The generated syntax.
00994  *  \retval NULL on error.
00995  */
00996 static char *xmldoc_get_syntax_manager(struct ast_xml_node *fixnode, const char *name)
00997 {
00998    struct ast_str *syntax;
00999    struct ast_xml_node *node = fixnode;
01000    const char *paramtype, *attrname;
01001    int required;
01002    char *ret;
01003 
01004    syntax = ast_str_create(128);
01005    if (!syntax) {
01006       return ast_strdup(name);
01007    }
01008 
01009    ast_str_append(&syntax, 0, "Action: %s", name);
01010 
01011    for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
01012       if (strcasecmp(ast_xml_node_get_name(node), "parameter")) {
01013          continue;
01014       }
01015 
01016       /* Is this parameter required? */
01017       required = 0;
01018       paramtype = ast_xml_get_attribute(node, "required");
01019       if (paramtype) {
01020          required = ast_true(paramtype);
01021          ast_xml_free_attr(paramtype);
01022       }
01023 
01024       attrname = ast_xml_get_attribute(node, "name");
01025       if (!attrname) {
01026          /* ignore this bogus parameter and continue. */
01027          continue;
01028       }
01029 
01030       ast_str_append(&syntax, 0, "\n%s%s:%s <value>",
01031          (required ? "" : "["),
01032          attrname,
01033          (required ? "" : "]"));
01034 
01035       ast_xml_free_attr(attrname);
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 /*! \brief Types of syntax that we are able to generate. */
01046 enum syntaxtype {
01047    FUNCTION_SYNTAX,
01048    MANAGER_SYNTAX,
01049    COMMAND_SYNTAX
01050 };
01051 
01052 /*! \brief Mapping between type of node and type of syntax to generate. */
01053 static struct strsyntaxtype {
01054    const char *type;
01055    enum syntaxtype stxtype;
01056 } stxtype[] = {
01057    { "function",     FUNCTION_SYNTAX   },
01058    { "application",  FUNCTION_SYNTAX   },
01059    { "manager",      MANAGER_SYNTAX  },
01060    { "agi",    COMMAND_SYNTAX }
01061 };
01062 
01063 /*! \internal
01064  *  \brief Get syntax type based on type of node.
01065  *  \param type Type of node.
01066  *  \retval The type of syntax to generate based on the type of node.
01067  */
01068 static enum syntaxtype xmldoc_get_syntax_type(const char *type)
01069 {
01070    int i;
01071    for (i=0; i < ARRAY_LEN(stxtype); i++) {
01072       if (!strcasecmp(stxtype[i].type, type)) {
01073          return stxtype[i].stxtype;
01074       }
01075    }
01076 
01077    return FUNCTION_SYNTAX;
01078 }
01079 
01080 char *ast_xmldoc_build_syntax(const char *type, const char *name)
01081 {
01082    struct ast_xml_node *node;
01083    char *syntax = NULL;
01084 
01085    node = xmldoc_get_node(type, name, documentation_language);
01086    if (!node) {
01087       return NULL;
01088    }
01089 
01090    for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
01091       if (!strcasecmp(ast_xml_node_get_name(node), "syntax")) {
01092          break;
01093       }
01094    }
01095 
01096    if (node) {
01097       switch (xmldoc_get_syntax_type(type)) {
01098       case FUNCTION_SYNTAX:
01099          syntax = xmldoc_get_syntax_fun(node, name, "parameter", 1, 1);
01100          break;
01101       case COMMAND_SYNTAX:
01102          syntax = xmldoc_get_syntax_cmd(node, name, 1);
01103          break;
01104       case MANAGER_SYNTAX:
01105          syntax = xmldoc_get_syntax_manager(node, name);
01106          break;
01107       default:
01108          syntax = xmldoc_get_syntax_fun(node, name, "parameter", 1, 1);
01109       }
01110    }
01111    return syntax;
01112 }
01113 
01114 /*! \internal
01115  *  \brief Parse a <para> element.
01116  *  \param node The <para> element pointer.
01117  *  \param tabs Added this string before the content of the <para> element.
01118  *  \param posttabs Added this string after the content of the <para> element.
01119  *  \param buffer This must be an already allocated ast_str. It will be used
01120  *         to store the result (if already has something it will be appended to the current
01121  *         string).
01122  *  \retval 1 If 'node' is a named 'para'.
01123  *  \retval 2 If data is appended in buffer.
01124  *  \retval 0 on error.
01125  */
01126 static int xmldoc_parse_para(struct ast_xml_node *node, const char *tabs, const char *posttabs, struct ast_str **buffer)
01127 {
01128    const char *tmptext;
01129    struct ast_xml_node *tmp;
01130    int ret = 0;
01131    struct ast_str *tmpstr;
01132 
01133    if (!node || !ast_xml_node_get_children(node)) {
01134       return ret;
01135    }
01136 
01137    if (strcasecmp(ast_xml_node_get_name(node), "para")) {
01138       return ret;
01139    }
01140 
01141    ast_str_append(buffer, 0, "%s", tabs);
01142 
01143    ret = 1;
01144 
01145    for (tmp = ast_xml_node_get_children(node); tmp; tmp = ast_xml_node_get_next(tmp)) {
01146       /* Get the text inside the <para> element and append it to buffer. */
01147       tmptext = ast_xml_get_text(tmp);
01148       if (tmptext) {
01149          /* Strip \n etc. */
01150          xmldoc_string_cleanup(tmptext, &tmpstr, 0);
01151          ast_xml_free_text(tmptext);
01152          if (tmpstr) {
01153             if (strcasecmp(ast_xml_node_get_name(tmp), "text")) {
01154                ast_str_append(buffer, 0, "<%s>%s</%s>", ast_xml_node_get_name(tmp),
01155                      ast_str_buffer(tmpstr), ast_xml_node_get_name(tmp));
01156             } else {
01157                ast_str_append(buffer, 0, "%s", ast_str_buffer(tmpstr));
01158             }
01159             ast_free(tmpstr);
01160             ret = 2;
01161          }
01162       }
01163    }
01164 
01165    ast_str_append(buffer, 0, "%s", posttabs);
01166 
01167    return ret;
01168 }
01169 
01170 /*! \internal
01171  *  \brief Parse special elements defined in 'struct special_tags' special elements must have a <para> element inside them.
01172  *  \param fixnode special tag node pointer.
01173  *  \param tabs put tabs before printing the node content.
01174  *  \param posttabs put posttabs after printing node content.
01175  *  \param buffer Output buffer, the special tags will be appended here.
01176  *  \retval 0 if no special element is parsed.
01177  *  \retval 1 if a special element is parsed (data is appended to buffer).
01178  *  \retval 2 if a special element is parsed and also a <para> element is parsed inside the specialtag.
01179  */
01180 static int xmldoc_parse_specialtags(struct ast_xml_node *fixnode, const char *tabs, const char *posttabs, struct ast_str **buffer)
01181 {
01182    struct ast_xml_node *node = fixnode;
01183    int ret = 0, i, count = 0;
01184 
01185    if (!node || !ast_xml_node_get_children(node)) {
01186       return ret;
01187    }
01188 
01189    for (i = 0; i < ARRAY_LEN(special_tags); i++) {
01190       if (strcasecmp(ast_xml_node_get_name(node), special_tags[i].tagname)) {
01191          continue;
01192       }
01193 
01194       ret = 1;
01195       /* This is a special tag. */
01196 
01197       /* concat data */
01198       if (!ast_strlen_zero(special_tags[i].init)) {
01199          ast_str_append(buffer, 0, "%s%s", tabs, special_tags[i].init);
01200       }
01201 
01202       /* parse <para> elements inside special tags. */
01203       for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
01204          /* first <para> just print it without tabs at the begining. */
01205          if (xmldoc_parse_para(node, (!count ? "" : tabs), posttabs, buffer) == 2) {
01206             ret = 2;
01207          }
01208       }
01209 
01210       if (!ast_strlen_zero(special_tags[i].end)) {
01211          ast_str_append(buffer, 0, "%s%s", special_tags[i].end, posttabs);
01212       }
01213 
01214       break;
01215    }
01216 
01217    return ret;
01218 }
01219 
01220 /*! \internal
01221  *  \brief Parse an <argument> element from the xml documentation.
01222  *  \param fixnode Pointer to the 'argument' xml node.
01223  *  \param insideparameter If we are parsing an <argument> inside a <parameter>.
01224  *  \param paramtabs pre tabs if we are inside a parameter element.
01225  *  \param tabs What to be printed before the argument name.
01226  *  \param buffer Output buffer to put values found inside the <argument> element.
01227  *  \retval 1 If there is content inside the argument.
01228  *  \retval 0 If the argument element is not parsed, or there is no content inside it.
01229  */
01230 static int xmldoc_parse_argument(struct ast_xml_node *fixnode, int insideparameter, const char *paramtabs, const char *tabs, struct ast_str **buffer)
01231 {
01232    struct ast_xml_node *node = fixnode;
01233    const char *argname;
01234    int count = 0, ret = 0;
01235 
01236    if (!node || !ast_xml_node_get_children(node)) {
01237       return ret;
01238    }
01239 
01240    /* Print the argument names */
01241    argname = ast_xml_get_attribute(node, "name");
01242    if (!argname) {
01243       return 0;
01244    }
01245    if (xmldoc_has_inside(node, "para") || xmldoc_has_specialtags(node)) {
01246       ast_str_append(buffer, 0, "%s%s%s", tabs, argname, (insideparameter ? "\n" : ""));
01247       ast_xml_free_attr(argname);
01248    } else {
01249       ast_xml_free_attr(argname);
01250       return 0;
01251    }
01252 
01253    for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
01254       if (xmldoc_parse_para(node, (insideparameter ? paramtabs : (!count ? " - " : tabs)), "\n", buffer) == 2) {
01255          count++;
01256          ret = 1;
01257       } else if (xmldoc_parse_specialtags(node, (insideparameter ? paramtabs : (!count ? " - " : tabs)), "\n", buffer) == 2) {
01258          count++;
01259          ret = 1;
01260       }
01261    }
01262 
01263    return ret;
01264 }
01265 
01266 /*! \internal
01267  *  \brief Parse a <variable> node inside a <variablelist> node.
01268  *  \param node The variable node to parse.
01269  *  \param tabs A string to be appended at the begining of the output that will be stored
01270  *         in buffer.
01271  *  \param buffer This must be an already created ast_str. It will be used
01272  *         to store the result (if already has something it will be appended to the current
01273  *         string).
01274  *  \retval 0 if no data is appended.
01275  *  \retval 1 if data is appended.
01276  */
01277 static int xmldoc_parse_variable(struct ast_xml_node *node, const char *tabs, struct ast_str **buffer)
01278 {
01279    struct ast_xml_node *tmp;
01280    const char *valname;
01281    const char *tmptext;
01282    struct ast_str *cleanstr;
01283    int ret = 0, printedpara=0;
01284 
01285    for (tmp = ast_xml_node_get_children(node); tmp; tmp = ast_xml_node_get_next(tmp)) {
01286       if (xmldoc_parse_para(tmp, (ret ? tabs : ""), "\n", buffer)) {
01287          printedpara = 1;
01288          continue;
01289       } else if (xmldoc_parse_specialtags(tmp, (ret ? tabs : ""), "\n", buffer)) {
01290          printedpara = 1;
01291          continue;
01292       }
01293 
01294       if (strcasecmp(ast_xml_node_get_name(tmp), "value")) {
01295          continue;
01296       }
01297 
01298       /* Parse a <value> tag only. */
01299       if (!printedpara) {
01300          ast_str_append(buffer, 0, "\n");
01301          printedpara = 1;
01302       }
01303       /* Parse each <value name='valuename'>desciption</value> */
01304       valname = ast_xml_get_attribute(tmp, "name");
01305       if (valname) {
01306          ret = 1;
01307          ast_str_append(buffer, 0, "%s<value>%s</value>", tabs, valname);
01308          ast_xml_free_attr(valname);
01309       }
01310       tmptext = ast_xml_get_text(tmp);
01311       /* Check inside this node for any explanation about its meaning. */
01312       if (tmptext) {
01313          /* Cleanup text. */
01314          xmldoc_string_cleanup(tmptext, &cleanstr, 1);
01315          ast_xml_free_text(tmptext);
01316          if (cleanstr && ast_str_strlen(cleanstr) > 0) {
01317             ast_str_append(buffer, 0, ":%s", ast_str_buffer(cleanstr));
01318          }
01319          ast_free(cleanstr);
01320       }
01321       ast_str_append(buffer, 0, "\n");
01322    }
01323 
01324    return ret;
01325 }
01326 
01327 /*! \internal
01328  *  \brief Parse a <variablelist> node and put all the output inside 'buffer'.
01329  *  \param node The variablelist node pointer.
01330  *  \param tabs A string to be appended at the begining of the output that will be stored
01331  *         in buffer.
01332  *  \param buffer This must be an already created ast_str. It will be used
01333  *         to store the result (if already has something it will be appended to the current
01334  *         string).
01335  *  \retval 1 If a <variablelist> element is parsed.
01336  *  \retval 0 On error.
01337  */
01338 static int xmldoc_parse_variablelist(struct ast_xml_node *node, const char *tabs, struct ast_str **buffer)
01339 {
01340    struct ast_xml_node *tmp;
01341    const char *varname;
01342    char *vartabs;
01343    int ret = 0;
01344 
01345    if (!node || !ast_xml_node_get_children(node)) {
01346       return ret;
01347    }
01348 
01349    if (strcasecmp(ast_xml_node_get_name(node), "variablelist")) {
01350       return ret;
01351    }
01352 
01353    /* use this spacing (add 4 spaces) inside a variablelist node. */
01354    ast_asprintf(&vartabs, "%s    ", tabs);
01355    if (!vartabs) {
01356       return ret;
01357    }
01358    for (tmp = ast_xml_node_get_children(node); tmp; tmp = ast_xml_node_get_next(tmp)) {
01359       /* We can have a <para> element inside the variable list */
01360       if ((xmldoc_parse_para(tmp, (ret ? tabs : ""), "\n", buffer))) {
01361          ret = 1;
01362          continue;
01363       } else if ((xmldoc_parse_specialtags(tmp, (ret ? tabs : ""), "\n", buffer))) {
01364          ret = 1;
01365          continue;
01366       }
01367 
01368       if (!strcasecmp(ast_xml_node_get_name(tmp), "variable")) {
01369          /* Store the variable name in buffer. */
01370          varname = ast_xml_get_attribute(tmp, "name");
01371          if (varname) {
01372             ast_str_append(buffer, 0, "%s<variable>%s</variable>: ", tabs, varname);
01373             ast_xml_free_attr(varname);
01374             /* Parse the <variable> possible values. */
01375             xmldoc_parse_variable(tmp, vartabs, buffer);
01376             ret = 1;
01377          }
01378       }
01379    }
01380 
01381    ast_free(vartabs);
01382 
01383    return ret;
01384 }
01385 
01386 char *ast_xmldoc_build_seealso(const char *type, const char *name)
01387 {
01388    struct ast_str *outputstr;
01389    char *output;
01390    struct ast_xml_node *node;
01391    const char *typename;
01392    const char *content;
01393    int first = 1;
01394 
01395    if (ast_strlen_zero(type) || ast_strlen_zero(name)) {
01396       return NULL;
01397    }
01398 
01399    /* get the application/function root node. */
01400    node = xmldoc_get_node(type, name, documentation_language);
01401    if (!node || !ast_xml_node_get_children(node)) {
01402       return NULL;
01403    }
01404 
01405    /* Find the <see-also> node. */
01406    for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
01407       if (!strcasecmp(ast_xml_node_get_name(node), "see-also")) {
01408          break;
01409       }
01410    }
01411 
01412    if (!node || !ast_xml_node_get_children(node)) {
01413       /* we couldnt find a <see-also> node. */
01414       return NULL;
01415    }
01416 
01417    /* prepare the output string. */
01418    outputstr = ast_str_create(128);
01419    if (!outputstr) {
01420       return NULL;
01421    }
01422 
01423    /* get into the <see-also> node. */
01424    for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
01425       if (strcasecmp(ast_xml_node_get_name(node), "ref")) {
01426          continue;
01427       }
01428 
01429       /* parse the <ref> node. 'type' attribute is required. */
01430       typename = ast_xml_get_attribute(node, "type");
01431       if (!typename) {
01432          continue;
01433       }
01434       content = ast_xml_get_text(node);
01435       if (!content) {
01436          ast_xml_free_attr(typename);
01437          continue;
01438       }
01439       if (!strcasecmp(typename, "application")) {
01440          ast_str_append(&outputstr, 0, "%s%s()",   (first ? "" : ", "), content);
01441       } else if (!strcasecmp(typename, "function")) {
01442          ast_str_append(&outputstr, 0, "%s%s", (first ? "" : ", "), content);
01443       } else if (!strcasecmp(typename, "astcli")) {
01444          ast_str_append(&outputstr, 0, "%s<astcli>%s</astcli>", (first ? "" : ", "), content);
01445       } else {
01446          ast_str_append(&outputstr, 0, "%s%s", (first ? "" : ", "), content);
01447       }
01448       first = 0;
01449       ast_xml_free_text(content);
01450       ast_xml_free_attr(typename);
01451    }
01452 
01453    output = ast_strdup(ast_str_buffer(outputstr));
01454    ast_free(outputstr);
01455 
01456    return output;
01457 }
01458 
01459 /*! \internal
01460  *  \brief Parse a <enum> node.
01461  *  \brief fixnode An ast_xml_node pointer to the <enum> node.
01462  *  \bried buffer The output buffer.
01463  *  \retval 0 if content is not found inside the enum element (data is not appended to buffer).
01464  *  \retval 1 if content is found and data is appended to buffer.
01465  */
01466 static int xmldoc_parse_enum(struct ast_xml_node *fixnode, const char *tabs, struct ast_str **buffer)
01467 {
01468    struct ast_xml_node *node = fixnode;
01469    int ret = 0;
01470    char *optiontabs;
01471 
01472    ast_asprintf(&optiontabs, "%s    ", tabs);
01473 
01474    for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
01475       if ((xmldoc_parse_para(node, (ret ? tabs : " - "), "\n", buffer))) {
01476          ret = 1;
01477       } else if ((xmldoc_parse_specialtags(node, (ret ? tabs : " - "), "\n", buffer))) {
01478          ret = 1;
01479       }
01480 
01481       xmldoc_parse_enumlist(node, optiontabs, buffer);
01482    }
01483 
01484    ast_free(optiontabs);
01485 
01486    return ret;
01487 }
01488 
01489 /*! \internal
01490  *  \brief Parse a <enumlist> node.
01491  *  \param fixnode As ast_xml pointer to the <enumlist> node.
01492  *  \param buffer The ast_str output buffer.
01493  *  \retval 0 if no <enumlist> node was parsed.
01494  *  \retval 1 if a <enumlist> node was parsed.
01495  */
01496 static int xmldoc_parse_enumlist(struct ast_xml_node *fixnode, const char *tabs, struct ast_str **buffer)
01497 {
01498    struct ast_xml_node *node = fixnode;
01499    const char *enumname;
01500    int ret = 0;
01501 
01502    for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
01503       if (strcasecmp(ast_xml_node_get_name(node), "enum")) {
01504          continue;
01505       }
01506 
01507       enumname = ast_xml_get_attribute(node, "name");
01508       if (enumname) {
01509          ast_str_append(buffer, 0, "%s<enum>%s</enum>", tabs, enumname);
01510          ast_xml_free_attr(enumname);
01511 
01512          /* parse only enum elements inside a enumlist node. */
01513          if ((xmldoc_parse_enum(node, tabs, buffer))) {
01514             ret = 1;
01515          } else {
01516             ast_str_append(buffer, 0, "\n");
01517          }
01518       }
01519    }
01520    return ret;
01521 }
01522 
01523 /*! \internal
01524  *  \brief Parse an <option> node.
01525  *  \param fixnode An ast_xml pointer to the <option> node.
01526  *  \param tabs A string to be appended at the begining of each line being added to the
01527  *              buffer string.
01528  *  \param buffer The output buffer.
01529  *  \retval 0 if no option node is parsed.
01530  *  \retval 1 if an option node is parsed.
01531  */
01532 static int xmldoc_parse_option(struct ast_xml_node *fixnode, const char *tabs, struct ast_str **buffer)
01533 {
01534    struct ast_xml_node *node;
01535    int ret = 0;
01536    char *optiontabs;
01537 
01538    ast_asprintf(&optiontabs, "%s    ", tabs);
01539    if (!optiontabs) {
01540       return ret;
01541    }
01542    for (node = ast_xml_node_get_children(fixnode); node; node = ast_xml_node_get_next(node)) {
01543       if (!strcasecmp(ast_xml_node_get_name(node), "argument")) {
01544          /* if this is the first data appended to buffer, print a \n*/
01545          if (!ret && ast_xml_node_get_children(node)) {
01546             /* print \n */
01547             ast_str_append(buffer, 0, "\n");
01548          }
01549          if (xmldoc_parse_argument(node, 0, NULL, optiontabs, buffer)) {
01550             ret = 1;
01551          }
01552          continue;
01553       }
01554 
01555       if (xmldoc_parse_para(node, (ret ? tabs :  ""), "\n", buffer)) {
01556          ret = 1;
01557       } else if (xmldoc_parse_specialtags(node, (ret ? tabs :  ""), "\n", buffer)) {
01558          ret = 1;
01559       }
01560 
01561       xmldoc_parse_variablelist(node, optiontabs, buffer);
01562 
01563       xmldoc_parse_enumlist(node, optiontabs, buffer);
01564    }
01565    ast_free(optiontabs);
01566 
01567    return ret;
01568 }
01569 
01570 /*! \internal
01571  *  \brief Parse an <optionlist> element from the xml documentation.
01572  *  \param fixnode Pointer to the optionlist xml node.
01573  *  \param tabs A string to be appended at the begining of each line being added to the
01574  *              buffer string.
01575  *  \param buffer Output buffer to put what is inside the optionlist tag.
01576  */
01577 static void xmldoc_parse_optionlist(struct ast_xml_node *fixnode, const char *tabs, struct ast_str **buffer)
01578 {
01579    struct ast_xml_node *node;
01580    const char *optname, *hasparams;
01581    char *optionsyntax;
01582    int optparams;
01583 
01584    for (node = ast_xml_node_get_children(fixnode); node; node = ast_xml_node_get_next(node)) {
01585       /* Start appending every option tag. */
01586       if (strcasecmp(ast_xml_node_get_name(node), "option")) {
01587          continue;
01588       }
01589 
01590       /* Get the option name. */
01591       optname = ast_xml_get_attribute(node, "name");
01592       if (!optname) {
01593          continue;
01594       }
01595 
01596       optparams = 1;
01597       hasparams = ast_xml_get_attribute(node, "hasparams");
01598       if (hasparams && !strcasecmp(hasparams, "optional")) {
01599          optparams = 2;
01600       }
01601 
01602       optionsyntax = xmldoc_get_syntax_fun(node, optname, "argument", 0, optparams);
01603       if (!optionsyntax) {
01604          ast_xml_free_attr(optname);
01605          ast_xml_free_attr(hasparams);
01606          continue;
01607       }
01608 
01609       ast_str_append(buffer, 0, "%s%s: ", tabs, optionsyntax);
01610 
01611       if (!xmldoc_parse_option(node, tabs, buffer)) {
01612          ast_str_append(buffer, 0, "\n");
01613       }
01614       ast_str_append(buffer, 0, "\n");
01615       ast_xml_free_attr(optname);
01616       ast_xml_free_attr(hasparams);
01617    }
01618 }
01619 
01620 /*! \internal
01621  *  \brief Parse a 'parameter' tag inside a syntax element.
01622  *  \param fixnode A pointer to the 'parameter' xml node.
01623  *  \param tabs A string to be appended at the beginning of each line being printed inside
01624  *              'buffer'.
01625  *  \param buffer String buffer to put values found inside the parameter element.
01626  */
01627 static void xmldoc_parse_parameter(struct ast_xml_node *fixnode, const char *tabs, struct ast_str **buffer)
01628 {
01629    const char *paramname;
01630    struct ast_xml_node *node = fixnode;
01631    int hasarguments, printed = 0;
01632    char *internaltabs;
01633 
01634    if (strcasecmp(ast_xml_node_get_name(node), "parameter")) {
01635       return;
01636    }
01637 
01638    hasarguments = xmldoc_has_inside(node, "argument");
01639    if (!(paramname = ast_xml_get_attribute(node, "name"))) {
01640       /* parameter MUST have an attribute name. */
01641       return;
01642    }
01643 
01644    ast_asprintf(&internaltabs, "%s    ", tabs);
01645    if (!internaltabs) {
01646       return;
01647    }
01648 
01649    if (!hasarguments && xmldoc_has_nodes(node)) {
01650       ast_str_append(buffer, 0, "%s\n", paramname);
01651       ast_xml_free_attr(paramname);
01652       printed = 1;
01653    }
01654 
01655    for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
01656       if (!strcasecmp(ast_xml_node_get_name(node), "optionlist")) {
01657          xmldoc_parse_optionlist(node, internaltabs, buffer);
01658       } else if (!strcasecmp(ast_xml_node_get_name(node), "enumlist")) {
01659          xmldoc_parse_enumlist(node, internaltabs, buffer);
01660       } else if (!strcasecmp(ast_xml_node_get_name(node), "argument")) {
01661          xmldoc_parse_argument(node, 1, internaltabs, (!hasarguments ? "        " : ""), buffer);
01662       } else if (!strcasecmp(ast_xml_node_get_name(node), "para")) {
01663          if (!printed) {
01664             ast_str_append(buffer, 0, "%s\n", paramname);
01665             ast_xml_free_attr(paramname);
01666             printed = 1;
01667          }
01668          xmldoc_parse_para(node, internaltabs, "\n", buffer);
01669          continue;
01670       } else if ((xmldoc_parse_specialtags(node, internaltabs, "\n", buffer))) {
01671          continue;
01672       }
01673    }
01674    if (!printed) {
01675       ast_xml_free_attr(paramname);
01676    }
01677    ast_free(internaltabs);
01678 }
01679 
01680 char *ast_xmldoc_build_arguments(const char *type, const char *name)
01681 {
01682    struct ast_xml_node *node;
01683    struct ast_str *ret = ast_str_create(128);
01684    char *retstr = NULL;
01685 
01686    if (ast_strlen_zero(type) || ast_strlen_zero(name)) {
01687       return NULL;
01688    }
01689 
01690    node = xmldoc_get_node(type, name, documentation_language);
01691 
01692    if (!node || !ast_xml_node_get_children(node)) {
01693       return NULL;
01694    }
01695 
01696    /* Find the syntax field. */
01697    for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
01698       if (!strcasecmp(ast_xml_node_get_name(node), "syntax")) {
01699          break;
01700       }
01701    }
01702 
01703    if (!node || !ast_xml_node_get_children(node)) {
01704       /* We couldn't find the syntax node. */
01705       return NULL;
01706    }
01707 
01708    for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
01709       xmldoc_parse_parameter(node, "", &ret);
01710    }
01711 
01712    if (ast_str_strlen(ret) > 0) {
01713       /* remove last '\n' */
01714       char *buf = ast_str_buffer(ret);
01715       if (buf[ast_str_strlen(ret) - 1] == '\n') {
01716          ast_str_truncate(ret, -1);
01717       }
01718       retstr = ast_strdup(ast_str_buffer(ret));
01719    }
01720    ast_free(ret);
01721 
01722    return retstr;
01723 }
01724 
01725 /*! \internal
01726  *  \brief Return the string within a node formatted with <para> and <variablelist> elements.
01727  *  \param node Parent node where content resides.
01728  *  \param raw If set, return the node's content without further processing.
01729  *  \param raw_wrap Wrap raw text.
01730  *  \retval NULL on error
01731  *  \retval Node content on success.
01732  */
01733 static struct ast_str *xmldoc_get_formatted(struct ast_xml_node *node, int raw_output, int raw_wrap)
01734 {
01735    struct ast_xml_node *tmp;
01736    const char *notcleanret, *tmpstr;
01737    struct ast_str *ret = ast_str_create(128);
01738 
01739    if (raw_output) {
01740       notcleanret = ast_xml_get_text(node);
01741       tmpstr = notcleanret;
01742       xmldoc_string_cleanup(ast_skip_blanks(notcleanret), &ret, 0);
01743       ast_xml_free_text(tmpstr);
01744    } else {
01745       for (tmp = ast_xml_node_get_children(node); tmp; tmp = ast_xml_node_get_next(tmp)) {
01746          /* if found, parse a <para> element. */
01747          if (xmldoc_parse_para(tmp, "", "\n", &ret)) {
01748             continue;
01749          } else if (xmldoc_parse_specialtags(tmp, "", "\n", &ret)) {
01750             continue;
01751          }
01752          /* if found, parse a <variablelist> element. */
01753          xmldoc_parse_variablelist(tmp, "", &ret);
01754          xmldoc_parse_enumlist(tmp, "    ", &ret);
01755       }
01756       /* remove last '\n' */
01757       /* XXX Don't modify ast_str internals manually */
01758       tmpstr = ast_str_buffer(ret);
01759       if (tmpstr[ast_str_strlen(ret) - 1] == '\n') {
01760          ast_str_truncate(ret, -1);
01761       }
01762    }
01763    return ret;
01764 }
01765 
01766 /*!
01767  *  \brief Get the content of a field (synopsis, description, etc) from an asterisk document tree
01768  *  \param type Type of element (application, function, ...).
01769  *  \param name Name of element (Dial, Echo, Playback, ...).
01770  *  \param var Name of field to return (synopsis, description, etc).
01771  *  \param raw Field only contains text, no other elements inside it.
01772  *  \retval NULL On error.
01773  *  \retval Field text content on success.
01774  */
01775 static char *xmldoc_build_field(const char *type, const char *name, const char *var, int raw)
01776 {
01777    struct ast_xml_node *node;
01778    char *ret = NULL;
01779    struct ast_str *formatted;
01780 
01781    if (ast_strlen_zero(type) || ast_strlen_zero(name)) {
01782       ast_log(LOG_ERROR, "Tried to look in XML tree with faulty values.\n");
01783       return ret;
01784    }
01785 
01786    node = xmldoc_get_node(type, name, documentation_language);
01787 
01788    if (!node) {
01789       ast_log(LOG_WARNING, "Couldn't find %s %s in XML documentation\n", type, name);
01790       return ret;
01791    }
01792 
01793    node = ast_xml_find_element(ast_xml_node_get_children(node), var, NULL, NULL);
01794 
01795    if (!node || !ast_xml_node_get_children(node)) {
01796       ast_log(LOG_DEBUG, "Cannot find variable '%s' in tree '%s'\n", var, name);
01797       return ret;
01798    }
01799 
01800    formatted = xmldoc_get_formatted(node, raw, raw);
01801    if (ast_str_strlen(formatted) > 0) {
01802       ret = ast_strdup(ast_str_buffer(formatted));
01803    }
01804    ast_free(formatted);
01805 
01806    return ret;
01807 }
01808 
01809 char *ast_xmldoc_build_synopsis(const char *type, const char *name)
01810 {
01811    return xmldoc_build_field(type, name, "synopsis", 1);
01812 }
01813 
01814 char *ast_xmldoc_build_description(const char *type, const char *name)
01815 {
01816    return xmldoc_build_field(type, name, "description", 0);
01817 }
01818 
01819 #if !defined(HAVE_GLOB_NOMAGIC) || !defined(HAVE_GLOB_BRACE) || defined(DEBUG_NONGNU)
01820 static int xml_pathmatch(char *xmlpattern, int xmlpattern_maxlen, glob_t *globbuf)
01821 {
01822    int globret;
01823 
01824    snprintf(xmlpattern, xmlpattern_maxlen, "%s/documentation/thirdparty/*-%s.xml",
01825       ast_config_AST_DATA_DIR, documentation_language);
01826    if((globret = glob(xmlpattern, GLOB_NOCHECK, NULL, globbuf))) {
01827       return globret;
01828    }
01829 
01830    snprintf(xmlpattern, xmlpattern_maxlen, "%s/documentation/thirdparty/*-%.2s_??.xml",
01831       ast_config_AST_DATA_DIR, documentation_language);
01832    if((globret = glob(xmlpattern, GLOB_APPEND | GLOB_NOCHECK, NULL, globbuf))) {
01833       return globret;
01834    }
01835 
01836    snprintf(xmlpattern, xmlpattern_maxlen, "%s/documentation/thirdparty/*-%s.xml",
01837       ast_config_AST_DATA_DIR, default_documentation_language);
01838    if((globret = glob(xmlpattern, GLOB_APPEND | GLOB_NOCHECK, NULL, globbuf))) {
01839       return globret;
01840    }
01841 
01842    snprintf(xmlpattern, xmlpattern_maxlen, "%s/documentation/*-%s.xml",
01843       ast_config_AST_DATA_DIR, documentation_language);
01844    if((globret = glob(xmlpattern, GLOB_APPEND | GLOB_NOCHECK, NULL, globbuf))) {
01845       return globret;
01846    }
01847 
01848    snprintf(xmlpattern, xmlpattern_maxlen, "%s/documentation/*-%.2s_??.xml",
01849       ast_config_AST_DATA_DIR, documentation_language);
01850    if((globret = glob(xmlpattern, GLOB_APPEND | GLOB_NOCHECK, NULL, globbuf))) {
01851       return globret;
01852    }
01853 
01854    snprintf(xmlpattern, xmlpattern_maxlen, "%s/documentation/*-%s.xml",
01855       ast_config_AST_DATA_DIR, default_documentation_language);
01856    globret = glob(xmlpattern, GLOB_APPEND | GLOB_NOCHECK, NULL, globbuf);
01857 
01858    return globret;
01859 }
01860 #endif
01861 
01862 /*! \brief Close and unload XML documentation. */
01863 static void xmldoc_unload_documentation(void)
01864 {
01865         struct documentation_tree *doctree;
01866 
01867    AST_RWLIST_WRLOCK(&xmldoc_tree);
01868    while ((doctree = AST_RWLIST_REMOVE_HEAD(&xmldoc_tree, entry))) {
01869       ast_free(doctree->filename);
01870       ast_xml_close(doctree->doc);
01871    }
01872    AST_RWLIST_UNLOCK(&xmldoc_tree);
01873 
01874    ast_xml_finish();
01875 }
01876 
01877 int ast_xmldoc_load_documentation(void)
01878 {
01879    struct ast_xml_node *root_node;
01880    struct ast_xml_doc *tmpdoc;
01881    struct documentation_tree *doc_tree;
01882    char *xmlpattern;
01883    struct ast_config *cfg = NULL;
01884    struct ast_variable *var = NULL;
01885    struct ast_flags cnfflags = { 0 };
01886    int globret, i, dup, duplicate;
01887    glob_t globbuf;
01888 #if !defined(HAVE_GLOB_NOMAGIC) || !defined(HAVE_GLOB_BRACE) || defined(DEBUG_NONGNU)
01889    int xmlpattern_maxlen;
01890 #endif
01891 
01892    /* setup default XML documentation language */
01893    snprintf(documentation_language, sizeof(documentation_language), default_documentation_language);
01894 
01895    if ((cfg = ast_config_load2("asterisk.conf", "" /* core can't reload */, cnfflags)) && cfg != CONFIG_STATUS_FILEINVALID) {
01896       for (var = ast_variable_browse(cfg, "options"); var; var = var->next) {
01897          if (!strcasecmp(var->name, "documentation_language")) {
01898             if (!ast_strlen_zero(var->value)) {
01899                snprintf(documentation_language, sizeof(documentation_language), "%s", var->value);
01900             }
01901          }
01902       }
01903       ast_config_destroy(cfg);
01904    }
01905 
01906    /* initialize the XML library. */
01907    ast_xml_init();
01908 
01909    /* register function to be run when asterisk finish. */
01910    ast_register_atexit(xmldoc_unload_documentation);
01911 
01912    globbuf.gl_offs = 0;    /* slots to reserve in gl_pathv */
01913 
01914 #if !defined(HAVE_GLOB_NOMAGIC) || !defined(HAVE_GLOB_BRACE) || defined(DEBUG_NONGNU)
01915    xmlpattern_maxlen = strlen(ast_config_AST_DATA_DIR) + strlen("/documentation/thirdparty") + strlen("/*-??_??.xml") + 1;
01916    xmlpattern = ast_malloc(xmlpattern_maxlen);
01917    globret = xml_pathmatch(xmlpattern, xmlpattern_maxlen, &globbuf);
01918 #else
01919    /* Get every *-LANG.xml file inside $(ASTDATADIR)/documentation */
01920    ast_asprintf(&xmlpattern, "%s/documentation{/thirdparty/,/}*-{%s,%.2s_??,%s}.xml", ast_config_AST_DATA_DIR,
01921       documentation_language, documentation_language, default_documentation_language);
01922    globret = glob(xmlpattern, MY_GLOB_FLAGS, NULL, &globbuf);
01923 #endif
01924 
01925    ast_debug(3, "gl_pathc %zd\n", globbuf.gl_pathc);
01926    if (globret == GLOB_NOSPACE) {
01927       ast_log(LOG_WARNING, "XML load failure, glob expansion of pattern '%s' failed: Not enough memory\n", xmlpattern);
01928       ast_free(xmlpattern);
01929       return 1;
01930    } else if (globret  == GLOB_ABORTED) {
01931       ast_log(LOG_WARNING, "XML load failure, glob expansion of pattern '%s' failed: Read error\n", xmlpattern);
01932       ast_free(xmlpattern);
01933       return 1;
01934    }
01935    ast_free(xmlpattern);
01936 
01937    AST_RWLIST_WRLOCK(&xmldoc_tree);
01938    /* loop over expanded files */
01939    for (i = 0; i < globbuf.gl_pathc; i++) {
01940       /* check for duplicates (if we already [try to] open the same file. */
01941       duplicate = 0;
01942       for (dup = 0; dup < i; dup++) {
01943          if (!strcmp(globbuf.gl_pathv[i], globbuf.gl_pathv[dup])) {
01944             duplicate = 1;
01945             break;
01946          }
01947       }
01948       if (duplicate || strchr(globbuf.gl_pathv[i], '*')) {
01949       /* skip duplicates as well as pathnames not found 
01950        * (due to use of GLOB_NOCHECK in xml_pathmatch) */
01951          continue;
01952       }
01953       tmpdoc = NULL;
01954       tmpdoc = ast_xml_open(globbuf.gl_pathv[i]);
01955       if (!tmpdoc) {
01956          ast_log(LOG_ERROR, "Could not open XML documentation at '%s'\n", globbuf.gl_pathv[i]);
01957          continue;
01958       }
01959       /* Get doc root node and check if it starts with '<docs>' */
01960       root_node = ast_xml_get_root(tmpdoc);
01961       if (!root_node) {
01962          ast_log(LOG_ERROR, "Error getting documentation root node");
01963          ast_xml_close(tmpdoc);
01964          continue;
01965       }
01966       /* Check root node name for malformed xmls. */
01967       if (strcmp(ast_xml_node_get_name(root_node), "docs")) {
01968          ast_log(LOG_ERROR, "Documentation file is not well formed!\n");
01969          ast_xml_close(tmpdoc);
01970          continue;
01971       }
01972       doc_tree = ast_calloc(1, sizeof(*doc_tree));
01973       if (!doc_tree) {
01974          ast_log(LOG_ERROR, "Unable to allocate documentation_tree structure!\n");
01975          ast_xml_close(tmpdoc);
01976          continue;
01977       }
01978       doc_tree->doc = tmpdoc;
01979       doc_tree->filename = ast_strdup(globbuf.gl_pathv[i]);
01980       AST_RWLIST_INSERT_TAIL(&xmldoc_tree, doc_tree, entry);
01981    }
01982    AST_RWLIST_UNLOCK(&xmldoc_tree);
01983 
01984    globfree(&globbuf);
01985 
01986    return 0;
01987 }
01988 
01989 #endif /* AST_XML_DOCS */
01990 
01991 

Generated on Wed Apr 6 11:29:48 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7