]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
CLI: Create ast_cli_completion_add function.
authorCorey Farrell <git@cfware.com>
Thu, 9 Nov 2017 05:42:10 +0000 (00:42 -0500)
committerCorey Farrell <git@cfware.com>
Tue, 21 Nov 2017 14:47:40 +0000 (09:47 -0500)
Some completion generators are very inefficent due to the way CLI
requests matches one at a time.  ast_cli_completion_add can be called
multiple times during one invokation of a CLI generator to add all
results without having to reinitialize the search state for each match.

Change-Id: I73d26d270bbbe1e3e6390799cfc1b639e39cceec

include/asterisk/cli.h
main/cli.c

index 0a05a4c112a1377405c95862a130fdba30244f67..c2401a85d3efe4898e55ff03071ccd5dadccb409 100644 (file)
@@ -300,6 +300,9 @@ int ast_cli_generatornummatches(const char *, const char *);
  * Subsequent entries are all possible values, followed by a NULL.
  * All strings and the array itself are malloc'ed and must be freed
  * by the caller.
+ *
+ * \warning This function cannot be called recursively so it will always
+ *          fail if called from a CLI_GENERATE callback.
  */
 char **ast_cli_completion_matches(const char *, const char *);
 
@@ -321,9 +324,29 @@ char **ast_cli_completion_matches(const char *, const char *);
  *       by the caller.
  *
  * \note The vector is sorted and does not contain any duplicates.
+ *
+ * \warning This function cannot be called recursively so it will always
+ *          fail if called from a CLI_GENERATE callback.
  */
 struct ast_vector_string *ast_cli_completion_vector(const char *text, const char *word);
 
+/*!
+ * \brief Add a result to a request for completion options.
+ *
+ * \param value A completion option text.
+ *
+ * \retval 0 Success
+ * \retval -1 Failure
+ *
+ * This is an alternative to returning individual values from CLI_GENERATE.  Instead
+ * of repeatedly being asked for the next match and having to start over, you can
+ * call this function repeatedly from your own stateful loop.  When all matches have
+ * been added you can return NULL from the CLI_GENERATE function.
+ *
+ * \note This function always eventually results in calling ast_free on \a value.
+ */
+int ast_cli_completion_add(char *value);
+
 /*!
  * \brief Command completion for the list of active channels.
  *
index 94ea959568d0adba2c0d6f1921a491f25b57152c..8e0cc3bd3ec1fd54b1b021411019192e0914afe4 100644 (file)
@@ -2500,6 +2500,44 @@ char **ast_cli_completion_matches(const char *text, const char *word)
        return match_list;
 }
 
+AST_THREADSTORAGE_RAW(completion_storage);
+
+/*!
+ * \internal
+ * \brief Add a value to the vector.
+ *
+ * \param vec Vector to add \a value to. Must be from threadstorage.
+ * \param value The value to add.
+ *
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+static int cli_completion_vector_add(struct ast_vector_string *vec, char *value)
+{
+       if (!value) {
+               return 0;
+       }
+
+       if (!vec || AST_VECTOR_ADD_SORTED(vec, value, strcasecmp)) {
+               if (vec) {
+                       ast_threadstorage_set_ptr(&completion_storage, NULL);
+
+                       AST_VECTOR_CALLBACK_VOID(vec, ast_free);
+                       AST_VECTOR_FREE(vec);
+               }
+               ast_free(value);
+
+               return -1;
+       }
+
+       return 0;
+}
+
+int ast_cli_completion_add(char *value)
+{
+       return cli_completion_vector_add(ast_threadstorage_get_ptr(&completion_storage), value);
+}
+
 struct ast_vector_string *ast_cli_completion_vector(const char *text, const char *word)
 {
        char *retstr, *prevstr;
@@ -2507,13 +2545,23 @@ struct ast_vector_string *ast_cli_completion_vector(const char *text, const char
        size_t which = 0;
        struct ast_vector_string *vec = ast_calloc(1, sizeof(*vec));
 
+       /* Recursion into this function is a coding error. */
+       ast_assert(!ast_threadstorage_get_ptr(&completion_storage));
+
        if (!vec) {
                return NULL;
        }
 
+       if (ast_threadstorage_set_ptr(&completion_storage, vec)) {
+               ast_log(LOG_ERROR, "Failed to initialize threadstorage for completion.\n");
+               ast_free(vec);
+
+               return NULL;
+       }
+
        while ((retstr = ast_cli_generator(text, word, which)) != NULL) {
-               if (AST_VECTOR_ADD_SORTED(vec, retstr, strcasecmp)) {
-                       ast_free(retstr);
+               if (cli_completion_vector_add(vec, retstr)) {
+                       ast_threadstorage_set_ptr(&completion_storage, NULL);
 
                        goto vector_cleanup;
                }
@@ -2521,6 +2569,8 @@ struct ast_vector_string *ast_cli_completion_vector(const char *text, const char
                ++which;
        }
 
+       ast_threadstorage_set_ptr(&completion_storage, NULL);
+
        if (!AST_VECTOR_SIZE(vec)) {
                AST_VECTOR_PTR_FREE(vec);
 
@@ -2559,6 +2609,7 @@ struct ast_vector_string *ast_cli_completion_vector(const char *text, const char
        retstr = ast_strndup(AST_VECTOR_GET(vec, 0), max_equal);
        if (!retstr || AST_VECTOR_INSERT_AT(vec, 0, retstr)) {
                ast_free(retstr);
+
                goto vector_cleanup;
        }