]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
docs: Enable since/version handling for XML, CLI and ARI documentation
authorGeorge Joseph <gjoseph@sangoma.com>
Thu, 9 Jan 2025 22:17:14 +0000 (15:17 -0700)
committerGeorge Joseph <gjoseph@sangoma.com>
Thu, 16 Jan 2025 14:20:34 +0000 (14:20 +0000)
* Added the "since" element to the XML configObject and configOption elements
  in appdocsxml.dtd.

* Added the "Since" section to the following CLI output:
  ```
  config show help <module> <object>
  config show help <module> <object> <option>
  core show application <app>
  core show function <func>
  manager show command <command>
  manager show event <event>
  agi show commands topic <topic>
  ```

* Refactored the commands above to output their sections in the same order:
  Synopsis, Since, Description, Syntax, Arguments, SeeAlso

* Refactored the commands above so they all use the same pattern for writing
  the output to the CLI.

* Fixed several memory leaks caused by failure to free temporary output
  buffers.

* Added a "since" array to the mustache template for the top-level resources
  (Channel, Endpoint, etc.) and to the paths/methods underneath them. These
  will be added to the generated markdown if present.
  Example:
  ```
    "resourcePath": "/api-docs/channels.{format}",
    "requiresModules": [
        "res_stasis_answer",
        "res_stasis_playback",
        "res_stasis_recording",
        "res_stasis_snoop"
    ],
    "since": [
        "18.0.0",
        "21.0.0"
    ],
    "apis": [
        {
            "path": "/channels",
            "description": "Active channels",
            "operations": [
                {
                    "httpMethod": "GET",
                    "since": [
                        "18.6.0",
                        "21.8.0"
                    ],
                    "summary": "List all active channels in Asterisk.",
                    "nickname": "list",
                    "responseClass": "List[Channel]"
                },

  ```

NOTE:  No versioning information is actually added in this commit.
Those will be added separately and instructions for adding and maintaining
them will be published on the documentation site at a later date.

13 files changed:
doc/appdocsxml.dtd
include/asterisk/agi.h
include/asterisk/manager.h
include/asterisk/pbx.h
include/asterisk/xmldoc.h
main/config_options.c
main/manager.c
main/pbx_app.c
main/pbx_functions.c
main/xmldoc.c
res/res_agi.c
rest-api-templates/api.wiki.mustache
rest-api-templates/swagger_model.py

index 426d959466cb1c29c4e1c427e6f1a631d68f1f9e..ad5f16c0b6b506235f3a097fe8c64d21bfbc7027 100644 (file)
   <!ELEMENT configFile (configObject|xi:include)+>
   <!ATTLIST configFile name CDATA #REQUIRED>
 
-  <!ELEMENT configObject (synopsis?|description?|syntax?|see-also?|(configOption|xi:include))*>
+  <!ELEMENT configObject (since?|synopsis?|description?|syntax?|see-also?|(configOption|xi:include))*>
   <!ATTLIST configObject name CDATA #REQUIRED>
 
-  <!ELEMENT configOption (synopsis,description?,syntax?,see-also?)*>
+  <!ELEMENT configOption (since?,synopsis,description?,syntax?,see-also?)*>
   <!ATTLIST configOption name CDATA #REQUIRED>
   <!ATTLIST configOption regex (yes|no|true|false) "false">
   <!ATTLIST configOption default CDATA #IMPLIED>
index 568cd5d11626441875167aa8450ada5b9758c183..dfa6dceacd7c9b46903a5efb0301412fa4b178df 100644 (file)
@@ -60,6 +60,10 @@ typedef struct agi_command {
        struct ast_module *mod;
        /*! Linked list pointer */
        AST_LIST_ENTRY(agi_command) list;
+       /*! Since content */
+       const char * const since;
+       /*! Syntax arguments content */
+       const char * const arguments;
 } agi_command;
 
 /*!
index 923f1738cdde7cce6e6a520a6c52298a5a4af2db..05f3462c8673695d34eef424ae84dd91a7d5515f 100644 (file)
@@ -181,6 +181,7 @@ struct manager_action {
         * function and unregistering the AMI action object.
         */
        unsigned int registered:1;
+       AST_STRING_FIELD_EXTENDED(since);       /*!< Documentation "since" element */
 };
 
 /*! \brief External routines may register/unregister manager callbacks this way
index ec752a96db499aa31ad9251dfdbe81b0d0e82136..f66b54c1bc397259cd442c381494e2e7fd91156a 100644 (file)
@@ -150,6 +150,7 @@ struct ast_custom_function {
                                         * \since 12 */
 
        AST_RWLIST_ENTRY(ast_custom_function) acflist;
+       AST_STRING_FIELD_EXTENDED(since); /*!< Since text for 'show functions' */
 };
 
 /*! \brief All switch functions have the same interface, so define a type for them */
index b14288f852c2b492b82022da77b7038ba26baf7c..5f9164fd2cd955c727db15ef8c8f860b0e1bdc11 100644 (file)
@@ -78,6 +78,8 @@ struct ast_xml_doc_item {
        struct ast_xml_node *node;
        /*! The next XML documentation item that matches the same name/item type */
        AST_LIST_ENTRY(ast_xml_doc_item) next;
+       /*! Since tagged information, if it exists */
+       struct ast_str *since;
 };
 
 /*! \brief Execute an XPath query on the loaded XML documentation
@@ -110,6 +112,16 @@ char *ast_xmldoc_build_syntax(const char *type, const char *name, const char *mo
  */
 char *ast_xmldoc_build_seealso(const char *type, const char *name, const char *module);
 
