diff options
author | Junio C Hamano <gitster@pobox.com> | 2025-04-24 17:25:33 -0700 |
---|---|---|
committer | Junio C Hamano <gitster@pobox.com> | 2025-04-24 17:25:34 -0700 |
commit | 2bc5414c411aab33c155b1070b7764ef6a49a02d (patch) | |
tree | 3f2b065f7c9c54838ab380ba5d16e7a9f742344b | |
parent | 68e5342e191a2de216dbf712a6dbfa49282429c4 (diff) | |
parent | 791aeddfa2fdb9e830e24c50c97bb5e8bf3613e6 (diff) |
Merge branch 'ps/parse-options-integers'
Update parse-options API to catch mistakes to pass address of an
integral variable of a wrong type/size.
* ps/parse-options-integers:
parse-options: detect mismatches in integer signedness
parse-options: introduce precision handling for `OPTION_UNSIGNED`
parse-options: introduce precision handling for `OPTION_INTEGER`
parse-options: rename `OPT_MAGNITUDE()` to `OPT_UNSIGNED()`
parse-options: support unit factors in `OPT_INTEGER()`
global: use designated initializers for options
parse: fix off-by-one for minimum signed values
37 files changed, 649 insertions, 245 deletions
diff --git a/Documentation/technical/api-parse-options.adoc b/Documentation/technical/api-parse-options.adoc index 61fa6ee167..880eb94642 100644 --- a/Documentation/technical/api-parse-options.adoc +++ b/Documentation/technical/api-parse-options.adoc @@ -211,11 +211,13 @@ There are some macros to easily define options: Use of `--no-option` will clear the list of preceding values. `OPT_INTEGER(short, long, &int_var, description)`:: - Introduce an option with integer argument. - The integer is put into `int_var`. + Introduce an option with integer argument. The argument must be a + integer and may include a suffix of 'k', 'm' or 'g' to + scale the provided value by 1024, 1024^2 or 1024^3 respectively. + The scaled value is put into `int_var`. -`OPT_MAGNITUDE(short, long, &unsigned_long_var, description)`:: - Introduce an option with a size argument. The argument must be a +`OPT_UNSIGNED(short, long, &unsigned_long_var, description)`:: + Introduce an option with an unsigned integer argument. The argument must be a non-negative integer and may include a suffix of 'k', 'm' or 'g' to scale the provided value by 1024, 1024^2 or 1024^3 respectively. The scaled value is put into `unsigned_long_var`. @@ -5123,8 +5123,8 @@ int apply_parse_options(int argc, const char **argv, /* Think twice before adding "--nul" synonym to this */ OPT_SET_INT('z', NULL, &state->line_termination, N_("paths are separated with NUL character"), '\0'), - OPT_INTEGER('C', NULL, &state->p_context, - N_("ensure at least <n> lines of context match")), + OPT_UNSIGNED('C', NULL, &state->p_context, + N_("ensure at least <n> lines of context match")), OPT_CALLBACK(0, "whitespace", state, N_("action"), N_("detect new or modified lines that have whitespace errors"), apply_option_parse_whitespace), @@ -650,20 +650,37 @@ static int parse_archive_args(int argc, const char **argv, OPT_STRING(0, "format", &format, N_("fmt"), N_("archive format")), OPT_STRING(0, "prefix", &base, N_("prefix"), N_("prepend prefix to each pathname in the archive")), - { OPTION_CALLBACK, 0, "add-file", args, N_("file"), - N_("add untracked file to archive"), 0, add_file_cb, - (intptr_t)&base }, - { OPTION_CALLBACK, 0, "add-virtual-file", args, - N_("path:content"), N_("add untracked file to archive"), 0, - add_file_cb, (intptr_t)&base }, + { + .type = OPTION_CALLBACK, + .long_name = "add-file", + .value = args, + .argh = N_("file"), + .help = N_("add untracked file to archive"), + .callback = add_file_cb, + .defval = (intptr_t) &base, + }, + { + .type = OPTION_CALLBACK, + .long_name = "add-virtual-file", + .value = args, + .argh = N_("path:content"), + .help = N_("add untracked file to archive"), + .callback = add_file_cb, + .defval = (intptr_t) &base, + }, OPT_STRING('o', "output", &output, N_("file"), N_("write the archive to this file")), OPT_BOOL(0, "worktree-attributes", &worktree_attributes, N_("read .gitattributes in working directory")), OPT__VERBOSE(&verbose, N_("report archived files on stderr")), - { OPTION_STRING, 0, "mtime", &mtime_option, N_("time"), - N_("set modification time of archive entries"), - PARSE_OPT_NONEG }, + { + .type = OPTION_STRING, + .long_name = "mtime", + .value = &mtime_option, + .argh = N_("time"), + .help = N_("set modification time of archive entries"), + .flags = PARSE_OPT_NONEG, + }, OPT_NUMBER_CALLBACK(&compression_level, N_("set compression level"), number_callback), OPT_GROUP(""), diff --git a/builtin/am.c b/builtin/am.c index 3b61bd4c33..4afb519830 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -2400,11 +2400,16 @@ int cmd_am(int argc, OPT_CMDMODE(0, "quit", &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_RAW }, + { + .type = OPTION_CALLBACK, + .long_name = "show-current-patch", + .value = &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_RAW, + }, OPT_CMDMODE(0, "retry", &resume_mode, N_("try to apply current patch again"), RESUME_APPLY), @@ -2417,9 +2422,16 @@ int cmd_am(int argc, OPT_BOOL(0, "ignore-date", &state.ignore_date, N_("use current timestamp for author date")), OPT_RERERE_AUTOUPDATE(&state.allow_rerere_autoupdate), - { OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"), - N_("GPG-sign commits"), - PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, + { + .type = OPTION_STRING, + .short_name = 'S', + .long_name = "gpg-sign", + .value = &state.sign_commit, + .argh = N_("key-id"), + .help = N_("GPG-sign commits"), + .flags = PARSE_OPT_OPTARG, + .defval = (intptr_t) "", + }, OPT_CALLBACK_F(0, "empty", &state.empty_type, "(stop|drop|keep)", N_("how to handle empty patches"), PARSE_OPT_NONEG, am_option_parse_empty), diff --git a/builtin/backfill.c b/builtin/backfill.c index aaa104bc91..fa82ad2f6f 100644 --- a/builtin/backfill.c +++ b/builtin/backfill.c @@ -123,8 +123,8 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit .sparse = 0, }; struct option options[] = { - OPT_INTEGER(0, "min-batch-size", &ctx.min_batch_size, - N_("Minimum number of objects to request at a time")), + OPT_UNSIGNED(0, "min-batch-size", &ctx.min_batch_size, + N_("Minimum number of objects to request at a time")), OPT_BOOL(0, "sparse", &ctx.sparse, N_("Restrict the missing objects to the current sparse-checkout")), OPT_END(), diff --git a/builtin/clone.c b/builtin/clone.c index 6b1d11a3ed..b6f378c438 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -932,9 +932,16 @@ int cmd_clone(int argc, N_("don't use local hardlinks, always copy")), OPT_BOOL('s', "shared", &option_shared, N_("setup as shared repository")), - { OPTION_CALLBACK, 0, "recurse-submodules", &option_recurse_submodules, - N_("pathspec"), N_("initialize submodules in the clone"), - PARSE_OPT_OPTARG, recurse_submodules_cb, (intptr_t)"." }, + { + .type = OPTION_CALLBACK, + .long_name = "recurse-submodules", + .value = &option_recurse_submodules, + .argh = N_("pathspec"), + .help = N_("initialize submodules in the clone"), + .flags = PARSE_OPT_OPTARG, + .callback = recurse_submodules_cb, + .defval = (intptr_t)".", + }, OPT_ALIAS(0, "recursive", "recurse-submodules"), OPT_INTEGER('j', "jobs", &max_jobs, N_("number of submodules cloned in parallel")), diff --git a/builtin/column.c b/builtin/column.c index 50314cc255..ce6443d5fa 100644 --- a/builtin/column.c +++ b/builtin/column.c @@ -31,7 +31,7 @@ int cmd_column(int argc, struct option options[] = { OPT_STRING(0, "command", &real_command, N_("name"), N_("lookup config vars")), OPT_COLUMN(0, "mode", &colopts, N_("layout to use")), - OPT_INTEGER(0, "raw-mode", &colopts, N_("layout to use")), + OPT_UNSIGNED(0, "raw-mode", &colopts, N_("layout to use")), OPT_INTEGER(0, "width", &copts.width, N_("maximum width")), OPT_STRING(0, "indent", &copts.indent, N_("string"), N_("padding space on left border")), OPT_STRING(0, "nl", &copts.nl, N_("string"), N_("padding space on right border")), diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c index 6f9975e7a8..ad6b2c9320 100644 --- a/builtin/commit-tree.c +++ b/builtin/commit-tree.c @@ -111,8 +111,16 @@ int cmd_commit_tree(int argc, OPT_CALLBACK_F('F', NULL, &buffer, N_("file"), N_("read commit log message from file"), PARSE_OPT_NONEG, parse_file_arg_callback), - { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"), - N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, + { + .type = OPTION_STRING, + .short_name = 'S', + .long_name = "gpg-sign", + .value = &sign_commit, + .argh = N_("key-id"), + .help = N_("GPG sign commit"), + .flags = PARSE_OPT_OPTARG, + .defval = (intptr_t) "", + }, OPT_END() }; int ret; diff --git a/builtin/commit.c b/builtin/commit.c index 2f45968222..66bd91fd52 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -1542,17 +1542,34 @@ struct repository *repo UNUSED) STATUS_FORMAT_LONG), OPT_BOOL('z', "null", &s.null_termination, N_("terminate entries with NUL")), - { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, - N_("mode"), - N_("show untracked files, optional modes: all, normal, no. (Default: all)"), - PARSE_OPT_OPTARG, NULL, (intptr_t)"all" }, - { OPTION_STRING, 0, "ignored", &ignored_arg, - N_("mode"), - N_("show ignored files, optional modes: traditional, matching, no. (Default: traditional)"), - PARSE_OPT_OPTARG, NULL, (intptr_t)"traditional" }, - { OPTION_STRING, 0, "ignore-submodules", &ignore_submodule_arg, N_("when"), - N_("ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)"), - PARSE_OPT_OPTARG, NULL, (intptr_t)"all" }, + { + .type = OPTION_STRING, + .short_name = 'u', + .long_name = "untracked-files", + .value = &untracked_files_arg, + .argh = N_("mode"), + .help = N_("show untracked files, optional modes: all, normal, no. (Default: all)"), + .flags = PARSE_OPT_OPTARG, + .defval = (intptr_t)"all", + }, + { + .type = OPTION_STRING, + .long_name = "ignored", + .value = &ignored_arg, + .argh = N_("mode"), + .help = N_("show ignored files, optional modes: traditional, matching, no. (Default: traditional)"), + .flags = PARSE_OPT_OPTARG, + .defval = (intptr_t)"traditional", + }, + { + .type = OPTION_STRING, + .long_name = "ignore-submodules", + .value = &ignore_submodule_arg, + .argh = N_("when"), + .help = N_("ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)"), + .flags = PARSE_OPT_OPTARG, + .defval = (intptr_t)"all", + }, OPT_COLUMN(0, "column", &s.colopts, N_("list untracked files in columns")), OPT_BOOL(0, "no-renames", &no_renames, N_("do not detect renames")), OPT_CALLBACK_F('M', "find-renames", &rename_score_arg, @@ -1688,8 +1705,16 @@ int cmd_commit(int argc, OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")), OPT_CLEANUP(&cleanup_arg), OPT_BOOL(0, "status", &include_status, N_("include status in commit message template")), - { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"), - N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, + { + .type = OPTION_STRING, + .short_name = 'S', + .long_name = "gpg-sign", + .value = &sign_commit, + .argh = N_("key-id"), + .help = N_("GPG sign commit"), + .flags = PARSE_OPT_OPTARG, + .defval = (intptr_t) "", + }, /* end commit message options */ OPT_GROUP(N_("Commit contents options")), @@ -1714,7 +1739,16 @@ int cmd_commit(int argc, N_("terminate entries with NUL")), OPT_BOOL(0, "amend", &amend, N_("amend previous commit")), OPT_BOOL(0, "no-post-rewrite", &no_post_rewrite, N_("bypass post-rewrite hook")), - { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, N_("mode"), N_("show untracked files, optional modes: all, normal, no. (Default: all)"), PARSE_OPT_OPTARG, NULL, (intptr_t)"all" }, + { + .type = OPTION_STRING, + .short_name = 'u', + .long_name = "untracked-files", + .value = &untracked_files_arg, + .argh = N_("mode"), + .help = N_("show untracked files, optional modes: all, normal, no. (Default: all)"), + .flags = PARSE_OPT_OPTARG, + .defval = (intptr_t)"all", + }, OPT_PATHSPEC_FROM_FILE(&pathspec_from_file), OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul), /* end commit contents options */ diff --git a/builtin/config.c b/builtin/config.c index 53a90094e3..f70d635477 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -131,9 +131,16 @@ struct config_display_options { #define TYPE_COLOR 6 #define TYPE_BOOL_OR_STR 7 -#define OPT_CALLBACK_VALUE(s, l, v, h, i) \ - { OPTION_CALLBACK, (s), (l), (v), NULL, (h), PARSE_OPT_NOARG | \ - PARSE_OPT_NONEG, option_parse_type, (i) } +#define OPT_CALLBACK_VALUE(s, l, v, h, i) { \ + .type = OPTION_CALLBACK, \ + .short_name = (s), \ + .long_name = (l), \ + .value = (v), \ + .help = (h), \ + .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG, \ + .callback = option_parse_type, \ + .defval = (i), \ +} static int option_parse_type(const struct option *opt, const char *arg, int unset) diff --git a/builtin/describe.c b/builtin/describe.c index 0f87fbceef..2d50883b72 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -601,12 +601,24 @@ int cmd_describe(int argc, N_("do not consider tags matching <pattern>")), OPT_BOOL(0, "always", &always, N_("show abbreviated commit object as fallback")), - {OPTION_STRING, 0, "dirty", &dirty, N_("mark"), - N_("append <mark> on dirty working tree (default: \"-dirty\")"), - PARSE_OPT_OPTARG, NULL, (intptr_t) "-dirty"}, - {OPTION_STRING, 0, "broken", &broken, N_("mark"), - N_("append <mark> on broken working tree (default: \"-broken\")"), - PARSE_OPT_OPTARG, NULL, (intptr_t) "-broken"}, + { + .type = OPTION_STRING, + .long_name = "dirty", + .value = &dirty, + .argh = N_("mark"), + .help = N_("append <mark> on dirty working tree (default: \"-dirty\")"), + .flags = PARSE_OPT_OPTARG, + .defval = (intptr_t) "-dirty", + }, + { + .type = OPTION_STRING, + .long_name = "broken", + .value = &broken, + .argh = N_("mark"), + .help = N_("append <mark> on broken working tree (default: \"-broken\")"), + .flags = PARSE_OPT_OPTARG, + .defval = (intptr_t) "-broken", + }, OPT_END(), }; diff --git a/builtin/fetch.c b/builtin/fetch.c index 95589b4994..5279997c96 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -2352,8 +2352,14 @@ int cmd_fetch(int argc, OPT_SET_INT_F(0, "refetch", &refetch, N_("re-fetch without negotiating common commits"), 1, PARSE_OPT_NONEG), - { OPTION_STRING, 0, "submodule-prefix", &submodule_prefix, N_("dir"), - N_("prepend this to submodule path output"), PARSE_OPT_HIDDEN }, + { + .type = OPTION_STRING, + .long_name = "submodule-prefix", + .value = &submodule_prefix, + .argh = N_("dir"), + .help = N_("prepend this to submodule path output"), + .flags = PARSE_OPT_HIDDEN, + }, OPT_CALLBACK_F(0, "recurse-submodules-default", &recurse_submodules_default, N_("on-demand"), N_("default for recursive fetching of submodules " diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c index 189cd1096a..3b6aac2cf7 100644 --- a/builtin/fmt-merge-msg.c +++ b/builtin/fmt-merge-msg.c @@ -20,13 +20,26 @@ int cmd_fmt_merge_msg(int argc, char *into_name = NULL; int shortlog_len = -1; struct option options[] = { - { OPTION_INTEGER, 0, "log", &shortlog_len, N_("n"), - N_("populate log with at most <n> entries from shortlog"), - PARSE_OPT_OPTARG, NULL, DEFAULT_MERGE_LOG_LEN }, - { OPTION_INTEGER, 0, "summary", &shortlog_len, N_("n"), - N_("alias for --log (deprecated)"), - PARSE_OPT_OPTARG | PARSE_OPT_HIDDEN, NULL, - DEFAULT_MERGE_LOG_LEN }, + { + .type = OPTION_INTEGER, + .long_name = "log", + .value = &shortlog_len, + .precision = sizeof(shortlog_len), + .argh = N_("n"), + .help = N_("populate log with at most <n> entries from shortlog"), + .flags = PARSE_OPT_OPTARG, + .defval = DEFAULT_MERGE_LOG_LEN, + }, + { + .type = OPTION_INTEGER, + .long_name = "summary", + .value = &shortlog_len, + .precision = sizeof(shortlog_len), + .argh = N_("n"), + .help = N_("alias for --log (deprecated)"), + .flags = PARSE_OPT_OPTARG | PARSE_OPT_HIDDEN, + .defval = DEFAULT_MERGE_LOG_LEN, + }, OPT_STRING('m', "message", &message, N_("text"), N_("use <text> as start of message")), OPT_STRING(0, "into-name", &into_name, N_("name"), diff --git a/builtin/gc.c b/builtin/gc.c index b5ce1d3276..856dd50559 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -743,12 +743,18 @@ struct repository *repo UNUSED) int ret; struct option builtin_gc_options[] = { OPT__QUIET(&quiet, N_("suppress progress reporting")), - { OPTION_STRING, 0, "prune", &prune_expire_arg, N_("date"), - N_("prune unreferenced objects"), - PARSE_OPT_OPTARG, NULL, (intptr_t)prune_expire_arg }, + { + .type = OPTION_STRING, + .long_name = "prune", + .value = &prune_expire_arg, + .argh = N_("date"), + .help = N_("prune unreferenced objects"), + .flags = PARSE_OPT_OPTARG, + .defval = (intptr_t)prune_expire_arg, + }, OPT_BOOL(0, "cruft", &cfg.cruft_packs, N_("pack unreferenced objects separately")), - OPT_MAGNITUDE(0, "max-cruft-size", &cfg.max_cruft_size, - N_("with --cruft, limit the size of new cruft packs")), + OPT_UNSIGNED(0, "max-cruft-size", &cfg.max_cruft_size, + N_("with --cruft, limit the size of new cruft packs")), OPT_BOOL(0, "aggressive", &aggressive, N_("be more thorough (increased runtime)")), OPT_BOOL_F(0, "auto", &opts.auto_flag, N_("enable auto-gc mode"), PARSE_OPT_NOCOMPLETE), diff --git a/builtin/grep.c b/builtin/grep.c index bcfbe5be5b..3ce574a605 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -983,9 +983,9 @@ int cmd_grep(int argc, OPT_CALLBACK('C', "context", &opt, N_("n"), N_("show <n> context lines before and after matches"), context_callback), - OPT_INTEGER('B', "before-context", &opt.pre_context, + OPT_UNSIGNED('B', "before-context", &opt.pre_context, N_("show <n> context lines before matches")), - OPT_INTEGER('A', "after-context", &opt.post_context, + OPT_UNSIGNED('A', "after-context", &opt.post_context, N_("show <n> context lines after matches")), OPT_INTEGER(0, "threads", &num_threads, N_("use <n> worker threads")), @@ -1017,10 +1017,16 @@ int cmd_grep(int argc, OPT_BOOL(0, "all-match", &opt.all_match, N_("show only matches from files that match all patterns")), OPT_GROUP(""), - { OPTION_STRING, 'O', "open-files-in-pager", &show_in_pager, - N_("pager"), N_("show matching files in the pager"), - PARSE_OPT_OPTARG | PARSE_OPT_NOCOMPLETE, - NULL, (intptr_t)default_pager }, + { + .type = OPTION_STRING, + .short_name = 'O', + .long_name = "open-files-in-pager", + .value = &show_in_pager, + .argh = N_("pager"), + .help = N_("show matching files in the pager"), + .flags = PARSE_OPT_OPTARG | PARSE_OPT_NOCOMPLETE, + .defval = (intptr_t)default_pager, + }, OPT_BOOL_F(0, "ext-grep", &external_grep_allowed__ignored, N_("allow calling of grep(1) (ignored by this build)"), PARSE_OPT_NOCOMPLETE), diff --git a/builtin/init-db.c b/builtin/init-db.c index 91c2563e34..bb853e69f5 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -92,10 +92,15 @@ int cmd_init_db(int argc, N_("directory from which templates will be used")), OPT_SET_INT(0, "bare", &is_bare_repository_cfg, N_("create a bare repository"), 1), - { OPTION_CALLBACK, 0, "shared", &init_shared_repository, - N_("permissions"), - N_("specify that the git repository is to be shared amongst several users"), - PARSE_OPT_OPTARG | PARSE_OPT_NONEG, shared_callback, 0}, + { + .type = OPTION_CALLBACK, + .long_name = "shared", + .value = &init_shared_repository, + .argh = N_("permissions"), + .help = N_("specify that the git repository is to be shared amongst several users"), + .flags = PARSE_OPT_OPTARG | PARSE_OPT_NONEG, + .callback = shared_callback + }, OPT_BIT('q', "quiet", &flags, N_("be quiet"), INIT_DB_QUIET), OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"), N_("separate git dir from working tree")), diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c index 42f34e1236..01a4d4daa1 100644 --- a/builtin/ls-remote.c +++ b/builtin/ls-remote.c @@ -67,9 +67,14 @@ int cmd_ls_remote(int argc, OPT__QUIET(&quiet, N_("do not print remote URL")), OPT_STRING(0, "upload-pack", &uploadpack, N_("exec"), N_("path of git-upload-pack on the remote host")), - { OPTION_STRING, 0, "exec", &uploadpack, N_("exec"), - N_("path of git-upload-pack on the remote host"), - PARSE_OPT_HIDDEN }, + { + .type = OPTION_STRING, + .long_name = "exec", + .value = &uploadpack, + .argh = N_("exec"), + .help = N_("path of git-upload-pack on the remote host"), + .flags = PARSE_OPT_HIDDEN, + }, OPT_BIT('t', "tags", &flags, N_("limit to tags"), REF_TAGS), OPT_BIT('b', "branches", &flags, N_("limit to branches"), REF_BRANCHES), OPT_BIT_F('h', "heads", &flags, diff --git a/builtin/merge.c b/builtin/merge.c index 9efd585842..ce90e52fe4 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -249,9 +249,16 @@ static struct option builtin_merge_options[] = { OPT_BOOL(0, "stat", &show_diffstat, N_("show a diffstat at the end of the merge")), OPT_BOOL(0, "summary", &show_diffstat, N_("(synonym to --stat)")), - { OPTION_INTEGER, 0, "log", &shortlog_len, N_("n"), - N_("add (at most <n>) entries from shortlog to merge commit message"), - PARSE_OPT_OPTARG, NULL, DEFAULT_MERGE_LOG_LEN }, + { + .type = OPTION_INTEGER, + .long_name = "log", + .value = &shortlog_len, + .precision = sizeof(shortlog_len), + .argh = N_("n"), + .help = N_("add (at most <n>) entries from shortlog to merge commit message"), + .flags = PARSE_OPT_OPTARG, + .defval = DEFAULT_MERGE_LOG_LEN, + }, OPT_BOOL(0, "squash", &squash, N_("create a single commit instead of doing a merge")), OPT_BOOL(0, "commit", &option_commit, @@ -273,9 +280,16 @@ static struct option builtin_merge_options[] = { OPT_CALLBACK('m', "message", &merge_msg, N_("message"), N_("merge commit message (for a non-fast-forward merge)"), option_parse_message), - { OPTION_LOWLEVEL_CALLBACK, 'F', "file", &merge_msg, N_("path"), - N_("read message from file"), PARSE_OPT_NONEG, - NULL, 0, option_read_message }, + { + .type = OPTION_LOWLEVEL_CALLBACK, + .short_name = 'F', + .long_name = "file", + .value = &merge_msg, + .argh = N_("path"), + .help = N_("read message from file"), + .flags = PARSE_OPT_NONEG, + .ll_callback = option_read_message, + }, OPT_STRING(0, "into-name", &into_name, N_("name"), N_("use <name> instead of the real target")), OPT__VERBOSITY(&verbosity), @@ -288,8 +302,16 @@ static struct option builtin_merge_options[] = { OPT_BOOL(0, "allow-unrelated-histories", &allow_unrelated_histories, N_("allow merging unrelated histories")), OPT_SET_INT(0, "progress", &show_progress, N_("force progress reporting"), 1), - { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"), - N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, + { + .type = OPTION_STRING, + .short_name = 'S', + .long_name = "gpg-sign", + .value = &sign_commit, + .argh = N_("key-id"), + .help = N_("GPG sign commit"), + .flags = PARSE_OPT_OPTARG, + .defval = (intptr_t) "", + }, OPT_AUTOSTASH(&autostash), OPT_BOOL(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")), OPT_BOOL(0, "signoff", &signoff, N_("add a Signed-off-by trailer")), diff --git a/builtin/multi-pack-index.c b/builtin/multi-pack-index.c index d98410ca6c..69a9750732 100644 --- a/builtin/multi-pack-index.c +++ b/builtin/multi-pack-index.c @@ -245,7 +245,7 @@ static int cmd_multi_pack_index_repack(int argc, const char **argv, { struct option *options; static struct option builtin_multi_pack_index_repack_options[] = { - OPT_MAGNITUDE(0, "batch-size", &opts.batch_size, + OPT_UNSIGNED(0, "batch-size", &opts.batch_size, N_("during repack, collect pack-files of smaller size into a batch that is larger than this size")), OPT_BIT(0, "progress", &opts.flags, N_("force progress reporting"), MIDX_PROGRESS), diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 8174c60c70..72ebe13fe0 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -4488,16 +4488,16 @@ int cmd_pack_objects(int argc, OPT_CALLBACK_F(0, "index-version", &pack_idx_opts, N_("<version>[,<offset>]"), N_("write the pack index file in the specified idx format 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_UNSIGNED(0, "max-pack-size", &pack_size_limit, + N_("maximum size of each output pack file")), OPT_BOOL(0, "local", &local, N_("ignore borrowed objects from alternate object store")), OPT_BOOL(0, "incremental", &incremental, N_("ignore packed objects")), OPT_INTEGER(0, "window", &window, N_("limit pack window by objects")), - OPT_MAGNITUDE(0, "window-memory", &window_memory_limit, - N_("limit pack window by memory in addition to object limit")), + OPT_UNSIGNED(0, "window-memory", &window_memory_limit, + N_("limit pack window by memory in addition to object limit")), OPT_INTEGER(0, "depth", &depth, N_("maximum length of delta chain allowed in the resulting pack")), OPT_BOOL(0, "reuse-delta", &reuse_delta, diff --git a/builtin/read-tree.c b/builtin/read-tree.c index d2a807a828..a8f352f7cd 100644 --- a/builtin/read-tree.c +++ b/builtin/read-tree.c @@ -135,9 +135,14 @@ int cmd_read_tree(int argc, N_("3-way merge in presence of adds and removes")), OPT_BOOL(0, "reset", &opts.reset, N_("same as -m, but discard unmerged entries")), - { OPTION_STRING, 0, "prefix", &opts.prefix, N_("<subdirectory>/"), - N_("read the tree into the index under <subdirectory>/"), - PARSE_OPT_NONEG }, + { + .type = OPTION_STRING, + .long_name = "prefix", + .value = &opts.prefix, + .argh = N_("<subdirectory>/"), + .help = N_("read the tree into the index under <subdirectory>/"), + .flags = PARSE_OPT_NONEG, + }, OPT_BOOL('u', NULL, &opts.update, N_("update working tree with merge result")), OPT_CALLBACK_F(0, "exclude-per-directory", &opts, diff --git a/builtin/rebase.c b/builtin/rebase.c index 36e336009c..2e8c4ee678 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -1123,9 +1123,15 @@ int cmd_rebase(int argc, OPT_BIT('v', "verbose", &options.flags, N_("display a diffstat of what changed upstream"), REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT), - {OPTION_NEGBIT, 'n', "no-stat", &options.flags, NULL, - N_("do not show diffstat of what changed upstream"), - PARSE_OPT_NOARG, NULL, REBASE_DIFFSTAT }, + { + .type = OPTION_NEGBIT, + .short_name = 'n', + .long_name = "no-stat", + .value = &options.flags, + .help = N_("do not show diffstat of what changed upstream"), + .flags = PARSE_OPT_NOARG, + .defval = REBASE_DIFFSTAT, + }, OPT_BOOL(0, "signoff", &options.signoff, N_("add a Signed-off-by trailer to each commit")), OPT_BOOL(0, "committer-date-is-author-date", @@ -1191,9 +1197,16 @@ int cmd_rebase(int argc, OPT_BOOL(0, "update-refs", &options.update_refs, N_("update branches that point to commits " "that are being rebased")), - { OPTION_STRING, 'S', "gpg-sign", &gpg_sign, N_("key-id"), - N_("GPG-sign commits"), - PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, + { + .type = OPTION_STRING, + .short_name = 'S', + .long_name = "gpg-sign", + .value = &gpg_sign, + .argh = N_("key-id"), + .help = N_("GPG-sign commits"), + .flags = PARSE_OPT_OPTARG, + .defval = (intptr_t) "", + }, OPT_AUTOSTASH(&options.autostash), OPT_STRING_LIST('x', "exec", &options.exec, N_("exec"), N_("add exec lines after each commit of the " diff --git a/builtin/repack.c b/builtin/repack.c index 1fd2874324..59214dbdfd 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -1171,11 +1171,11 @@ int cmd_repack(int argc, PACK_CRUFT), OPT_STRING(0, "cruft-expiration", &cruft_expiration, N_("approxidate"), N_("with --cruft, expire objects older than this")), - OPT_MAGNITUDE(0, "combine-cruft-below-size", - &combine_cruft_below_size, - N_("with --cruft, only repack cruft packs smaller than this")), - OPT_MAGNITUDE(0, "max-cruft-size", &cruft_po_args.max_pack_size, - N_("with --cruft, limit the size of new cruft packs")), + OPT_UNSIGNED(0, "combine-cruft-below-size", + &combine_cruft_below_size, + N_("with --cruft, only repack cruft packs smaller than this")), + OPT_UNSIGNED(0, "max-cruft-size", &cruft_po_args.max_pack_size, + N_("with --cruft, limit the size of new cruft packs")), OPT_BOOL('d', NULL, &delete_redundant, N_("remove redundant packs, and run git-prune-packed")), OPT_BOOL('f', NULL, &po_args.no_reuse_delta, @@ -1205,8 +1205,8 @@ int cmd_repack(int argc, N_("limits the maximum delta depth")), OPT_STRING(0, "threads", &opt_threads, N_("n"), N_("limits the maximum number of threads")), - OPT_MAGNITUDE(0, "max-pack-size", &po_args.max_pack_size, - N_("maximum size of each packfile")), + OPT_UNSIGNED(0, "max-pack-size", &po_args.max_pack_size, + N_("maximum size of each packfile")), OPT_PARSE_LIST_OBJECTS_FILTER(&po_args.filter_options), OPT_BOOL(0, "pack-kept-objects", &pack_kept_objects, N_("repack objects in packs marked with .keep")), diff --git a/builtin/revert.c b/builtin/revert.c index 2654f769a8..e07c2217fe 100644 --- a/builtin/revert.c +++ b/builtin/revert.c @@ -132,8 +132,16 @@ static int run_sequencer(int argc, const char **argv, const char *prefix, OPT_STRING(0, "strategy", &strategy, N_("strategy"), N_("merge strategy")), OPT_STRVEC('X', "strategy-option", &opts->xopts, N_("option"), N_("option for merge strategy")), - { OPTION_STRING, 'S', "gpg-sign", &gpg_sign, N_("key-id"), - N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, + { + .type = OPTION_STRING, + .short_name = 'S', + .long_name = "gpg-sign", + .value = &gpg_sign, + .argh = N_("key-id"), + .help = N_("GPG sign commit"), + .flags = PARSE_OPT_OPTARG, + .defval = (intptr_t) "", + }, OPT_END() }; struct option *options = base_options; diff --git a/builtin/show-branch.c b/builtin/show-branch.c index fce6b404e9..b549d8c3f5 100644 --- a/builtin/show-branch.c +++ b/builtin/show-branch.c @@ -667,9 +667,16 @@ int cmd_show_branch(int ac, N_("show remote-tracking branches")), OPT__COLOR(&showbranch_use_color, N_("color '*!+-' corresponding to the branch")), - { OPTION_INTEGER, 0, "more", &extra, N_("n"), - N_("show <n> more commits after the common ancestor"), - PARSE_OPT_OPTARG, NULL, (intptr_t)1 }, + { + .type = OPTION_INTEGER, + .long_name = "more", + .value = &extra, + .precision = sizeof(extra), + .argh = N_("n"), + .help = N_("show <n> more commits after the common ancestor"), + .flags = PARSE_OPT_OPTARG, + .defval = 1, + }, OPT_SET_INT(0, "list", &extra, N_("synonym to more=-1"), -1), OPT_BOOL(0, "no-name", &no_name, N_("suppress naming strings")), OPT_BOOL(0, "current", &with_current_branch, diff --git a/builtin/tag.c b/builtin/tag.c index e6b372cebf..4742b27d16 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -480,9 +480,16 @@ int cmd_tag(int argc, int edit_flag = 0; struct option options[] = { OPT_CMDMODE('l', "list", &cmdmode, N_("list tag names"), 'l'), - { OPTION_INTEGER, 'n', NULL, &filter.lines, N_("n"), - N_("print <n> lines of each tag message"), - PARSE_OPT_OPTARG, NULL, 1 }, + { + .type = OPTION_INTEGER, + .short_name = 'n', + .value = &filter.lines, + .precision = sizeof(filter.lines), + .argh = N_("n"), + .help = N_("print <n> lines of each tag message"), + .flags = PARSE_OPT_OPTARG, + .defval = 1, + }, OPT_CMDMODE('d', "delete", &cmdmode, N_("delete tags"), 'd'), OPT_CMDMODE('v', "verify", &cmdmode, N_("verify tags"), 'v'), @@ -514,9 +521,14 @@ int cmd_tag(int argc, N_("do not output a newline after empty formatted refs")), OPT_REF_SORT(&sorting_options), { - OPTION_CALLBACK, 0, "points-at", &filter.points_at, N_("object"), - N_("print only tags of the object"), PARSE_OPT_LASTARG_DEFAULT, - parse_opt_object_name, (intptr_t) "HEAD" + .type = OPTION_CALLBACK, + .long_name = "points-at", + .value = &filter.points_at, + .argh = N_("object"), + .help = N_("print only tags of the object"), + .flags = PARSE_OPT_LASTARG_DEFAULT, + .callback = parse_opt_object_name, + .defval = (intptr_t) "HEAD", }, OPT_STRING( 0 , "format", &format.format, N_("format"), N_("format to use for the output")), diff --git a/builtin/update-index.c b/builtin/update-index.c index f0cf964294..538b619ba4 100644 --- a/builtin/update-index.c +++ b/builtin/update-index.c @@ -964,29 +964,51 @@ int cmd_update_index(int argc, N_("like --refresh, but ignore assume-unchanged setting"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, really_refresh_callback), - {OPTION_LOWLEVEL_CALLBACK, 0, "cacheinfo", NULL, - N_("<mode>,<object>,<path>"), - N_("add the specified entry to the index"), - PARSE_OPT_NOARG | /* disallow --cacheinfo=<mode> form */ - PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP, - NULL, 0, - cacheinfo_callback}, + { + .type = OPTION_LOWLEVEL_CALLBACK, + .long_name = "cacheinfo", + .argh = N_("<mode>,<object>,<path>"), + .help = N_("add the specified entry to the index"), + .flags = PARSE_OPT_NOARG | /* disallow --cacheinfo=<mode> form */ + PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP, + .ll_callback = cacheinfo_callback, + }, OPT_CALLBACK_F(0, "chmod", &set_executable_bit, "(+|-)x", N_("override the executable bit of the listed files"), PARSE_OPT_NONEG, chmod_callback), - {OPTION_SET_INT, 0, "assume-unchanged", &mark_valid_only, NULL, - N_("mark files as \"not changing\""), - PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, MARK_FLAG}, - {OPTION_SET_INT, 0, "no-assume-unchanged", &mark_valid_only, NULL, - N_("clear assumed-unchanged bit"), - PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG}, - {OPTION_SET_INT, 0, "skip-worktree", &mark_skip_worktree_only, NULL, - N_("mark files as \"index-only\""), - PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, MARK_FLAG}, - {OPTION_SET_INT, 0, "no-skip-worktree", &mark_skip_worktree_only, NULL, - N_("clear skip-worktree bit"), - PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG}, + { + .type = OPTION_SET_INT, + .long_name = "assume-unchanged", + .value = &mark_valid_only, + .help = N_("mark files as \"not changing\""), + .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG, + .defval = MARK_FLAG, + }, + { + .type = OPTION_SET_INT, + .long_name = "no-assume-unchanged", + .value = &mark_valid_only, + .help = N_("clear assumed-unchanged bit"), + .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG, + .defval = UNMARK_FLAG, + }, + { + .type = OPTION_SET_INT, + .long_name = "skip-worktree", + .value = &mark_skip_worktree_only, + .help = N_("mark files as \"index-only\""), + .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG, + .defval = MARK_FLAG, + }, + { + .type = OPTION_SET_INT, + .long_name = "no-skip-worktree", + .value = &mark_skip_worktree_only, + .help = N_("clear skip-worktree bit"), + .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG, + .defval = UNMARK_FLAG, + }, OPT_BOOL(0, "ignore-skip-worktree-entries", &ignore_skip_worktree_entries, N_("do not touch index-only entries")), OPT_SET_INT(0, "info-only", &info_only, @@ -995,22 +1017,39 @@ int cmd_update_index(int argc, N_("remove named paths even if present in worktree"), 1), OPT_BOOL('z', NULL, &nul_term_line, N_("with --stdin: input lines are terminated by null bytes")), - {OPTION_LOWLEVEL_CALLBACK, 0, "stdin", &read_from_stdin, NULL, - N_("read list of paths to be updated from standard input"), - PARSE_OPT_NONEG | PARSE_OPT_NOARG, - NULL, 0, stdin_callback}, - {OPTION_LOWLEVEL_CALLBACK, 0, "index-info", &nul_term_line, NULL, - N_("add entries from standard input to the index"), - PARSE_OPT_NONEG | PARSE_OPT_NOARG, - NULL, 0, stdin_cacheinfo_callback}, - {OPTION_LOWLEVEL_CALLBACK, 0, "unresolve", &has_errors, NULL, - N_("repopulate stages #2 and #3 for the listed paths"), - PARSE_OPT_NONEG | PARSE_OPT_NOARG, - NULL, 0, unresolve_callback}, - {OPTION_LOWLEVEL_CALLBACK, 'g', "again", &has_errors, NULL, - N_("only update entries that differ from HEAD"), - PARSE_OPT_NONEG | PARSE_OPT_NOARG, - NULL, 0, reupdate_callback}, + { + .type = OPTION_LOWLEVEL_CALLBACK, + .long_name = "stdin", + .value = &read_from_stdin, + .help = N_("read list of paths to be updated from standard input"), + .flags = PARSE_OPT_NONEG | PARSE_OPT_NOARG, + .ll_callback = stdin_callback, + }, + { + .type = OPTION_LOWLEVEL_CALLBACK, + .long_name = "index-info", + .value = &nul_term_line, + .help = N_("add entries from standard input to the index"), + .flags = PARSE_OPT_NONEG | PARSE_OPT_NOARG, + .ll_callback = stdin_cacheinfo_callback, + }, + { + .type = OPTION_LOWLEVEL_CALLBACK, + .long_name = "unresolve", + .value = &has_errors, + .help = N_("repopulate stages #2 and #3 for the listed paths"), + .flags = PARSE_OPT_NONEG | PARSE_OPT_NOARG, + .ll_callback = unresolve_callback, + }, + { + .type = OPTION_LOWLEVEL_CALLBACK, + .short_name = 'g', + .long_name = "again", + .value = &has_errors, + .help = N_("only update entries that differ from HEAD"), + .flags = PARSE_OPT_NONEG | PARSE_OPT_NOARG, + .ll_callback = reupdate_callback, + }, OPT_BIT(0, "ignore-missing", &refresh_args.flags, N_("ignore files missing from worktree"), REFRESH_IGNORE_MISSING), @@ -1036,12 +1075,22 @@ int cmd_update_index(int argc, N_("write out the index even if is not flagged as changed"), 1), OPT_BOOL(0, "fsmonitor", &fsmonitor, N_("enable or disable file system monitor")), - {OPTION_SET_INT, 0, "fsmonitor-valid", &mark_fsmonitor_only, NULL, - N_("mark files as fsmonitor valid"), - PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, MARK_FLAG}, - {OPTION_SET_INT, 0, "no-fsmonitor-valid", &mark_fsmonitor_only, NULL, - N_("clear fsmonitor valid bit"), - PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG}, + { + .type = OPTION_SET_INT, + .long_name = "fsmonitor-valid", + .value = &mark_fsmonitor_only, + .help = N_("mark files as fsmonitor valid"), + .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG, + .defval = MARK_FLAG, + }, + { + .type = OPTION_SET_INT, + .long_name = "no-fsmonitor-valid", + .value = &mark_fsmonitor_only, + .help = N_("clear fsmonitor valid bit"), + .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG, + .defval = UNMARK_FLAG, + }, OPT_END() }; diff --git a/builtin/write-tree.c b/builtin/write-tree.c index 43f233e69b..5a8dc377ec 100644 --- a/builtin/write-tree.c +++ b/builtin/write-tree.c @@ -31,10 +31,14 @@ int cmd_write_tree(int argc, WRITE_TREE_MISSING_OK), OPT_STRING(0, "prefix", &tree_prefix, N_("<prefix>/"), N_("write tree object for a subdirectory <prefix>")), - { OPTION_BIT, 0, "ignore-cache-tree", &flags, NULL, - N_("only useful for debugging"), - PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, NULL, - WRITE_TREE_IGNORE_CACHE_TREE }, + { + .type = OPTION_BIT, + .long_name = "ignore-cache-tree", + .value = &flags, + .help = N_("only useful for debugging"), + .flags = PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, + .defval = WRITE_TREE_IGNORE_CACHE_TREE, + }, OPT_END() }; @@ -5894,10 +5894,15 @@ struct option *add_diff_options(const struct option *opts, OPT_CALLBACK_F(0, "diff-filter", options, N_("[(A|C|D|M|R|T|U|X|B)...[*]]"), N_("select files by diff type"), PARSE_OPT_NONEG, diff_opt_diff_filter), - { OPTION_CALLBACK, 0, "output", options, N_("<file>"), - N_("output to a specific file"), - PARSE_OPT_NONEG, NULL, 0, diff_opt_output }, - + { + .type = OPTION_CALLBACK, + .long_name = "output", + .value = options, + .argh = N_("<file>"), + .help = N_("output to a specific file"), + .flags = PARSE_OPT_NONEG, + .ll_callback = diff_opt_output, + }, OPT_END() }; diff --git a/git-compat-util.h b/git-compat-util.h index 591bb78ad9..36b9577c8d 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -93,12 +93,19 @@ DISABLE_WARNING(-Wsign-compare) # define BARF_UNLESS_COPYABLE(dst, src) \ BUILD_ASSERT_OR_ZERO(__builtin_types_compatible_p(__typeof__(*(dst)), \ __typeof__(*(src)))) + +# define BARF_UNLESS_SIGNED(var) BUILD_ASSERT_OR_ZERO(((__typeof__(var)) -1) < 0) +# define BARF_UNLESS_UNSIGNED(var) BUILD_ASSERT_OR_ZERO(((__typeof__(var)) -1) > 0) #else # define BARF_UNLESS_AN_ARRAY(arr) 0 # define BARF_UNLESS_COPYABLE(dst, src) \ BUILD_ASSERT_OR_ZERO(0 ? ((*(dst) = *(src)), 0) : \ sizeof(*(dst)) == sizeof(*(src))) + +# define BARF_UNLESS_SIGNED(var) 0 +# define BARF_UNLESS_UNSIGNED(var) 0 #endif + /* * ARRAY_SIZE - get the number of elements in a visible array * @x: the array whose size you want. diff --git a/parse-options.c b/parse-options.c index 35fbb3b0d6..a9a39ecaef 100644 --- a/parse-options.c +++ b/parse-options.c @@ -73,7 +73,7 @@ static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p, enum opt_parsed flags, const char **argp) { - const char *s, *arg; + const char *arg; const int unset = flags & OPT_UNSET; int err; @@ -172,41 +172,93 @@ static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p, return (*opt->ll_callback)(p, opt, p_arg, p_unset); } case OPTION_INTEGER: + { + intmax_t upper_bound = INTMAX_MAX >> (bitsizeof(intmax_t) - CHAR_BIT * opt->precision); + intmax_t lower_bound = -upper_bound - 1; + intmax_t value; + if (unset) { - *(int *)opt->value = 0; - return 0; - } - if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { - *(int *)opt->value = opt->defval; - return 0; - } - if (get_arg(p, opt, flags, &arg)) + value = 0; + } else if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { + value = opt->defval; + } else if (get_arg(p, opt, flags, &arg)) { return -1; - if (!*arg) + } else if (!*arg) { return error(_("%s expects a numerical value"), optname(opt, flags)); - *(int *)opt->value = strtol(arg, (char **)&s, 10); - if (*s) - return error(_("%s expects a numerical value"), + } else if (!git_parse_signed(arg, &value, upper_bound)) { + if (errno == ERANGE) + return error(_("value %s for %s not in range [%"PRIdMAX",%"PRIdMAX"]"), + arg, optname(opt, flags), lower_bound, upper_bound); + + return error(_("%s expects an integer value with an optional k/m/g suffix"), optname(opt, flags)); - return 0; + } - case OPTION_MAGNITUDE: - if (unset) { - *(unsigned long *)opt->value = 0; + if (value < lower_bound) + return error(_("value %s for %s not in range [%"PRIdMAX",%"PRIdMAX"]"), + arg, optname(opt, flags), (intmax_t)lower_bound, (intmax_t)upper_bound); + + switch (opt->precision) { + case 1: + *(int8_t *)opt->value = value; return 0; - } - if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { - *(unsigned long *)opt->value = opt->defval; + case 2: + *(int16_t *)opt->value = value; + return 0; + case 4: + *(int32_t *)opt->value = value; return 0; + case 8: + *(int64_t *)opt->value = value; + return 0; + default: + BUG("invalid precision for option %s", + optname(opt, flags)); } - if (get_arg(p, opt, flags, &arg)) + } + case OPTION_UNSIGNED: + { + uintmax_t upper_bound = UINTMAX_MAX >> (bitsizeof(uintmax_t) - CHAR_BIT * opt->precision); + uintmax_t value; + + if (unset) { + value = 0; + } else if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { + value = opt->defval; + } else if (get_arg(p, opt, flags, &arg)) { return -1; - if (!git_parse_ulong(arg, opt->value)) + } else if (!*arg) { + return error(_("%s expects a numerical value"), + optname(opt, flags)); + } else if (!git_parse_unsigned(arg, &value, upper_bound)) { + if (errno == ERANGE) + return error(_("value %s for %s not in range [%"PRIdMAX",%"PRIdMAX"]"), + arg, optname(opt, flags), (uintmax_t) 0, upper_bound); + return error(_("%s expects a non-negative integer value" " with an optional k/m/g suffix"), optname(opt, flags)); - return 0; + } + + switch (opt->precision) { + case 1: + *(uint8_t *)opt->value = value; + return 0; + case 2: + *(uint16_t *)opt->value = value; + return 0; + case 4: + *(uint32_t *)opt->value = value; + return 0; + case 8: + *(uint64_t *)opt->value = value; + return 0; + default: + BUG("invalid precision for option %s", + optname(opt, flags)); + } + } default: BUG("opt->type %d should not happen", opt->type); @@ -656,7 +708,7 @@ static void show_negated_gitcomp(const struct option *opts, int show_all, case OPTION_STRING: case OPTION_FILENAME: case OPTION_INTEGER: - case OPTION_MAGNITUDE: + case OPTION_UNSIGNED: case OPTION_CALLBACK: case OPTION_BIT: case OPTION_NEGBIT: @@ -708,7 +760,7 @@ static int show_gitcomp(const struct option *opts, int show_all) case OPTION_STRING: case OPTION_FILENAME: case OPTION_INTEGER: - case OPTION_MAGNITUDE: + case OPTION_UNSIGNED: case OPTION_CALLBACK: if (opts->flags & PARSE_OPT_NOARG) break; diff --git a/parse-options.h b/parse-options.h index 997ffbee80..91c3e3c29b 100644 --- a/parse-options.h +++ b/parse-options.h @@ -25,7 +25,7 @@ enum parse_opt_type { /* options with arguments (usually) */ OPTION_STRING, OPTION_INTEGER, - OPTION_MAGNITUDE, + OPTION_UNSIGNED, OPTION_CALLBACK, OPTION_LOWLEVEL_CALLBACK, OPTION_FILENAME @@ -92,6 +92,10 @@ typedef int parse_opt_subcommand_fn(int argc, const char **argv, * `value`:: * stores pointers to the values to be filled. * + * `precision`:: + * precision of the integer pointed to by `value` in number of bytes. Should + * typically be its `sizeof()`. + * * `argh`:: * token to explain the kind of argument this option wants. Does not * begin in capital letter, and does not end with a full stop. @@ -151,6 +155,7 @@ struct option { int short_name; const char *long_name; void *value; + size_t precision; const char *argh; const char *help; @@ -213,7 +218,8 @@ struct option { .type = OPTION_INTEGER, \ .short_name = (s), \ .long_name = (l), \ - .value = (v), \ + .value = (v) + BARF_UNLESS_SIGNED(*(v)), \ + .precision = sizeof(*v), \ .argh = N_("n"), \ .help = (h), \ .flags = (f), \ @@ -270,11 +276,12 @@ struct option { #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) -#define OPT_MAGNITUDE(s, l, v, h) { \ - .type = OPTION_MAGNITUDE, \ +#define OPT_UNSIGNED(s, l, v, h) { \ + .type = OPTION_UNSIGNED, \ .short_name = (s), \ .long_name = (l), \ - .value = (v), \ + .value = (v) + BARF_UNLESS_UNSIGNED(*(v)), \ + .precision = sizeof(*v), \ .argh = N_("n"), \ .help = (h), \ .flags = PARSE_OPT_NONEG, \ @@ -38,7 +38,7 @@ int git_parse_signed(const char *value, intmax_t *ret, intmax_t max) errno = EINVAL; return 0; } - if ((val < 0 && -max / factor > val) || + if ((val < 0 && (-max - 1) / factor > val) || (val > 0 && max / factor < val)) { errno = ERANGE; return 0; @@ -51,7 +51,7 @@ int git_parse_signed(const char *value, intmax_t *ret, intmax_t max) return 0; } -static int git_parse_unsigned(const char *value, uintmax_t *ret, uintmax_t max) +int git_parse_unsigned(const char *value, uintmax_t *ret, uintmax_t max) { if (value && *value) { char *end; @@ -2,6 +2,7 @@ #define PARSE_H int git_parse_signed(const char *value, intmax_t *ret, intmax_t max); +int git_parse_unsigned(const char *value, uintmax_t *ret, uintmax_t max); int git_parse_ssize_t(const char *, ssize_t *); int git_parse_ulong(const char *, unsigned long *); int git_parse_int(const char *value, int *ret); diff --git a/ref-filter.h b/ref-filter.h index 013d4cfa64..c98c4fbd4c 100644 --- a/ref-filter.h +++ b/ref-filter.h @@ -114,11 +114,16 @@ struct ref_format { } /* Macros for checking --merged and --no-merged options */ -#define _OPT_MERGED_NO_MERGED(option, filter, h) \ - { OPTION_CALLBACK, 0, option, (filter), N_("commit"), (h), \ - PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NONEG, \ - parse_opt_merge_filter, (intptr_t) "HEAD" \ - } +#define _OPT_MERGED_NO_MERGED(option, filter, h) { \ + .type = OPTION_CALLBACK, \ + .long_name = option, \ + .value = (filter), \ + .argh = N_("commit"), \ + .help = (h), \ + .flags = PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NONEG, \ + .callback = parse_opt_merge_filter, \ + .defval = (intptr_t) "HEAD", \ +} #define OPT_MERGED(f, h) _OPT_MERGED_NO_MERGED("merged", f, h) #define OPT_NO_MERGED(f, h) _OPT_MERGED_NO_MERGED("no-merged", f, h) diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c index bfe45ec68b..f2663dd0c0 100644 --- a/t/helper/test-parse-options.c +++ b/t/helper/test-parse-options.c @@ -6,7 +6,7 @@ static int boolean = 0; static int integer = 0; -static unsigned long magnitude = 0; +static unsigned long unsigned_integer = 0; static timestamp_t timestamp; static int abbrev = 7; static int verbose = -1; /* unspecified */ @@ -120,20 +120,31 @@ int cmd__parse_options(int argc, const char **argv) }; struct string_list expect = STRING_LIST_INIT_NODUP; struct string_list list = STRING_LIST_INIT_NODUP; + uint16_t u16 = 0; + int16_t i16 = 0; struct option options[] = { OPT_BOOL(0, "yes", &boolean, "get a boolean"), OPT_BOOL('D', "no-doubt", &boolean, "begins with 'no-'"), - { OPTION_SET_INT, 'B', "no-fear", &boolean, NULL, - "be brave", PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 }, + { + .type = OPTION_SET_INT, + .short_name = 'B', + .long_name = "no-fear", + .value = &boolean, + .help = "be brave", + .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG, + .defval = 1, + }, OPT_COUNTUP('b', "boolean", &boolean, "increment by one"), OPT_BIT('4', "or4", &boolean, "bitwise-or boolean with ...0100", 4), OPT_NEGBIT(0, "neg-or4", &boolean, "same as --no-or4", 4), OPT_GROUP(""), OPT_INTEGER('i', "integer", &integer, "get a integer"), + OPT_INTEGER(0, "i16", &i16, "get a 16 bit integer"), OPT_INTEGER('j', NULL, &integer, "get a integer, too"), - OPT_MAGNITUDE('m', "magnitude", &magnitude, "get a magnitude"), + OPT_UNSIGNED('u', "unsigned", &unsigned_integer, "get an unsigned integer"), + OPT_UNSIGNED(0, "u16", &u16, "get a 16 bit unsigned integer"), OPT_SET_INT(0, "set23", &integer, "set integer to 23", 23), OPT_CMDMODE(0, "mode1", &integer, "set integer to 1 (cmdmode option)", 1), OPT_CMDMODE(0, "mode2", &integer, "set integer to 2 (cmdmode option)", 2), @@ -155,12 +166,27 @@ int cmd__parse_options(int argc, const char **argv) OPT_GROUP("Magic arguments"), OPT_NUMBER_CALLBACK(&integer, "set integer to NUM", number_callback), - { OPTION_COUNTUP, '+', NULL, &boolean, NULL, "same as -b", - PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH }, - { OPTION_COUNTUP, 0, "ambiguous", &ambiguous, NULL, - "positive ambiguity", PARSE_OPT_NOARG | PARSE_OPT_NONEG }, - { OPTION_COUNTUP, 0, "no-ambiguous", &ambiguous, NULL, - "negative ambiguity", PARSE_OPT_NOARG | PARSE_OPT_NONEG }, + { + .type = OPTION_COUNTUP, + .short_name = '+', + .value = &boolean, + .help = "same as -b", + .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH, + }, + { + .type = OPTION_COUNTUP, + .long_name = "ambiguous", + .value = &ambiguous, + .help = "positive ambiguity", + .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG, + }, + { + .type = OPTION_COUNTUP, + .long_name = "no-ambiguous", + .value = &ambiguous, + .help = "negative ambiguity", + .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG, + }, OPT_GROUP("Standard options"), OPT__ABBREV(&abbrev), OPT__VERBOSE(&verbose, "be verbose"), @@ -188,7 +214,9 @@ int cmd__parse_options(int argc, const char **argv) } show(&expect, &ret, "boolean: %d", boolean); show(&expect, &ret, "integer: %d", integer); - show(&expect, &ret, "magnitude: %lu", magnitude); + show(&expect, &ret, "i16: %"PRIdMAX, (intmax_t) i16); + show(&expect, &ret, "unsigned: %lu", unsigned_integer); + show(&expect, &ret, "u16: %"PRIuMAX, (uintmax_t) u16); show(&expect, &ret, "timestamp: %"PRItime, timestamp); show(&expect, &ret, "string: %s", string ? string : "(not set)"); show(&expect, &ret, "abbrev: %d", abbrev); diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh index 2fe3522305..ca55ea8228 100755 --- a/t/t0040-parse-options.sh +++ b/t/t0040-parse-options.sh @@ -22,8 +22,10 @@ usage: test-tool parse-options <options> -i, --[no-]integer <n> get a integer + --[no-]i16 <n> get a 16 bit integer -j <n> get a integer, too - -m, --magnitude <n> get a magnitude + -u, --unsigned <n> get an unsigned integer + --u16 <n> get a 16 bit unsigned integer --[no-]set23 set integer to 23 --mode1 set integer to 1 (cmdmode option) --mode2 set integer to 2 (cmdmode option) @@ -111,32 +113,36 @@ test_expect_success 'OPT_BOOL() no negation #2' 'check_unknown_i18n --no-no-fear test_expect_success 'OPT_BOOL() positivation' 'check boolean: 0 -D --doubt' -test_expect_success 'OPT_INT() negative' 'check integer: -2345 -i -2345' +test_expect_success 'OPT_INTEGER() negative' 'check integer: -2345 -i -2345' +test_expect_success 'OPT_INTEGER() kilo' 'check integer: 239616 -i 234k' +test_expect_success 'OPT_INTEGER() negative kilo' 'check integer: -239616 -i -234k' -test_expect_success 'OPT_MAGNITUDE() simple' ' - check magnitude: 2345678 -m 2345678 +test_expect_success 'OPT_UNSIGNED() simple' ' + check unsigned: 2345678 -u 2345678 ' -test_expect_success 'OPT_MAGNITUDE() kilo' ' - check magnitude: 239616 -m 234k +test_expect_success 'OPT_UNSIGNED() kilo' ' + check unsigned: 239616 -u 234k ' -test_expect_success 'OPT_MAGNITUDE() mega' ' - check magnitude: 104857600 -m 100m +test_expect_success 'OPT_UNSIGNED() mega' ' + check unsigned: 104857600 -u 100m ' -test_expect_success 'OPT_MAGNITUDE() giga' ' - check magnitude: 1073741824 -m 1g +test_expect_success 'OPT_UNSIGNED() giga' ' + check unsigned: 1073741824 -u 1g ' -test_expect_success 'OPT_MAGNITUDE() 3giga' ' - check magnitude: 3221225472 -m 3g +test_expect_success 'OPT_UNSIGNED() 3giga' ' + check unsigned: 3221225472 -u 3g ' cat >expect <<\EOF boolean: 2 integer: 1729 -magnitude: 16384 +i16: 0 +unsigned: 16384 +u16: 0 timestamp: 0 string: 123 abbrev: 7 @@ -147,7 +153,7 @@ file: prefix/my.file EOF test_expect_success 'short options' ' - test-tool parse-options -s123 -b -i 1729 -m 16k -b -vv -n -F my.file \ + test-tool parse-options -s123 -b -i 1729 -u 16k -b -vv -n -F my.file \ >output 2>output.err && test_cmp expect output && test_must_be_empty output.err @@ -156,7 +162,9 @@ test_expect_success 'short options' ' cat >expect <<\EOF boolean: 2 integer: 1729 -magnitude: 16384 +i16: 9000 +unsigned: 16384 +u16: 32768 timestamp: 0 string: 321 abbrev: 10 @@ -167,8 +175,8 @@ file: prefix/fi.le EOF test_expect_success 'long options' ' - test-tool parse-options --boolean --integer 1729 --magnitude 16k \ - --boolean --string2=321 --verbose --verbose --no-dry-run \ + test-tool parse-options --boolean --integer 1729 --i16 9000 --unsigned 16k \ + --u16 32k --boolean --string2=321 --verbose --verbose --no-dry-run \ --abbrev=10 --file fi.le --obsolete \ >output 2>output.err && test_must_be_empty output.err && @@ -179,7 +187,9 @@ test_expect_success 'abbreviate to something longer than SHA1 length' ' cat >expect <<-EOF && boolean: 0 integer: 0 - magnitude: 0 + i16: 0 + unsigned: 0 + u16: 0 timestamp: 0 string: (not set) abbrev: 100 @@ -253,7 +263,9 @@ test_expect_success 'superfluous value provided: cmdmode' ' cat >expect <<\EOF boolean: 1 integer: 13 -magnitude: 0 +i16: 0 +unsigned: 0 +u16: 0 timestamp: 0 string: 123 abbrev: 7 @@ -276,7 +288,9 @@ test_expect_success 'intermingled arguments' ' cat >expect <<\EOF boolean: 0 integer: 2 -magnitude: 0 +i16: 0 +unsigned: 0 +u16: 0 timestamp: 0 string: (not set) abbrev: 7 @@ -343,7 +357,9 @@ cat >expect <<\EOF Callback: "four", 0 boolean: 5 integer: 4 -magnitude: 0 +i16: 0 +unsigned: 0 +u16: 0 timestamp: 0 string: (not set) abbrev: 7 @@ -368,7 +384,9 @@ test_expect_success 'OPT_CALLBACK() and callback errors work' ' cat >expect <<\EOF boolean: 1 integer: 23 -magnitude: 0 +i16: 0 +unsigned: 0 +u16: 0 timestamp: 0 string: (not set) abbrev: 7 @@ -447,7 +465,9 @@ test_expect_success 'OPT_NUMBER_CALLBACK() works' ' cat >expect <<\EOF boolean: 0 integer: 0 -magnitude: 0 +i16: 0 +unsigned: 0 +u16: 0 timestamp: 0 string: (not set) abbrev: 7 @@ -771,16 +791,35 @@ test_expect_success 'subcommands are incompatible with KEEP_DASHDASH unless in c grep ^BUG err ' -test_expect_success 'negative magnitude' ' - test_must_fail test-tool parse-options --magnitude -1 >out 2>err && +test_expect_success 'negative unsigned' ' + test_must_fail test-tool parse-options --unsigned -1 >out 2>err && grep "non-negative integer" err && test_must_be_empty out ' -test_expect_success 'magnitude with units but no numbers' ' - test_must_fail test-tool parse-options --magnitude m >out 2>err && +test_expect_success 'unsigned with units but no numbers' ' + test_must_fail test-tool parse-options --unsigned m >out 2>err && grep "non-negative integer" err && test_must_be_empty out ' +test_expect_success 'i16 limits range' ' + test-tool parse-options --i16 32767 >out && + test_grep "i16: 32767" out && + test_must_fail test-tool parse-options --i16 32768 2>err && + test_grep "value 32768 for option .i16. not in range \[-32768,32767\]" err && + + test-tool parse-options --i16 -32768 >out && + test_grep "i16: -32768" out && + test_must_fail test-tool parse-options --i16 -32769 2>err && + test_grep "value -32769 for option .i16. not in range \[-32768,32767\]" err +' + +test_expect_success 'u16 limits range' ' + test-tool parse-options --u16 65535 >out && + test_grep "u16: 65535" out && + test_must_fail test-tool parse-options --u16 65536 2>err && + test_grep "value 65536 for option .u16. not in range \[0,65535\]" err +' + test_done |