[libvirt] [PATCH 0/8] virsh: More intelligent auto-completion

Hi, this patch series is a prototype for my GSoC project (Michal Privoznik is my mentor). I'm working on virsh auto-completion, trying to make it more "intelligent". At this stage, prototype is capable of command and option completion. Three completer functions are currently implemented so you can test it. If it turns out that this prototype is good enough, I will implement more completer functions. Tomas Meszaros (8): virsh: Add vshCmdCompleter and vshOptCompleter virsh: Add vshDomainCompleter virsh: Add vshSuspendTargetCompleter virsh: Add vshRebootShutdownModeCompleter virsh: Improve readline generators and readline completion virsh: Add completer functions to domManaggementCmds virsh: Add completer functions to snapshotCmds virsh: Add completer functions to domMonitoringCmds tools/virsh-domain-monitor.c | 32 ++- tools/virsh-domain.c | 240 +++++++++++++++----- tools/virsh-snapshot.c | 45 +++- tools/virsh.c | 507 +++++++++++++++++++++++++++++++++++++++++-- tools/virsh.h | 11 + 5 files changed, 742 insertions(+), 93 deletions(-) -- 1.8.3.1

completer and completer_flags added to the _vshCmdDef and _vshCmdOptDef structures so it will be possible for completion generators to conveniently call completer functions with desired flags. --- tools/virsh-domain.c | 10 +++++----- tools/virsh.h | 7 +++++++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 8cafce4..5e1196f 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -7889,10 +7889,10 @@ static const vshCmdInfo info_lxc_enter_namespace[] = { }; static const vshCmdOptDef opts_lxc_enter_namespace[] = { - {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")}, - {"noseclabel", VSH_OT_BOOL, 0, N_("Do not change process security label")}, - {"cmd", VSH_OT_ARGV, VSH_OFLAG_REQ, N_("namespace")}, - {NULL, 0, 0, NULL} + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid"), NULL, 0}, + {"noseclabel", VSH_OT_BOOL, 0, N_("Do not change process security label"), NULL, 0}, + {"cmd", VSH_OT_ARGV, VSH_OFLAG_REQ, N_("namespace"), NULL, 0}, + {NULL, 0, 0, NULL, NULL, 0} }; static bool @@ -10276,7 +10276,7 @@ static const vshCmdOptDef opts_domfstrim[] = { .type = VSH_OT_DATA, .help = N_("which mount point to trim") }, - {NULL, 0, 0, NULL} + {NULL, 0, 0, NULL, NULL, 0} }; static bool cmdDomFSTrim(vshControl *ctl, const vshCmd *cmd) diff --git a/tools/virsh.h b/tools/virsh.h index a407428..e07b546 100644 --- a/tools/virsh.h +++ b/tools/virsh.h @@ -146,6 +146,9 @@ typedef struct _vshCmdOptDef vshCmdOptDef; typedef struct _vshControl vshControl; typedef struct _vshCtrlData vshCtrlData; +typedef char **(*vshCmdCompleter)(unsigned int flags); +typedef char **(*vshOptCompleter)(unsigned int flags); + /* * vshCmdInfo -- name/value pair for information about command * @@ -167,6 +170,8 @@ struct _vshCmdOptDef { unsigned int flags; /* flags */ const char *help; /* non-NULL help string; or for VSH_OT_ALIAS * the name of a later public option */ + vshOptCompleter completer; /* option completer */ + unsigned int completer_flags; /* option completer flags */ }; /* @@ -198,6 +203,8 @@ struct _vshCmdDef { const vshCmdOptDef *opts; /* definition of command options */ const vshCmdInfo *info; /* details about command */ unsigned int flags; /* bitwise OR of VSH_CMD_FLAG */ + vshCmdCompleter completer; /* command completer */ + unsigned int completer_flags; /* command completer flags */ }; /* -- 1.8.3.1

On 08.08.2013 16:38, Tomas Meszaros wrote:
completer and completer_flags added to the _vshCmdDef and _vshCmdOptDef structures so it will be possible for completion generators to conveniently call completer functions with desired flags. --- tools/virsh-domain.c | 10 +++++----- tools/virsh.h | 7 +++++++ 2 files changed, 12 insertions(+), 5 deletions(-)
diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 8cafce4..5e1196f 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -7889,10 +7889,10 @@ static const vshCmdInfo info_lxc_enter_namespace[] = { };
static const vshCmdOptDef opts_lxc_enter_namespace[] = { - {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")}, - {"noseclabel", VSH_OT_BOOL, 0, N_("Do not change process security label")}, - {"cmd", VSH_OT_ARGV, VSH_OFLAG_REQ, N_("namespace")}, - {NULL, 0, 0, NULL} + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid"), NULL, 0}, + {"noseclabel", VSH_OT_BOOL, 0, N_("Do not change process security label"), NULL, 0}, + {"cmd", VSH_OT_ARGV, VSH_OFLAG_REQ, N_("namespace"), NULL, 0}, + {NULL, 0, 0, NULL, NULL, 0} };
static bool @@ -10276,7 +10276,7 @@ static const vshCmdOptDef opts_domfstrim[] = { .type = VSH_OT_DATA, .help = N_("which mount point to trim") }, - {NULL, 0, 0, NULL} + {NULL, 0, 0, NULL, NULL, 0} };
These two ^^ has slipped my switch to C99 style initialization done a while ago. In fact, if they were properly initialized in C99 these two chunks wouldn't be here. I think you want to prepend a separate patch, which will convert info_lxc_enter_namespace and opts_domfstrim into C99 style.
static bool cmdDomFSTrim(vshControl *ctl, const vshCmd *cmd) diff --git a/tools/virsh.h b/tools/virsh.h index a407428..e07b546 100644 --- a/tools/virsh.h +++ b/tools/virsh.h @@ -146,6 +146,9 @@ typedef struct _vshCmdOptDef vshCmdOptDef; typedef struct _vshControl vshControl; typedef struct _vshCtrlData vshCtrlData;
+typedef char **(*vshCmdCompleter)(unsigned int flags); +typedef char **(*vshOptCompleter)(unsigned int flags); + /* * vshCmdInfo -- name/value pair for information about command * @@ -167,6 +170,8 @@ struct _vshCmdOptDef { unsigned int flags; /* flags */ const char *help; /* non-NULL help string; or for VSH_OT_ALIAS * the name of a later public option */ + vshOptCompleter completer; /* option completer */ + unsigned int completer_flags; /* option completer flags */ };
/* @@ -198,6 +203,8 @@ struct _vshCmdDef { const vshCmdOptDef *opts; /* definition of command options */ const vshCmdInfo *info; /* details about command */ unsigned int flags; /* bitwise OR of VSH_CMD_FLAG */ + vshCmdCompleter completer; /* command completer */ + unsigned int completer_flags; /* command completer flags */ };
/*
The rest looks okay. Michal

Function vshDomainCompler returns domains names. Michal Privoznik recommended to add global variable __my_conn so we can get the list of domains from the virConnecTListAllDomains(). vshReconnect() is called before the first command is executed in order to provide autocompletion for the very first command. --- tools/virsh.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh.h | 2 ++ 2 files changed, 55 insertions(+) diff --git a/tools/virsh.c b/tools/virsh.c index f65dc79..af31b9a 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -88,6 +88,8 @@ static char *progname; static const vshCmdGrp cmdGroups[]; +virConnectPtr *__my_conn; + /* Bypass header poison */ #undef strdup @@ -2502,6 +2504,51 @@ vshCloseLogFile(vshControl *ctl) #ifdef USE_READLINE +/* ------------- + * Completers + * ------------- + */ + +char ** +vshDomainCompleter(unsigned int flags) +{ + virDomainPtr *domains; + size_t i; + char **names = NULL; + int ndomains; + + if (!*__my_conn) + return NULL; + + ndomains = virConnectListAllDomains(*__my_conn, &domains, flags); + + if (ndomains < 0) + return NULL; + + names = vshMalloc(NULL, sizeof(char *) * (ndomains + 1)); + + if (!names) + return NULL; + + for (i = 0; i < ndomains; i++) { + char *name = (char *)virDomainGetName(domains[i]); + if (VIR_STRDUP(names[i], name) < 0) { + virDomainFree(domains[i]); + goto cleanup; + } + virDomainFree(domains[i]); + } + names[i] = NULL; + VIR_FREE(domains); + return names; + +cleanup: + for (i = 0; names[i]; i++) + VIR_FREE(names[i]); + VIR_FREE(names); + return NULL; +} + /* ----------------- * Readline stuff * ----------------- @@ -3158,6 +3205,7 @@ main(int argc, char **argv) ctl->debug = VSH_DEBUG_DEFAULT; ctl->escapeChar = "^]"; /* Same default as telnet */ + __my_conn = &ctl->conn; if (!setlocale(LC_ALL, "")) { perror("setlocale"); @@ -3219,6 +3267,11 @@ main(int argc, char **argv) exit(EXIT_FAILURE); } + /* Need to connect immediately after start in order to provide + * autocompletion for the very first command. + */ + vshReconnect(ctl); + do { const char *prompt = ctl->readonly ? VSH_PROMPT_RO : VSH_PROMPT_RW; ctl->cmdstr = diff --git a/tools/virsh.h b/tools/virsh.h index e07b546..c4a9c13 100644 --- a/tools/virsh.h +++ b/tools/virsh.h @@ -254,6 +254,8 @@ struct _vshCmdGrp { const vshCmdDef *commands; }; +char **vshDomainCompleter(unsigned int flags); + void vshError(vshControl *ctl, const char *format, ...) ATTRIBUTE_FMT_PRINTF(2, 3); void vshOpenLogFile(vshControl *ctl); -- 1.8.3.1

