Show changes to 9 files +159 −76
Documentation/technical/api-parse-options.txt, builtin/am.c, builtin/help.c, builtin/ls-tree.c, builtin/rebase.c, builtin/replace.c, builtin/stripspace.c, parse-options.c, parse-options.h
diff --git a/Documentation/technical/api-parse-options.txt b/Documentation/technical/api-parse-options.txt
index 61fa6ee167..3958ab7c94 100644
--- a/Documentation/technical/api-parse-options.txt
+++ b/Documentation/technical/api-parse-options.txt
@@ -278,6 +278,13 @@ There are some macros to easily define options:
option has already set its value to the same `int_var`.
In new commands consider using subcommands instead.
+`OPT_CMDMODE_T(short, long, type_name, &enum_var, description, enum_val)`::
+ Same as `OPT_CMDMODE`, but allows specifying an enum variable
+ instead of an int.
+ Requires `DEFINE_OPTION_VALUE_TYPE(type_name, enum_type);` at
+ file scope to declare `type_name`; `enum_type` must be the type
+ of `enum_var`.
+
`OPT_SUBCOMMAND(long, &fn_ptr, subcommand_fn)`::
Define a subcommand. `subcommand_fn` is put into `fn_ptr` when
this subcommand is used.
diff --git a/builtin/am.c b/builtin/am.c
index 6655059a57..4642dc5099 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -2268,6 +2268,8 @@ enum resume_type {
RESUME_ALLOW_EMPTY,
};
+DEFINE_OPTION_VALUE_TYPE(resume_type, enum resume_type);
+
struct resume_mode {
enum resume_type mode;
enum show_patch_type sub_mode;
@@ -2275,7 +2277,7 @@ struct resume_mode {
static int parse_opt_show_current_patch(const struct option *opt, const char *arg, int unset)
{
- int *opt_value = opt->value;
+ enum resume_type *opt_value = opt->value;
struct resume_mode *resume = container_of(opt_value, struct resume_mode, mode);
/*
@@ -2388,27 +2390,33 @@ int cmd_am(int argc, const char **argv, const char *prefix)
PARSE_OPT_NOARG),
OPT_STRING(0, "resolvemsg", &state.resolvemsg, NULL,
N_("override error message when patch failure occurs")),
- OPT_CMDMODE(0, "continue", &resume.mode,
+ OPT_CMDMODE_T(0, "continue", resume_type, &resume.mode,
N_("continue applying patches after resolving a conflict"),
RESUME_RESOLVED),
- OPT_CMDMODE('r', "resolved", &resume.mode,
+ OPT_CMDMODE_T('r', "resolved", resume_type, &resume.mode,
N_("synonyms for --continue"),
RESUME_RESOLVED),
- OPT_CMDMODE(0, "skip", &resume.mode,
+ OPT_CMDMODE_T(0, "skip", resume_type, &resume.mode,
N_("skip the current patch"),
RESUME_SKIP),
- OPT_CMDMODE(0, "abort", &resume.mode,
+ OPT_CMDMODE_T(0, "abort", resume_type, &resume.mode,
N_("restore the original branch and abort the patching operation"),
RESUME_ABORT),
- OPT_CMDMODE(0, "quit", &resume.mode,
+ OPT_CMDMODE_T(0, "quit", resume_type, &resume.mode,
N_("abort the patching operation but keep HEAD where it is"),
RESUME_QUIT),
- { OPTION_CALLBACK, 0, "show-current-patch", &resume.mode,
- "(diff|raw)",
- N_("show the patch being applied"),
- PARSE_OPT_CMDMODE | PARSE_OPT_OPTARG | PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
- parse_opt_show_current_patch, RESUME_SHOW_PATCH },
- OPT_CMDMODE(0, "allow-empty", &resume.mode,
+ {
+ .type = OPTION_CALLBACK,
+ .long_name = "show-current-patch",
+ OPTION_VALUE(resume_type, &resume.mode),
+ .argh = "(diff|raw)",
+ .help = N_("show the patch being applied"),
+ .flags = PARSE_OPT_CMDMODE | PARSE_OPT_OPTARG |
+ PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
+ .callback = parse_opt_show_current_patch,
+ .defval = RESUME_SHOW_PATCH,
+ },
+ OPT_CMDMODE_T(0, "allow-empty", resume_type, &resume.mode,
N_("record the empty patch as an empty commit"),
RESUME_ALLOW_EMPTY),
OPT_BOOL(0, "committer-date-is-author-date",
diff --git a/builtin/help.c b/builtin/help.c
index dc1fbe2b98..48437c8636 100644
--- a/builtin/help.c
+++ b/builtin/help.c
@@ -52,6 +52,8 @@ static enum help_action {
HELP_ACTION_CONFIG_SECTIONS_FOR_COMPLETION,
} cmd_mode;
+DEFINE_OPTION_VALUE_TYPE(help_action, enum help_action);
+
static const char *html_path;
static int verbose = 1;
static enum help_format help_format = HELP_FORMAT_NONE;
@@ -59,8 +61,8 @@ static int exclude_guides;
static int show_external_commands = -1;
static int show_aliases = -1;
static struct option builtin_help_options[] = {
- OPT_CMDMODE('a', "all", &cmd_mode, N_("print all available commands"),
- HELP_ACTION_ALL),
+ OPT_CMDMODE_T('a', "all", help_action, &cmd_mode,
+ N_("print all available commands"), HELP_ACTION_ALL),
OPT_BOOL(0, "external-commands", &show_external_commands,
N_("show external commands in --all")),
OPT_BOOL(0, "aliases", &show_aliases, N_("show aliases in --all")),
@@ -72,20 +74,23 @@ static struct option builtin_help_options[] = {
HELP_FORMAT_INFO),
OPT__VERBOSE(&verbose, N_("print command description")),
- OPT_CMDMODE('g', "guides", &cmd_mode, N_("print list of useful guides"),
- HELP_ACTION_GUIDES),
- OPT_CMDMODE(0, "user-interfaces", &cmd_mode,
- N_("print list of user-facing repository, command and file interfaces"),
- HELP_ACTION_USER_INTERFACES),
- OPT_CMDMODE(0, "developer-interfaces", &cmd_mode,
- N_("print list of file formats, protocols and other developer interfaces"),
- HELP_ACTION_DEVELOPER_INTERFACES),
- OPT_CMDMODE('c', "config", &cmd_mode, N_("print all configuration variable names"),
- HELP_ACTION_CONFIG),
- OPT_CMDMODE_F(0, "config-for-completion", &cmd_mode, "",
- HELP_ACTION_CONFIG_FOR_COMPLETION, PARSE_OPT_HIDDEN),
- OPT_CMDMODE_F(0, "config-sections-for-completion", &cmd_mode, "",
- HELP_ACTION_CONFIG_SECTIONS_FOR_COMPLETION, PARSE_OPT_HIDDEN),
+ OPT_CMDMODE_T('g', "guides", help_action, &cmd_mode,
+ N_("print list of useful guides"), HELP_ACTION_GUIDES),
+ OPT_CMDMODE_T(0, "user-interfaces", help_action, &cmd_mode,
+ N_("print list of user-facing repository, command and file interfaces"),
+ HELP_ACTION_USER_INTERFACES),
+ OPT_CMDMODE_T(0, "developer-interfaces", help_action, &cmd_mode,
+ N_("print list of file formats, protocols and other developer interfaces"),
+ HELP_ACTION_DEVELOPER_INTERFACES),
+ OPT_CMDMODE_T('c', "config", help_action, &cmd_mode,
+ N_("print all configuration variable names"),
+ HELP_ACTION_CONFIG),
+ OPT_CMDMODE_T_F(0, "config-for-completion", help_action, &cmd_mode, "",
+ HELP_ACTION_CONFIG_FOR_COMPLETION, PARSE_OPT_HIDDEN),
+ OPT_CMDMODE_T_F(0, "config-sections-for-completion",
+ help_action, &cmd_mode, "",
+ HELP_ACTION_CONFIG_SECTIONS_FOR_COMPLETION,
+ PARSE_OPT_HIDDEN),
OPT_END(),
};
diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c
index 209d2dc0d5..2e7faf510d 100644
--- a/builtin/ls-tree.c
+++ b/builtin/ls-tree.c
@@ -339,6 +339,8 @@ static struct ls_tree_cmdmode_to_fmt ls_tree_cmdmode_format[] = {
},
};
+DEFINE_OPTION_VALUE_TYPE(cmdmode, enum ls_tree_cmdmode);
+
int cmd_ls_tree(int argc, const char **argv, const char *prefix)
{
struct object_id oid;
@@ -358,14 +360,14 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
LS_SHOW_TREES),
OPT_BOOL('z', NULL, &null_termination,
N_("terminate entries with NUL byte")),
- OPT_CMDMODE('l', "long", &cmdmode, N_("include object size"),
- MODE_LONG),
- OPT_CMDMODE(0, "name-only", &cmdmode, N_("list only filenames"),
- MODE_NAME_ONLY),
- OPT_CMDMODE(0, "name-status", &cmdmode, N_("list only filenames"),
- MODE_NAME_STATUS),
- OPT_CMDMODE(0, "object-only", &cmdmode, N_("list only objects"),
- MODE_OBJECT_ONLY),
+ OPT_CMDMODE_T('l', "long", cmdmode, &cmdmode,
+ N_("include object size"), MODE_LONG),
+ OPT_CMDMODE_T(0, "name-only", cmdmode, &cmdmode,
+ N_("list only filenames"), MODE_NAME_ONLY),
+ OPT_CMDMODE_T(0, "name-status", cmdmode, &cmdmode,
+ N_("list only filenames"), MODE_NAME_STATUS),
+ OPT_CMDMODE_T(0, "object-only", cmdmode, &cmdmode,
+ N_("list only objects"), MODE_OBJECT_ONLY),
OPT_BOOL(0, "full-name", &full_name, N_("use full path names")),
OPT_BOOL(0, "full-tree", &full_tree,
N_("list entire tree; not just current directory "
diff --git a/builtin/rebase.c b/builtin/rebase.c
index ed15accec9..50739327bd 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -75,6 +75,8 @@ enum action {
ACTION_SHOW_CURRENT_PATCH
};
+DEFINE_OPTION_VALUE_TYPE(action, enum action);
+
static const char *action_names[] = {
"undefined",
"continue",
@@ -1116,20 +1118,23 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
OPT_BIT(0, "no-ff", &options.flags,
N_("cherry-pick all commits, even if unchanged"),
REBASE_FORCE),
- OPT_CMDMODE(0, "continue", &options.action, N_("continue"),
- ACTION_CONTINUE),
- OPT_CMDMODE(0, "skip", &options.action,
- N_("skip current patch and continue"), ACTION_SKIP),
- OPT_CMDMODE(0, "abort", &options.action,
- N_("abort and check out the original branch"),
- ACTION_ABORT),
- OPT_CMDMODE(0, "quit", &options.action,
- N_("abort but keep HEAD where it is"), ACTION_QUIT),
- OPT_CMDMODE(0, "edit-todo", &options.action, N_("edit the todo list "
- "during an interactive rebase"), ACTION_EDIT_TODO),
- OPT_CMDMODE(0, "show-current-patch", &options.action,
- N_("show the patch file being applied or merged"),
- ACTION_SHOW_CURRENT_PATCH),
+ OPT_CMDMODE_T(0, "continue", action, &options.action,
+ N_("continue"), ACTION_CONTINUE),
+ OPT_CMDMODE_T(0, "skip", action, &options.action,
+ N_("skip current patch and continue"),
+ ACTION_SKIP),
+ OPT_CMDMODE_T(0, "abort", action, &options.action,
+ N_("abort and check out the original branch"),
+ ACTION_ABORT),
+ OPT_CMDMODE_T(0, "quit", action, &options.action,
+ N_("abort but keep HEAD where it is"),
+ ACTION_QUIT),
+ OPT_CMDMODE_T(0, "edit-todo", action, &options.action,
+ N_("edit the todo list during an interactive rebase"),
+ ACTION_EDIT_TODO),
+ OPT_CMDMODE_T(0, "show-current-patch", action, &options.action,
+ N_("show the patch file being applied or merged"),
+ ACTION_SHOW_CURRENT_PATCH),
OPT_CALLBACK_F(0, "apply", &options, NULL,
N_("use apply strategies to rebase"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG,
diff --git a/builtin/replace.c b/builtin/replace.c
index da59600ad2..09aebc9de2 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -539,27 +539,36 @@ static int convert_graft_file(int force)
return -1;
}
+enum cmdmode {
+ MODE_UNSPECIFIED = 0,
+ MODE_LIST,
+ MODE_DELETE,
+ MODE_EDIT,
+ MODE_GRAFT,
+ MODE_CONVERT_GRAFT_FILE,
+ MODE_REPLACE
+};
+
+DEFINE_OPTION_VALUE_TYPE(cmdmode, enum cmdmode);
int cmd_replace(int argc, const char **argv, const char *prefix)
{
int force = 0;
int raw = 0;
const char *format = NULL;
- enum {
- MODE_UNSPECIFIED = 0,
- MODE_LIST,
- MODE_DELETE,
- MODE_EDIT,
- MODE_GRAFT,
- MODE_CONVERT_GRAFT_FILE,
- MODE_REPLACE
- } cmdmode = MODE_UNSPECIFIED;
+ enum cmdmode cmdmode = MODE_UNSPECIFIED;
struct option options[] = {
- OPT_CMDMODE('l', "list", &cmdmode, N_("list replace refs"), MODE_LIST),
- OPT_CMDMODE('d', "delete", &cmdmode, N_("delete replace refs"), MODE_DELETE),
- OPT_CMDMODE('e', "edit", &cmdmode, N_("edit existing object"), MODE_EDIT),
- OPT_CMDMODE('g', "graft", &cmdmode, N_("change a commit's parents"), MODE_GRAFT),
- OPT_CMDMODE(0, "convert-graft-file", &cmdmode, N_("convert existing graft file"), MODE_CONVERT_GRAFT_FILE),
+ OPT_CMDMODE_T('l', "list", cmdmode, &cmdmode,
+ N_("list replace refs"), MODE_LIST),
+ OPT_CMDMODE_T('d', "delete", cmdmode, &cmdmode,
+ N_("delete replace refs"), MODE_DELETE),
+ OPT_CMDMODE_T('e', "edit", cmdmode, &cmdmode,
+ N_("edit existing object"), MODE_EDIT),
+ OPT_CMDMODE_T('g', "graft", cmdmode, &cmdmode,
+ N_("change a commit's parents"), MODE_GRAFT),
+ OPT_CMDMODE_T(0, "convert-graft-file", cmdmode, &cmdmode,
+ N_("convert existing graft file"),
+ MODE_CONVERT_GRAFT_FILE),
OPT_BOOL_F('f', "force", &force, N_("replace the ref if it exists"),
PARSE_OPT_NOCOMPLETE),
OPT_BOOL(0, "raw", &raw, N_("do not pretty-print contents for --edit")),
diff --git a/builtin/stripspace.c b/builtin/stripspace.c
index 7b700a9fb1..82b9a95a4f 100644
--- a/builtin/stripspace.c
+++ b/builtin/stripspace.c
@@ -29,6 +29,8 @@ enum stripspace_mode {
COMMENT_LINES
};
+DEFINE_OPTION_VALUE_TYPE(mode, enum stripspace_mode);
+
int cmd_stripspace(int argc, const char **argv, const char *prefix)
{
struct strbuf buf = STRBUF_INIT;
@@ -36,12 +38,12 @@ int cmd_stripspace(int argc, const char **argv, const char *prefix)
int nongit;
const struct option options[] = {
- OPT_CMDMODE('s', "strip-comments", &mode,
- N_("skip and remove all lines starting with comment character"),
- STRIP_COMMENTS),
- OPT_CMDMODE('c', "comment-lines", &mode,
- N_("prepend comment character and space to each line"),
- COMMENT_LINES),
+ OPT_CMDMODE_T('s', "strip-comments", mode, &mode,
+ N_("skip and remove all lines starting with comment character"),
+ STRIP_COMMENTS),
+ OPT_CMDMODE_T('c', "comment-lines", mode, &mode,
+ N_("prepend comment character and space to each line"),
+ COMMENT_LINES),
OPT_END()
};
diff --git a/parse-options.c b/parse-options.c
index e8e076c3a6..63a2247128 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -85,7 +85,7 @@ static enum parse_opt_result opt_command_mode_error(
if (that == opt ||
!(that->flags & PARSE_OPT_CMDMODE) ||
that->value != opt->value ||
- that->defval != *(int *)opt->value)
+ that->defval != opt->get_value(opt->value))
continue;
if (that->long_name)
@@ -122,7 +122,8 @@ static enum parse_opt_result get_value(struct parse_opt_ctx_t *p,
* is not a grave error, so let it pass.
*/
if ((opt->flags & PARSE_OPT_CMDMODE) &&
- *(int *)opt->value && *(int *)opt->value != opt->defval)
+ opt->get_value(opt->value) &&
+ opt->get_value(opt->value) != opt->defval)
return opt_command_mode_error(opt, all_opts, flags);
switch (opt->type) {
@@ -160,6 +161,10 @@ static enum parse_opt_result get_value(struct parse_opt_ctx_t *p,
*(int *)opt->value = unset ? 0 : opt->defval;
return 0;
+ case OPTION_SET_VALUE:
+ opt->set_value(opt->value, unset ? 0 : opt->defval);
+ return 0;
+
case OPTION_STRING:
if (unset)
*(const char **)opt->value = NULL;
@@ -483,11 +488,21 @@ static void parse_options_check(const struct option *opts)
if (opts->type == OPTION_SET_INT && !opts->defval &&
opts->long_name && !(opts->flags & PARSE_OPT_NONEG))
optbug(opts, "OPTION_SET_INT 0 should not be negatable");
+ if (opts->type == OPTION_SET_VALUE && !opts->defval &&
+ opts->long_name && !(opts->flags & PARSE_OPT_NONEG))
+ optbug(opts, "OPTION_SET_VALUE 0 should not be negatable");
+ if (opts->type == OPTION_SET_VALUE &&
+ (!opts->get_value || !opts->set_value))
+ optbug(opts, "OPTION_SET_VALUE requires accessors");
+ if ((opts->flags & PARSE_OPT_CMDMODE) &&
+ (!opts->get_value || !opts->set_value))
+ optbug(opts, "PARSE_OPT_CMDMODE requires accessors");
switch (opts->type) {
case OPTION_COUNTUP:
case OPTION_BIT:
case OPTION_NEGBIT:
case OPTION_SET_INT:
+ case OPTION_SET_VALUE:
case OPTION_NUMBER:
if ((opts->flags & PARSE_OPT_OPTARG) ||
!(opts->flags & PARSE_OPT_NOARG))
@@ -611,6 +626,7 @@ static void show_negated_gitcomp(const struct option *opts, int show_all,
case OPTION_NEGBIT:
case OPTION_COUNTUP:
case OPTION_SET_INT:
+ case OPTION_SET_VALUE:
has_unset_form = 1;
break;
default:
diff --git a/parse-options.h b/parse-options.h
index 57a7fe9d91..764e7f7896 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -20,6 +20,7 @@ enum parse_opt_type {
OPTION_BITOP,
OPTION_COUNTUP,
OPTION_SET_INT,
+ OPTION_SET_VALUE,
/* options with arguments (usually) */
OPTION_STRING,
OPTION_INTEGER,
@@ -158,8 +159,34 @@ struct option {
parse_opt_ll_cb *ll_callback;
intptr_t extra;
parse_opt_subcommand_fn *subcommand_fn;
+ intptr_t (*get_value)(void *);
+ void (*set_value)(void *, intptr_t);
};
+#define DEFINE_OPTION_VALUE_TYPE(type_name, type) \
+static inline intptr_t type_name##__get(void *void_ptr) \
+{ \
+ type *ptr = void_ptr; \
+ return (intptr_t)*ptr; \
+} \
+static inline void type_name##__set(void *void_ptr, intptr_t value) \
+{ \
+ type *ptr = void_ptr; \
+ *ptr = (type)value; \
+} \
+static inline void *type_name##__check(type *ptr) \
+{ \
+ return ptr; \
+} \
+static inline void *type_name##__check(type *ptr)
+
+DEFINE_OPTION_VALUE_TYPE(int, int);
+
+#define OPTION_VALUE(type_name, v) \
+ .get_value = type_name##__get, \
+ .set_value = type_name##__set, \
+ .value = (1 ? (v) : type_name##__check(v))
+
#define OPT_BIT_F(s, l, v, h, b, f) { \
.type = OPTION_BIT, \
.short_name = (s), \
@@ -256,15 +283,18 @@ struct option {
.flags = PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, \
.defval = 1, \
}
-#define OPT_CMDMODE_F(s, l, v, h, i, f) { \
- .type = OPTION_SET_INT, \
+
+#define OPT_CMDMODE_T_F(s, l, t, v, h, i, f) { \
+ .type = OPTION_SET_VALUE, \
.short_name = (s), \
.long_name = (l), \
- .value = (v), \
+ OPTION_VALUE(t, (v)), \
.help = (h), \
.flags = PARSE_OPT_CMDMODE|PARSE_OPT_NOARG|PARSE_OPT_NONEG | (f), \
.defval = (i), \
}
+#define OPT_CMDMODE_T(s, l, t, v, h, i) OPT_CMDMODE_T_F(s, l, t, v, h, i, 0)
+#define OPT_CMDMODE_F(s, l, v, h, i, f) OPT_CMDMODE_T_F(s, l, int, v, h, i, f)
#define OPT_CMDMODE(s, l, v, h, i) OPT_CMDMODE_F(s, l, v, h, i, 0)
#define OPT_INTEGER(s, l, v, h) OPT_INTEGER_F(s, l, v, h, 0)
--
2.42.0