提交 104d3794 编写于 作者: J Junio C Hamano

Merge branch 'rs/grep-parseopt'

* rs/grep-parseopt:
  grep: make callback functions static
  grep: use parseopt
  grep: remove global variable builtin_grep
  parseopt: add PARSE_OPT_NODASH
  parseopt: add OPT_NUMBER_CALLBACK
  parseopt: add OPT_NEGBIT
...@@ -137,6 +137,10 @@ There are some macros to easily define options: ...@@ -137,6 +137,10 @@ There are some macros to easily define options:
Introduce a boolean option. Introduce a boolean option.
If used, `int_var` is bitwise-ored with `mask`. If used, `int_var` is bitwise-ored with `mask`.
`OPT_NEGBIT(short, long, &int_var, description, mask)`::
Introduce a boolean option.
If used, `int_var` is bitwise-anded with the inverted `mask`.
`OPT_SET_INT(short, long, &int_var, description, integer)`:: `OPT_SET_INT(short, long, &int_var, description, integer)`::
Introduce a boolean option. Introduce a boolean option.
If used, set `int_var` to `integer`. If used, set `int_var` to `integer`.
...@@ -166,6 +170,14 @@ There are some macros to easily define options: ...@@ -166,6 +170,14 @@ There are some macros to easily define options:
`OPT_ARGUMENT(long, description)`:: `OPT_ARGUMENT(long, description)`::
Introduce a long-option argument that will be kept in `argv[]`. Introduce a long-option argument that will be kept in `argv[]`.
`OPT_NUMBER_CALLBACK(&var, description, func_ptr)`::
Recognize numerical options like -123 and feed the integer as
if it was an argument to the function given by `func_ptr`.
The result will be put into `var`. There can be only one such
option definition. It cannot be negated and it takes no
arguments. Short options that happen to be digits take
precedence over it.
The last element of the array must be `OPT_END()`. The last element of the array must be `OPT_END()`.
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "tag.h" #include "tag.h"
#include "tree-walk.h" #include "tree-walk.h"
#include "builtin.h" #include "builtin.h"
#include "parse-options.h"
#include "grep.h" #include "grep.h"
#ifndef NO_EXTERNAL_GREP #ifndef NO_EXTERNAL_GREP
...@@ -20,7 +21,10 @@ ...@@ -20,7 +21,10 @@
#endif #endif
#endif #endif
static int builtin_grep; static char const * const grep_usage[] = {
"git grep [options] [-e] <pattern> [<rev>...] [[--] path...]",
NULL
};
static int grep_config(const char *var, const char *value, void *cb) static int grep_config(const char *var, const char *value, void *cb)
{ {
...@@ -432,7 +436,8 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached) ...@@ -432,7 +436,8 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
} }
#endif #endif
static int grep_cache(struct grep_opt *opt, const char **paths, int cached) static int grep_cache(struct grep_opt *opt, const char **paths, int cached,
int external_grep_allowed)
{ {
int hit = 0; int hit = 0;
int nr; int nr;
...@@ -444,7 +449,7 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached) ...@@ -444,7 +449,7 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
* we grep through the checked-out files. It tends to * we grep through the checked-out files. It tends to
* be a lot more optimized * be a lot more optimized
*/ */
if (!cached && !builtin_grep) { if (!cached && external_grep_allowed) {
hit = external_grep(opt, paths, cached); hit = external_grep(opt, paths, cached);
if (hit >= 0) if (hit >= 0)
return hit; return hit;
...@@ -560,25 +565,182 @@ static int grep_object(struct grep_opt *opt, const char **paths, ...@@ -560,25 +565,182 @@ static int grep_object(struct grep_opt *opt, const char **paths,
die("unable to grep from object of type %s", typename(obj->type)); die("unable to grep from object of type %s", typename(obj->type));
} }
static const char builtin_grep_usage[] = static int context_callback(const struct option *opt, const char *arg,
"git grep <option>* [-e] <pattern> <rev>* [[--] <path>...]"; int unset)
{
struct grep_opt *grep_opt = opt->value;
int value;
const char *endp;
if (unset) {
grep_opt->pre_context = grep_opt->post_context = 0;
return 0;
}
value = strtol(arg, (char **)&endp, 10);
if (*endp) {
return error("switch `%c' expects a numerical value",
opt->short_name);
}
grep_opt->pre_context = grep_opt->post_context = value;
return 0;
}
static int file_callback(const struct option *opt, const char *arg, int unset)
{
struct grep_opt *grep_opt = opt->value;
FILE *patterns;
int lno = 0;
struct strbuf sb;
patterns = fopen(arg, "r");
if (!patterns)
die("'%s': %s", arg, strerror(errno));
while (strbuf_getline(&sb, patterns, '\n') == 0) {
/* ignore empty line like grep does */
if (sb.len == 0)
continue;
append_grep_pattern(grep_opt, strbuf_detach(&sb, NULL), arg,
++lno, GREP_PATTERN);
}
fclose(patterns);
strbuf_release(&sb);
return 0;
}
static int not_callback(const struct option *opt, const char *arg, int unset)
{
struct grep_opt *grep_opt = opt->value;
append_grep_pattern(grep_opt, "--not", "command line", 0, GREP_NOT);
return 0;
}
static int and_callback(const struct option *opt, const char *arg, int unset)
{
struct grep_opt *grep_opt = opt->value;
append_grep_pattern(grep_opt, "--and", "command line", 0, GREP_AND);
return 0;
}
static int open_callback(const struct option *opt, const char *arg, int unset)
{
struct grep_opt *grep_opt = opt->value;
append_grep_pattern(grep_opt, "(", "command line", 0, GREP_OPEN_PAREN);
return 0;
}
static int close_callback(const struct option *opt, const char *arg, int unset)
{
struct grep_opt *grep_opt = opt->value;
append_grep_pattern(grep_opt, ")", "command line", 0, GREP_CLOSE_PAREN);
return 0;
}
static const char emsg_invalid_context_len[] = static int pattern_callback(const struct option *opt, const char *arg,
"%s: invalid context length argument"; int unset)
static const char emsg_missing_context_len[] = {
"missing context length argument"; struct grep_opt *grep_opt = opt->value;
static const char emsg_missing_argument[] = append_grep_pattern(grep_opt, arg, "-e option", 0, GREP_PATTERN);
"option requires an argument -%s"; return 0;
}
static int help_callback(const struct option *opt, const char *arg, int unset)
{
return -1;
}
int cmd_grep(int argc, const char **argv, const char *prefix) int cmd_grep(int argc, const char **argv, const char *prefix)
{ {
int hit = 0; int hit = 0;
int cached = 0; int cached = 0;
int external_grep_allowed = 1;
int seen_dashdash = 0; int seen_dashdash = 0;
struct grep_opt opt; struct grep_opt opt;
struct object_array list = { 0, 0, NULL }; struct object_array list = { 0, 0, NULL };
const char **paths = NULL; const char **paths = NULL;
int i; int i;
int dummy;
struct option options[] = {
OPT_BOOLEAN(0, "cached", &cached,
"search in index instead of in the work tree"),
OPT_GROUP(""),
OPT_BOOLEAN('v', "invert-match", &opt.invert,
"show non-matching lines"),
OPT_BIT('i', "ignore-case", &opt.regflags,
"case insensitive matching", REG_ICASE),
OPT_BOOLEAN('w', "word-regexp", &opt.word_regexp,
"match patterns only at word boundaries"),
OPT_SET_INT('a', "text", &opt.binary,
"process binary files as text", GREP_BINARY_TEXT),
OPT_SET_INT('I', NULL, &opt.binary,
"don't match patterns in binary files",
GREP_BINARY_NOMATCH),
OPT_GROUP(""),
OPT_BIT('E', "extended-regexp", &opt.regflags,
"use extended POSIX regular expressions", REG_EXTENDED),
OPT_NEGBIT('G', "basic-regexp", &opt.regflags,
"use basic POSIX regular expressions (default)",
REG_EXTENDED),
OPT_BOOLEAN('F', "fixed-strings", &opt.fixed,
"interpret patterns as fixed strings"),
OPT_GROUP(""),
OPT_BOOLEAN('n', NULL, &opt.linenum, "show line numbers"),
OPT_NEGBIT('h', NULL, &opt.pathname, "don't show filenames", 1),
OPT_BIT('H', NULL, &opt.pathname, "show filenames", 1),
OPT_NEGBIT(0, "full-name", &opt.relative,
"show filenames relative to top directory", 1),
OPT_BOOLEAN('l', "files-with-matches", &opt.name_only,
"show only filenames instead of matching lines"),
OPT_BOOLEAN(0, "name-only", &opt.name_only,
"synonym for --files-with-matches"),
OPT_BOOLEAN('L', "files-without-match",
&opt.unmatch_name_only,
"show only the names of files without match"),
OPT_BOOLEAN('z', "null", &opt.null_following_name,
"print NUL after filenames"),
OPT_BOOLEAN('c', "count", &opt.count,
"show the number of matches instead of matching lines"),
OPT_SET_INT(0, "color", &opt.color, "highlight matches", 1),
OPT_GROUP(""),
OPT_CALLBACK('C', NULL, &opt, "n",
"show <n> context lines before and after matches",
context_callback),
OPT_INTEGER('B', NULL, &opt.pre_context,
"show <n> context lines before matches"),
OPT_INTEGER('A', NULL, &opt.post_context,
"show <n> context lines after matches"),
OPT_NUMBER_CALLBACK(&opt, "shortcut for -C NUM",
context_callback),
OPT_GROUP(""),
OPT_CALLBACK('f', NULL, &opt, "file",
"read patterns from file", file_callback),
{ OPTION_CALLBACK, 'e', NULL, &opt, "pattern",
"match <pattern>", PARSE_OPT_NONEG, pattern_callback },
{ OPTION_CALLBACK, 0, "and", &opt, NULL,
"combine patterns specified with -e",
PARSE_OPT_NOARG | PARSE_OPT_NONEG, and_callback },
OPT_BOOLEAN(0, "or", &dummy, ""),
{ OPTION_CALLBACK, 0, "not", &opt, NULL, "",
PARSE_OPT_NOARG | PARSE_OPT_NONEG, not_callback },
{ OPTION_CALLBACK, '(', NULL, &opt, NULL, "",
PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH,
open_callback },
{ OPTION_CALLBACK, ')', NULL, &opt, NULL, "",
PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH,
close_callback },
OPT_BOOLEAN(0, "all-match", &opt.all_match,
"show only matches from files that match all patterns"),
OPT_GROUP(""),
#if NO_EXTERNAL_GREP
OPT_BOOLEAN(0, "ext-grep", &external_grep_allowed,
"allow calling of grep(1) (ignored by this build)"),
#else
OPT_BOOLEAN(0, "ext-grep", &external_grep_allowed,
"allow calling of grep(1) (default)"),
#endif
{ OPTION_CALLBACK, 0, "help-all", &options, NULL, "show usage",
PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, help_callback },
OPT_END()
};
memset(&opt, 0, sizeof(opt)); memset(&opt, 0, sizeof(opt));
opt.prefix_length = (prefix && *prefix) ? strlen(prefix) : 0; opt.prefix_length = (prefix && *prefix) ? strlen(prefix) : 0;
...@@ -603,227 +765,21 @@ int cmd_grep(int argc, const char **argv, const char *prefix) ...@@ -603,227 +765,21 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
* unrecognized non option is the beginning of the refs list * unrecognized non option is the beginning of the refs list
* that continues up to the -- (if exists), and then paths. * that continues up to the -- (if exists), and then paths.
*/ */
argc = parse_options(argc, argv, options, grep_usage,
while (1 < argc) { PARSE_OPT_KEEP_DASHDASH |
const char *arg = argv[1]; PARSE_OPT_STOP_AT_NON_OPTION |
argc--; argv++; PARSE_OPT_NO_INTERNAL_HELP);
if (!strcmp("--cached", arg)) {
cached = 1; /* First unrecognized non-option token */
continue; if (argc > 0 && !opt.pattern_list) {
} append_grep_pattern(&opt, argv[0], "command line", 0,
if (!strcmp("--no-ext-grep", arg)) { GREP_PATTERN);
builtin_grep = 1; argv++;
continue; argc--;
}
if (!strcmp("-a", arg) ||
!strcmp("--text", arg)) {
opt.binary = GREP_BINARY_TEXT;
continue;
}
if (!strcmp("-i", arg) ||
!strcmp("--ignore-case", arg)) {
opt.regflags |= REG_ICASE;
continue;
}
if (!strcmp("-I", arg)) {
opt.binary = GREP_BINARY_NOMATCH;
continue;
}
if (!strcmp("-v", arg) ||
!strcmp("--invert-match", arg)) {
opt.invert = 1;
continue;
}
if (!strcmp("-E", arg) ||
!strcmp("--extended-regexp", arg)) {
opt.regflags |= REG_EXTENDED;
continue;
}
if (!strcmp("-F", arg) ||
!strcmp("--fixed-strings", arg)) {
opt.fixed = 1;
continue;
}
if (!strcmp("-G", arg) ||
!strcmp("--basic-regexp", arg)) {
opt.regflags &= ~REG_EXTENDED;
continue;
}
if (!strcmp("-n", arg)) {
opt.linenum = 1;
continue;
}
if (!strcmp("-h", arg)) {
opt.pathname = 0;
continue;
}
if (!strcmp("-H", arg)) {
opt.pathname = 1;
continue;
}
if (!strcmp("-l", arg) ||
!strcmp("--name-only", arg) ||
!strcmp("--files-with-matches", arg)) {
opt.name_only = 1;
continue;
}
if (!strcmp("-L", arg) ||
!strcmp("--files-without-match", arg)) {
opt.unmatch_name_only = 1;
continue;
}
if (!strcmp("-z", arg) ||
!strcmp("--null", arg)) {
opt.null_following_name = 1;
continue;
}
if (!strcmp("-c", arg) ||
!strcmp("--count", arg)) {
opt.count = 1;
continue;
}
if (!strcmp("-w", arg) ||
!strcmp("--word-regexp", arg)) {
opt.word_regexp = 1;
continue;
}
if (!prefixcmp(arg, "-A") ||
!prefixcmp(arg, "-B") ||
!prefixcmp(arg, "-C") ||
(arg[0] == '-' && '1' <= arg[1] && arg[1] <= '9')) {
unsigned num;
const char *scan;
switch (arg[1]) {
case 'A': case 'B': case 'C':
if (!arg[2]) {
if (argc <= 1)
die(emsg_missing_context_len);
scan = *++argv;
argc--;
}
else
scan = arg + 2;
break;
default:
scan = arg + 1;
break;
}
if (strtoul_ui(scan, 10, &num))
die(emsg_invalid_context_len, scan);
switch (arg[1]) {
case 'A':
opt.post_context = num;
break;
default:
case 'C':
opt.post_context = num;
case 'B':
opt.pre_context = num;
break;
}
continue;
}
if (!strcmp("-f", arg)) {
FILE *patterns;
int lno = 0;
char buf[1024];
if (argc <= 1)
die(emsg_missing_argument, arg);
patterns = fopen(argv[1], "r");
if (!patterns)
die("'%s': %s", argv[1], strerror(errno));
while (fgets(buf, sizeof(buf), patterns)) {
int len = strlen(buf);
if (len && buf[len-1] == '\n')
buf[len-1] = 0;
/* ignore empty line like grep does */
if (!buf[0])
continue;
append_grep_pattern(&opt, xstrdup(buf),
argv[1], ++lno,
GREP_PATTERN);
}
fclose(patterns);
argv++;
argc--;
continue;
}
if (!strcmp("--not", arg)) {
append_grep_pattern(&opt, arg, "command line", 0,
GREP_NOT);
continue;
}
if (!strcmp("--and", arg)) {
append_grep_pattern(&opt, arg, "command line", 0,
GREP_AND);
continue;
}
if (!strcmp("--or", arg))
continue; /* no-op */
if (!strcmp("(", arg)) {
append_grep_pattern(&opt, arg, "command line", 0,
GREP_OPEN_PAREN);
continue;
}
if (!strcmp(")", arg)) {
append_grep_pattern(&opt, arg, "command line", 0,
GREP_CLOSE_PAREN);
continue;
}
if (!strcmp("--all-match", arg)) {
opt.all_match = 1;
continue;
}
if (!strcmp("-e", arg)) {
if (1 < argc) {
append_grep_pattern(&opt, argv[1],
"-e option", 0,
GREP_PATTERN);
argv++;
argc--;
continue;
}
die(emsg_missing_argument, arg);
}
if (!strcmp("--full-name", arg)) {
opt.relative = 0;
continue;
}
if (!strcmp("--color", arg)) {
opt.color = 1;
continue;
}
if (!strcmp("--no-color", arg)) {
opt.color = 0;
continue;
}
if (!strcmp("--", arg)) {
/* later processing wants to have this at argv[1] */
argv--;
argc++;
break;
}
if (*arg == '-')
usage(builtin_grep_usage);
/* First unrecognized non-option token */
if (!opt.pattern_list) {
append_grep_pattern(&opt, arg, "command line", 0,
GREP_PATTERN);
break;
}
else {
/* We are looking at the first path or rev;
* it is found at argv[1] after leaving the
* loop.
*/
argc++; argv--;
break;
}
} }
if (opt.color && !opt.color_external) if (opt.color && !opt.color_external)
builtin_grep = 1; external_grep_allowed = 0;
if (!opt.pattern_list) if (!opt.pattern_list)
die("no pattern given."); die("no pattern given.");
if ((opt.regflags != REG_NEWLINE) && opt.fixed) if ((opt.regflags != REG_NEWLINE) && opt.fixed)
...@@ -831,7 +787,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) ...@@ -831,7 +787,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
compile_grep_patterns(&opt); compile_grep_patterns(&opt);
/* Check revs and then paths */ /* Check revs and then paths */
for (i = 1; i < argc; i++) { for (i = 0; i < argc; i++) {
const char *arg = argv[i]; const char *arg = argv[i];
unsigned char sha1[20]; unsigned char sha1[20];
/* Is it a rev? */ /* Is it a rev? */
...@@ -874,7 +830,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) ...@@ -874,7 +830,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
if (!list.nr) { if (!list.nr) {
if (!cached) if (!cached)
setup_work_tree(); setup_work_tree();
return !grep_cache(&opt, paths, cached); return !grep_cache(&opt, paths, cached, external_grep_allowed);
} }
if (cached) if (cached)
......
...@@ -61,23 +61,23 @@ struct grep_opt { ...@@ -61,23 +61,23 @@ struct grep_opt {
struct grep_expr *pattern_expression; struct grep_expr *pattern_expression;
int prefix_length; int prefix_length;
regex_t regexp; regex_t regexp;
unsigned linenum:1; int linenum;
unsigned invert:1; int invert;
unsigned status_only:1; int status_only;
unsigned name_only:1; int name_only;
unsigned unmatch_name_only:1; int unmatch_name_only;
unsigned count:1; int count;
unsigned word_regexp:1; int word_regexp;
unsigned fixed:1; int fixed;
unsigned all_match:1; int all_match;
#define GREP_BINARY_DEFAULT 0 #define GREP_BINARY_DEFAULT 0
#define GREP_BINARY_NOMATCH 1 #define GREP_BINARY_NOMATCH 1
#define GREP_BINARY_TEXT 2 #define GREP_BINARY_TEXT 2
unsigned binary:2; int binary;
unsigned extended:1; int extended;
unsigned relative:1; int relative;
unsigned pathname:1; int pathname;
unsigned null_following_name:1; int null_following_name;
int color; int color;
char color_match[COLOR_MAXLEN]; char color_match[COLOR_MAXLEN];
const char *color_external; const char *color_external;
......
...@@ -50,6 +50,7 @@ static int get_value(struct parse_opt_ctx_t *p, ...@@ -50,6 +50,7 @@ static int get_value(struct parse_opt_ctx_t *p,
/* FALLTHROUGH */ /* FALLTHROUGH */
case OPTION_BOOLEAN: case OPTION_BOOLEAN:
case OPTION_BIT: case OPTION_BIT:
case OPTION_NEGBIT:
case OPTION_SET_INT: case OPTION_SET_INT:
case OPTION_SET_PTR: case OPTION_SET_PTR:
return opterror(opt, "takes no value", flags); return opterror(opt, "takes no value", flags);
...@@ -66,6 +67,13 @@ static int get_value(struct parse_opt_ctx_t *p, ...@@ -66,6 +67,13 @@ static int get_value(struct parse_opt_ctx_t *p,
*(int *)opt->value |= opt->defval; *(int *)opt->value |= opt->defval;
return 0; return 0;
case OPTION_NEGBIT:
if (unset)
*(int *)opt->value |= opt->defval;
else
*(int *)opt->value &= ~opt->defval;
return 0;
case OPTION_BOOLEAN: case OPTION_BOOLEAN:
*(int *)opt->value = unset ? 0 : *(int *)opt->value + 1; *(int *)opt->value = unset ? 0 : *(int *)opt->value + 1;
return 0; return 0;
...@@ -121,11 +129,33 @@ static int get_value(struct parse_opt_ctx_t *p, ...@@ -121,11 +129,33 @@ 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 int parse_short_opt(struct parse_opt_ctx_t *p, const struct option *options)
{ {
const struct option *numopt = NULL;
for (; options->type != OPTION_END; options++) { for (; options->type != OPTION_END; options++) {
if (options->short_name == *p->opt) { if (options->short_name == *p->opt) {
p->opt = p->opt[1] ? p->opt + 1 : NULL; p->opt = p->opt[1] ? p->opt + 1 : NULL;
return get_value(p, options, OPT_SHORT); return get_value(p, options, OPT_SHORT);
} }
/*
* 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;
rc = (*numopt->callback)(numopt, arg, 0) ? (-1) : 0;
free(arg);
return rc;
} }
return -2; return -2;
} }
...@@ -215,6 +245,25 @@ static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg, ...@@ -215,6 +245,25 @@ static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,
return -2; return -2;
} }
static int parse_nodash_opt(struct parse_opt_ctx_t *p, const char *arg,
const struct option *options)
{
for (; options->type != OPTION_END; options++) {
if (!(options->flags & PARSE_OPT_NODASH))
continue;
if ((options->flags & PARSE_OPT_OPTARG) ||
!(options->flags & PARSE_OPT_NOARG))
die("BUG: dashless options don't support arguments");
if (!(options->flags & PARSE_OPT_NONEG))
die("BUG: dashless options don't support negation");
if (options->long_name)
die("BUG: dashless options can't be long");
if (options->short_name == arg[0] && arg[1] == '\0')
return get_value(p, options, OPT_SHORT);
}
return -2;
}
static void check_typos(const char *arg, const struct option *options) static void check_typos(const char *arg, const struct option *options)
{ {
if (strlen(arg) < 3) if (strlen(arg) < 3)
...@@ -265,6 +314,8 @@ int parse_options_step(struct parse_opt_ctx_t *ctx, ...@@ -265,6 +314,8 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
const char *arg = ctx->argv[0]; const char *arg = ctx->argv[0];
if (*arg != '-' || !arg[1]) { if (*arg != '-' || !arg[1]) {
if (parse_nodash_opt(ctx, arg, options) == 0)
continue;
if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION) if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION)
break; break;
ctx->out[ctx->cpidx++] = ctx->argv[0]; ctx->out[ctx->cpidx++] = ctx->argv[0];
...@@ -397,12 +448,18 @@ int usage_with_options_internal(const char * const *usagestr, ...@@ -397,12 +448,18 @@ int usage_with_options_internal(const char * const *usagestr,
continue; continue;
pos = fprintf(stderr, " "); pos = fprintf(stderr, " ");
if (opts->short_name) if (opts->short_name) {
pos += fprintf(stderr, "-%c", opts->short_name); if (opts->flags & PARSE_OPT_NODASH)
pos += fprintf(stderr, "%c", opts->short_name);
else
pos += fprintf(stderr, "-%c", opts->short_name);
}
if (opts->long_name && opts->short_name) if (opts->long_name && opts->short_name)
pos += fprintf(stderr, ", "); pos += fprintf(stderr, ", ");
if (opts->long_name) if (opts->long_name)
pos += fprintf(stderr, "--%s", opts->long_name); pos += fprintf(stderr, "--%s", opts->long_name);
if (opts->type == OPTION_NUMBER)
pos += fprintf(stderr, "-NUM");
switch (opts->type) { switch (opts->type) {
case OPTION_ARGUMENT: case OPTION_ARGUMENT:
...@@ -439,7 +496,7 @@ int usage_with_options_internal(const char * const *usagestr, ...@@ -439,7 +496,7 @@ int usage_with_options_internal(const char * const *usagestr,
pos += fprintf(stderr, " ..."); pos += fprintf(stderr, " ...");
} }
break; break;
default: /* OPTION_{BIT,BOOLEAN,SET_INT,SET_PTR} */ default: /* OPTION_{BIT,BOOLEAN,NUMBER,SET_INT,SET_PTR} */
break; break;
} }
......
...@@ -6,8 +6,10 @@ enum parse_opt_type { ...@@ -6,8 +6,10 @@ enum parse_opt_type {
OPTION_END, OPTION_END,
OPTION_ARGUMENT, OPTION_ARGUMENT,
OPTION_GROUP, OPTION_GROUP,
OPTION_NUMBER,
/* options with no arguments */ /* options with no arguments */
OPTION_BIT, OPTION_BIT,
OPTION_NEGBIT,
OPTION_BOOLEAN, /* _INCR would have been a better name */ OPTION_BOOLEAN, /* _INCR would have been a better name */
OPTION_SET_INT, OPTION_SET_INT,
OPTION_SET_PTR, OPTION_SET_PTR,
...@@ -31,6 +33,7 @@ enum parse_opt_option_flags { ...@@ -31,6 +33,7 @@ enum parse_opt_option_flags {
PARSE_OPT_NONEG = 4, PARSE_OPT_NONEG = 4,
PARSE_OPT_HIDDEN = 8, PARSE_OPT_HIDDEN = 8,
PARSE_OPT_LASTARG_DEFAULT = 16, PARSE_OPT_LASTARG_DEFAULT = 16,
PARSE_OPT_NODASH = 32,
}; };
struct option; struct option;
...@@ -64,8 +67,11 @@ typedef int parse_opt_cb(const struct option *, const char *arg, int unset); ...@@ -64,8 +67,11 @@ typedef int parse_opt_cb(const struct option *, const char *arg, int unset);
* PARSE_OPT_OPTARG: says that the argument is optional (not for BOOLEANs) * PARSE_OPT_OPTARG: says that the argument is optional (not for BOOLEANs)
* PARSE_OPT_NOARG: says that this option takes no argument, for CALLBACKs * PARSE_OPT_NOARG: says that this option takes no argument, for CALLBACKs
* PARSE_OPT_NONEG: says that this option cannot be negated * PARSE_OPT_NONEG: says that this option cannot be negated
* PARSE_OPT_HIDDEN this option is skipped in the default usage, showed in * PARSE_OPT_HIDDEN: this option is skipped in the default usage, and
* the long one. * shown only in the full usage.
* PARSE_OPT_LASTARG_DEFAULT: if no argument is given, the default value
* is used.
* PARSE_OPT_NODASH: this option doesn't start with a dash.
* *
* `callback`:: * `callback`::
* pointer to the callback to use for OPTION_CALLBACK. * pointer to the callback to use for OPTION_CALLBACK.
...@@ -93,6 +99,7 @@ struct option { ...@@ -93,6 +99,7 @@ struct option {
#define OPT_ARGUMENT(l, h) { OPTION_ARGUMENT, 0, (l), NULL, NULL, (h) } #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_NEGBIT(s, l, v, h, b) { OPTION_NEGBIT, (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) }
#define OPT_SET_INT(s, l, v, h, i) { OPTION_SET_INT, (s), (l), (v), NULL, (h), 0, NULL, (i) } #define OPT_SET_INT(s, l, v, h, i) { OPTION_SET_INT, (s), (l), (v), NULL, (h), 0, NULL, (i) }
#define OPT_SET_PTR(s, l, v, h, p) { OPTION_SET_PTR, (s), (l), (v), NULL, (h), 0, NULL, (p) } #define OPT_SET_PTR(s, l, v, h, p) { OPTION_SET_PTR, (s), (l), (v), NULL, (h), 0, NULL, (p) }
...@@ -103,6 +110,9 @@ struct option { ...@@ -103,6 +110,9 @@ struct option {
parse_opt_approxidate_cb } parse_opt_approxidate_cb }
#define OPT_CALLBACK(s, l, v, a, h, f) \ #define OPT_CALLBACK(s, l, v, a, h, f) \
{ OPTION_CALLBACK, (s), (l), (v), (a), (h), 0, (f) } { OPTION_CALLBACK, (s), (l), (v), (a), (h), 0, (f) }
#define OPT_NUMBER_CALLBACK(v, h, f) \
{ OPTION_NUMBER, 0, NULL, (v), NULL, (h), \
PARSE_OPT_NOARG | PARSE_OPT_NONEG, (f) }
/* parse_options() will filter out the processed options and leave the /* parse_options() will filter out the processed options and leave the
* non-option arguments in argv[]. * non-option arguments in argv[].
......
...@@ -12,6 +12,7 @@ usage: test-parse-options <options> ...@@ -12,6 +12,7 @@ usage: test-parse-options <options>
-b, --boolean get a boolean -b, --boolean get a boolean
-4, --or4 bitwise-or boolean with ...0100 -4, --or4 bitwise-or boolean with ...0100
--neg-or4 same as --no-or4
-i, --integer <n> get a integer -i, --integer <n> get a integer
-j <n> get a integer, too -j <n> get a integer, too
...@@ -29,6 +30,8 @@ String options ...@@ -29,6 +30,8 @@ String options
Magic arguments Magic arguments
--quux means --quux --quux means --quux
-NUM set integer to NUM
+ same as -b
Standard options Standard options
--abbrev[=<n>] use <n> digits to display SHA-1s --abbrev[=<n>] use <n> digits to display SHA-1s
...@@ -245,7 +248,56 @@ test_expect_success 'OPT_BIT() and OPT_SET_INT() work' ' ...@@ -245,7 +248,56 @@ test_expect_success 'OPT_BIT() and OPT_SET_INT() work' '
test_cmp expect output test_cmp expect output
' '
# --or4 test_expect_success 'OPT_NEGBIT() and OPT_SET_INT() work' '
# --no-or4 test-parse-options --set23 -bbbbb --neg-or4 > output 2> output.err &&
test ! -s output.err &&
test_cmp expect output
'
cat > expect <<EOF
boolean: 6
integer: 0
timestamp: 0
string: (not set)
abbrev: 7
verbose: 0
quiet: no
dry run: no
EOF
test_expect_success 'OPT_BIT() works' '
test-parse-options -bb --or4 > output 2> output.err &&
test ! -s output.err &&
test_cmp expect output
'
test_expect_success 'OPT_NEGBIT() works' '
test-parse-options -bb --no-neg-or4 > output 2> output.err &&
test ! -s output.err &&
test_cmp expect output
'
test_expect_success 'OPT_BOOLEAN() with PARSE_OPT_NODASH works' '
test-parse-options + + + + + + > output 2> output.err &&
test ! -s output.err &&
test_cmp expect output
'
cat > expect <<EOF
boolean: 0
integer: 12345
timestamp: 0
string: (not set)
abbrev: 7
verbose: 0
quiet: no
dry run: no
EOF
test_expect_success 'OPT_NUMBER_CALLBACK() works' '
test-parse-options -12345 > output 2> output.err &&
test ! -s output.err &&
test_cmp expect output
'
test_done test_done
...@@ -19,6 +19,12 @@ int length_callback(const struct option *opt, const char *arg, int unset) ...@@ -19,6 +19,12 @@ int length_callback(const struct option *opt, const char *arg, int unset)
return 0; return 0;
} }
int number_callback(const struct option *opt, const char *arg, int unset)
{
*(int *)opt->value = strtol(arg, NULL, 10);
return 0;
}
int main(int argc, const char **argv) int main(int argc, const char **argv)
{ {
const char *usage[] = { const char *usage[] = {
...@@ -29,6 +35,7 @@ int main(int argc, const char **argv) ...@@ -29,6 +35,7 @@ int main(int argc, const char **argv)
OPT_BOOLEAN('b', "boolean", &boolean, "get a boolean"), OPT_BOOLEAN('b', "boolean", &boolean, "get a boolean"),
OPT_BIT('4', "or4", &boolean, OPT_BIT('4', "or4", &boolean,
"bitwise-or boolean with ...0100", 4), "bitwise-or boolean with ...0100", 4),
OPT_NEGBIT(0, "neg-or4", &boolean, "same as --no-or4", 4),
OPT_GROUP(""), OPT_GROUP(""),
OPT_INTEGER('i', "integer", &integer, "get a integer"), OPT_INTEGER('i', "integer", &integer, "get a integer"),
OPT_INTEGER('j', NULL, &integer, "get a integer, too"), OPT_INTEGER('j', NULL, &integer, "get a integer, too"),
...@@ -45,6 +52,10 @@ int main(int argc, const char **argv) ...@@ -45,6 +52,10 @@ int main(int argc, const char **argv)
"set string to default", (unsigned long)"default"), "set string to default", (unsigned long)"default"),
OPT_GROUP("Magic arguments"), OPT_GROUP("Magic arguments"),
OPT_ARGUMENT("quux", "means --quux"), OPT_ARGUMENT("quux", "means --quux"),
OPT_NUMBER_CALLBACK(&integer, "set integer to NUM",
number_callback),
{ OPTION_BOOLEAN, '+', NULL, &boolean, NULL, "same as -b",
PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH },
OPT_GROUP("Standard options"), OPT_GROUP("Standard options"),
OPT__ABBREV(&abbrev), OPT__ABBREV(&abbrev),
OPT__VERBOSE(&verbose), OPT__VERBOSE(&verbose),
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册