Merge branch 'ph/parseopt'

* ph/parseopt:
  parse-options: new option type to treat an option-like parameter as an argument.
  parse-opt: bring PARSE_OPT_HIDDEN and NONEG to git-rev-parse --parseopt
This commit is contained in:
Junio C Hamano 2008-03-08 21:29:59 -08:00
commit 1cbcefb107
6 changed files with 68 additions and 23 deletions

View File

@ -325,7 +325,7 @@ The lines after the separator describe the options.
Each line of options has this format: Each line of options has this format:
------------ ------------
<opt_spec><arg_spec>? SP+ help LF <opt_spec><flags>* SP+ help LF
------------ ------------
`<opt_spec>`:: `<opt_spec>`::
@ -334,10 +334,17 @@ Each line of options has this format:
is necessary. `h,help`, `dry-run` and `f` are all three correct is necessary. `h,help`, `dry-run` and `f` are all three correct
`<opt_spec>`. `<opt_spec>`.
`<arg_spec>`:: `<flags>`::
an `<arg_spec>` tells the option parser if the option has an argument `<flags>` are of `*`, `=`, `?` or `!`.
(`=`), an optional one (`?` though its use is discouraged) or none * Use `=` if the option takes an argument.
(no `<arg_spec>` in that case).
* Use `?` to mean that the option is optional (though its use is discouraged).
* Use `*` to mean that this option should not be listed in the usage
generated for the `-h` argument. It's shown for `--help-all` as
documented in linkgit:gitcli[5].
* Use `!` to not make the corresponding negated long option available.
The remainder of the line, after stripping the spaces, is used The remainder of the line, after stripping the spaces, is used
as the help associated to the option. as the help associated to the option.

View File

@ -322,18 +322,24 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
o->type = OPTION_CALLBACK; o->type = OPTION_CALLBACK;
o->help = xstrdup(skipspaces(s)); o->help = xstrdup(skipspaces(s));
o->value = &parsed; o->value = &parsed;
o->flags = PARSE_OPT_NOARG;
o->callback = &parseopt_dump; o->callback = &parseopt_dump;
switch (s[-1]) { while (s > sb.buf && strchr("*=?!", s[-1])) {
case '=': switch (*--s) {
s--; case '=':
break; o->flags &= ~PARSE_OPT_NOARG;
case '?': break;
o->flags = PARSE_OPT_OPTARG; case '?':
s--; o->flags &= ~PARSE_OPT_NOARG;
break; o->flags |= PARSE_OPT_OPTARG;
default: break;
o->flags = PARSE_OPT_NOARG; case '!':
break; o->flags |= PARSE_OPT_NONEG;
break;
case '*':
o->flags |= PARSE_OPT_HIDDEN;
break;
}
} }
if (s - sb.buf == 1) /* short option only */ if (s - sb.buf == 1) /* short option only */

View File

