<p>Corey Farrell has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/7559">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">CLI: Fix composite completion generators.<br><br>In cases where a parameter can be "all" or a list of channels we assume<br>that "all" will always match when generating the list of channels.<br><br>This issue is resolved by adding support for ast_cli_complete and<br>ast_complete_channels to use ast_cli_completion_add instead of returning<br>values. Both functions switch modes when they are given -1 instead of<br>a->n. Now adding "all" and adding the list of channels are two<br>independent operations.<br><br>ASTERISK-21038<br><br>Change-Id: Iebef61424689569338d0916ff2e37df944ac76fa<br>---<br>M channels/chan_iax2.c<br>M channels/chan_sip.c<br>M channels/chan_skinny.c<br>M include/asterisk/cli.h<br>M main/bridge.c<br>M main/cli.c<br>6 files changed, 49 insertions(+), 42 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/7559/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c<br>index 69261f3..1e2ffaf 100644<br>--- a/channels/chan_iax2.c<br>+++ b/channels/chan_iax2.c<br>@@ -3636,7 +3636,6 @@<br> struct iax2_peer *peer = NULL;<br> struct iax2_user *user = NULL;<br> static const char * const choices[] = { "all", NULL };<br>- char *cmplt;<br> <br> switch (cmd) {<br> case CLI_INIT:<br>@@ -3647,10 +3646,8 @@<br> return NULL;<br> case CLI_GENERATE:<br> if (a->pos == 3) {<br>- cmplt = ast_cli_complete(a->word, choices, a->n);<br>- if (!cmplt)<br>- cmplt = complete_iax2_peers(a->line, a->word, a->pos, a->n - sizeof(choices), IAX_RTCACHEFRIENDS);<br>- return cmplt;<br>+ ast_cli_complete(a->word, choices, -1);<br>+ return complete_iax2_peers(a->line, a->word, a->pos, a->n, IAX_RTCACHEFRIENDS);<br> }<br> return NULL;<br> }<br>diff --git a/channels/chan_sip.c b/channels/chan_sip.c<br>index 4bae39d..40629bb 100644<br>--- a/channels/chan_sip.c<br>+++ b/channels/chan_sip.c<br>@@ -20535,7 +20535,6 @@<br> int havepattern = 0;<br> struct ao2_iterator i;<br> static const char * const choices[] = { "all", "like", NULL };<br>- char *cmplt;<br> <br> if (cmd == CLI_INIT) {<br> e->command = "sip prune realtime [peer|all]";<br>@@ -20546,10 +20545,9 @@<br> return NULL;<br> } else if (cmd == CLI_GENERATE) {<br> if (a->pos == 4 && !strcasecmp(a->argv[3], "peer")) {<br>- cmplt = ast_cli_complete(a->word, choices, a->n);<br>- if (!cmplt)<br>- cmplt = complete_sip_peer(a->word, a->n - sizeof(choices), SIP_PAGE2_RTCACHEFRIENDS);<br>- return cmplt;<br>+ ast_cli_complete(a->word, choices, -1);<br>+<br>+ return complete_sip_peer(a->word, a->n, SIP_PAGE2_RTCACHEFRIENDS);<br> }<br> if (a->pos == 5 && !strcasecmp(a->argv[4], "like"))<br> return complete_sip_peer(a->word, a->n, SIP_PAGE2_RTCACHEFRIENDS);<br>diff --git a/channels/chan_skinny.c b/channels/chan_skinny.c<br>index 6f4231a..3b27f5e 100644<br>--- a/channels/chan_skinny.c<br>+++ b/channels/chan_skinny.c<br>@@ -3923,11 +3923,8 @@<br> {<br> if (pos == 2) {<br> static const char * const completions[] = { "all", NULL };<br>- char *ret = ast_cli_complete(word, completions, state);<br>- if (!ret) {<br>- ret = complete_skinny_devices(word, state - 1);<br>- }<br>- return ret;<br>+ ast_cli_complete(word, completions, -1);<br>+ return complete_skinny_devices(word, state);<br> } else if (pos == 3) {<br> static const char * const completions[] = { "restart", NULL };<br> return ast_cli_complete(word, completions, state);<br>diff --git a/include/asterisk/cli.h b/include/asterisk/cli.h<br>index c75fc29..3ab98be 100644<br>--- a/include/asterisk/cli.h<br>+++ b/include/asterisk/cli.h<br>@@ -201,6 +201,10 @@<br> * Helper function to generate cli entries from a NULL-terminated array.<br> * Returns the n-th matching entry from the array, or NULL if not found.<br> * Can be used to implement generate() for static entries as below<br>+ *<br>+ * If 'pos' is less than 0 this function will always return NULL after all<br>+ * matching channels are added with ast_cli_completion_add.<br>+ *<br> * (in this example we complete the word in position 2):<br> \code<br> char *my_generate(const char *line, const char *word, int pos, int n)<br>@@ -359,6 +363,9 @@<br> * complete from the list of active channels. 'rpos' is the required<br> * position in the command. This function will return NULL immediately if<br> * 'rpos' is not the same as the current position, 'pos'.<br>+ *<br>+ * If 'state' is less than 0 this function will always return NULL after all<br>+ * matching channels are added with ast_cli_completion_add.<br> */<br> char *ast_complete_channels(const char *line, const char *word, int pos, int state, int rpos);<br> <br>diff --git a/main/bridge.c b/main/bridge.c<br>index 5d9c0c1..fbb6465 100644<br>--- a/main/bridge.c<br>+++ b/main/bridge.c<br>@@ -5243,7 +5243,6 @@<br> static char *handle_bridge_kick_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)<br> {<br> static const char * const completions[] = { "all", NULL };<br>- char *complete;<br> struct ast_bridge *bridge;<br> <br> switch (cmd) {<br>@@ -5260,11 +5259,9 @@<br> return complete_bridge_live(a->word, a->n);<br> }<br> if (a->pos == 3) {<br>- complete = ast_cli_complete(a->word, completions, a->n);<br>- if (!complete) {<br>- complete = complete_bridge_participant(a->argv[2], a->line, a->word, a->pos, a->n - 1);<br>- }<br>- return complete;<br>+ ast_cli_complete(a->word, completions, -1);<br>+<br>+ return complete_bridge_participant(a->argv[2], a->line, a->word, a->pos, a->n);<br> }<br> return NULL;<br> }<br>diff --git a/main/cli.c b/main/cli.c<br>index 0f023b2..5a51164 100644<br>--- a/main/cli.c<br>+++ b/main/cli.c<br>@@ -913,9 +913,12 @@<br> return NULL;<br> <br> case CLI_GENERATE:<br>- if (a->pos != e->args)<br>+ if (a->pos != e->args) {<br> return NULL;<br>- return ast_cli_complete(a->word, completions, a->n);<br>+ }<br>+ ast_cli_complete(a->word, completions, -1);<br>+<br>+ return NULL;<br> }<br> <br> /* regular handler */<br>@@ -1088,7 +1091,6 @@<br> {<br> struct ast_channel *c = NULL;<br> static const char * const completions[] = { "all", NULL };<br>- char *complete;<br> <br> switch (cmd) {<br> case CLI_INIT:<br>@@ -1104,11 +1106,10 @@<br> if (a->pos != e->args) {<br> return NULL;<br> }<br>- complete = ast_cli_complete(a->word, completions, a->n);<br>- if (!complete) {<br>- complete = ast_complete_channels(a->line, a->word, a->pos, a->n - 1, e->args);<br>- }<br>- return complete;<br>+ ast_cli_complete(a->word, completions, -1);<br>+ ast_complete_channels(a->line, a->word, a->pos, -1, e->args);<br>+<br>+ return NULL;<br> }<br> <br> if (a->argc != 4) {<br>@@ -1410,14 +1411,13 @@<br> return NULL;<br> case CLI_GENERATE:<br> if (a->pos == 4) {<br>- char *complete = ast_cli_complete(a->word, completions_all, a->n);<br>- if (!complete) {<br>- complete = ast_complete_channels(a->line, a->word, a->pos, a->n - 1, e->args);<br>- }<br>- return complete;<br>+ ast_cli_complete(a->word, completions_all, -1);<br>+ ast_complete_channels(a->line, a->word, a->pos, -1, e->args);<br> } else if (a->pos == 5) {<br>- return ast_cli_complete(a->word, completions_off, a->n);<br>+ ast_cli_complete(a->word, completions_off, -1);<br> }<br>+<br>+ return NULL;<br> }<br> <br> if (cmd == (CLI_HANDLER + 1000)) {<br>@@ -1509,7 +1509,9 @@<br> " Shows lots of information about the specified channel.\n";<br> return NULL;<br> case CLI_GENERATE:<br>- return ast_complete_channels(a->line, a->word, a->pos, a->n, 3);<br>+ ast_complete_channels(a->line, a->word, a->pos, a->n, -1);<br>+<br>+ return NULL;<br> }<br> <br> if (a->argc != 4) {<br>@@ -1677,8 +1679,13 @@<br> len = ast_strlen_zero(word) ? 0 : strlen(word);<br> <br> for (i = 0; choices[i]; i++) {<br>- if ((!len || !strncasecmp(word, choices[i], len)) && ++which > state)<br>- return ast_strdup(choices[i]);<br>+ if ((!len || !strncasecmp(word, choices[i], len))) {<br>+ if (state < 0) {<br>+ ast_cli_completion_add(ast_strdup(choices[i]));<br>+ } else if (++which > state) {<br>+ return ast_strdup(choices[i]);<br>+ }<br>+ }<br> }<br> return NULL;<br> }<br>@@ -1703,10 +1710,14 @@<br> for (; (msg = ao2_iterator_next(&iter)); ao2_ref(msg, -1)) {<br> struct ast_channel_snapshot *snapshot = stasis_message_data(msg);<br> <br>- if (!strncasecmp(word, snapshot->name, wordlen) && (++which > state)) {<br>- ret = ast_strdup(snapshot->name);<br>- ao2_ref(msg, -1);<br>- break;<br>+ if (!strncasecmp(word, snapshot->name, wordlen)) {<br>+ if (state < 0) {<br>+ ast_cli_completion_add(ast_strdup(snapshot->name));<br>+ } else if (++which > state) {<br>+ ret = ast_strdup(snapshot->name);<br>+ ao2_ref(msg, -1);<br>+ break;<br>+ }<br> }<br> }<br> ao2_iterator_destroy(&iter);<br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/7559">change 7559</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/7559"/><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: newchange </div>
<div style="display:none"> Gerrit-Change-Id: Iebef61424689569338d0916ff2e37df944ac76fa </div>
<div style="display:none"> Gerrit-Change-Number: 7559 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Corey Farrell <git@cfware.com> </div>