vsh: Introduce complete command

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@redhat.com>
This commit is contained in:
Michal Privoznik 2017-11-01 15:34:14 +01:00
parent cc005fe5a8
commit a0e1ada63c
4 changed files with 96 additions and 0 deletions

View File

@ -833,6 +833,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,

View File

@ -1351,6 +1351,7 @@ static const vshCmdDef vshAdmCmds[] = {
VSH_CMD_PWD,
VSH_CMD_QUIT,
VSH_CMD_SELF_TEST,
VSH_CMD_COMPLETE,
{.name = "uri",
.handler = cmdURI,
.opts = NULL,

View File

@ -3475,3 +3475,83 @@ 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 = "";
const vshCmdOpt *opt = NULL;
char **matches = NULL, **iter;
virBuffer buf = VIR_BUFFER_INITIALIZER;
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)))
goto cleanup;
while ((opt = vshCommandOptArgv(ctl, cmd, opt))) {
if (virBufferUse(&buf) != 0)
virBufferAddChar(&buf, ' ');
virBufferAddStr(&buf, opt->data);
arg = opt->data;
}
if (virBufferCheckError(&buf) < 0)
goto cleanup;
vshReadlineInit(ctl);
if (!(rl_line_buffer = virBufferContentAndReset(&buf)) &&
VIR_STRDUP(rl_line_buffer, "") < 0)
goto cleanup;
/* rl_point is current cursor position in rl_line_buffer.
* In our case it's at the end of the whole line. */
rl_point = strlen(rl_line_buffer);
if (!(matches = vshReadlineCompletion(arg, 0, 0)))
goto cleanup;
for (iter = matches; *iter; iter++)
printf("%s\n", *iter);
ret = true;
cleanup:
virBufferFreeAndReset(&buf);
virStringListFree(matches);
#endif /* WITH_READLINE */
return ret;
}

View File

@ -382,6 +382,8 @@ extern const vshCmdInfo info_echo[];
extern const vshCmdInfo info_pwd[];
extern const vshCmdInfo info_quit[];
extern const vshCmdInfo info_selftest[];
extern const vshCmdOptDef opts_complete[];
extern const vshCmdInfo info_complete[];
bool cmdHelp(vshControl *ctl, const vshCmd *cmd);
bool cmdCd(vshControl *ctl, const vshCmd *cmd);
@ -389,6 +391,7 @@ bool cmdEcho(vshControl *ctl, const vshCmd *cmd);
bool cmdPwd(vshControl *ctl, const vshCmd *cmd);
bool cmdQuit(vshControl *ctl, const vshCmd *cmd);
bool cmdSelfTest(vshControl *ctl, const vshCmd *cmd);
bool cmdComplete(vshControl *ctl, const vshCmd *cmd);
# define VSH_CMD_CD \
{ \
@ -454,6 +457,17 @@ bool cmdSelfTest(vshControl *ctl, const vshCmd *cmd);
.alias = "self-test" \
}
# define VSH_CMD_COMPLETE \
{ \
.name = "complete", \
.handler = cmdComplete, \
.opts = opts_complete, \
.info = info_complete, \
.flags = VSH_CMD_FLAG_NOCONNECT | VSH_CMD_FLAG_ALIAS, \
.alias = "complete" \
}
/* readline */
char * vshReadline(vshControl *ctl, const char *prompt);