@ -6,7 +6,8 @@
struct optparse_t { struct optparse_t {
const char **argv; const char **argv;
int argc; const char **out;
int argc, cpidx;
const char *opt; const char *opt;
}; };
@ -159,6 +160,16 @@ static int parse_long_opt(struct optparse_t *p, const char *arg,
continue; continue;
rest = skip_prefix(arg, options->long_name); rest = skip_prefix(arg, options->long_name);
if (options->type == OPTION_ARGUMENT) {
if (!rest)
continue;
if (*rest == '=')
return opterror(options, "takes no value", flags);
if (*rest)
continue;
p->out[p->cpidx++] = arg - 2;
return 0;
}
if (!rest) { if (!rest) {
/* abbreviated? */ /* abbreviated? */
if (!strncmp(options->long_name, arg, arg_end - arg)) { if (!strncmp(options->long_name, arg, arg_end - arg)) {
@ -242,14 +253,13 @@ static NORETURN void usage_with_options_internal(const char * const *,
int parse_options(int argc, const char **argv, const struct option *options, int parse_options(int argc, const char **argv, const struct option *options,
const char * const usagestr[], int flags) const char * const usagestr[], int flags)
{ {
struct optparse_t args = { argv + 1, argc - 1, NULL }; struct optparse_t args = { argv + 1, argv, argc - 1, 0, NULL };
int j = 0;
for (; args.argc; args.argc--, args.argv++) { for (; args.argc; args.argc--, args.argv++) {
const char *arg = args.argv[0]; const char *arg = args.argv[0];
if (*arg != '-' || !arg[1]) { if (*arg != '-' || !arg[1]) {
argv[j++] = args.argv[0]; args.out[args.cpidx++] = args.argv[0];
continue; continue;
} }
@ -286,9 +296,9 @@ int parse_options(int argc, const char **argv, const struct option *options,
usage_with_options(usagestr, options); usage_with_options(usagestr, options);
} }
memmove(argv + j, args.argv, args.argc * sizeof(*argv)); memmove(args.out + args.cpidx, args.argv, args.argc * sizeof(*args.out));
argv[j + args.argc] = NULL; args.out[args.cpidx + args.argc] = NULL;
return j + args.argc; return args.cpidx + args.argc;
} }
#define USAGE_OPTS_WIDTH 24 #define USAGE_OPTS_WIDTH 24
@ -328,6 +338,8 @@ void usage_with_options_internal(const char * const *usagestr,
pos += fprintf(stderr, "--%s", opts->long_name); pos += fprintf(stderr, "--%s", opts->long_name);
switch (opts->type) { switch (opts->type) {
case OPTION_ARGUMENT:
break;
case OPTION_INTEGER: case OPTION_INTEGER:
if (opts->flags & PARSE_OPT_OPTARG) if (opts->flags & PARSE_OPT_OPTARG)
pos += fprintf(stderr, " [<n>]"); pos += fprintf(stderr, " [<n>]");

View File

@ -4,6 +4,7 @@
enum parse_opt_type { enum parse_opt_type {
/* special types */ /* special types */
OPTION_END, OPTION_END,
OPTION_ARGUMENT,
OPTION_GROUP, OPTION_GROUP,
/* options with no arguments */ /* options with no arguments */
OPTION_BIT, OPTION_BIT,
@ -84,6 +85,7 @@ struct option {
}; };
#define OPT_END() { OPTION_END } #define OPT_END() { OPTION_END }
#define OPT_ARGUMENT(l, h) { OPTION_ARGUMENT, 0, (l), NULL, NULL, (h) }
#define OPT_GROUP(h) { OPTION_GROUP, 0, NULL, NULL, NULL, (h) } #define OPT_GROUP(h) { OPTION_GROUP, 0, NULL, NULL, NULL, (h) }
#define OPT_BIT(s, l, v, h, b) { OPTION_BIT, (s), (l), (v), NULL, (h), 0, NULL, (b) } #define OPT_BIT(s, l, v, h, b) { OPTION_BIT, (s), (l), (v), NULL, (h), 0, NULL, (b) }
#define OPT_BOOLEAN(s, l, v, h) { OPTION_BOOLEAN, (s), (l), (v), NULL, (h) } #define OPT_BOOLEAN(s, l, v, h) { OPTION_BOOLEAN, (s), (l), (v), NULL, (h) }

View File

@ -21,6 +21,9 @@ string options
--st <st> get another string (pervert ordering) --st <st> get another string (pervert ordering)
-o <str> get another string -o <str> get another string
magic arguments
--quux means --quux
EOF EOF
test_expect_success 'test help' ' test_expect_success 'test help' '
@ -114,4 +117,17 @@ test_expect_success 'detect possible typos' '
git diff expect.err output.err git diff expect.err output.err
' '
cat > expect <<EOF
boolean: 0
integer: 0
string: (not set)
arg 00: --quux
EOF
test_expect_success 'keep some options as arguments' '
test-parse-options --quux > output 2> output.err &&
test ! -s output.err &&
git diff expect output
'
test_done test_done

View File

@ -20,6 +20,8 @@ int main(int argc, const char **argv)
OPT_STRING(0, "string2", &string, "str", "get another string"), OPT_STRING(0, "string2", &string, "str", "get another string"),
OPT_STRING(0, "st", &string, "st", "get another string (pervert ordering)"), OPT_STRING(0, "st", &string, "st", "get another string (pervert ordering)"),
OPT_STRING('o', NULL, &string, "str", "get another string"), OPT_STRING('o', NULL, &string, "str", "get another string"),
OPT_GROUP("magic arguments"),
OPT_ARGUMENT("quux", "means --quux"),
OPT_END(), OPT_END(),
}; };
int i; int i;