]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
CLI: Create ast_cli_completion_vector.
authorCorey Farrell <git@cfware.com>
Tue, 7 Nov 2017 23:07:35 +0000 (18:07 -0500)
committerCorey Farrell <git@cfware.com>
Fri, 17 Nov 2017 14:28:43 +0000 (09:28 -0500)
This is a rewrite of ast_cli_completion_matches using a vector to build
the list.  The original function calls the vector version, NULL
terminates the vector and extracts the elements array.

One change in behavior the results are now sorted and deduplicated. This
will solve bugs where some duplicate checking was done before the list
was sorted.

Change-Id: Iede20c5b4d965fa5ec71fda136ce9425eeb69519

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

index c51d89eb882bfea56b32fec83bd9de7d6878ec2e..7bf473c61bf7f3d3cc74ea2dd7b40c3df829662f 100644 (file)
@@ -300,6 +300,27 @@ int ast_cli_generatornummatches(const char *, const char *);
  */
 char **ast_cli_completion_matches(const char *, const char *);
 
+/*!
+ * \brief Generates a vector of strings for CLI completion.
+ *
+ * \param text Complete input being matched.
+ * \param word Current word being matched
+ *
+ * The results contain strings that both:
+ * 1) Begin with the string in \a word.
+ * 2) Are valid in a command after the string in \a text.
+ *
+ * The first entry (offset 0) of the result is the longest common substring
+ * in the results, useful to extend the string that has been completed.
+ * Subsequent entries are all possible values.
+ *
+ * \note All strings and the vector itself are malloc'ed and must be freed
+ *       by the caller.
+ *
+ * \note The vector is sorted and does not contain any duplicates.
+ */
+struct ast_vector_string *ast_cli_completion_vector(const char *text, const char *word);
+
 /*!
  * \brief Command completion for the list of active channels.
  *
index 64882a3a72b3085c7aa8ffcaf1d4b5921dc42d18..a9f02fd0113f76ab0abe95c7d3786941d010576b 100644 (file)
@@ -2476,76 +2476,98 @@ int ast_cli_generatornummatches(const char *text, const char *word)
        return matches;
 }
 
-static void destroy_match_list(char **match_list, int matches)
+char **ast_cli_completion_matches(const char *text, const char *word)
 {
-       if (match_list) {
-               int idx;
+       struct ast_vector_string *vec = ast_cli_completion_vector(text, word);
+       char **match_list;
 
-               for (idx = 1; idx < matches; ++idx) {
-                       ast_free(match_list[idx]);
-               }
-               ast_free(match_list);
+       if (!vec) {
+               return NULL;
+       }
+
+       if (AST_VECTOR_APPEND(vec, NULL)) {
+               /* We failed to NULL terminate the elements */
+               AST_VECTOR_CALLBACK_VOID(vec, ast_free);
+               AST_VECTOR_PTR_FREE(vec);
+
+               return NULL;
        }
+
+       match_list = AST_VECTOR_STEAL_ELEMENTS(vec);
+       AST_VECTOR_PTR_FREE(vec);
+
+       return match_list;
 }
 
-char **ast_cli_completion_matches(const char *text, const char *word)
+struct ast_vector_string *ast_cli_completion_vector(const char *text, const char *word)
 {
-       char **match_list = NULL, *retstr, *prevstr;
-       char **new_list;
-       size_t match_list_len, max_equal, which, i;
-       int matches = 0;
+       char *retstr, *prevstr;
+       size_t max_equal;
+       size_t which = 0;
+       struct ast_vector_string *vec = ast_calloc(1, sizeof(*vec));
 
-       /* leave entry 0 free for the longest common substring */
-       match_list_len = 1;
-       while ((retstr = ast_cli_generator(text, word, matches)) != NULL) {
-               if (matches + 1 >= match_list_len) {
-                       match_list_len <<= 1;
-                       new_list = ast_realloc(match_list, match_list_len * sizeof(*match_list));
-                       if (!new_list) {
-                               destroy_match_list(match_list, matches);
-                               return NULL;
-                       }
-                       match_list = new_list;
+       if (!vec) {
+               return NULL;
+       }
+
+       while ((retstr = ast_cli_generator(text, word, which)) != NULL) {
+               if (AST_VECTOR_ADD_SORTED(vec, retstr, strcasecmp)) {
+                       ast_free(retstr);
+
+                       goto vector_cleanup;
                }
-               match_list[++matches] = retstr;
+
+               ++which;
        }
 
-       if (!match_list) {
-               return match_list; /* NULL */
+       if (!AST_VECTOR_SIZE(vec)) {
+               AST_VECTOR_PTR_FREE(vec);
+
+               return NULL;
        }
 
+       prevstr = AST_VECTOR_GET(vec, 0);
+       max_equal = strlen(prevstr);
+       which = 1;
+
        /* Find the longest substring that is common to all results
         * (it is a candidate for completion), and store a copy in entry 0.
         */
-       prevstr = match_list[1];
-       max_equal = strlen(prevstr);
-       for (which = 2; which <= matches; which++) {
-               for (i = 0; i < max_equal && toupper(prevstr[i]) == toupper(match_list[which][i]); i++)
+       while (which < AST_VECTOR_SIZE(vec)) {
+               size_t i = 0;
+
+               retstr = AST_VECTOR_GET(vec, which);
+               /* Check for and remove duplicate strings. */
+               if (!strcasecmp(prevstr, retstr)) {
+                       AST_VECTOR_REMOVE(vec, which, 1);
+                       ast_free(retstr);
+
                        continue;
+               }
+
+               while (i < max_equal && toupper(prevstr[i]) == toupper(retstr[i])) {
+                       i++;
+               }
+
                max_equal = i;
+               prevstr = retstr;
+               ++which;
        }
 
-       retstr = ast_malloc(max_equal + 1);
-       if (!retstr) {
-               destroy_match_list(match_list, matches);
-               return NULL;
+       /* Insert longest match to position 0. */
+       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;
        }
-       ast_copy_string(retstr, match_list[1], max_equal + 1);
-       match_list[0] = retstr;
 
-       /* ensure that the array is NULL terminated */
-       if (matches + 1 >= match_list_len) {
-               new_list = ast_realloc(match_list, (match_list_len + 1) * sizeof(*match_list));
-               if (!new_list) {
-                       ast_free(retstr);
-                       destroy_match_list(match_list, matches);
-                       return NULL;
-               }
-               match_list = new_list;
-       }
-       match_list[matches + 1] = NULL;
+       return vec;
 
-       return match_list;
+vector_cleanup:
+       AST_VECTOR_CALLBACK_VOID(vec, ast_free);
+       AST_VECTOR_PTR_FREE(vec);
+
+       return NULL;
 }
 
 /*! \brief returns true if there are more words to match */