提交 2b172a8e 编写于 作者: E Eric Blake

virsh: allow alias to expand to opt=value pair

We want to treat 'attach-disk --shareable' as an undocumented
alias for 'attach-disk --mode=shareable'.  By improving our
alias handling, we can allow all such --bool -> --opt=value
replacements, and guarantee up front that the alias is not
mixed with its replacement.

* tools/virsh.c (vshCmddefOptParse, vshCmddefGetOption): Add
support for expanding bool alias to --opt=value.
(opts_echo): Add another alias to test it.
* tests/virshtest.c (mymain): Test it.
Signed-off-by: NEric Blake <eblake@redhat.com>
上级 394d6e0a
...@@ -393,6 +393,7 @@ mymain(void) ...@@ -393,6 +393,7 @@ mymain(void)
DO_TEST(32, "hello\n", "echo --string hello"); DO_TEST(32, "hello\n", "echo --string hello");
DO_TEST(33, "hello\n", "echo", "--str", "hello"); DO_TEST(33, "hello\n", "echo", "--str", "hello");
DO_TEST(34, "hello\n", "echo --str hello"); DO_TEST(34, "hello\n", "echo --str hello");
DO_TEST(35, "hello\n", "echo --hi");
# undef DO_TEST # undef DO_TEST
......
...@@ -885,6 +885,10 @@ static const vshCmdOptDef opts_echo[] = { ...@@ -885,6 +885,10 @@ static const vshCmdOptDef opts_echo[] = {
.type = VSH_OT_ALIAS, .type = VSH_OT_ALIAS,
.help = "string" .help = "string"
}, },
{.name = "hi",
.type = VSH_OT_ALIAS,
.help = "string=hello"
},
{.name = "string", {.name = "string",
.type = VSH_OT_ARGV, .type = VSH_OT_ARGV,
.help = N_("arguments to echo") .help = N_("arguments to echo")
...@@ -1011,12 +1015,25 @@ vshCmddefOptParse(const vshCmdDef *cmd, uint32_t *opts_need_arg, ...@@ -1011,12 +1015,25 @@ vshCmddefOptParse(const vshCmdDef *cmd, uint32_t *opts_need_arg,
} }
if (opt->type == VSH_OT_ALIAS) { if (opt->type == VSH_OT_ALIAS) {
size_t j; size_t j;
char *name = (char *)opt->help; /* cast away const */
char *p;
if (opt->flags || !opt->help) if (opt->flags || !opt->help)
return -1; /* alias options are tracked by the original name */ return -1; /* alias options are tracked by the original name */
if ((p = strchr(name, '=')) &&
VIR_STRNDUP(name, name, p - name) < 0)
return -1;
for (j = i + 1; cmd->opts[j].name; j++) { for (j = i + 1; cmd->opts[j].name; j++) {
if (STREQ(opt->help, cmd->opts[j].name)) if (STREQ(name, cmd->opts[j].name) &&
cmd->opts[j].type != VSH_OT_ALIAS)
break; break;
} }
if (name != opt->help) {
VIR_FREE(name);
/* If alias comes with value, replacement must not be bool */
if (cmd->opts[j].type == VSH_OT_BOOL)
return -1;
}
if (!cmd->opts[j].name) if (!cmd->opts[j].name)
return -1; /* alias option must map to a later option name */ return -1; /* alias option must map to a later option name */
continue; continue;
...@@ -1049,9 +1066,11 @@ static vshCmdOptDef helpopt = { ...@@ -1049,9 +1066,11 @@ static vshCmdOptDef helpopt = {
}; };
static const vshCmdOptDef * static const vshCmdOptDef *
vshCmddefGetOption(vshControl *ctl, const vshCmdDef *cmd, const char *name, vshCmddefGetOption(vshControl *ctl, const vshCmdDef *cmd, const char *name,
uint32_t *opts_seen, int *opt_index) uint32_t *opts_seen, int *opt_index, char **optstr)
{ {
size_t i; size_t i;
const vshCmdOptDef *ret = NULL;
char *alias = NULL;
if (STREQ(name, helpopt.name)) { if (STREQ(name, helpopt.name)) {
return &helpopt; return &helpopt;
...@@ -1062,16 +1081,36 @@ vshCmddefGetOption(vshControl *ctl, const vshCmdDef *cmd, const char *name, ...@@ -1062,16 +1081,36 @@ vshCmddefGetOption(vshControl *ctl, const vshCmdDef *cmd, const char *name,
if (STREQ(opt->name, name)) { if (STREQ(opt->name, name)) {
if (opt->type == VSH_OT_ALIAS) { if (opt->type == VSH_OT_ALIAS) {
name = opt->help; char *value;
/* Two types of replacements:
opt->help = "string": straight replacement of name
opt->help = "string=value": treat boolean flag as
alias of option and its default value */
sa_assert(!alias);
if (VIR_STRDUP(alias, opt->help) < 0)
goto cleanup;
name = alias;
if ((value = strchr(name, '='))) {
*value = '\0';
if (*optstr) {
vshError(ctl, _("invalid '=' after option --%s"),
opt->name);
goto cleanup;
}
if (VIR_STRDUP(*optstr, value + 1) < 0)
goto cleanup;
}
continue; continue;
} }
if ((*opts_seen & (1 << i)) && opt->type != VSH_OT_ARGV) { if ((*opts_seen & (1 << i)) && opt->type != VSH_OT_ARGV) {
vshError(ctl, _("option --%s already seen"), name); vshError(ctl, _("option --%s already seen"), name);
return NULL; goto cleanup;
} }
*opts_seen |= 1 << i; *opts_seen |= 1 << i;
*opt_index = i; *opt_index = i;
return opt; ret = opt;
goto cleanup;
} }
} }
...@@ -1079,7 +1118,9 @@ vshCmddefGetOption(vshControl *ctl, const vshCmdDef *cmd, const char *name, ...@@ -1079,7 +1118,9 @@ vshCmddefGetOption(vshControl *ctl, const vshCmdDef *cmd, const char *name,
vshError(ctl, _("command '%s' doesn't support option --%s"), vshError(ctl, _("command '%s' doesn't support option --%s"),
cmd->name, name); cmd->name, name);
} }
return NULL; cleanup:
VIR_FREE(alias);
return ret;
} }
static const vshCmdOptDef * static const vshCmdOptDef *
...@@ -1845,7 +1886,8 @@ vshCommandParse(vshControl *ctl, vshCommandParser *parser) ...@@ -1845,7 +1886,8 @@ vshCommandParse(vshControl *ctl, vshCommandParser *parser)
} }
/* Special case 'help' to ignore all spurious options */ /* Special case 'help' to ignore all spurious options */
if (!(opt = vshCmddefGetOption(ctl, cmd, tkdata + 2, if (!(opt = vshCmddefGetOption(ctl, cmd, tkdata + 2,
&opts_seen, &opt_index))) { &opts_seen, &opt_index,
&optstr))) {
VIR_FREE(optstr); VIR_FREE(optstr);
if (STREQ(cmd->name, "help")) if (STREQ(cmd->name, "help"))
continue; continue;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册