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