2007-10-14 23:35:37 +00:00
|
|
|
#include "git-compat-util.h"
|
|
|
|
#include "parse-options.h"
|
2008-06-23 20:55:11 +00:00
|
|
|
#include "cache.h"
|
2017-06-14 18:07:36 +00:00
|
|
|
#include "config.h"
|
2009-01-26 14:13:23 +00:00
|
|
|
#include "commit.h"
|
Add an optional argument for --color options
Make git-branch, git-show-branch, git-grep, and all the diff-based
programs accept an optional argument <when> for --color. The argument
is a colorbool: "always", "never", or "auto". If no argument is given,
"always" is used; --no-color is an alias for --color=never. This makes
the command-line interface consistent with other GNU tools, such as `ls'
and `grep', and with the git-config color options. Note that, without
an argument, --color and --no-color work exactly as before.
To implement this, two internal changes were made:
1. Allow the first argument of git_config_colorbool() to be NULL,
in which case it returns -1 if the argument isn't "always", "never",
or "auto".
2. Add OPT_COLOR_FLAG(), OPT__COLOR(), and parse_opt_color_flag_cb()
to the option parsing library. The callback uses
git_config_colorbool(), so color.h is now a dependency
of parse-options.c.
Signed-off-by: Mark Lodato <lodatom@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2010-02-17 04:55:58 +00:00
|
|
|
#include "color.h"
|
2013-02-09 06:31:09 +00:00
|
|
|
#include "utf8.h"
|
2007-10-14 23:35:37 +00:00
|
|
|
|
tests: disallow the use of abbreviated options (by default)
Git's command-line parsers support uniquely abbreviated options, e.g.
`git init --ba` would automatically expand `--ba` to `--bare`.
This is a very convenient feature in every day life for Git users, in
particular when tab completion is not available.
However, it is not a good idea to rely on that in Git's test suite, as
something that is a unique abbreviation of a command line option today
might no longer be a unique abbreviation tomorrow.
For example, if a future contribution added a new mode
`git init --babyproofing` and a previously-introduced test case used the
fact that `git init --ba` expanded to `git init --bare`, that future
contribution would now have to touch seemingly unrelated tests just to
keep the test suite from failing.
So let's disallow abbreviated options in the test suite by default.
Note: for ease of implementation, this patch really only touches the
`parse-options` machinery: more and more hand-rolled option parsers are
converted to use that internal API, and more and more scripts are
converted to built-ins (naturally using the parse-options API, too), so
in practice this catches most issues, and is definitely the biggest bang
for the buck.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-04-12 09:37:24 +00:00
|
|
|
static int disallow_abbreviated_options;
|
|
|
|
|
2007-10-14 23:35:37 +00:00
|
|
|
#define OPT_SHORT 1
|
|
|
|
#define OPT_UNSET 2
|
|
|
|
|
2011-08-11 09:15:37 +00:00
|
|
|
int optbug(const struct option *opt, const char *reason)
|
2010-12-02 06:01:18 +00:00
|
|
|
{
|
2014-09-03 19:42:37 +00:00
|
|
|
if (opt->long_name) {
|
|
|
|
if (opt->short_name)
|
|
|
|
return error("BUG: switch '%c' (--%s) %s",
|
|
|
|
opt->short_name, opt->long_name, reason);
|
2010-12-02 06:01:18 +00:00
|
|
|
return error("BUG: option '%s' %s", opt->long_name, reason);
|
2014-09-03 19:42:37 +00:00
|
|
|
}
|
2010-12-02 06:01:18 +00:00
|
|
|
return error("BUG: switch '%c' %s", opt->short_name, reason);
|
|
|
|
}
|
|
|
|
|
2019-01-27 00:35:27 +00:00
|
|
|
static enum parse_opt_result get_arg(struct parse_opt_ctx_t *p,
|
|
|
|
const struct option *opt,
|
|
|
|
int flags, const char **arg)
|
2008-07-08 10:34:08 +00:00
|
|
|
{
|
|
|
|
if (p->opt) {
|
|
|
|
*arg = p->opt;
|
|
|
|
p->opt = NULL;
|
|
|
|
} else if (p->argc == 1 && (opt->flags & PARSE_OPT_LASTARG_DEFAULT)) {
|
|
|
|
*arg = (const char *)opt->defval;
|
2008-07-21 18:30:36 +00:00
|
|
|
} else if (p->argc > 1) {
|
2008-07-08 10:34:08 +00:00
|
|
|
p->argc--;
|
|
|
|
*arg = *++p->argv;
|
|
|
|
} else
|
2018-11-10 05:16:11 +00:00
|
|
|
return error(_("%s requires a value"), optname(opt, flags));
|
2008-07-08 10:34:08 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-05-23 18:53:13 +00:00
|
|
|
static void fix_filename(const char *prefix, const char **file)
|
|
|
|
{
|
|
|
|
if (!file || !*file || !prefix || is_absolute_path(*file)
|
|
|
|
|| !strcmp("-", *file))
|
|
|
|
return;
|
2017-03-21 01:28:49 +00:00
|
|
|
*file = prefix_filename(prefix, *file);
|
2009-05-23 18:53:13 +00:00
|
|
|
}
|
|
|
|
|
2019-01-27 00:35:27 +00:00
|
|
|
static enum parse_opt_result opt_command_mode_error(
|
|
|
|
const struct option *opt,
|
|
|
|
const struct option *all_opts,
|
|
|
|
int flags)
|
2013-07-30 19:06:01 +00:00
|
|
|
{
|
|
|
|
const struct option *that;
|
|
|
|
struct strbuf that_name = STRBUF_INIT;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find the other option that was used to set the variable
|
|
|
|
* already, and report that this is not compatible with it.
|
|
|
|
*/
|
|
|
|
for (that = all_opts; that->type != OPTION_END; that++) {
|
|
|
|
if (that == opt ||
|
2020-02-20 14:15:16 +00:00
|
|
|
!(that->flags & PARSE_OPT_CMDMODE) ||
|
2013-07-30 19:06:01 +00:00
|
|
|
that->value != opt->value ||
|
|
|
|
that->defval != *(int *)opt->value)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (that->long_name)
|
|
|
|
strbuf_addf(&that_name, "--%s", that->long_name);
|
|
|
|
else
|
|
|
|
strbuf_addf(&that_name, "-%c", that->short_name);
|
2018-11-10 05:16:11 +00:00
|
|
|
error(_("%s is incompatible with %s"),
|
|
|
|
optname(opt, flags), that_name.buf);
|
2013-07-30 19:06:01 +00:00
|
|
|
strbuf_release(&that_name);
|
2019-01-27 00:35:27 +00:00
|
|
|
return PARSE_OPT_ERROR;
|
2013-07-30 19:06:01 +00:00
|
|
|
}
|
2018-11-10 05:16:11 +00:00
|
|
|
return error(_("%s : incompatible with something else"),
|
|
|
|
optname(opt, flags));
|
2013-07-30 19:06:01 +00:00
|
|
|
}
|
|
|
|
|
2019-01-27 00:35:27 +00:00
|
|
|
static enum parse_opt_result get_value(struct parse_opt_ctx_t *p,
|
|
|
|
const struct option *opt,
|
|
|
|
const struct option *all_opts,
|
|
|
|
int flags)
|
2007-10-14 23:35:37 +00:00
|
|
|
{
|
2007-10-14 23:45:45 +00:00
|
|
|
const char *s, *arg;
|
2007-11-07 10:20:27 +00:00
|
|
|
const int unset = flags & OPT_UNSET;
|
2009-05-23 18:53:13 +00:00
|
|
|
int err;
|
2007-10-14 23:35:37 +00:00
|
|
|
|
2007-11-07 10:20:27 +00:00
|
|
|
if (unset && p->opt)
|
2018-11-10 05:16:11 +00:00
|
|
|
return error(_("%s takes no value"), optname(opt, flags));
|
2007-11-07 10:20:27 +00:00
|
|
|
if (unset && (opt->flags & PARSE_OPT_NONEG))
|
2018-11-10 05:16:11 +00:00
|
|
|
return error(_("%s isn't available"), optname(opt, flags));
|
2010-12-01 23:30:40 +00:00
|
|
|
if (!(flags & OPT_SHORT) && p->opt && (opt->flags & PARSE_OPT_NOARG))
|
2018-11-10 05:16:11 +00:00
|
|
|
return error(_("%s takes no value"), optname(opt, flags));
|
2007-11-07 10:20:27 +00:00
|
|
|
|
2020-02-20 14:15:16 +00:00
|
|
|
/*
|
|
|
|
* Giving the same mode option twice, although unnecessary,
|
|
|
|
* is not a grave error, so let it pass.
|
|
|
|
*/
|
|
|
|
if ((opt->flags & PARSE_OPT_CMDMODE) &&
|
|
|
|
*(int *)opt->value && *(int *)opt->value != opt->defval)
|
|
|
|
return opt_command_mode_error(opt, all_opts, flags);
|
|
|
|
|
2007-11-07 10:20:27 +00:00
|
|
|
switch (opt->type) {
|
parse-options: allow git commands to invent new option types
parse-options provides a variety of option behaviors, including
OPTION_CALLBACK, which should take care of just about any sane
behavior. All supported behaviors obey the following constraint:
A --foo option can only accept (and base its behavior on)
one argument, which would be the following command-line
argument in the "unsticked" form.
Alas, some existing git commands have options that do not obey that
constraint. For example, update-index --cacheinfo takes three
arguments, and update-index --resolve takes all later parameters as
arguments.
Introduces an OPTION_LOWLEVEL_CALLBACK backdoor to parse-options so
such option types can be supported without tempting inventors of other
commands through mention in the public API. Commands can set the
callback field to a function accepting three arguments: the option
parsing context, the option itself, and a flag indicating whether the
the option was negated. When the option is encountered, that function
is called to take over from get_value(). The return value should be
zero for success, -1 for usage errors.
Thanks to Stephen Boyd for API guidance.
Improved-by: Stephen Boyd <bebarino@gmail.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2010-12-01 23:32:16 +00:00
|
|
|
case OPTION_LOWLEVEL_CALLBACK:
|
2019-01-27 00:35:28 +00:00
|
|
|
return opt->ll_callback(p, opt, NULL, unset);
|
parse-options: allow git commands to invent new option types
parse-options provides a variety of option behaviors, including
OPTION_CALLBACK, which should take care of just about any sane
behavior. All supported behaviors obey the following constraint:
A --foo option can only accept (and base its behavior on)
one argument, which would be the following command-line
argument in the "unsticked" form.
Alas, some existing git commands have options that do not obey that
constraint. For example, update-index --cacheinfo takes three
arguments, and update-index --resolve takes all later parameters as
arguments.
Introduces an OPTION_LOWLEVEL_CALLBACK backdoor to parse-options so
such option types can be supported without tempting inventors of other
commands through mention in the public API. Commands can set the
callback field to a function accepting three arguments: the option
parsing context, the option itself, and a flag indicating whether the
the option was negated. When the option is encountered, that function
is called to take over from get_value(). The return value should be
zero for success, -1 for usage errors.
Thanks to Stephen Boyd for API guidance.
Improved-by: Stephen Boyd <bebarino@gmail.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2010-12-01 23:32:16 +00:00
|
|
|
|
2007-11-07 10:20:27 +00:00
|
|
|
case OPTION_BIT:
|
|
|
|
if (unset)
|
|
|
|
*(int *)opt->value &= ~opt->defval;
|
2007-10-14 23:35:37 +00:00
|
|
|
else
|
2007-11-07 10:20:27 +00:00
|
|
|
*(int *)opt->value |= opt->defval;
|
|
|
|
return 0;
|
|
|
|
|
2009-05-07 19:44:17 +00:00
|
|
|
case OPTION_NEGBIT:
|
|
|
|
if (unset)
|
|
|
|
*(int *)opt->value |= opt->defval;
|
|
|
|
else
|
|
|
|
*(int *)opt->value &= ~opt->defval;
|
|
|
|
return 0;
|
|
|
|
|
2019-01-27 00:35:25 +00:00
|
|
|
case OPTION_BITOP:
|
|
|
|
if (unset)
|
|
|
|
BUG("BITOP can't have unset form");
|
|
|
|
*(int *)opt->value &= ~opt->extra;
|
|
|
|
*(int *)opt->value |= opt->defval;
|
|
|
|
return 0;
|
|
|
|
|
parse-options: deprecate OPT_BOOLEAN
It is natural to expect that an option defined with OPT_BOOLEAN() could be
used in this way:
int option = -1; /* unspecified */
struct option options[] = {
OPT_BOOLEAN(0, "option", &option, "set option"),
OPT_END()
};
parse_options(ac, av, prefix, options, usage, 0);
if (option < 0)
... do the default thing ...
else if (!option)
... --no-option was given ...
else
... --option was given ...
to easily tell three cases apart:
- There is no mention of the `--option` on the command line;
- The variable is positively set with `--option`; or
- The variable is explicitly negated with `--no-option`.
Unfortunately, this is not the case. OPT_BOOLEAN() increments the variable
every time `--option` is given, and resets it to zero when `--no-option`
is given.
As a first step to remedy this, introduce a true boolean OPT_BOOL(), and
rename OPT_BOOLEAN() to OPT_COUNTUP(). To help transitioning, OPT_BOOLEAN
and OPTION_BOOLEAN are defined as deprecated synonyms to OPT_COUNTUP and
OPTION_COUNTUP respectively.
This is what db7244b (parse-options new features., 2007-11-07) from four
years ago started by marking OPTION_BOOLEAN as "INCR would have been a
better name".
Some existing users do depend on the count-up semantics; for example,
users of OPT__VERBOSE() could use it to raise the verbosity level with
repeated use of `-v` on the command line, but they probably should be
rewritten to use OPT__VERBOSITY() instead these days. I suspect that some
users of OPT__FORCE() may also use it to implement different level of
forcibleness but I didn't check.
On top of this patch, here are the remaining clean-up tasks that other
people can help:
- Look at each hit in "git grep -e OPT_BOOLEAN"; trace all uses of the
value that is set to the underlying variable, and if it can proven that
the variable is only used as a boolean, replace it with OPT_BOOL(). If
the caller does depend on the count-up semantics, replace it with
OPT_COUNTUP() instead.
- Same for OPTION_BOOLEAN; replace it with OPTION_SET_INT and arrange to
set 1 to the variable for a true boolean, and otherwise replace it with
OPTION_COUNTUP.
- Look at each hit in "git grep -e OPT__VERBOSE -e OPT__QUIET" and see if
they can be replaced with OPT__VERBOSITY().
I'll follow this message up with a separate patch as an example.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-09-27 23:56:49 +00:00
|
|
|
case OPTION_COUNTUP:
|
parse-options.c: make OPTION_COUNTUP respect "unspecified" values
OPT_COUNTUP() merely increments the counter upon --option, and resets it
to 0 upon --no-option, which means that there is no "unspecified" value
with which a client can initialize the counter to determine whether or
not --[no]-option was seen at all.
Make OPT_COUNTUP() treat any negative number as an "unspecified" value
to address this shortcoming. In particular, if a client initializes the
counter to -1, then if it is still -1 after parse_options(), then
neither --option nor --no-option was seen; if it is 0, then --no-option
was seen last, and if it is 1 or greater, than --option was seen last.
This change does not affect the behavior of existing clients because
they all use the initial value of 0 (or more).
Note that builtin/clean.c initializes the variable used with
OPT__FORCE (which uses OPT_COUNTUP()) to a negative value, but it is set
to either 0 or 1 by reading the configuration before the code calls
parse_options(), i.e. as far as parse_options() is concerned, the
initial value of the variable is not negative.
To test this behavior, in test-parse-options.c, "verbose" is set to
"unspecified" while quiet is set to 0 which will test the new behavior
with all sets of values.
Helped-by: Jeff King <peff@peff.net>
Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Helped-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Pranit Bauva <pranit.bauva@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-05-05 09:50:00 +00:00
|
|
|
if (*(int *)opt->value < 0)
|
|
|
|
*(int *)opt->value = 0;
|
2007-11-07 10:20:27 +00:00
|
|
|
*(int *)opt->value = unset ? 0 : *(int *)opt->value + 1;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case OPTION_SET_INT:
|
|
|
|
*(int *)opt->value = unset ? 0 : opt->defval;
|
|
|
|
return 0;
|
|
|
|
|
2007-10-14 23:35:37 +00:00
|
|
|
case OPTION_STRING:
|
2008-07-08 10:34:08 +00:00
|
|
|
if (unset)
|
2007-11-07 10:20:27 +00:00
|
|
|
*(const char **)opt->value = NULL;
|
2008-07-08 10:34:08 +00:00
|
|
|
else if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
|
2007-10-14 23:45:45 +00:00
|
|
|
*(const char **)opt->value = (const char *)opt->defval;
|
2008-07-08 10:34:08 +00:00
|
|
|
else
|
|
|
|
return get_arg(p, opt, flags, (const char **)opt->value);
|
2007-10-14 23:35:37 +00:00
|
|
|
return 0;
|
|
|
|
|
2009-05-23 18:53:13 +00:00
|
|
|
case OPTION_FILENAME:
|
|
|
|
err = 0;
|
|
|
|
if (unset)
|
|
|
|
*(const char **)opt->value = NULL;
|
|
|
|
else if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
|
|
|
|
*(const char **)opt->value = (const char *)opt->defval;
|
|
|
|
else
|
|
|
|
err = get_arg(p, opt, flags, (const char **)opt->value);
|
|
|
|
|
|
|
|
if (!err)
|
|
|
|
fix_filename(p->prefix, (const char **)opt->value);
|
|
|
|
return err;
|
|
|
|
|
2007-10-14 23:45:45 +00:00
|
|
|
case OPTION_CALLBACK:
|
2019-01-27 00:35:28 +00:00
|
|
|
{
|
|
|
|
const char *p_arg = NULL;
|
|
|
|
int p_unset;
|
|
|
|
|
2007-11-07 10:20:27 +00:00
|
|
|
if (unset)
|
2019-01-27 00:35:28 +00:00
|
|
|
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))
|
2008-07-08 10:34:08 +00:00
|
|
|
return -1;
|
2019-01-27 00:35:28 +00:00
|
|
|
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);
|
|
|
|
}
|
2007-10-14 23:35:37 +00:00
|
|
|
case OPTION_INTEGER:
|
2007-11-07 10:20:27 +00:00
|
|
|
if (unset) {
|
2007-10-14 23:35:37 +00:00
|
|
|
*(int *)opt->value = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
2007-12-21 10:41:41 +00:00
|
|
|
if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
|
2007-10-14 23:45:45 +00:00
|
|
|
*(int *)opt->value = opt->defval;
|
|
|
|
return 0;
|
|
|
|
}
|
2008-07-08 10:34:08 +00:00
|
|
|
if (get_arg(p, opt, flags, &arg))
|
|
|
|
return -1;
|
2019-05-29 09:11:16 +00:00
|
|
|
if (!*arg)
|
|
|
|
return error(_("%s expects a numerical value"),
|
|
|
|
optname(opt, flags));
|
2008-07-08 10:34:08 +00:00
|
|
|
*(int *)opt->value = strtol(arg, (char **)&s, 10);
|
2007-10-14 23:35:37 +00:00
|
|
|
if (*s)
|
2018-11-10 05:16:11 +00:00
|
|
|
return error(_("%s expects a numerical value"),
|
|
|
|
optname(opt, flags));
|
2007-10-14 23:35:37 +00:00
|
|
|
return 0;
|
|
|
|
|
2015-06-21 18:25:44 +00:00
|
|
|
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))
|
|
|
|
return -1;
|
|
|
|
if (!git_parse_ulong(arg, opt->value))
|
2018-11-10 05:16:11 +00:00
|
|
|
return error(_("%s expects a non-negative integer value"
|
|
|
|
" with an optional k/m/g suffix"),
|
|
|
|
optname(opt, flags));
|
2015-06-21 18:25:44 +00:00
|
|
|
return 0;
|
|
|
|
|
2007-10-14 23:35:37 +00:00
|
|
|
default:
|
2018-11-10 05:16:12 +00:00
|
|
|
BUG("opt->type %d should not happen", opt->type);
|
2007-10-14 23:35:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-27 00:35:27 +00:00
|
|
|
static enum parse_opt_result parse_short_opt(struct parse_opt_ctx_t *p,
|
|
|
|
const struct option *options)
|
2007-10-14 23:35:37 +00:00
|
|
|
{
|
2013-07-30 19:06:01 +00:00
|
|
|
const struct option *all_opts = options;
|
2009-05-07 19:45:08 +00:00
|
|
|
const struct option *numopt = NULL;
|
|
|
|
|
2007-10-14 23:35:37 +00:00
|
|
|
for (; options->type != OPTION_END; options++) {
|
|
|
|
if (options->short_name == *p->opt) {
|
|
|
|
p->opt = p->opt[1] ? p->opt + 1 : NULL;
|
2013-07-30 19:06:01 +00:00
|
|
|
return get_value(p, options, all_opts, OPT_SHORT);
|
2007-10-14 23:35:37 +00:00
|
|
|
}
|
2009-05-07 19:45:08 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Handle the numerical option later, explicit one-digit
|
|
|
|
* options take precedence over it.
|
|
|
|
*/
|
|
|
|
if (options->type == OPTION_NUMBER)
|
|
|
|
numopt = options;
|
|
|
|
}
|
|
|
|
if (numopt && isdigit(*p->opt)) {
|
|
|
|
size_t len = 1;
|
|
|
|
char *arg;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
while (isdigit(p->opt[len]))
|
|
|
|
len++;
|
|
|
|
arg = xmemdupz(p->opt, len);
|
|
|
|
p->opt = p->opt[len] ? p->opt + len : NULL;
|
2019-01-27 00:35:28 +00:00
|
|
|
if (numopt->callback)
|
|
|
|
rc = (*numopt->callback)(numopt, arg, 0) ? (-1) : 0;
|
|
|
|
else
|
|
|
|
rc = (*numopt->ll_callback)(p, numopt, arg, 0);
|
2009-05-07 19:45:08 +00:00
|
|
|
free(arg);
|
|
|
|
return rc;
|
2007-10-14 23:35:37 +00:00
|
|
|
}
|
2019-01-27 00:35:27 +00:00
|
|
|
return PARSE_OPT_UNKNOWN;
|
2007-10-14 23:35:37 +00:00
|
|
|
}
|
|
|
|
|
2019-04-29 10:05:25 +00:00
|
|
|
static int has_string(const char *it, const char **array)
|
|
|
|
{
|
|
|
|
while (*array)
|
|
|
|
if (!strcmp(it, *(array++)))
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int is_alias(struct parse_opt_ctx_t *ctx,
|
|
|
|
const struct option *one_opt,
|
|
|
|
const struct option *another_opt)
|
|
|
|
{
|
|
|
|
const char **group;
|
|
|
|
|
|
|
|
if (!ctx->alias_groups)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (!one_opt->long_name || !another_opt->long_name)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (group = ctx->alias_groups; *group; group += 3) {
|
|
|
|
/* it and other are from the same family? */
|
|
|
|
if (has_string(one_opt->long_name, group) &&
|
|
|
|
has_string(another_opt->long_name, group))
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-01-27 00:35:27 +00:00
|
|
|
static enum parse_opt_result parse_long_opt(
|
|
|
|
struct parse_opt_ctx_t *p, const char *arg,
|
|
|
|
const struct option *options)
|
2007-10-14 23:35:37 +00:00
|
|
|
{
|
2013-07-30 19:06:01 +00:00
|
|
|
const struct option *all_opts = options;
|
2014-03-08 06:48:31 +00:00
|
|
|
const char *arg_end = strchrnul(arg, '=');
|
2007-11-05 13:15:21 +00:00
|
|
|
const struct option *abbrev_option = NULL, *ambiguous_option = NULL;
|
|
|
|
int abbrev_flags = 0, ambiguous_flags = 0;
|
2007-10-14 16:54:06 +00:00
|
|
|
|
2007-10-14 23:35:37 +00:00
|
|
|
for (; options->type != OPTION_END; options++) {
|
2012-02-25 19:14:54 +00:00
|
|
|
const char *rest, *long_name = options->long_name;
|
|
|
|
int flags = 0, opt_flags = 0;
|
2007-10-14 23:35:37 +00:00
|
|
|
|
2012-02-25 19:14:54 +00:00
|
|
|
if (!long_name)
|
2007-10-14 23:35:37 +00:00
|
|
|
continue;
|
|
|
|
|
2012-02-25 19:14:54 +00:00
|
|
|
again:
|
refactor skip_prefix to return a boolean
The skip_prefix() function returns a pointer to the content
past the prefix, or NULL if the prefix was not found. While
this is nice and simple, in practice it makes it hard to use
for two reasons:
1. When you want to conditionally skip or keep the string
as-is, you have to introduce a temporary variable.
For example:
tmp = skip_prefix(buf, "foo");
if (tmp)
buf = tmp;
2. It is verbose to check the outcome in a conditional, as
you need extra parentheses to silence compiler
warnings. For example:
if ((cp = skip_prefix(buf, "foo"))
/* do something with cp */
Both of these make it harder to use for long if-chains, and
we tend to use starts_with() instead. However, the first line
of "do something" is often to then skip forward in buf past
the prefix, either using a magic constant or with an extra
strlen(3) (which is generally computed at compile time, but
means we are repeating ourselves).
This patch refactors skip_prefix() to return a simple boolean,
and to provide the pointer value as an out-parameter. If the
prefix is not found, the out-parameter is untouched. This
lets you write:
if (skip_prefix(arg, "foo ", &arg))
do_foo(arg);
else if (skip_prefix(arg, "bar ", &arg))
do_bar(arg);
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-18 19:44:19 +00:00
|
|
|
if (!skip_prefix(arg, long_name, &rest))
|
|
|
|
rest = NULL;
|
2008-03-02 10:35:56 +00:00
|
|
|
if (options->type == OPTION_ARGUMENT) {
|
|
|
|
if (!rest)
|
|
|
|
continue;
|
|
|
|
if (*rest == '=')
|
2018-11-10 05:16:11 +00:00
|
|
|
return error(_("%s takes no value"),
|
|
|
|
optname(options, flags));
|
2008-03-02 10:35:56 +00:00
|
|
|
if (*rest)
|
|
|
|
continue;
|
2019-03-14 11:25:04 +00:00
|
|
|
if (options->value)
|
|
|
|
*(int *)options->value = options->defval;
|
2008-03-02 10:35:56 +00:00
|
|
|
p->out[p->cpidx++] = arg - 2;
|
2019-01-27 00:35:27 +00:00
|
|
|
return PARSE_OPT_DONE;
|
2008-03-02 10:35:56 +00:00
|
|
|
}
|
2007-10-14 23:35:37 +00:00
|
|
|
if (!rest) {
|
2007-10-14 16:54:06 +00:00
|
|
|
/* abbreviated? */
|
2019-01-27 00:35:24 +00:00
|
|
|
if (!(p->flags & PARSE_OPT_KEEP_UNKNOWN) &&
|
|
|
|
!strncmp(long_name, arg, arg_end - arg)) {
|
2007-10-14 16:54:06 +00:00
|
|
|
is_abbreviated:
|
2019-04-29 10:05:25 +00:00
|
|
|
if (abbrev_option &&
|
|
|
|
!is_alias(p, abbrev_option, options)) {
|
2007-11-05 13:15:21 +00:00
|
|
|
/*
|
|
|
|
* If this is abbreviated, it is
|
|
|
|
* ambiguous. So when there is no
|
|
|
|
* exact match later, we need to
|
|
|
|
* error out.
|
|
|
|
*/
|
|
|
|
ambiguous_option = abbrev_option;
|
|
|
|
ambiguous_flags = abbrev_flags;
|
|
|
|
}
|
2007-10-14 16:54:06 +00:00
|
|
|
if (!(flags & OPT_UNSET) && *arg_end)
|
|
|
|
p->opt = arg_end + 1;
|
|
|
|
abbrev_option = options;
|
2012-02-25 19:14:54 +00:00
|
|
|
abbrev_flags = flags ^ opt_flags;
|
2007-10-14 16:54:06 +00:00
|
|
|
continue;
|
|
|
|
}
|
2009-09-25 18:44:44 +00:00
|
|
|
/* negation allowed? */
|
|
|
|
if (options->flags & PARSE_OPT_NONEG)
|
|
|
|
continue;
|
2007-10-14 16:54:06 +00:00
|
|
|
/* negated and abbreviated very much? */
|
2013-11-30 20:55:40 +00:00
|
|
|
if (starts_with("no-", arg)) {
|
2007-10-14 16:54:06 +00:00
|
|
|
flags |= OPT_UNSET;
|
|
|
|
goto is_abbreviated;
|
|
|
|
}
|
|
|
|
/* negated? */
|
2013-11-30 20:55:40 +00:00
|
|
|
if (!starts_with(arg, "no-")) {
|
2020-01-30 19:35:46 +00:00
|
|
|
if (skip_prefix(long_name, "no-", &long_name)) {
|
2012-02-25 19:14:54 +00:00
|
|
|
opt_flags |= OPT_UNSET;
|
|
|
|
goto again;
|
|
|
|
}
|
2007-10-14 23:35:37 +00:00
|
|
|
continue;
|
2012-02-25 19:14:54 +00:00
|
|
|
}
|
2007-10-14 23:35:37 +00:00
|
|
|
flags |= OPT_UNSET;
|
refactor skip_prefix to return a boolean
The skip_prefix() function returns a pointer to the content
past the prefix, or NULL if the prefix was not found. While
this is nice and simple, in practice it makes it hard to use
for two reasons:
1. When you want to conditionally skip or keep the string
as-is, you have to introduce a temporary variable.
For example:
tmp = skip_prefix(buf, "foo");
if (tmp)
buf = tmp;
2. It is verbose to check the outcome in a conditional, as
you need extra parentheses to silence compiler
warnings. For example:
if ((cp = skip_prefix(buf, "foo"))
/* do something with cp */
Both of these make it harder to use for long if-chains, and
we tend to use starts_with() instead. However, the first line
of "do something" is often to then skip forward in buf past
the prefix, either using a magic constant or with an extra
strlen(3) (which is generally computed at compile time, but
means we are repeating ourselves).
This patch refactors skip_prefix() to return a simple boolean,
and to provide the pointer value as an out-parameter. If the
prefix is not found, the out-parameter is untouched. This
lets you write:
if (skip_prefix(arg, "foo ", &arg))
do_foo(arg);
else if (skip_prefix(arg, "bar ", &arg))
do_bar(arg);
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-06-18 19:44:19 +00:00
|
|
|
if (!skip_prefix(arg + 3, long_name, &rest)) {
|
|
|
|
/* abbreviated and negated? */
|
|
|
|
if (starts_with(long_name, arg + 3))
|
|
|
|
goto is_abbreviated;
|
|
|
|
else
|
|
|
|
continue;
|
|
|
|
}
|
2007-10-14 23:35:37 +00:00
|
|
|
}
|
|
|
|
if (*rest) {
|
|
|
|
if (*rest != '=')
|
|
|
|
continue;
|
|
|
|
p->opt = rest + 1;
|
|
|
|
}
|
2013-07-30 19:06:01 +00:00
|
|
|
return get_value(p, options, all_opts, flags ^ opt_flags);
|
2007-10-14 23:35:37 +00:00
|
|
|
}
|
2007-11-05 13:15:21 +00:00
|
|
|
|
tests: disallow the use of abbreviated options (by default)
Git's command-line parsers support uniquely abbreviated options, e.g.
`git init --ba` would automatically expand `--ba` to `--bare`.
This is a very convenient feature in every day life for Git users, in
particular when tab completion is not available.
However, it is not a good idea to rely on that in Git's test suite, as
something that is a unique abbreviation of a command line option today
might no longer be a unique abbreviation tomorrow.
For example, if a future contribution added a new mode
`git init --babyproofing` and a previously-introduced test case used the
fact that `git init --ba` expanded to `git init --bare`, that future
contribution would now have to touch seemingly unrelated tests just to
keep the test suite from failing.
So let's disallow abbreviated options in the test suite by default.
Note: for ease of implementation, this patch really only touches the
`parse-options` machinery: more and more hand-rolled option parsers are
converted to use that internal API, and more and more scripts are
converted to built-ins (naturally using the parse-options API, too), so
in practice this catches most issues, and is definitely the biggest bang
for the buck.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-04-12 09:37:24 +00:00
|
|
|
if (disallow_abbreviated_options && (ambiguous_option || abbrev_option))
|
|
|
|
die("disallowed abbreviated or ambiguous option '%.*s'",
|
|
|
|
(int)(arg_end - arg), arg);
|
|
|
|
|
2018-03-22 18:43:51 +00:00
|
|
|
if (ambiguous_option) {
|
2018-11-10 05:16:13 +00:00
|
|
|
error(_("ambiguous option: %s "
|
|
|
|
"(could be --%s%s or --%s%s)"),
|
2007-11-05 13:15:21 +00:00
|
|
|
arg,
|
|
|
|
(ambiguous_flags & OPT_UNSET) ? "no-" : "",
|
|
|
|
ambiguous_option->long_name,
|
|
|
|
(abbrev_flags & OPT_UNSET) ? "no-" : "",
|
|
|
|
abbrev_option->long_name);
|
2019-01-27 00:35:27 +00:00
|
|
|
return PARSE_OPT_HELP;
|
2018-03-22 18:43:51 +00:00
|
|
|
}
|
2007-10-14 16:54:06 +00:00
|
|
|
if (abbrev_option)
|
2013-07-30 19:06:01 +00:00
|
|
|
return get_value(p, abbrev_option, all_opts, abbrev_flags);
|
2019-01-27 00:35:27 +00:00
|
|
|
return PARSE_OPT_UNKNOWN;
|
2007-10-14 23:35:37 +00:00
|
|
|
}
|
|
|
|
|
2009-05-07 19:45:42 +00:00
|
|
|
static int parse_nodash_opt(struct parse_opt_ctx_t *p, const char *arg,
|
|
|
|
const struct option *options)
|
|
|
|
{
|
2013-07-30 19:06:01 +00:00
|
|
|
const struct option *all_opts = options;
|
|
|
|
|
2009-05-07 19:45:42 +00:00
|
|
|
for (; options->type != OPTION_END; options++) {
|
|
|
|
if (!(options->flags & PARSE_OPT_NODASH))
|
|
|
|
continue;
|
|
|
|
if (options->short_name == arg[0] && arg[1] == '\0')
|
2013-07-30 19:06:01 +00:00
|
|
|
return get_value(p, options, all_opts, OPT_SHORT);
|
2009-05-07 19:45:42 +00:00
|
|
|
}
|
|
|
|
return -2;
|
|
|
|
}
|
|
|
|
|
2008-07-16 10:42:18 +00:00
|
|
|
static void check_typos(const char *arg, const struct option *options)
|
2008-01-26 11:26:57 +00:00
|
|
|
{
|
|
|
|
if (strlen(arg) < 3)
|
|
|
|
return;
|
|
|
|
|
2013-11-30 20:55:40 +00:00
|
|
|
if (starts_with(arg, "no-")) {
|
2020-02-05 13:07:23 +00:00
|
|
|
error(_("did you mean `--%s` (with two dashes)?"), arg);
|
2008-01-26 11:26:57 +00:00
|
|
|
exit(129);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (; options->type != OPTION_END; options++) {
|
|
|
|
if (!options->long_name)
|
|
|
|
continue;
|
2013-11-30 20:55:40 +00:00
|
|
|
if (starts_with(options->long_name, arg)) {
|
2020-02-05 13:07:23 +00:00
|
|
|
error(_("did you mean `--%s` (with two dashes)?"), arg);
|
2008-01-26 11:26:57 +00:00
|
|
|
exit(129);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-06-09 08:23:44 +00:00
|
|
|
static void parse_options_check(const struct option *opts)
|
|
|
|
{
|
|
|
|
int err = 0;
|
2014-09-03 19:42:37 +00:00
|
|
|
char short_opts[128];
|
2009-06-09 08:23:44 +00:00
|
|
|
|
2014-09-03 19:42:37 +00:00
|
|
|
memset(short_opts, '\0', sizeof(short_opts));
|
2009-06-09 08:23:44 +00:00
|
|
|
for (; opts->type != OPTION_END; opts++) {
|
|
|
|
if ((opts->flags & PARSE_OPT_LASTARG_DEFAULT) &&
|
2010-12-02 06:01:18 +00:00
|
|
|
(opts->flags & PARSE_OPT_OPTARG))
|
|
|
|
err |= optbug(opts, "uses incompatible flags "
|
|
|
|
"LASTARG_DEFAULT and OPTARG");
|
2014-09-03 19:42:37 +00:00
|
|
|
if (opts->short_name) {
|
|
|
|
if (0x7F <= opts->short_name)
|
|
|
|
err |= optbug(opts, "invalid short name");
|
|
|
|
else if (short_opts[opts->short_name]++)
|
|
|
|
err |= optbug(opts, "short name already used");
|
|
|
|
}
|
2010-12-02 06:05:05 +00:00
|
|
|
if (opts->flags & PARSE_OPT_NODASH &&
|
|
|
|
((opts->flags & PARSE_OPT_OPTARG) ||
|
|
|
|
!(opts->flags & PARSE_OPT_NOARG) ||
|
|
|
|
!(opts->flags & PARSE_OPT_NONEG) ||
|
|
|
|
opts->long_name))
|
|
|
|
err |= optbug(opts, "uses feature "
|
|
|
|
"not supported for dashless options");
|
2010-12-02 06:08:57 +00:00
|
|
|
switch (opts->type) {
|
parse-options: deprecate OPT_BOOLEAN
It is natural to expect that an option defined with OPT_BOOLEAN() could be
used in this way:
int option = -1; /* unspecified */
struct option options[] = {
OPT_BOOLEAN(0, "option", &option, "set option"),
OPT_END()
};
parse_options(ac, av, prefix, options, usage, 0);
if (option < 0)
... do the default thing ...
else if (!option)
... --no-option was given ...
else
... --option was given ...
to easily tell three cases apart:
- There is no mention of the `--option` on the command line;
- The variable is positively set with `--option`; or
- The variable is explicitly negated with `--no-option`.
Unfortunately, this is not the case. OPT_BOOLEAN() increments the variable
every time `--option` is given, and resets it to zero when `--no-option`
is given.
As a first step to remedy this, introduce a true boolean OPT_BOOL(), and
rename OPT_BOOLEAN() to OPT_COUNTUP(). To help transitioning, OPT_BOOLEAN
and OPTION_BOOLEAN are defined as deprecated synonyms to OPT_COUNTUP and
OPTION_COUNTUP respectively.
This is what db7244b (parse-options new features., 2007-11-07) from four
years ago started by marking OPTION_BOOLEAN as "INCR would have been a
better name".
Some existing users do depend on the count-up semantics; for example,
users of OPT__VERBOSE() could use it to raise the verbosity level with
repeated use of `-v` on the command line, but they probably should be
rewritten to use OPT__VERBOSITY() instead these days. I suspect that some
users of OPT__FORCE() may also use it to implement different level of
forcibleness but I didn't check.
On top of this patch, here are the remaining clean-up tasks that other
people can help:
- Look at each hit in "git grep -e OPT_BOOLEAN"; trace all uses of the
value that is set to the underlying variable, and if it can proven that
the variable is only used as a boolean, replace it with OPT_BOOL(). If
the caller does depend on the count-up semantics, replace it with
OPT_COUNTUP() instead.
- Same for OPTION_BOOLEAN; replace it with OPTION_SET_INT and arrange to
set 1 to the variable for a true boolean, and otherwise replace it with
OPTION_COUNTUP.
- Look at each hit in "git grep -e OPT__VERBOSE -e OPT__QUIET" and see if
they can be replaced with OPT__VERBOSITY().
I'll follow this message up with a separate patch as an example.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-09-27 23:56:49 +00:00
|
|
|
case OPTION_COUNTUP:
|
2010-12-02 06:08:57 +00:00
|
|
|
case OPTION_BIT:
|
|
|
|
case OPTION_NEGBIT:
|
|
|
|
case OPTION_SET_INT:
|
|
|
|
case OPTION_NUMBER:
|
|
|
|
if ((opts->flags & PARSE_OPT_OPTARG) ||
|
|
|
|
!(opts->flags & PARSE_OPT_NOARG))
|
|
|
|
err |= optbug(opts, "should not accept an argument");
|
2019-01-27 00:35:26 +00:00
|
|
|
break;
|
|
|
|
case OPTION_CALLBACK:
|
2019-01-27 00:35:28 +00:00
|
|
|
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");
|
2019-01-27 00:35:26 +00:00
|
|
|
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;
|
2019-04-29 10:05:25 +00:00
|
|
|
case OPTION_ALIAS:
|
|
|
|
BUG("OPT_ALIAS() should not remain at this point. "
|
|
|
|
"Are you using parse_options_step() directly?\n"
|
|
|
|
"That case is not supported yet.");
|
2010-12-02 06:08:57 +00:00
|
|
|
default:
|
|
|
|
; /* ok. (usually accepts an argument) */
|
|
|
|
}
|
2014-03-23 23:04:36 +00:00
|
|
|
if (opts->argh &&
|
|
|
|
strcspn(opts->argh, " _") != strlen(opts->argh))
|
|
|
|
err |= optbug(opts, "multi-word argh should use dash to separate words");
|
2009-06-09 08:23:44 +00:00
|
|
|
}
|
|
|
|
if (err)
|
2010-12-02 06:01:18 +00:00
|
|
|
exit(128);
|
2009-06-09 08:23:44 +00:00
|
|
|
}
|
|
|
|
|
2019-04-29 10:05:25 +00:00
|
|
|
static void parse_options_start_1(struct parse_opt_ctx_t *ctx,
|
|
|
|
int argc, const char **argv, const char *prefix,
|
|
|
|
const struct option *options, int flags)
|
2008-06-23 19:59:37 +00:00
|
|
|
{
|
2019-01-27 00:35:23 +00:00
|
|
|
ctx->argc = argc;
|
|
|
|
ctx->argv = argv;
|
|
|
|
if (!(flags & PARSE_OPT_ONE_SHOT)) {
|
|
|
|
ctx->argc--;
|
|
|
|
ctx->argv++;
|
|
|
|
}
|
|
|
|
ctx->total = ctx->argc;
|
|
|
|
ctx->out = argv;
|
2009-05-23 18:53:12 +00:00
|
|
|
ctx->prefix = prefix;
|
2008-06-23 22:31:31 +00:00
|
|
|
ctx->cpidx = ((flags & PARSE_OPT_KEEP_ARGV0) != 0);
|
2008-06-23 19:59:37 +00:00
|
|
|
ctx->flags = flags;
|
2009-03-09 20:57:38 +00:00
|
|
|
if ((flags & PARSE_OPT_KEEP_UNKNOWN) &&
|
2019-01-27 00:35:23 +00:00
|
|
|
(flags & PARSE_OPT_STOP_AT_NON_OPTION) &&
|
|
|
|
!(flags & PARSE_OPT_ONE_SHOT))
|
2018-11-10 05:16:12 +00:00
|
|
|
BUG("STOP_AT_NON_OPTION and KEEP_UNKNOWN don't go together");
|
2019-01-27 00:35:23 +00:00
|
|
|
if ((flags & PARSE_OPT_ONE_SHOT) &&
|
|
|
|
(flags & PARSE_OPT_KEEP_ARGV0))
|
|
|
|
BUG("Can't keep argv0 if you don't have it");
|
2010-12-06 07:57:42 +00:00
|
|
|
parse_options_check(options);
|
2008-06-23 19:59:37 +00:00
|
|
|
}
|
|
|
|
|
2019-04-29 10:05:25 +00:00
|
|
|
void parse_options_start(struct parse_opt_ctx_t *ctx,
|
|
|
|
int argc, const char **argv, const char *prefix,
|
|
|
|
const struct option *options, int flags)
|
|
|
|
{
|
|
|
|
memset(ctx, 0, sizeof(*ctx));
|
|
|
|
parse_options_start_1(ctx, argc, argv, prefix, options, flags);
|
|
|
|
}
|
|
|
|
|
2020-08-19 23:06:08 +00:00
|
|
|
static void show_negated_gitcomp(const struct option *opts, int show_all,
|
|
|
|
int nr_noopts)
|
2018-06-06 09:41:39 +00:00
|
|
|
{
|
|
|
|
int printed_dashdash = 0;
|
|
|
|
|
|
|
|
for (; opts->type != OPTION_END; opts++) {
|
|
|
|
int has_unset_form = 0;
|
|
|
|
const char *name;
|
|
|
|
|
|
|
|
if (!opts->long_name)
|
|
|
|
continue;
|
2020-08-19 23:06:08 +00:00
|
|
|
if (!show_all &&
|
|
|
|
(opts->flags & (PARSE_OPT_HIDDEN | PARSE_OPT_NOCOMPLETE)))
|
2018-06-06 09:41:39 +00:00
|
|
|
continue;
|
|
|
|
if (opts->flags & PARSE_OPT_NONEG)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
switch (opts->type) {
|
|
|
|
case OPTION_STRING:
|
|
|
|
case OPTION_FILENAME:
|
|
|
|
case OPTION_INTEGER:
|
|
|
|
case OPTION_MAGNITUDE:
|
|
|
|
case OPTION_CALLBACK:
|
|
|
|
case OPTION_BIT:
|
|
|
|
case OPTION_NEGBIT:
|
|
|
|
case OPTION_COUNTUP:
|
|
|
|
case OPTION_SET_INT:
|
|
|
|
has_unset_form = 1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!has_unset_form)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (skip_prefix(opts->long_name, "no-", &name)) {
|
|
|
|
if (nr_noopts < 0)
|
|
|
|
printf(" --%s", name);
|
|
|
|
} else if (nr_noopts >= 0) {
|
|
|
|
if (nr_noopts && !printed_dashdash) {
|
|
|
|
printf(" --");
|
|
|
|
printed_dashdash = 1;
|
|
|
|
}
|
|
|
|
printf(" --no-%s", opts->long_name);
|
|
|
|
nr_noopts++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-19 23:06:08 +00:00
|
|
|
static int show_gitcomp(const struct option *opts, int show_all)
|
2018-02-09 11:01:40 +00:00
|
|
|
{
|
2018-06-06 09:41:39 +00:00
|
|
|
const struct option *original_opts = opts;
|
|
|
|
int nr_noopts = 0;
|
|
|
|
|
2018-02-09 11:01:40 +00:00
|
|
|
for (; opts->type != OPTION_END; opts++) {
|
|
|
|
const char *suffix = "";
|
|
|
|
|
|
|
|
if (!opts->long_name)
|
|
|
|
continue;
|
2020-08-19 23:06:08 +00:00
|
|
|
if (!show_all &&
|
parse-options: don't complete option aliases by default
Since 'OPT_ALIAS' was created in 5c387428f1 (parse-options: don't emit
"ambiguous option" for aliases, 2019-04-29), 'git clone
--git-completion-helper', which is used by the Bash completion script to
list options accepted by clone (via '__gitcomp_builtin'), lists both
'--recurse-submodules' and its alias '--recursive', which was not the
case before since '--recursive' had the PARSE_OPT_HIDDEN flag set, and
options with this flag are skipped by 'parse-options.c::show_gitcomp',
which implements 'git <cmd> --git-completion-helper'.
This means that typing 'git clone --recurs<TAB>' will yield both
'--recurse-submodules' and '--recursive', which is not ideal since both
do the same thing, and so the completion should directly complete the
canonical option.
At the point where 'show_gitcomp' is called in 'parse_options_step',
'preprocess_options' was already called in 'parse_options', so any
aliases are now copies of the original options with a modified help text
indicating they are aliases.
Helpfully, since 64cc539fd2 (parse-options: don't leak alias help
messages, 2021-03-21) these copies have the PARSE_OPT_FROM_ALIAS flag
set, so check that flag early in 'show_gitcomp' and do not print them,
unless the user explicitely requested that *all* completion be shown (by
setting 'GIT_COMPLETION_SHOW_ALL'). After all, if we want to encourage
the use of '--recurse-submodules' over '--recursive', we'd better just
suggest the former.
The only other options alias is 'log' and friends' '--mailmap', which is
an alias for '--use-mailmap', but the Bash completion helpers for these
commands do not use '__gitcomp_builtin', and thus are unnaffected by
this change.
Test the new behaviour in t9902-completion.sh. As a side effect, this
also tests the correct behaviour of GIT_COMPLETION_SHOW_ALL, which was
not tested before. Note that since '__gitcomp_builtin' caches the
options it shows, we need to re-source the completion script to clear
that cache for the second test.
Signed-off-by: Philippe Blain <levraiphilippeblain@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-07-16 01:55:43 +00:00
|
|
|
(opts->flags & (PARSE_OPT_HIDDEN | PARSE_OPT_NOCOMPLETE | PARSE_OPT_FROM_ALIAS)))
|
2018-02-09 11:01:40 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
switch (opts->type) {
|
|
|
|
case OPTION_GROUP:
|
|
|
|
continue;
|
|
|
|
case OPTION_STRING:
|
|
|
|
case OPTION_FILENAME:
|
|
|
|
case OPTION_INTEGER:
|
|
|
|
case OPTION_MAGNITUDE:
|
|
|
|
case OPTION_CALLBACK:
|
|
|
|
if (opts->flags & PARSE_OPT_NOARG)
|
|
|
|
break;
|
|
|
|
if (opts->flags & PARSE_OPT_OPTARG)
|
|
|
|
break;
|
|
|
|
if (opts->flags & PARSE_OPT_LASTARG_DEFAULT)
|
|
|
|
break;
|
|
|
|
suffix = "=";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2018-02-09 11:02:12 +00:00
|
|
|
if (opts->flags & PARSE_OPT_COMP_ARG)
|
|
|
|
suffix = "=";
|
2018-06-06 09:41:39 +00:00
|
|
|
if (starts_with(opts->long_name, "no-"))
|
|
|
|
nr_noopts++;
|
2018-02-09 11:01:40 +00:00
|
|
|
printf(" --%s%s", opts->long_name, suffix);
|
|
|
|
}
|
2020-08-19 23:06:08 +00:00
|
|
|
show_negated_gitcomp(original_opts, show_all, -1);
|
|
|
|
show_negated_gitcomp(original_opts, show_all, nr_noopts);
|
2018-02-09 11:01:40 +00:00
|
|
|
fputc('\n', stdout);
|
2018-12-11 15:35:01 +00:00
|
|
|
return PARSE_OPT_COMPLETE;
|
2018-02-09 11:01:40 +00:00
|
|
|
}
|
|
|
|
|
2019-04-29 10:05:25 +00:00
|
|
|
/*
|
|
|
|
* Scan and may produce a new option[] array, which should be used
|
|
|
|
* instead of the original 'options'.
|
|
|
|
*
|
2019-11-05 17:07:23 +00:00
|
|
|
* Right now this is only used to preprocess and substitute
|
2019-04-29 10:05:25 +00:00
|
|
|
* OPTION_ALIAS.
|
2021-03-21 16:58:36 +00:00
|
|
|
*
|
|
|
|
* The returned options should be freed using free_preprocessed_options.
|
2019-04-29 10:05:25 +00:00
|
|
|
*/
|
|
|
|
static struct option *preprocess_options(struct parse_opt_ctx_t *ctx,
|
|
|
|
const struct option *options)
|
|
|
|
{
|
|
|
|
struct option *newopt;
|
|
|
|
int i, nr, alias;
|
|
|
|
int nr_aliases = 0;
|
|
|
|
|
|
|
|
for (nr = 0; options[nr].type != OPTION_END; nr++) {
|
|
|
|
if (options[nr].type == OPTION_ALIAS)
|
|
|
|
nr_aliases++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!nr_aliases)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
ALLOC_ARRAY(newopt, nr + 1);
|
|
|
|
COPY_ARRAY(newopt, options, nr + 1);
|
|
|
|
|
|
|
|
/* each alias has two string pointers and NULL */
|
|
|
|
CALLOC_ARRAY(ctx->alias_groups, 3 * (nr_aliases + 1));
|
|
|
|
|
|
|
|
for (alias = 0, i = 0; i < nr; i++) {
|
|
|
|
int short_name;
|
|
|
|
const char *long_name;
|
|
|
|
const char *source;
|
2020-03-16 20:22:54 +00:00
|
|
|
struct strbuf help = STRBUF_INIT;
|
2019-04-29 10:05:25 +00:00
|
|
|
int j;
|
|
|
|
|
|
|
|
if (newopt[i].type != OPTION_ALIAS)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
short_name = newopt[i].short_name;
|
|
|
|
long_name = newopt[i].long_name;
|
|
|
|
source = newopt[i].value;
|
|
|
|
|
|
|
|
if (!long_name)
|
|
|
|
BUG("An alias must have long option name");
|
2020-03-16 20:22:54 +00:00
|
|
|
strbuf_addf(&help, _("alias of --%s"), source);
|
2019-04-29 10:05:25 +00:00
|
|
|
|
|
|
|
for (j = 0; j < nr; j++) {
|
|
|
|
const char *name = options[j].long_name;
|
|
|
|
|
|
|
|
if (!name || strcmp(name, source))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (options[j].type == OPTION_ALIAS)
|
|
|
|
BUG("No please. Nested aliases are not supported.");
|
|
|
|
|
|
|
|
memcpy(newopt + i, options + j, sizeof(*newopt));
|
|
|
|
newopt[i].short_name = short_name;
|
|
|
|
newopt[i].long_name = long_name;
|
2020-03-16 20:22:54 +00:00
|
|
|
newopt[i].help = strbuf_detach(&help, NULL);
|
2021-03-21 16:58:36 +00:00
|
|
|
newopt[i].flags |= PARSE_OPT_FROM_ALIAS;
|
2019-04-29 10:05:25 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (j == nr)
|
|
|
|
BUG("could not find source option '%s' of alias '%s'",
|
|
|
|
source, newopt[i].long_name);
|
|
|
|
ctx->alias_groups[alias * 3 + 0] = newopt[i].long_name;
|
|
|
|
ctx->alias_groups[alias * 3 + 1] = options[j].long_name;
|
|
|
|
ctx->alias_groups[alias * 3 + 2] = NULL;
|
|
|
|
alias++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return newopt;
|
|
|
|
}
|
|
|
|
|
2021-03-21 16:58:36 +00:00
|
|
|
static void free_preprocessed_options(struct option *options)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!options)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; options[i].type != OPTION_END; i++) {
|
|
|
|
if (options[i].flags & PARSE_OPT_FROM_ALIAS)
|
|
|
|
free((void *)options[i].help);
|
|
|
|
}
|
|
|
|
free(options);
|
|
|
|
}
|
|
|
|
|
2010-06-12 12:57:39 +00:00
|
|
|
static int usage_with_options_internal(struct parse_opt_ctx_t *,
|
|
|
|
const char * const *,
|
2010-05-17 15:34:41 +00:00
|
|
|
const struct option *, int, int);
|
2007-11-19 09:21:44 +00:00
|
|
|
|
2008-06-23 20:38:58 +00:00
|
|
|
int parse_options_step(struct parse_opt_ctx_t *ctx,
|
|
|
|
const struct option *options,
|
|
|
|
const char * const usagestr[])
|
2007-10-14 23:35:37 +00:00
|
|
|
{
|
2009-03-08 18:15:08 +00:00
|
|
|
int internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP);
|
|
|
|
|
2008-06-23 20:55:11 +00:00
|
|
|
/* we must reset ->opt, unknown short option leave it dangling */
|
|
|
|
ctx->opt = NULL;
|
|
|
|
|
2008-06-23 20:38:58 +00:00
|
|
|
for (; ctx->argc; ctx->argc--, ctx->argv++) {
|
|
|
|
const char *arg = ctx->argv[0];
|
2007-10-14 23:35:37 +00:00
|
|
|
|
2019-01-27 00:35:23 +00:00
|
|
|
if (ctx->flags & PARSE_OPT_ONE_SHOT &&
|
|
|
|
ctx->argc != ctx->total)
|
|
|
|
break;
|
|
|
|
|
2007-10-14 23:35:37 +00:00
|
|
|
if (*arg != '-' || !arg[1]) {
|
2009-05-07 19:45:42 +00:00
|
|
|
if (parse_nodash_opt(ctx, arg, options) == 0)
|
|
|
|
continue;
|
2008-06-23 20:38:58 +00:00
|
|
|
if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION)
|
2010-12-01 23:32:55 +00:00
|
|
|
return PARSE_OPT_NON_OPTION;
|
2008-06-23 20:38:58 +00:00
|
|
|
ctx->out[ctx->cpidx++] = ctx->argv[0];
|
2007-10-14 23:35:37 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-11-17 10:25:38 +00:00
|
|
|
/* lone -h asks for help */
|
|
|
|
if (internal_help && ctx->total == 1 && !strcmp(arg + 1, "h"))
|
|
|
|
goto show_usage;
|
|
|
|
|
2020-08-19 23:06:08 +00:00
|
|
|
/*
|
|
|
|
* lone --git-completion-helper and --git-completion-helper-all
|
|
|
|
* are asked by git-completion.bash
|
|
|
|
*/
|
|
|
|
if (ctx->total == 1 && !strcmp(arg, "--git-completion-helper"))
|
|
|
|
return show_gitcomp(options, 0);
|
|
|
|
if (ctx->total == 1 && !strcmp(arg, "--git-completion-helper-all"))
|
|
|
|
return show_gitcomp(options, 1);
|
2018-02-09 11:01:40 +00:00
|
|
|
|
2007-10-14 23:35:37 +00:00
|
|
|
if (arg[1] != '-') {
|
2008-06-23 20:38:58 +00:00
|
|
|
ctx->opt = arg + 1;
|
2008-06-23 20:46:36 +00:00
|
|
|
switch (parse_short_opt(ctx, options)) {
|
2019-01-27 00:35:27 +00:00
|
|
|
case PARSE_OPT_ERROR:
|
2018-03-22 18:43:51 +00:00
|
|
|
return PARSE_OPT_ERROR;
|
2019-01-27 00:35:27 +00:00
|
|
|
case PARSE_OPT_UNKNOWN:
|
2012-03-03 11:00:29 +00:00
|
|
|
if (ctx->opt)
|
|
|
|
check_typos(arg + 1, options);
|
2015-11-17 10:25:38 +00:00
|
|
|
if (internal_help && *ctx->opt == 'h')
|
|
|
|
goto show_usage;
|
2009-03-08 18:12:47 +00:00
|
|
|
goto unknown;
|
2019-01-27 00:35:27 +00:00
|
|
|
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;
|
2008-06-23 20:46:36 +00:00
|
|
|
}
|
2008-06-23 20:38:58 +00:00
|
|
|
if (ctx->opt)
|
2008-01-26 11:26:57 +00:00
|
|
|
check_typos(arg + 1, options);
|
2008-06-23 20:38:58 +00:00
|
|
|
while (ctx->opt) {
|
2008-06-23 20:46:36 +00:00
|
|
|
switch (parse_short_opt(ctx, options)) {
|
2019-01-27 00:35:27 +00:00
|
|
|
case PARSE_OPT_ERROR:
|
2018-03-22 18:43:51 +00:00
|
|
|
return PARSE_OPT_ERROR;
|
2019-01-27 00:35:27 +00:00
|
|
|
case PARSE_OPT_UNKNOWN:
|
2015-11-17 10:25:38 +00:00
|
|
|
if (internal_help && *ctx->opt == 'h')
|
|
|
|
goto show_usage;
|
|
|
|
|
2008-06-23 20:55:11 +00:00
|
|
|
/* fake a short option thing to hide the fact that we may have
|
|
|
|
* started to parse aggregated stuff
|
|
|
|
*
|
|
|
|
* This is leaky, too bad.
|
|
|
|
*/
|
|
|
|
ctx->argv[0] = xstrdup(ctx->opt - 1);
|
|
|
|
*(char *)ctx->argv[0] = '-';
|
2009-03-08 18:12:47 +00:00
|
|
|
goto unknown;
|
2019-01-27 00:35:27 +00:00
|
|
|
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;
|
2008-06-23 20:46:36 +00:00
|
|
|
}
|
2008-01-26 11:26:57 +00:00
|
|
|
}
|
2007-10-14 23:35:37 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-08-06 14:40:16 +00:00
|
|
|
if (!arg[2] /* "--" */ ||
|
|
|
|
!strcmp(arg + 2, "end-of-options")) {
|
2008-06-23 20:38:58 +00:00
|
|
|
if (!(ctx->flags & PARSE_OPT_KEEP_DASHDASH)) {
|
|
|
|
ctx->argc--;
|
|
|
|
ctx->argv++;
|
2007-10-14 23:35:37 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2009-03-08 18:15:08 +00:00
|
|
|
if (internal_help && !strcmp(arg + 2, "help-all"))
|
2010-06-12 12:57:39 +00:00
|
|
|
return usage_with_options_internal(ctx, usagestr, options, 1, 0);
|
2009-03-08 18:15:08 +00:00
|
|
|
if (internal_help && !strcmp(arg + 2, "help"))
|
2015-11-17 10:25:14 +00:00
|
|
|
goto show_usage;
|
2008-06-23 20:46:36 +00:00
|
|
|
switch (parse_long_opt(ctx, arg + 2, options)) {
|
2019-01-27 00:35:27 +00:00
|
|
|
case PARSE_OPT_ERROR:
|
2018-03-22 18:43:51 +00:00
|
|
|
return PARSE_OPT_ERROR;
|
2019-01-27 00:35:27 +00:00
|
|
|
case PARSE_OPT_UNKNOWN:
|
2009-03-08 18:12:47 +00:00
|
|
|
goto unknown;
|
2019-01-27 00:35:27 +00:00
|
|
|
case PARSE_OPT_HELP:
|
2018-03-22 18:43:51 +00:00
|
|
|
goto show_usage;
|
2019-01-27 00:35:27 +00:00
|
|
|
case PARSE_OPT_NON_OPTION:
|
|
|
|
case PARSE_OPT_COMPLETE:
|
|
|
|
BUG("parse_long_opt() cannot return these");
|
|
|
|
case PARSE_OPT_DONE:
|
|
|
|
break;
|
2008-06-23 20:46:36 +00:00
|
|
|
}
|
2009-03-08 18:12:47 +00:00
|
|
|
continue;
|
|
|
|
unknown:
|
2019-01-27 00:35:23 +00:00
|
|
|
if (ctx->flags & PARSE_OPT_ONE_SHOT)
|
|
|
|
break;
|
2009-03-08 18:12:47 +00:00
|
|
|
if (!(ctx->flags & PARSE_OPT_KEEP_UNKNOWN))
|
|
|
|
return PARSE_OPT_UNKNOWN;
|
|
|
|
ctx->out[ctx->cpidx++] = ctx->argv[0];
|
|
|
|
ctx->opt = NULL;
|
2008-06-23 20:38:58 +00:00
|
|
|
}
|
|
|
|
return PARSE_OPT_DONE;
|
2015-11-17 10:25:14 +00:00
|
|
|
|
|
|
|
show_usage:
|
2018-03-22 18:43:51 +00:00
|
|
|
return usage_with_options_internal(ctx, usagestr, options, 0, 0);
|
2008-06-23 20:38:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int parse_options_end(struct parse_opt_ctx_t *ctx)
|
|
|
|
{
|
2019-01-27 00:35:23 +00:00
|
|
|
if (ctx->flags & PARSE_OPT_ONE_SHOT)
|
|
|
|
return ctx->total - ctx->argc;
|
|
|
|
|
2018-01-22 17:50:09 +00:00
|
|
|
MOVE_ARRAY(ctx->out + ctx->cpidx, ctx->argv, ctx->argc);
|
2008-06-23 20:38:58 +00:00
|
|
|
ctx->out[ctx->cpidx + ctx->argc] = NULL;
|
|
|
|
return ctx->cpidx + ctx->argc;
|
|
|
|
}
|
|
|
|
|
2009-05-23 18:53:12 +00:00
|
|
|
int parse_options(int argc, const char **argv, const char *prefix,
|
|
|
|
const struct option *options, const char * const usagestr[],
|
|
|
|
int flags)
|
2008-06-23 20:38:58 +00:00
|
|
|
{
|
|
|
|
struct parse_opt_ctx_t ctx;
|
2019-04-29 10:05:25 +00:00
|
|
|
struct option *real_options;
|
2008-06-23 20:38:58 +00:00
|
|
|
|
tests: disallow the use of abbreviated options (by default)
Git's command-line parsers support uniquely abbreviated options, e.g.
`git init --ba` would automatically expand `--ba` to `--bare`.
This is a very convenient feature in every day life for Git users, in
particular when tab completion is not available.
However, it is not a good idea to rely on that in Git's test suite, as
something that is a unique abbreviation of a command line option today
might no longer be a unique abbreviation tomorrow.
For example, if a future contribution added a new mode
`git init --babyproofing` and a previously-introduced test case used the
fact that `git init --ba` expanded to `git init --bare`, that future
contribution would now have to touch seemingly unrelated tests just to
keep the test suite from failing.
So let's disallow abbreviated options in the test suite by default.
Note: for ease of implementation, this patch really only touches the
`parse-options` machinery: more and more hand-rolled option parsers are
converted to use that internal API, and more and more scripts are
converted to built-ins (naturally using the parse-options API, too), so
in practice this catches most issues, and is definitely the biggest bang
for the buck.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-04-12 09:37:24 +00:00
|
|
|
disallow_abbreviated_options =
|
|
|
|
git_env_bool("GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS", 0);
|
|
|
|
|
2019-04-29 10:05:25 +00:00
|
|
|
memset(&ctx, 0, sizeof(ctx));
|
|
|
|
real_options = preprocess_options(&ctx, options);
|
|
|
|
if (real_options)
|
|
|
|
options = real_options;
|
|
|
|
parse_options_start_1(&ctx, argc, argv, prefix, options, flags);
|
2008-06-23 20:38:58 +00:00
|
|
|
switch (parse_options_step(&ctx, options, usagestr)) {
|
|
|
|
case PARSE_OPT_HELP:
|
2018-03-22 18:43:51 +00:00
|
|
|
case PARSE_OPT_ERROR:
|
2008-06-23 20:38:58 +00:00
|
|
|
exit(129);
|
2018-12-11 15:35:01 +00:00
|
|
|
case PARSE_OPT_COMPLETE:
|
|
|
|
exit(0);
|
2010-12-01 23:32:55 +00:00
|
|
|
case PARSE_OPT_NON_OPTION:
|
2008-06-23 20:38:58 +00:00
|
|
|
case PARSE_OPT_DONE:
|
|
|
|
break;
|
|
|
|
default: /* PARSE_OPT_UNKNOWN */
|
2008-06-23 20:46:36 +00:00
|
|
|
if (ctx.argv[0][1] == '-') {
|
2018-11-10 05:16:13 +00:00
|
|
|
error(_("unknown option `%s'"), ctx.argv[0] + 2);
|
2013-02-11 23:13:48 +00:00
|
|
|
} else if (isascii(*ctx.opt)) {
|
2018-11-10 05:16:13 +00:00
|
|
|
error(_("unknown switch `%c'"), *ctx.opt);
|
2013-02-11 23:13:48 +00:00
|
|
|
} else {
|
2018-11-10 05:16:13 +00:00
|
|
|
error(_("unknown non-ascii option in string: `%s'"),
|
2013-02-11 23:13:48 +00:00
|
|
|
ctx.argv[0]);
|
2008-06-23 20:46:36 +00:00
|
|
|
}
|
|
|
|
usage_with_options(usagestr, options);
|
2007-10-14 23:35:37 +00:00
|
|
|
}
|
|
|
|
|
MacOS: precompose_argv_prefix()
The following sequence leads to a "BUG" assertion running under MacOS:
DIR=git-test-restore-p
Adiarnfd=$(printf 'A\314\210')
DIRNAME=xx${Adiarnfd}yy
mkdir $DIR &&
cd $DIR &&
git init &&
mkdir $DIRNAME &&
cd $DIRNAME &&
echo "Initial" >file &&
git add file &&
echo "One more line" >>file &&
echo y | git restore -p .
Initialized empty Git repository in /tmp/git-test-restore-p/.git/
BUG: pathspec.c:495: error initializing pathspec_item
Cannot close git diff-index --cached --numstat
[snip]
The command `git restore` is run from a directory inside a Git repo.
Git needs to split the $CWD into 2 parts:
The path to the repo and "the rest", if any.
"The rest" becomes a "prefix" later used inside the pathspec code.
As an example, "/path/to/repo/dir-inside-repå" would determine
"/path/to/repo" as the root of the repo, the place where the
configuration file .git/config is found.
The rest becomes the prefix ("dir-inside-repå"), from where the
pathspec machinery expands the ".", more about this later.
If there is a decomposed form, (making the decomposing visible like this),
"dir-inside-rep°a" doesn't match "dir-inside-repå".
Git commands need to:
(a) read the configuration variable "core.precomposeunicode"
(b) precocompose argv[]
(c) precompose the prefix, if there was any
The first commit,
76759c7dff53 "git on Mac OS and precomposed unicode"
addressed (a) and (b).
The call to precompose_argv() was added into parse-options.c,
because that seemed to be a good place when the patch was written.
Commands that don't use parse-options need to do (a) and (b) themselfs.
The commands `diff-files`, `diff-index`, `diff-tree` and `diff`
learned (a) and (b) in
commit 90a78b83e0b8 "diff: run arguments through precompose_argv"
Branch names (or refs in general) using decomposed code points
resulting in decomposed file names had been fixed in
commit 8e712ef6fc97 "Honor core.precomposeUnicode in more places"
The bug report from above shows 2 things:
- more commands need to handle precomposed unicode
- (c) should be implemented for all commands using pathspecs
Solution:
precompose_argv() now handles the prefix (if needed), and is renamed into
precompose_argv_prefix().
Inside this function the config variable core.precomposeunicode is read
into the global variable precomposed_unicode, as before.
This reading is skipped if precomposed_unicode had been read before.
The original patch for preocomposed unicode, 76759c7dff53, placed
precompose_argv() into parse-options.c
Now add it into git.c::run_builtin() as well. Existing precompose
calls in diff-files.c and others may become redundant, and if we
audit the callflows that reach these places to make sure that they
can never be reached without going through the new call added to
run_builtin(), we might be able to remove these existing ones.
But in this commit, we do not bother to do so and leave these
precompose callsites as they are. Because precompose() is
idempotent and can be called on an already precomposed string
safely, this is safer than removing existing calls without fully
vetting the callflows.
There is certainly room for cleanups - this change intends to be a bug fix.
Cleanups needs more tests in e.g. t/t3910-mac-os-precompose.sh, and should
be done in future commits.
[1] git-bugreport-2021-01-06-1209.txt (git can't deal with special characters)
[2] https://lore.kernel.org/git/A102844A-9501-4A86-854D-E3B387D378AA@icloud.com/
Reported-by: Daniel Troger <random_n0body@icloud.com>
Helped-By: Philippe Blain <levraiphilippeblain@gmail.com>
Signed-off-by: Torsten Bögershausen <tboegi@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-02-03 16:28:23 +00:00
|
|
|
precompose_argv_prefix(argc, argv, NULL);
|
2021-03-21 16:58:36 +00:00
|
|
|
free_preprocessed_options(real_options);
|
2019-04-29 10:05:25 +00:00
|
|
|
free(ctx.alias_groups);
|
2008-06-23 19:59:37 +00:00
|
|
|
return parse_options_end(&ctx);
|
2007-10-14 23:35:37 +00:00
|
|
|
}
|
2007-10-14 23:38:30 +00:00
|
|
|
|
2010-05-17 15:34:41 +00:00
|
|
|
static int usage_argh(const struct option *opts, FILE *outfile)
|
2009-05-21 07:33:17 +00:00
|
|
|
{
|
|
|
|
const char *s;
|
2018-08-02 19:18:14 +00:00
|
|
|
int literal = (opts->flags & PARSE_OPT_LITERAL_ARGHELP) ||
|
|
|
|
!opts->argh || !!strpbrk(opts->argh, "()<>[]|");
|
2009-05-21 07:33:17 +00:00
|
|
|
if (opts->flags & PARSE_OPT_OPTARG)
|
|
|
|
if (opts->long_name)
|
|
|
|
s = literal ? "[=%s]" : "[=<%s>]";
|
|
|
|
else
|
|
|
|
s = literal ? "[%s]" : "[<%s>]";
|
|
|
|
else
|
|
|
|
s = literal ? " %s" : " <%s>";
|
2013-02-09 06:31:09 +00:00
|
|
|
return utf8_fprintf(outfile, s, opts->argh ? _(opts->argh) : _("..."));
|
2009-05-21 07:33:17 +00:00
|
|
|
}
|
|
|
|
|
2007-10-14 23:38:30 +00:00
|
|
|
#define USAGE_OPTS_WIDTH 24
|
|
|
|
#define USAGE_GAP 2
|
|
|
|
|
2010-06-12 12:57:39 +00:00
|
|
|
static int usage_with_options_internal(struct parse_opt_ctx_t *ctx,
|
|
|
|
const char * const *usagestr,
|
|
|
|
const struct option *opts, int full, int err)
|
2007-10-14 23:38:30 +00:00
|
|
|
{
|
2010-05-17 15:34:41 +00:00
|
|
|
FILE *outfile = err ? stderr : stdout;
|
2017-09-25 04:08:05 +00:00
|
|
|
int need_newline;
|
2010-05-17 15:34:41 +00:00
|
|
|
|
2009-03-08 18:16:58 +00:00
|
|
|
if (!usagestr)
|
|
|
|
return PARSE_OPT_HELP;
|
|
|
|
|
2010-06-12 12:57:39 +00:00
|
|
|
if (!err && ctx && ctx->flags & PARSE_OPT_SHELL_EVAL)
|
|
|
|
fprintf(outfile, "cat <<\\EOF\n");
|
|
|
|
|
2012-05-06 14:23:51 +00:00
|
|
|
fprintf_ln(outfile, _("usage: %s"), _(*usagestr++));
|
2007-10-13 22:10:51 +00:00
|
|
|
while (*usagestr && **usagestr)
|
C style: use standard style for "TRANSLATORS" comments
Change all the "TRANSLATORS: [...]" comments in the C code to use the
regular Git coding style, and amend the style guide so that the
example there uses that style.
This custom style was necessary back in 2010 when the gettext support
was initially added, and was subsequently documented in commit
cbcfd4e3ea ("i18n: mention "TRANSLATORS:" marker in
Documentation/CodingGuidelines", 2014-04-18).
GNU xgettext hasn't had the parsing limitation that necessitated this
exception for almost 3 years. Since its 0.19 release on 2014-06-02
it's been able to recognize TRANSLATOR comments in the standard Git
comment syntax[1].
Usually we'd like to keep compatibility with software that's that
young, but in this case literally the only person who needs to be
using a gettext newer than 3 years old is Jiang Xin (the only person
who runs & commits "make pot" results), so I think in this case we can
make an exception.
This xgettext parsing feature was added after a thread on the Git
mailing list[2] which continued on the bug-gettext[3] list, but we
never subsequently changed our style & styleguide, do so.
There are already longstanding changes in git that use the standard
comment style & have their TRANSLATORS comments extracted properly
without getting the literal "*"'s mixed up in the text, as would
happen before xgettext 0.19.
Commit 7ff2683253 ("builtin-am: implement -i/--interactive",
2015-08-04) added one such comment, which in commit df0617bfa7 ("l10n:
git.pot: v2.6.0 round 1 (123 new, 41 removed)", 2015-09-05) got picked
up in the po/git.pot file with the right format, showing that Jiang
already runs a modern xgettext.
The xgettext parser does not handle the sort of non-standard comment
style that I'm amending here in sequencer.c, but that isn't standard
Git comment syntax anyway. With this change to sequencer.c & "make
pot" the comment in the pot file is now correct:
#. TRANSLATORS: %s will be "revert", "cherry-pick" or
-#. * "rebase -i".
+#. "rebase -i".
1. http://git.savannah.gnu.org/cgit/gettext.git/commit/?id=10af7fe6bd
2. <2ce9ec406501d112e032c8208417f8100bed04c6.1397712142.git.worldhello.net@gmail.com>
(https://public-inbox.org/git/2ce9ec406501d112e032c8208417f8100bed04c6.1397712142.git.worldhello.net@gmail.com/)
3. https://lists.gnu.org/archive/html/bug-gettext/2014-04/msg00016.html
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Acked-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-05-11 21:20:12 +00:00
|
|
|
/*
|
|
|
|
* TRANSLATORS: the colon here should align with the
|
|
|
|
* one in "usage: %s" translation.
|
|
|
|
*/
|
2012-05-06 14:23:51 +00:00
|
|
|
fprintf_ln(outfile, _(" or: %s"), _(*usagestr++));
|
2008-06-14 07:27:21 +00:00
|
|
|
while (*usagestr) {
|
2012-05-06 14:23:51 +00:00
|
|
|
if (**usagestr)
|
|
|
|
fprintf_ln(outfile, _(" %s"), _(*usagestr));
|
|
|
|
else
|
2017-09-25 04:08:04 +00:00
|
|
|
fputc('\n', outfile);
|
2008-06-14 07:27:21 +00:00
|
|
|
usagestr++;
|
|
|
|
}
|
2007-10-14 23:38:30 +00:00
|
|
|
|
2017-09-25 04:08:05 +00:00
|
|
|
need_newline = 1;
|
2007-10-14 23:38:30 +00:00
|
|
|
|
|
|
|
for (; opts->type != OPTION_END; opts++) {
|
|
|
|
size_t pos;
|
|
|
|
int pad;
|
|
|
|
|
|
|
|
if (opts->type == OPTION_GROUP) {
|
2010-05-17 15:34:41 +00:00
|
|
|
fputc('\n', outfile);
|
2017-09-25 04:08:05 +00:00
|
|
|
need_newline = 0;
|
2007-10-14 23:38:30 +00:00
|
|
|
if (*opts->help)
|
2012-05-06 14:23:51 +00:00
|
|
|
fprintf(outfile, "%s\n", _(opts->help));
|
2007-10-14 23:38:30 +00:00
|
|
|
continue;
|
|
|
|
}
|
2007-11-19 09:21:44 +00:00
|
|
|
if (!full && (opts->flags & PARSE_OPT_HIDDEN))
|
|
|
|
continue;
|
2007-10-14 23:38:30 +00:00
|
|
|
|
2017-09-25 04:08:05 +00:00
|
|
|
if (need_newline) {
|
|
|
|
fputc('\n', outfile);
|
|
|
|
need_newline = 0;
|
|
|
|
}
|
|
|
|
|
2010-05-17 15:34:41 +00:00
|
|
|
pos = fprintf(outfile, " ");
|
2012-02-28 19:06:09 +00:00
|
|
|
if (opts->short_name) {
|
2009-05-07 19:45:42 +00:00
|
|
|
if (opts->flags & PARSE_OPT_NODASH)
|
2010-05-17 15:34:41 +00:00
|
|
|
pos += fprintf(outfile, "%c", opts->short_name);
|
2009-05-07 19:45:42 +00:00
|
|
|
else
|
2010-05-17 15:34:41 +00:00
|
|
|
pos += fprintf(outfile, "-%c", opts->short_name);
|
2009-05-07 19:45:42 +00:00
|
|
|
}
|
2007-10-14 23:38:30 +00:00
|
|
|
if (opts->long_name && opts->short_name)
|
2010-05-17 15:34:41 +00:00
|
|
|
pos += fprintf(outfile, ", ");
|
2007-10-14 23:38:30 +00:00
|
|
|
if (opts->long_name)
|
2012-02-28 19:06:09 +00:00
|
|
|
pos += fprintf(outfile, "--%s", opts->long_name);
|
2009-05-07 19:45:08 +00:00
|
|
|
if (opts->type == OPTION_NUMBER)
|
2013-02-09 06:31:09 +00:00
|
|
|
pos += utf8_fprintf(outfile, _("-NUM"));
|
2007-10-14 23:38:30 +00:00
|
|
|
|
2010-12-01 23:31:36 +00:00
|
|
|
if ((opts->flags & PARSE_OPT_LITERAL_ARGHELP) ||
|
|
|
|
!(opts->flags & PARSE_OPT_NOARG))
|
2010-05-17 15:34:41 +00:00
|
|
|
pos += usage_argh(opts, outfile);
|
2007-10-14 23:38:30 +00:00
|
|
|
|
2007-10-13 22:10:51 +00:00
|
|
|
if (pos <= USAGE_OPTS_WIDTH)
|
|
|
|
pad = USAGE_OPTS_WIDTH - pos;
|
2007-10-14 23:38:30 +00:00
|
|
|
else {
|
2010-05-17 15:34:41 +00:00
|
|
|
fputc('\n', outfile);
|
2007-10-14 23:38:30 +00:00
|
|
|
pad = USAGE_OPTS_WIDTH;
|
|
|
|
}
|
2019-04-29 10:05:25 +00:00
|
|
|
if (opts->type == OPTION_ALIAS) {
|
|
|
|
fprintf(outfile, "%*s", pad + USAGE_GAP, "");
|
|
|
|
fprintf_ln(outfile, _("alias of --%s"),
|
|
|
|
(const char *)opts->value);
|
|
|
|
continue;
|
|
|
|
}
|
2012-05-06 14:23:51 +00:00
|
|
|
fprintf(outfile, "%*s%s\n", pad + USAGE_GAP, "", _(opts->help));
|
2007-10-14 23:38:30 +00:00
|
|
|
}
|
2010-05-17 15:34:41 +00:00
|
|
|
fputc('\n', outfile);
|
2007-10-13 22:10:51 +00:00
|
|
|
|
2010-06-12 12:57:39 +00:00
|
|
|
if (!err && ctx && ctx->flags & PARSE_OPT_SHELL_EVAL)
|
|
|
|
fputs("EOF\n", outfile);
|
|
|
|
|
2008-06-23 20:28:04 +00:00
|
|
|
return PARSE_OPT_HELP;
|
2007-10-14 23:38:30 +00:00
|
|
|
}
|
2007-10-14 09:05:12 +00:00
|
|
|
|
Fix sparse warnings
Fix warnings from 'make check'.
- These files don't include 'builtin.h' causing sparse to complain that
cmd_* isn't declared:
builtin/clone.c:364, builtin/fetch-pack.c:797,
builtin/fmt-merge-msg.c:34, builtin/hash-object.c:78,
builtin/merge-index.c:69, builtin/merge-recursive.c:22
builtin/merge-tree.c:341, builtin/mktag.c:156, builtin/notes.c:426
builtin/notes.c:822, builtin/pack-redundant.c:596,
builtin/pack-refs.c:10, builtin/patch-id.c:60, builtin/patch-id.c:149,
builtin/remote.c:1512, builtin/remote-ext.c:240,
builtin/remote-fd.c:53, builtin/reset.c:236, builtin/send-pack.c:384,
builtin/unpack-file.c:25, builtin/var.c:75
- These files have symbols which should be marked static since they're
only file scope:
submodule.c:12, diff.c:631, replace_object.c:92, submodule.c:13,
submodule.c:14, trace.c:78, transport.c:195, transport-helper.c:79,
unpack-trees.c:19, url.c:3, url.c:18, url.c:104, url.c:117, url.c:123,
url.c:129, url.c:136, thread-utils.c:21, thread-utils.c:48
- These files redeclare symbols to be different types:
builtin/index-pack.c:210, parse-options.c:564, parse-options.c:571,
usage.c:49, usage.c:58, usage.c:63, usage.c:72
- These files use a literal integer 0 when they really should use a NULL
pointer:
daemon.c:663, fast-import.c:2942, imap-send.c:1072, notes-merge.c:362
While we're in the area, clean up some unused #includes in builtin files
(mostly exec_cmd.h).
Signed-off-by: Stephen Boyd <bebarino@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-03-22 07:51:05 +00:00
|
|
|
void NORETURN usage_with_options(const char * const *usagestr,
|
2008-06-23 20:38:58 +00:00
|
|
|
const struct option *opts)
|
2007-11-19 09:21:44 +00:00
|
|
|
{
|
2010-06-12 12:57:39 +00:00
|
|
|
usage_with_options_internal(NULL, usagestr, opts, 0, 1);
|
2008-06-23 20:38:58 +00:00
|
|
|
exit(129);
|
2007-11-19 09:21:44 +00:00
|
|
|
}
|
|
|
|
|
Fix sparse warnings
Fix warnings from 'make check'.
- These files don't include 'builtin.h' causing sparse to complain that
cmd_* isn't declared:
builtin/clone.c:364, builtin/fetch-pack.c:797,
builtin/fmt-merge-msg.c:34, builtin/hash-object.c:78,
builtin/merge-index.c:69, builtin/merge-recursive.c:22
builtin/merge-tree.c:341, builtin/mktag.c:156, builtin/notes.c:426
builtin/notes.c:822, builtin/pack-redundant.c:596,
builtin/pack-refs.c:10, builtin/patch-id.c:60, builtin/patch-id.c:149,
builtin/remote.c:1512, builtin/remote-ext.c:240,
builtin/remote-fd.c:53, builtin/reset.c:236, builtin/send-pack.c:384,
builtin/unpack-file.c:25, builtin/var.c:75
- These files have symbols which should be marked static since they're
only file scope:
submodule.c:12, diff.c:631, replace_object.c:92, submodule.c:13,
submodule.c:14, trace.c:78, transport.c:195, transport-helper.c:79,
unpack-trees.c:19, url.c:3, url.c:18, url.c:104, url.c:117, url.c:123,
url.c:129, url.c:136, thread-utils.c:21, thread-utils.c:48
- These files redeclare symbols to be different types:
builtin/index-pack.c:210, parse-options.c:564, parse-options.c:571,
usage.c:49, usage.c:58, usage.c:63, usage.c:72
- These files use a literal integer 0 when they really should use a NULL
pointer:
daemon.c:663, fast-import.c:2942, imap-send.c:1072, notes-merge.c:362
While we're in the area, clean up some unused #includes in builtin files
(mostly exec_cmd.h).
Signed-off-by: Stephen Boyd <bebarino@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-03-22 07:51:05 +00:00
|
|
|
void NORETURN usage_msg_opt(const char *msg,
|
2009-02-02 05:12:58 +00:00
|
|
|
const char * const *usagestr,
|
|
|
|
const struct option *options)
|
|
|
|
{
|
2016-12-14 15:10:10 +00:00
|
|
|
fprintf(stderr, "fatal: %s\n\n", msg);
|
2009-02-02 05:12:58 +00:00
|
|
|
usage_with_options(usagestr, options);
|
|
|
|
}
|
|
|
|
|
2018-11-10 05:16:11 +00:00
|
|
|
const char *optname(const struct option *opt, int flags)
|
2012-12-15 17:42:10 +00:00
|
|
|
{
|
2018-11-10 05:16:11 +00:00
|
|
|
static struct strbuf sb = STRBUF_INIT;
|
|
|
|
|
|
|
|
strbuf_reset(&sb);
|
2012-12-15 17:42:10 +00:00
|
|
|
if (flags & OPT_SHORT)
|
2018-11-10 05:16:11 +00:00
|
|
|
strbuf_addf(&sb, "switch `%c'", opt->short_name);
|
|
|
|
else if (flags & OPT_UNSET)
|
|
|
|
strbuf_addf(&sb, "option `no-%s'", opt->long_name);
|
|
|
|
else
|
|
|
|
strbuf_addf(&sb, "option `%s'", opt->long_name);
|
|
|
|
|
|
|
|
return sb.buf;
|
2012-12-15 17:42:10 +00:00
|
|
|
}
|