On 08.08.2013 16:38, Tomas Meszaros wrote:
Function vshDomainCompler returns domains names.
Michal Privoznik recommended to add global variable __my_conn so we can get the list of domains from the virConnecTListAllDomains().
vshReconnect() is called before the first command is executed in order to provide autocompletion for the very first command. --- tools/virsh.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh.h | 2 ++ 2 files changed, 55 insertions(+)
diff --git a/tools/virsh.c b/tools/virsh.c index f65dc79..af31b9a 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -88,6 +88,8 @@ static char *progname;
static const vshCmdGrp cmdGroups[];
+virConnectPtr *__my_conn; +
Sigh. Unfortunately, we can't tunnel a pointer into readline's callbacks. While I hate global variables, this one makes sense.
/* Bypass header poison */ #undef strdup
@@ -2502,6 +2504,51 @@ vshCloseLogFile(vshControl *ctl)
#ifdef USE_READLINE
+/* ------------- + * Completers + * ------------- + */ + +char ** +vshDomainCompleter(unsigned int flags) +{ + virDomainPtr *domains; + size_t i; + char **names = NULL; + int ndomains; + + if (!*__my_conn) + return NULL; + + ndomains = virConnectListAllDomains(*__my_conn, &domains, flags); + + if (ndomains < 0) + return NULL; + + names = vshMalloc(NULL, sizeof(char *) * (ndomains + 1)); + + if (!names) + return NULL;
if vshMalloc fails, you leak domains[].
+ + for (i = 0; i < ndomains; i++) { + char *name = (char *)virDomainGetName(domains[i]);
what about making @name const char *?
+ if (VIR_STRDUP(names[i], name) < 0) { + virDomainFree(domains[i]); + goto cleanup; + } + virDomainFree(domains[i]); + } + names[i] = NULL; + VIR_FREE(domains); + return names; + +cleanup:
This should be rather error: as the path is not common for success and fail cases. The path is taken only on error.
+ for (i = 0; names[i]; i++) + VIR_FREE(names[i]); + VIR_FREE(names); + return NULL; +} + /* ----------------- * Readline stuff * ----------------- @@ -3158,6 +3205,7 @@ main(int argc, char **argv) ctl->debug = VSH_DEBUG_DEFAULT; ctl->escapeChar = "^]"; /* Same default as telnet */
+ __my_conn = &ctl->conn;
if (!setlocale(LC_ALL, "")) { perror("setlocale"); @@ -3219,6 +3267,11 @@ main(int argc, char **argv) exit(EXIT_FAILURE); }
+ /* Need to connect immediately after start in order to provide + * autocompletion for the very first command. + */ + vshReconnect(ctl); + do { const char *prompt = ctl->readonly ? VSH_PROMPT_RO : VSH_PROMPT_RW; ctl->cmdstr = diff --git a/tools/virsh.h b/tools/virsh.h index e07b546..c4a9c13 100644 --- a/tools/virsh.h +++ b/tools/virsh.h @@ -254,6 +254,8 @@ struct _vshCmdGrp { const vshCmdDef *commands; };
+char **vshDomainCompleter(unsigned int flags); + void vshError(vshControl *ctl, const char *format, ...) ATTRIBUTE_FMT_PRINTF(2, 3); void vshOpenLogFile(vshControl *ctl);
Michal

On 08/19/2013 08:45 AM, Michal Privoznik wrote:
On 08.08.2013 16:38, Tomas Meszaros wrote:
Function vshDomainCompler returns domains names.
Michal Privoznik recommended to add global variable __my_conn so we can get the list of domains from the virConnecTListAllDomains().
vshReconnect() is called before the first command is executed in order to provide autocompletion for the very first command. --- tools/virsh.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh.h | 2 ++ 2 files changed, 55 insertions(+)
diff --git a/tools/virsh.c b/tools/virsh.c index f65dc79..af31b9a 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -88,6 +88,8 @@ static char *progname;
static const vshCmdGrp cmdGroups[];
+virConnectPtr *__my_conn; +
Sigh. Unfortunately, we can't tunnel a pointer into readline's callbacks. While I hate global variables, this one makes sense.
Naming a variable with leading __ is not advised - too much potential for collision with system headers. Name it vshConn or some-such name in the vsh namespace.
+ + names = vshMalloc(NULL, sizeof(char *) * (ndomains + 1)); + + if (!names) + return NULL;
if vshMalloc fails, you leak domains[].
vshMalloc cannot fail. If it returns at all, it returns a non-NULL pointer (since it calls exit() on OOM). -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

vshSuspendTargetCompleter returns targets available for suspend. This completer can be used for the command option completion (for dompmsuspend, etc.). --- tools/virsh.c | 28 ++++++++++++++++++++++++++++ tools/virsh.h | 1 + 2 files changed, 29 insertions(+) diff --git a/tools/virsh.c b/tools/virsh.c index af31b9a..f9c9ccb 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -2549,6 +2549,34 @@ cleanup: return NULL; } +char ** +vshSuspendTargetCompleter(unsigned int unused_flags ATTRIBUTE_UNUSED) +{ + const char *targets[] = {"mem", "disk", "hybrid"}; + const unsigned int targets_size = ARRAY_CARDINALITY(targets); + char **names = NULL; + size_t i; + + names = vshMalloc(NULL, sizeof(char *) * (targets_size + 1)); + + if (!names) + return NULL; + + for (i = 0; i < targets_size; i++) { + if (VIR_STRDUP(names[i], targets[i]) < 0) + goto cleanup; + } + + names[i] = NULL; + return names; + +cleanup: + for (i = 0; names[i]; i++) + VIR_FREE(names[i]); + VIR_FREE(names); + return NULL; +} + /* ----------------- * Readline stuff * ----------------- diff --git a/tools/virsh.h b/tools/virsh.h index c4a9c13..5100c4b 100644 --- a/tools/virsh.h +++ b/tools/virsh.h @@ -255,6 +255,7 @@ struct _vshCmdGrp { }; char **vshDomainCompleter(unsigned int flags); +char **vshSuspendTargetCompleter(unsigned int unused_flags); void vshError(vshControl *ctl, const char *format, ...) ATTRIBUTE_FMT_PRINTF(2, 3); -- 1.8.3.1

vshRebootShutdownModeCompleter returns available modes for reboot/shutdown commands. --- tools/virsh.c | 28 ++++++++++++++++++++++++++++ tools/virsh.h | 1 + 2 files changed, 29 insertions(+) diff --git a/tools/virsh.c b/tools/virsh.c index f9c9ccb..13d66c7 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -2577,6 +2577,34 @@ cleanup: return NULL; } +char ** +vshRebootShutdownModeCompleter(unsigned int unused_flags ATTRIBUTE_UNUSED) +{ + const char *modes[] = {"acpi", "agent", "initctl", "signal"}; + const unsigned int modes_size = ARRAY_CARDINALITY(modes); + char **names = NULL; + size_t i; + + names = vshMalloc(NULL, sizeof(char *) * (modes_size + 1)); + + if (!names) + return NULL; + + for (i = 0; i < modes_size; i++) { + if (VIR_STRDUP(names[i], modes[i]) < 0) + goto cleanup; + } + + names[i] = NULL; + return names; + +cleanup: + for (i = 0; names[i]; i++) + VIR_FREE(names[i]); + VIR_FREE(names); + return NULL; +} + /* ----------------- * Readline stuff * ----------------- diff --git a/tools/virsh.h b/tools/virsh.h index 5100c4b..803d858 100644 --- a/tools/virsh.h +++ b/tools/virsh.h @@ -256,6 +256,7 @@ struct _vshCmdGrp { char **vshDomainCompleter(unsigned int flags); char **vshSuspendTargetCompleter(unsigned int unused_flags); +char **vshRebootShutdownModeCompleter(unsigned int unused_flags); void vshError(vshControl *ctl, const char *format, ...) ATTRIBUTE_FMT_PRINTF(2, 3); -- 1.8.3.1

New completion generators responsible for advances command and command options completions. vshReadlineCommandCompletionGenerator - generator for advanced command completions. This function will call some vshCmdCompleter function (e.g. vshDomainCompleter), which will return relevant data used for autocompletion (e.g. domain names). vshReadlineOptionsCompletionGenerator - almost the same as the vshReadlineCommandCompletionGenerator, but this one completes cmd options. vshReadlineCompletion() has become much more complex because we now have more generator functions and therefore more states to choose from. --- tools/virsh.c | 398 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 376 insertions(+), 22 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index 13d66c7..aec26b4 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -2610,6 +2610,25 @@ cleanup: * ----------------- */ +static const vshCmdDef * +vshDetermineCommandName(void) +{ + const vshCmdDef *cmd = NULL; + char *p; + char *cmdname; + + if (!(p = strchr(rl_line_buffer, ' '))) + return NULL; + + cmdname = vshCalloc(NULL, (p - rl_line_buffer) + 1, 1); + memcpy(cmdname, rl_line_buffer, p - rl_line_buffer); + + cmd = vshCmddefSearch(cmdname); + VIR_FREE(cmdname); + + return cmd; +} + /* * Generator function for command completion. STATE lets us * know whether to start from scratch; without any state @@ -2657,25 +2676,14 @@ vshReadlineCommandGenerator(const char *text, int state) static char * vshReadlineOptionsGenerator(const char *text, int state) { - static int list_index, len; static const vshCmdDef *cmd = NULL; + static int list_index, len; const char *name; if (!state) { - /* determine command name */ - char *p; - char *cmdname; - - if (!(p = strchr(rl_line_buffer, ' '))) - return NULL; - - cmdname = vshCalloc(NULL, (p - rl_line_buffer) + 1, 1); - memcpy(cmdname, rl_line_buffer, p - rl_line_buffer); - - cmd = vshCmddefSearch(cmdname); + cmd = vshDetermineCommandName(); list_index = 0; len = strlen(text); - VIR_FREE(cmdname); } if (!cmd) @@ -2707,22 +2715,368 @@ vshReadlineOptionsGenerator(const char *text, int state) return NULL; } +/* + * Generator function for command completion, but unlike + * the vshRaadlineCommandGenerator which completes command name, this function + * provides more advanced completion for commands by calling specific command + * completers (e.g. vshDomainCompleter). + */ +static char * +vshReadlineCommandCompletionGenerator(const char *text, int state) +{ + static const vshCmdDef *cmd = NULL; + static int list_index, len; + char **completed_names = NULL; + char *name; + + if (!state) { + cmd = vshDetermineCommandName(); + list_index = 0; + len = strlen(text); + } + + if (!cmd) + return NULL; + + if (!cmd->completer) + return NULL; + + completed_names = cmd->completer(cmd->completer_flags); + + if (!completed_names) + return NULL; + + while ((name = completed_names[list_index])) { + char *res; + list_index++; + + if (STRNEQLEN(name, text, len)) + /* Skip irrelevant names */ + continue; + + res = vshMalloc(NULL, strlen(name) + 1); + snprintf(res, strlen(name) + 1, "%s", name); + VIR_FREE(name); + return res; + } + VIR_FREE(completed_names); + + return NULL; +} + +/* + * Generator function for command option completion. Provides advances + * completion for command options. + */ +static char * +vshReadlineOptionsCompletionGenerator(const char *text ATTRIBUTE_UNUSED, + int state ATTRIBUTE_UNUSED) +{ + static const vshCmdDef *cmd = NULL; + static const vshCmdOptDef *opt = NULL; + static int list_index, len; + unsigned long int opt_index = 0; + size_t i; + char **completed_names = NULL; + char *name; + char *ptr = NULL; + + if (!state) { + cmd = vshDetermineCommandName(); + list_index = 0; + len = strlen(text); + } + + if (!cmd) + return NULL; + + if (!cmd->opts) + return NULL; + + for (i = 0; cmd->opts[i].name; i++) { + if ((ptr = strstr(rl_line_buffer, cmd->opts[i].name))) { + if (opt_index < (ptr - rl_line_buffer)) { + opt_index = ptr - rl_line_buffer; + opt = &cmd->opts[i]; + } + } + } + + if (!opt) + return NULL; + + if (!opt->completer) + return NULL; + + completed_names = opt->completer(opt->completer_flags); + + if (!completed_names) + return NULL; + + while ((name = completed_names[list_index])) { + char *res; + list_index++; + + if (STRNEQLEN(name, text, len)) + /* Skip irrelevant names */ + continue; + + res = vshMalloc(NULL, strlen(name) + 1); + snprintf(res, strlen(name) + 1, "%s", name); + VIR_FREE(name); + return res; + } + VIR_FREE(completed_names); + + return NULL; +} + +/* + * Returns current valid command name present in the rl_line_buffer. + */ +static char * +vshCurrentCmd(void) +{ + const char *name; + const vshCmdGrp *grp; + const vshCmdDef *cmds; + size_t grp_list_index, cmd_list_index; + char *found_cmd = NULL; + char *rl_copy = NULL; + char *pch; + + grp_list_index = 0; + cmd_list_index = 0; + grp = cmdGroups; + + while (grp[grp_list_index].name) { + cmds = grp[grp_list_index].commands; + + if (cmds[cmd_list_index].name) { + while ((name = cmds[cmd_list_index].name)) { + cmd_list_index++; + + if (VIR_STRDUP(rl_copy, rl_line_buffer) < 0) + return NULL; + + char *saveptr; + pch = strtok_r(rl_copy, " ", &saveptr); + + while (pch != NULL) { + if (STREQ(pch, name)) + if (VIR_STRDUP(found_cmd, name) < 0) + goto cleanup; + pch = strtok_r(NULL, " ", &saveptr); + } + } + } else { + cmd_list_index = 0; + grp_list_index++; + } + } + + if (!found_cmd) + goto cleanup; + + return found_cmd; + +cleanup: + VIR_FREE(rl_copy); + return NULL; +} + +/* + * Returns current valid command option name present in the rl_line_buffer. + */ +static char * +vshCurrentOpt(const char *cmd_name) +{ + const vshCmdDef *cmd = NULL; + const char *name; + unsigned long int opt_index = 0; + size_t cmd_opt_list_index; + char *found_opt = NULL; + char *ptr = NULL; + + if (!cmd_name) + return NULL; + + cmd = vshCmddefSearch(cmd_name); + + if (!cmd) + return NULL; + + if (!cmd->opts) + return NULL; + + cmd_opt_list_index = 0; + + while ((name = cmd->opts[cmd_opt_list_index].name)) { + cmd_opt_list_index++; + + if ((ptr = strstr(rl_line_buffer, name))) { + if (opt_index < (ptr - rl_line_buffer)) { + opt_index = ptr - rl_line_buffer; + if (VIR_STRDUP(found_opt, name) < 0) + return NULL; + } + } + } + + if (!found_opt) + return NULL; + + return found_opt; +} + +/* + * Returns name of the command completion currently present in the rl_line_buffer. + */ +static char* +vshCurrentCmdCom(const char *cmd_name) +{ + const vshCmdDef *cmd = NULL; + size_t i; + char **completed_names = NULL; + char *found_cmd_com = NULL; + + if (!cmd_name) + return NULL; + + cmd = vshCmddefSearch(cmd_name); + + if (!cmd) + return NULL; + + if (!cmd->completer) + return NULL; + + completed_names = cmd->completer(cmd->completer_flags); + + if (!completed_names) + return NULL; + + for (i = 0; completed_names[i]; i++) { + if (strstr(rl_line_buffer, completed_names[i])) { + if (VIR_STRDUP(found_cmd_com, completed_names[i]) < 0) + return NULL; + } + } + + if (!found_cmd_com) + return NULL; + + return found_cmd_com; +} + +/* + * Returns name of the option completion currently present in the rl_line_buffer. + */ +static char * +vshCurrentOptCom(const char *cmd_name) +{ + const vshCmdDef *cmd = NULL; + const vshCmdOptDef *opt = NULL; + unsigned long int opt_index = 0; + size_t i; + char **completed_names = NULL; + char *found_opt_com = NULL; + char *ptr = NULL; + + if (!cmd_name) + return NULL; + + cmd = vshCmddefSearch(cmd_name); + + if (!cmd) + return NULL; + + if (!cmd->opts) + return NULL; + + for (i = 0; cmd->opts[i].name; i++) { + if ((ptr = strstr(rl_line_buffer, cmd->opts[i].name))) { + if (opt_index < (ptr - rl_line_buffer)) { + opt_index = ptr - rl_line_buffer; + opt = &cmd->opts[i]; + } + } + } + + if (!opt) + return NULL; + + if (!opt->completer) + return NULL; + + completed_names = opt->completer(opt->completer_flags); + + if (!completed_names) + return NULL; + + for (i = 0; completed_names[i]; i++) { + if (strstr(rl_line_buffer, completed_names[i])) { + if (VIR_STRDUP(found_opt_com, completed_names[i]) < 0) + return NULL; + } + } + + if (!found_opt_com) + return NULL; + + return found_opt_com; +} + static char ** -vshReadlineCompletion(const char *text, int start, - int end ATTRIBUTE_UNUSED) +vshReadlineCompletion(const char *text, int start, int end ATTRIBUTE_UNUSED) { - char **matches = (char **) NULL; + const char *cmd = vshCurrentCmd(); + const char *cmd_com = vshCurrentCmdCom(cmd); + const char *opt = vshCurrentOpt(cmd); + const char *opt_com = vshCurrentOptCom(cmd); + char **matches = (char **)NULL; - if (start == 0) - /* command name generator */ + if (start == 0) { + /* Command name generator. */ matches = rl_completion_matches(text, vshReadlineCommandGenerator); - else - /* commands options */ - matches = rl_completion_matches(text, vshReadlineOptionsGenerator); + } else { + /* Command completer, commands options and commands options completer + * generators. + */ + if (strstr(text, "-")) { + /* When user wants to see options. */ + matches = rl_completion_matches(text, vshReadlineOptionsGenerator); + } else if (!cmd_com && !opt && !opt_com) { + matches = rl_completion_matches(text, vshReadlineCommandCompletionGenerator); + if (!matches) + matches = rl_completion_matches(text, vshReadlineOptionsGenerator); + } else if (cmd_com && !opt && !opt_com) { + matches = rl_completion_matches(text, vshReadlineOptionsGenerator); + } else if (cmd_com && opt && !opt_com) { + matches = rl_completion_matches(text, vshReadlineOptionsCompletionGenerator); + if (!matches) + matches = rl_completion_matches(text, vshReadlineOptionsGenerator); + } else if (!cmd_com && opt && !opt_com) { + matches = rl_completion_matches(text, vshReadlineCommandCompletionGenerator); + if (!matches) + matches = rl_completion_matches(text, vshReadlineOptionsCompletionGenerator); + if (!matches) + matches = rl_completion_matches(text, vshReadlineOptionsGenerator); + } else if (!cmd_com && opt && opt_com) { + matches = rl_completion_matches(text, vshReadlineOptionsGenerator); + } else if (cmd_com && opt && opt_com) { + matches = rl_completion_matches(text, vshReadlineOptionsGenerator); + } + } + + VIR_FREE(cmd); + VIR_FREE(cmd_com); + VIR_FREE(opt); + VIR_FREE(opt_com); + return matches; } - static int vshReadlineInit(vshControl *ctl) { -- 1.8.3.1

On 08.08.2013 16:38, Tomas Meszaros wrote:
New completion generators responsible for advances command and command options completions.
vshReadlineCommandCompletionGenerator - generator for advanced command completions. This function will call some vshCmdCompleter function (e.g. vshDomainCompleter), which will return relevant data used for autocompletion (e.g. domain names).
vshReadlineOptionsCompletionGenerator - almost the same as the vshReadlineCommandCompletionGenerator, but this one completes cmd options.
vshReadlineCompletion() has become much more complex because we now have more generator functions and therefore more states to choose from. --- tools/virsh.c | 398 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 376 insertions(+), 22 deletions(-)
diff --git a/tools/virsh.c b/tools/virsh.c index 13d66c7..aec26b4 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -2610,6 +2610,25 @@ cleanup: * ----------------- */
+static const vshCmdDef * +vshDetermineCommandName(void) +{ + const vshCmdDef *cmd = NULL; + char *p; + char *cmdname; + + if (!(p = strchr(rl_line_buffer, ' '))) + return NULL; + + cmdname = vshCalloc(NULL, (p - rl_line_buffer) + 1, 1);
so this is effectively vshMalloc().
+ memcpy(cmdname, rl_line_buffer, p - rl_line_buffer); + + cmd = vshCmddefSearch(cmdname); + VIR_FREE(cmdname); + + return cmd; +} + /* * Generator function for command completion. STATE lets us * know whether to start from scratch; without any state @@ -2657,25 +2676,14 @@ vshReadlineCommandGenerator(const char *text, int state) static char * vshReadlineOptionsGenerator(const char *text, int state) { - static int list_index, len; static const vshCmdDef *cmd = NULL; + static int list_index, len; const char *name;
if (!state) { - /* determine command name */ - char *p; - char *cmdname; - - if (!(p = strchr(rl_line_buffer, ' '))) - return NULL; - - cmdname = vshCalloc(NULL, (p - rl_line_buffer) + 1, 1); - memcpy(cmdname, rl_line_buffer, p - rl_line_buffer); - - cmd = vshCmddefSearch(cmdname); + cmd = vshDetermineCommandName(); list_index = 0; len = strlen(text); - VIR_FREE(cmdname); }
if (!cmd) @@ -2707,22 +2715,368 @@ vshReadlineOptionsGenerator(const char *text, int state) return NULL; }
+/* + * Generator function for command completion, but unlike + * the vshRaadlineCommandGenerator which completes command name, this function + * provides more advanced completion for commands by calling specific command + * completers (e.g. vshDomainCompleter). + */ +static char * +vshReadlineCommandCompletionGenerator(const char *text, int state) +{ + static const vshCmdDef *cmd = NULL; + static int list_index, len; + char **completed_names = NULL; + char *name; + + if (!state) { + cmd = vshDetermineCommandName(); + list_index = 0; + len = strlen(text); + } + + if (!cmd) + return NULL; + + if (!cmd->completer) + return NULL; + + completed_names = cmd->completer(cmd->completer_flags); + + if (!completed_names) + return NULL; + + while ((name = completed_names[list_index])) { + char *res; + list_index++; + + if (STRNEQLEN(name, text, len)) + /* Skip irrelevant names */ + continue; + + res = vshMalloc(NULL, strlen(name) + 1); + snprintf(res, strlen(name) + 1, "%s", name);
Consider using vshStrdup() instead of these two lines.
+ VIR_FREE(name); + return res; + } + VIR_FREE(completed_names); + + return NULL; +} + +/* + * Generator function for command option completion. Provides advances + * completion for command options. + */ +static char * +vshReadlineOptionsCompletionGenerator(const char *text ATTRIBUTE_UNUSED, + int state ATTRIBUTE_UNUSED) +{ + static const vshCmdDef *cmd = NULL; + static const vshCmdOptDef *opt = NULL; + static int list_index, len; + unsigned long int opt_index = 0; + size_t i; + char **completed_names = NULL; + char *name; + char *ptr = NULL; + + if (!state) { + cmd = vshDetermineCommandName(); + list_index = 0; + len = strlen(text); + } + + if (!cmd) + return NULL; + + if (!cmd->opts) + return NULL; + + for (i = 0; cmd->opts[i].name; i++) { + if ((ptr = strstr(rl_line_buffer, cmd->opts[i].name))) { + if (opt_index < (ptr - rl_line_buffer)) {
These to if's can be joined into one.
+ opt_index = ptr - rl_line_buffer; + opt = &cmd->opts[i]; + } + } + } + + if (!opt) + return NULL; + + if (!opt->completer) + return NULL; + + completed_names = opt->completer(opt->completer_flags); + + if (!completed_names) + return NULL; + + while ((name = completed_names[list_index])) { + char *res; + list_index++; + + if (STRNEQLEN(name, text, len)) + /* Skip irrelevant names */ + continue; + + res = vshMalloc(NULL, strlen(name) + 1); + snprintf(res, strlen(name) + 1, "%s", name);
Again, nice candidate to be replaced by vshStrdup().
+ VIR_FREE(name); + return res; + } + VIR_FREE(completed_names); + + return NULL; +} +
Michal

