[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH RFC 1/2] qemu-arg: introduce a general purpose argum
From: |
Leandro Dorileo |
Subject: |
[Qemu-devel] [PATCH RFC 1/2] qemu-arg: introduce a general purpose argument parser |
Date: |
Sat, 8 Mar 2014 15:47:17 -0300 |
qemu-arg defines a standardized API for argument parsing, help displaying and
texi generation/sync.
The implementation supports command + arguments form (i.e qemu-img requirements)
and a more general simple arguments parsing. So we can parse:
$ prog <command> --arg1 --arg2
$ prog --arg1 --arg2
We support the following:
+ basic arguments validation (i.e required arguments and required values);
+ basic arguments transformations (integer, bool values)
+ repeated/"cumullated" arguments (i.e -o opt1=val -o opt2=val2 will result
the
string "opt1=val,opt2=val2")
+ positional arguments;
+ identified positional for fixed/defined numbers of expected positional
args;
+ listed positional for N expected positional args;
+ help messages generation;
+ texi generation;
+ default value setting;
+ mutually exclusive arguments;
+ display and parsing decorated arguments ("--argument value" and
"--argument=value"
are valid)
Signed-off-by: Leandro Dorileo <address@hidden>
---
include/qemu/qemu-arg.h | 287 ++++++++++++++++
util/Makefile.objs | 1 +
util/qemu-arg.c | 887 ++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 1175 insertions(+)
create mode 100644 include/qemu/qemu-arg.h
create mode 100644 util/qemu-arg.c
diff --git a/include/qemu/qemu-arg.h b/include/qemu/qemu-arg.h
new file mode 100644
index 0000000..c8d8fb4
--- /dev/null
+++ b/include/qemu/qemu-arg.h
@@ -0,0 +1,287 @@
+/*
+ * QEMU argument helper
+ *
+ * Copyright (c) 2014 Leandro Dorileo <address@hidden>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef _QEMU_ARG_H_
+#define _QEMU_ARG_H_
+
+#include <libintl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+typedef struct _QemuArgContext QemuArgContext;
+typedef struct _QemuArgCommand QemuArgCommand;
+
+typedef enum _QemuArgOptType {
+ QEMU_ARG_OPT_TYPE_INT,
+ QEMU_ARG_OPT_TYPE_BOOL,
+ QEMU_ARG_OPT_TYPE_STR,
+ QEMU_ARG_OPT_TYPE_POSITIONAL,
+ QEMU_ARG_OPT_TYPE_POSITIONAL_LIST,
+ QEMU_ARG_OPT_TYPE_DEPRECATED,
+ QEMU_ARG_OPT_TYPE_GROUP,
+ QEMU_ARG_OPT_TYPE_SENTINEL,
+} QemuArgOptType;
+
+typedef struct _QemuArgIntValue {
+ /** default value */
+ int def_val;
+
+ /** user value pointer */
+ int *value;
+} QemuArgIntValue;
+
+typedef struct _QemuArgBoolValue {
+ /** default value */
+ bool def_val;
+
+ /** user value pointer */
+ bool *value;
+} QemuArgBoolValue;
+
+typedef struct _QemuArgStrValue {
+ /** default value */
+ char *def_val;
+
+ /** user value pointer */
+ char **value;
+} QemuArgStrValue;
+
+typedef struct _QemuArgStrListValue {
+ /** default value */
+ char **def_val;
+
+ /** user value pointer */
+ char ***value;
+
+ /** list elements counter */
+ int list_cnt;
+} QemuArgStrListValue;
+
+typedef enum _QemuArgOptFlag {
+ ARG_FLAG_NONE = 0,
+
+ /** provide many arguments instances, their value are concatenated in a
+ comman separated string */
+ ARG_FLAG_CUMULATE = 1 << 0,
+
+ /** the argument is required */
+ ARG_FLAG_REQUIRED = 1 << 1,
+
+ /** the argument requires a value like --foo bar where --foo requires bar
*/
+ ARG_FLAG_REQ_VALUE = 1 << 2,
+} QemuArgOptFlag;
+
+typedef struct _QemuArgOpt {
+ /** argument type, bool, int, str, etc @see QemuArgOptType */
+ QemuArgOptType type;
+
+ /** the argument's short name i.e -c */
+ const char short_name;
+
+ /** argument's long name i.e --cache */
+ const char *long_name;
+
+ /** argument's description, used to display a hint about the argument's
+ value i.e -f fmt where fmt is the arg's desc */
+ const char *desc;
+
+ /** help message, describes the argument */
+ const char *help;
+
+ /** some behavior flags @see QemuArgOptFlag for possible modifiers */
+ int flags;
+
+ /** indicates the argument was set, for bool values it tells we got the
+ argument and the others we got the argument's value */
+ bool value_set;
+
+ /** value pointer, @see QemuArgIntValue @see QemuArgBoolValue
+ @see QemuArgStrValue @see QemuArgStrListValue */
+ const void *value;
+} QemuArgOpt;
+
+struct _QemuArgCommand {
+ /** the command itself i.e commit, create */
+ const char *cmd;
+
+ /** help msg displayed on main help msg - command listing msg */
+ const char *help;
+
+ /** the command's arguments */
+ QemuArgOpt *args;
+
+ /** number of arguments */
+ int args_cnt;
+
+ /** mutual exclusive groups - it's an array of QemuArgOpt arrays, each
array
+ composes arguments mutually exclusive */
+ QemuArgOpt **mutual_groups;
+
+ int (* cb)(const QemuArgContext *ctx, const QemuArgCommand *cmd);
+};
+
+struct _QemuArgContext {
+ /** text displayed on top of help msg */
+ const char *prologue;
+
+ /** text displayed on bottom of help msg */
+ const char *epilogue;
+
+ /** the program name, usage on "usage" msg */
+ const char *prog_name;
+
+ /** command list */
+ const QemuArgCommand *commands;
+
+ /** non command usage or common arguments */
+ QemuArgOpt *args;
+
+ /** number of arguments (non command usage) */
+ int args_cnt;
+
+ /** non command usage */
+ QemuArgOpt **mutual_groups;
+
+ /** support decorated mode, like --arg=value. actually we'll always parse
+ this form, this flag will indicate we must show the long options on help
+ messages @see OPT_DECORATE_LONG and @see OPT_DECORATE_SHORT */
+ int decorate;
+};
+
+/** decoration modes, OPT_DECORATE_LONG | OPT_DECORATE_SHORT will decorate both
+ short and long arguments */
+#define OPT_DECORATE_LONG (1 << 0)
+#define OPT_DECORATE_SHORT (1 << 1)
+
+/** type wrappers macros */
+#define INT_VALUE(_val, _default) \
+ &(QemuArgIntValue) {.value = _val, .def_val = _default} \
+
+#define BOOL_VALUE(_val, _default) \
+ &(QemuArgBoolValue) {.value = _val, .def_val = _default} \
+
+#define STR_VALUE(_val, _default) \
+ &(QemuArgStrValue) {.value = _val, .def_val = (char *)_default} \
+
+#define STR_LIST_VALUE(_val, _default) \
+ &(QemuArgStrListValue) {.value = _val, .def_val = _default} \
+
+/** QemuArgOpt constructors */
+#define OPT_BOOL(s, l, h, p, df) \
+ {QEMU_ARG_OPT_TYPE_BOOL, s, l, NULL, h, ARG_FLAG_NONE, false, \
+ BOOL_VALUE(p, df)} \
+
+#define OPT_STR(s, l, d, h, p, df) \
+ {QEMU_ARG_OPT_TYPE_STR, s, l, d, h, ARG_FLAG_REQ_VALUE, false, \
+ STR_VALUE(p, df)} \
+
+#define OPT_INT(s, l, d, h, p, df) \
+ {QEMU_ARG_OPT_TYPE_INT, s, l, d, h, ARG_FLAG_REQ_VALUE, false, \
+ INT_VALUE(p, df)} \
+
+#define OPT_CUMUL_STR(s, l, d, h, p, df) \
+ {QEMU_ARG_OPT_TYPE_STR, s, l, d, h, \
+ ARG_FLAG_CUMULATE | ARG_FLAG_REQ_VALUE, false, \
+ STR_VALUE(p, df)} \
+
+#define OPT_CUMUL_STR_REQ(s, l, d, h, p, df) \
+ {QEMU_ARG_OPT_TYPE_STR, s, l, d, h, \
+ ARG_FLAG_CUMULATE | ARG_FLAG_REQ_VALUE | ARG_FLAG_REQUIRED, \
+ false, STR_VALUE(p, df)} \
+
+#define OPT_POS(d, h, p, df) \
+ {QEMU_ARG_OPT_TYPE_POSITIONAL, 0, NULL, d, h, ARG_FLAG_NONE, \
+ false, STR_VALUE(p, df)} \
+
+#define OPT_POS_REQ(d, h, p, df) \
+ {QEMU_ARG_OPT_TYPE_POSITIONAL, 0, NULL, d, h, ARG_FLAG_REQUIRED, \
+ false, STR_VALUE(p, df)} \
+
+#define OPT_POS_LIST(d, h, p, df) \
+ {QEMU_ARG_OPT_TYPE_POSITIONAL_LIST, 0, NULL, d, h, ARG_FLAG_NONE, \
+ false, STR_LIST_VALUE(p, df)} \
+
+#define OPT_POS_LIST_REQ(d, h, p, df) \
+ {QEMU_ARG_OPT_TYPE_POSITIONAL_LIST, 0, NULL, d, h, \
+ ARG_FLAG_REQUIRED, false, STR_LIST_VALUE(p, df)} \
+
+#define OPT_DEP(s, l, h) \
+ {QEMU_ARG_OPT_TYPE_DEPRECATED, s, l, NULL, h, ARG_FLAG_NONE} \
+
+#define OPT_GROUP(d) \
+ {QEMU_ARG_OPT_TYPE_GROUP, 0, NULL, d, NULL, ARG_FLAG_NONE} \
+
+/** command and context macro constructor */
+#define OPT_CMD(c, h, a, m, cb) \
+ {c, h, a, sizeof(a) / sizeof(QemuArgOpt), m, cb} \
+
+#define OPT_CTX(n, p, e, pn, c, a, m, d) \
+ const QemuArgContext n = {p, e, pn, c, a, \
+ sizeof(a) / sizeof(QemuArgOpt), m, d} \
+
+#define OPT_MUTUAL_GROUP_SENTINEL NULL
+#define OPT_SENTINEL { QEMU_ARG_OPT_TYPE_SENTINEL }
+#define CMD_SENTINEL { NULL }
+
+/**
+ *
+ * @param argc The program arguments counter
+ * @param argv The program arguments vector
+ * @param ctx The qemu arg context of interest
+ *
+ * Parse commands.
+ *
+ * @return negative number on failure, 0 otherwise.
+ */
+int qemu_arg_parse(int argc, char *argv[], const QemuArgContext *ctx);
+
+/**
+ * @param ctx the context to be cleaned up
+ *
+ * Clean up the argument context.
+ */
+void qemu_arg_context_cleanup(const QemuArgContext *ctx);
+
+/**
+ * @param cmd The command of interest
+ *
+ * Query a command about its parsed positional arguments list.
+ *
+ * @return How many items a positional list has. @see OPT_POS_LIST and
+ * @see OPT_POS_LIST_REQ
+ */
+int qemu_arg_get_command_positional_list_cnt(const QemuArgCommand *cmd);
+
+/**
+ * @param ctx The qemu arg context of interest
+ * @param cmd The qemu command of interest
+ *
+ * Print a command help message.
+ */
+void qemu_arg_print_command_help(const QemuArgContext *ctx,
+ const QemuArgCommand *cmd);
+
+#endif /* _QEMU_ARG_H_ */
diff --git a/util/Makefile.objs b/util/Makefile.objs
index 937376b..9d05ec3 100644
--- a/util/Makefile.objs
+++ b/util/Makefile.objs
@@ -14,3 +14,4 @@ util-obj-y += crc32c.o
util-obj-y += throttle.o
util-obj-y += getauxval.o
util-obj-y += readline.o
+util-obj-y += qemu-arg.o
diff --git a/util/qemu-arg.c b/util/qemu-arg.c
new file mode 100644
index 0000000..e611d97
--- /dev/null
+++ b/util/qemu-arg.c
@@ -0,0 +1,887 @@
+/*
+ * QEMU argument helper
+ *
+ * Copyright (c) 2014 Leandro Dorileo <address@hidden>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/qemu-arg.h"
+
+#define SUCCESS 0
+#define ERR -1
+
+#define HELP_BLOCK_PADDING 25
+#define HELP_BLOCK_WIDTH 90
+#define HELP_DESC_BLOCK_WIDTH (HELP_BLOCK_WIDTH - HELP_BLOCK_PADDING)
+
+typedef void (*HelpFunc)(const QemuArgContext *ctx, const void *opaque);
+
+#define OPT_INT_VAL_GET(_opt) \
+ ((QemuArgIntValue *)_opt->value) \
+
+#define OPT_BOOL_VAL_GET(_opt) \
+ ((QemuArgBoolValue *)_opt->value) \
+
+#define OPT_STR_VAL_GET(_opt) \
+ ((QemuArgStrValue *)_opt->value) \
+
+#define OPT_STR_LIST_VAL_GET(_opt) \
+ ((QemuArgStrListValue *)_opt->value) \
+
+#define OPT_HELP \
+ "show this help message. <command> -h will show the command " \
+ "specific help message." \
+
+static QemuArgOpt _internal_arg_opts[] = {
+ OPT_STR('h', "help", NULL, OPT_HELP, NULL, NULL),
+ OPT_SENTINEL
+};
+
+static void print_help_block(const char *help, int padding_len,
+ int length)
+{
+ const char *c;
+ int i, j;
+
+ for (i = 0, c = help; *c != '\0'; c++, i++) {
+ /** should break the line? */
+ if (i >= length && *c == ' ') {
+ printf("\n");
+ for (j = 0; padding_len && j <= padding_len; j++) {
+ printf(" ");
+ }
+ i = 0;
+ continue;
+ }
+ printf("%c", *c);
+ }
+}
+
+static int format_short_opt(QemuArgOpt *opt, int decorate, bool texi)
+{
+ int cnt = 0;
+
+ if (!opt->short_name) {
+ return cnt;
+ }
+
+ cnt += printf("-%c", opt->short_name);
+
+ if (decorate & OPT_DECORATE_SHORT && opt->desc) {
+ if (texi) {
+ cnt += printf("address@hidden", opt->desc);
+ } else {
+ cnt += printf("=%s", opt->desc);
+ }
+ } else if (opt->desc) {
+ if (texi) {
+ cnt += printf(" @var{%s}", opt->desc);
+ } else {
+ cnt += printf(" %s", opt->desc);
+ }
+ }
+
+ if (opt->long_name) {
+ cnt += printf(", ");
+ }
+
+ return cnt;
+}
+
+static int format_long_opt(QemuArgOpt *opt, int decorate, bool texi)
+{
+ int cnt = 0;
+
+ if (!opt->long_name) {
+ return cnt;
+ }
+
+ cnt += printf("--%s", opt->long_name);
+
+ if (decorate & OPT_DECORATE_LONG && opt->desc) {
+ if (texi) {
+ cnt += printf("address@hidden", opt->desc);
+ } else {
+ cnt += printf("=%s", opt->desc);
+ }
+ } else if (opt->desc) {
+ if (texi) {
+ cnt += printf(" @var{%s}", opt->desc);
+ } else {
+ cnt += printf(" %s", opt->desc);
+ }
+ }
+
+ return cnt;
+}
+
+static int format_mutual_opt(QemuArgOpt **opt, int decorate, bool texi)
+{
+ QemuArgOpt **mutual, *itr;
+ int cnt = 0;
+
+ for (mutual = opt; *mutual; mutual++) {
+ cnt += printf(" [");
+
+ for (itr = *mutual; itr->type != QEMU_ARG_OPT_TYPE_SENTINEL; itr++) {
+ cnt += format_short_opt(itr, decorate, texi);
+ cnt += format_long_opt(itr, decorate, texi);
+
+ if ((itr + 1)->type != QEMU_ARG_OPT_TYPE_SENTINEL) {
+ cnt += printf(" | ");
+ }
+ }
+
+ cnt += printf("]");
+ }
+
+ return cnt;
+}
+
+static void print_positional_arg_list(QemuArgOpt *list)
+{
+ QemuArgOpt *itr;
+
+ for (itr = list; itr->type != QEMU_ARG_OPT_TYPE_SENTINEL; itr++) {
+ int cnt = 0;
+
+ if (itr->type != QEMU_ARG_OPT_TYPE_POSITIONAL &&
+ itr->type != QEMU_ARG_OPT_TYPE_POSITIONAL_LIST) {
+ continue;
+ }
+
+ cnt += printf(" %s", itr->desc);
+
+ if (cnt < HELP_BLOCK_PADDING) {
+ for (; cnt < HELP_BLOCK_PADDING; cnt++) {
+ printf(" ");
+ }
+ } else {
+ printf("\n");
+ for (cnt = 0; cnt < HELP_BLOCK_PADDING; cnt++) {
+ printf(" ");
+ }
+ }
+
+ print_help_block(itr->help, HELP_BLOCK_PADDING - 1,
+ HELP_DESC_BLOCK_WIDTH);
+
+ printf("\n");
+ }
+}
+
+static void print_arg_list(QemuArgOpt *list, int decorate)
+{
+ QemuArgOpt *itr;
+
+ for (itr = list; itr->type != QEMU_ARG_OPT_TYPE_SENTINEL; itr++) {
+ int cnt = 0;
+
+ if (itr->type == QEMU_ARG_OPT_TYPE_GROUP) {
+ printf("\n%s options:\n", itr->desc);
+ continue;
+ }
+
+ cnt += printf(" ");
+ cnt += format_short_opt(itr, decorate, false);
+ cnt += format_long_opt(itr, decorate, false);
+
+ if (cnt < HELP_BLOCK_PADDING) {
+ for (; cnt < HELP_BLOCK_PADDING; cnt++) {
+ printf(" ");
+ }
+ } else {
+ printf("\n");
+ for (cnt = 0; cnt < HELP_BLOCK_PADDING; cnt++) {
+ printf(" ");
+ }
+ }
+
+ print_help_block(itr->help, HELP_BLOCK_PADDING - 1,
+ HELP_DESC_BLOCK_WIDTH);
+ printf("\n");
+ }
+}
+
+static void print_help(const QemuArgContext *ctx)
+{
+ const QemuArgCommand *cmd;
+
+ if (ctx->prologue) {
+ printf("%s\n\n", ctx->prologue);
+ }
+
+ printf("%s\n", "Global options:");
+ print_arg_list(_internal_arg_opts, 0);
+
+ if (ctx->args) {
+ print_arg_list(ctx->args, ctx->decorate);
+ printf("\n");
+ }
+
+ if (ctx->commands) {
+ printf("%s\n", "Commands:");
+
+ for (cmd = ctx->commands; cmd->cmd; cmd++) {
+ printf(" %-10s %s\n", cmd->cmd, cmd->help);
+ }
+ printf("\n");
+ }
+
+ if (ctx->epilogue) {
+ print_help_block(ctx->epilogue, 0, HELP_BLOCK_WIDTH);
+ printf("\n");
+ }
+}
+
+static int format_positional_opt(QemuArgOpt *opt, bool texi)
+{
+ int cnt = 0;
+
+ if (!(opt->flags & ARG_FLAG_REQUIRED)) {
+ cnt += printf(" [");
+ } else {
+ cnt += printf(" ");
+ }
+
+ if (texi) {
+ cnt += printf("@var{%s}", opt->desc);
+ } else {
+ cnt += printf("%s", opt->desc);
+ }
+
+ if (!(opt->flags & ARG_FLAG_REQUIRED)) {
+ cnt += printf("]");
+ }
+
+ return cnt;
+}
+
+static void print_command_desc(const QemuArgContext *ctx,
+ const QemuArgCommand *cmd, bool texi)
+{
+ QemuArgOpt *itr;
+
+ printf("%s", cmd->cmd);
+
+#define PRINT_OPT(_args) \
+ for (itr = _args; itr->type != QEMU_ARG_OPT_TYPE_SENTINEL; itr++) { \
+ if (itr->type == QEMU_ARG_OPT_TYPE_DEPRECATED || \
+ itr->type == QEMU_ARG_OPT_TYPE_GROUP) { \
+ continue; \
+ } \
+ if (itr->type == QEMU_ARG_OPT_TYPE_POSITIONAL || \
+ itr->type == QEMU_ARG_OPT_TYPE_POSITIONAL_LIST) { \
+ format_positional_opt(itr, texi); \
+ } else { \
+ printf(" ["); \
+ format_short_opt(itr, ctx->decorate, texi); \
+ format_long_opt(itr, ctx->decorate, texi); \
+ printf("]"); \
+ } \
+ } \
+
+ if (ctx->args) {
+ PRINT_OPT(ctx->args);
+ }
+
+ if (cmd->mutual_groups) {
+ format_mutual_opt(cmd->mutual_groups, ctx->decorate, texi);
+ }
+
+ if (cmd->args) {
+ PRINT_OPT(cmd->args);
+ }
+
+#undef PRINT_OPT
+}
+
+static void print_command_usage(const QemuArgContext *ctx,
+ const QemuArgCommand *cmd)
+{
+ printf("usage:");
+
+ if (ctx->prog_name) {
+ printf(" %s ", ctx->prog_name);
+ }
+
+ print_command_desc(ctx, cmd, false);
+ printf("\n\n");
+}
+
+static bool command_has_positional(const QemuArgCommand *cmd)
+{
+ QemuArgOpt *itr;
+
+ if (!cmd->args) {
+ return false;
+ }
+
+ for (itr = cmd->args; itr->type != QEMU_ARG_OPT_TYPE_SENTINEL; itr++) {
+ if (itr->type == QEMU_ARG_OPT_TYPE_POSITIONAL ||
+ QEMU_ARG_OPT_TYPE_POSITIONAL_LIST) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void qemu_arg_print_command_help(const QemuArgContext *ctx,
+ const QemuArgCommand *cmd)
+{
+ QemuArgOpt **itr;
+ bool cmd_opts = false;
+
+ if (ctx->prologue) {
+ printf("%s\n\n", ctx->prologue);
+ }
+
+ print_command_usage(ctx, cmd);
+
+ printf("%s\n", "Common options:");
+ print_arg_list(_internal_arg_opts, 0);
+
+ if (ctx->args) {
+ print_arg_list(ctx->args, ctx->decorate);
+ }
+ printf("\n");
+
+ if (command_has_positional(cmd)) {
+ printf("%s\n", "Positional options:");
+ print_positional_arg_list(cmd->args);
+ printf("\n");
+ }
+
+ if (cmd->mutual_groups) {
+ printf("%s\n", "Command options:");
+
+ for (itr = cmd->mutual_groups; *itr; itr++) {
+ print_arg_list(*itr, ctx->decorate);
+ }
+ cmd_opts = true;
+ }
+
+ if (cmd->args) {
+ if (!cmd_opts) {
+ printf("%s\n", "Command options:");
+ }
+
+ if (cmd->args) {
+ print_arg_list(cmd->args, ctx->decorate);
+ }
+ cmd_opts = true;
+ }
+
+ if (cmd_opts) {
+ printf("\n");
+ }
+
+ if (ctx->epilogue) {
+ print_help_block(ctx->epilogue, 0, HELP_BLOCK_WIDTH);
+ printf("\n");
+ }
+}
+
+static void print_command_help_cb(const QemuArgContext *ctx, const void
*opaque)
+{
+ qemu_arg_print_command_help(ctx, opaque);
+}
+
+static const QemuArgCommand *find_command(char *cmd, const QemuArgCommand
*list)
+{
+ const QemuArgCommand *itr;
+
+ for (itr = list; itr->cmd; itr++) {
+ if (!strcmp(cmd, itr->cmd)) {
+ return itr;
+ }
+ }
+
+ return NULL;
+}
+
+static QemuArgOpt *find_long_opt(char *opt, char *end, QemuArgOpt *list)
+{
+ QemuArgOpt *itr;
+
+ if (!list) {
+ return NULL;
+ }
+
+ for (itr = list; itr->type != QEMU_ARG_OPT_TYPE_SENTINEL; itr++) {
+ if (itr->long_name && !strncmp(itr->long_name, opt, end - opt)) {
+ return itr;
+ }
+ }
+
+ return NULL;
+}
+
+static QemuArgOpt *find_short_opt(char opt, QemuArgOpt *list)
+{
+ QemuArgOpt *itr;
+
+ if (!list) {
+ return NULL;
+ }
+
+ for (itr = list; itr->type != QEMU_ARG_OPT_TYPE_SENTINEL; itr++) {
+ if (itr->short_name && itr->short_name == opt) {
+ return itr;
+ }
+ }
+
+ return NULL;
+}
+
+static QemuArgOpt *find_opt(char *opt, char *end, QemuArgOpt *list)
+{
+ char *long_opt, short_opt = 0;
+ size_t len;
+
+ len = strlen("--");
+ if (!strncmp(opt, "--", len)) {
+ long_opt = opt + len;
+ return find_long_opt(long_opt, end, list);
+ }
+
+ if (strlen(opt) == 2 && opt[0] == '-') {
+ short_opt = opt[1];
+ return find_short_opt(short_opt, list);
+ }
+
+ return NULL;
+}
+
+static QemuArgOpt *opt_get_next_positional(char *opt, QemuArgOpt *args)
+{
+ QemuArgOpt *itr;
+
+ for (itr = args; itr->type != QEMU_ARG_OPT_TYPE_SENTINEL; itr++) {
+ if (itr->type == QEMU_ARG_OPT_TYPE_POSITIONAL_LIST) {
+ return itr;
+ }
+
+ if (itr->type == QEMU_ARG_OPT_TYPE_POSITIONAL && itr->value &&
+ !itr->value_set) {
+ return itr;
+ }
+ }
+
+ return NULL;
+}
+
+static bool opt_set_value(char *curr, char *next, int *cnt, QemuArgOpt *opt)
+{
+ if (opt->flags & ARG_FLAG_REQ_VALUE && !next) {
+ printf("argument %s requires a value\n", curr);
+ return false;
+ }
+
+ if (opt->value && opt->type == QEMU_ARG_OPT_TYPE_BOOL) {
+ *OPT_BOOL_VAL_GET(opt)->value = true;
+ opt->value_set = true;
+ return true;
+ }
+
+ if (opt->value && opt->type == QEMU_ARG_OPT_TYPE_INT) {
+ *OPT_INT_VAL_GET(opt)->value = atoi(next);
+ opt->value_set = true;
+ return true;
+ }
+
+ if (opt->value && opt->type == QEMU_ARG_OPT_TYPE_STR) {
+ if (opt->flags & ARG_FLAG_CUMULATE) {
+ char **str = OPT_STR_VAL_GET(opt)->value;
+ char *cp;
+
+ if (*str) {
+ cp = strdup(*str);
+ free(*str);
+
+ *str = calloc(1, strlen(cp) + strlen(next) + 1);
+ sprintf(*str, "%s,%s", cp, next);
+
+ free(cp);
+ } else {
+ *str = calloc(1, strlen(next) + 1);
+ sprintf(*str, "%s", next);
+ }
+ } else {
+ *OPT_STR_VAL_GET(opt)->value = next;
+ }
+
+ *cnt += 1;
+ opt->value_set = true;
+ return true;
+ }
+
+ if (opt->value && opt->type == QEMU_ARG_OPT_TYPE_POSITIONAL) {
+ *OPT_STR_VAL_GET(opt)->value = curr;
+ opt->value_set = true;
+ return true;
+ }
+
+ if (opt->value && opt->type == QEMU_ARG_OPT_TYPE_POSITIONAL_LIST) {
+ char **val;
+ size_t value_size;
+ int list_cnt;
+
+ val = *OPT_STR_LIST_VAL_GET(opt)->value;
+
+ list_cnt = OPT_STR_LIST_VAL_GET(opt)->list_cnt;
+ if (!list_cnt) {
+ val = realloc(val, sizeof(char *));
+ }
+
+ value_size = sizeof(val) + sizeof(char *);
+ val = realloc(val, value_size);
+
+ val[list_cnt] = (char *)curr;
+
+ list_cnt++;
+ *OPT_STR_LIST_VAL_GET(opt)->value = val;
+ OPT_STR_LIST_VAL_GET(opt)->list_cnt = list_cnt;
+ val[list_cnt] = NULL;
+
+ opt->value_set = true;
+ return true;
+ }
+
+ return true;
+}
+
+static QemuArgOpt *find_opt_set(QemuArgOpt *list, QemuArgOpt *opt)
+{
+ QemuArgOpt *itr;
+
+ for (itr = list; itr->type != QEMU_ARG_OPT_TYPE_SENTINEL; itr++) {
+ if (itr->value_set && itr != opt) {
+ return itr;
+ }
+ }
+
+ return NULL;
+}
+
+static bool validate_required(const QemuArgContext *ctx, QemuArgOpt *opt)
+{
+ QemuArgOpt *itr;
+
+ for (itr = opt; itr->type != QEMU_ARG_OPT_TYPE_SENTINEL; itr++) {
+ if (itr->flags & ARG_FLAG_REQUIRED && !itr->value_set) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static void set_default_values(QemuArgOpt *opt)
+{
+ QemuArgOpt *itr;
+
+ for (itr = opt; itr->type != QEMU_ARG_OPT_TYPE_SENTINEL; itr++) {
+ if (itr->value_set || !itr->value) {
+ continue;
+ }
+
+ if (itr->type == QEMU_ARG_OPT_TYPE_INT) {
+ *OPT_INT_VAL_GET(itr)->value = OPT_INT_VAL_GET(itr)->def_val;
+ } else if (itr->type == QEMU_ARG_OPT_TYPE_STR) {
+ *OPT_STR_VAL_GET(itr)->value = OPT_STR_VAL_GET(itr)->def_val;
+ } else if (itr->type == QEMU_ARG_OPT_TYPE_BOOL) {
+ OPT_BOOL_VAL_GET(itr)->value = &OPT_BOOL_VAL_GET(itr)->def_val;
+ }
+ }
+}
+
+static int arg_parse(int argc, char *argv[], int begin,
+ const QemuArgContext *ctx, QemuArgOpt *args,
+ QemuArgOpt **mutual_groups, HelpFunc cb,
+ const void *opaque)
+{
+ int i;
+
+ for (i = begin; i < argc; i++) {
+ QemuArgOpt *opt, **itr, *mutual;
+ char *curr, *next, *curr_end, size_char;
+ bool positional, size;
+
+ size = false;
+ opt = NULL;
+ curr = argv[i];
+ curr_end = strchr(curr, '=');
+
+ if (!curr_end) {
+ curr_end = curr + strlen(curr);
+ next = argv[i + 1];
+ } else {
+ next = curr_end + 1;
+ }
+
+ if (!strncmp(curr, "-h", curr_end - curr) ||
+ !strncmp(curr, "--help", curr_end - curr)) {
+ cb(ctx, opaque);
+ return SUCCESS;
+ }
+
+ opt = find_opt(curr, curr_end, args);
+ if (!opt && mutual_groups) {
+ for (itr = mutual_groups; *itr; itr++) {
+ opt = find_opt(curr, curr_end, *itr);
+
+ if (opt) {
+ mutual = find_opt_set(*itr, opt);
+ if (mutual) {
+ printf("the following arguments are mutually exclusive
"
+ "and should not be used in conjunction:\n");
+ format_mutual_opt(itr, ctx->decorate, false);
+ printf("\n");
+
+ return ERR;
+ }
+ }
+ }
+ }
+
+ if (opt && opt->type == QEMU_ARG_OPT_TYPE_DEPRECATED) {
+ printf("%s\n", opt->help);
+ return ERR;
+ }
+
+ size_char = curr[strlen(curr) - 1];
+
+ if (size_char == 'k' || size_char == 'K' || size_char == 'M' ||
+ size_char == 'G' || size_char == 'T' || size_char == 'P' ||
+ size_char == 'E') {
+ size = true;
+ }
+
+ /** not a valid --argument or not a size specifier (like qemu-img does
+ for resize i.e +1G or -1G) **/
+ positional = curr[0] != '-' || size;
+ if (!opt && !positional) {
+ printf("unknown option %s\n", curr);
+ return ERR;
+ } else if (positional) {
+ opt = opt_get_next_positional(curr, args);
+ if (!opt) {
+ continue;
+ }
+ }
+
+ if (opt->value_set && !(opt->flags & ARG_FLAG_CUMULATE) &&
+ opt->type != QEMU_ARG_OPT_TYPE_BOOL &&
+ opt->type != QEMU_ARG_OPT_TYPE_POSITIONAL_LIST) {
+ printf("repeated argument, you can provide %s argument just"
+ " once.\n", curr);
+ return ERR;
+ }
+
+ if (!opt_set_value(curr, next, &i, opt)) {
+ return ERR;
+ }
+ }
+
+ return 0;
+}
+
+static QemuArgOpt *merge_args(const QemuArgOpt *list, int list_cnt,
+ const QemuArgOpt *list2, int list2_cnt)
+{
+ QemuArgOpt *tmp, *lst;
+ size_t list_size, list2_size;
+
+ if (list_cnt && list2_cnt) {
+ list_cnt--;
+ }
+
+ list_size = list_cnt * sizeof(QemuArgOpt);
+ list2_size = list2_cnt * sizeof(QemuArgOpt);
+
+ lst = calloc(1, list_size + list2_size);
+ lst = memcpy(lst, list, list_size);
+
+ tmp = lst + list_cnt;
+ tmp = memcpy(tmp, list2, list2_size);
+
+ return lst;
+}
+
+static void generate_hx(const QemuArgContext *ctx)
+{
+ const QemuArgCommand *itr;
+
+ printf("HXCOMM generated with %s generate-hx\n\n",
+ ctx->prog_name);
+
+ printf("STEXI\n"
+ "@table @option\n"
+ "ETEXI\n\n");
+
+ for (itr = ctx->commands; itr->cmd; itr++) {
+ printf("STEXI\n");
+ printf("@item ");
+ print_command_desc(ctx, itr, true);
+
+ if (!(itr + 1)->cmd) {
+ printf("address@hidden table");
+ }
+
+ printf("\nETEXI\n\n");
+ }
+}
+
+static int command_parse(int argc, char *argv[], const QemuArgContext *ctx)
+{
+ const QemuArgCommand *cmd;
+ QemuArgOpt *args;
+ int ret;
+
+ ret = 0;
+
+ if (!strcmp(argv[1], "generate-hx")) {
+ generate_hx(ctx);
+ return SUCCESS;
+ }
+
+ cmd = find_command(argv[1], ctx->commands);
+ if (!cmd) {
+ print_help(ctx);
+ return ERR;
+ }
+
+ if (!cmd->args) {
+ return ERR;
+ }
+
+ args = merge_args(ctx->args, ctx->args_cnt, cmd->args, cmd->args_cnt);
+ ret = arg_parse(argc, argv, 2, ctx, args, cmd->mutual_groups,
+ print_command_help_cb, cmd);
+
+ if (!validate_required(ctx, args)) {
+ qemu_arg_print_command_help(ctx, cmd);
+ goto err;
+ }
+
+ set_default_values(args);
+
+ if (cmd->cb) {
+ ret = cmd->cb(ctx, cmd);
+ }
+
+err:
+ free(args);
+ return ret;
+}
+
+static void print_help_cb(const QemuArgContext *ctx, const void *opaque)
+{
+ print_help(ctx);
+}
+
+static int arg_list_parse(int argc, char *argv[], const QemuArgContext *ctx)
+{
+ int ret = 0;
+
+ ret = arg_parse(argc, argv, 1, ctx, ctx->args, ctx->mutual_groups,
+ print_help_cb, NULL);
+
+ if (!validate_required(ctx, ctx->args)) {
+ print_help(ctx);
+ return ERR;
+ }
+
+ set_default_values(ctx->args);
+
+ return ret;
+}
+
+int qemu_arg_parse(int argc, char *argv[], const QemuArgContext *ctx)
+{
+ if (argc == 1) {
+ print_help(ctx);
+ return ERR;
+ }
+
+ if (ctx->commands) {
+ return command_parse(argc, argv, ctx);
+ } else {
+ return arg_list_parse(argc, argv, ctx);
+ }
+}
+
+static void opt_cleanup(QemuArgOpt *opt)
+{
+ QemuArgOpt *itr;
+
+ for (itr = opt; itr->type != QEMU_ARG_OPT_TYPE_SENTINEL; itr++) {
+ if (!itr->value) {
+ continue;
+ }
+
+ if (itr->type == QEMU_ARG_OPT_TYPE_POSITIONAL_LIST) {
+ free(*OPT_STR_LIST_VAL_GET(itr)->value);
+ } else if (itr->flags & ARG_FLAG_CUMULATE) {
+ free(*OPT_STR_VAL_GET(itr)->value);
+ }
+ }
+}
+
+void qemu_arg_context_cleanup(const QemuArgContext *ctx)
+{
+ const QemuArgCommand *itr;
+ QemuArgOpt **opt;
+
+ for (itr = ctx->commands; itr->cmd; itr++) {
+ if (!itr->args || !itr->mutual_groups) {
+ continue;
+ }
+
+ opt_cleanup(itr->args);
+
+ if (!itr->mutual_groups) {
+ continue;
+ }
+
+ for (opt = itr->mutual_groups; *opt; opt++) {
+ opt_cleanup(*opt);
+ }
+ }
+}
+
+int qemu_arg_get_command_positional_list_cnt(const QemuArgCommand *cmd)
+{
+ QemuArgOpt *itr;
+
+ if (!cmd) {
+ return 0;
+ }
+
+ for (itr = cmd->args; itr->type != QEMU_ARG_OPT_TYPE_SENTINEL; itr++) {
+ if (itr->type == QEMU_ARG_OPT_TYPE_POSITIONAL_LIST) {
+ return OPT_STR_LIST_VAL_GET(itr)->list_cnt;
+ }
+ }
+
+ return 0;
+}
--
1.9.0
Re: [Qemu-devel] [PATCH RFC 0/2] qemu-arg: general purpose argument parser, Peter Maydell, 2014/03/08
Re: [Qemu-devel] [PATCH RFC 0/2] qemu-arg: general purpose argument parser, Andreas Färber, 2014/03/09
Re: [Qemu-devel] [PATCH RFC 0/2] qemu-arg: general purpose argument parser, Kevin Wolf, 2014/03/11