Merge branch 'nd/diff-parseopt'
The diff machinery, one of the oldest parts of the system, which long predates the parse-options API, uses fairly long and complex handcrafted option parser. This is being rewritten to use the parse-options API. * nd/diff-parseopt: diff.c: convert --raw diff.c: convert -W|--[no-]function-context diff.c: convert -U|--unified diff.c: convert -u|-p|--patch diff.c: prepare to use parse_options() for parsing diff.h: avoid bit fields in struct diff_flags diff.h: keep forward struct declarations sorted parse-options: allow ll_callback with OPTION_CALLBACK parse-options: avoid magic return codes parse-options: stop abusing 'callback' for lowlevel callbacks parse-options: add OPT_BITOP() parse-options: disable option abbreviation with PARSE_OPT_KEEP_UNKNOWN parse-options: add one-shot mode parse-options.h: remove extern on function prototypes
This commit is contained in:
commit
54b469b9e9
@ -36,7 +36,7 @@ endif::git-format-patch[]
|
||||
-U<n>::
|
||||
--unified=<n>::
|
||||
Generate diffs with <n> lines of context instead of
|
||||
the usual three.
|
||||
the usual three. Implies `--patch`.
|
||||
ifndef::git-format-patch[]
|
||||
Implies `-p`.
|
||||
endif::git-format-patch[]
|
||||
|
@ -814,7 +814,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
|
||||
* and are only included here to get included in the "-h"
|
||||
* output:
|
||||
*/
|
||||
{ OPTION_LOWLEVEL_CALLBACK, 0, "indent-heuristic", NULL, NULL, N_("Use an experimental heuristic to improve diffs"), PARSE_OPT_NOARG, parse_opt_unknown_cb },
|
||||
{ OPTION_LOWLEVEL_CALLBACK, 0, "indent-heuristic", NULL, NULL, N_("Use an experimental heuristic to improve diffs"), PARSE_OPT_NOARG, NULL, 0, parse_opt_unknown_cb },
|
||||
|
||||
OPT_BIT(0, "minimal", &xdl_opts, N_("Spend extra cycles to find better match"), XDF_NEED_MINIMAL),
|
||||
OPT_STRING('S', NULL, &revs_file, N_("file"), N_("Use revisions from <file> instead of calling git-rev-list")),
|
||||
|
@ -113,12 +113,15 @@ static int option_parse_message(const struct option *opt,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int option_read_message(struct parse_opt_ctx_t *ctx,
|
||||
const struct option *opt, int unset)
|
||||
static enum parse_opt_result option_read_message(struct parse_opt_ctx_t *ctx,
|
||||
const struct option *opt,
|
||||
const char *arg_not_used,
|
||||
int unset)
|
||||
{
|
||||
struct strbuf *buf = opt->value;
|
||||
const char *arg;
|
||||
|
||||
BUG_ON_OPT_ARG(arg_not_used);
|
||||
if (unset)
|
||||
BUG("-F cannot be negated");
|
||||
|
||||
@ -262,7 +265,7 @@ static struct option builtin_merge_options[] = {
|
||||
option_parse_message),
|
||||
{ OPTION_LOWLEVEL_CALLBACK, 'F', "file", &merge_msg, N_("path"),
|
||||
N_("read message from file"), PARSE_OPT_NONEG,
|
||||
(parse_opt_cb *) option_read_message },
|
||||
NULL, 0, option_read_message },
|
||||
OPT__VERBOSITY(&verbosity),
|
||||
OPT_BOOL(0, "abort", &abort_current_merge,
|
||||
N_("abort the current in-progress merge")),
|
||||
|
@ -848,14 +848,16 @@ static int parse_new_style_cacheinfo(const char *arg,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cacheinfo_callback(struct parse_opt_ctx_t *ctx,
|
||||
const struct option *opt, int unset)
|
||||
static enum parse_opt_result cacheinfo_callback(
|
||||
struct parse_opt_ctx_t *ctx, const struct option *opt,
|
||||
const char *arg, int unset)
|
||||
{
|
||||
struct object_id oid;
|
||||
unsigned int mode;
|
||||
const char *path;
|
||||
|
||||
BUG_ON_OPT_NEG(unset);
|
||||
BUG_ON_OPT_ARG(arg);
|
||||
|
||||
if (!parse_new_style_cacheinfo(ctx->argv[1], &mode, &oid, &path)) {
|
||||
if (add_cacheinfo(mode, &oid, path, 0))
|
||||
@ -874,12 +876,14 @@ static int cacheinfo_callback(struct parse_opt_ctx_t *ctx,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stdin_cacheinfo_callback(struct parse_opt_ctx_t *ctx,
|
||||
const struct option *opt, int unset)
|
||||
static enum parse_opt_result stdin_cacheinfo_callback(
|
||||
struct parse_opt_ctx_t *ctx, const struct option *opt,
|
||||
const char *arg, int unset)
|
||||
{
|
||||
int *nul_term_line = opt->value;
|
||||
|
||||
BUG_ON_OPT_NEG(unset);
|
||||
BUG_ON_OPT_ARG(arg);
|
||||
|
||||
if (ctx->argc != 1)
|
||||
return error("option '%s' must be the last argument", opt->long_name);
|
||||
@ -888,12 +892,14 @@ static int stdin_cacheinfo_callback(struct parse_opt_ctx_t *ctx,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stdin_callback(struct parse_opt_ctx_t *ctx,
|
||||
const struct option *opt, int unset)
|
||||
static enum parse_opt_result stdin_callback(
|
||||
struct parse_opt_ctx_t *ctx, const struct option *opt,
|
||||
const char *arg, int unset)
|
||||
{
|
||||
int *read_from_stdin = opt->value;
|
||||
|
||||
BUG_ON_OPT_NEG(unset);
|
||||
BUG_ON_OPT_ARG(arg);
|
||||
|
||||
if (ctx->argc != 1)
|
||||
return error("option '%s' must be the last argument", opt->long_name);
|
||||
@ -901,13 +907,15 @@ static int stdin_callback(struct parse_opt_ctx_t *ctx,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int unresolve_callback(struct parse_opt_ctx_t *ctx,
|
||||
const struct option *opt, int unset)
|
||||
static enum parse_opt_result unresolve_callback(
|
||||
struct parse_opt_ctx_t *ctx, const struct option *opt,
|
||||
const char *arg, int unset)
|
||||
{
|
||||
int *has_errors = opt->value;
|
||||
const char *prefix = startup_info->prefix;
|
||||
|
||||
BUG_ON_OPT_NEG(unset);
|
||||
BUG_ON_OPT_ARG(arg);
|
||||
|
||||
/* consume remaining arguments. */
|
||||
*has_errors = do_unresolve(ctx->argc, ctx->argv,
|
||||
@ -920,13 +928,15 @@ static int unresolve_callback(struct parse_opt_ctx_t *ctx,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int reupdate_callback(struct parse_opt_ctx_t *ctx,
|
||||
const struct option *opt, int unset)
|
||||
static enum parse_opt_result reupdate_callback(
|
||||
struct parse_opt_ctx_t *ctx, const struct option *opt,
|
||||
const char *arg, int unset)
|
||||
{
|
||||
int *has_errors = opt->value;
|
||||
const char *prefix = startup_info->prefix;
|
||||
|
||||
BUG_ON_OPT_NEG(unset);
|
||||
BUG_ON_OPT_ARG(arg);
|
||||
|
||||
/* consume remaining arguments. */
|
||||
setup_work_tree();
|
||||
@ -986,7 +996,8 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
|
||||
N_("add the specified entry to the index"),
|
||||
PARSE_OPT_NOARG | /* disallow --cacheinfo=<mode> form */
|
||||
PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
|
||||
(parse_opt_cb *) cacheinfo_callback},
|
||||
NULL, 0,
|
||||
cacheinfo_callback},
|
||||
{OPTION_CALLBACK, 0, "chmod", &set_executable_bit, "(+|-)x",
|
||||
N_("override the executable bit of the listed files"),
|
||||
PARSE_OPT_NONEG,
|
||||
@ -1012,19 +1023,19 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
|
||||
{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,
|
||||
(parse_opt_cb *) stdin_callback},
|
||||
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,
|
||||
(parse_opt_cb *) stdin_cacheinfo_callback},
|
||||
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,
|
||||
(parse_opt_cb *) unresolve_callback},
|
||||
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,
|
||||
(parse_opt_cb *) reupdate_callback},
|
||||
NULL, 0, reupdate_callback},
|
||||
OPT_BIT(0, "ignore-missing", &refresh_args.flags,
|
||||
N_("ignore files missing from worktree"),
|
||||
REFRESH_IGNORE_MISSING),
|
||||
|
71
diff.c
71
diff.c
@ -23,6 +23,7 @@
|
||||
#include "argv-array.h"
|
||||
#include "graph.h"
|
||||
#include "packfile.h"
|
||||
#include "parse-options.h"
|
||||
#include "help.h"
|
||||
|
||||
#ifdef NO_FAST_WORKING_DIRECTORY
|
||||
@ -4491,6 +4492,8 @@ static void run_checkdiff(struct diff_filepair *p, struct diff_options *o)
|
||||
builtin_checkdiff(name, other, attr_path, p->one, p->two, o);
|
||||
}
|
||||
|
||||
static void prep_parse_options(struct diff_options *options);
|
||||
|
||||
void repo_diff_setup(struct repository *r, struct diff_options *options)
|
||||
{
|
||||
memcpy(options, &default_diff_options, sizeof(*options));
|
||||
@ -4532,6 +4535,8 @@ void repo_diff_setup(struct repository *r, struct diff_options *options)
|
||||
|
||||
options->color_moved = diff_color_moved_default;
|
||||
options->color_moved_ws_handling = diff_color_moved_ws_default;
|
||||
|
||||
prep_parse_options(options);
|
||||
}
|
||||
|
||||
void diff_setup_done(struct diff_options *options)
|
||||
@ -4635,6 +4640,8 @@ void diff_setup_done(struct diff_options *options)
|
||||
|
||||
if (!options->use_color || external_diff())
|
||||
options->color_moved = 0;
|
||||
|
||||
FREE_AND_NULL(options->parseopts);
|
||||
}
|
||||
|
||||
static int opt_arg(const char *arg, int arg_short, const char *arg_long, int *val)
|
||||
@ -4926,6 +4933,47 @@ static int parse_objfind_opt(struct diff_options *opt, const char *arg)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int diff_opt_unified(const struct option *opt,
|
||||
const char *arg, int unset)
|
||||
{
|
||||
struct diff_options *options = opt->value;
|
||||
char *s;
|
||||
|
||||
BUG_ON_OPT_NEG(unset);
|
||||
|
||||
options->context = strtol(arg, &s, 10);
|
||||
if (*s)
|
||||
return error(_("%s expects a numerical value"), "--unified");
|
||||
enable_patch_output(&options->output_format);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void prep_parse_options(struct diff_options *options)
|
||||
{
|
||||
struct option parseopts[] = {
|
||||
OPT_GROUP(N_("Diff output format options")),
|
||||
OPT_BITOP('p', "patch", &options->output_format,
|
||||
N_("generate patch"),
|
||||
DIFF_FORMAT_PATCH, DIFF_FORMAT_NO_OUTPUT),
|
||||
OPT_BITOP('u', NULL, &options->output_format,
|
||||
N_("generate patch"),
|
||||
DIFF_FORMAT_PATCH, DIFF_FORMAT_NO_OUTPUT),
|
||||
OPT_CALLBACK_F('U', "unified", options, N_("<n>"),
|
||||
N_("generate diffs with <n> lines context"),
|
||||
PARSE_OPT_NONEG, diff_opt_unified),
|
||||
OPT_BOOL('W', "function-context", &options->flags.funccontext,
|
||||
N_("generate diffs with <n> lines context")),
|
||||
OPT_BIT_F(0, "raw", &options->output_format,
|
||||
N_("generate the diff in raw format"),
|
||||
DIFF_FORMAT_RAW, PARSE_OPT_NONEG),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
ALLOC_ARRAY(options->parseopts, ARRAY_SIZE(parseopts));
|
||||
memcpy(options->parseopts, parseopts, sizeof(parseopts));
|
||||
}
|
||||
|
||||
int diff_opt_parse(struct diff_options *options,
|
||||
const char **av, int ac, const char *prefix)
|
||||
{
|
||||
@ -4936,13 +4984,18 @@ int diff_opt_parse(struct diff_options *options,
|
||||
if (!prefix)
|
||||
prefix = "";
|
||||
|
||||
ac = parse_options(ac, av, prefix, options->parseopts, NULL,
|
||||
PARSE_OPT_KEEP_DASHDASH |
|
||||
PARSE_OPT_KEEP_UNKNOWN |
|
||||
PARSE_OPT_NO_INTERNAL_HELP |
|
||||
PARSE_OPT_ONE_SHOT |
|
||||
PARSE_OPT_STOP_AT_NON_OPTION);
|
||||
|
||||
if (ac)
|
||||
return ac;
|
||||
|
||||
/* Output format options */
|
||||
if (!strcmp(arg, "-p") || !strcmp(arg, "-u") || !strcmp(arg, "--patch")
|
||||
|| opt_arg(arg, 'U', "unified", &options->context))
|
||||
enable_patch_output(&options->output_format);
|
||||
else if (!strcmp(arg, "--raw"))
|
||||
options->output_format |= DIFF_FORMAT_RAW;
|
||||
else if (!strcmp(arg, "--patch-with-raw")) {
|
||||
if (!strcmp(arg, "--patch-with-raw")) {
|
||||
enable_patch_output(&options->output_format);
|
||||
options->output_format |= DIFF_FORMAT_RAW;
|
||||
} else if (!strcmp(arg, "--numstat"))
|
||||
@ -5230,12 +5283,6 @@ int diff_opt_parse(struct diff_options *options,
|
||||
else if (opt_arg(arg, '\0', "inter-hunk-context",
|
||||
&options->interhunkcontext))
|
||||
;
|
||||
else if (!strcmp(arg, "-W"))
|
||||
options->flags.funccontext = 1;
|
||||
else if (!strcmp(arg, "--function-context"))
|
||||
options->flags.funccontext = 1;
|
||||
else if (!strcmp(arg, "--no-function-context"))
|
||||
options->flags.funccontext = 0;
|
||||
else if ((argcount = parse_long_opt("output", av, &optarg))) {
|
||||
char *path = prefix_filename(prefix, optarg);
|
||||
options->file = xfopen(path, "w");
|
||||
|
80
diff.h
80
diff.h
@ -9,16 +9,17 @@
|
||||
#include "object.h"
|
||||
#include "oidset.h"
|
||||
|
||||
struct rev_info;
|
||||
struct combine_diff_path;
|
||||
struct commit;
|
||||
struct diff_filespec;
|
||||
struct diff_options;
|
||||
struct diff_queue_struct;
|
||||
struct strbuf;
|
||||
struct diff_filespec;
|
||||
struct userdiff_driver;
|
||||
struct oid_array;
|
||||
struct commit;
|
||||
struct combine_diff_path;
|
||||
struct option;
|
||||
struct repository;
|
||||
struct rev_info;
|
||||
struct strbuf;
|
||||
struct userdiff_driver;
|
||||
|
||||
typedef int (*pathchange_fn_t)(struct diff_options *options,
|
||||
struct combine_diff_path *path);
|
||||
@ -64,39 +65,39 @@ typedef struct strbuf *(*diff_prefix_fn_t)(struct diff_options *opt, void *data)
|
||||
|
||||
#define DIFF_FLAGS_INIT { 0 }
|
||||
struct diff_flags {
|
||||
unsigned recursive:1;
|
||||
unsigned tree_in_recursive:1;
|
||||
unsigned binary:1;
|
||||
unsigned text:1;
|
||||
unsigned full_index:1;
|
||||
unsigned silent_on_remove:1;
|
||||
unsigned find_copies_harder:1;
|
||||
unsigned follow_renames:1;
|
||||
unsigned rename_empty:1;
|
||||
unsigned has_changes:1;
|
||||
unsigned quick:1;
|
||||
unsigned no_index:1;
|
||||
unsigned allow_external:1;
|
||||
unsigned exit_with_status:1;
|
||||
unsigned reverse_diff:1;
|
||||
unsigned check_failed:1;
|
||||
unsigned relative_name:1;
|
||||
unsigned ignore_submodules:1;
|
||||
unsigned dirstat_cumulative:1;
|
||||
unsigned dirstat_by_file:1;
|
||||
unsigned allow_textconv:1;
|
||||
unsigned textconv_set_via_cmdline:1;
|
||||
unsigned diff_from_contents:1;
|
||||
unsigned dirty_submodules:1;
|
||||
unsigned ignore_untracked_in_submodules:1;
|
||||
unsigned ignore_dirty_submodules:1;
|
||||
unsigned override_submodule_config:1;
|
||||
unsigned dirstat_by_line:1;
|
||||
unsigned funccontext:1;
|
||||
unsigned default_follow_renames:1;
|
||||
unsigned stat_with_summary:1;
|
||||
unsigned suppress_diff_headers:1;
|
||||
unsigned dual_color_diffed_diffs:1;
|
||||
unsigned recursive;
|
||||
unsigned tree_in_recursive;
|
||||
unsigned binary;
|
||||
unsigned text;
|
||||
unsigned full_index;
|
||||
unsigned silent_on_remove;
|
||||
unsigned find_copies_harder;
|
||||
unsigned follow_renames;
|
||||
unsigned rename_empty;
|
||||
unsigned has_changes;
|
||||
unsigned quick;
|
||||
unsigned no_index;
|
||||
unsigned allow_external;
|
||||
unsigned exit_with_status;
|
||||
unsigned reverse_diff;
|
||||
unsigned check_failed;
|
||||
unsigned relative_name;
|
||||
unsigned ignore_submodules;
|
||||
unsigned dirstat_cumulative;
|
||||
unsigned dirstat_by_file;
|
||||
unsigned allow_textconv;
|
||||
unsigned textconv_set_via_cmdline;
|
||||
unsigned diff_from_contents;
|
||||
unsigned dirty_submodules;
|
||||
unsigned ignore_untracked_in_submodules;
|
||||
unsigned ignore_dirty_submodules;
|
||||
unsigned override_submodule_config;
|
||||
unsigned dirstat_by_line;
|
||||
unsigned funccontext;
|
||||
unsigned default_follow_renames;
|
||||
unsigned stat_with_summary;
|
||||
unsigned suppress_diff_headers;
|
||||
unsigned dual_color_diffed_diffs;
|
||||
};
|
||||
|
||||
static inline void diff_flags_or(struct diff_flags *a,
|
||||
@ -229,6 +230,7 @@ struct diff_options {
|
||||
unsigned color_moved_ws_handling;
|
||||
|
||||
struct repository *repo;
|
||||
struct option *parseopts;
|
||||
};
|
||||
|
||||
void diff_emit_submodule_del(struct diff_options *o, const char *line);
|
||||
|
@ -170,9 +170,12 @@ int parse_opt_noop_cb(const struct option *opt, const char *arg, int unset)
|
||||
* "-h" output even if it's not being handled directly by
|
||||
* parse_options().
|
||||
*/
|
||||
int parse_opt_unknown_cb(const struct option *opt, const char *arg, int unset)
|
||||
enum parse_opt_result parse_opt_unknown_cb(struct parse_opt_ctx_t *ctx,
|
||||
const struct option *opt,
|
||||
const char *arg, int unset)
|
||||
{
|
||||
return -2;
|
||||
BUG_ON_OPT_ARG(arg);
|
||||
return PARSE_OPT_UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
|
154
parse-options.c
154
parse-options.c
@ -20,8 +20,9 @@ int optbug(const struct option *opt, const char *reason)
|
||||
return error("BUG: switch '%c' %s", opt->short_name, reason);
|
||||
}
|
||||
|
||||
static int get_arg(struct parse_opt_ctx_t *p, const struct option *opt,
|
||||
int flags, const char **arg)
|
||||
static enum parse_opt_result get_arg(struct parse_opt_ctx_t *p,
|
||||
const struct option *opt,
|
||||
int flags, const char **arg)
|
||||
{
|
||||
if (p->opt) {
|
||||
*arg = p->opt;
|
||||
@ -44,9 +45,10 @@ static void fix_filename(const char *prefix, const char **file)
|
||||
*file = prefix_filename(prefix, *file);
|
||||
}
|
||||
|
||||
static int opt_command_mode_error(const struct option *opt,
|
||||
const struct option *all_opts,
|
||||
int flags)
|
||||
static enum parse_opt_result opt_command_mode_error(
|
||||
const struct option *opt,
|
||||
const struct option *all_opts,
|
||||
int flags)
|
||||
{
|
||||
const struct option *that;
|
||||
struct strbuf that_name = STRBUF_INIT;
|
||||
@ -69,16 +71,16 @@ static int opt_command_mode_error(const struct option *opt,
|
||||
error(_("%s is incompatible with %s"),
|
||||
optname(opt, flags), that_name.buf);
|
||||
strbuf_release(&that_name);
|
||||
return -1;
|
||||
return PARSE_OPT_ERROR;
|
||||
}
|
||||
return error(_("%s : incompatible with something else"),
|
||||
optname(opt, flags));
|
||||
}
|
||||
|
||||
static int get_value(struct parse_opt_ctx_t *p,
|
||||
const struct option *opt,
|
||||
const struct option *all_opts,
|
||||
int flags)
|
||||
static enum parse_opt_result get_value(struct parse_opt_ctx_t *p,
|
||||
const struct option *opt,
|
||||
const struct option *all_opts,
|
||||
int flags)
|
||||
{
|
||||
const char *s, *arg;
|
||||
const int unset = flags & OPT_UNSET;
|
||||
@ -93,7 +95,7 @@ static int get_value(struct parse_opt_ctx_t *p,
|
||||
|
||||
switch (opt->type) {
|
||||
case OPTION_LOWLEVEL_CALLBACK:
|
||||
return (*(parse_opt_ll_cb *)opt->callback)(p, opt, unset);
|
||||
return opt->ll_callback(p, opt, NULL, unset);
|
||||
|
||||
case OPTION_BIT:
|
||||
if (unset)
|
||||
@ -109,6 +111,13 @@ static int get_value(struct parse_opt_ctx_t *p,
|
||||
*(int *)opt->value &= ~opt->defval;
|
||||
return 0;
|
||||
|
||||
case OPTION_BITOP:
|
||||
if (unset)
|
||||
BUG("BITOP can't have unset form");
|
||||
*(int *)opt->value &= ~opt->extra;
|
||||
*(int *)opt->value |= opt->defval;
|
||||
return 0;
|
||||
|
||||
case OPTION_COUNTUP:
|
||||
if (*(int *)opt->value < 0)
|
||||
*(int *)opt->value = 0;
|
||||
@ -152,16 +161,27 @@ static int get_value(struct parse_opt_ctx_t *p,
|
||||
return err;
|
||||
|
||||
case OPTION_CALLBACK:
|
||||
if (unset)
|
||||
return (*opt->callback)(opt, NULL, 1) ? (-1) : 0;
|
||||
if (opt->flags & PARSE_OPT_NOARG)
|
||||
return (*opt->callback)(opt, NULL, 0) ? (-1) : 0;
|
||||
if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
|
||||
return (*opt->callback)(opt, NULL, 0) ? (-1) : 0;
|
||||
if (get_arg(p, opt, flags, &arg))
|
||||
return -1;
|
||||
return (*opt->callback)(opt, arg, 0) ? (-1) : 0;
|
||||
{
|
||||
const char *p_arg = NULL;
|
||||
int p_unset;
|
||||
|
||||
if (unset)
|
||||
p_unset = 1;
|
||||
else if (opt->flags & PARSE_OPT_NOARG)
|
||||
p_unset = 0;
|
||||
else if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
|
||||
p_unset = 0;
|
||||
else if (get_arg(p, opt, flags, &arg))
|
||||
return -1;
|
||||
else {
|
||||
p_unset = 0;
|
||||
p_arg = arg;
|
||||
}
|
||||
if (opt->callback)
|
||||
return (*opt->callback)(opt, p_arg, p_unset) ? (-1) : 0;
|
||||
else
|
||||
return (*opt->ll_callback)(p, opt, p_arg, p_unset);
|
||||
}
|
||||
case OPTION_INTEGER:
|
||||
if (unset) {
|
||||
*(int *)opt->value = 0;
|
||||
@ -201,7 +221,8 @@ static int get_value(struct parse_opt_ctx_t *p,
|
||||
}
|
||||
}
|
||||
|
||||
static int parse_short_opt(struct parse_opt_ctx_t *p, const struct option *options)
|
||||
static enum parse_opt_result parse_short_opt(struct parse_opt_ctx_t *p,
|
||||
const struct option *options)
|
||||
{
|
||||
const struct option *all_opts = options;
|
||||
const struct option *numopt = NULL;
|
||||
@ -228,15 +249,19 @@ static int parse_short_opt(struct parse_opt_ctx_t *p, const struct option *optio
|
||||
len++;
|
||||
arg = xmemdupz(p->opt, len);
|
||||
p->opt = p->opt[len] ? p->opt + len : NULL;
|
||||
rc = (*numopt->callback)(numopt, arg, 0) ? (-1) : 0;
|
||||
if (numopt->callback)
|
||||
rc = (*numopt->callback)(numopt, arg, 0) ? (-1) : 0;
|
||||
else
|
||||
rc = (*numopt->ll_callback)(p, numopt, arg, 0);
|
||||
free(arg);
|
||||
return rc;
|
||||
}
|
||||
return -2;
|
||||
return PARSE_OPT_UNKNOWN;
|
||||
}
|
||||
|
||||
static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,
|
||||
const struct option *options)
|
||||
static enum parse_opt_result parse_long_opt(
|
||||
struct parse_opt_ctx_t *p, const char *arg,
|
||||
const struct option *options)
|
||||
{
|
||||
const struct option *all_opts = options;
|
||||
const char *arg_end = strchrnul(arg, '=');
|
||||
@ -262,11 +287,12 @@ again:
|
||||
if (*rest)
|
||||
continue;
|
||||
p->out[p->cpidx++] = arg - 2;
|
||||
return 0;
|
||||
return PARSE_OPT_DONE;
|
||||
}
|
||||
if (!rest) {
|
||||
/* abbreviated? */
|
||||
if (!strncmp(long_name, arg, arg_end - arg)) {
|
||||
if (!(p->flags & PARSE_OPT_KEEP_UNKNOWN) &&
|
||||
!strncmp(long_name, arg, arg_end - arg)) {
|
||||
is_abbreviated:
|
||||
if (abbrev_option) {
|
||||
/*
|
||||
@ -326,11 +352,11 @@ is_abbreviated:
|
||||
ambiguous_option->long_name,
|
||||
(abbrev_flags & OPT_UNSET) ? "no-" : "",
|
||||
abbrev_option->long_name);
|
||||
return -3;
|
||||
return PARSE_OPT_HELP;
|
||||
}
|
||||
if (abbrev_option)
|
||||
return get_value(p, abbrev_option, all_opts, abbrev_flags);
|
||||
return -2;
|
||||
return PARSE_OPT_UNKNOWN;
|
||||
}
|
||||
|
||||
static int parse_nodash_opt(struct parse_opt_ctx_t *p, const char *arg,
|
||||
@ -400,6 +426,19 @@ static void parse_options_check(const struct option *opts)
|
||||
if ((opts->flags & PARSE_OPT_OPTARG) ||
|
||||
!(opts->flags & PARSE_OPT_NOARG))
|
||||
err |= optbug(opts, "should not accept an argument");
|
||||
break;
|
||||
case OPTION_CALLBACK:
|
||||
if (!opts->callback && !opts->ll_callback)
|
||||
BUG("OPTION_CALLBACK needs one callback");
|
||||
if (opts->callback && opts->ll_callback)
|
||||
BUG("OPTION_CALLBACK can't have two callbacks");
|
||||
break;
|
||||
case OPTION_LOWLEVEL_CALLBACK:
|
||||
if (!opts->ll_callback)
|
||||
BUG("OPTION_LOWLEVEL_CALLBACK needs a callback");
|
||||
if (opts->callback)
|
||||
BUG("OPTION_LOWLEVEL_CALLBACK needs no high level callback");
|
||||
break;
|
||||
default:
|
||||
; /* ok. (usually accepts an argument) */
|
||||
}
|
||||
@ -416,15 +455,24 @@ void parse_options_start(struct parse_opt_ctx_t *ctx,
|
||||
const struct option *options, int flags)
|
||||
{
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
ctx->argc = ctx->total = argc - 1;
|
||||
ctx->argv = argv + 1;
|
||||
ctx->out = argv;
|
||||
ctx->argc = argc;
|
||||
ctx->argv = argv;
|
||||
if (!(flags & PARSE_OPT_ONE_SHOT)) {
|
||||
ctx->argc--;
|
||||
ctx->argv++;
|
||||
}
|
||||
ctx->total = ctx->argc;
|
||||
ctx->out = argv;
|
||||
ctx->prefix = prefix;
|
||||
ctx->cpidx = ((flags & PARSE_OPT_KEEP_ARGV0) != 0);
|
||||
ctx->flags = flags;
|
||||
if ((flags & PARSE_OPT_KEEP_UNKNOWN) &&
|
||||
(flags & PARSE_OPT_STOP_AT_NON_OPTION))
|
||||
(flags & PARSE_OPT_STOP_AT_NON_OPTION) &&
|
||||
!(flags & PARSE_OPT_ONE_SHOT))
|
||||
BUG("STOP_AT_NON_OPTION and KEEP_UNKNOWN don't go together");
|
||||
if ((flags & PARSE_OPT_ONE_SHOT) &&
|
||||
(flags & PARSE_OPT_KEEP_ARGV0))
|
||||
BUG("Can't keep argv0 if you don't have it");
|
||||
parse_options_check(options);
|
||||
}
|
||||
|
||||
@ -536,6 +584,10 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
|
||||
for (; ctx->argc; ctx->argc--, ctx->argv++) {
|
||||
const char *arg = ctx->argv[0];
|
||||
|
||||
if (ctx->flags & PARSE_OPT_ONE_SHOT &&
|
||||
ctx->argc != ctx->total)
|
||||
break;
|
||||
|
||||
if (*arg != '-' || !arg[1]) {
|
||||
if (parse_nodash_opt(ctx, arg, options) == 0)
|
||||
continue;
|
||||
@ -556,22 +608,28 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
|
||||
if (arg[1] != '-') {
|
||||
ctx->opt = arg + 1;
|
||||
switch (parse_short_opt(ctx, options)) {
|
||||
case -1:
|
||||
case PARSE_OPT_ERROR:
|
||||
return PARSE_OPT_ERROR;
|
||||
case -2:
|
||||
case PARSE_OPT_UNKNOWN:
|
||||
if (ctx->opt)
|
||||
check_typos(arg + 1, options);
|
||||
if (internal_help && *ctx->opt == 'h')
|
||||
goto show_usage;
|
||||
goto unknown;
|
||||
case PARSE_OPT_NON_OPTION:
|
||||
case PARSE_OPT_HELP:
|
||||
case PARSE_OPT_COMPLETE:
|
||||
BUG("parse_short_opt() cannot return these");
|
||||
case PARSE_OPT_DONE:
|
||||
break;
|
||||
}
|
||||
if (ctx->opt)
|
||||
check_typos(arg + 1, options);
|
||||
while (ctx->opt) {
|
||||
switch (parse_short_opt(ctx, options)) {
|
||||
case -1:
|
||||
case PARSE_OPT_ERROR:
|
||||
return PARSE_OPT_ERROR;
|
||||
case -2:
|
||||
case PARSE_OPT_UNKNOWN:
|
||||
if (internal_help && *ctx->opt == 'h')
|
||||
goto show_usage;
|
||||
|
||||
@ -583,6 +641,12 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
|
||||
ctx->argv[0] = xstrdup(ctx->opt - 1);
|
||||
*(char *)ctx->argv[0] = '-';
|
||||
goto unknown;
|
||||
case PARSE_OPT_NON_OPTION:
|
||||
case PARSE_OPT_COMPLETE:
|
||||
case PARSE_OPT_HELP:
|
||||
BUG("parse_short_opt() cannot return these");
|
||||
case PARSE_OPT_DONE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
@ -601,15 +665,22 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
|
||||
if (internal_help && !strcmp(arg + 2, "help"))
|
||||
goto show_usage;
|
||||
switch (parse_long_opt(ctx, arg + 2, options)) {
|
||||
case -1:
|
||||
case PARSE_OPT_ERROR:
|
||||
return PARSE_OPT_ERROR;
|
||||
case -2:
|
||||
case PARSE_OPT_UNKNOWN:
|
||||
goto unknown;
|
||||
case -3:
|
||||
case PARSE_OPT_HELP:
|
||||
goto show_usage;
|
||||
case PARSE_OPT_NON_OPTION:
|
||||
case PARSE_OPT_COMPLETE:
|
||||
BUG("parse_long_opt() cannot return these");
|
||||
case PARSE_OPT_DONE:
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
unknown:
|
||||
if (ctx->flags & PARSE_OPT_ONE_SHOT)
|
||||
break;
|
||||
if (!(ctx->flags & PARSE_OPT_KEEP_UNKNOWN))
|
||||
return PARSE_OPT_UNKNOWN;
|
||||
ctx->out[ctx->cpidx++] = ctx->argv[0];
|
||||
@ -623,6 +694,9 @@ unknown:
|
||||
|
||||
int parse_options_end(struct parse_opt_ctx_t *ctx)
|
||||
{
|
||||
if (ctx->flags & PARSE_OPT_ONE_SHOT)
|
||||
return ctx->total - ctx->argc;
|
||||
|
||||
MOVE_ARRAY(ctx->out + ctx->cpidx, ctx->argv, ctx->argc);
|
||||
ctx->out[ctx->cpidx + ctx->argc] = NULL;
|
||||
return ctx->cpidx + ctx->argc;
|
||||
|
108
parse-options.h
108
parse-options.h
@ -10,6 +10,7 @@ enum parse_opt_type {
|
||||
/* options with no arguments */
|
||||
OPTION_BIT,
|
||||
OPTION_NEGBIT,
|
||||
OPTION_BITOP,
|
||||
OPTION_COUNTUP,
|
||||
OPTION_SET_INT,
|
||||
OPTION_CMDMODE,
|
||||
@ -27,7 +28,8 @@ enum parse_opt_flags {
|
||||
PARSE_OPT_STOP_AT_NON_OPTION = 2,
|
||||
PARSE_OPT_KEEP_ARGV0 = 4,
|
||||
PARSE_OPT_KEEP_UNKNOWN = 8,
|
||||
PARSE_OPT_NO_INTERNAL_HELP = 16
|
||||
PARSE_OPT_NO_INTERNAL_HELP = 16,
|
||||
PARSE_OPT_ONE_SHOT = 32
|
||||
};
|
||||
|
||||
enum parse_opt_option_flags {
|
||||
@ -47,8 +49,9 @@ struct option;
|
||||
typedef int parse_opt_cb(const struct option *, const char *arg, int unset);
|
||||
|
||||
struct parse_opt_ctx_t;
|
||||
typedef int parse_opt_ll_cb(struct parse_opt_ctx_t *ctx,
|
||||
const struct option *opt, int unset);
|
||||
typedef enum parse_opt_result parse_opt_ll_cb(struct parse_opt_ctx_t *ctx,
|
||||
const struct option *opt,
|
||||
const char *arg, int unset);
|
||||
|
||||
/*
|
||||
* `type`::
|
||||
@ -98,13 +101,16 @@ typedef int parse_opt_ll_cb(struct parse_opt_ctx_t *ctx,
|
||||
* the option takes optional argument.
|
||||
*
|
||||
* `callback`::
|
||||
* pointer to the callback to use for OPTION_CALLBACK or
|
||||
* OPTION_LOWLEVEL_CALLBACK.
|
||||
* pointer to the callback to use for OPTION_CALLBACK
|
||||
*
|
||||
* `defval`::
|
||||
* default value to fill (*->value) with for PARSE_OPT_OPTARG.
|
||||
* OPTION_{BIT,SET_INT} store the {mask,integer} to put in the value when met.
|
||||
* CALLBACKS can use it like they want.
|
||||
*
|
||||
* `ll_callback`::
|
||||
* pointer to the callback to use for OPTION_LOWLEVEL_CALLBACK
|
||||
*
|
||||
*/
|
||||
struct option {
|
||||
enum parse_opt_type type;
|
||||
@ -117,6 +123,8 @@ struct option {
|
||||
int flags;
|
||||
parse_opt_cb *callback;
|
||||
intptr_t defval;
|
||||
parse_opt_ll_cb *ll_callback;
|
||||
intptr_t extra;
|
||||
};
|
||||
|
||||
#define OPT_BIT_F(s, l, v, h, b, f) { OPTION_BIT, (s), (l), (v), NULL, (h), \
|
||||
@ -126,12 +134,17 @@ struct option {
|
||||
#define OPT_SET_INT_F(s, l, v, h, i, f) { OPTION_SET_INT, (s), (l), (v), NULL, \
|
||||
(h), PARSE_OPT_NOARG | (f), NULL, (i) }
|
||||
#define OPT_BOOL_F(s, l, v, h, f) OPT_SET_INT_F(s, l, v, h, 1, f)
|
||||
#define OPT_CALLBACK_F(s, l, v, a, h, f, cb) \
|
||||
{ OPTION_CALLBACK, (s), (l), (v), (a), (h), (f), (cb) }
|
||||
|
||||
#define OPT_END() { OPTION_END }
|
||||
#define OPT_ARGUMENT(l, h) { OPTION_ARGUMENT, 0, (l), NULL, NULL, \
|
||||
(h), PARSE_OPT_NOARG}
|
||||
#define OPT_GROUP(h) { OPTION_GROUP, 0, NULL, NULL, NULL, (h) }
|
||||
#define OPT_BIT(s, l, v, h, b) OPT_BIT_F(s, l, v, h, b, 0)
|
||||
#define OPT_BITOP(s, l, v, h, set, clear) { OPTION_BITOP, (s), (l), (v), NULL, (h), \
|
||||
PARSE_OPT_NOARG|PARSE_OPT_NONEG, NULL, \
|
||||
(set), NULL, (clear) }
|
||||
#define OPT_NEGBIT(s, l, v, h, b) { OPTION_NEGBIT, (s), (l), (v), NULL, \
|
||||
(h), PARSE_OPT_NOARG, NULL, (b) }
|
||||
#define OPT_COUNTUP(s, l, v, h) OPT_COUNTUP_F(s, l, v, h, 0)
|
||||
@ -153,8 +166,7 @@ struct option {
|
||||
#define OPT_EXPIRY_DATE(s, l, v, h) \
|
||||
{ OPTION_CALLBACK, (s), (l), (v), N_("expiry-date"),(h), 0, \
|
||||
parse_opt_expiry_date_cb }
|
||||
#define OPT_CALLBACK(s, l, v, a, h, f) \
|
||||
{ OPTION_CALLBACK, (s), (l), (v), (a), (h), 0, (f) }
|
||||
#define OPT_CALLBACK(s, l, v, a, h, f) OPT_CALLBACK_F(s, l, v, a, h, 0, f)
|
||||
#define OPT_NUMBER_CALLBACK(v, h, f) \
|
||||
{ OPTION_NUMBER, 0, NULL, (v), NULL, (h), \
|
||||
PARSE_OPT_NOARG | PARSE_OPT_NONEG, (f) }
|
||||
@ -169,23 +181,31 @@ struct option {
|
||||
N_("no-op (backward compatibility)"), \
|
||||
PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, parse_opt_noop_cb }
|
||||
|
||||
/* parse_options() will filter out the processed options and leave the
|
||||
* non-option arguments in argv[]. usagestr strings should be marked
|
||||
* for translation with N_().
|
||||
/*
|
||||
* parse_options() will filter out the processed options and leave the
|
||||
* non-option arguments in argv[]. argv0 is assumed program name and
|
||||
* skipped.
|
||||
*
|
||||
* usagestr strings should be marked for translation with N_().
|
||||
*
|
||||
* Returns the number of arguments left in argv[].
|
||||
*
|
||||
* In one-shot mode, argv0 is not a program name, argv[] is left
|
||||
* untouched and parse_options() returns the number of options
|
||||
* processed.
|
||||
*/
|
||||
extern int parse_options(int argc, const char **argv, const char *prefix,
|
||||
const struct option *options,
|
||||
const char * const usagestr[], int flags);
|
||||
int parse_options(int argc, const char **argv, const char *prefix,
|
||||
const struct option *options,
|
||||
const char * const usagestr[], int flags);
|
||||
|
||||
extern NORETURN void usage_with_options(const char * const *usagestr,
|
||||
const struct option *options);
|
||||
NORETURN void usage_with_options(const char * const *usagestr,
|
||||
const struct option *options);
|
||||
|
||||
extern NORETURN void usage_msg_opt(const char *msg,
|
||||
const char * const *usagestr,
|
||||
const struct option *options);
|
||||
NORETURN void usage_msg_opt(const char *msg,
|
||||
const char * const *usagestr,
|
||||
const struct option *options);
|
||||
|
||||
extern int optbug(const struct option *opt, const char *reason);
|
||||
int optbug(const struct option *opt, const char *reason);
|
||||
const char *optname(const struct option *opt, int flags);
|
||||
|
||||
/*
|
||||
@ -204,12 +224,12 @@ const char *optname(const struct option *opt, int flags);
|
||||
|
||||
/*----- incremental advanced APIs -----*/
|
||||
|
||||
enum {
|
||||
PARSE_OPT_COMPLETE = -2,
|
||||
PARSE_OPT_HELP = -1,
|
||||
PARSE_OPT_DONE,
|
||||
enum parse_opt_result {
|
||||
PARSE_OPT_COMPLETE = -3,
|
||||
PARSE_OPT_HELP = -2,
|
||||
PARSE_OPT_ERROR = -1, /* must be the same as error() */
|
||||
PARSE_OPT_DONE = 0, /* fixed so that "return 0" works */
|
||||
PARSE_OPT_NON_OPTION,
|
||||
PARSE_OPT_ERROR,
|
||||
PARSE_OPT_UNKNOWN
|
||||
};
|
||||
|
||||
@ -227,31 +247,31 @@ struct parse_opt_ctx_t {
|
||||
const char *prefix;
|
||||
};
|
||||
|
||||
extern void parse_options_start(struct parse_opt_ctx_t *ctx,
|
||||
int argc, const char **argv, const char *prefix,
|
||||
const struct option *options, int flags);
|
||||
void parse_options_start(struct parse_opt_ctx_t *ctx,
|
||||
int argc, const char **argv, const char *prefix,
|
||||
const struct option *options, int flags);
|
||||
|
||||
extern int parse_options_step(struct parse_opt_ctx_t *ctx,
|
||||
const struct option *options,
|
||||
const char * const usagestr[]);
|
||||
int parse_options_step(struct parse_opt_ctx_t *ctx,
|
||||
const struct option *options,
|
||||
const char * const usagestr[]);
|
||||
|
||||
extern int parse_options_end(struct parse_opt_ctx_t *ctx);
|
||||
int parse_options_end(struct parse_opt_ctx_t *ctx);
|
||||
|
||||
extern struct option *parse_options_concat(struct option *a, struct option *b);
|
||||
struct option *parse_options_concat(struct option *a, struct option *b);
|
||||
|
||||
/*----- some often used options -----*/
|
||||
extern int parse_opt_abbrev_cb(const struct option *, const char *, int);
|
||||
extern int parse_opt_expiry_date_cb(const struct option *, const char *, int);
|
||||
extern int parse_opt_color_flag_cb(const struct option *, const char *, int);
|
||||
extern int parse_opt_verbosity_cb(const struct option *, const char *, int);
|
||||
extern int parse_opt_object_name(const struct option *, const char *, int);
|
||||
extern int parse_opt_commits(const struct option *, const char *, int);
|
||||
extern int parse_opt_tertiary(const struct option *, const char *, int);
|
||||
extern int parse_opt_string_list(const struct option *, const char *, int);
|
||||
extern int parse_opt_noop_cb(const struct option *, const char *, int);
|
||||
extern int parse_opt_unknown_cb(const struct option *, const char *, int);
|
||||
extern int parse_opt_passthru(const struct option *, const char *, int);
|
||||
extern int parse_opt_passthru_argv(const struct option *, const char *, int);
|
||||
int parse_opt_abbrev_cb(const struct option *, const char *, int);
|
||||
int parse_opt_expiry_date_cb(const struct option *, const char *, int);
|
||||
int parse_opt_color_flag_cb(const struct option *, const char *, int);
|
||||
int parse_opt_verbosity_cb(const struct option *, const char *, int);
|
||||
int parse_opt_object_name(const struct option *, const char *, int);
|
||||
int parse_opt_commits(const struct option *, const char *, int);
|
||||
int parse_opt_tertiary(const struct option *, const char *, int);
|
||||
int parse_opt_string_list(const struct option *, const char *, int);
|
||||
int parse_opt_noop_cb(const struct option *, const char *, int);
|
||||
int parse_opt_unknown_cb(struct parse_opt_ctx_t *ctx, const struct option *, const char *, int);
|
||||
int parse_opt_passthru(const struct option *, const char *, int);
|
||||
int parse_opt_passthru_argv(const struct option *, const char *, int);
|
||||
|
||||
#define OPT__VERBOSE(var, h) OPT_COUNTUP('v', "verbose", (var), (h))
|
||||
#define OPT__QUIET(var, h) OPT_COUNTUP('q', "quiet", (var), (h))
|
||||
|
@ -546,7 +546,7 @@ do
|
||||
done >actual
|
||||
EOF
|
||||
|
||||
test_expect_success SYMLINKS 'difftool --dir-diff --symlink without unstaged changes' '
|
||||
test_expect_success SYMLINKS 'difftool --dir-diff --symlinks without unstaged changes' '
|
||||
cat >expect <<-EOF &&
|
||||
file
|
||||
$PWD/file
|
||||
@ -555,7 +555,7 @@ test_expect_success SYMLINKS 'difftool --dir-diff --symlink without unstaged cha
|
||||
sub/sub
|
||||
$PWD/sub/sub
|
||||
EOF
|
||||
git difftool --dir-diff --symlink \
|
||||
git difftool --dir-diff --symlinks \
|
||||
--extcmd "./.git/CHECK_SYMLINKS" branch HEAD &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
Loading…
Reference in New Issue
Block a user