This patch introduces a way for completers to retrieve the
current vshControl object by using the vshGetCompleterCtl()
macro. When readline is enabled, the main method stores the
vshControl pointer in a variable that is file-global to virsh.c.
Then, that pointer can be retrieved by the _vshGetCompleterCtl()
function, which is mapped to by the macro. If readline is not
enabled, the macro simply maps to NULL.
---
tests/virshtest.c | 13 +++++++++++++
tools/virsh.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++-----
tools/virsh.h | 8 ++++++++
3 files changed, 68 insertions(+), 5 deletions(-)
diff --git a/tests/virshtest.c b/tests/virshtest.c
index 0c698fa..d068e5e 100644
--- a/tests/virshtest.c
+++ b/tests/virshtest.c
@@ -333,6 +333,15 @@ static int testCompletionInArgvMode(const void *data
ATTRIBUTE_UNUSED)
return testCompareOutputLit(exp, NULL, argv);
}
+static int testCompletionRequiringCtl(const void *data ATTRIBUTE_UNUSED)
+{
+ const char *const argv[] = { VIRSH_CUSTOM, "complete",
+ "fake-command --string3 ", NULL };
+ const char *exp = ("test:///home/directxman12/dev/libvirt"
+ "/tests/../examples/xml/test/testnode.xml\n\n");
+ return testCompareOutputLit(exp, NULL, argv);
+}
+
# endif /* WITH_READLINE */
@@ -575,6 +584,10 @@ mymain(void)
testCompletionInArgvMode, NULL) != 0)
ret = -1;
+ if (virtTestRun("virsh completion (completer requiring control object)",
+ testCompletionRequiringCtl, NULL) != 0)
+ ret = -1;
+
# endif /* WITH_READLINE */
/* It's a bit awkward listing result before argument, but that's a
diff --git a/tools/virsh.c b/tools/virsh.c
index 808a125..71076dc 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -96,6 +96,16 @@ static char *progname;
static const vshCmdGrp cmdGroups[];
+#if WITH_READLINE
+static vshControl *completer_ctl;
+
+vshControl *
+_vshGetCompleterCtl(void)
+{
+ return completer_ctl;
+}
+#endif
+
/* Bypass header poison */
#undef strdup
@@ -1033,6 +1043,21 @@ VSH_STRING_COMPLETER(NULL, FakeCommandStr2, "value a",
"value b");
VSH_STRING_COMPLETER(NULL, FakeCommandData1, "ab", "cd");
VSH_STRING_COMPLETER(NULL, FakeCommandData2, "i e f", "i g h");
+static char **
+vshCompleteFakeCommandStr3(unsigned int flags)
+{
+ virCheckFlags(0, NULL);
+
+ vshControl *ctl = vshGetCompleterCtl();
+ char *uri = virConnectGetURI(ctl->conn);
+ char **res = vshCalloc(NULL, 2, sizeof(char*));
+
+ res[0] = uri;
+ res[1] = NULL;
+
+ return res;
+}
+
static const vshCmdOptDef opts_fake_command[] = {
{.name = "abool",
.type = VSH_OT_BOOL,
@@ -1060,6 +1085,7 @@ static const vshCmdOptDef opts_fake_command[] = {
},
{.name = "string3",
.type = VSH_OT_STRING,
+ .completer = vshCompleteFakeCommandStr3,
.help = N_("another string")
},
{.name = "file",
@@ -3112,7 +3138,7 @@ vshReadlineCommandGenerator(const char *text, int state)
}
static char *
-vshDelegateToCustomCompleter(const vshCmdOptDef *opt,
+vshDelegateToCustomCompleter(const vshCmdOptDef *opt, bool reconnect,
const char *text, int state)
{
static int list_index;
@@ -3125,6 +3151,10 @@ vshDelegateToCustomCompleter(const vshCmdOptDef *opt,
return NULL;
if (opt->completer) {
+ vshControl *ctl = completer_ctl;
+ if ((!ctl->conn || disconnected) && reconnect)
+ vshReconnect(ctl);
+
list_index = 0;
completions = opt->completer(opt->completer_flags);
}
@@ -3276,10 +3306,13 @@ vshReadlineOptionsGenerator(const char *text, int state)
if (waiting_for_flag_arg) {
char* res;
+ bool may_connect = !(cmd->flags & VSH_CMD_FLAG_NOCONNECT);
if (continue_from_error)
- res = vshDelegateToCustomCompleter(curr_opt, last_tok, substate);
+ res = vshDelegateToCustomCompleter(curr_opt, may_connect,
+ last_tok, substate);
else
- res = vshDelegateToCustomCompleter(curr_opt, text, substate);
+ res = vshDelegateToCustomCompleter(curr_opt, may_connect,
+ text, substate);
substate++;
/* if we're in a flag's argument, we don't
@@ -3343,10 +3376,14 @@ vshReadlineOptionsGenerator(const char *text, int state)
/* we don't need to ignore args without custom completers,
* since vshDelegateToCustomCompleter will do this for us */
+ bool may_connect = !(cmd->flags & VSH_CMD_FLAG_NOCONNECT);
if (continue_from_error)
- res = vshDelegateToCustomCompleter(opt, last_tok, substate);
+ res = vshDelegateToCustomCompleter(opt, may_connect,
+ last_tok, substate);
else
- res = vshDelegateToCustomCompleter(opt, text, substate);
+ res = vshDelegateToCustomCompleter(opt, may_connect,
+ text, substate);
+
substate++;
if (res) {
if (strchr(res, ' ')) {
@@ -4134,9 +4171,14 @@ int
main(int argc, char **argv)
{
vshControl _ctl, *ctl = &_ctl;
+
const char *defaultConn;
bool ret = true;
+#if WITH_READLINE
+ completer_ctl = ctl;
+#endif
+
memset(ctl, 0, sizeof(vshControl));
ctl->imode = true; /* default is interactive mode */
ctl->log_fd = -1; /* Initialize log file descriptor */
diff --git a/tools/virsh.h b/tools/virsh.h
index 227e4e8..38f3663 100644
--- a/tools/virsh.h
+++ b/tools/virsh.h
@@ -180,6 +180,14 @@ struct _vshCmdOptDef {
* readline file completer */
# define VSH_COMPLETE_AS_FILE (1 << 8)
+vshControl * _vshGetCompleterCtl(void);
+
+# if WITH_READLINE
+# define vshGetCompleterCtl() _vshGetCompleterCtl()
+# else
+# define vshGetCompleterCtl() NULL
+# endif
+
/*
* vshCmdOpt - command options
*
--
1.8.3.2