Add .completer and .completer_flags to domManagementCmds. vshDomainCompleter (with appropriate flags) is enabled for commands requiring only single <domain> argument. --- tools/virsh-domain.c | 230 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 180 insertions(+), 50 deletions(-) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 5e1196f..b89b219 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -2752,7 +2752,8 @@ static const vshCmdOptDef opts_dom_pm_suspend[] = { .flags = VSH_OFLAG_REQ, .help = N_("mem(Suspend-to-RAM), " "disk(Suspend-to-Disk), " - "hybrid(Hybrid-Suspend)") + "hybrid(Hybrid-Suspend)"), + .completer = vshSuspendTargetCompleter }, {.name = NULL} }; @@ -4707,7 +4708,8 @@ static const vshCmdOptDef opts_shutdown[] = { }, {.name = "mode", .type = VSH_OT_STRING, - .help = N_("shutdown mode: acpi|agent|initctl|signal") + .help = N_("shutdown mode: acpi|agent|initctl|signal"), + .completer = vshRebootShutdownModeCompleter }, {.name = NULL} }; @@ -4793,7 +4795,8 @@ static const vshCmdOptDef opts_reboot[] = { }, {.name = "mode", .type = VSH_OT_STRING, - .help = N_("shutdown mode: acpi|agent|initctl|signal") + .help = N_("shutdown mode: acpi|agent|initctl|signal"), + .completer = vshRebootShutdownModeCompleter }, {.name = NULL} }; @@ -10333,7 +10336,10 @@ const vshCmdDef domManagementCmds[] = { .handler = cmdAutostart, .opts = opts_autostart, .info = info_autostart, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_INACTIVE }, {.name = "blkdeviotune", .handler = cmdBlkdeviotune, @@ -10345,7 +10351,10 @@ const vshCmdDef domManagementCmds[] = { .handler = cmdBlkiotune, .opts = opts_blkiotune, .info = info_blkiotune, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_INACTIVE }, {.name = "blockcommit", .handler = cmdBlockCommit, @@ -10388,7 +10397,10 @@ const vshCmdDef domManagementCmds[] = { .handler = cmdConsole, .opts = opts_console, .info = info_console, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_INACTIVE }, #endif {.name = "cpu-baseline", @@ -10425,13 +10437,18 @@ const vshCmdDef domManagementCmds[] = { .handler = cmdDesc, .opts = opts_desc, .info = info_desc, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_INACTIVE }, {.name = "destroy", .handler = cmdDestroy, .opts = opts_destroy, .info = info_destroy, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE }, {.name = "detach-device", .handler = cmdDetachDevice, @@ -10455,25 +10472,37 @@ const vshCmdDef domManagementCmds[] = { .handler = cmdDomDisplay, .opts = opts_domdisplay, .info = info_domdisplay, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_INACTIVE }, {.name = "domfstrim", .handler = cmdDomFSTrim, .opts = opts_domfstrim, .info = info_domfstrim, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_INACTIVE }, {.name = "domhostname", .handler = cmdDomHostname, .opts = opts_domhostname, .info = info_domhostname, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_INACTIVE }, {.name = "domid", .handler = cmdDomid, .opts = opts_domid, .info = info_domid, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_INACTIVE }, {.name = "domif-setlink", .handler = cmdDomIfSetLink, @@ -10491,13 +10520,17 @@ const vshCmdDef domManagementCmds[] = { .handler = cmdDomjobabort, .opts = opts_domjobabort, .info = info_domjobabort, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE }, {.name = "domjobinfo", .handler = cmdDomjobinfo, .opts = opts_domjobinfo, .info = info_domjobinfo, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE }, {.name = "domname", .handler = cmdDomname, @@ -10509,19 +10542,27 @@ const vshCmdDef domManagementCmds[] = { .handler = cmdDomPMSuspend, .opts = opts_dom_pm_suspend, .info = info_dom_pm_suspend, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_RUNNING }, {.name = "dompmwakeup", .handler = cmdDomPMWakeup, .opts = opts_dom_pm_wakeup, .info = info_dom_pm_wakeup, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE }, {.name = "domuuid", .handler = cmdDomuuid, .opts = opts_domuuid, .info = info_domuuid, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_INACTIVE }, {.name = "domxml-from-native", .handler = cmdDomXMLFromNative, @@ -10539,31 +10580,46 @@ const vshCmdDef domManagementCmds[] = { .handler = cmdDump, .opts = opts_dump, .info = info_dump, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_RUNNING }, {.name = "dumpxml", .handler = cmdDumpXML, .opts = opts_dumpxml, .info = info_dumpxml, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_INACTIVE }, {.name = "edit", .handler = cmdEdit, .opts = opts_edit, .info = info_edit, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_INACTIVE }, {.name = "inject-nmi", .handler = cmdInjectNMI, .opts = opts_inject_nmi, .info = info_inject_nmi, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_RUNNING }, {.name = "send-key", .handler = cmdSendKey, .opts = opts_send_key, .info = info_send_key, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_RUNNING }, {.name = "send-process-signal", .handler = cmdSendProcessSignal, @@ -10575,19 +10631,29 @@ const vshCmdDef domManagementCmds[] = { .handler = cmdLxcEnterNamespace, .opts = opts_lxc_enter_namespace, .info = info_lxc_enter_namespace, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_INACTIVE }, {.name = "managedsave", .handler = cmdManagedSave, .opts = opts_managedsave, .info = info_managedsave, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_RUNNING | + VIR_CONNECT_LIST_DOMAINS_NO_MANAGEDSAVE }, {.name = "managedsave-remove", .handler = cmdManagedSaveRemove, .opts = opts_managedsaveremove, .info = info_managedsaveremove, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_INACTIVE | + VIR_CONNECT_LIST_DOMAINS_MANAGEDSAVE }, {.name = "maxvcpus", .handler = cmdMaxvcpus, @@ -10599,7 +10665,10 @@ const vshCmdDef domManagementCmds[] = { .handler = cmdMemtune, .opts = opts_memtune, .info = info_memtune, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_INACTIVE }, {.name = "migrate", .handler = cmdMigrate, @@ -10611,31 +10680,46 @@ const vshCmdDef domManagementCmds[] = { .handler = cmdMigrateSetMaxDowntime, .opts = opts_migrate_setmaxdowntime, .info = info_migrate_setmaxdowntime, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_INACTIVE }, {.name = "migrate-compcache", .handler = cmdMigrateCompCache, .opts = opts_migrate_compcache, .info = info_migrate_compcache, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_RUNNING }, {.name = "migrate-setspeed", .handler = cmdMigrateSetMaxSpeed, .opts = opts_migrate_setspeed, .info = info_migrate_setspeed, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_INACTIVE }, {.name = "migrate-getspeed", .handler = cmdMigrateGetMaxSpeed, .opts = opts_migrate_getspeed, .info = info_migrate_getspeed, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_INACTIVE }, {.name = "numatune", .handler = cmdNumatune, .opts = opts_numatune, .info = info_numatune, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_INACTIVE }, {.name = "qemu-attach", .handler = cmdQemuAttach, @@ -10647,25 +10731,35 @@ const vshCmdDef domManagementCmds[] = { .handler = cmdQemuMonitorCommand, .opts = opts_qemu_monitor_command, .info = info_qemu_monitor_command, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_INACTIVE }, {.name = "qemu-agent-command", .handler = cmdQemuAgentCommand, .opts = opts_qemu_agent_command, .info = info_qemu_agent_command, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_INACTIVE }, {.name = "reboot", .handler = cmdReboot, .opts = opts_reboot, .info = info_reboot, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE }, {.name = "reset", .handler = cmdReset, .opts = opts_reset, .info = info_reset, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE }, {.name = "restore", .handler = cmdRestore, @@ -10677,7 +10771,10 @@ const vshCmdDef domManagementCmds[] = { .handler = cmdResume, .opts = opts_resume, .info = info_resume, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_PAUSED }, {.name = "save", .handler = cmdSave, @@ -10707,61 +10804,83 @@ const vshCmdDef domManagementCmds[] = { .handler = cmdSchedinfo, .opts = opts_schedinfo, .info = info_schedinfo, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_INACTIVE }, {.name = "screenshot", .handler = cmdScreenshot, .opts = opts_screenshot, .info = info_screenshot, - .flags = 0 + .flags = 0, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE }, {.name = "setmaxmem", .handler = cmdSetmaxmem, .opts = opts_setmaxmem, .info = info_setmaxmem, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_INACTIVE }, {.name = "setmem", .handler = cmdSetmem, .opts = opts_setmem, .info = info_setmem, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE }, {.name = "setvcpus", .handler = cmdSetvcpus, .opts = opts_setvcpus, .info = info_setvcpus, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE }, {.name = "shutdown", .handler = cmdShutdown, .opts = opts_shutdown, .info = info_shutdown, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE }, {.name = "start", .handler = cmdStart, .opts = opts_start, .info = info_start, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_INACTIVE | + VIR_CONNECT_LIST_DOMAINS_PERSISTENT, }, {.name = "suspend", .handler = cmdSuspend, .opts = opts_suspend, .info = info_suspend, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE }, {.name = "ttyconsole", .handler = cmdTTYConsole, .opts = opts_ttyconsole, .info = info_ttyconsole, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE }, {.name = "undefine", .handler = cmdUndefine, .opts = opts_undefine, .info = info_undefine, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_INACTIVE }, {.name = "update-device", .handler = cmdUpdateDevice, @@ -10779,25 +10898,36 @@ const vshCmdDef domManagementCmds[] = { .handler = cmdVcpuinfo, .opts = opts_vcpuinfo, .info = info_vcpuinfo, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_INACTIVE }, {.name = "vcpupin", .handler = cmdVcpuPin, .opts = opts_vcpupin, .info = info_vcpupin, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_INACTIVE }, {.name = "emulatorpin", .handler = cmdEmulatorPin, .opts = opts_emulatorpin, .info = info_emulatorpin, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_INACTIVE }, {.name = "vncdisplay", .handler = cmdVNCDisplay, .opts = opts_vncdisplay, .info = info_vncdisplay, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE }, {.name = NULL} }; -- 1.8.3.1

On 08.08.2013 16:38, Tomas Meszaros wrote:
Add .completer and .completer_flags to domManagementCmds.
vshDomainCompleter (with appropriate flags) is enabled for commands requiring only single <domain> argument. --- tools/virsh-domain.c | 230 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 180 insertions(+), 50 deletions(-)
diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 5e1196f..b89b219 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -2752,7 +2752,8 @@ static const vshCmdOptDef opts_dom_pm_suspend[] = { .flags = VSH_OFLAG_REQ, .help = N_("mem(Suspend-to-RAM), " "disk(Suspend-to-Disk), " - "hybrid(Hybrid-Suspend)") + "hybrid(Hybrid-Suspend)"), + .completer = vshSuspendTargetCompleter
It makes more sense to add this chunk to the patch introducing vshSuspendTargetCompleter (3/8 that is). In fact, I think you need to reorder the patches. 1/8 is correct (from ordering POV). It should be followed by 5/8 and then the rest of the patches which add completers.
}, {.name = NULL} }; @@ -4707,7 +4708,8 @@ static const vshCmdOptDef opts_shutdown[] = { }, {.name = "mode", .type = VSH_OT_STRING, - .help = N_("shutdown mode: acpi|agent|initctl|signal") + .help = N_("shutdown mode: acpi|agent|initctl|signal"), + .completer = vshRebootShutdownModeCompleter }, {.name = NULL} }; @@ -4793,7 +4795,8 @@ static const vshCmdOptDef opts_reboot[] = { }, {.name = "mode", .type = VSH_OT_STRING, - .help = N_("shutdown mode: acpi|agent|initctl|signal") + .help = N_("shutdown mode: acpi|agent|initctl|signal"), + .completer = vshRebootShutdownModeCompleter }, {.name = NULL} }; @@ -10333,7 +10336,10 @@ const vshCmdDef domManagementCmds[] = { .handler = cmdAutostart, .opts = opts_autostart, .info = info_autostart, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_INACTIVE
Nice! It's super-easy for other commands to profit from well written (read generic enough) completer. Just pass the correct flags. Michal

Add .completer and .completer_flags to snapshotCmds. Provides domain completion for most of the snapshotCmds commands. --- tools/virsh-snapshot.c | 45 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/tools/virsh-snapshot.c b/tools/virsh-snapshot.c index db9715b..cdc358b 100644 --- a/tools/virsh-snapshot.c +++ b/tools/virsh-snapshot.c @@ -2012,25 +2012,37 @@ const vshCmdDef snapshotCmds[] = { .handler = cmdSnapshotCreate, .opts = opts_snapshot_create, .info = info_snapshot_create, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_INACTIVE }, {.name = "snapshot-create-as", .handler = cmdSnapshotCreateAs, .opts = opts_snapshot_create_as, .info = info_snapshot_create_as, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_INACTIVE }, {.name = "snapshot-current", .handler = cmdSnapshotCurrent, .opts = opts_snapshot_current, .info = info_snapshot_current, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_INACTIVE }, {.name = "snapshot-delete", .handler = cmdSnapshotDelete, .opts = opts_snapshot_delete, .info = info_snapshot_delete, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_INACTIVE }, {.name = "snapshot-dumpxml", .handler = cmdSnapshotDumpXML, @@ -2042,31 +2054,46 @@ const vshCmdDef snapshotCmds[] = { .handler = cmdSnapshotEdit, .opts = opts_snapshot_edit, .info = info_snapshot_edit, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_INACTIVE }, {.name = "snapshot-info", .handler = cmdSnapshotInfo, .opts = opts_snapshot_info, .info = info_snapshot_info, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_INACTIVE }, {.name = "snapshot-list", .handler = cmdSnapshotList, .opts = opts_snapshot_list, .info = info_snapshot_list, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_INACTIVE }, {.name = "snapshot-parent", .handler = cmdSnapshotParent, .opts = opts_snapshot_parent, .info = info_snapshot_parent, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_INACTIVE }, {.name = "snapshot-revert", .handler = cmdDomainSnapshotRevert, .opts = opts_snapshot_revert, .info = info_snapshot_revert, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_INACTIVE }, {.name = NULL} }; -- 1.8.3.1

Add .completer and .completer_flags to domMonitoringCmds. Provides domain completion for domMonitoringCmds commands. --- tools/virsh-domain-monitor.c | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/tools/virsh-domain-monitor.c b/tools/virsh-domain-monitor.c index b29b82a..0f30902 100644 --- a/tools/virsh-domain-monitor.c +++ b/tools/virsh-domain-monitor.c @@ -1876,7 +1876,9 @@ const vshCmdDef domMonitoringCmds[] = { .handler = cmdDomBlkError, .opts = opts_domblkerror, .info = info_domblkerror, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE }, {.name = "domblkinfo", .handler = cmdDomblkinfo, @@ -1888,7 +1890,10 @@ const vshCmdDef domMonitoringCmds[] = { .handler = cmdDomblklist, .opts = opts_domblklist, .info = info_domblklist, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_INACTIVE }, {.name = "domblkstat", .handler = cmdDomblkstat, @@ -1900,7 +1905,9 @@ const vshCmdDef domMonitoringCmds[] = { .handler = cmdDomControl, .opts = opts_domcontrol, .info = info_domcontrol, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE }, {.name = "domif-getlink", .handler = cmdDomIfGetLink, @@ -1912,7 +1919,10 @@ const vshCmdDef domMonitoringCmds[] = { .handler = cmdDomiflist, .opts = opts_domiflist, .info = info_domiflist, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_INACTIVE }, {.name = "domifstat", .handler = cmdDomIfstat, @@ -1924,19 +1934,27 @@ const vshCmdDef domMonitoringCmds[] = { .handler = cmdDominfo, .opts = opts_dominfo, .info = info_dominfo, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_INACTIVE }, {.name = "dommemstat", .handler = cmdDomMemStat, .opts = opts_dommemstat, .info = info_dommemstat, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE }, {.name = "domstate", .handler = cmdDomstate, .opts = opts_domstate, .info = info_domstate, - .flags = 0 + .flags = 0, + .completer = vshDomainCompleter, + .completer_flags = VIR_CONNECT_LIST_DOMAINS_ACTIVE | + VIR_CONNECT_LIST_DOMAINS_INACTIVE }, {.name = "list", .handler = cmdList, -- 1.8.3.1

On 08.08.2013 16:38, Tomas Meszaros wrote:
Hi, this patch series is a prototype for my GSoC project (Michal Privoznik is my mentor).
I'm working on virsh auto-completion, trying to make it more "intelligent". At this stage, prototype is capable of command and option completion. Three completer functions are currently implemented so you can test it. If it turns out that this prototype is good enough, I will implement more completer functions.
Tomas Meszaros (8): virsh: Add vshCmdCompleter and vshOptCompleter virsh: Add vshDomainCompleter virsh: Add vshSuspendTargetCompleter virsh: Add vshRebootShutdownModeCompleter virsh: Improve readline generators and readline completion virsh: Add completer functions to domManaggementCmds virsh: Add completer functions to snapshotCmds virsh: Add completer functions to domMonitoringCmds
tools/virsh-domain-monitor.c | 32 ++- tools/virsh-domain.c | 240 +++++++++++++++----- tools/virsh-snapshot.c | 45 +++- tools/virsh.c | 507 +++++++++++++++++++++++++++++++++++++++++-- tools/virsh.h | 11 + 5 files changed, 742 insertions(+), 93 deletions(-)
I think this is very good starting point. See my inline comments. Looking forward to v2. Michal
participants (3)
-
Eric Blake
-
Michal Privoznik
-
Tomas Meszaros