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
main
Junio C Hamano 2025-04-24 17:25:33 -07:00
commit 2bc5414c41
37 changed files with 656 additions and 252 deletions

View File

@ -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`.

View File

@ -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),

View File

@ -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(""),

View File

@ -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),

View File

@ -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(),

View File

@ -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")),

View File

@ -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")),

View File

@ -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;

View File

@ -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 */

View File

@ -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)

View File

@ -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(),
};


View File

@ -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 "

View File

@ -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"),

View File

@ -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),

View File

@ -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),

View File

@ -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")),

View File

@ -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,

View File

@ -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")),

View File

@ -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),

View File

@ -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,

View File

@ -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,

View File

@ -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 "

View File

@ -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")),

View File

@ -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;

View File

@ -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,

View File

@ -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")),

View File

@ -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()
};


View File

@ -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()
};


13
diff.c
View File

@ -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()
};


View File

@ -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.

View File

@ -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:
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))
return -1;
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"),
optname(opt, flags));
return 0;
{
intmax_t upper_bound = INTMAX_MAX >> (bitsizeof(intmax_t) - CHAR_BIT * opt->precision);
intmax_t lower_bound = -upper_bound - 1;
intmax_t value;

case OPTION_MAGNITUDE:
if (unset) {
*(unsigned long *)opt->value = 0;
return 0;
}
if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
*(unsigned long *)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 (!git_parse_ulong(arg, opt->value))
} else if (!*arg) {
return error(_("%s expects a numerical value"),
optname(opt, flags));
} 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));
}

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;
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));
}
}
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;
} 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;

View File

@ -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, \

View File

@ -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;

View File

@ -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);

View File

@ -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)


View File

@ -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);

View File

@ -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