From 23adb8e5096fbeea54805da2b4d0e09070af8375 Mon Sep 17 00:00:00 2001 From: "Eliel C. Sardanons" Date: Mon, 10 Nov 2008 13:53:23 +0000 Subject: Move all the XML documentation API from pbx.c to xmldoc.c. Export the XML documentation API: ast_xmldoc_build_synopsis() ast_xmldoc_build_syntax() ast_xmldoc_build_description() ast_xmldoc_build_seealso() ast_xmldoc_build_arguments() ast_xmldoc_printable() ast_xmldoc_load_documentation() git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@155711 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- include/asterisk/_private.h | 4 +- include/asterisk/term.h | 4 + include/asterisk/xmldoc.h | 85 ++ main/Makefile | 2 +- main/asterisk.c | 4 +- main/pbx.c | 1883 ++++--------------------------------------- main/xmldoc.c | 1621 +++++++++++++++++++++++++++++++++++++ 7 files changed, 1855 insertions(+), 1748 deletions(-) create mode 100644 include/asterisk/xmldoc.h create mode 100644 main/xmldoc.c diff --git a/include/asterisk/_private.h b/include/asterisk/_private.h index be4542d7a..83ae166de 100644 --- a/include/asterisk/_private.h +++ b/include/asterisk/_private.h @@ -56,10 +56,10 @@ int ast_timing_init(void); /*!< Provided by timing.c */ */ int ast_module_reload(const char *name); -/*! \brief Load XML documentation. Provided by pbx.c +/*! \brief Load XML documentation. Provided by xmldoc.c * \retval 1 on error. * \retval 0 on success. */ -int ast_load_documentation(void); +int ast_xmldoc_load_documentation(void); #endif /* _ASTERISK__PRIVATE_H */ diff --git a/include/asterisk/term.h b/include/asterisk/term.h index 8c6cab43e..d75cf99d0 100644 --- a/include/asterisk/term.h +++ b/include/asterisk/term.h @@ -62,6 +62,10 @@ extern "C" { #define COLOR_BRWHITE (37 | 128) /*@} */ +/*! \brief Maximum number of characters needed for a color escape sequence, + * plus a null char */ +#define AST_TERM_MAX_ESCAPE_CHARS 23 + char *term_color(char *outbuf, const char *inbuf, int fgcolor, int bgcolor, int maxout); /*! diff --git a/include/asterisk/xmldoc.h b/include/asterisk/xmldoc.h new file mode 100644 index 000000000..0e8275a2d --- /dev/null +++ b/include/asterisk/xmldoc.h @@ -0,0 +1,85 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2008, Eliel C. Sardanons (LU1ALY) + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +#ifndef _ASTERISK_XMLDOC_H +#define _ASTERISK_XMLDOC_H + +/*! \file + * \brief Asterisk XML Documentation API + */ + +#include "asterisk/xml.h" + +#ifdef AST_XML_DOCS + +/*! + * \brief Get the syntax for a specified application or function. + * \param type Application, Function or AGI ? + * \param name Name of the application or function. + * \retval NULL on error. + * \retval The generated syntax in a ast_malloc'ed string. + */ +char *ast_xmldoc_build_syntax(const char *type, const char *name); + +/*! + * \brief Parse the node content. + * \param type 'application', 'function' or 'agi'. + * \param name Application or functions name. + * \retval NULL on error. + * \retval Content of the see-also node. + */ +char *ast_xmldoc_build_seealso(const char *type, const char *name); + +/*! + * \brief Generate the [arguments] tag based on type of node ('application', + * 'function' or 'agi') and name. + * \param type 'application', 'function' or 'agi' ? + * \param name Name of the application or function to build the 'arguments' tag. + * \retval NULL on error. + * \retval Output buffer with the [arguments] tag content. + */ +char *ast_xmldoc_build_arguments(const char *type, const char *name); + +/*! + * \brief Colorize and put delimiters (instead of tags) to the xmldoc output. + * \param bwinput Not colorized input with tags. + * \param withcolors Result output with colors. + * \retval NULL on error. + * \retval New malloced buffer colorized and with delimiters. + */ +char *ast_xmldoc_printable(const char *bwinput, int withcolors); + +/*! + * \brief Generate synopsis documentation from XML. + * \param type The source of documentation (application, function, etc). + * \param name The name of the application, function, etc. + * \retval NULL on error. + * \retval A malloc'ed string with the synopsis. + */ +char *ast_xmldoc_build_synopsis(const char *type, const char *name); + +/*! + * \brief Generate description documentation from XML. + * \param type The source of documentation (application, function, etc). + * \param name The name of the application, function, etc. + * \retval NULL on error. + * \retval A malloc'ed string with the formatted description. + */ +char *ast_xmldoc_build_description(const char *type, const char *name); + +#endif /* AST_XML_DOCS */ + +#endif /* _ASTERISK_XMLDOC_H */ diff --git a/main/Makefile b/main/Makefile index 3ed689991..51acf20ee 100644 --- a/main/Makefile +++ b/main/Makefile @@ -28,7 +28,7 @@ OBJS= tcptls.o io.o sched.o logger.o frame.o loader.o config.o channel.o \ cryptostub.o sha1.o http.o fixedjitterbuf.o abstract_jb.o \ strcompat.o threadstorage.o dial.o event.o adsistub.o audiohook.o \ astobj2.o hashtab.o global_datastores.o version.o \ - features.o taskprocessor.o timing.o datastore.o xml.o + features.o taskprocessor.o timing.o datastore.o xml.o xmldoc.o # we need to link in the objects statically, not as a library, because # otherwise modules will not have them available if none of the static diff --git a/main/asterisk.c b/main/asterisk.c index 22454349d..27c221353 100644 --- a/main/asterisk.c +++ b/main/asterisk.c @@ -117,8 +117,8 @@ int daemon(int, int); /* defined in libresolv of all places */ #include "asterisk/devicestate.h" #include "asterisk/module.h" #include "asterisk/dsp.h" -#include "asterisk/xml.h" #include "asterisk/buildinfo.h" +#include "asterisk/xmldoc.h" #include "asterisk/doxyref.h" /* Doxygen documentation */ @@ -3346,7 +3346,7 @@ int main(int argc, char *argv[]) #ifdef AST_XML_DOCS /* Load XML documentation. */ - ast_load_documentation(); + ast_xmldoc_load_documentation(); #endif if (load_modules(1)) { /* Load modules, pre-load only */ diff --git a/main/pbx.c b/main/pbx.c index e120eeebc..737fe6d53 100644 --- a/main/pbx.c +++ b/main/pbx.c @@ -64,7 +64,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/module.h" #include "asterisk/indications.h" #include "asterisk/taskprocessor.h" -#include "asterisk/xml.h" +#include "asterisk/xmldoc.h" /*! * \note I M P O R T A N T : @@ -921,42 +921,6 @@ struct pbx_exception { int priority; /*!< Priority associated with this exception */ }; -#ifdef AST_XML_DOCS -/*! \brief Default documentation language. */ -static const char default_documentation_language[] = "en_US"; - -/*! \brief Number of columns to print when showing the XML documentation with a - * 'core show application/function *' CLI command. Used in text wrapping.*/ -static const int xmldoc_text_columns = 74; - -/*! \brief This is a value that we will use to let the wrapping mechanism move the cursor - * backward and forward xmldoc_max_diff positions before cutting the middle of a - * word, trying to find a space or a \n. */ -static const int xmldoc_max_diff = 5; - -/*! \brief XML documentation language. */ -static char documentation_language[6]; - -/*! \brief XML documentation tree */ -struct documentation_tree { - char *filename; /*!< XML document filename. */ - struct ast_xml_doc *doc; /*!< Open document pointer. */ - AST_RWLIST_ENTRY(documentation_tree) entry; -}; - -/*! - * \brief Container of documentation trees - * - * \note A RWLIST is a sufficient container type to use here for now. - * However, some changes will need to be made to implement ref counting - * if reload support is added in the future. - */ -static AST_RWLIST_HEAD_STATIC(xmldoc_tree, documentation_tree); -#endif - -/*! \brief Maximum number of characters needed for a color escape sequence, plus a null char */ -#define MAX_ESCAPE_CHARS 23 - static int pbx_builtin_answer(struct ast_channel *, void *); static int pbx_builtin_goto(struct ast_channel *, void *); static int pbx_builtin_hangup(struct ast_channel *, void *); @@ -1002,9 +966,6 @@ static unsigned int hashtab_hash_extens(const void *obj); static unsigned int hashtab_hash_priority(const void *obj); static unsigned int hashtab_hash_labels(const void *obj); static void __ast_internal_context_destroy( struct ast_context *con); -#ifdef AST_XML_DOCS -static char *xmldoc_colorization(const char *bwinput); -#endif /* a func for qsort to use to sort a char array */ static int compare_char(const void *a, const void *b) @@ -3124,7 +3085,7 @@ static char *handle_show_function(struct ast_cli_entry *e, int cmd, struct ast_c return CLI_FAILURE; } - syntax_size = strlen(S_OR(acf->syntax, "Not Available")) + MAX_ESCAPE_CHARS; + syntax_size = strlen(S_OR(acf->syntax, "Not Available")) + AST_TERM_MAX_ESCAPE_CHARS; if (!(syntax = ast_malloc(syntax_size))) { ast_cli(a->fd, "Memory allocation failure!\n"); return CLI_FAILURE; @@ -3140,23 +3101,23 @@ static char *handle_show_function(struct ast_cli_entry *e, int cmd, struct ast_c term_color(syntax, S_OR(acf->syntax, "Not available"), COLOR_CYAN, 0, syntax_size); #ifdef AST_XML_DOCS if (acf->docsrc == AST_XML_DOC) { - arguments = xmldoc_colorization(S_OR(acf->arguments, "Not available")); - synopsis = xmldoc_colorization(S_OR(acf->synopsis, "Not available")); - description = xmldoc_colorization(S_OR(acf->desc, "Not available")); - seealso = xmldoc_colorization(S_OR(acf->seealso, "Not available")); - } else + arguments = ast_xmldoc_printable(S_OR(acf->arguments, "Not available"), 1); + synopsis = ast_xmldoc_printable(S_OR(acf->synopsis, "Not available"), 1); + description = ast_xmldoc_printable(S_OR(acf->desc, "Not available"), 1); + seealso = ast_xmldoc_printable(S_OR(acf->seealso, "Not available"), 1); + } else #endif { - synopsis_size = strlen(S_OR(acf->synopsis, "Not Available")) + MAX_ESCAPE_CHARS; + synopsis_size = strlen(S_OR(acf->synopsis, "Not Available")) + AST_TERM_MAX_ESCAPE_CHARS; synopsis = ast_malloc(synopsis_size); - description_size = strlen(S_OR(acf->desc, "Not Available")) + MAX_ESCAPE_CHARS; + description_size = strlen(S_OR(acf->desc, "Not Available")) + AST_TERM_MAX_ESCAPE_CHARS; description = ast_malloc(description_size); - arguments_size = strlen(S_OR(acf->arguments, "Not Available")) + MAX_ESCAPE_CHARS; + arguments_size = strlen(S_OR(acf->arguments, "Not Available")) + AST_TERM_MAX_ESCAPE_CHARS; arguments = ast_malloc(arguments_size); - seealso_size = strlen(S_OR(acf->seealso, "Not Available")) + MAX_ESCAPE_CHARS; + seealso_size = strlen(S_OR(acf->seealso, "Not Available")) + AST_TERM_MAX_ESCAPE_CHARS; seealso = ast_malloc(seealso_size); /* check allocated memory. */ @@ -3221,1620 +3182,171 @@ int ast_custom_function_unregister(struct ast_custom_function *acf) return cur ? 0 : -1; } -#ifdef AST_XML_DOCS -static const struct strcolorized_tags { - const char *init; /*!< Replace initial tag with this string. */ - const char *end; /*!< Replace end tag with this string. */ - const int colorfg; /*!< Foreground color. */ - const char *inittag; /*!< Initial tag description. */ - const char *endtag; /*!< Ending tag description. */ -} colorized_tags[] = { - { "<", ">", COLOR_GREEN, "", "" }, - { "\'", "\'", COLOR_BLUE, "", "" }, - { "*", "*", COLOR_RED, "", "" }, - { "\"", "\"", COLOR_YELLOW, "", "" }, - { "\"", "\"", COLOR_CYAN, "", "" }, - { "${", "}", COLOR_GREEN, "", "" }, - { "", "", COLOR_BLUE, "", "" }, - { "", "", COLOR_BLUE, "", "" }, - { "\'", "\'", COLOR_GRAY, "", "" }, - - /* Special tags */ - { "", "", COLOR_YELLOW, "", "" }, - { "", "", COLOR_RED, "", "" } -}; - -static const struct strspecial_tags { - const char *tagname; /*!< Special tag name. */ - const char *init; /*!< Print this at the beginning. */ - const char *end; /*!< Print this at the end. */ -} special_tags[] = { - { "note", "NOTE: ", "" }, - { "warning", "WARNING!!!: ", "" } -}; - /*! \internal - * \brief Calculate the space in bytes used by a format string - * that will be passed to a sprintf function. - * \param postbr The format string to use to calculate the length. - * \retval The postbr length. + * \brief Retrieve the XML documentation of a specified ast_custom_function, + * and populate ast_custom_function string fields. + * \param acf ast_custom_function structure with empty 'desc' and 'synopsis' + * but with a function 'name'. + * \retval -1 On error. + * \retval 0 On succes. */ -static int xmldoc_postbrlen(const char *postbr) +static int acf_retrieve_docs(struct ast_custom_function *acf) { - int postbrreallen = 0, i; - size_t postbrlen; +#ifdef AST_XML_DOCS + char *tmpxml; - if (!postbr) { + /* Let's try to find it in the Documentation XML */ + if (!ast_strlen_zero(acf->desc) || !ast_strlen_zero(acf->synopsis)) { return 0; } - postbrlen = strlen(postbr); - for (i = 0; i < postbrlen; i++) { - if (postbr[i] == '\t') { - postbrreallen += 8 - (postbrreallen % 8); - } else { - postbrreallen++; - } - } - return postbrreallen; -} - -/*! \internal - * \brief Setup postbr to be used while wrapping the text. - * Add to postbr array all the spaces and tabs at the beginning of text. - * \param postbr output array. - * \param len text array length. - * \param text Text with format string before the actual string. - */ -static void xmldoc_setpostbr(char *postbr, size_t len, const char *text) -{ - int c, postbrlen = 0; - - if (!text) { - return; - } - - for (c = 0; c < len; c++) { - if (text[c] == '\t' || text[c] == ' ') { - postbr[postbrlen++] = text[c]; - } else { - break; - } - } - postbr[postbrlen] = '\0'; -} - -/*! \internal - * \brief Try to find a space or a break in text starting at currentpost - * and moving at most maxdiff positions. - * Helper for xmldoc_string_wrap(). - * \param text Input string where it will search. - * \param currentpos Current position within text. - * \param maxdiff Not move more than maxdiff inside text. - * \retval 1 if a space or break is found inside text while moving. - * \retval 0 if no space or break is found. - */ -static int xmldoc_wait_nextspace(const char *text, int currentpos, int maxdiff) -{ - int i, textlen; - if (!text) { - return 0; + if (ast_string_field_init(acf, 128)) { + return -1; } - textlen = strlen(text); - for (i = currentpos; i < textlen; i++) { - if (text[i] == ESC) { - /* Move to the end of the escape sequence */ - while (i < textlen && text[i] != 'm') { - i++; - } - } else if (text[i] == ' ' || text[i] == '\n') { - /* Found the next space or linefeed */ - return 1; - } else if (i - currentpos > maxdiff) { - /* We have looked the max distance and didn't find it */ - return 0; - } - } + /* load synopsis */ + tmpxml = ast_xmldoc_build_synopsis("function", acf->name); + ast_string_field_set(acf, synopsis, tmpxml); + ast_free(tmpxml); - /* Reached the end and did not find it */ + /* load description */ + tmpxml = ast_xmldoc_build_description("function", acf->name); + ast_string_field_set(acf, desc, tmpxml); + ast_free(tmpxml); - return 0; -} + /* load syntax */ + tmpxml = ast_xmldoc_build_syntax("function", acf->name); + ast_string_field_set(acf, syntax, tmpxml); + ast_free(tmpxml); -/*! \internal - * \brief Helper function for xmldoc_string_wrap(). - * Try to found a space or a break inside text moving backward - * not more than maxdiff positions. - * \param text The input string where to search for a space. - * \param currentpos The current cursor position. - * \param maxdiff The max number of positions to move within text. - * \retval 0 If no space is found (Notice that text[currentpos] is not a space or a break) - * \retval > 0 If a space or a break is found, and the result is the position relative to - * currentpos. - */ -static int xmldoc_foundspace_backward(const char *text, int currentpos, int maxdiff) -{ - int i; + /* load arguments */ + tmpxml = ast_xmldoc_build_arguments("function", acf->name); + ast_string_field_set(acf, arguments, tmpxml); + ast_free(tmpxml); - for (i = currentpos; i > 0; i--) { - if (text[i] == ' ' || text[i] == '\n') { - return (currentpos - i); - } else if (text[i] == 'm' && (text[i - 1] >= '0' || text[i - 1] <= '9')) { - /* give up, we found the end of a possible ESC sequence. */ - return 0; - } else if (currentpos - i > maxdiff) { - /* give up, we can't move anymore. */ - return 0; - } - } + /* load seealso */ + tmpxml = ast_xmldoc_build_seealso("function", acf->name); + ast_string_field_set(acf, seealso, tmpxml); + ast_free(tmpxml); - /* we found the beginning of the text */ + acf->docsrc = AST_XML_DOC; +#endif return 0; } -/*! \internal - * \brief Justify a text to a number of columns. - * \param text Input text to be justified. - * \param columns Number of columns to preserve in the text. - * \param maxdiff Try to not cut a word when goinf down. - * \retval NULL on error. - * \retval The wrapped text. - */ -static char *xmldoc_string_wrap(const char *text, int columns, int maxdiff) +int __ast_custom_function_register(struct ast_custom_function *acf, struct ast_module *mod) { - struct ast_str *tmp; - char *ret, postbr[160]; - int count = 1, i, backspace, needtobreak = 0, colmax, textlen; - - /* sanity check */ - if (!text || columns <= 0 || maxdiff < 0) { - ast_log(LOG_WARNING, "Passing wrong arguments while trying to wrap the text\n"); - return NULL; - } - - tmp = ast_str_create(strlen(text) * 3); - - if (!tmp) { - return NULL; - } + struct ast_custom_function *cur; + char tmps[80]; - /* Check for blanks and tabs and put them in postbr. */ - xmldoc_setpostbr(postbr, sizeof(postbr), text); - colmax = columns - xmldoc_postbrlen(postbr); - - textlen = strlen(text); - for (i = 0; i < textlen; i++) { - if (needtobreak || !(count % colmax)) { - if (text[i] == ' ') { - ast_str_append(&tmp, 0, "\n%s", postbr); - needtobreak = 0; - count = 1; - } else if (text[i] != '\n') { - needtobreak = 1; - if (xmldoc_wait_nextspace(text, i, maxdiff)) { - /* wait for the next space */ - ast_str_append(&tmp, 0, "%c", text[i]); - continue; - } - /* Try to look backwards */ - backspace = xmldoc_foundspace_backward(text, i, maxdiff); - if (backspace) { - needtobreak = 1; - tmp->used -= backspace; - tmp->str[tmp->used] = '\0'; - i -= backspace + 1; - continue; - } - ast_str_append(&tmp, 0, "\n%s", postbr); - needtobreak = 0; - count = 1; - } - /* skip blanks after a \n */ - while (text[i] == ' ') { - i++; - } - } - if (text[i] == '\n') { - xmldoc_setpostbr(postbr, sizeof(postbr), &text[i] + 1); - colmax = columns - xmldoc_postbrlen(postbr); - needtobreak = 0; - count = 1; - } - if (text[i] == ESC) { - /* Ignore Escape sequences. */ - do { - ast_str_append(&tmp, 0, "%c", text[i]); - i++; - } while (i < textlen && text[i] != 'm'); - } else { - count++; - } - ast_str_append(&tmp, 0, "%c", text[i]); + if (!acf) { + return -1; } - ret = ast_strdup(tmp->str); - ast_free(tmp); - - return ret; -} - -/*! \internal - * \brief Colorize the xmldoc output. - * \param bwinput Not colorized input. - * \retval NULL on error. - * \retval New malloced buffer colorized. - */ -static char *xmldoc_colorization(const char *bwinput) -{ - struct ast_str *colorized; - char *wrapped = NULL; - int i, c, len, colorsection; - char *tmp; - size_t bwinputlen; - static const int base_fg = COLOR_CYAN; + acf->mod = mod; + acf->docsrc = AST_STATIC_DOC; - if (!bwinput) { - return NULL; + if (acf_retrieve_docs(acf)) { + return -1; } - bwinputlen = strlen(bwinput); - - if (!(colorized = ast_str_create(256))) { - return NULL; - } + AST_RWLIST_WRLOCK(&acf_root); - ast_term_color_code(&colorized, base_fg, 0); - if (!colorized) { - return NULL; + AST_RWLIST_TRAVERSE(&acf_root, cur, acflist) { + if (!strcmp(acf->name, cur->name)) { + ast_log(LOG_ERROR, "Function %s already registered.\n", acf->name); + AST_RWLIST_UNLOCK(&acf_root); + return -1; + } } - for (i = 0; i < bwinputlen; i++) { - colorsection = 0; - /* Check if we are at the beginning of a tag to be colorized. */ - for (c = 0; c < ARRAY_LEN(colorized_tags); c++) { - if (strncasecmp(bwinput + i, colorized_tags[c].inittag, strlen(colorized_tags[c].inittag))) { - continue; - } - - if (!(tmp = strcasestr(bwinput + i + strlen(colorized_tags[c].inittag), colorized_tags[c].endtag))) { - continue; - } - - len = tmp - (bwinput + i + strlen(colorized_tags[c].inittag)); - - /* Setup color */ - ast_term_color_code(&colorized, colorized_tags[c].colorfg, 0); - if (!colorized) { - return NULL; - } - - /* copy initial string replace */ - ast_str_append(&colorized, 0, "%s", colorized_tags[c].init); - if (!colorized) { - return NULL; - } - { - char buf[len + 1]; - ast_copy_string(buf, bwinput + i + strlen(colorized_tags[c].inittag), sizeof(buf)); - ast_str_append(&colorized, 0, "%s", buf); - } - if (!colorized) { - return NULL; - } - - /* copy the ending string replace */ - ast_str_append(&colorized, 0, "%s", colorized_tags[c].end); - if (!colorized) { - return NULL; - } - - /* Continue with the last color. */ - ast_term_color_code(&colorized, base_fg, 0); - if (!colorized) { - return NULL; - } - - i += len + strlen(colorized_tags[c].endtag) + strlen(colorized_tags[c].inittag) - 1; - colorsection = 1; + /* Store in alphabetical order */ + AST_RWLIST_TRAVERSE_SAFE_BEGIN(&acf_root, cur, acflist) { + if (strcasecmp(acf->name, cur->name) < 0) { + AST_RWLIST_INSERT_BEFORE_CURRENT(acf, acflist); break; } - - if (!colorsection) { - ast_str_append(&colorized, 0, "%c", bwinput[i]); - if (!colorized) { - return NULL; - } - } } + AST_RWLIST_TRAVERSE_SAFE_END; - ast_term_color_code(&colorized, COLOR_BRWHITE, 0); - if (!colorized) { - return NULL; + if (!cur) { + AST_RWLIST_INSERT_TAIL(&acf_root, acf, acflist); } - /* Wrap the text, notice that string wrap will avoid cutting an ESC sequence. */ - wrapped = xmldoc_string_wrap(colorized->str, xmldoc_text_columns, xmldoc_max_diff); + AST_RWLIST_UNLOCK(&acf_root); - ast_free(colorized); + ast_verb(2, "Registered custom function '%s'\n", term_color(tmps, acf->name, COLOR_BRCYAN, 0, sizeof(tmps))); - return wrapped; + return 0; } -/*! \internal - * \brief Cleanup spaces and tabs after a \n - * \param text String to be cleaned up. - * \param output buffer (not already allocated). - * \param lastspaces Remove last spaces in the string. +/*! \brief return a pointer to the arguments of the function, + * and terminates the function name with '\\0' */ -static void xmldoc_string_cleanup(const char *text, struct ast_str **output, int lastspaces) +static char *func_args(char *function) { - int i; - size_t textlen; - - if (!text) { - *output = NULL; - return; - } - - textlen = strlen(text); - - *output = ast_str_create(textlen); - if (!(*output)) { - ast_log(LOG_ERROR, "Problem allocating output buffer\n"); - return; - } - - for (i = 0; i < textlen; i++) { - if (text[i] == '\n' || text[i] == '\r') { - /* remove spaces/tabs/\n after a \n. */ - while (text[i + 1] == '\t' || text[i + 1] == '\r' || text[i + 1] == '\n') { - i++; - } - ast_str_append(output, 0, " "); - continue; - } else { - ast_str_append(output, 0, "%c", text[i]); - } - } + char *args = strchr(function, '('); - /* remove last spaces (we dont want always to remove the trailing spaces). */ - if (lastspaces) { - ast_str_trim_blanks(*output); + if (!args) + ast_log(LOG_WARNING, "Function doesn't contain parentheses. Assuming null argument.\n"); + else { + char *p; + *args++ = '\0'; + if ((p = strrchr(args, ')')) ) + *p = '\0'; + else + ast_log(LOG_WARNING, "Can't find trailing parenthesis?\n"); } + return args; } -/*! \internal - * \brief Get the application/function node for 'name' application/function with language 'language' - * if we don't find any, get the first application with 'name' no matter which language with. - * \param type 'application', 'function', ... - * \param name Application or Function name. - * \param language Try to get this language (if not found try with en_US) - * \retval NULL on error. - * \retval A node of type ast_xml_node. - */ -static struct ast_xml_node *xmldoc_get_node(const char *type, const char *name, const char *language) -{ - struct ast_xml_node *node = NULL; - struct documentation_tree *doctree; - const char *lang; - - AST_RWLIST_RDLOCK(&xmldoc_tree); - AST_LIST_TRAVERSE(&xmldoc_tree, doctree, entry) { - /* the core xml documents have priority over thirdparty document. */ - node = ast_xml_get_root(doctree->doc); - while ((node = ast_xml_find_element(node, type, "name", name))) { - /* Check language */ - lang = ast_xml_get_attribute(node, "language"); - if (lang && !strcmp(lang, language)) { - ast_xml_free_attr(lang); - break; - } else if (lang) { - ast_xml_free_attr(lang); - } - } - - if (node && ast_xml_node_get_children(node)) { - break; - } +int ast_func_read(struct ast_channel *chan, const char *function, char *workspace, size_t len) +{ + char *copy = ast_strdupa(function); + char *args = func_args(copy); + struct ast_custom_function *acfptr = ast_custom_function_find(copy); - /* We didn't find the application documentation for the specified language, - so, try to load documentation for any language */ - node = ast_xml_get_root(doctree->doc); - if (ast_xml_node_get_children(node)) { - if ((node = ast_xml_find_element(ast_xml_node_get_children(node), type, "name", name))) { - break; - } - } + if (acfptr == NULL) + ast_log(LOG_ERROR, "Function %s not registered\n", copy); + else if (!acfptr->read) + ast_log(LOG_ERROR, "Function %s cannot be read\n", copy); + else { + int res; + struct ast_module_user *u = NULL; + if (acfptr->mod) + u = __ast_module_user_add(acfptr->mod, chan); + res = acfptr->read(chan, copy, args, workspace, len); + if (acfptr->mod && u) + __ast_module_user_remove(acfptr->mod, u); + return res; } - AST_RWLIST_UNLOCK(&xmldoc_tree); - - return node; + return -1; } -/*! \internal - * \brief Helper function used to build the syntax, it allocates the needed buffer (or reallocates it), - * and based on the reverse value it makes use of fmt to print the parameter list inside the - * realloced buffer (syntax). - * \param reverse We are going backwards while generating the syntax? - * \param len Current length of 'syntax' buffer. - * \param syntax Output buffer for the concatenated values. - * \param fmt A format string that will be used in a sprintf call. - */ -static __attribute__((format(printf,4,5))) void xmldoc_reverse_helper(int reverse, int *len, char **syntax, const char *fmt, ...) +int ast_func_write(struct ast_channel *chan, const char *function, const char *value) { - int totlen, tmpfmtlen; - char *tmpfmt, tmp; - va_list ap; + char *copy = ast_strdupa(function); + char *args = func_args(copy); + struct ast_custom_function *acfptr = ast_custom_function_find(copy); - va_start(ap, fmt); - if (ast_vasprintf(&tmpfmt, fmt, ap) < 0) { - va_end(ap); - return; + if (acfptr == NULL) + ast_log(LOG_ERROR, "Function %s not registered\n", copy); + else if (!acfptr->write) + ast_log(LOG_ERROR, "Function %s cannot be written to\n", copy); + else { + int res; + struct ast_module_user *u = NULL; + if (acfptr->mod) + u = __ast_module_user_add(acfptr->mod, chan); + res = acfptr->write(chan, copy, args, value); + if (acfptr->mod && u) + __ast_module_user_remove(acfptr->mod, u); + return res; } - va_end(ap); - tmpfmtlen = strlen(tmpfmt); - totlen = *len + tmpfmtlen + 1; - - *syntax = ast_realloc(*syntax, totlen); - - if (!*syntax) { - ast_free(tmpfmt); - return; - } - - if (reverse) { - memmove(*syntax + tmpfmtlen, *syntax, *len); - /* Save this char, it will be overwritten by the \0 of strcpy. */ - tmp = (*syntax)[0]; - strcpy(*syntax, tmpfmt); - /* Restore the already saved char. */ - (*syntax)[tmpfmtlen] = tmp; - (*syntax)[totlen - 1] = '\0'; - } else { - strcpy(*syntax + *len, tmpfmt); - } - - *len = totlen - 1; - ast_free(tmpfmt); -} - -/*! \internal - * \brief Check if the passed node has tags inside it. - * \param node Root node to search argument elements. - * \retval 1 If a element is found inside 'node'. - * \retval 0 If no is found inside 'node'. - */ -static int xmldoc_has_arguments(struct ast_xml_node *fixnode) -{ - struct ast_xml_node *node = fixnode; - - for (node = ast_xml_node_get_children(fixnode); node; node = ast_xml_node_get_next(node)) { - if (!strcasecmp(ast_xml_node_get_name(node), "argument")) { - return 1; - } - } - return 0; -} - -/*! \internal - * \brief Build the syntax for a specified starting node. - * \param rootnode A pointer to the ast_xml root node. - * \param rootname Name of the application, function, option, etc. to build the syntax. - * \param childname The name of each parameter node. - * \param printparenthesis Boolean if we must print parenthesis if not parameters are found in the rootnode. - * \param printrootname Boolean if we must print the rootname before the syntax and parenthesis at the begining/end. - * \retval NULL on error. - * \retval An ast_malloc'ed string with the syntax generated. - */ -static char *xmldoc_get_syntax(struct ast_xml_node *rootnode, const char *rootname, const char *childname, int printparenthesis, int printrootname) -{ -#define GOTONEXT(__rev, __a) (__rev ? ast_xml_node_get_prev(__a) : ast_xml_node_get_next(__a)) -#define ISLAST(__rev, __a) (__rev == 1 ? (ast_xml_node_get_prev(__a) ? 0 : 1) : (ast_xml_node_get_next(__a) ? 0 : 1)) -#define MP(__a) ((multiple ? __a : "")) - struct ast_xml_node *node = NULL, *firstparam = NULL, *lastparam = NULL; - const char *paramtype, *multipletype, *paramname, *attrargsep, *parenthesis, *argname; - int reverse, required, paramcount = 0, openbrackets = 0, len = 0, hasparams=0; - int reqfinode = 0, reqlanode = 0, optmidnode = 0, prnparenthesis; - char *syntax = NULL, *argsep; - int paramnamemalloc, multiple; - - if (ast_strlen_zero(rootname) || ast_strlen_zero(childname)) { - ast_log(LOG_WARNING, "Tried to look in XML tree with faulty rootname or childname while creating a syntax.\n"); - return NULL; - } - - if (!rootnode || !ast_xml_node_get_children(rootnode)) { - /* If the rootnode field is not found, at least print name. */ - ast_asprintf(&syntax, "%s%s", (printrootname ? rootname : ""), (printparenthesis ? "()" : "")); - return syntax; - } - - /* Get the argument separator from the root node attribute name 'argsep', if not found - defaults to ','. */ - attrargsep = ast_xml_get_attribute(rootnode, "argsep"); - if (attrargsep) { - argsep = ast_strdupa(attrargsep); - ast_xml_free_attr(attrargsep); - } else { - argsep = ast_strdupa(","); - } - - /* Get order of evaluation. */ - for (node = ast_xml_node_get_children(rootnode); node; node = ast_xml_node_get_next(node)) { - if (strcasecmp(ast_xml_node_get_name(node), childname)) { - continue; - } - required = 0; - hasparams = 1; - if ((paramtype = ast_xml_get_attribute(node, "required"))) { - if (ast_true(paramtype)) { - required = 1; - } - ast_xml_free_attr(paramtype); - } - - lastparam = node; - reqlanode = required; - - if (!firstparam) { - /* first parameter node */ - firstparam = node; - reqfinode = required; - } - } - - if (!hasparams) { - /* This application, function, option, etc, doesn't have any params. */ - ast_asprintf(&syntax, "%s%s", (printrootname ? rootname : ""), (printparenthesis ? "()" : "")); - return syntax; - } - - if (reqfinode && reqlanode) { - /* check midnode */ - for (node = ast_xml_node_get_children(rootnode); node; node = ast_xml_node_get_next(node)) { - if (strcasecmp(ast_xml_node_get_name(node), childname)) { - continue; - } - if (node != firstparam && node != lastparam) { - if ((paramtype = ast_xml_get_attribute(node, "required"))) { - if (!ast_true(paramtype)) { - optmidnode = 1; - break; - } - ast_xml_free_attr(paramtype); - } - } - } - } - - if ((!reqfinode && reqlanode) || (reqfinode && reqlanode && optmidnode)) { - reverse = 1; - node = lastparam; - } else { - reverse = 0; - node = firstparam; - } - - /* init syntax string. */ - if (reverse) { - xmldoc_reverse_helper(reverse, &len, &syntax, - (printrootname ? (printrootname == 2 ? ")]" : ")"): "")); - } else { - xmldoc_reverse_helper(reverse, &len, &syntax, "%s%s", (printrootname ? rootname : ""), - (printrootname ? (printrootname == 2 ? "[(" : "(") : "")); - } - - for (; node; node = GOTONEXT(reverse, node)) { - if (strcasecmp(ast_xml_node_get_name(node), childname)) { - continue; - } - - /* Get the argument name, if it is not the leaf, go inside that parameter. */ - if (xmldoc_has_arguments(node)) { - parenthesis = ast_xml_get_attribute(node, "hasparams"); - prnparenthesis = 0; - if (parenthesis) { - prnparenthesis = ast_true(parenthesis); - if (!strcasecmp(parenthesis, "optional")) { - prnparenthesis = 2; - } - ast_xml_free_attr(parenthesis); - } - argname = ast_xml_get_attribute(node, "name"); - if (argname) { - paramname = xmldoc_get_syntax(node, argname, "argument", prnparenthesis, prnparenthesis); - ast_xml_free_attr(argname); - paramnamemalloc = 1; - } else { - /* Malformed XML, print **UNKOWN** */ - paramname = ast_strdup("**unknown**"); - } - paramnamemalloc = 1; - } else { - paramnamemalloc = 0; - paramname = ast_xml_get_attribute(node, "name"); - if (!paramname) { - ast_log(LOG_WARNING, "Malformed XML %s: no %s name\n", rootname, childname); - if (syntax) { - /* Free already allocated syntax */ - ast_free(syntax); - } - /* to give up is ok? */ - ast_asprintf(&syntax, "%s%s", (printrootname ? rootname : ""), (printparenthesis ? "()" : "")); - return syntax; - } - } - - /* Defaults to 'false'. */ - multiple = 0; - if ((multipletype = ast_xml_get_attribute(node, "multiple"))) { - if (ast_true(multipletype)) { - multiple = 1; - } - ast_xml_free_attr(multipletype); - } - - required = 0; /* Defaults to 'false'. */ - if ((paramtype = ast_xml_get_attribute(node, "required"))) { - if (ast_true(paramtype)) { - required = 1; - } - ast_xml_free_attr(paramtype); - } - - /* build syntax core. */ - - if (required) { - /* First parameter */ - if (!paramcount) { - xmldoc_reverse_helper(reverse, &len, &syntax, "%s%s%s%s", paramname, MP("["), MP(argsep), MP("...]")); - } else { - /* Time to close open brackets. */ - while (openbrackets > 0) { - xmldoc_reverse_helper(reverse, &len, &syntax, (reverse ? "[" : "]")); - openbrackets--; - } - if (reverse) { - xmldoc_reverse_helper(reverse, &len, &syntax, "%s%s", paramname, argsep); - } else { - xmldoc_reverse_helper(reverse, &len, &syntax, "%s%s", argsep, paramname); - } - xmldoc_reverse_helper(reverse, &len, &syntax, "%s%s%s", MP("["), MP(argsep), MP("...]")); - } - } else { - /* First parameter */ - if (!paramcount) { - xmldoc_reverse_helper(reverse, &len, &syntax, "[%s%s%s%s]", paramname, MP("["), MP(argsep), MP("...]")); - } else { - if (ISLAST(reverse, node)) { - /* This is the last parameter. */ - if (reverse) { - xmldoc_reverse_helper(reverse, &len, &syntax, "[%s%s%s%s]%s", paramname, - MP("["), MP(argsep), MP("...]"), argsep); - } else { - xmldoc_reverse_helper(reverse, &len, &syntax, "%s[%s%s%s%s]", argsep, paramname, - MP("["), MP(argsep), MP("...]")); - } - } else { - if (reverse) { - xmldoc_reverse_helper(reverse, &len, &syntax, "%s%s%s%s%s]", paramname, argsep, - MP("["), MP(argsep), MP("...]")); - } else { - xmldoc_reverse_helper(reverse, &len, &syntax, "[%s%s%s%s%s", argsep, paramname, - MP("["), MP(argsep), MP("...]")); - } - openbrackets++; - } - } - } - if (paramnamemalloc) { - ast_free((char *) paramname); - } else { - ast_xml_free_attr(paramname); - } - - paramcount++; - } - - /* Time to close open brackets. */ - while (openbrackets > 0) { - xmldoc_reverse_helper(reverse, &len, &syntax, (reverse ? "[" : "]")); - openbrackets--; - } - - /* close syntax string. */ - if (reverse) { - xmldoc_reverse_helper(reverse, &len, &syntax, "%s%s", (printrootname ? rootname : ""), - (printrootname ? (printrootname == 2 ? "[(" : "(") : "")); - } else { - xmldoc_reverse_helper(reverse, &len, &syntax, (printrootname ? (printrootname == 2 ? ")]" : ")") : "")); - } - - return syntax; -#undef ISLAST -#undef GOTONEXT -#undef MP -} - -/*! \internal - * \brief Get the syntax for a specified application or function. - * \param type Application or Function ? - * \param name Name of the application or function. - * \retval NULL on error. - * \retval The generated syntax in a ast_malloc'ed string. - */ -static char *xmldoc_build_syntax(const char *type, const char *name) -{ - struct ast_xml_node *node; - char *syntax = NULL; - - node = xmldoc_get_node(type, name, documentation_language); - if (!node) { - return NULL; - } - - for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) { - if (!strcasecmp(ast_xml_node_get_name(node), "syntax")) { - break; - } - } - - if (node) { - syntax = xmldoc_get_syntax(node, name, "parameter", 1, 1); - } - return syntax; -} - -/*! \internal - * \brief Parse a element. - * \param node The element pointer. - * \param tabs Added this string before the content of the element. - * \param posttabs Added this string after the content of the element. - * \param buffer This must be an already allocated ast_str. It will be used - * to store the result (if already has something it will be appended to the current - * string). - * \retval 1 If 'node' is a named 'para'. - * \retval 2 If data is appended in buffer. - * \retval 0 on error. - */ -static int xmldoc_parse_para(struct ast_xml_node *node, const char *tabs, const char *posttabs, struct ast_str **buffer) -{ - const char *tmptext; - struct ast_xml_node *tmp; - int ret = 0; - struct ast_str *tmpstr; - - if (!node || !ast_xml_node_get_children(node)) { - return ret; - } - - if (strcasecmp(ast_xml_node_get_name(node), "para")) { - return ret; - } - - ast_str_append(buffer, 0, "%s", tabs); - - ret = 1; - - for (tmp = ast_xml_node_get_children(node); tmp; tmp = ast_xml_node_get_next(tmp)) { - /* Get the text inside the element and append it to buffer. */ - tmptext = ast_xml_get_text(tmp); - if (tmptext) { - /* Strip \n etc. */ - xmldoc_string_cleanup(tmptext, &tmpstr, 0); - ast_xml_free_text(tmptext); - if (tmpstr) { - if (strcasecmp(ast_xml_node_get_name(tmp), "text")) { - ast_str_append(buffer, 0, "<%s>%s", ast_xml_node_get_name(tmp), - tmpstr->str, ast_xml_node_get_name(tmp)); - } else { - ast_str_append(buffer, 0, "%s", tmpstr->str); - } - ast_free(tmpstr); - ret = 2; - } - } - } - - ast_str_append(buffer, 0, "%s", posttabs); - - return ret; -} - -/*! \internal - * \brief Parse special elements defined in 'struct special_tags' special elements must have a element inside them. - * \param fixnode special tag node pointer. - * \param tabs put tabs before printing the node content. - * \param posttabs put posttabs after printing node content. - * \param buffer Output buffer, the special tags will be appended here. - * \retval 0 if no special element is parsed. - * \retval 1 if a special element is parsed (data is appended to buffer). - * \retval 2 if a special element is parsed and also a element is parsed inside the specialtag. - */ -static int xmldoc_parse_specialtags(struct ast_xml_node *fixnode, const char *tabs, const char *posttabs, struct ast_str **buffer) -{ - struct ast_xml_node *node = fixnode; - int ret = 0, i, count = 0; - - if (!node || !ast_xml_node_get_children(node)) { - return ret; - } - - for (i = 0; i < ARRAY_LEN(special_tags); i++) { - if (strcasecmp(ast_xml_node_get_name(node), special_tags[i].tagname)) { - continue; - } - - ret = 1; - /* This is a special tag. */ - - /* concat data */ - if (!ast_strlen_zero(special_tags[i].init)) { - ast_str_append(buffer, 0, "%s%s", tabs, special_tags[i].init); - } - - /* parse elements inside special tags. */ - for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) { - /* first just print it without tabs at the begining. */ - if (xmldoc_parse_para(node, (!count ? "" : tabs), posttabs, buffer) == 2) { - ret = 2; - } - } - - if (!ast_strlen_zero(special_tags[i].end)) { - ast_str_append(buffer, 0, "%s%s", special_tags[i].end, posttabs); - } - - break; - } - - return ret; -} - -/*! \internal - * \brief Parse an element from the xml documentation. - * \param fixnode Pointer to the 'argument' xml node. - * \param insideparameter If we are parsing an inside a . - * \param paramtabs pre tabs if we are inside a parameter element. - * \param tabs What to be printed before the argument name. - * \param buffer Output buffer to put values found inside the element. - * \retval 1 If there is content inside the argument. - * \retval 0 If the argument element is not parsed, or there is no content inside it. - */ -static int xmldoc_parse_argument(struct ast_xml_node *fixnode, int insideparameter, const char *paramtabs, const char *tabs, struct ast_str **buffer) -{ - struct ast_xml_node *node = fixnode; - const char *argname; - int count = 0, ret = 0; - - if (!node || !ast_xml_node_get_children(node)) { - return ret; - } - - /* Print the argument names */ - argname = ast_xml_get_attribute(node, "name"); - if (!argname) { - return 0; - } - ast_str_append(buffer, 0, "%s%s%s", tabs, argname, (insideparameter ? "\n" : "")); - ast_xml_free_attr(argname); - - for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) { - if (xmldoc_parse_para(node, (insideparameter ? paramtabs : (!count ? " - " : tabs)), "\n", buffer) == 2) { - count++; - ret = 1; - } else if (xmldoc_parse_specialtags(node, (insideparameter ? paramtabs : (!count ? " - " : tabs)), "\n", buffer) == 2) { - count++; - ret = 1; - } - } - - return ret; -} - -/*! \internal - * \brief Parse a node inside a node. - * \param node The variable node to parse. - * \param tabs A string to be appended at the begining of the output that will be stored - * in buffer. - * \param buffer This must be an already created ast_str. It will be used - * to store the result (if already has something it will be appended to the current - * string). - * \retval 0 if no data is appended. - * \retval 1 if data is appended. - */ -static int xmldoc_parse_variable(struct ast_xml_node *node, const char *tabs, struct ast_str **buffer) -{ - struct ast_xml_node *tmp; - const char *valname; - const char *tmptext; - struct ast_str *cleanstr; - int ret = 0, printedpara=0; - - for (tmp = ast_xml_node_get_children(node); tmp; tmp = ast_xml_node_get_next(tmp)) { - if (xmldoc_parse_para(tmp, (ret ? tabs : ""), "\n", buffer)) { - printedpara = 1; - continue; - } else if (xmldoc_parse_specialtags(tmp, (ret ? tabs : ""), "\n", buffer)) { - printedpara = 1; - continue; - } - - if (strcasecmp(ast_xml_node_get_name(tmp), "value")) { - continue; - } - - /* Parse a tag only. */ - if (!printedpara) { - ast_str_append(buffer, 0, "\n"); - printedpara = 1; - } - /* Parse each desciption */ - valname = ast_xml_get_attribute(tmp, "name"); - if (valname) { - ret = 1; - ast_str_append(buffer, 0, "%s%s", tabs, valname); - ast_xml_free_attr(valname); - } - tmptext = ast_xml_get_text(tmp); - /* Check inside this node for any explanation about its meaning. */ - if (tmptext) { - /* Cleanup text. */ - xmldoc_string_cleanup(tmptext, &cleanstr, 1); - ast_xml_free_text(tmptext); - if (cleanstr && cleanstr->used > 0) { - ast_str_append(buffer, 0, ":%s", cleanstr->str); - } - ast_free(cleanstr); - } - ast_str_append(buffer, 0, "\n"); - } - - return ret; -} - -/*! \internal - * \brief Parse a node and put all the output inside 'buffer'. - * \param node The variablelist node pointer. - * \param tabs A string to be appended at the begining of the output that will be stored - * in buffer. - * \param buffer This must be an already created ast_str. It will be used - * to store the result (if already has something it will be appended to the current - * string). - * \retval 1 If a element is parsed. - * \retval 0 On error. - */ -static int xmldoc_parse_variablelist(struct ast_xml_node *node, const char *tabs, struct ast_str **buffer) -{ - struct ast_xml_node *tmp; - const char *varname; - char *vartabs; - int ret = 0; - - if (!node || !ast_xml_node_get_children(node)) { - return ret; - } - - if (strcasecmp(ast_xml_node_get_name(node), "variablelist")) { - return ret; - } - - /* use this spacing (add 4 spaces) inside a variablelist node. */ - ast_asprintf(&vartabs, "%s ", tabs); - if (!vartabs) { - return ret; - } - for (tmp = ast_xml_node_get_children(node); tmp; tmp = ast_xml_node_get_next(tmp)) { - /* We can have a element inside the variable list */ - if ((xmldoc_parse_para(tmp, (ret ? tabs : ""), "\n", buffer))) { - ret = 1; - continue; - } else if ((xmldoc_parse_specialtags(tmp, (ret ? tabs : ""), "\n", buffer))) { - ret = 1; - continue; - } - - if (!strcasecmp(ast_xml_node_get_name(tmp), "variable")) { - /* Store the variable name in buffer. */ - varname = ast_xml_get_attribute(tmp, "name"); - if (varname) { - ast_str_append(buffer, 0, "%s%s: ", tabs, varname); - ast_xml_free_attr(varname); - /* Parse the possible values. */ - xmldoc_parse_variable(tmp, vartabs, buffer); - ret = 1; - } - } - } - - ast_free(vartabs); - - return ret; -} - -/*! \internal - * \brief Parse the node content. - * \param type 'application' or 'function'. - * \param name Application or functions name. - * \retval NULL on error. - * \retval Content of the see-also node. - */ -static char *xmldoc_build_seealso(const char *type, const char *name) -{ - struct ast_str *outputstr; - char *output; - struct ast_xml_node *node; - const char *typename; - const char *content; - int first = 1; - - if (ast_strlen_zero(type) || ast_strlen_zero(name)) { - return NULL; - } - - /* get the application/function root node. */ - node = xmldoc_get_node(type, name, documentation_language); - if (!node || !ast_xml_node_get_children(node)) { - return NULL; - } - - /* Find the node. */ - for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) { - if (!strcasecmp(ast_xml_node_get_name(node), "see-also")) { - break; - } - } - - if (!node || !ast_xml_node_get_children(node)) { - /* we couldnt find a node. */ - return NULL; - } - - /* prepare the output string. */ - outputstr = ast_str_create(128); - if (!outputstr) { - return NULL; - } - - /* get into the node. */ - for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) { - if (strcasecmp(ast_xml_node_get_name(node), "ref")) { - continue; - } - - /* parse the node. 'type' attribute is required. */ - typename = ast_xml_get_attribute(node, "type"); - if (!typename) { - continue; - } - content = ast_xml_get_text(node); - if (!content) { - ast_xml_free_attr(typename); - continue; - } - if (!strcasecmp(typename, "application")) { - ast_str_append(&outputstr, 0, "%s%s()", (first ? "" : ", "), content); - } else if (!strcasecmp(typename, "function")) { - ast_str_append(&outputstr, 0, "%s%s", (first ? "" : ", "), content); - } else if (!strcasecmp(typename, "astcli")) { - ast_str_append(&outputstr, 0, "%s%s", (first ? "" : ", "), content); - } else { - ast_str_append(&outputstr, 0, "%s%s", (first ? "" : ", "), content); - } - first = 0; - ast_xml_free_text(content); - } - - output = ast_strdup(outputstr->str); - ast_free(outputstr); - - return output; -} - -/*! \internal - * \brief Parse a node. - * \brief fixnode An ast_xml_node pointer to the node. - * \bried buffer The output buffer. - * \retval 0 if content is not found inside the enum element (data is not appended to buffer). - * \retval 1 if content is found and data is appended to buffer. - */ -static int xmldoc_parse_enum(struct ast_xml_node *fixnode, const char *tabs, struct ast_str **buffer) -{ - struct ast_xml_node *node = fixnode; - int ret = 0; - - for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) { - if ((xmldoc_parse_para(node, (ret ? tabs : " - "), "\n", buffer))) { - ret = 1; - } else if ((xmldoc_parse_specialtags(node, (ret ? tabs : " - "), "\n", buffer))) { - ret = 1; - } - } - return ret; -} - -/*! \internal - * \brief Parse a node. - * \param fixnode As ast_xml pointer to the node. - * \param buffer The ast_str output buffer. - * \retval 0 if no node was parsed. - * \retval 1 if a node was parsed. - */ -static int xmldoc_parse_enumlist(struct ast_xml_node *fixnode, const char *tabs, struct ast_str **buffer) -{ - struct ast_xml_node *node = fixnode; - const char *enumname; - int ret = 0; - - for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) { - if (strcasecmp(ast_xml_node_get_name(node), "enum")) { - continue; - } - - enumname = ast_xml_get_attribute(node, "name"); - if (enumname) { - ast_str_append(buffer, 0, "%s%s", tabs, enumname); - ast_xml_free_attr(enumname); - - /* parse only enum elements inside a enumlist node. */ - if ((xmldoc_parse_enum(node, tabs, buffer))) { - ret = 1; - } else { - ast_str_append(buffer, 0, "\n"); - } - } - } - return ret; -} - -/*! \internal - * \brief Parse an