Thu Sep 7 01:03:05 2017

Asterisk developer's documentation


xmldoc.c

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

Generated on 7 Sep 2017 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1