From ca92c85756f27920cc6aa566278437abad628e9e Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Fri, 15 Jul 2011 11:23:17 -0600 Subject: [PATCH] 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. --- tools/virsh.c | 304 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 207 insertions(+), 97 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index ef8dea3385..e75a249544 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -162,29 +162,36 @@ typedef struct __vshControl vshControl; 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 { - const char *name; /* name of information */ - const char *data; /* information */ + const char *name; /* name of information, or NULL for list end */ + const char *data; /* non-NULL information */ } vshCmdInfo; /* * vshCmdOptDef - command option definition */ 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 */ int flag; /* flags */ - const char *help; /* help string */ + const char *help; /* non-NULL help string */ } vshCmdOptDef; /* * vshCmdOpt - command options + * + * After parsing a command, all arguments to the command have been + * collected into a list of these objects. */ typedef struct vshCmdOpt { - const vshCmdOptDef *def; /* pointer to relevant option */ - char *data; /* allocated data */ + const vshCmdOptDef *def; /* non-NULL pointer to option definition */ + char *data; /* allocated data, or NULL for bool option */ struct vshCmdOpt *next; } vshCmdOpt; @@ -199,7 +206,7 @@ enum { * vshCmdDef - command definition */ typedef struct { - const char *name; + const char *name; /* name of command, or NULL for list end */ bool (*handler) (vshControl *, const vshCmd *); /* command handler */ const vshCmdOptDef *opts; /* definition of command options */ const vshCmdInfo *info; /* details about command */ @@ -239,7 +246,7 @@ typedef struct __vshControl { } __vshControl; typedef struct vshCmdGrp { - const char *name; + const char *name; /* name of group, or NULL for list end */ const char *keyword; /* help keyword */ const vshCmdDef *commands; } vshCmdGrp; @@ -264,7 +271,9 @@ static bool vshCmddefHelp(vshControl *ctl, const char *name); static const vshCmdGrp *vshCmdGrpSearch(const char *grpname); 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) ATTRIBUTE_NONNULL(3) ATTRIBUTE_RETURN_CHECK; static int vshCommandOptUInt(const vshCmd *cmd, const char *name, @@ -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 * -vshCommandOpt(const vshCmd *cmd, const char *name) +static int +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) { - if (opt->def && STREQ(opt->def->name, name)) - return opt; - opt = opt->next; + /* See if option is present on command line. */ + while (candidate) { + if (STREQ(candidate->def->name, name)) { + *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 * @name option name * @value result @@ -12530,102 +12564,144 @@ vshCommandOpt(const vshCmd *cmd, const char *name) * Convert option to int * Return value: * >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) */ static int vshCommandOptInt(const vshCmd *cmd, const char *name, int *value) { - vshCmdOpt *arg = vshCommandOpt(cmd, name); - int ret = 0, num; + vshCmdOpt *arg; + int ret; + int num; char *end_p = NULL; - if ((arg != NULL) && (arg->data != NULL)) { - num = strtol(arg->data, &end_p, 10); - ret = -1; - if ((arg->data != end_p) && (*end_p == 0)) { - *value = num; - ret = 1; - } + ret = vshCommandOpt(cmd, name, &arg); + if (ret <= 0) + return ret; + if (!arg->data) { + /* only possible on bool, but if name is bool, this is a + * 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 * See vshCommandOptInt() */ static int vshCommandOptUInt(const vshCmd *cmd, const char *name, unsigned int *value) { - vshCmdOpt *arg = vshCommandOpt(cmd, name); - unsigned int ret = 0, num; + vshCmdOpt *arg; + int ret; + unsigned int num; char *end_p = NULL; - if ((arg != NULL) && (arg->data != NULL)) { - num = strtoul(arg->data, &end_p, 10); - ret = -1; - if ((arg->data != end_p) && (*end_p == 0)) { - *value = num; - ret = 1; - } + ret = vshCommandOpt(cmd, name, &arg); + if (ret <= 0) + return ret; + if (!arg->data) { + /* only possible on bool, but if name is bool, this is a + * 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 * See vshCommandOptInt() */ static int vshCommandOptUL(const vshCmd *cmd, const char *name, unsigned long *value) { - vshCmdOpt *arg = vshCommandOpt(cmd, name); - int ret = 0; + vshCmdOpt *arg; + int ret; unsigned long num; char *end_p = NULL; - if ((arg != NULL) && (arg->data != NULL)) { - num = strtoul(arg->data, &end_p, 10); - ret = -1; - if ((arg->data != end_p) && (*end_p == 0)) { - *value = num; - ret = 1; - } + ret = vshCommandOpt(cmd, name, &arg); + if (ret <= 0) + return ret; + if (!arg->data) { + /* only possible on bool, but if name is bool, this is a + * 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 - * 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 vshCommandOptString(const vshCmd *cmd, const char *name, const char **value) { - vshCmdOpt *arg = vshCommandOpt(cmd, name); - int ret = 0; - - if (arg && arg->data) { - if (*arg->data - || (arg->def && (arg->def->flag & VSH_OFLAG_EMPTY_OK))) { - *value = arg->data; - ret = 1; - } else if (arg->def && ((arg->def->flag) & VSH_OFLAG_REQ)) { - vshError(NULL, _("Missing required option '%s'"), name); - ret = -1; - } else { - /* Treat "--option ''" as if option had not been specified. */ - ret = 0; - } + vshCmdOpt *arg; + int ret; + + ret = vshCommandOpt(cmd, name, &arg); + if (ret <= 0) + return ret; + if (!arg->data) { + /* only possible on bool, but if name is bool, this is a + * programming bug */ + return -2; } - 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 * See vshCommandOptInt() */ @@ -12633,53 +12709,87 @@ static int vshCommandOptLongLong(const vshCmd *cmd, const char *name, long long *value) { - vshCmdOpt *arg = vshCommandOpt(cmd, name); - int ret = 0; + vshCmdOpt *arg; + int ret; long long num; char *end_p = NULL; - if ((arg != NULL) && (arg->data != NULL)) { - num = strtoll(arg->data, &end_p, 10); - ret = -1; - if ((arg->data != end_p) && (*end_p == 0)) { - *value = num; - ret = 1; - } + ret = vshCommandOpt(cmd, name, &arg); + if (ret <= 0) + return ret; + if (!arg->data) { + /* only possible on bool, but if name is bool, this is a + * 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 vshCommandOptULongLong(const vshCmd *cmd, const char *name, unsigned long long *value) { - vshCmdOpt *arg = vshCommandOpt(cmd, name); - int ret = 0; + vshCmdOpt *arg; + int ret; unsigned long long num; char *end_p = NULL; - if ((arg != NULL) && (arg->data != NULL)) { - num = strtoull(arg->data, &end_p, 10); - ret = -1; - if ((arg->data != end_p) && (*end_p == 0)) { - *value = num; - ret = 1; - } + ret = vshCommandOpt(cmd, name, &arg); + if (ret <= 0) + return ret; + if (!arg->data) { + /* only possible on bool, but if name is bool, this is a + * 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 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 * is NULL), or NULL if no more are present. * @@ -12692,7 +12802,7 @@ vshCommandOptArgv(const vshCmd *cmd, const vshCmdOpt *opt) opt = opt ? opt->next : cmd->opts; while (opt) { - if (opt->def && opt->def->type == VSH_OT_ARGV) { + if (opt->def->type == VSH_OT_ARGV) { return opt; } opt = opt->next; -- GitLab