summaryrefslogtreecommitdiffstats
path: root/src/vlib/cli.c
diff options
context:
space:
mode:
authorYoann Desmouceaux <ydesmouc@cisco.com>2017-05-18 11:00:48 +0200
committerChris Luke <chris_luke@comcast.com>2017-05-23 17:55:11 +0000
commit3060e07b71fd4dd6203769d73c4f8f21709f9b9c (patch)
tree5f1750c2c0e2c97fd141064655ecebeb609682d1 /src/vlib/cli.c
parent8c4611b39162da9753caaf654741faa115eaf612 (diff)
Add TAB-based auto-completion to the CLI
Hitting tab: - in the middle of a uniquely defined subcommand will expand the subcommand - in the middle of a non-uniquely defined (or empty) subcommand will display all possible subcommands, and possibly expand to the lowest common prefix Change-Id: Ib858eefdb0353cd2c3aad472799d15cd537455a0 Signed-off-by: Yoann Desmouceaux <ydesmouc@cisco.com>
Diffstat (limited to 'src/vlib/cli.c')
-rw-r--r--src/vlib/cli.c102
1 files changed, 102 insertions, 0 deletions
diff --git a/src/vlib/cli.c b/src/vlib/cli.c
index 5a0f7ec3c10..9e14bee219e 100644
--- a/src/vlib/cli.c
+++ b/src/vlib/cli.c
@@ -40,6 +40,7 @@
#include <vlib/vlib.h>
#include <vppinfra/cpu.h>
#include <unistd.h>
+#include <ctype.h>
/* Root of all show commands. */
/* *INDENT-OFF* */
@@ -235,6 +236,107 @@ unformat_vlib_cli_sub_command (unformat_input_t * i, va_list * args)
return is_unique;
}
+static int
+vlib_cli_cmp_strings (void *a1, void *a2)
+{
+ u8 *c1 = *(u8 **) a1;
+ u8 *c2 = *(u8 **) a2;
+
+ return vec_cmp (c1, c2);
+}
+
+u8 **
+vlib_cli_get_possible_completions (u8 * str)
+{
+ vlib_cli_command_t *c;
+ vlib_cli_sub_command_t *sc;
+ vlib_main_t *vm = vlib_get_main ();
+ vlib_cli_main_t *vcm = &vm->cli_main;
+ uword *match_bitmap = 0;
+ uword index, is_unique, help_next_level;
+ u8 **result = 0;
+ unformat_input_t input;
+ unformat_init_vector (&input, vec_dup (str));
+ c = vec_elt_at_index (vcm->commands, 0);
+
+ /* remove trailing whitespace, except for one of them */
+ while (vec_len (input.buffer) >= 2 &&
+ isspace (input.buffer[vec_len (input.buffer) - 1]) &&
+ isspace (input.buffer[vec_len (input.buffer) - 2]))
+ {
+ vec_del1 (input.buffer, vec_len (input.buffer) - 1);
+ }
+
+ /* if input is empty, directly return list of root commands */
+ if (vec_len (input.buffer) == 0 ||
+ (vec_len (input.buffer) == 1 && isspace (input.buffer[0])))
+ {
+ vec_foreach (sc, c->sub_commands)
+ {
+ vec_add1 (result, (u8 *) sc->name);
+ }
+ goto done;
+ }
+
+ /* add a trailing '?' so that vlib_cli_sub_command_match can find
+ * all commands starting with the input string */
+ vec_add1 (input.buffer, '?');
+
+ while (1)
+ {
+ match_bitmap = vlib_cli_sub_command_match (c, &input);
+ /* no match: return no result */
+ if (match_bitmap == 0)
+ {
+ goto done;
+ }
+ is_unique = clib_bitmap_count_set_bits (match_bitmap) == 1;
+ /* unique match: try to step one subcommand level further */
+ if (is_unique)
+ {
+ /* stop if no more input */
+ if (input.index >= vec_len (input.buffer) - 1)
+ {
+ break;
+ }
+
+ index = clib_bitmap_first_set (match_bitmap);
+ c = get_sub_command (vcm, c, index);
+ clib_bitmap_free (match_bitmap);
+ continue;
+ }
+ /* multiple matches: stop here, return all matches */
+ break;
+ }
+
+ /* remove trailing '?' */
+ vec_del1 (input.buffer, vec_len (input.buffer) - 1);
+
+ /* if we have a space at the end of input, and a unique match,
+ * autocomplete the next level of subcommands */
+ help_next_level = (vec_len (str) == 0) || isspace (str[vec_len (str) - 1]);
+ /* *INDENT-OFF* */
+ clib_bitmap_foreach(index, match_bitmap, {
+ if (help_next_level && is_unique) {
+ c = get_sub_command (vcm, c, index);
+ vec_foreach (sc, c->sub_commands) {
+ vec_add1 (result, (u8*) sc->name);
+ }
+ goto done; /* break doesn't work in this macro-loop */
+ }
+ sc = &c->sub_commands[index];
+ vec_add1(result, (u8*) sc->name);
+ });
+ /* *INDENT-ON* */
+
+done:
+ clib_bitmap_free (match_bitmap);
+ unformat_free (&input);
+
+ vec_sort_with_function (result, vlib_cli_cmp_strings);
+ return result;
+}
+
static u8 *
format_vlib_cli_command_help (u8 * s, va_list * args)
{