<p>Corey Farrell has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/7259">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">CLI: Create ast_cli_completion_add funciton.<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, 74 insertions(+), 3 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/59/7259/1</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 0a05a4c..c2401a8 100644<br>--- a/include/asterisk/cli.h<br>+++ b/include/asterisk/cli.h<br>@@ -300,6 +300,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>@@ -321,10 +324,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 55130e9..7d17765 100644<br>--- a/main/cli.c<br>+++ b/main/cli.c<br>@@ -2498,6 +2498,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>@@ -2505,20 +2543,30 @@<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>- while ((retstr = ast_cli_generator(text, word, which)) != NULL) {<br>-            if (AST_VECTOR_ADD_SORTED(vec, retstr, strcasecmp)) {<br>-                        ast_free(retstr);<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 (cli_completion_vector_add(vec, retstr)) {<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> <br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/7259">change 7259</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/7259"/><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: newchange </div>
<div style="display:none"> Gerrit-Change-Id: I73d26d270bbbe1e3e6390799cfc1b639e39cceec </div>
<div style="display:none"> Gerrit-Change-Number: 7259 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Corey Farrell <git@cfware.com> </div>