<p>George Joseph <strong>merged</strong> this change.</p><p><a href="https://gerrit.asterisk.org/7256">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  Kevin Harwell: Looks good to me, but someone else must approve
  Joshua Colp: Looks good to me, but someone else must approve
  George Joseph: 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_add function.<br><br>Some completion generators are very inefficent due to the way CLI<br>requests matches one at a time.  ast_cli_completion_add can be called<br>multiple times during one invokation of a CLI generator to add all<br>results without having to reinitialize the search state for each match.<br><br>Change-Id: I73d26d270bbbe1e3e6390799cfc1b639e39cceec<br>---<br>M include/asterisk/cli.h<br>M main/cli.c<br>2 files changed, 76 insertions(+), 2 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 51f077f..c75fc29 100644<br>--- a/include/asterisk/cli.h<br>+++ b/include/asterisk/cli.h<br>@@ -305,6 +305,9 @@<br>  * Subsequent entries are all possible values, followed by a NULL.<br>  * All strings and the array itself are malloc'ed and must be freed<br>  * by the caller.<br>+ *<br>+ * \warning This function cannot be called recursively so it will always<br>+ *          fail if called from a CLI_GENERATE callback.<br>  */<br> char **ast_cli_completion_matches(const char *, const char *);<br> <br>@@ -326,10 +329,30 @@<br>  *       by the caller.<br>  *<br>  * \note The vector is sorted and does not contain any duplicates.<br>+ *<br>+ * \warning This function cannot be called recursively so it will always<br>+ *          fail if called from a CLI_GENERATE callback.<br>  */<br> struct ast_vector_string *ast_cli_completion_vector(const char *text, const char *word);<br> <br> /*!<br>+ * \brief Add a result to a request for completion options.<br>+ *<br>+ * \param value A completion option text.<br>+ *<br>+ * \retval 0 Success<br>+ * \retval -1 Failure<br>+ *<br>+ * This is an alternative to returning individual values from CLI_GENERATE.  Instead<br>+ * of repeatedly being asked for the next match and having to start over, you can<br>+ * call this function repeatedly from your own stateful loop.  When all matches have<br>+ * been added you can return NULL from the CLI_GENERATE function.<br>+ *<br>+ * \note This function always eventually results in calling ast_free on \a value.<br>+ */<br>+int ast_cli_completion_add(char *value);<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 1653838..0f023b2 100644<br>--- a/main/cli.c<br>+++ b/main/cli.c<br>@@ -2512,6 +2512,44 @@<br>         return match_list;<br> }<br> <br>+AST_THREADSTORAGE_RAW(completion_storage);<br>+<br>+/*!<br>+ * \internal<br>+ * \brief Add a value to the vector.<br>+ *<br>+ * \param vec Vector to add \a value to. Must be from threadstorage.<br>+ * \param value The value to add.<br>+ *<br>+ * \retval 0 Success<br>+ * \retval -1 Failure<br>+ */<br>+static int cli_completion_vector_add(struct ast_vector_string *vec, char *value)<br>+{<br>+       if (!value) {<br>+                return 0;<br>+    }<br>+<br>+ if (!vec || AST_VECTOR_ADD_SORTED(vec, value, strcasecmp)) {<br>+         if (vec) {<br>+                   ast_threadstorage_set_ptr(&completion_storage, NULL);<br>+<br>+                 AST_VECTOR_CALLBACK_VOID(vec, ast_free);<br>+                     AST_VECTOR_FREE(vec);<br>+                }<br>+            ast_free(value);<br>+<br>+          return -1;<br>+   }<br>+<br>+ return 0;<br>+}<br>+<br>+int ast_cli_completion_add(char *value)<br>+{<br>+       return cli_completion_vector_add(ast_threadstorage_get_ptr(&completion_storage), value);<br>+}<br>+<br> struct ast_vector_string *ast_cli_completion_vector(const char *text, const char *word)<br> {<br>     char *retstr, *prevstr;<br>@@ -2519,19 +2557,31 @@<br>      size_t which = 0;<br>     struct ast_vector_string *vec = ast_calloc(1, sizeof(*vec));<br> <br>+      /* Recursion into this function is a coding error. */<br>+        ast_assert(!ast_threadstorage_get_ptr(&completion_storage));<br>+<br>   if (!vec) {<br>           return NULL;<br>  }<br> <br>+ if (ast_threadstorage_set_ptr(&completion_storage, vec)) {<br>+               ast_log(LOG_ERROR, "Failed to initialize threadstorage for completion.\n");<br>+                ast_free(vec);<br>+<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>+            if (cli_completion_vector_add(vec, retstr)) {<br>+                        ast_threadstorage_set_ptr(&completion_storage, NULL);<br> <br>                  goto vector_cleanup;<br>          }<br> <br>          ++which;<br>      }<br>+<br>+ ast_threadstorage_set_ptr(&completion_storage, NULL);<br> <br>  if (!AST_VECTOR_SIZE(vec)) {<br>          AST_VECTOR_PTR_FREE(vec);<br>@@ -2571,6 +2621,7 @@<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>+<br>          goto vector_cleanup;<br>  }<br> <br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/7256">change 7256</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/7256"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: 15 </div>
<div style="display:none"> Gerrit-MessageType: merged </div>
<div style="display:none"> Gerrit-Change-Id: I73d26d270bbbe1e3e6390799cfc1b639e39cceec </div>
<div style="display:none"> Gerrit-Change-Number: 7256 </div>
<div style="display:none"> Gerrit-PatchSet: 3 </div>
<div style="display:none"> Gerrit-Owner: Corey Farrell <git@cfware.com> </div>
<div style="display:none"> Gerrit-Reviewer: George Joseph <gjoseph@digium.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>