diff --git a/tools/virsh-checkpoint.c b/tools/virsh-checkpoint.c
index fea6b4fb4b..972b2f979c 100644
--- a/tools/virsh-checkpoint.c
+++ b/tools/virsh-checkpoint.c
@@ -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, "%s\n", name);
virBufferEscapeString(&buf, "%s\n", desc);
- if (vshCommandOptBool(cmd, "diskspec")) {
+ if ((diskspec = vshCommandOptArgv(cmd, "diskspec"))) {
virBufferAddLit(&buf, "\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, "\n");
}
diff --git a/tools/virsh-domain-monitor.c b/tools/virsh-domain-monitor.c
index 700f3ae094..ef07ace577 100644
--- a/tools/virsh-domain-monitor.c
+++ b/tools/virsh-domain-monitor.c
@@ -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;
diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c
index 28d90377a1..50e80689a2 100644
--- a/tools/virsh-domain.c
+++ b/tools/virsh-domain.c
@@ -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"));
diff --git a/tools/virsh-network.c b/tools/virsh-network.c
index 24049a66f3..6fcc7fd8ee 100644
--- a/tools/virsh-network.c
+++ b/tools/virsh-network.c
@@ -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;
diff --git a/tools/virsh-snapshot.c b/tools/virsh-snapshot.c
index 415a390786..8b6a950a01 100644
--- a/tools/virsh-snapshot.c
+++ b/tools/virsh-snapshot.c
@@ -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, "\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);
diff --git a/tools/vsh.c b/tools/vsh.c
index 69948a36a5..91d01cc73f 100644
--- a/tools/vsh.c
+++ b/tools/vsh.c
@@ -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.
- *
- * Requires that a VSH_OT_ARGV option be last in the
- * list of supported options in CMD->def->opts.
+ * 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 vshCmdOpt *
-vshCommandOptArgv(vshControl *ctl G_GNUC_UNUSED, const vshCmd *cmd,
- const vshCmdOpt *opt)
+const char **
+vshCommandOptArgv(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->argv;
+}
+
+
+/**
+ * vshCommandOptArgvString:
+ * @cmd: command reference
+ * @name: name of argument
+ *
+ * Returns a string containing all values passed as ARGV argument @name
+ * delimited/concatenated by adding spaces.
+ */
+const char *
+vshCommandOptArgvString(const vshCmd *cmd,
+ const char *name)
+{
+ vshCmdOpt *opt = vshCommandOptArgvInternal(cmd, name);
+
+ 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)
diff --git a/tools/vsh.h b/tools/vsh.h
index fa3f1406e7..04d7e163f1 100644
--- a/tools/vsh.h
+++ b/tools/vsh.h
@@ -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);