[libvirt] [PATCH 5/8] virsh: Improve readline generators and readline completion

Tomas Meszaros exo at tty.sk
Thu Aug 8 14:38:48 UTC 2013


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




More information about the libvir-list mailing list