Commit 54b469b9 authored by Junio C Hamano's avatar Junio C Hamano

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
parents 7d0c1f45 ed881486
......@@ -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),
......
......@@ -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");
......
......@@ -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;
}
/**
......
......@@ -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:
{
const char *p_arg = NULL;
int p_unset;
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))
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;
return (*opt->callback)(opt, arg, 0) ? (-1) : 0;
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 @@ static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,
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 @@ static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,
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 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
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;
......
......@@ -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 par