From 314f382a5093a5531a6c86cf1abcfdfd8c294fa4 Mon Sep 17 00:00:00 2001 From: Vincent Bernat Date: Sat, 12 Jul 2014 13:09:37 +0200 Subject: [PATCH] lldpcli: provide a hidden complete command for shell completion --- debian/etc/bash_completion.d/lldpcli | 6 +-- src/client/client.h | 1 + src/client/commands.c | 70 ++++++++++++++++++++-------- src/client/lldpcli.c | 2 + 4 files changed, 56 insertions(+), 23 deletions(-) diff --git a/debian/etc/bash_completion.d/lldpcli b/debian/etc/bash_completion.d/lldpcli index 0779c5cc..f52539e6 100755 --- a/debian/etc/bash_completion.d/lldpcli +++ b/debian/etc/bash_completion.d/lldpcli @@ -10,10 +10,8 @@ _lldpcli() unset cmd[COMP_CWORD] fi - local choices=$(sudo ${cmd[0]} help ${cmd[@]:1} 2>&1 | \ - tail -n +3 | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g" | \ - sed -e 's/^[ \t]*//g' -e 's///g' -e 's/WORD//g' -e '/^$/d' | \ - tr -s " " | cut -d " " -f 1) + local choices=$(sudo ${cmd[0]} complete ${cmd[@]:1} | \ + cut -d " " -f 1) COMPREPLY=($(compgen -W '${choices}' -- ${cur} )) return 0 } diff --git a/src/client/client.h b/src/client/client.h index 8c306edc..db60fd0b 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -69,6 +69,7 @@ struct cmd_node *commands_new( struct cmd_env*, void *), void *); struct cmd_node* commands_privileged(struct cmd_node *); +struct cmd_node* commands_hidden(struct cmd_node *); void commands_free(struct cmd_node *); const char *cmdenv_arg(struct cmd_env*); const char *cmdenv_get(struct cmd_env*, const char*); diff --git a/src/client/commands.c b/src/client/commands.c index d1bf242b..8092f8c2 100644 --- a/src/client/commands.c +++ b/src/client/commands.c @@ -68,6 +68,7 @@ struct cmd_node { const char *token; /**< Token to enter this cnode */ const char *doc; /**< Documentation string */ int privileged; /**< Privileged command? */ + int hidden; /**< Hidden command? */ /** * Function validating entry in this node. Can be @c NULL. @@ -100,7 +101,7 @@ commands_root(void) /** * Make a node accessible only to privileged users. * - * @param node Node to change privileges + * @param node node to change privileges * @return the modified node * * The node is modified. It is returned to ease chaining. @@ -112,6 +113,21 @@ commands_privileged(struct cmd_node *node) return node; } +/** + * Hide a node from help or completion. + * + * @param node node to hide + * @return the modified node + * + * The node is modified. It is returned to ease chaining. + */ +struct cmd_node* +commands_hidden(struct cmd_node *node) +{ + if (node) node->hidden = 1; + return node; +} + /** * Create a new node acessible by any user. * @@ -304,6 +320,7 @@ struct candidate_word { TAILQ_ENTRY(candidate_word) next; const char *word; const char *doc; + int hidden; }; /** @@ -326,6 +343,7 @@ _commands_execute(struct lldpctl_conn_t *conn, struct writer *w, { int n, rc = 0, completion = (word != NULL); int help = 0; /* Are we asking for help? */ + int complete = 0; /* Are we asking for possible completions? */ struct cmd_env env = { .elements = TAILQ_HEAD_INITIALIZER(env.elements), .stack = TAILQ_HEAD_INITIALIZER(env.stack), @@ -348,12 +366,14 @@ _commands_execute(struct lldpctl_conn_t *conn, struct writer *w, * execution until we reach the cursor position. */ struct cmd_node *current = NULL; while ((current = cmdenv_top(&env))) { - if (!completion) + if (!completion) { help = !!cmdenv_get(&env, "help"); /* Are we asking for help? */ + complete = !!cmdenv_get(&env, "complete"); /* Or completion? */ + } struct cmd_node *candidate, *best = NULL; const char *token = (env.argp < env.argc) ? env.argv[env.argp] : - (env.argp == env.argc && !help) ? NEWLINE : NULL; + (env.argp == env.argc && !help && !complete) ? NEWLINE : NULL; if (token == NULL || (completion && env.argp == env.argc - 1)) goto end; @@ -408,21 +428,21 @@ _commands_execute(struct lldpctl_conn_t *conn, struct writer *w, env.argp++; } end: - if (!completion && !help) { + if (!completion && !help && !complete) { if (rc == 0 && env.argp != env.argc + 1) { log_warnx("lldpctl", "incomplete command"); rc = -1; } - } else if (rc == 0 && (env.argp == env.argc - 1 || help)) { + } else if (rc == 0 && (env.argp == env.argc - 1 || help || complete)) { /* We need to complete. Let's build the list of candidate words. */ struct cmd_node *candidate = NULL; - int maxl = 10; /* Max length of a word */ + size_t maxl = 10; /* Max length of a word */ TAILQ_HEAD(, candidate_word) words; /* List of subnodes */ TAILQ_INIT(&words); current = cmdenv_top(&env); if (!TAILQ_EMPTY(¤t->subentries)) { TAILQ_FOREACH(candidate, ¤t->subentries, next) { - if ((!candidate->token || help || + if ((!candidate->token || help || complete || !strncmp(env.argv[env.argc - 1], candidate->token, strlen(env.argv[env.argc -1 ]))) && CAN_EXECUTE(candidate)) { @@ -431,6 +451,7 @@ end: if (!cword) break; cword->word = candidate->token; cword->doc = candidate->doc; + cword->hidden = candidate->hidden; if (cword->word && strlen(cword->word) > maxl) maxl = strlen(cword->word); TAILQ_INSERT_TAIL(&words, cword, next); @@ -439,14 +460,14 @@ end: } if (!TAILQ_EMPTY(&words)) { /* Search if there is a common prefix, then return it. */ - int c = 0; char prefix[maxl + 2]; /* Extra space may be added at the end */ struct candidate_word *cword, *cword_next; memset(prefix, 0, maxl+2); - for (int n = 0; n < maxl; n++) { - c = 1; /* Set to 0 to exit outer loop */ + for (size_t n = 0; n < maxl; n++) { + int c = 1; /* Set to 0 to exit outer loop */ TAILQ_FOREACH(cword, &words, next) { c = 0; + if (cword->hidden) continue; if (cword->word == NULL) break; if (!strcmp(cword->word, NEWLINE)) break; if (strlen(cword->word) == n) break; @@ -461,7 +482,7 @@ end: } /* If the prefix is complete, add a space, otherwise, * just return it as is. */ - if (!all && !help && strcmp(prefix, NEWLINE) && + if (!all && !help && !complete && strcmp(prefix, NEWLINE) && strlen(prefix) > 0 && strlen(env.argv[env.argc-1]) < strlen(prefix)) { TAILQ_FOREACH(cword, &words, next) { @@ -473,16 +494,27 @@ end: *word = strdup(prefix); } else { /* No common prefix, print possible completions */ - fprintf(stderr, "\n-- \033[1;34m%s\033[0m\n", - current->doc ? current->doc : "Help"); + if (!complete) + fprintf(stderr, "\n-- \033[1;34m%s\033[0m\n", + current->doc ? current->doc : "Help"); TAILQ_FOREACH(cword, &words, next) { + if (cword->hidden) continue; + char fmt[100]; - snprintf(fmt, sizeof(fmt), - "%s%%%ds%s %%s\n", - "\033[1;30m", maxl, "\033[0m"); - fprintf(stderr, fmt, - cword->word ? cword->word : "WORD", - cword->doc ? cword->doc : "..."); + if (!complete) { + snprintf(fmt, sizeof(fmt), + "%s%%%ds%s %%s\n", + "\033[1;30m", (int)maxl, "\033[0m"); + fprintf(stderr, fmt, + cword->word ? cword->word : "WORD", + cword->doc ? cword->doc : "..."); + } else { + if (!cword->word || !strcmp(cword->word, NEWLINE)) + continue; + fprintf(stdout, "%s %s\n", + cword->word ? cword->word : "WORD", + cword->doc ? cword->doc : "..."); + } } } for (cword = TAILQ_FIRST(&words); cword != NULL; diff --git a/src/client/lldpcli.c b/src/client/lldpcli.c index f96b9c57..964c6bbe 100644 --- a/src/client/lldpcli.c +++ b/src/client/lldpcli.c @@ -331,6 +331,8 @@ register_commands() NEWLINE, "Update information and send LLDPU on all ports", NULL, cmd_update, NULL)); register_commands_configure(root); + commands_hidden(commands_new(root, "complete", "Get possible completions from a given command", + NULL, cmd_store_env_and_pop, "complete")); commands_new(root, "help", "Get help on a possible command", NULL, cmd_store_env_and_pop, "help"); commands_new( -- 2.39.5