From d5d202537f2cfec41304b559624b4cc0a2d8c9fe Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 5 Nov 2018 01:38:19 -0500 Subject: [PATCH 01/14] apply: mark include/exclude options as NONEG The options callback for "git apply --no-include" is not ready to handle the "unset" parameter, and as a result will segfault when it adds a NULL argument to the include list (likewise for "--no-exclude"). In theory this might be used to clear the list, but since both "--include" and "--exclude" add to the same list, it's not immediately obvious what the semantics should be. Let's punt on that for now and just disallow the broken options. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- apply.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apply.c b/apply.c index 073d5f0451..d1ca6addeb 100644 --- a/apply.c +++ b/apply.c @@ -4939,10 +4939,10 @@ int apply_parse_options(int argc, const char **argv, struct option builtin_apply_options[] = { { OPTION_CALLBACK, 0, "exclude", state, N_("path"), N_("don't apply changes matching the given path"), - 0, apply_option_parse_exclude }, + PARSE_OPT_NONEG, apply_option_parse_exclude }, { OPTION_CALLBACK, 0, "include", state, N_("path"), N_("apply changes matching the given path"), - 0, apply_option_parse_include }, + PARSE_OPT_NONEG, apply_option_parse_include }, { OPTION_CALLBACK, 'p', NULL, state, N_("num"), N_("remove leading slashes from traditional diff paths"), 0, apply_option_parse_p }, From fce56648056acf476aa810a02c6eb33cbcabb6c9 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 5 Nov 2018 01:38:39 -0500 Subject: [PATCH 02/14] am: handle --no-patch-format option Running "git am --no-patch-format" will currently segfault, since it tries to parse a NULL argument. Instead, let's have it cancel any previous --patch-format option. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/am.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/builtin/am.c b/builtin/am.c index 3ee9a9d2a9..dcb880b699 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -2165,7 +2165,9 @@ static int parse_opt_patchformat(const struct option *opt, const char *arg, int { int *opt_value = opt->value; - if (!strcmp(arg, "mbox")) + if (unset) + *opt_value = PATCH_FORMAT_UNKNOWN; + else if (!strcmp(arg, "mbox")) *opt_value = PATCH_FORMAT_MBOX; else if (!strcmp(arg, "stgit")) *opt_value = PATCH_FORMAT_STGIT; From ccf659e87c8fdb4edb5d2653c53bc9062c8eee76 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 5 Nov 2018 01:39:20 -0500 Subject: [PATCH 03/14] ls-files: mark exclude options as NONEG Running "git ls-files --no-exclude" will currently segfault, as its option callback does not handle the "unset" parameter. In theory this could be used to clear the exclude list, but it is not clear how that would interact with the other exclude options, nor is the current code capable of clearing the list. Let's just disable the broken option. Note that --no-exclude-from will similarly segfault, but --no-exclude-standard will not. It just silently does the wrong thing (pretending as if --exclude-standard was specified). Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/ls-files.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/builtin/ls-files.c b/builtin/ls-files.c index 7f9919a362..2787453eb1 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -548,15 +548,16 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) N_("show resolve-undo information")), { OPTION_CALLBACK, 'x', "exclude", &exclude_list, N_("pattern"), N_("skip files matching pattern"), - 0, option_parse_exclude }, + PARSE_OPT_NONEG, option_parse_exclude }, { OPTION_CALLBACK, 'X', "exclude-from", &dir, N_("file"), N_("exclude patterns are read from "), - 0, option_parse_exclude_from }, + PARSE_OPT_NONEG, option_parse_exclude_from }, OPT_STRING(0, "exclude-per-directory", &dir.exclude_per_dir, N_("file"), N_("read additional per-directory exclude patterns in ")), { OPTION_CALLBACK, 0, "exclude-standard", &dir, NULL, N_("add the standard git exclusions"), - PARSE_OPT_NOARG, option_parse_exclude_standard }, + PARSE_OPT_NOARG | PARSE_OPT_NONEG, + option_parse_exclude_standard }, OPT_SET_INT_F(0, "full-name", &prefix_len, N_("make the output relative to the project top directory"), 0, PARSE_OPT_NONEG), From f53c163bcd7ae9bf83aa87be81dc6a4661924f3e Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 5 Nov 2018 01:39:38 -0500 Subject: [PATCH 04/14] pack-objects: mark index-version option as NONEG Running "git pack-objects --no-index-version" will segfault, since the callback is not prepared to handle the "unset" flag. In theory this might be used to counteract an earlier "--index-version", or override a pack.indexversion config setting. But the semantics aren't immediately obvious, and it's unlikely anybody wants this. Let's just disable the broken option for now. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/pack-objects.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index e50c6cd1ff..4eac2f997b 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -3252,7 +3252,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) N_("similar to --all-progress when progress meter is shown")), { OPTION_CALLBACK, 0, "index-version", NULL, N_("[,]"), N_("write the pack index file in the specified idx format version"), - 0, option_parse_index_version }, + PARSE_OPT_NONEG, option_parse_index_version }, OPT_MAGNITUDE(0, "max-pack-size", &pack_size_limit, N_("maximum size of each output pack file")), OPT_BOOL(0, "local", &local, From 0ad1efb4b379b4d47166c04b3103c6ead3a79673 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 5 Nov 2018 01:40:10 -0500 Subject: [PATCH 05/14] cat-file: mark batch options with NONEG Running "cat-file --no-batch" will behave as if "--batch" was given, since the option callback does not handle the "unset" flag (likewise for "--no-batch-check"). In theory this might be used to cancel an earlier --batch, but it's not immediately obvious how that would interact with --batch-check. Let's just disallow the negated form of both options. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/cat-file.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/builtin/cat-file.c b/builtin/cat-file.c index 8d97c84725..4a5289079c 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -631,10 +631,12 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix) OPT_BOOL(0, "buffer", &batch.buffer_output, N_("buffer --batch output")), { OPTION_CALLBACK, 0, "batch", &batch, "format", N_("show info and content of objects fed from the standard input"), - PARSE_OPT_OPTARG, batch_option_callback }, + PARSE_OPT_OPTARG | PARSE_OPT_NONEG, + batch_option_callback }, { OPTION_CALLBACK, 0, "batch-check", &batch, "format", N_("show info about objects fed from the standard input"), - PARSE_OPT_OPTARG, batch_option_callback }, + PARSE_OPT_OPTARG | PARSE_OPT_NONEG, + batch_option_callback }, OPT_BOOL(0, "follow-symlinks", &batch.follow_symlinks, N_("follow in-tree symlinks (used with --batch or --batch-check)")), OPT_BOOL(0, "batch-all-objects", &batch.all_objects, From d6627fb8999ac7275c1eeb42482dabd4fabfbd61 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 5 Nov 2018 01:40:53 -0500 Subject: [PATCH 06/14] status: mark --find-renames option with NONEG If you run "git status --no-find-renames", it will behave the same as "--find-renames", because we ignore the "unset" parameter (we see a NULL "arg", but since the score argument is optional, we just think that the user did not provide a score). We already have a separate "--no-renames" to disable renames, so there's not much point in supporting "--no-find-renames". Let's just flag it as an error. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/commit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/commit.c b/builtin/commit.c index 074bd9a551..4d7754ca43 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -1335,7 +1335,7 @@ int cmd_status(int argc, const char **argv, const char *prefix) OPT_BOOL(0, "no-renames", &no_renames, N_("do not detect renames")), { OPTION_CALLBACK, 'M', "find-renames", &rename_score_arg, N_("n"), N_("detect renames, optionally set similarity index"), - PARSE_OPT_OPTARG, opt_parse_rename_score }, + PARSE_OPT_OPTARG | PARSE_OPT_NONEG, opt_parse_rename_score }, OPT_END(), }; From 964fd83b12cbf7907f0bdb4671a467fe381487d3 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 5 Nov 2018 01:41:12 -0500 Subject: [PATCH 07/14] format-patch: mark "--no-numbered" option with NONEG We have separate parse-options entries for "numbered" and "no-numbered", which means that we accept "--no-no-numbered". It does not behave sensibly, though (it ignores the "unset" flag and acts like "--no-numbered"). We could fix that, but obviously this is silly and unintentional. Let's just disallow it. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/log.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/log.c b/builtin/log.c index 061d4fd864..41188e723c 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -1508,7 +1508,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) PARSE_OPT_NOARG, numbered_callback }, { OPTION_CALLBACK, 'N', "no-numbered", &numbered, NULL, N_("use [PATCH] even with multiple patches"), - PARSE_OPT_NOARG, no_numbered_callback }, + PARSE_OPT_NOARG | PARSE_OPT_NONEG, no_numbered_callback }, OPT_BOOL('s', "signoff", &do_signoff, N_("add Signed-off-by:")), OPT_BOOL(0, "stdout", &use_stdout, N_("print patches to standard out")), From 403d2ba52cb63d4bf5a8baf5480efc538f405c09 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 5 Nov 2018 01:42:40 -0500 Subject: [PATCH 08/14] show-branch: mark --reflog option as NONEG Running "git show-branch --no-reflog" will behave as if "--reflog" was given with no options, which makes no sense. In theory this option might be used to cancel an earlier "--reflog" option, but the semantics are not clear. Let's punt on it and just disallow the broken option. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/show-branch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/show-branch.c b/builtin/show-branch.c index 65f4a4c83c..b1b540f7f6 100644 --- a/builtin/show-branch.c +++ b/builtin/show-branch.c @@ -674,7 +674,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) { OPTION_CALLBACK, 'g', "reflog", &reflog_base, N_("[,]"), N_("show most recent ref-log entries starting at " "base"), - PARSE_OPT_OPTARG, + PARSE_OPT_OPTARG | PARSE_OPT_NONEG, parse_reflog_param }, OPT_END() }; From 1f5db32d89b62317cf59486d14e8b2d4d004cd1e Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 5 Nov 2018 01:43:12 -0500 Subject: [PATCH 09/14] tag: mark "--message" option with NONEG We do not allow "--no-message" to work now, as the option callback returns "-1" when it sees a NULL arg. However, that will cause parse-options to exit(129) without printing anything further, leaving the user confused about what happened. Instead, let's explicitly mark it as PARSE_OPT_NONEG, which will give a useful error message (and print the usual -h output). In theory this could be used to override an earlier "-m", but it's not clear how it would interact with other message options (e.g., would it also clear data read for "-F"?). Since it's already disabled and nobody is asking for it, let's punt on that and just improve the error message. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/tag.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builtin/tag.c b/builtin/tag.c index f623632186..6a396a5090 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -390,8 +390,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix) OPT_GROUP(N_("Tag creation options")), OPT_BOOL('a', "annotate", &annotate, N_("annotated tag, needs a message")), - OPT_CALLBACK('m', "message", &msg, N_("message"), - N_("tag message"), parse_msg_arg), + { OPTION_CALLBACK, 'm', "message", &msg, N_("message"), + N_("tag message"), PARSE_OPT_NONEG, parse_msg_arg }, OPT_FILENAME('F', "file", &msgfile, N_("read message from file")), OPT_BOOL('e', "edit", &edit_flag, N_("force edit of tag message")), OPT_BOOL('s', "sign", &opt.sign, N_("annotated and GPG-signed tag")), From 0eb8d3767c8f5c87f3bdcbbf9d3d5f73c39422d9 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 5 Nov 2018 01:43:44 -0500 Subject: [PATCH 10/14] cat-file: report an error on multiple --batch options The options callback for --batch and --batch-check detects when the two mutually incompatible options are used. But it simply returns an error code to parse-options, meaning the program will quit without any kind of message to the user. Instead, let's use error() to print something and return -1. Note that this flips the error return from 1 to -1, but negative values are more idiomatic here (and parse-options treats them the same). Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/cat-file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/cat-file.c b/builtin/cat-file.c index 4a5289079c..0f6b692df6 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -596,7 +596,7 @@ static int batch_option_callback(const struct option *opt, struct batch_options *bo = opt->value; if (bo->enabled) { - return 1; + return error(_("only one batch option may be specified")); } bo->enabled = 1; From 735ca208c5463ebbb0991ed02f41b1e30f9dddc1 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 5 Nov 2018 01:43:59 -0500 Subject: [PATCH 11/14] apply: return -1 from option callback instead of calling exit(1) The option callback for "apply --whitespace" exits with status "1" on error. It makes more sense for it to just return an error to parse-options. That code will exit, too, but it will use status "129" that is customary for option errors. The exit() dates back to aaf6c447aa (builtin/apply: make parse_whitespace_option() return -1 instead of die()ing, 2016-08-08). That commit gives no reason why we'd prefer the current exit status (it looks like it was just bumping the "die" up a level in the callstack, but did not go as far as it could have). Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- apply.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apply.c b/apply.c index d1ca6addeb..c4c9f849b1 100644 --- a/apply.c +++ b/apply.c @@ -4812,7 +4812,7 @@ static int apply_option_parse_whitespace(const struct option *opt, struct apply_state *state = opt->value; state->whitespace_option = arg; if (parse_whitespace_option(state, arg)) - exit(1); + return -1; return 0; } From 0a8a16ade6b3a55114cd0f28e5e71c2a3483d825 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 5 Nov 2018 01:44:27 -0500 Subject: [PATCH 12/14] parse-options: drop OPT_DATE() There are no users of OPT_DATE except for test-parse-options; its only caller went away in 27ec394a97 (prune: introduce OPT_EXPIRY_DATE() and use it, 2013-04-25). It also has a bug: it does not specify PARSE_OPT_NONEG, but its callback does not respect the "unset" flag, and will feed NULL to approxidate() and segfault. Probably this should be marked with NONEG, or the callback should set the timestamp to some sentinel value (e.g,. "0", or "(time_t)-1"). But since there are no callers, deleting it means we don't even have to think about what the right behavior should be. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- Documentation/technical/api-parse-options.txt | 4 ---- parse-options-cb.c | 7 ------ parse-options.h | 4 ---- t/helper/test-parse-options.c | 1 - t/t0040-parse-options.sh | 22 ------------------- 5 files changed, 38 deletions(-) diff --git a/Documentation/technical/api-parse-options.txt b/Documentation/technical/api-parse-options.txt index 829b558110..2b036d7838 100644 --- a/Documentation/technical/api-parse-options.txt +++ b/Documentation/technical/api-parse-options.txt @@ -183,10 +183,6 @@ There are some macros to easily define options: scale the provided value by 1024, 1024^2 or 1024^3 respectively. The scaled value is put into `unsigned_long_var`. -`OPT_DATE(short, long, ×tamp_t_var, description)`:: - Introduce an option with date argument, see `approxidate()`. - The timestamp is put into `timestamp_t_var`. - `OPT_EXPIRY_DATE(short, long, ×tamp_t_var, description)`:: Introduce an option with expiry date argument, see `parse_expiry_date()`. The timestamp is put into `timestamp_t_var`. diff --git a/parse-options-cb.c b/parse-options-cb.c index e8236534ac..6a61166b26 100644 --- a/parse-options-cb.c +++ b/parse-options-cb.c @@ -28,13 +28,6 @@ int parse_opt_abbrev_cb(const struct option *opt, const char *arg, int unset) return 0; } -int parse_opt_approxidate_cb(const struct option *opt, const char *arg, - int unset) -{ - *(timestamp_t *)(opt->value) = approxidate(arg); - return 0; -} - int parse_opt_expiry_date_cb(const struct option *opt, const char *arg, int unset) { diff --git a/parse-options.h b/parse-options.h index dd14911a29..c3f2d2eceb 100644 --- a/parse-options.h +++ b/parse-options.h @@ -150,9 +150,6 @@ struct option { (h), 0, &parse_opt_string_list } #define OPT_UYN(s, l, v, h) { OPTION_CALLBACK, (s), (l), (v), NULL, \ (h), PARSE_OPT_NOARG, &parse_opt_tertiary } -#define OPT_DATE(s, l, v, h) \ - { OPTION_CALLBACK, (s), (l), (v), N_("time"),(h), 0, \ - parse_opt_approxidate_cb } #define OPT_EXPIRY_DATE(s, l, v, h) \ { OPTION_CALLBACK, (s), (l), (v), N_("expiry-date"),(h), 0, \ parse_opt_expiry_date_cb } @@ -232,7 +229,6 @@ extern struct option *parse_options_concat(struct option *a, struct option *b); /*----- some often used options -----*/ extern int parse_opt_abbrev_cb(const struct option *, const char *, int); -extern int parse_opt_approxidate_cb(const struct option *, const char *, int); extern int parse_opt_expiry_date_cb(const struct option *, const char *, int); extern int parse_opt_color_flag_cb(const struct option *, const char *, int); extern int parse_opt_verbosity_cb(const struct option *, const char *, int); diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c index 9cb8a0ea0f..f0623bb42b 100644 --- a/t/helper/test-parse-options.c +++ b/t/helper/test-parse-options.c @@ -119,7 +119,6 @@ int cmd__parse_options(int argc, const char **argv) OPT_INTEGER('j', NULL, &integer, "get a integer, too"), OPT_MAGNITUDE('m', "magnitude", &magnitude, "get a magnitude"), OPT_SET_INT(0, "set23", &integer, "set integer to 23", 23), - OPT_DATE('t', NULL, ×tamp, "get timestamp of