On 11/08/2017 03:23 PM, Martin Kletzander wrote:
On Tue, Nov 07, 2017 at 01:22:55PM +0100, Michal Privoznik wrote:
> This command is going to be called from bash completion script in
> the following form:
>
> virsh complete "start --domain"
>
> Its only purpose is to return list of possible strings for
> completion. Note that this is a 'hidden', unlisted command and
> therefore there's no documentation to it.
>
> Signed-off-by: Michal Privoznik <mprivozn(a)redhat.com>
> ---
> tools/virsh.c | 1 +
> tools/virt-admin.c | 1 +
> tools/vsh.c | 68
> ++++++++++++++++++++++++++++++++++++++++++++++++++++++
> tools/vsh.h | 14 +++++++++++
> 4 files changed, 84 insertions(+)
>
> diff --git a/tools/virsh.c b/tools/virsh.c
> index 7d6dc2620..f830331f6 100644
> --- a/tools/virsh.c
> +++ b/tools/virsh.c
> @@ -846,6 +846,7 @@ static const vshCmdDef virshCmds[] = {
> VSH_CMD_PWD,
> VSH_CMD_QUIT,
> VSH_CMD_SELF_TEST,
> + VSH_CMD_COMPLETE,
> {.name = "connect",
> .handler = cmdConnect,
> .opts = opts_connect,
> diff --git a/tools/virt-admin.c b/tools/virt-admin.c
> index 5d7ef7988..c24ed95c0 100644
> --- a/tools/virt-admin.c
> +++ b/tools/virt-admin.c
> @@ -1356,6 +1356,7 @@ static const vshCmdDef vshAdmCmds[] = {
> VSH_CMD_PWD,
> VSH_CMD_QUIT,
> VSH_CMD_SELF_TEST,
> + VSH_CMD_COMPLETE,
> {.name = "uri",
> .handler = cmdURI,
> .opts = NULL,
> diff --git a/tools/vsh.c b/tools/vsh.c
> index 121669574..136acb0ab 100644
> --- a/tools/vsh.c
> +++ b/tools/vsh.c
> @@ -3419,3 +3419,71 @@ cmdSelfTest(vshControl *ctl ATTRIBUTE_UNUSED,
>
> return true;
> }
> +
> +/* ----------------------
> + * Autocompletion command
> + * ---------------------- */
> +
> +const vshCmdOptDef opts_complete[] = {
> + {.name = "string",
> + .type = VSH_OT_ARGV,
> + .flags = VSH_OFLAG_EMPTY_OK,
> + .help = N_("partial string to autocomplete")
> + },
> + {.name = NULL}
> +};
> +
> +const vshCmdInfo info_complete[] = {
> + {.name = "help",
> + .data = N_("internal command for autocompletion")
> + },
> + {.name = "desc",
> + .data = N_("internal use only")
> + },
> + {.name = NULL}
> +};
> +
> +bool
> +cmdComplete(vshControl *ctl, const vshCmd *cmd)
> +{
> + bool ret = false;
> +#ifdef WITH_READLINE
> + const vshClientHooks *hooks = ctl->hooks;
> + int stdin_fileno = STDIN_FILENO;
> + const char *arg = NULL;
> + char **matches = NULL, *tmp = NULL, **iter;
> +
> + if (vshCommandOptStringQuiet(ctl, cmd, "string", &arg) <= 0)
> + goto cleanup;
> +
> + /* This command is flagged VSH_CMD_FLAG_NOCONNECT because we
> + * need to prevent auth hooks reading any input. Therefore we
> + * have to close stdin and then connect ourselves. */
> + VIR_FORCE_CLOSE(stdin_fileno);
> +
> + if (!(hooks && hooks->connHandler &&
hooks->connHandler(ctl, true)))
> + goto cleanup;
> +
> + autoCompleteOpaque = ctl;
> +
> + rl_basic_word_break_characters = " \t\n\\`@$><=;|&{(";
> + if (VIR_STRDUP(rl_line_buffer, arg) < 0)
> + goto cleanup;
> +
> + while ((tmp = strpbrk(arg, rl_basic_word_break_characters)))
> + arg = tmp + 1;
> +
> + if (!(matches = vshReadlineCompletion(arg, 0, 0)))
> + goto cleanup;
> +
> + for (iter = matches; *iter; iter++)
> + printf("%s\n", *iter);
> +
> + ret = true;
> + cleanup:
> + for (iter = matches; iter && *iter; iter++)
> + VIR_FREE(*iter);
> + VIR_FREE(matches);
> +#endif /* WITH_READLINE */
Do we really need readline for it? Did I miss something or are we
really just using it here just so we don't have to split the
vshReadlineParse() in some way? Don't get me wrong, I'm OK with that,
the split would be too complicated and who doesn't use readline, right?
It just feels weird.
Yes we do need readline unfortunately. vshReadlineCompletion() calls
rl_completion_matches() which filters strings returned by
vshReadlineParse() so that list of strings presented to user corresponds
to their input. We could reimplement the rl_* function ourselves if we
really want to be readline independent. On the other hand - readline is
everywhere, so why should we bother?
Michal