提交 ca92c857 编写于 作者: E Eric Blake

virsh: improve option handling

The documentation for vshCommandOptString claims that it returns
-1 on a missing required argument, but in reality, that error
message was unreachable (it was buried inside an if clause that
is true only if the argument was present).  The code was so hairy
that I decided a rewrite would make it easier to understand,
and actually return the error values we want.

Meanwhile, our construction guarantees that all vshCmdOpt have
a non-null def member, so there are some redundant checks that
can be trimmed.

* tools/virsh.c (vshCommandOpt): Alter signature.
(vshCommandOptInt, vshCommandOptUInt, vshCommandOptUL)
(vshCommandOptString, vshCommandOptLongLong)
(vshCommandOptULongLong, vshCommandOptBool): Adjust all callers.
(vshCommandOptArgv): Remove dead condition.
上级 20135c70
...@@ -162,29 +162,36 @@ typedef struct __vshControl vshControl; ...@@ -162,29 +162,36 @@ typedef struct __vshControl vshControl;
typedef struct __vshCmd vshCmd; typedef struct __vshCmd vshCmd;
/* /*
* vshCmdInfo -- information about command * vshCmdInfo -- name/value pair for information about command
*
* Commands should have at least the following names:
* "name" - command name
* "desc" - description of command, or empty string
*/ */
typedef struct { typedef struct {
const char *name; /* name of information */ const char *name; /* name of information, or NULL for list end */
const char *data; /* information */ const char *data; /* non-NULL information */
} vshCmdInfo; } vshCmdInfo;
/* /*
* vshCmdOptDef - command option definition * vshCmdOptDef - command option definition
*/ */
typedef struct { typedef struct {
const char *name; /* the name of option */ const char *name; /* the name of option, or NULL for list end */
vshCmdOptType type; /* option type */ vshCmdOptType type; /* option type */
int flag; /* flags */ int flag; /* flags */
const char *help; /* help string */ const char *help; /* non-NULL help string */
} vshCmdOptDef; } vshCmdOptDef;
/* /*
* vshCmdOpt - command options * vshCmdOpt - command options
*
* After parsing a command, all arguments to the command have been
* collected into a list of these objects.
*/ */
typedef struct vshCmdOpt { typedef struct vshCmdOpt {
const vshCmdOptDef *def; /* pointer to relevant option */ const vshCmdOptDef *def; /* non-NULL pointer to option definition */
char *data; /* allocated data */ char *data; /* allocated data, or NULL for bool option */
struct vshCmdOpt *next; struct vshCmdOpt *next;
} vshCmdOpt; } vshCmdOpt;
...@@ -199,7 +206,7 @@ enum { ...@@ -199,7 +206,7 @@ enum {
* vshCmdDef - command definition * vshCmdDef - command definition
*/ */
typedef struct { typedef struct {
const char *name; const char *name; /* name of command, or NULL for list end */
bool (*handler) (vshControl *, const vshCmd *); /* command handler */ bool (*handler) (vshControl *, const vshCmd *); /* command handler */
const vshCmdOptDef *opts; /* definition of command options */ const vshCmdOptDef *opts; /* definition of command options */
const vshCmdInfo *info; /* details about command */ const vshCmdInfo *info; /* details about command */
...@@ -239,7 +246,7 @@ typedef struct __vshControl { ...@@ -239,7 +246,7 @@ typedef struct __vshControl {
} __vshControl; } __vshControl;
typedef struct vshCmdGrp { typedef struct vshCmdGrp {
const char *name; const char *name; /* name of group, or NULL for list end */
const char *keyword; /* help keyword */ const char *keyword; /* help keyword */
const vshCmdDef *commands; const vshCmdDef *commands;
} vshCmdGrp; } vshCmdGrp;
...@@ -264,7 +271,9 @@ static bool vshCmddefHelp(vshControl *ctl, const char *name); ...@@ -264,7 +271,9 @@ static bool vshCmddefHelp(vshControl *ctl, const char *name);
static const vshCmdGrp *vshCmdGrpSearch(const char *grpname); static const vshCmdGrp *vshCmdGrpSearch(const char *grpname);
static bool vshCmdGrpHelp(vshControl *ctl, const char *name); static bool vshCmdGrpHelp(vshControl *ctl, const char *name);
static vshCmdOpt *vshCommandOpt(const vshCmd *cmd, const char *name); static int vshCommandOpt(const vshCmd *cmd, const char *name, vshCmdOpt **opt)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3)
ATTRIBUTE_RETURN_CHECK;
static int vshCommandOptInt(const vshCmd *cmd, const char *name, int *value) static int vshCommandOptInt(const vshCmd *cmd, const char *name, int *value)
ATTRIBUTE_NONNULL(3) ATTRIBUTE_RETURN_CHECK; ATTRIBUTE_NONNULL(3) ATTRIBUTE_RETURN_CHECK;
static int vshCommandOptUInt(const vshCmd *cmd, const char *name, static int vshCommandOptUInt(const vshCmd *cmd, const char *name,
...@@ -12506,23 +12515,48 @@ vshCommandFree(vshCmd *cmd) ...@@ -12506,23 +12515,48 @@ vshCommandFree(vshCmd *cmd)
} }
} }
/* /**
* Returns option by name * vshCommandOpt:
* @cmd: parsed command line to search
* @name: option name to search for
* @opt: result of the search
*
* Look up an option passed to CMD by NAME. Returns 1 with *OPT set
* to the option if found, 0 with *OPT set to NULL if the name is
* valid and the option is not required, -1 with *OPT set to NULL if
* the option is required but not present, and -2 if NAME is not valid
* (-2 indicates a programming error). No error messages are issued.
*/ */
static vshCmdOpt * static int
vshCommandOpt(const vshCmd *cmd, const char *name) vshCommandOpt(const vshCmd *cmd, const char *name, vshCmdOpt **opt)
{ {
vshCmdOpt *opt = cmd->opts; vshCmdOpt *candidate = cmd->opts;
const vshCmdOptDef *valid = cmd->def->opts;
while (opt) { /* See if option is present on command line. */
if (opt->def && STREQ(opt->def->name, name)) while (candidate) {
return opt; if (STREQ(candidate->def->name, name)) {
opt = opt->next; *opt = candidate;
return 1;
}
candidate = candidate->next;
} }
return NULL;
/* Option not present, see if command requires it. */
*opt = NULL;
while (valid) {
if (!valid->name)
break;
if (STREQ(name, valid->name))
return (valid->flag & VSH_OFLAG_REQ) == 0 ? 0 : -1;
valid++;
}
/* If we got here, the name is unknown. */
return -2;
} }
/* /**
* vshCommandOptInt:
* @cmd command reference * @cmd command reference
* @name option name * @name option name
* @value result * @value result
...@@ -12530,102 +12564,144 @@ vshCommandOpt(const vshCmd *cmd, const char *name) ...@@ -12530,102 +12564,144 @@ vshCommandOpt(const vshCmd *cmd, const char *name)
* Convert option to int * Convert option to int
* Return value: * Return value:
* >0 if option found and valid (@value updated) * >0 if option found and valid (@value updated)
* 0 in case option not found (@value untouched) * 0 if option not found and not required (@value untouched)
* <0 in all other cases (@value untouched) * <0 in all other cases (@value untouched)
*/ */
static int static int
vshCommandOptInt(const vshCmd *cmd, const char *name, int *value) vshCommandOptInt(const vshCmd *cmd, const char *name, int *value)
{ {
vshCmdOpt *arg = vshCommandOpt(cmd, name); vshCmdOpt *arg;
int ret = 0, num; int ret;
int num;
char *end_p = NULL; char *end_p = NULL;
if ((arg != NULL) && (arg->data != NULL)) { ret = vshCommandOpt(cmd, name, &arg);
num = strtol(arg->data, &end_p, 10); if (ret <= 0)
ret = -1; return ret;
if ((arg->data != end_p) && (*end_p == 0)) { if (!arg->data) {
*value = num; /* only possible on bool, but if name is bool, this is a
ret = 1; * programming bug */
} return -2;
} }
return ret;
num = strtol(arg->data, &end_p, 10);
if ((arg->data != end_p) && (*end_p == 0)) {
*value = num;
return 1;
}
return -1;
} }
/* /**
* vshCommandOptUInt:
* @cmd command reference
* @name option name
* @value result
*
* Convert option to unsigned int * Convert option to unsigned int
* See vshCommandOptInt() * See vshCommandOptInt()
*/ */
static int static int
vshCommandOptUInt(const vshCmd *cmd, const char *name, unsigned int *value) vshCommandOptUInt(const vshCmd *cmd, const char *name, unsigned int *value)
{ {
vshCmdOpt *arg = vshCommandOpt(cmd, name); vshCmdOpt *arg;
unsigned int ret = 0, num; int ret;
unsigned int num;
char *end_p = NULL; char *end_p = NULL;
if ((arg != NULL) && (arg->data != NULL)) { ret = vshCommandOpt(cmd, name, &arg);
num = strtoul(arg->data, &end_p, 10); if (ret <= 0)
ret = -1; return ret;
if ((arg->data != end_p) && (*end_p == 0)) { if (!arg->data) {
*value = num; /* only possible on bool, but if name is bool, this is a
ret = 1; * programming bug */
} return -2;
} }
return ret;
num = strtoul(arg->data, &end_p, 10);
if ((arg->data != end_p) && (*end_p == 0)) {
*value = num;
return 1;
}
return -1;
} }
/* /*
* vshCommandOptUL:
* @cmd command reference
* @name option name
* @value result
*
* Convert option to unsigned long * Convert option to unsigned long
* See vshCommandOptInt() * See vshCommandOptInt()
*/ */
static int static int
vshCommandOptUL(const vshCmd *cmd, const char *name, unsigned long *value) vshCommandOptUL(const vshCmd *cmd, const char *name, unsigned long *value)
{ {
vshCmdOpt *arg = vshCommandOpt(cmd, name); vshCmdOpt *arg;
int ret = 0; int ret;
unsigned long num; unsigned long num;
char *end_p = NULL; char *end_p = NULL;
if ((arg != NULL) && (arg->data != NULL)) { ret = vshCommandOpt(cmd, name, &arg);
num = strtoul(arg->data, &end_p, 10); if (ret <= 0)
ret = -1; return ret;
if ((arg->data != end_p) && (*end_p == 0)) { if (!arg->data) {
*value = num; /* only possible on bool, but if name is bool, this is a
ret = 1; * programming bug */
} return -2;
} }
return ret;
num = strtoul(arg->data, &end_p, 10);
if ((arg->data != end_p) && (*end_p == 0)) {
*value = num;
return 1;
}
return -1;
} }
/* /**
* vshCommandOptString:
* @cmd command reference
* @name option name
* @value result
*
* Returns option as STRING * Returns option as STRING
* See vshCommandOptInt() * Return value:
* >0 if option found and valid (@value updated)
* 0 if option not found and not required (@value untouched)
* <0 in all other cases (@value untouched)
*/ */
static int static int
vshCommandOptString(const vshCmd *cmd, const char *name, const char **value) vshCommandOptString(const vshCmd *cmd, const char *name, const char **value)
{ {
vshCmdOpt *arg = vshCommandOpt(cmd, name); vshCmdOpt *arg;
int ret = 0; int ret;
if (arg && arg->data) { ret = vshCommandOpt(cmd, name, &arg);
if (*arg->data if (ret <= 0)
|| (arg->def && (arg->def->flag & VSH_OFLAG_EMPTY_OK))) { return ret;
*value = arg->data; if (!arg->data) {
ret = 1; /* only possible on bool, but if name is bool, this is a
} else if (arg->def && ((arg->def->flag) & VSH_OFLAG_REQ)) { * programming bug */
vshError(NULL, _("Missing required option '%s'"), name); return -2;
ret = -1;
} else {
/* Treat "--option ''" as if option had not been specified. */
ret = 0;
}
} }
return ret; if (!*arg->data && !(arg->def->flag & VSH_OFLAG_EMPTY_OK)) {
return -1;
}
*value = arg->data;
return 1;
} }
/* /**
* vshCommandOptLongLong:
* @cmd command reference
* @name option name
* @value result
*
* Returns option as long long * Returns option as long long
* See vshCommandOptInt() * See vshCommandOptInt()
*/ */
...@@ -12633,53 +12709,87 @@ static int ...@@ -12633,53 +12709,87 @@ static int
vshCommandOptLongLong(const vshCmd *cmd, const char *name, vshCommandOptLongLong(const vshCmd *cmd, const char *name,
long long *value) long long *value)
{ {
vshCmdOpt *arg = vshCommandOpt(cmd, name); vshCmdOpt *arg;
int ret = 0; int ret;
long long num; long long num;
char *end_p = NULL; char *end_p = NULL;
if ((arg != NULL) && (arg->data != NULL)) { ret = vshCommandOpt(cmd, name, &arg);
num = strtoll(arg->data, &end_p, 10); if (ret <= 0)
ret = -1; return ret;
if ((arg->data != end_p) && (*end_p == 0)) { if (!arg->data) {
*value = num; /* only possible on bool, but if name is bool, this is a
ret = 1; * programming bug */
} return -2;
} }
return ret;
num = strtoll(arg->data, &end_p, 10);
if ((arg->data != end_p) && (*end_p == 0)) {
*value = num;
return 1;
}
return -1;
} }
/**
* vshCommandOptULongLong:
* @cmd command reference
* @name option name
* @value result
*
* Returns option as long long
* See vshCommandOptInt()
*/
static int static int
vshCommandOptULongLong(const vshCmd *cmd, const char *name, vshCommandOptULongLong(const vshCmd *cmd, const char *name,
unsigned long long *value) unsigned long long *value)
{ {
vshCmdOpt *arg = vshCommandOpt(cmd, name); vshCmdOpt *arg;
int ret = 0; int ret;
unsigned long long num; unsigned long long num;
char *end_p = NULL; char *end_p = NULL;
if ((arg != NULL) && (arg->data != NULL)) { ret = vshCommandOpt(cmd, name, &arg);
num = strtoull(arg->data, &end_p, 10); if (ret <= 0)
ret = -1; return ret;
if ((arg->data != end_p) && (*end_p == 0)) { if (!arg->data) {
*value = num; /* only possible on bool, but if name is bool, this is a
ret = 1; * programming bug */
} return -2;
} }
return ret;
num = strtoull(arg->data, &end_p, 10);
if ((arg->data != end_p) && (*end_p == 0)) {
*value = num;
return 1;
}
return -1;
} }
/* /**
* Returns true/false if the option exists * vshCommandOptBool:
* @cmd command reference
* @name option name
*
* Returns true/false if the option exists. Note that this does NOT
* validate whether the option is actually boolean, or even whether
* name is legal; so that this can be used to probe whether a data
* option is present without actually using that data.
*/ */
static bool static bool
vshCommandOptBool(const vshCmd *cmd, const char *name) vshCommandOptBool(const vshCmd *cmd, const char *name)
{ {
return vshCommandOpt(cmd, name) != NULL; vshCmdOpt *dummy;
return vshCommandOpt(cmd, name, &dummy) == 1;
} }
/* /**
* vshCommandOptArgv:
* @cmd command reference
* @opt starting point for the search
*
* Returns the next argv argument after OPT (or the first one if OPT * Returns the next argv argument after OPT (or the first one if OPT
* is NULL), or NULL if no more are present. * is NULL), or NULL if no more are present.
* *
...@@ -12692,7 +12802,7 @@ vshCommandOptArgv(const vshCmd *cmd, const vshCmdOpt *opt) ...@@ -12692,7 +12802,7 @@ vshCommandOptArgv(const vshCmd *cmd, const vshCmdOpt *opt)
opt = opt ? opt->next : cmd->opts; opt = opt ? opt->next : cmd->opts;
while (opt) { while (opt) {
if (opt->def && opt->def->type == VSH_OT_ARGV) { if (opt->def->type == VSH_OT_ARGV) {
return opt; return opt;
} }
opt = opt->next; opt = opt->next;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册