<p>Joshua Colp <strong>merged</strong> this change.</p><p><a href="https://gerrit.asterisk.org/7254">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 c79a4e9..3ed88eb 100644<br>--- a/include/asterisk/cli.h<br>+++ b/include/asterisk/cli.h<br>@@ -306,6 +306,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 5c16e8b..b7626d4 100644<br>--- a/main/cli.c<br>+++ b/main/cli.c<br>@@ -2488,76 +2488,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/7254">change 7254</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/7254"/><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: Iede20c5b4d965fa5ec71fda136ce9425eeb69519 </div>
<div style="display:none"> Gerrit-Change-Number: 7254 </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>