[libvirt] [PATCH v2 3/9] virsh: Improve readline generators and readline completion

Tomas Meszaros exo at tty.sk
Tue Aug 20 20:02:50 UTC 2013


* 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




More information about the libvir-list mailing list