]> xenbits.xensource.com Git - libvirt.git/commitdiff
virsh: Introduce new 'VSH_OT_ARGV' accessors
authorPeter Krempa <pkrempa@redhat.com>
Wed, 6 Mar 2024 16:26:56 +0000 (17:26 +0100)
committerPeter Krempa <pkrempa@redhat.com>
Thu, 25 Apr 2024 12:13:19 +0000 (14:13 +0200)
In preparation for internal parser refactor introduce new accessors for
the VSH_OT_ARGV type which will return a NULL-terminated string list or
even a concatenated string for the given argument.

Signed-off-by: Peter Krempa <pkrempa@redhat.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
tools/virsh-checkpoint.c
tools/virsh-domain-monitor.c
tools/virsh-domain.c
tools/virsh-network.c
tools/virsh-snapshot.c
tools/vsh.c
tools/vsh.h

index fea6b4fb4b96b06ce11b5e1281361b7c92d45584..972b2f979c22e469710972c38540249ce6a7ef84 100644 (file)
@@ -226,7 +226,7 @@ cmdCheckpointCreateAs(vshControl *ctl,
     const char *desc = NULL;
     g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
     unsigned int flags = 0;
-    const vshCmdOpt *opt = NULL;
+    const char **diskspec = NULL;
 
     if (vshCommandOptBool(cmd, "quiesce"))
         flags |= VIR_DOMAIN_CHECKPOINT_CREATE_QUIESCE;
@@ -243,13 +243,15 @@ cmdCheckpointCreateAs(vshControl *ctl,
     virBufferEscapeString(&buf, "<name>%s</name>\n", name);
     virBufferEscapeString(&buf, "<description>%s</description>\n", desc);
 
-    if (vshCommandOptBool(cmd, "diskspec")) {
+    if ((diskspec = vshCommandOptArgv(cmd, "diskspec"))) {
         virBufferAddLit(&buf, "<disks>\n");
         virBufferAdjustIndent(&buf, 2);
-        while ((opt = vshCommandOptArgv(ctl, cmd, opt))) {
-            if (virshParseCheckpointDiskspec(ctl, &buf, opt->data) < 0)
+
+        for (; *diskspec; diskspec++) {
+            if (virshParseCheckpointDiskspec(ctl, &buf, *diskspec) < 0)
                 return false;
         }
+
         virBufferAdjustIndent(&buf, -2);
         virBufferAddLit(&buf, "</disks>\n");
     }
index 700f3ae094620b03c01978b83a0139e6f77aae6f..ef07ace5777ab2167b3a043647c6bb4e4d12a84a 100644 (file)
@@ -2096,7 +2096,7 @@ cmdDomstats(vshControl *ctl, const vshCmd *cmd)
     virDomainStatsRecordPtr *next;
     bool raw = vshCommandOptBool(cmd, "raw");
     int flags = 0;
-    const vshCmdOpt *opt = NULL;
+    const char **doms;
     bool ret = false;
     virshControl *priv = ctl->privData;
 
@@ -2166,12 +2166,12 @@ cmdDomstats(vshControl *ctl, const vshCmd *cmd)
     if (vshCommandOptBool(cmd, "nowait"))
         flags |= VIR_CONNECT_GET_ALL_DOMAINS_STATS_NOWAIT;
 
-    if (vshCommandOptBool(cmd, "domain")) {
+    if ((doms = vshCommandOptArgv(cmd, "domain"))) {
         domlist = g_new0(virDomainPtr, 1);
         ndoms = 1;
 
-        while ((opt = vshCommandOptArgv(ctl, cmd, opt))) {
-            if (!(dom = virshLookupDomainBy(ctl, opt->data,
+        for (; *doms; doms++) {
+            if (!(dom = virshLookupDomainBy(ctl, *doms,
                                             VIRSH_BYID |
                                             VIRSH_BYUUID | VIRSH_BYNAME)))
                 goto cleanup;
index 28d90377a12a2ad503327ac5a406f52e69d11de3..50e80689a28ba307c3ef464e80b87b5fd00473a1 100644 (file)
@@ -5065,15 +5065,15 @@ cmdSchedInfoUpdate(vshControl *ctl, const vshCmd *cmd,
 {
     char *set_val = NULL;
     const char *val = NULL;
-    const vshCmdOpt *opt = NULL;
+    const char **opt;
     virTypedParameterPtr params = NULL;
     int nparams = 0;
     int maxparams = 0;
     int ret = -1;
     int rv;
 
-    while ((opt = vshCommandOptArgv(ctl, cmd, opt))) {
-        g_autofree char *set_field = g_strdup(opt->data);
+    for (opt = vshCommandOptArgv(cmd, "set"); opt && *opt; opt++) {
+        g_autofree char *set_field = g_strdup(*opt);
 
         if (!(set_val = strchr(set_field, '='))) {
             vshError(ctl, "%s", _("Invalid syntax for --set, expecting name=value"));
@@ -8248,8 +8248,6 @@ cmdDesc(vshControl *ctl, const vshCmd *cmd)
     int state;
     int type;
     g_autofree char *descArg = NULL;
-    const vshCmdOpt *opt = NULL;
-    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
     unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT;
     unsigned int queryflags = 0;
 
@@ -8274,12 +8272,7 @@ cmdDesc(vshControl *ctl, const vshCmd *cmd)
     else
         type = VIR_DOMAIN_METADATA_DESCRIPTION;
 
-    while ((opt = vshCommandOptArgv(ctl, cmd, opt)))
-        virBufferAsprintf(&buf, "%s ", opt->data);
-
-    virBufferTrim(&buf, " ");
-
-    descArg = virBufferContentAndReset(&buf);
+    descArg = g_strdup(vshCommandOptArgvString(cmd, "new-desc"));
 
     if (edit || descArg) {
         g_autofree char *descDom = NULL;
@@ -8559,7 +8552,7 @@ cmdSendKey(vshControl *ctl, const vshCmd *cmd)
     int codeset;
     unsigned int holdtime = 0;
     int count = 0;
-    const vshCmdOpt *opt = NULL;
+    const char **opt = NULL;
     int keycode;
     unsigned int keycodes[VIR_DOMAIN_SEND_KEY_MAX_KEYS];
 
@@ -8583,15 +8576,15 @@ cmdSendKey(vshControl *ctl, const vshCmd *cmd)
         return false;
     }
 
-    while ((opt = vshCommandOptArgv(ctl, cmd, opt))) {
+    for (opt = vshCommandOptArgv(cmd, "keycode"); opt && *opt; opt++) {
         if (count == VIR_DOMAIN_SEND_KEY_MAX_KEYS) {
             vshError(ctl, _("too many keycodes"));
             return false;
         }
 
-        if ((keycode = virshKeyCodeGetInt(opt->data)) < 0) {
-            if ((keycode = virKeycodeValueFromString(codeset, opt->data)) < 0) {
-                vshError(ctl, _("invalid keycode: '%1$s'"), opt->data);
+        if ((keycode = virshKeyCodeGetInt(*opt)) < 0) {
+            if ((keycode = virKeycodeValueFromString(codeset, *opt)) < 0) {
+                vshError(ctl, _("invalid keycode: '%1$s'"), *opt);
                 return false;
             }
         }
@@ -9659,31 +9652,15 @@ static const vshCmdOptDef opts_qemu_monitor_command[] = {
 };
 
 
-static char *
-cmdQemuMonitorCommandConcatCmd(vshControl *ctl,
-                               const vshCmd *cmd,
-                               const vshCmdOpt *opt)
-{
-    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
-
-    while ((opt = vshCommandOptArgv(ctl, cmd, opt)))
-        virBufferAsprintf(&buf, "%s ", opt->data);
-
-    virBufferTrim(&buf, " ");
-
-    return virBufferContentAndReset(&buf);
-}
-
-
 static char *
 cmdQemuMonitorCommandQMPWrap(vshControl *ctl,
                              const vshCmd *cmd)
 {
-    g_autofree char *fullcmd = cmdQemuMonitorCommandConcatCmd(ctl, cmd, NULL);
+    const char *fullcmd = vshCommandOptArgvString(cmd, "cmd");
     g_autoptr(virJSONValue) fullcmdjson = NULL;
     g_autofree char *fullargs = NULL;
     g_autoptr(virJSONValue) fullargsjson = NULL;
-    const vshCmdOpt *opt = NULL;
+    const char **opt = NULL;
     const char *commandname = NULL;
     g_autoptr(virJSONValue) command = NULL;
     g_autoptr(virJSONValue) arguments = NULL;
@@ -9695,16 +9672,17 @@ cmdQemuMonitorCommandQMPWrap(vshControl *ctl,
 
     /* if we've got a JSON object, pass it through */
     if (virJSONValueIsObject(fullcmdjson))
-        return g_steal_pointer(&fullcmd);
+        return g_strdup(fullcmd);
 
     /* we try to wrap the command and possible arguments into a JSON object, if
      * we as fall back we pass through what we've got from the user */
 
-    if ((opt = vshCommandOptArgv(ctl, cmd, opt)))
-        commandname = opt->data;
+    opt = vshCommandOptArgv(cmd, "cmd");
+    commandname = *opt;
+    opt++;
 
     /* now we process arguments similarly to how we've dealt with the full command */
-    if ((fullargs = cmdQemuMonitorCommandConcatCmd(ctl, cmd, opt)) &&
+    if ((fullargs = g_strjoinv(" ", (GStrv) opt)) &&
         !(fullargsjson = virJSONValueFromString(fullargs))) {
         /* Reset the error before adding wrapping. */
         vshResetLibvirtError();
@@ -9721,8 +9699,8 @@ cmdQemuMonitorCommandQMPWrap(vshControl *ctl,
         virBufferAddLit(&buf, "{");
         /* opt points to the _ARGV option bit containing the command so we'll
          * iterate through the arguments now */
-        while ((opt = vshCommandOptArgv(ctl, cmd, opt)))
-            virBufferAsprintf(&buf, "%s,", opt->data);
+        for (; *opt; opt++)
+            virBufferAsprintf(&buf, "%s,", *opt);
 
         virBufferTrim(&buf, ",");
         virBufferAddLit(&buf, "}");
@@ -9767,7 +9745,7 @@ cmdQemuMonitorCommand(vshControl *ctl, const vshCmd *cmd)
 
     if (vshCommandOptBool(cmd, "hmp")) {
         flags |= VIR_DOMAIN_QEMU_MONITOR_COMMAND_HMP;
-        monitor_cmd = cmdQemuMonitorCommandConcatCmd(ctl, cmd, NULL);
+        monitor_cmd = g_strdup(vshCommandOptArgvString(cmd, "cmd"));
     } else {
         monitor_cmd = cmdQemuMonitorCommandQMPWrap(ctl, cmd);
     }
@@ -10062,26 +10040,16 @@ cmdQemuAgentCommand(vshControl *ctl, const vshCmd *cmd)
 {
     g_autoptr(virshDomain) dom = NULL;
     bool ret = false;
-    g_autofree char *guest_agent_cmd = NULL;
     char *result = NULL;
     int timeout = VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT;
     int judge = 0;
     unsigned int flags = 0;
-    const vshCmdOpt *opt = NULL;
-    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
     virJSONValue *pretty = NULL;
 
     dom = virshCommandOptDomain(ctl, cmd, NULL);
     if (dom == NULL)
         goto cleanup;
 
-    while ((opt = vshCommandOptArgv(ctl, cmd, opt)))
-        virBufferAsprintf(&buf, "%s ", opt->data);
-
-    virBufferTrim(&buf, " ");
-
-    guest_agent_cmd = virBufferContentAndReset(&buf);
-
     judge = vshCommandOptInt(ctl, cmd, "timeout", &timeout);
     if (judge < 0)
         goto cleanup;
@@ -10106,7 +10074,7 @@ cmdQemuAgentCommand(vshControl *ctl, const vshCmd *cmd)
         goto cleanup;
     }
 
-    result = virDomainQemuAgentCommand(dom, guest_agent_cmd, timeout, flags);
+    result = virDomainQemuAgentCommand(dom, vshCommandOptArgvString(cmd, "cmd"), timeout, flags);
     if (!result)
         goto cleanup;
 
@@ -10158,9 +10126,7 @@ static bool
 cmdLxcEnterNamespace(vshControl *ctl, const vshCmd *cmd)
 {
     g_autoptr(virshDomain) dom = NULL;
-    const vshCmdOpt *opt = NULL;
-    g_autofree char **cmdargv = NULL;
-    size_t ncmdargv = 0;
+    const char **cmdargv = NULL;
     pid_t pid;
     int nfdlist;
     int *fdlist;
@@ -10178,12 +10144,7 @@ cmdLxcEnterNamespace(vshControl *ctl, const vshCmd *cmd)
     if (vshCommandOptBool(cmd, "noseclabel"))
         setlabel = false;
 
-    while ((opt = vshCommandOptArgv(ctl, cmd, opt))) {
-        VIR_EXPAND_N(cmdargv, ncmdargv, 1);
-        cmdargv[ncmdargv-1] = opt->data;
-    }
-    VIR_EXPAND_N(cmdargv, ncmdargv, 1);
-    cmdargv[ncmdargv - 1] = NULL;
+    cmdargv = vshCommandOptArgv(cmd, "cmd");
 
     if ((nfdlist = virDomainLxcOpenNamespace(dom, &fdlist, 0)) < 0)
         return false;
@@ -10233,7 +10194,7 @@ cmdLxcEnterNamespace(vshControl *ctl, const vshCmd *cmd)
         _exit(EXIT_CANCELED);
 
     if (pid == 0) {
-        execv(cmdargv[0], cmdargv);
+        execv(cmdargv[0], (char **) cmdargv);
         _exit(errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
     }
 
@@ -12872,18 +12833,15 @@ static bool
 cmdDomFSFreeze(vshControl *ctl, const vshCmd *cmd)
 {
     g_autoptr(virshDomain) dom = NULL;
-    const vshCmdOpt *opt = NULL;
-    g_autofree const char **mountpoints = NULL;
+    const char **mountpoints = vshCommandOptArgv(cmd, "mountpoint");
     size_t nmountpoints = 0;
     int count = 0;
 
     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
         return false;
 
-    while ((opt = vshCommandOptArgv(ctl, cmd, opt))) {
-        VIR_EXPAND_N(mountpoints, nmountpoints, 1);
-        mountpoints[nmountpoints-1] = opt->data;
-    }
+    if (mountpoints)
+        nmountpoints = g_strv_length((GStrv) mountpoints);
 
     if ((count = virDomainFSFreeze(dom, mountpoints, nmountpoints, 0)) < 0) {
         vshError(ctl, _("Unable to freeze filesystems"));
@@ -12913,18 +12871,15 @@ static bool
 cmdDomFSThaw(vshControl *ctl, const vshCmd *cmd)
 {
     g_autoptr(virshDomain) dom = NULL;
-    const vshCmdOpt *opt = NULL;
-    g_autofree const char **mountpoints = NULL;
+    const char **mountpoints = vshCommandOptArgv(cmd, "mountpoint");
     size_t nmountpoints = 0;
     int count = 0;
 
     if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
         return false;
 
-    while ((opt = vshCommandOptArgv(ctl, cmd, opt))) {
-        VIR_EXPAND_N(mountpoints, nmountpoints, 1);
-        mountpoints[nmountpoints-1] = opt->data;
-    }
+    if (mountpoints)
+        nmountpoints = g_strv_length((GStrv) mountpoints);
 
     if ((count = virDomainFSThaw(dom, mountpoints, nmountpoints, 0)) < 0) {
         vshError(ctl, _("Unable to thaw filesystems"));
index 24049a66f36bfd4bc576ad0a709e939afbd1721b..6fcc7fd8ee32cc4a4fef9f97d53afffe26d7258e 100644 (file)
@@ -388,8 +388,6 @@ cmdNetworkDesc(vshControl *ctl, const vshCmd *cmd)
 
     int type;
     g_autofree char *descArg = NULL;
-    const vshCmdOpt *opt = NULL;
-    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
     unsigned int flags = VIR_NETWORK_UPDATE_AFFECT_CURRENT;
     unsigned int queryflags = 0;
 
@@ -411,12 +409,7 @@ cmdNetworkDesc(vshControl *ctl, const vshCmd *cmd)
     else
         type = VIR_NETWORK_METADATA_DESCRIPTION;
 
-    while ((opt = vshCommandOptArgv(ctl, cmd, opt)))
-        virBufferAsprintf(&buf, "%s ", opt->data);
-
-    virBufferTrim(&buf, " ");
-
-    descArg = virBufferContentAndReset(&buf);
+    descArg = g_strdup(vshCommandOptArgvString(cmd, "new-desc"));
 
     if (edit || descArg) {
         g_autofree char *descNet = NULL;
index 415a390786048183aec53247a2c91a3001182537..8b6a950a0186d7944a8619978e08cae14582e228 100644 (file)
@@ -379,7 +379,7 @@ cmdSnapshotCreateAs(vshControl *ctl, const vshCmd *cmd)
     const char *memspec = NULL;
     g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
     unsigned int flags = 0;
-    const vshCmdOpt *opt = NULL;
+    const char **diskspec;
 
     if (vshCommandOptBool(cmd, "no-metadata"))
         flags |= VIR_DOMAIN_SNAPSHOT_CREATE_NO_METADATA;
@@ -416,11 +416,12 @@ cmdSnapshotCreateAs(vshControl *ctl, const vshCmd *cmd)
     if (memspec && virshParseSnapshotMemspec(ctl, &buf, memspec) < 0)
         return false;
 
-    if (vshCommandOptBool(cmd, "diskspec")) {
+    if ((diskspec = vshCommandOptArgv(cmd, "diskspec"))) {
         virBufferAddLit(&buf, "<disks>\n");
         virBufferAdjustIndent(&buf, 2);
-        while ((opt = vshCommandOptArgv(ctl, cmd, opt))) {
-            if (virshParseSnapshotDiskspec(ctl, &buf, opt->data) < 0)
+
+        for (; *diskspec; diskspec++) {
+            if (virshParseSnapshotDiskspec(ctl, &buf, *diskspec) < 0)
                 return false;
         }
         virBufferAdjustIndent(&buf, -2);
index 69948a36a5a46b64ca406384a8f77cf6ab182d78..91d01cc73f84afbf9daf4cefdb7b5e33993967d7 100644 (file)
@@ -779,6 +779,9 @@ vshCommandOptFree(vshCmdOpt * arg)
         a = a->next;
 
         g_free(tmp->data);
+        /* 'argv' doesn't own the strings themselves */
+        g_free(tmp->argv);
+        g_free(tmp->argvstr);
         g_free(tmp);
     }
 }
@@ -1226,30 +1229,80 @@ vshCommandOptBool(const vshCmd *cmd, const char *name)
     return vshCommandOpt(cmd, name, &dummy, false) == 1;
 }
 
+
+static vshCmdOpt *
+vshCommandOptArgvInternal(const vshCmd *cmd,
+                          const char *name)
+{
+    vshCmdOpt *first = NULL;
+    vshCmdOpt *opt;
+    size_t nargv = 0;
+    size_t nargv_alloc = 0;
+
+    for (opt = cmd->opts; opt; opt = opt->next) {
+        if (STRNEQ(opt->def->name, name))
+            continue;
+
+        if (!first) {
+            /* Return existing data if we'we already processed it */
+            if (opt->argv)
+                return opt;
+
+            first = opt;
+        }
+
+        /* extra NULL terminator */
+        VIR_RESIZE_N(first->argv, nargv_alloc, nargv, 2);
+        first->argv[nargv++] = opt->data;
+    }
+
+    if (first)
+        first->argvstr = g_strjoinv(" ", (GStrv) first->argv);
+
+    return first;
+}
+
+
 /**
  * vshCommandOptArgv:
- * @ctl virtshell control structure
- * @cmd command reference
- * @opt starting point for the search
+ * @cmd: command reference
+ * @name: name of argument
  *
- * Returns the next argv argument after OPT (or the first one if OPT
- * is NULL), or NULL if no more are present.
+ * Returns a NULL terminated list of strings of values passed as argument of
+ * ARGV argument named @name. The returned string list is owned by @cmd and
+ * caller must not free or modify it.
+ */
+const char **
+vshCommandOptArgv(const vshCmd *cmd,
+                  const char *name)
+{
+    vshCmdOpt *opt = vshCommandOptArgvInternal(cmd, name);
+
+    if (!opt)
+        return NULL;
+
+    return opt->argv;
+}
+
+
+/**
+ * vshCommandOptArgvString:
+ * @cmd: command reference
+ * @name: name of argument
  *
- * Requires that a VSH_OT_ARGV option be last in the
- * list of supported options in CMD->def->opts.
+ * Returns a string containing all values passed as ARGV argument @name
+ * delimited/concatenated by adding spaces.
  */
-const vshCmdOpt *
-vshCommandOptArgv(vshControl *ctl G_GNUC_UNUSED, const vshCmd *cmd,
-                  const vshCmdOpt *opt)
+const char *
+vshCommandOptArgvString(const vshCmd *cmd,
+                        const char *name)
 {
-    opt = opt ? opt->next : cmd->opts;
+    vshCmdOpt *opt = vshCommandOptArgvInternal(cmd, name);
 
-    while (opt) {
-        if (opt->def->type == VSH_OT_ARGV)
-            return opt;
-        opt = opt->next;
-    }
-    return NULL;
+    if (!opt)
+        return NULL;
+
+    return opt->argvstr;
 }
 
 
@@ -3279,9 +3332,9 @@ cmdEcho(vshControl *ctl, const vshCmd *cmd)
     bool err = vshCommandOptBool(cmd, "err");
     bool split = vshCommandOptBool(cmd, "split");
     const char *prefix;
-    const vshCmdOpt *opt = NULL;
     g_autofree char *arg = NULL;
     g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
+    const char **o;
 
     VSH_EXCLUSIVE_OPTIONS_VAR(shell, xml);
     VSH_EXCLUSIVE_OPTIONS_VAR(shell, split);
@@ -3292,8 +3345,8 @@ cmdEcho(vshControl *ctl, const vshCmd *cmd)
     if (prefix)
         virBufferAsprintf(&buf, "%s ", prefix);
 
-    while ((opt = vshCommandOptArgv(ctl, cmd, opt))) {
-        const char *curr = opt->data;
+    for (o = vshCommandOptArgv(cmd, "string"); o && *o; o++) {
+        const char *curr = *o;
 
         if (xml) {
             virBufferEscapeString(&buf, "%s", curr);
@@ -3435,14 +3488,14 @@ bool
 cmdComplete(vshControl *ctl, const vshCmd *cmd)
 {
     const vshClientHooks *hooks = ctl->hooks;
-    const char *arg = "";
-    const vshCmdOpt *opt = NULL;
+    const char *lastArg = NULL;
+    const char **args = NULL;
     g_auto(GStrv) matches = NULL;
     char **iter;
-    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
 
-    if (vshCommandOptStringQuiet(ctl, cmd, "string", &arg) <= 0)
-        return false;
+    /* The completer needs also the last component */
+    for (args = vshCommandOptArgv(cmd, "string"); args && *args; args++)
+        lastArg = *args;
 
     /* This command is flagged VSH_CMD_FLAG_NOCONNECT because we
      * need to prevent auth hooks reading any input. Therefore, we
@@ -3455,23 +3508,16 @@ cmdComplete(vshControl *ctl, const vshCmd *cmd)
     if (!(hooks && hooks->connHandler && hooks->connHandler(ctl)))
         return false;
 
-    while ((opt = vshCommandOptArgv(ctl, cmd, opt))) {
-        if (virBufferUse(&buf) != 0)
-            virBufferAddChar(&buf, ' ');
-        virBufferAddStr(&buf, opt->data);
-        arg = opt->data;
-    }
-
     vshReadlineInit(ctl);
 
-    if (!(rl_line_buffer = virBufferContentAndReset(&buf)))
+    if (!(rl_line_buffer = g_strdup(vshCommandOptArgvString(cmd, "string"))))
         rl_line_buffer = g_strdup("");
 
     /* rl_point is current cursor position in rl_line_buffer.
      * In our case it's at the end of the whole line. */
     rl_point = strlen(rl_line_buffer);
 
-    matches = vshReadlineCompletion(arg, 0, 0);
+    matches = vshReadlineCompletion(lastArg, 0, 0);
     g_clear_pointer(&rl_line_buffer, g_free);
 
     if (!matches)
index fa3f1406e78e70cd36241d07f24bf84913b4c81a..04d7e163f1e4cace20b6937dbf35ababe57d41fc 100644 (file)
@@ -147,6 +147,8 @@ struct _vshCmdOptDef {
 struct _vshCmdOpt {
     const vshCmdOptDef *def;    /* non-NULL pointer to option definition */
     char *data;                 /* allocated data, or NULL for bool option */
+    const char **argv;          /* for VSH_OT_ARGV, the list of options */
+    char *argvstr;              /* space-joined @argv */
     bool completeThis;          /* true if this is the option user's wishing to
                                    autocomplete */
     vshCmdOpt *next;
@@ -292,8 +294,12 @@ bool vshCommandRun(vshControl *ctl, const vshCmd *cmd);
 bool vshCommandStringParse(vshControl *ctl, char *cmdstr,
                            vshCmd **partial, size_t point);
 
-const vshCmdOpt *vshCommandOptArgv(vshControl *ctl, const vshCmd *cmd,
-                                   const vshCmdOpt *opt);
+const char **
+vshCommandOptArgv(const vshCmd *cmd,
+                  const char *name);
+const char *
+vshCommandOptArgvString(const vshCmd *cmd,
+                        const char *name);
 bool vshCommandArgvParse(vshControl *ctl, int nargs, char **argv);
 int vshCommandOptTimeoutToMs(vshControl *ctl, const vshCmd *cmd, int *timeout);