virsh: add option aliases

In the past, we have created some virsh options with less-than-stellar
names.  For back-compat reasons, those names must continue to parse,
but we don't want to document them in help output.  This introduces
a new option type, an alias, which points to a canonical option name
later in the option list.

I'm actually quite impressed that our code has already been factored
to do all option parsing through common entry points, such that I
got this added in relatively few lines of code!

* tools/virsh.c (VSH_OT_ALIAS): New option type.
(opts_echo): Hook up an alias, for easy testing.
(vshCmddefOptParse, vshCmddefHelp, vshCmddefGetOption): Allow for
aliases.
* tools/virsh.pod (NOTES): Document promise of back-compat.
* tests/virshtest.c (mymain): Test new feature.
This commit is contained in:
Eric Blake 2012-03-02 11:01:15 -07:00
parent 2e22f23bde
commit 1c56b9fe53
3 changed files with 38 additions and 2 deletions

View File

@ -386,6 +386,12 @@ mymain(void)
DO_TEST(30, "--shell a\n", DO_TEST(30, "--shell a\n",
"echo \t '-'\"-\" \t --shell \t a"); "echo \t '-'\"-\" \t --shell \t a");
/* Tests of alias handling. */
DO_TEST(31, "hello\n", "echo", "--string", "hello");
DO_TEST(32, "hello\n", "echo --string hello");
DO_TEST(33, "hello\n", "echo", "--str", "hello");
DO_TEST(34, "hello\n", "echo --str hello");
# undef DO_TEST # undef DO_TEST
VIR_FREE(custom_uri); VIR_FREE(custom_uri);

View File

@ -138,7 +138,8 @@ typedef enum {
VSH_OT_STRING, /* optional string option */ VSH_OT_STRING, /* optional string option */
VSH_OT_INT, /* optional or mandatory int option */ VSH_OT_INT, /* optional or mandatory int option */
VSH_OT_DATA, /* string data (as non-option) */ VSH_OT_DATA, /* string data (as non-option) */
VSH_OT_ARGV /* remaining arguments */ VSH_OT_ARGV, /* remaining arguments */
VSH_OT_ALIAS, /* alternate spelling for a later argument */
} vshCmdOptType; } vshCmdOptType;
/* /*
@ -190,7 +191,8 @@ typedef struct {
const char *name; /* the name of option, or NULL for list end */ const char *name; /* the name of option, or NULL for list end */
vshCmdOptType type; /* option type */ vshCmdOptType type; /* option type */
unsigned int flags; /* flags */ unsigned int flags; /* flags */
const char *help; /* non-NULL help string */ const char *help; /* non-NULL help string; or for VSH_OT_ALIAS
* the name of a later public option */
} vshCmdOptDef; } vshCmdOptDef;
/* /*
@ -15363,6 +15365,7 @@ static const vshCmdInfo info_echo[] = {
static const vshCmdOptDef opts_echo[] = { static const vshCmdOptDef opts_echo[] = {
{"shell", VSH_OT_BOOL, 0, N_("escape for shell use")}, {"shell", VSH_OT_BOOL, 0, N_("escape for shell use")},
{"xml", VSH_OT_BOOL, 0, N_("escape for XML use")}, {"xml", VSH_OT_BOOL, 0, N_("escape for XML use")},
{"str", VSH_OT_ALIAS, 0, "string"},
{"string", VSH_OT_ARGV, 0, N_("arguments to echo")}, {"string", VSH_OT_ARGV, 0, N_("arguments to echo")},
{NULL, 0, 0, NULL} {NULL, 0, 0, NULL}
}; };
@ -17411,6 +17414,18 @@ vshCmddefOptParse(const vshCmdDef *cmd, uint32_t *opts_need_arg,
return -1; /* bool options can't be mandatory */ return -1; /* bool options can't be mandatory */
continue; continue;
} }
if (opt->type == VSH_OT_ALIAS) {
int j;
if (opt->flags || !opt->help)
return -1; /* alias options are tracked by the original name */
for (j = i + 1; cmd->opts[j].name; j++) {
if (STREQ(opt->help, cmd->opts[j].name))
break;
}
if (!cmd->opts[j].name)
return -1; /* alias option must map to a later option name */
continue;
}
if (opt->flags & VSH_OFLAG_REQ_OPT) { if (opt->flags & VSH_OFLAG_REQ_OPT) {
if (opt->flags & VSH_OFLAG_REQ) if (opt->flags & VSH_OFLAG_REQ)
*opts_required |= 1 << i; *opts_required |= 1 << i;
@ -17442,6 +17457,10 @@ vshCmddefGetOption(vshControl *ctl, const vshCmdDef *cmd, const char *name,
const vshCmdOptDef *opt = &cmd->opts[i]; const vshCmdOptDef *opt = &cmd->opts[i];
if (STREQ(opt->name, name)) { if (STREQ(opt->name, name)) {
if (opt->type == VSH_OT_ALIAS) {
name = opt->help;
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; return NULL;
@ -17620,6 +17639,9 @@ vshCmddefHelp(vshControl *ctl, const char *cmdname)
: _("[<%s>]..."); : _("[<%s>]...");
} }
break; break;
case VSH_OT_ALIAS:
/* aliases are intentionally undocumented */
continue;
default: default:
assert(0); assert(0);
} }
@ -17661,6 +17683,8 @@ vshCmddefHelp(vshControl *ctl, const char *cmdname)
shortopt ? _("[--%s] <string>") : _("<%s>"), shortopt ? _("[--%s] <string>") : _("<%s>"),
opt->name); opt->name);
break; break;
case VSH_OT_ALIAS:
continue;
default: default:
assert(0); assert(0);
} }

View File

@ -115,6 +115,12 @@ program returned, may not mean the action is complete and you
must poll periodically to detect that the guest completed the must poll periodically to detect that the guest completed the
operation. operation.
B<virsh> strives for backward compatibility. Although the B<help>
command only lists the preferred usage of a command, if an older
version of B<virsh> supported an alternate spelling of a command or
option (such as I<--tunnelled> instead of I<--tunneled>), then
scripts using that older spelling will continue to work.
=head1 GENERIC COMMANDS =head1 GENERIC COMMANDS
The following commands are generic i.e. not specific to a domain. The following commands are generic i.e. not specific to a domain.