<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>