* vshMalloc is now used in vshDetermineCommandName
* vshStrdup instead of vshMalloc + snprintf
* joined if's in vshReadlineOptionsCompletionGenerator
---
tools/virsh.c | 395 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 373 insertions(+), 22 deletions(-)
diff --git a/tools/virsh.c b/tools/virsh.c
index 15f529e..af6e939 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -2508,6 +2508,25 @@ vshCloseLogFile(vshControl *ctl)
* -----------------
*/
+static const vshCmdDef *
+vshDetermineCommandName(void)
+{
+ const vshCmdDef *cmd = NULL;
+ char *p;
+ char *cmdname;
+
+ if (!(p = strchr(rl_line_buffer, ' ')))
+ return NULL;
+
+ cmdname = vshMalloc(NULL, (p - rl_line_buffer) + 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
@@ -2555,25 +2574,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)
@@ -2605,22 +2613,365 @@ 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 = vshStrdup(NULL, 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)) &&
+ (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 = vshStrdup(NULL, 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