<p>Joshua Colp <strong>merged</strong> this change.</p><p><a href="https://gerrit.asterisk.org/7257">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  Joshua Colp: Looks good to me, but someone else must approve
  Kevin Harwell: Looks good to me, approved
  Jenkins2: Approved for Submit

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">CLI: Create ast_cli_completion_vector.<br><br>This is a rewrite of ast_cli_completion_matches using a vector to build<br>the list.  The original function calls the vector version, NULL<br>terminates the vector and extracts the elements array.<br><br>One change in behavior the results are now sorted and deduplicated. This<br>will solve bugs where some duplicate checking was done before the list<br>was sorted.<br><br>Change-Id: Iede20c5b4d965fa5ec71fda136ce9425eeb69519<br>---<br>M include/asterisk/cli.h<br>M main/cli.c<br>2 files changed, 99 insertions(+), 56 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">diff --git a/include/asterisk/cli.h b/include/asterisk/cli.h<br>index c51d89e..7bf473c 100644<br>--- a/include/asterisk/cli.h<br>+++ b/include/asterisk/cli.h<br>@@ -301,6 +301,27 @@<br> char **ast_cli_completion_matches(const char *, const char *);<br> <br> /*!<br>+ * \brief Generates a vector of strings for CLI completion.<br>+ *<br>+ * \param text Complete input being matched.<br>+ * \param word Current word being matched<br>+ *<br>+ * The results contain strings that both:<br>+ * 1) Begin with the string in \a word.<br>+ * 2) Are valid in a command after the string in \a text.<br>+ *<br>+ * The first entry (offset 0) of the result is the longest common substring<br>+ * in the results, useful to extend the string that has been completed.<br>+ * Subsequent entries are all possible values.<br>+ *<br>+ * \note All strings and the vector itself are malloc'ed and must be freed<br>+ *       by the caller.<br>+ *<br>+ * \note The vector is sorted and does not contain any duplicates.<br>+ */<br>+struct ast_vector_string *ast_cli_completion_vector(const char *text, const char *word);<br>+<br>+/*!<br>  * \brief Command completion for the list of active channels.<br>  *<br>  * This can be called from a CLI command completion function that wants to<br>diff --git a/main/cli.c b/main/cli.c<br>index 64882a3..a9f02fd 100644<br>--- a/main/cli.c<br>+++ b/main/cli.c<br>@@ -2476,76 +2476,98 @@<br>        return matches;<br> }<br> <br>-static void destroy_match_list(char **match_list, int matches)<br>-{<br>-  if (match_list) {<br>-            int idx;<br>-<br>-          for (idx = 1; idx < matches; ++idx) {<br>-                     ast_free(match_list[idx]);<br>-           }<br>-            ast_free(match_list);<br>-        }<br>-}<br>-<br> char **ast_cli_completion_matches(const char *text, const char *word)<br> {<br>- char **match_list = NULL, *retstr, *prevstr;<br>- char **new_list;<br>-     size_t match_list_len, max_equal, which, i;<br>-  int matches = 0;<br>+     struct ast_vector_string *vec = ast_cli_completion_vector(text, word);<br>+       char **match_list;<br> <br>-        /* leave entry 0 free for the longest common substring */<br>-    match_list_len = 1;<br>-  while ((retstr = ast_cli_generator(text, word, matches)) != NULL) {<br>-          if (matches + 1 >= match_list_len) {<br>-                      match_list_len <<= 1;<br>-                  new_list = ast_realloc(match_list, match_list_len * sizeof(*match_list));<br>-                    if (!new_list) {<br>-                             destroy_match_list(match_list, matches);<br>-                             return NULL;<br>-                 }<br>-                    match_list = new_list;<br>+       if (!vec) {<br>+          return NULL;<br>+ }<br>+<br>+ if (AST_VECTOR_APPEND(vec, NULL)) {<br>+          /* We failed to NULL terminate the elements */<br>+               AST_VECTOR_CALLBACK_VOID(vec, ast_free);<br>+             AST_VECTOR_PTR_FREE(vec);<br>+<br>+         return NULL;<br>+ }<br>+<br>+ match_list = AST_VECTOR_STEAL_ELEMENTS(vec);<br>+ AST_VECTOR_PTR_FREE(vec);<br>+<br>+ return match_list;<br>+}<br>+<br>+struct ast_vector_string *ast_cli_completion_vector(const char *text, const char *word)<br>+{<br>+      char *retstr, *prevstr;<br>+      size_t max_equal;<br>+    size_t which = 0;<br>+    struct ast_vector_string *vec = ast_calloc(1, sizeof(*vec));<br>+<br>+      if (!vec) {<br>+          return NULL;<br>+ }<br>+<br>+ while ((retstr = ast_cli_generator(text, word, which)) != NULL) {<br>+            if (AST_VECTOR_ADD_SORTED(vec, retstr, strcasecmp)) {<br>+                        ast_free(retstr);<br>+<br>+                 goto vector_cleanup;<br>          }<br>-            match_list[++matches] = retstr;<br>+<br>+           ++which;<br>      }<br> <br>- if (!match_list) {<br>-           return match_list; /* NULL */<br>+        if (!AST_VECTOR_SIZE(vec)) {<br>+         AST_VECTOR_PTR_FREE(vec);<br>+<br>+         return NULL;<br>  }<br>+<br>+ prevstr = AST_VECTOR_GET(vec, 0);<br>+    max_equal = strlen(prevstr);<br>+ which = 1;<br> <br>         /* Find the longest substring that is common to all results<br>    * (it is a candidate for completion), and store a copy in entry 0.<br>    */<br>-  prevstr = match_list[1];<br>-     max_equal = strlen(prevstr);<br>- for (which = 2; which <= matches; which++) {<br>-              for (i = 0; i < max_equal && toupper(prevstr[i]) == toupper(match_list[which][i]); i++)<br>-                   continue;<br>-            max_equal = i;<br>-       }<br>+    while (which < AST_VECTOR_SIZE(vec)) {<br>+            size_t i = 0;<br> <br>-     retstr = ast_malloc(max_equal + 1);<br>-  if (!retstr) {<br>-               destroy_match_list(match_list, matches);<br>-             return NULL;<br>- }<br>-    ast_copy_string(retstr, match_list[1], max_equal + 1);<br>-       match_list[0] = retstr;<br>-<br>-   /* ensure that the array is NULL terminated */<br>-       if (matches + 1 >= match_list_len) {<br>-              new_list = ast_realloc(match_list, (match_list_len + 1) * sizeof(*match_list));<br>-              if (!new_list) {<br>+             retstr = AST_VECTOR_GET(vec, which);<br>+         /* Check for and remove duplicate strings. */<br>+                if (!strcasecmp(prevstr, retstr)) {<br>+                  AST_VECTOR_REMOVE(vec, which, 1);<br>                     ast_free(retstr);<br>-                    destroy_match_list(match_list, matches);<br>-                     return NULL;<br>-         }<br>-            match_list = new_list;<br>-       }<br>-    match_list[matches + 1] = NULL;<br> <br>-   return match_list;<br>+                   continue;<br>+            }<br>+<br>+         while (i < max_equal && toupper(prevstr[i]) == toupper(retstr[i])) {<br>+                      i++;<br>+         }<br>+<br>+         max_equal = i;<br>+               prevstr = retstr;<br>+            ++which;<br>+     }<br>+<br>+ /* Insert longest match to position 0. */<br>+    retstr = ast_strndup(AST_VECTOR_GET(vec, 0), max_equal);<br>+     if (!retstr || AST_VECTOR_INSERT_AT(vec, 0, retstr)) {<br>+               ast_free(retstr);<br>+            goto vector_cleanup;<br>+ }<br>+<br>+ return vec;<br>+<br>+vector_cleanup:<br>+     AST_VECTOR_CALLBACK_VOID(vec, ast_free);<br>+     AST_VECTOR_PTR_FREE(vec);<br>+<br>+ return NULL;<br> }<br> <br> /*! \brief returns true if there are more words to match */<br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/7257">change 7257</a>. To unsubscribe, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/7257"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: 13 </div>
<div style="display:none"> Gerrit-MessageType: merged </div>
<div style="display:none"> Gerrit-Change-Id: Iede20c5b4d965fa5ec71fda136ce9425eeb69519 </div>
<div style="display:none"> Gerrit-Change-Number: 7257 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Corey Farrell <git@cfware.com> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins2 </div>
<div style="display:none"> Gerrit-Reviewer: Joshua Colp <jcolp@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Kevin Harwell <kharwell@digium.com> </div>