From 4445ee7fc093810e791e9855815f07da2a60619d Mon Sep 17 00:00:00 2001 From: Kinsey Moore Date: Thu, 24 Jul 2014 13:00:59 +0000 Subject: AMI: Allow for command response documentation Allow for responses to AMI actions/commands to be documented properly in XML and displayed via the CLI. Response events are documented exactly as standard AMI events are documented. Review: https://reviewboard.asterisk.org/r/3812/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@419342 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- doc/appdocsxml.dtd | 6 +- include/asterisk/manager.h | 4 + include/asterisk/xmldoc.h | 37 ++++++++- main/config_options.c | 16 ++-- main/manager.c | 109 ++++++++++++++++++--------- main/manager_bridges.c | 20 +++++ main/xmldoc.c | 181 +++++++++++++++++++++++++++++++++++++++------ 7 files changed, 304 insertions(+), 69 deletions(-) diff --git a/doc/appdocsxml.dtd b/doc/appdocsxml.dtd index a81b64400..65167d559 100644 --- a/doc/appdocsxml.dtd +++ b/doc/appdocsxml.dtd @@ -26,11 +26,15 @@ - + + + + + diff --git a/include/asterisk/manager.h b/include/asterisk/manager.h index 66dafb1be..fad7af1e7 100644 --- a/include/asterisk/manager.h +++ b/include/asterisk/manager.h @@ -148,6 +148,10 @@ struct manager_action { AST_STRING_FIELD(arguments); /*!< Description of each argument. */ AST_STRING_FIELD(seealso); /*!< See also */ ); + /*! Possible list element response events. */ + struct ast_xml_doc_item *list_responses; + /*! Final response event. */ + struct ast_xml_doc_item *final_response; /*! Permission required for action. EVENT_FLAG_* */ int authority; /*! Function to be called */ diff --git a/include/asterisk/xmldoc.h b/include/asterisk/xmldoc.h index c09f693c8..782fa1e94 100644 --- a/include/asterisk/xmldoc.h +++ b/include/asterisk/xmldoc.h @@ -37,6 +37,13 @@ enum ast_doc_src { struct ao2_container; struct ast_xml_node; +/*! + * \brief The struct to be used as the head of an ast_xml_doc_item list + * when being manipulated + * \since 13.0.0 + */ +AST_LIST_HEAD(ast_xml_doc_item_list, ast_xml_doc_item); + /*! \brief Struct that contains the XML documentation for a particular item. Note * that this is an ao2 ref counted object. * @@ -70,7 +77,7 @@ struct ast_xml_doc_item { */ struct ast_xml_node *node; /*! The next XML documentation item that matches the same name/item type */ - struct ast_xml_doc_item *next; + AST_LIST_ENTRY(ast_xml_doc_item) next; }; /*! \brief Execute an XPath query on the loaded XML documentation @@ -114,6 +121,34 @@ char *ast_xmldoc_build_seealso(const char *type, const char *name, const char *m */ char *ast_xmldoc_build_arguments(const char *type, const char *name, const char *module); +/*! + * \brief Generate the [final response] 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 'responses' tag. + * \param module The module the item is in (optional, can be NULL) + * + * \return An XMLDoc item list with the [final response] tag content. + * + * \since 13.0.0 + */ +struct ast_xml_doc_item *ast_xmldoc_build_final_response(const char *type, const char *name, const char *module); + +/*! + * \brief Generate the [list responses] 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 'responses' tag. + * \param module The module the item is in (optional, can be NULL) + * + * \return An XMLDoc item list with the [list responses] tag content. + * + * \since 13.0.0 + */ +struct ast_xml_doc_item *ast_xmldoc_build_list_responses(const char *type, const char *name, const char *module); + /*! * \brief Colorize and put delimiters (instead of tags) to the xmldoc output. * \param bwinput Not colorized input with tags. diff --git a/main/config_options.c b/main/config_options.c index c86db0247..89b19e05b 100644 --- a/main/config_options.c +++ b/main/config_options.c @@ -249,7 +249,7 @@ static struct ast_xml_doc_item *find_xmldoc_option(struct ast_xml_doc_item *conf return NULL; } /* First is just the configInfo, we can skip it */ - while ((iter = iter->next)) { + while ((iter = AST_LIST_NEXT(iter, next))) { size_t x; if (strcasecmp(iter->name, name)) { continue; @@ -274,7 +274,7 @@ static struct ast_xml_doc_item *find_xmldoc_type(struct ast_xml_doc_item *config return NULL; } /* First is just the config Info, skip it */ - while ((iter = iter->next)) { + while ((iter = AST_LIST_NEXT(iter, next))) { if (!strcasecmp(iter->type, "configObject") && !strcasecmp(iter->name, name)) { break; } @@ -915,7 +915,7 @@ static char *complete_config_type(const char *module, const char *word, int pos, } cur = info; - while ((cur = cur->next)) { + while ((cur = AST_LIST_NEXT(cur, next))) { if (!strcasecmp(cur->type, "configObject") && !strncasecmp(word, cur->name, wordlen) && ++which > state) { c = ast_strdup(cur->name); break; @@ -944,7 +944,7 @@ static char *complete_config_option(const char *module, const char *option, cons } cur = info; - while ((cur = cur->next)) { + while ((cur = AST_LIST_NEXT(cur, next))) { if (!strcasecmp(cur->type, "configOption") && !strcasecmp(cur->ref, option) && !strncasecmp(word, cur->name, wordlen) && ++which > state) { c = ast_strdup(cur->name); break; @@ -1109,7 +1109,7 @@ static void cli_show_module_types(struct ast_cli_args *a) tmp = item; ast_cli(a->fd, "Configuration option types for %s:\n", tmp->name); - while ((tmp = tmp->next)) { + while ((tmp = AST_LIST_NEXT(tmp, next))) { if (!strcasecmp(tmp->type, "configObject")) { ast_cli(a->fd, "%-25s -- %-65.65s\n", tmp->name, ast_str_buffer(tmp->synopsis)); @@ -1135,7 +1135,7 @@ static void cli_show_module_type(struct ast_cli_args *a) } tmp = item; - while ((tmp = tmp->next)) { + while ((tmp = AST_LIST_NEXT(tmp, next))) { if (!strcasecmp(tmp->type, "configObject") && !strcasecmp(tmp->name, a->argv[4])) { match = 1; term_color(option_type, tmp->name, COLOR_MAGENTA, COLOR_BLACK, sizeof(option_type)); @@ -1161,7 +1161,7 @@ static void cli_show_module_type(struct ast_cli_args *a) /* Now iterate over the options for the type */ tmp = item; - while ((tmp = tmp->next)) { + while ((tmp = AST_LIST_NEXT(tmp, next))) { if (!strcasecmp(tmp->type, "configOption") && !strcasecmp(tmp->ref, a->argv[4])) { ast_cli(a->fd, "%-25s -- %-65.65s\n", tmp->name, ast_str_buffer(tmp->synopsis)); @@ -1186,7 +1186,7 @@ static void cli_show_module_options(struct ast_cli_args *a) return; } tmp = item; - while ((tmp = tmp->next)) { + while ((tmp = AST_LIST_NEXT(tmp, next))) { if (!strcasecmp(tmp->type, "configOption") && !strcasecmp(tmp->ref, a->argv[4]) && !strcasecmp(tmp->name, a->argv[5])) { if (match) { ast_cli(a->fd, "\n"); diff --git a/main/manager.c b/main/manager.c index f5d00d1d3..47a909b68 100644 --- a/main/manager.c +++ b/main/manager.c @@ -1912,6 +1912,8 @@ static int manager_displayconnects(struct mansession_session *session) return ret; } +static void print_event_instance(struct ast_cli_args *a, struct ast_xml_doc_item *instance); + static char *handle_showmancmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { struct manager_action *cur; @@ -1919,7 +1921,8 @@ static char *handle_showmancmd(struct ast_cli_entry *e, int cmd, struct ast_cli_ int num, l, which; char *ret = NULL; #ifdef AST_XML_DOCS - char syntax_title[64], description_title[64], synopsis_title[64], seealso_title[64], arguments_title[64], privilege_title[64]; + char syntax_title[64], description_title[64], synopsis_title[64], seealso_title[64]; + char arguments_title[64], privilege_title[64], final_response_title[64], list_responses_title[64]; #endif switch (cmd) { @@ -1955,6 +1958,8 @@ static char *handle_showmancmd(struct ast_cli_entry *e, int cmd, struct ast_cli_ term_color(seealso_title, "[See Also]\n", COLOR_MAGENTA, 0, 40); term_color(arguments_title, "[Arguments]\n", COLOR_MAGENTA, 0, 40); term_color(privilege_title, "[Privilege]\n", COLOR_MAGENTA, 0, 40); + term_color(final_response_title, "[Final Response]\n", COLOR_MAGENTA, 0, 40); + term_color(list_responses_title, "[List Responses]\n", COLOR_MAGENTA, 0, 40); #endif AST_RWLIST_RDLOCK(&actions); @@ -1971,13 +1976,34 @@ static char *handle_showmancmd(struct ast_cli_entry *e, int cmd, struct ast_cli_ char *arguments = ast_xmldoc_printable(S_OR(cur->arguments, "Not available"), 1); char *seealso = ast_xmldoc_printable(S_OR(cur->seealso, "Not available"), 1); char *privilege = ast_xmldoc_printable(S_OR(authority->str, "Not available"), 1); - ast_cli(a->fd, "%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n", + char *responses = ast_xmldoc_printable("None", 1); + ast_cli(a->fd, "%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n%s", syntax_title, syntax, synopsis_title, synopsis, description_title, description, arguments_title, arguments, seealso_title, seealso, - privilege_title, privilege); + privilege_title, privilege, + list_responses_title); + + if (!cur->list_responses) { + ast_cli(a->fd, "%s\n\n", responses); + } else { + struct ast_xml_doc_item *temp; + for (temp = cur->list_responses; temp; temp = AST_LIST_NEXT(temp, next)) { + ast_cli(a->fd, "Event: %s\n", temp->name); + print_event_instance(a, temp); + } + } + + ast_cli(a->fd, "%s", final_response_title); + + if (!cur->final_response) { + ast_cli(a->fd, "%s\n\n", responses); + } else { + ast_cli(a->fd, "Event: %s\n", cur->final_response->name); + print_event_instance(a, cur->final_response); + } } else #endif { @@ -6309,6 +6335,8 @@ static void action_destroy(void *obj) /* The string fields were initialized. */ ast_string_field_free_memory(doomed); } + ao2_cleanup(doomed->final_response); + ao2_cleanup(doomed->list_responses); } /*! \brief register a new command with manager, including online help. This is @@ -6354,6 +6382,9 @@ int ast_manager_register2(const char *action, int auth, int (*func)(struct manse ast_string_field_set(cur, arguments, tmpxml); ast_free(tmpxml); + cur->final_response = ast_xmldoc_build_final_response("manager", action, NULL); + cur->list_responses = ast_xmldoc_build_list_responses("manager", action, NULL); + cur->docsrc = AST_XML_DOC; } else #endif @@ -7745,6 +7776,43 @@ static char *handle_manager_show_events(struct ast_cli_entry *e, int cmd, struct return CLI_SUCCESS; } +static void print_event_instance(struct ast_cli_args *a, struct ast_xml_doc_item *instance) +{ + char syntax_title[64], description_title[64], synopsis_title[64], seealso_title[64], arguments_title[64]; + + term_color(synopsis_title, "[Synopsis]\n", COLOR_MAGENTA, 0, 40); + term_color(description_title, "[Description]\n", COLOR_MAGENTA, 0, 40); + term_color(syntax_title, "[Syntax]\n", COLOR_MAGENTA, 0, 40); + term_color(seealso_title, "[See Also]\n", COLOR_MAGENTA, 0, 40); + term_color(arguments_title, "[Arguments]\n", COLOR_MAGENTA, 0, 40); + + if (!ast_strlen_zero(ast_str_buffer(instance->synopsis))) { + char *synopsis = ast_xmldoc_printable(ast_str_buffer(instance->synopsis), 1); + ast_cli(a->fd, "%s%s\n\n", synopsis_title, synopsis); + ast_free(synopsis); + } + if (!ast_strlen_zero(ast_str_buffer(instance->syntax))) { + char *syntax = ast_xmldoc_printable(ast_str_buffer(instance->syntax), 1); + ast_cli(a->fd, "%s%s\n\n", syntax_title, syntax); + ast_free(syntax); + } + if (!ast_strlen_zero(ast_str_buffer(instance->description))) { + char *description = ast_xmldoc_printable(ast_str_buffer(instance->description), 1); + ast_cli(a->fd, "%s%s\n\n", description_title, description); + ast_free(description); + } + if (!ast_strlen_zero(ast_str_buffer(instance->arguments))) { + char *arguments = ast_xmldoc_printable(ast_str_buffer(instance->arguments), 1); + ast_cli(a->fd, "%s%s\n\n", arguments_title, arguments); + ast_free(arguments); + } + if (!ast_strlen_zero(ast_str_buffer(instance->seealso))) { + char *seealso = ast_xmldoc_printable(ast_str_buffer(instance->seealso), 1); + ast_cli(a->fd, "%s%s\n\n", seealso_title, seealso); + ast_free(seealso); + } +} + static char *handle_manager_show_event(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { RAII_VAR(struct ao2_container *, events, NULL, ao2_cleanup); @@ -7753,7 +7821,6 @@ static char *handle_manager_show_event(struct ast_cli_entry *e, int cmd, struct int length; int which; char *match = NULL; - char syntax_title[64], description_title[64], synopsis_title[64], seealso_title[64], arguments_title[64]; if (cmd == CLI_INIT) { e->command = "manager show event"; @@ -7794,39 +7861,9 @@ static char *handle_manager_show_event(struct ast_cli_entry *e, int cmd, struct return CLI_SUCCESS; } - term_color(synopsis_title, "[Synopsis]\n", COLOR_MAGENTA, 0, 40); - term_color(description_title, "[Description]\n", COLOR_MAGENTA, 0, 40); - term_color(syntax_title, "[Syntax]\n", COLOR_MAGENTA, 0, 40); - term_color(seealso_title, "[See Also]\n", COLOR_MAGENTA, 0, 40); - term_color(arguments_title, "[Arguments]\n", COLOR_MAGENTA, 0, 40); - ast_cli(a->fd, "Event: %s\n", a->argv[3]); - for (temp = item; temp; temp = temp->next) { - if (!ast_strlen_zero(ast_str_buffer(temp->synopsis))) { - char *synopsis = ast_xmldoc_printable(ast_str_buffer(temp->synopsis), 1); - ast_cli(a->fd, "%s%s\n\n", synopsis_title, synopsis); - ast_free(synopsis); - } - if (!ast_strlen_zero(ast_str_buffer(temp->syntax))) { - char *syntax = ast_xmldoc_printable(ast_str_buffer(temp->syntax), 1); - ast_cli(a->fd, "%s%s\n\n", syntax_title, syntax); - ast_free(syntax); - } - if (!ast_strlen_zero(ast_str_buffer(temp->description))) { - char *description = ast_xmldoc_printable(ast_str_buffer(temp->description), 1); - ast_cli(a->fd, "%s%s\n\n", description_title, description); - ast_free(description); - } - if (!ast_strlen_zero(ast_str_buffer(temp->arguments))) { - char *arguments = ast_xmldoc_printable(ast_str_buffer(temp->arguments), 1); - ast_cli(a->fd, "%s%s\n\n", arguments_title, arguments); - ast_free(arguments); - } - if (!ast_strlen_zero(ast_str_buffer(temp->seealso))) { - char *seealso = ast_xmldoc_printable(ast_str_buffer(temp->seealso), 1); - ast_cli(a->fd, "%s%s\n\n", seealso_title, seealso); - ast_free(seealso); - } + for (temp = item; temp; temp = AST_LIST_NEXT(temp, next)) { + print_event_instance(a, temp); } ao2_ref(item, -1); diff --git a/main/manager_bridges.c b/main/manager_bridges.c index 63a927246..26b0e7882 100644 --- a/main/manager_bridges.c +++ b/main/manager_bridges.c @@ -100,6 +100,26 @@ static struct stasis_message_router *bridge_state_router; Returns detailed information about a bridge and the channels in it. + + + + + Information about a channel in a bridge. + + + + + + + + + Information about a bridge. + + + + + + diff --git a/main/xmldoc.c b/main/xmldoc.c index 28bea86bd..a84b05982 100644 --- a/main/xmldoc.c +++ b/main/xmldoc.c @@ -2300,9 +2300,9 @@ static void ast_xml_doc_item_destructor(void *obj) ast_free(doc->description); ast_string_field_free_memory(doc); - if (doc->next) { - ao2_ref(doc->next, -1); - doc->next = NULL; + if (AST_LIST_NEXT(doc, next)) { + ao2_ref(AST_LIST_NEXT(doc, next), -1); + AST_LIST_NEXT(doc, next) = NULL; } } @@ -2428,6 +2428,139 @@ static struct ast_xml_doc_item *xmldoc_build_documentation_item(struct ast_xml_n return item; } +/*! + * \internal + * \brief Build the list responses for an item + * + * \param manager_action The action node to parse + * + * \note This method exists for when you already have the node. This + * prevents having to lock the documentation tree twice + * + * \retval A list of ast_xml_doc_items + * \retval NULL on failure + * + * \since 13.0.0 + */ +static struct ast_xml_doc_item *xmldoc_build_list_responses(struct ast_xml_node *manager_action) +{ + struct ast_xml_node *event; + struct ast_xml_node *responses; + struct ast_xml_node *list_elements; + struct ast_xml_doc_item_list root; + + AST_LIST_HEAD_INIT(&root); + + responses = ast_xml_find_element(ast_xml_node_get_children(manager_action), "responses", NULL, NULL); + if (!responses) { + return NULL; + } + + list_elements = ast_xml_find_element(ast_xml_node_get_children(responses), "list-elements", NULL, NULL); + if (!list_elements) { + return NULL; + } + + /* Iterate over managerEvent nodes */ + for (event = ast_xml_node_get_children(list_elements); event; event = ast_xml_node_get_next(event)) { + struct ast_xml_node *event_instance; + const char *name = ast_xml_get_attribute(event, "name"); + struct ast_xml_doc_item *new_item; + + if (!name || strcmp(ast_xml_node_get_name(event), "managerEvent")) { + continue; + } + + event_instance = ast_xml_find_element(ast_xml_node_get_children(event), + "managerEventInstance", NULL, NULL); + new_item = xmldoc_build_documentation_item(event_instance, name, "managerEvent"); + if (!new_item) { + ao2_cleanup(AST_LIST_FIRST(&root)); + return NULL; + } + + AST_LIST_INSERT_TAIL(&root, new_item, next); + } + + return AST_LIST_FIRST(&root); +} + +struct ast_xml_doc_item *ast_xmldoc_build_list_responses(const char *type, const char *name, const char *module) +{ + struct ast_xml_node *node; + + if (ast_strlen_zero(type) || ast_strlen_zero(name)) { + return NULL; + } + + node = xmldoc_get_node(type, name, module, documentation_language); + + if (!node || !ast_xml_node_get_children(node)) { + return NULL; + } + + return xmldoc_build_list_responses(node); +} + +/*! + * \internal + * \brief Build the final response for an item + * + * \param manager_action The action node to parse + * + * \note This method exists for when you already have the node. This + * prevents having to lock the documentation tree twice + * + * \retval An ast_xml_doc_item + * \retval NULL on failure + * + * \since 13.0.0 + */ +static struct ast_xml_doc_item *xmldoc_build_final_response(struct ast_xml_node *manager_action) +{ + struct ast_xml_node *responses; + struct ast_xml_node *final_response_event; + struct ast_xml_node *event_instance; + + responses = ast_xml_find_element(ast_xml_node_get_children(manager_action), + "responses", NULL, NULL); + if (!responses) { + return NULL; + } + + final_response_event = ast_xml_find_element(ast_xml_node_get_children(responses), + "managerEvent", NULL, NULL); + if (!final_response_event) { + return NULL; + } + + event_instance = ast_xml_find_element(ast_xml_node_get_children(final_response_event), + "managerEventInstance", NULL, NULL); + if (!event_instance) { + return NULL; + } + + return xmldoc_build_documentation_item(event_instance, + ast_xml_get_attribute(final_response_event, "name"), "managerEvent"); +} + +struct ast_xml_doc_item *ast_xmldoc_build_final_response(const char *type, const char *name, const char *module) +{ + struct ast_xml_node *node; + + if (ast_strlen_zero(type) || ast_strlen_zero(name)) { + return NULL; + } + + node = xmldoc_get_node(type, name, module, documentation_language); + + if (!node || !ast_xml_node_get_children(node)) { + return NULL; + } + + return xmldoc_build_final_response(node); +} + struct ast_xml_xpath_results *__attribute__((format(printf, 1, 2))) ast_xmldoc_query(const char *fmt, ...) { struct ast_xml_xpath_results *results = NULL; @@ -2455,7 +2588,7 @@ struct ast_xml_xpath_results *__attribute__((format(printf, 1, 2))) ast_xmldoc_q return results; } -static void build_config_docs(struct ast_xml_node *cur, struct ast_xml_doc_item **tail) +static void build_config_docs(struct ast_xml_node *cur, struct ast_xml_doc_item_list *root) { struct ast_xml_node *iter; struct ast_xml_doc_item *item; @@ -2478,9 +2611,8 @@ static void build_config_docs(struct ast_xml_node *cur, struct ast_xml_doc_item ast_string_field_set(item, ref, name); ast_xml_free_attr(name); } - (*tail)->next = item; - *tail = (*tail)->next; - build_config_docs(iter, tail); + AST_LIST_INSERT_TAIL(root, item, next); + build_config_docs(iter, root); } } @@ -2536,7 +2668,6 @@ int ast_xmldoc_regenerate_doc_item(struct ast_xml_doc_item *item) struct ao2_container *ast_xmldoc_build_documentation(const char *type) { struct ao2_container *docs; - struct ast_xml_doc_item *item = NULL, *root = NULL; struct ast_xml_node *node = NULL, *instance = NULL; struct documentation_tree *doctree; const char *name; @@ -2555,6 +2686,8 @@ struct ao2_container *ast_xmldoc_build_documentation(const char *type) } for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) { + struct ast_xml_doc_item *item = NULL; + /* Ignore empty nodes or nodes that aren't of the type requested */ if (!ast_xml_node_get_children(node) || strcasecmp(ast_xml_node_get_name(node), type)) { continue; @@ -2566,6 +2699,10 @@ struct ao2_container *ast_xmldoc_build_documentation(const char *type) switch (xmldoc_get_syntax_type(type)) { case MANAGER_EVENT_SYNTAX: + { + struct ast_xml_doc_item_list root; + + AST_LIST_HEAD_INIT(&root); for (instance = ast_xml_node_get_children(node); instance; instance = ast_xml_node_get_next(instance)) { struct ast_xml_doc_item *temp; if (!ast_xml_node_get_children(instance) || strcasecmp(ast_xml_node_get_name(instance), "managerEventInstance")) { @@ -2575,28 +2712,27 @@ struct ao2_container *ast_xmldoc_build_documentation(const char *type) if (!temp) { break; } - if (!item) { - item = temp; - root = item; - } else { - item->next = temp; - item = temp; - } + AST_LIST_INSERT_TAIL(&root, temp, next); } - item = root; + item = AST_LIST_FIRST(&root); break; + } case CONFIG_INFO_SYNTAX: { - struct ast_xml_doc_item *tail; RAII_VAR(const char *, name, ast_xml_get_attribute(node, "name"), ast_xml_free_attr); - if (item || !ast_xml_node_get_children(node) || strcasecmp(ast_xml_node_get_name(node), "configInfo")) { + + if (!ast_xml_node_get_children(node) || strcasecmp(ast_xml_node_get_name(node), "configInfo")) { break; } - if (!(item = xmldoc_build_documentation_item(node, name, "configInfo"))) { - break; + + item = xmldoc_build_documentation_item(node, name, "configInfo"); + if (item) { + struct ast_xml_doc_item_list root; + + AST_LIST_HEAD_INIT(&root); + AST_LIST_INSERT_TAIL(&root, item, next); + build_config_docs(node, &root); } - tail = item; - build_config_docs(node, &tail); break; } default: @@ -2607,7 +2743,6 @@ struct ao2_container *ast_xmldoc_build_documentation(const char *type) if (item) { ao2_link(docs, item); ao2_t_ref(item, -1, "Dispose of creation ref"); - item = NULL; } } } -- cgit v1.2.3