+/*!
+ *  \brief Parse the <since> node content.
+ *  \param type 'application', 'function' or 'agi'.
+ *  \param name Application or functions name.
+ *  \param module The module the item is in (optional, can be NULL)
+ *  \retval NULL on error.
+ *  \retval Content of the since node.
+ */
+char *ast_xmldoc_build_since(const char *type, const char *name, const char *module);
+
 /*!
  *  \brief Generate the [arguments] tag based on type of node ('application',
  *         'function' or 'agi') and name.
index 9769fd3ae16c6f3354cb7609af89dfbad7aa8975..ae3fae549fe0d06e6c7c86b218a30b5bd2c3022b 100644 (file)
@@ -1244,10 +1244,14 @@ static void cli_show_module_types(struct ast_cli_args *a)
        }
 
        if (ast_str_strlen(item->synopsis)) {
-               ast_cli(a->fd, "%s\n\n", ast_xmldoc_printable(ast_str_buffer(item->synopsis), 1));
+               char *value = ast_xmldoc_printable(ast_str_buffer(item->synopsis), 1);
+               ast_cli(a->fd, "%s\n\n", value);
+               ast_free(value);
        }
        if (ast_str_strlen(item->description)) {
-               ast_cli(a->fd, "%s\n\n", ast_xmldoc_printable(ast_str_buffer(item->description), 1));
+               char *value = ast_xmldoc_printable(ast_str_buffer(item->description), 1);
+               ast_cli(a->fd, "%s\n\n",value);
+               ast_free(value);
        }
 
        tmp = item;
@@ -1267,8 +1271,8 @@ static void cli_show_module_type(struct ast_cli_args *a)
 {
        RAII_VAR(struct ast_xml_doc_item *, item, NULL, ao2_cleanup);
        struct ast_xml_doc_item *tmp;
-       char option_type[64];
        int match = 0;
+       char *synopsis, *since, *description, *syntax, *seealso;
 
        ast_assert(a->argc == 5);
 
@@ -1281,19 +1285,48 @@ static void cli_show_module_type(struct ast_cli_args *a)
        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));
-                       ast_cli(a->fd, "%s", option_type);
-                       if (ast_str_strlen(tmp->syntax)) {
-                               ast_cli(a->fd, ": [%s]\n\n", ast_xmldoc_printable(ast_str_buffer(tmp->syntax), 1));
-                       } else {
-                               ast_cli(a->fd, "\n\n");
-                       }
-                       if (ast_str_strlen(tmp->synopsis)) {
-                               ast_cli(a->fd, "%s\n\n", ast_xmldoc_printable(ast_str_buffer(tmp->synopsis), 1));
-                       }
-                       if (ast_str_strlen(tmp->description)) {
-                               ast_cli(a->fd, "%s\n\n", ast_xmldoc_printable(ast_str_buffer(tmp->description), 1));
+
+                       synopsis = ast_xmldoc_printable(AS_OR(tmp->synopsis, "Not available"), 1);
+                       since = ast_xmldoc_printable(AS_OR(tmp->since, "Not available"), 1);
+                       description = ast_xmldoc_printable(AS_OR(tmp->description, "Not available"), 1);
+                       syntax = ast_xmldoc_printable(AS_OR(tmp->syntax, "Not available"), 1);
+                       seealso = ast_xmldoc_printable(AS_OR(tmp->seealso, "Not available"), 1);
+
+                       if (!synopsis || !since || !description || !syntax || !seealso ) {
+                               ast_free(synopsis);
+                               ast_free(since);
+                               ast_free(description);
+                               ast_free(syntax);
+                               ast_free(seealso);
+                               ast_cli(a->fd, "Error: Memory allocation failed\n");
+                               break;
                        }
+
+                       ast_cli(a->fd, "\n"
+                               "%s  -= Info about Config Object '%s:%s' =- %s\n\n"
+                               COLORIZE_FMT "\n"
+                               "%s\n\n"
+                               COLORIZE_FMT "\n"
+                               "%s\n\n"
+                               COLORIZE_FMT "\n"
+                               "%s\n\n"
+                               COLORIZE_FMT "\n"
+                               "%s [%s]\n\n"
+                               COLORIZE_FMT "\n"
+                               "%s\n\n",
+                               ast_term_color(COLOR_MAGENTA, 0), a->argv[3], tmp->name, ast_term_reset(),
+                               COLORIZE(COLOR_MAGENTA, 0, "[Synopsis]"), synopsis,
+                               COLORIZE(COLOR_MAGENTA, 0, "[Since]"), since,
+                               COLORIZE(COLOR_MAGENTA, 0, "[Description]"), description,
+                               COLORIZE(COLOR_MAGENTA, 0, "[Syntax]"), tmp->name, syntax,
+                               COLORIZE(COLOR_MAGENTA, 0, "[See Also]"), seealso
+                               );
+                       ast_free(synopsis);
+                       ast_free(since);
+                       ast_free(description);
+                       ast_free(syntax);
+                       ast_free(seealso);
+
                }
        }
 
@@ -1304,6 +1337,8 @@ static void cli_show_module_type(struct ast_cli_args *a)
 
        /* Now iterate over the options for the type */
        tmp = item;
+       ast_cli(a->fd, COLORIZE_FMT "\n", COLORIZE(COLOR_MAGENTA, 0, "[Config Options]"));
+
        while ((tmp = AST_LIST_NEXT(tmp, next))) {
                if (!strcasecmp(tmp->type, "configOption") && !strcasecmp(tmp->ref, a->argv[4])) {
                        ast_cli(a->fd, "%-25s -- %-120.120s\n", tmp->name,
@@ -1319,8 +1354,8 @@ static void cli_show_module_options(struct ast_cli_args *a)
 {
        RAII_VAR(struct ast_xml_doc_item *, item, NULL, ao2_cleanup);
        struct ast_xml_doc_item *tmp;
-       char option_name[64];
        int match = 0;
+       char *synopsis, *since, *description, *syntax, *seealso;
 
        ast_assert(a->argc == 6);
 
@@ -1334,21 +1369,48 @@ static void cli_show_module_options(struct ast_cli_args *a)
                        if (match) {
                                ast_cli(a->fd, "\n");
                        }
-                       term_color(option_name, tmp->ref, COLOR_MAGENTA, COLOR_BLACK, sizeof(option_name));
-                       ast_cli(a->fd, "[%s%s]\n", option_name, ast_term_reset());
-                       if (ast_str_strlen(tmp->syntax)) {
-                               ast_cli(a->fd, "%s\n", ast_xmldoc_printable(ast_str_buffer(tmp->syntax), 1));
-                       }
-                       ast_cli(a->fd, "%s\n\n", ast_xmldoc_printable(AS_OR(tmp->synopsis, "No information available"), 1));
-                       if (ast_str_strlen(tmp->description)) {
-                               ast_cli(a->fd, "%s\n\n", ast_xmldoc_printable(ast_str_buffer(tmp->description), 1));
-                       }
 
-                       if (ast_str_strlen(tmp->seealso)) {
-                               ast_cli(a->fd, "See Also:\n");
-                               ast_cli(a->fd, "%s\n\n", ast_xmldoc_printable(ast_str_buffer(tmp->seealso), 1));
+                       synopsis = ast_xmldoc_printable(AS_OR(tmp->synopsis, "Not available"), 1);
+                       since = ast_xmldoc_printable(AS_OR(tmp->since, "Not available"), 1);
+                       description = ast_xmldoc_printable(AS_OR(tmp->description, "Not available"), 1);
+                       syntax = ast_xmldoc_printable(AS_OR(tmp->syntax, "Not available"), 1);
+                       seealso = ast_xmldoc_printable(AS_OR(tmp->seealso, "Not available"), 1);
+
+                       if (!synopsis || !since || !description || !syntax || !seealso ) {
+                               ast_free(synopsis);
+                               ast_free(since);
+                               ast_free(description);
+                               ast_free(syntax);
+                               ast_free(seealso);
+                               ast_cli(a->fd, "Error: Memory allocation failed\n");
+                               break;
                        }
 
+                       ast_cli(a->fd, "\n"
+                               "%s  -= Info about Config Option '%s:%s:%s' =- %s\n\n"
+                               COLORIZE_FMT "\n"
+                               "%s\n\n"
+                               COLORIZE_FMT "\n"
+                               "%s\n\n"
+                               COLORIZE_FMT "\n"
+                               "%s\n\n"
+                               COLORIZE_FMT "\n"
+                               "%s\n\n"
+                               COLORIZE_FMT "\n"
+                               "%s\n\n",
+                               ast_term_color(COLOR_MAGENTA, 0), a->argv[3], a->argv[4], tmp->name, ast_term_reset(),
+                               COLORIZE(COLOR_MAGENTA, 0, "[Synopsis]"), synopsis,
+                               COLORIZE(COLOR_MAGENTA, 0, "[Since]"), since,
+                               COLORIZE(COLOR_MAGENTA, 0, "[Description]"), description,
+                               COLORIZE(COLOR_MAGENTA, 0, "[Syntax]"), syntax,
+                               COLORIZE(COLOR_MAGENTA, 0, "[See Also]"), seealso
+                               );
+                       ast_free(synopsis);
+                       ast_free(since);
+                       ast_free(description);
+                       ast_free(syntax);
+                       ast_free(seealso);
+
                        match = 1;
                }
        }
index 2a543151b25897e0117a241c3a78f38617fe1fa6..7fe245335f66641f9ada871dd2bfeffc8d9fa1c5 100644 (file)
@@ -1084,10 +1084,6 @@ static char *handle_showmancmd(struct ast_cli_entry *e, int cmd, struct ast_cli_
        int num;
        int l;
        const char *auth_str;
-#ifdef AST_XML_DOCS
-       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) {
        case CLI_INIT:
@@ -1115,18 +1111,6 @@ static char *handle_showmancmd(struct ast_cli_entry *e, int cmd, struct ast_cli_
 
        authority = ast_str_alloca(MAX_AUTH_PERM_STRING);
 
-#ifdef AST_XML_DOCS
-       /* setup the titles */
-       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);
-       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);
        AST_RWLIST_TRAVERSE(&actions, cur, list) {
                for (num = 3; num < a->argc; num++) {
@@ -1135,22 +1119,24 @@ static char *handle_showmancmd(struct ast_cli_entry *e, int cmd, struct ast_cli_
 
 #ifdef AST_XML_DOCS
                                if (cur->docsrc == AST_XML_DOC) {
-                                       char *syntax = ast_xmldoc_printable(S_OR(cur->syntax, "Not available"), 1);
                                        char *synopsis = ast_xmldoc_printable(S_OR(cur->synopsis, "Not available"), 1);
+                                       char *since = ast_xmldoc_printable(S_OR(cur->since, "Not available"), 1);
                                        char *description = ast_xmldoc_printable(S_OR(cur->description, "Not available"), 1);
+                                       char *syntax = ast_xmldoc_printable(S_OR(cur->syntax, "Not available"), 1);
                                        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(auth_str, "Not available"), 1);
+                                       char *seealso = ast_xmldoc_printable(S_OR(cur->seealso, "Not available"), 1);
                                        char *responses = ast_xmldoc_printable("None", 1);
 
-                                       if (!syntax || !synopsis || !description || !arguments
-                                                       || !seealso || !privilege || !responses) {
-                                               ast_free(syntax);
+                                       if (!synopsis || !since || !description || !syntax || !arguments
+                                                       || !privilege || !seealso || !responses) {
                                                ast_free(synopsis);
+                                               ast_free(since);
                                                ast_free(description);
+                                               ast_free(syntax);
                                                ast_free(arguments);
-                                               ast_free(seealso);
                                                ast_free(privilege);
+                                               ast_free(seealso);
                                                ast_free(responses);
                                                ast_cli(a->fd, "Allocation failure.\n");
                                                AST_RWLIST_UNLOCK(&actions);
@@ -1158,14 +1144,33 @@ static char *handle_showmancmd(struct ast_cli_entry *e, int cmd, struct ast_cli_
                                                return CLI_FAILURE;
                                        }
 
-                                       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,
-                                               list_responses_title);
+                                       ast_cli(a->fd, "\n"
+                                               "%s  -= Info about Manager Command '%s' =- %s\n\n"
+                                               COLORIZE_FMT "\n"
+                                               "%s\n\n"
+                                               COLORIZE_FMT "\n"
+                                               "%s\n\n"
+                                               COLORIZE_FMT "\n"
+                                               "%s\n\n"
+                                               COLORIZE_FMT "\n"
+                                               "%s\n\n"
+                                               COLORIZE_FMT "\n"
+                                               "%s\n\n"
+                                               COLORIZE_FMT "\n"
+                                               "%s\n\n"
+                                               COLORIZE_FMT "\n"
+                                               "%s\n\n"
+                                               COLORIZE_FMT "\n",
+                                               ast_term_color(COLOR_MAGENTA, 0), cur->action, ast_term_reset(),
+                                               COLORIZE(COLOR_MAGENTA, 0, "[Synopsis]"), synopsis,
+                                               COLORIZE(COLOR_MAGENTA, 0, "[Since]"), since,
+                                               COLORIZE(COLOR_MAGENTA, 0, "[Description]"), description,
+                                               COLORIZE(COLOR_MAGENTA, 0, "[Syntax]"), syntax,
+                                               COLORIZE(COLOR_MAGENTA, 0, "[Arguments]"), arguments,
+                                               COLORIZE(COLOR_MAGENTA, 0, "[Privilege]"), privilege,
+                                               COLORIZE(COLOR_MAGENTA, 0, "[See Also]"), seealso,
+                                               COLORIZE(COLOR_MAGENTA, 0, "[List Responses]")
+                                               );
 
                                        if (!cur->list_responses) {
                                                ast_cli(a->fd, "%s\n\n", responses);
@@ -1176,22 +1181,33 @@ static char *handle_showmancmd(struct ast_cli_entry *e, int cmd, struct ast_cli_
                                                        print_event_instance(a, temp);
                                                }
                                        }
-
-                                       ast_cli(a->fd, "%s", final_response_title);
-
+                                       ast_cli(a->fd,
+                                               COLORIZE_FMT "\n",
+                                               COLORIZE(COLOR_MAGENTA, 0, "[End List Responses]")
+                                               );
+
+                                       ast_cli(a->fd, "\n"
+                                               COLORIZE_FMT "\n",
+                                               COLORIZE(COLOR_MAGENTA, 0, "[Final Response]")
+                                               );
                                        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);
                                        }
+                                       ast_cli(a->fd,
+                                               COLORIZE_FMT "\n",
+                                               COLORIZE(COLOR_MAGENTA, 0, "[End Final Response]")
+                                               );
 
-                                       ast_free(syntax);
                                        ast_free(synopsis);
+                                       ast_free(since);
                                        ast_free(description);
+                                       ast_free(syntax);
                                        ast_free(arguments);
-                                       ast_free(seealso);
                                        ast_free(privilege);
+                                       ast_free(seealso);
                                        ast_free(responses);
                                } else
 #endif
@@ -7754,6 +7770,11 @@ int ast_manager_register2(const char *action, int auth, int (*func)(struct manse
                return -1;
        }
 
+       if (ast_string_field_init_extended(cur, since)) {
+               ao2_t_ref(cur, -1, "action object creation failed");
+               return -1;
+       }
+
        cur->action = action;
        cur->authority = auth;
        cur->func = func;
@@ -7762,6 +7783,10 @@ int ast_manager_register2(const char *action, int auth, int (*func)(struct manse
        if (ast_strlen_zero(synopsis) && ast_strlen_zero(description)) {
                char *tmpxml;
 
+               tmpxml = ast_xmldoc_build_since("manager", action, NULL);
+               ast_string_field_set(cur, since, tmpxml);
+               ast_free(tmpxml);
+
                tmpxml = ast_xmldoc_build_synopsis("manager", action, NULL);
                ast_string_field_set(cur, synopsis, tmpxml);
                ast_free(tmpxml);
@@ -9174,39 +9199,50 @@ static char *handle_manager_show_events(struct ast_cli_entry *e, int cmd, struct
 
 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);
-       }
+       char *since, *syntax, *description, *synopsis, *seealso, *arguments;
+
+       synopsis = ast_xmldoc_printable(AS_OR(instance->synopsis, "Not available"), 1);
+       since = ast_xmldoc_printable(AS_OR(instance->since, "Not available"), 1);
+       description = ast_xmldoc_printable(AS_OR(instance->description, "Not available"), 1);
+       syntax = ast_xmldoc_printable(AS_OR(instance->syntax, "Not available"), 1);
+       arguments = ast_xmldoc_printable(AS_OR(instance->arguments, "Not available"), 1);
+       seealso = ast_xmldoc_printable(AS_OR(instance->seealso, "Not available"), 1);
+
+       if (!synopsis || !since || !description || !syntax || !arguments || !seealso) {
+               ast_cli(a->fd, "Error: Memory allocation failed\n");
+               goto free_docs;
+       }
+
+       ast_cli(a->fd, "\n"
+               "%s  -= Info about Manager Event '%s' =- %s\n\n"
+               COLORIZE_FMT "\n"
+               "%s\n\n"
+               COLORIZE_FMT "\n"
+               "%s\n\n"
+               COLORIZE_FMT "\n"
+               "%s\n\n"
+               COLORIZE_FMT "\n"
+               "%s\n\n"
+               COLORIZE_FMT "\n"
+               "%s\n\n"
+               COLORIZE_FMT "\n"
+               "%s\n\n",
+               ast_term_color(COLOR_MAGENTA, 0), instance->name, ast_term_reset(),
+               COLORIZE(COLOR_MAGENTA, 0, "[Synopsis]"), synopsis,
+               COLORIZE(COLOR_MAGENTA, 0, "[Since]"), since,
+               COLORIZE(COLOR_MAGENTA, 0, "[Description]"), description,
+               COLORIZE(COLOR_MAGENTA, 0, "[Syntax]"), syntax,
+               COLORIZE(COLOR_MAGENTA, 0, "[Arguments]"), arguments,
+               COLORIZE(COLOR_MAGENTA, 0, "[See Also]"), seealso
+               );
+
+free_docs:
+       ast_free(synopsis);
+       ast_free(since);
+       ast_free(description);
+       ast_free(syntax);
+       ast_free(arguments);
+       ast_free(seealso);
 }
 
 static char *handle_manager_show_event(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
index ecbca1d85f9ad3a66f11aa823009d938889795a0..9b2068d29f22ae7145f26ea65c379a8dc573f59e 100644 (file)
@@ -46,6 +46,7 @@ struct ast_app {
        int (*execute)(struct ast_channel *chan, const char *data);
        AST_DECLARE_STRING_FIELDS(
                AST_STRING_FIELD(synopsis);     /*!< Synopsis text for 'show applications' */
+               AST_STRING_FIELD(since);        /*!< Since text for 'show applications' */
                AST_STRING_FIELD(description);  /*!< Description (help text) for 'show application &lt;name&gt;' */
                AST_STRING_FIELD(syntax);       /*!< Syntax text for 'core show applications' */
                AST_STRING_FIELD(arguments);    /*!< Arguments description */
@@ -142,6 +143,11 @@ int ast_register_application2(const char *app, int (*execute)(struct ast_channel
                ast_string_field_set(tmp, synopsis, tmpxml);
                ast_free(tmpxml);
 
+               /* load since */
+               tmpxml = ast_xmldoc_build_since("application", app, ast_module_name(tmp->module));
+               ast_string_field_set(tmp, since, tmpxml);
+               ast_free(tmpxml);
+
                /* load description */
                tmpxml = ast_xmldoc_build_description("application", app, ast_module_name(tmp->module));
                ast_string_field_set(tmp, description, tmpxml);
@@ -191,67 +197,61 @@ int ast_register_application2(const char *app, int (*execute)(struct ast_channel
 
 static void print_app_docs(struct ast_app *aa, int fd)
 {
+       char *synopsis = NULL, *since = NULL, *description = NULL, *syntax = NULL, *arguments = NULL, *seealso = NULL;
+
 #ifdef AST_XML_DOCS
-       char *synopsis = NULL, *description = NULL, *arguments = NULL, *seealso = NULL;
        if (aa->docsrc == AST_XML_DOC) {
                synopsis = ast_xmldoc_printable(S_OR(aa->synopsis, "Not available"), 1);
+               since = ast_xmldoc_printable(S_OR(aa->since, "Not available"), 1);
                description = ast_xmldoc_printable(S_OR(aa->description, "Not available"), 1);
+               syntax = ast_xmldoc_printable(S_OR(aa->syntax, "Not available"), 1);
                arguments = ast_xmldoc_printable(S_OR(aa->arguments, "Not available"), 1);
                seealso = ast_xmldoc_printable(S_OR(aa->seealso, "Not available"), 1);
-               if (!synopsis || !description || !arguments || !seealso) {
-                       goto free_docs;
-               }
-               ast_cli(fd, "\n"
-                       "%s  -= Info about application '%s' =- %s\n\n"
-                       COLORIZE_FMT "\n"
-                       "%s\n\n"
-                       COLORIZE_FMT "\n"
-                       "%s\n\n"
-                       COLORIZE_FMT "\n"
-                       "%s%s%s\n\n"
-                       COLORIZE_FMT "\n"
-                       "%s\n\n"
-                       COLORIZE_FMT "\n"
-                       "%s\n",
-                       ast_term_color(COLOR_MAGENTA, 0), aa->name, ast_term_reset(),
-                       COLORIZE(COLOR_MAGENTA, 0, "[Synopsis]"), synopsis,
-                       COLORIZE(COLOR_MAGENTA, 0, "[Description]"), description,
-                       COLORIZE(COLOR_MAGENTA, 0, "[Syntax]"),
-                               ast_term_color(COLOR_CYAN, 0), S_OR(aa->syntax, "Not available"), ast_term_reset(),
-                       COLORIZE(COLOR_MAGENTA, 0, "[Arguments]"), arguments,
-                       COLORIZE(COLOR_MAGENTA, 0, "[See Also]"), seealso);
-free_docs:
-               ast_free(synopsis);
-               ast_free(description);
-               ast_free(arguments);
-               ast_free(seealso);
        } else
 #endif
        {
-               ast_cli(fd, "\n"
-                       "%s  -= Info about application '%s' =- %s\n\n"
-                       COLORIZE_FMT "\n"
-                       COLORIZE_FMT "\n\n"
-                       COLORIZE_FMT "\n"
-                       COLORIZE_FMT "\n\n"
-                       COLORIZE_FMT "\n"
-                       COLORIZE_FMT "\n\n"
-                       COLORIZE_FMT "\n"
-                       COLORIZE_FMT "\n\n"
-                       COLORIZE_FMT "\n"
-                       COLORIZE_FMT "\n",
-                       ast_term_color(COLOR_MAGENTA, 0), aa->name, ast_term_reset(),
-                       COLORIZE(COLOR_MAGENTA, 0, "[Synopsis]"),
-                       COLORIZE(COLOR_CYAN, 0, S_OR(aa->synopsis, "Not available")),
-                       COLORIZE(COLOR_MAGENTA, 0, "[Description]"),
-                       COLORIZE(COLOR_CYAN, 0, S_OR(aa->description, "Not available")),
-                       COLORIZE(COLOR_MAGENTA, 0, "[Syntax]"),
-                       COLORIZE(COLOR_CYAN, 0, S_OR(aa->syntax, "Not available")),
-                       COLORIZE(COLOR_MAGENTA, 0, "[Arguments]"),
-                       COLORIZE(COLOR_CYAN, 0, S_OR(aa->arguments, "Not available")),
-                       COLORIZE(COLOR_MAGENTA, 0, "[See Also]"),
-                       COLORIZE(COLOR_CYAN, 0, S_OR(aa->seealso, "Not available")));
+               synopsis = ast_strdup(S_OR(aa->synopsis, "Not Available"));
+               since = ast_strdup(S_OR(aa->since, "Not Available"));
+               description = ast_strdup(S_OR(aa->description, "Not Available"));
+               syntax = ast_strdup(S_OR(aa->syntax, "Not Available"));
+               arguments = ast_strdup(S_OR(aa->arguments, "Not Available"));
+               seealso = ast_strdup(S_OR(aa->seealso, "Not Available"));
        }
+               /* check allocated memory. */
+       if (!synopsis || !since || !description || !syntax || !arguments || !seealso) {
+               goto free_docs;
+       }
+
+       ast_cli(fd, "\n"
+               "%s  -= Info about Application '%s' =- %s\n\n"
+               COLORIZE_FMT "\n"
+               "%s\n\n"
+               COLORIZE_FMT "\n"
+               "%s\n\n"
+               COLORIZE_FMT "\n"
+               "%s\n\n"
+               COLORIZE_FMT "\n"
+               "%s\n\n"
+               COLORIZE_FMT "\n"
+               "%s\n\n"
+               COLORIZE_FMT "\n"
+               "%s\n\n",
+               ast_term_color(COLOR_MAGENTA, 0), aa->name, ast_term_reset(),
+               COLORIZE(COLOR_MAGENTA, 0, "[Synopsis]"), synopsis,
+               COLORIZE(COLOR_MAGENTA, 0, "[Since]"), since,
+               COLORIZE(COLOR_MAGENTA, 0, "[Description]"), description,
+               COLORIZE(COLOR_MAGENTA, 0, "[Syntax]"), syntax,
+               COLORIZE(COLOR_MAGENTA, 0, "[Arguments]"), arguments,
+               COLORIZE(COLOR_MAGENTA, 0, "[See Also]"), seealso
+               );
+
+free_docs:
+       ast_free(synopsis);
+       ast_free(since);
+       ast_free(description);
+       ast_free(syntax);
+       ast_free(arguments);
+       ast_free(seealso);
 }
 
 /*!
index 1402696b6b2b5c54bf78087a9dae7688b397c882..a244ddcc085534b3816236370a2d7e4170d5c0dc 100644 (file)
@@ -144,10 +144,8 @@ static char *handle_show_function(struct ast_cli_entry *e, int cmd, struct ast_c
 {
        struct ast_custom_function *acf;
        /* Maximum number of characters added by terminal coloring is 22 */
-       char infotitle[64 + AST_MAX_APP + 22], syntitle[40], desctitle[40], argtitle[40], seealsotitle[40];
-       char info[64 + AST_MAX_APP], *synopsis = NULL, *description = NULL, *seealso = NULL;
-       char stxtitle[40], *syntax = NULL, *arguments = NULL;
-       int syntax_size, description_size, synopsis_size, arguments_size, seealso_size;
+       char *synopsis = NULL, *since = NULL, *description = NULL, *syntax = NULL, *arguments = NULL, *seealso = NULL;
+       char *rtn = CLI_SUCCESS;
 
        switch (cmd) {
        case CLI_INIT:
@@ -170,71 +168,62 @@ 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")) + AST_TERM_MAX_ESCAPE_CHARS;
-       syntax = ast_malloc(syntax_size);
-       if (!syntax) {
-               ast_cli(a->fd, "Memory allocation failure!\n");
-
-               return CLI_FAILURE;
-       }
-
-       snprintf(info, sizeof(info), "\n  -= Info about function '%s' =- \n\n", acf->name);
-       term_color(infotitle, info, COLOR_MAGENTA, 0, sizeof(infotitle));
-       term_color(syntitle, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
-       term_color(desctitle, "[Description]\n", COLOR_MAGENTA, 0, 40);
-       term_color(stxtitle, "[Syntax]\n", COLOR_MAGENTA, 0, 40);
-       term_color(argtitle, "[Arguments]\n", COLOR_MAGENTA, 0, 40);
-       term_color(seealsotitle, "[See Also]\n", COLOR_MAGENTA, 0, 40);
-       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 = ast_xmldoc_printable(S_OR(acf->arguments, "Not available"), 1);
                synopsis = ast_xmldoc_printable(S_OR(acf->synopsis, "Not available"), 1);
+               since = ast_xmldoc_printable(S_OR(acf->since, "Not available"), 1);
                description = ast_xmldoc_printable(S_OR(acf->desc, "Not available"), 1);
+               syntax = ast_xmldoc_printable(S_OR(acf->syntax, "Not available"), 1);
+               arguments = ast_xmldoc_printable(S_OR(acf->arguments, "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")) + AST_TERM_MAX_ESCAPE_CHARS;
-               synopsis = ast_malloc(synopsis_size);
-
-               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")) + AST_TERM_MAX_ESCAPE_CHARS;
-               arguments = ast_malloc(arguments_size);
-
-               seealso_size = strlen(S_OR(acf->seealso, "Not Available")) + AST_TERM_MAX_ESCAPE_CHARS;
-               seealso = ast_malloc(seealso_size);
-
-               /* check allocated memory. */
-               if (!synopsis || !description || !arguments || !seealso) {
-                       ast_free(synopsis);
-                       ast_free(description);
-                       ast_free(arguments);
-                       ast_free(seealso);
-                       ast_free(syntax);
-
-                       return CLI_FAILURE;
-               }
-
-               term_color(arguments, S_OR(acf->arguments, "Not available"), COLOR_CYAN, 0, arguments_size);
-               term_color(synopsis, S_OR(acf->synopsis, "Not available"), COLOR_CYAN, 0, synopsis_size);
-               term_color(description, S_OR(acf->desc, "Not available"), COLOR_CYAN, 0, description_size);
-               term_color(seealso, S_OR(acf->seealso, "Not available"), COLOR_CYAN, 0, seealso_size);
+               synopsis = ast_strdup(S_OR(acf->synopsis, "Not Available"));
+               since = ast_strdup(S_OR(acf->since, "Not Available"));
+               description = ast_strdup(S_OR(acf->desc, "Not Available"));
+               syntax = ast_strdup(S_OR(acf->syntax, "Not Available"));
+               arguments = ast_strdup(S_OR(acf->arguments, "Not Available"));
+               seealso = ast_strdup(S_OR(acf->seealso, "Not Available"));
        }
-
-       ast_cli(a->fd, "%s%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n",
-                       infotitle, syntitle, synopsis, desctitle, description,
-                       stxtitle, syntax, argtitle, arguments, seealsotitle, seealso);
-
-       ast_free(arguments);
+               /* check allocated memory. */
+       if (!synopsis || !since || !description || !syntax || !arguments || !seealso) {
+               rtn = CLI_FAILURE;
+               goto free_docs;
+       }
+
+       ast_cli(a->fd, "\n"
+               "%s  -= Info about Function '%s' =- %s\n\n"
+               COLORIZE_FMT "\n"
+               "%s\n\n"
+               COLORIZE_FMT "\n"
+               "%s\n\n"
+               COLORIZE_FMT "\n"
+               "%s\n\n"
+               COLORIZE_FMT "\n"
+               "%s\n\n"
+               COLORIZE_FMT "\n"
+               "%s\n\n"
+               COLORIZE_FMT "\n"
+               "%s\n\n",
+               ast_term_color(COLOR_MAGENTA, 0), acf->name, ast_term_reset(),
+               COLORIZE(COLOR_MAGENTA, 0, "[Synopsis]"), synopsis,
+               COLORIZE(COLOR_MAGENTA, 0, "[Since]"), since,
+               COLORIZE(COLOR_MAGENTA, 0, "[Description]"), description,
+               COLORIZE(COLOR_MAGENTA, 0, "[Syntax]"), syntax,
+               COLORIZE(COLOR_MAGENTA, 0, "[Arguments]"), arguments,
+               COLORIZE(COLOR_MAGENTA, 0, "[See Also]"), seealso
+               );
+
+free_docs:
        ast_free(synopsis);
+       ast_free(since);
        ast_free(description);
-       ast_free(seealso);
        ast_free(syntax);
+       ast_free(arguments);
+       ast_free(seealso);
 
-       return CLI_SUCCESS;
+       return rtn;
 }
 
 static struct ast_custom_function *ast_custom_function_find_nolock(const char *name)
@@ -339,11 +328,21 @@ static int acf_retrieve_docs(struct ast_custom_function *acf)
                return -1;
        }
 
+       if (ast_string_field_init_extended(acf, since)) {
+               ast_string_field_free_memory(acf);
+               return -1;
+       }
+
        /* load synopsis */
        tmpxml = ast_xmldoc_build_synopsis("function", acf->name, ast_module_name(acf->mod));
        ast_string_field_set(acf, synopsis, tmpxml);
        ast_free(tmpxml);
 
+       /* load since */
+       tmpxml = ast_xmldoc_build_since("function", acf->name, ast_module_name(acf->mod));
+       ast_string_field_set(acf, since, tmpxml);
+       ast_free(tmpxml);
+
        /* load description */
        tmpxml = ast_xmldoc_build_description("function", acf->name, ast_module_name(acf->mod));
        ast_string_field_set(acf, desc, tmpxml);
index 2b75523402a3fea588d4ee270fe7b5a4360712a8..a960addb36a9c01f64979450d6499483f1899c49 100644 (file)
@@ -1722,6 +1722,91 @@ char *ast_xmldoc_build_seealso(const char *type, const char *name, const char *m
        return output;
 }
 
+/*!
+ * \internal
+ * \brief Build since information for an item
+ *
+ * \param node The since 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 malloc'd character pointer to the since information of the item
+ * \retval NULL on failure
+ *
+ * \since 22
+ */
+static char *_ast_xmldoc_build_since(struct ast_xml_node *node)
+{
+       char *output;
+       struct ast_str *outputstr;
+       const char *content;
+       int first = 1;
+
+       /* Find the <since> 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), "since")) {
+                       break;
+               }
+       }
+
+       if (!node || !ast_xml_node_get_children(node)) {
+               /* we couldnt find a <since> node. */
+               return NULL;
+       }
+
+       /* prepare the output string. */
+       outputstr = ast_str_create(128);
+       if (!outputstr) {
+               return NULL;
+       }
+
+       /* get into the <since> 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), "version")) {
+                       continue;
+               }
+
+               content = ast_xml_get_text(node);
+               if (!content) {
+                       continue;
+               }
+
+               ast_str_append(&outputstr, 0, "%s%s", (first ? "" : ", "), content);
+
+               first = 0;
+               ast_xml_free_text(content);
+       }
+
+       output = ast_strdup(ast_str_buffer(outputstr));
+       ast_free(outputstr);
+
+       return output;
+}
+
+char *ast_xmldoc_build_since(const char *type, const char *name, const char *module)
+{
+       char *output;
+       struct ast_xml_node *node;
+
+       if (ast_strlen_zero(type) || ast_strlen_zero(name)) {
+               return NULL;
+       }
+
+       /* get the application/function root node. */
+       AST_RWLIST_RDLOCK(&xmldoc_tree);
+       node = xmldoc_get_node(type, name, module, documentation_language);
+       if (!node || !ast_xml_node_get_children(node)) {
+               AST_RWLIST_UNLOCK(&xmldoc_tree);
+               return NULL;
+       }
+
+       output = _ast_xmldoc_build_since(node);
+       AST_RWLIST_UNLOCK(&xmldoc_tree);
+
+       return output;
+}
+
 /*!
  * \internal
  * \brief Parse a \<enum\> node.
@@ -2286,11 +2371,12 @@ static void ast_xml_doc_item_destructor(void *obj)
                return;
        }
 
-       ast_free(doc->syntax);
-       ast_free(doc->seealso);
-       ast_free(doc->arguments);
        ast_free(doc->synopsis);
+       ast_free(doc->since);
        ast_free(doc->description);
+       ast_free(doc->syntax);
+       ast_free(doc->arguments);
+       ast_free(doc->seealso);
        ast_string_field_free_memory(doc);
 
        if (AST_LIST_NEXT(doc, next)) {
@@ -2318,11 +2404,13 @@ static struct ast_xml_doc_item *ast_xml_doc_item_alloc(const char *name, const c
                return NULL;
        }
 
-       if (   !(item->syntax = ast_str_create(128))
-               || !(item->seealso = ast_str_create(128))
+       if (   !(item->synopsis = ast_str_create(128))
+               || !(item->since = ast_str_create(128))
+               || !(item->description = ast_str_create(128))
+               || !(item->syntax = ast_str_create(128))
                || !(item->arguments = ast_str_create(128))
-               || !(item->synopsis = ast_str_create(128))
-               || !(item->description = ast_str_create(128))) {
+               || !(item->seealso = ast_str_create(128))
+               ) {
                ast_log(AST_LOG_ERROR, "Failed to allocate strings for ast_xml_doc_item instance\n");
                goto ast_xml_doc_item_failure;
        }
@@ -2381,44 +2469,50 @@ static int ast_xml_doc_item_cmp(void *obj, void *arg, int flags)
 static struct ast_xml_doc_item *xmldoc_build_documentation_item(struct ast_xml_node *node, const char *name, const char *type)
 {
        struct ast_xml_doc_item *item;
-       char *syntax;
-       char *seealso;
-       char *arguments;
        char *synopsis;
+       char *since;
        char *description;
+       char *syntax;
+       char *arguments;
+       char *seealso;
 
        if (!(item = ast_xml_doc_item_alloc(name, type))) {
                return NULL;
        }
        item->node = node;
 
-       syntax = _ast_xmldoc_build_syntax(node, type, name);
-       seealso = _ast_xmldoc_build_seealso(node);
-       arguments = _ast_xmldoc_build_arguments(node);
        synopsis = _ast_xmldoc_build_synopsis(node);
+       since = _ast_xmldoc_build_since(node);
        description = _ast_xmldoc_build_description(node);
+       syntax = _ast_xmldoc_build_syntax(node, type, name);
+       arguments = _ast_xmldoc_build_arguments(node);
+       seealso = _ast_xmldoc_build_seealso(node);
 
+       if (synopsis) {
+               ast_str_set(&item->synopsis, 0, "%s", synopsis);
+       }
+       if (since) {
+               ast_str_set(&item->since, 0, "%s", since);
+       }
+       if (description) {
+               ast_str_set(&item->description, 0, "%s", description);
+       }
        if (syntax) {
                ast_str_set(&item->syntax, 0, "%s", syntax);
        }
-       if (seealso) {
-               ast_str_set(&item->seealso, 0, "%s", seealso);
-       }
        if (arguments) {
                ast_str_set(&item->arguments, 0, "%s", arguments);
        }
-       if (synopsis) {
-               ast_str_set(&item->synopsis, 0, "%s", synopsis);
-       }
-       if (description) {
-               ast_str_set(&item->description, 0, "%s", description);
+       if (seealso) {
+               ast_str_set(&item->seealso, 0, "%s", seealso);
        }
 
-       ast_free(syntax);
-       ast_free(seealso);
-       ast_free(arguments);
        ast_free(synopsis);
+       ast_free(since);
        ast_free(description);
+       ast_free(syntax);
+       ast_free(arguments);
+       ast_free(seealso);
 
        return item;
 }
index b3eb8034d6bd9e301c675a655c51928328c26dd0..f008139611e8487c31e86fd4b3e8d31c8ae5fc57 100644 (file)
@@ -3834,8 +3834,10 @@ int AST_OPTIONAL_API_NAME(ast_agi_register)(struct ast_module *mod, agi_command
                if (ast_strlen_zero(cmd->summary) && ast_strlen_zero(cmd->usage)) {
 #ifdef AST_XML_DOCS
                        *((char **) &cmd->summary) = ast_xmldoc_build_synopsis("agi", fullcmd, NULL);
+                       *((char **) &cmd->since) = ast_xmldoc_build_since("agi", fullcmd, NULL);
                        *((char **) &cmd->usage) = ast_xmldoc_build_description("agi", fullcmd, NULL);
                        *((char **) &cmd->syntax) = ast_xmldoc_build_syntax("agi", fullcmd, NULL);
+                       *((char **) &cmd->arguments) = ast_xmldoc_build_arguments("agi", fullcmd, NULL);
                        *((char **) &cmd->seealso) = ast_xmldoc_build_seealso("agi", fullcmd, NULL);
                        *((enum ast_doc_src *) &cmd->docsrc) = AST_XML_DOC;
 #endif
@@ -3882,12 +3884,16 @@ int AST_OPTIONAL_API_NAME(ast_agi_unregister)(agi_command *cmd)
 #ifdef AST_XML_DOCS
                        if (e->docsrc == AST_XML_DOC) {
                                ast_free((char *) e->summary);
+                               ast_free((char *) e->since);
                                ast_free((char *) e->usage);
                                ast_free((char *) e->syntax);
+                               ast_free((char *) e->arguments);
                                ast_free((char *) e->seealso);
                                *((char **) &e->summary) = NULL;
+                               *((char **) &e->since) = NULL;
                                *((char **) &e->usage) = NULL;
                                *((char **) &e->syntax) = NULL;
+                               *((char **) &e->arguments) = NULL;
                                *((char **) &e->seealso) = NULL;
                        }
 #endif
@@ -4346,72 +4352,66 @@ static char *handle_cli_agi_show(struct ast_cli_entry *e, int cmd, struct ast_cl
        if (a->argc > e->args - 1) {
                command = find_command(a->argv + e->args, 1);
                if (command) {
-                       char *synopsis = NULL, *description = NULL, *syntax = NULL, *seealso = NULL;
-                       char info[30 + MAX_CMD_LEN];                                    /* '-= Info about...' */
-                       char infotitle[30 + MAX_CMD_LEN + AST_TERM_MAX_ESCAPE_CHARS];   /* '-= Info about...' with colors */
-                       char syntitle[11 + AST_TERM_MAX_ESCAPE_CHARS];                  /* [Syntax]\n with colors */
-                       char desctitle[15 + AST_TERM_MAX_ESCAPE_CHARS];                 /* [Description]\n with colors */
-                       char deadtitle[13 + AST_TERM_MAX_ESCAPE_CHARS];                 /* [Runs Dead]\n with colors */
-                       char deadcontent[3 + AST_TERM_MAX_ESCAPE_CHARS];                /* 'Yes' or 'No' with colors */
-                       char seealsotitle[12 + AST_TERM_MAX_ESCAPE_CHARS];              /* [See Also]\n with colors */
-                       char stxtitle[10 + AST_TERM_MAX_ESCAPE_CHARS];                  /* [Syntax]\n with colors */
-                       size_t synlen, desclen, seealsolen, stxlen;
-
-                       term_color(syntitle, "[Synopsis]\n", COLOR_MAGENTA, 0, sizeof(syntitle));
-                       term_color(desctitle, "[Description]\n", COLOR_MAGENTA, 0, sizeof(desctitle));
-                       term_color(deadtitle, "[Runs Dead]\n", COLOR_MAGENTA, 0, sizeof(deadtitle));
-                       term_color(seealsotitle, "[See Also]\n", COLOR_MAGENTA, 0, sizeof(seealsotitle));
-                       term_color(stxtitle, "[Syntax]\n", COLOR_MAGENTA, 0, sizeof(stxtitle));
-                       term_color(deadcontent, command->dead ? "Yes" : "No", COLOR_CYAN, 0, sizeof(deadcontent));
+                       char *synopsis = NULL, *since = NULL, *description = NULL, *syntax = NULL, *arguments = NULL, *seealso = NULL;
 
                        ast_join(fullcmd, sizeof(fullcmd), a->argv + e->args);
-                       snprintf(info, sizeof(info), "\n  -= Info about agi '%s' =- ", fullcmd);
-                       term_color(infotitle, info, COLOR_CYAN, 0, sizeof(infotitle));
+
 #ifdef AST_XML_DOCS
                        if (command->docsrc == AST_XML_DOC) {
                                synopsis = ast_xmldoc_printable(S_OR(command->summary, "Not available"), 1);
+                               since = ast_xmldoc_printable(S_OR(command->since, "Not available"), 1);
                                description = ast_xmldoc_printable(S_OR(command->usage, "Not available"), 1);
+                               syntax = ast_xmldoc_printable(S_OR(command->syntax, "Not available"), 1);
+                               arguments = ast_xmldoc_printable(S_OR(command->arguments, "Not available"), 1);
                                seealso = ast_xmldoc_printable(S_OR(command->seealso, "Not available"), 1);
-                               if (!seealso || !description || !synopsis) {
-                                       error = 1;
-                                       goto return_cleanup;
-                               }
                        } else
 #endif
                        {
-                               synlen = strlen(S_OR(command->summary, "Not available")) + AST_TERM_MAX_ESCAPE_CHARS;
-                               synopsis = ast_malloc(synlen);
-
-                               desclen = strlen(S_OR(command->usage, "Not available")) + AST_TERM_MAX_ESCAPE_CHARS;
-                               description = ast_malloc(desclen);
-
-                               seealsolen = strlen(S_OR(command->seealso, "Not available")) + AST_TERM_MAX_ESCAPE_CHARS;
-                               seealso = ast_malloc(seealsolen);
-
-                               if (!synopsis || !description || !seealso) {
-                                       error = 1;
-                                       goto return_cleanup;
-                               }
-                               term_color(synopsis, S_OR(command->summary, "Not available"), COLOR_CYAN, 0, synlen);
-                               term_color(description, S_OR(command->usage, "Not available"), COLOR_CYAN, 0, desclen);
-                               term_color(seealso, S_OR(command->seealso, "Not available"), COLOR_CYAN, 0, seealsolen);
+                               synopsis = ast_strdup(S_OR(command->summary, "Not Available"));
+                               since = ast_strdup(S_OR(command->since, "Not Available"));
+                               description = ast_strdup(S_OR(command->usage, "Not Available"));
+                               syntax = ast_strdup(S_OR(command->syntax, "Not Available"));
+                               arguments = ast_strdup(S_OR(command->arguments, "Not Available"));
+                               seealso = ast_strdup(S_OR(command->seealso, "Not Available"));
                        }
 
-                       stxlen = strlen(S_OR(command->syntax, "Not available")) + AST_TERM_MAX_ESCAPE_CHARS;
-                       syntax = ast_malloc(stxlen);
-                       if (!syntax) {
+                       if (!synopsis || !since || !description || !syntax || !arguments || !seealso) {
                                error = 1;
                                goto return_cleanup;
                        }
-                       term_color(syntax, S_OR(command->syntax, "Not available"), COLOR_CYAN, 0, stxlen);
 
-                       ast_cli(a->fd, "%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n", infotitle, stxtitle, syntax,
-                                       desctitle, description, syntitle, synopsis, deadtitle, deadcontent,
-                                       seealsotitle, seealso);
+                       ast_cli(a->fd, "\n"
+                               "%s  -= Info about AGI '%s' =- %s\n\n"
+                               COLORIZE_FMT "\n"
+                               "%s\n\n"
+                               COLORIZE_FMT "\n"
+                               "%s\n\n"
+                               COLORIZE_FMT "\n"
+                               "%s\n\n"
+                               COLORIZE_FMT "\n"
+                               "%s\n\n"
+                               COLORIZE_FMT "\n"
+                               "%s\n\n"
+                               COLORIZE_FMT "\n"
+                               "%s\n\n"
+                               COLORIZE_FMT "\n"
+                               "%s\n\n",
+                               ast_term_color(COLOR_MAGENTA, 0), fullcmd, ast_term_reset(),
+                               COLORIZE(COLOR_MAGENTA, 0, "[Synopsis]"), synopsis,
+                               COLORIZE(COLOR_MAGENTA, 0, "[Since]"), since,
+                               COLORIZE(COLOR_MAGENTA, 0, "[Description]"), description,
+                               COLORIZE(COLOR_MAGENTA, 0, "[Syntax]"), syntax,
+                               COLORIZE(COLOR_MAGENTA, 0, "[Arguments]"), arguments,
+                               COLORIZE(COLOR_MAGENTA, 0, "[Runs Dead]"), command->dead ? "Yes" : "No",
+                               COLORIZE(COLOR_MAGENTA, 0, "[See Also]"), seealso
+                               );
+
 return_cleanup:
                        ast_free(synopsis);
+                       ast_free(since);
                        ast_free(description);
                        ast_free(syntax);
+                       ast_free(arguments);
                        ast_free(seealso);
                } else {
                        if (find_command(a->argv + e->args, -1)) {
index 268064582279c2db6e7827923043a6debedba164..57da975901b5aa4490eb82240b32d6eee4fc7ec6 100644 (file)
@@ -1,11 +1,14 @@
 {{#api_declaration}}
 # {{name_title}}
+{{#since}}
+## Since: {{since}}
+{{/since}}
 
-| Method | Path (Parameters are case-sensitive) | Return Model | Summary |
-|:------ |:------------------------------------ |:------------ |:------- |
+| Method | Path (Parameters are case-sensitive) | Return Model | Summary | Since |
+|:------ |:------------------------------------ |:------------ |:------- |:----- |
 {{#apis}}
 {{#operations}}
-| {{http_method}} | [{{wiki_path}}](#{{nickname_lc}}) | {{#response_class}}{{#is_primitive}}{{name}}{{/is_primitive}}{{^is_primitive}}[{{wiki_name}}]({{wiki_prefix}}Asterisk_REST_Data_Models#{{lc_singular_name}}){{/is_primitive}}{{/response_class}} | {{{summary}}} |
+| {{http_method}} | [{{wiki_path}}](#{{nickname_lc}}) | {{#response_class}}{{#is_primitive}}{{name}}{{/is_primitive}}{{^is_primitive}}[{{wiki_name}}]({{wiki_prefix}}Asterisk_REST_Data_Models#{{lc_singular_name}}){{/is_primitive}}{{/response_class}} | {{{summary}}} | {{since}} |
 {{/operations}}
 {{/apis}}
 {{#apis}}
@@ -14,6 +17,9 @@
 ---
 [//]: # (anchor:{{nickname_lc}})
 ## {{nickname}}
+{{#since}}
+### Since: {{since}}
+{{/since}}
 ### {{http_method}} {{wiki_path}}
 {{{wiki_summary}}}{{#wiki_notes}} {{{wiki_notes}}}{{/wiki_notes}}
 {{#has_path_parameters}}
index 23dcb8cbf1b8c3b4a02d508a1c00e50637d3dd73..cfc23a18515b2aff092893a38aca5621d07ef9dc 100644 (file)
@@ -373,6 +373,7 @@ class Operation(Stringify):
         self.summary = None
         self.notes = None
         self.error_responses = []
+        self.since = []
 
     def load(self, op_json, processor, context):
         context = context.next_stack(op_json, 'nickname')
@@ -383,6 +384,8 @@ class Operation(Stringify):
         response_class = op_json.get('responseClass')
         self.response_class = response_class and SwaggerType().load(
             response_class, processor, context)
+        since = op_json.get('since') or []
+        self.since = ", ".join(since)
 
         # Specifying WebSocket URL's is our own extension
         self.is_websocket = op_json.get('upgrade') == 'websocket'
@@ -611,6 +614,7 @@ class ApiDeclaration(Stringify):
         self.api_version = None
         self.base_path = None
         self.resource_path = None
+        self.since = []
         self.apis = []
         self.models = []
 
@@ -658,6 +662,8 @@ class ApiDeclaration(Stringify):
         self.base_path = api_decl_json.get('basePath')
         self.resource_path = api_decl_json.get('resourcePath')
         self.requires_modules = api_decl_json.get('requiresModules') or []
+        since = api_decl_json.get('since') or []
+        self.since = ", ".join(since)
         api_json = api_decl_json.get('apis') or []
         self.apis = [
             Api().load(j, processor, context) for j in api_json]