X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=grep.c;h=4db1510d167c9fc3cc7b129c90aa874a4c7c2b0f;hb=5d384a13c4d14ddd759c8b8a484cc8f88b6e9448;hp=04e3ec6c6e90e8bd96495c7b40bcb014384b07d8;hpb=189627cd588c2937de7b655595b8ed777f445c85;p=platform%2Fupstream%2Fgit.git diff --git a/grep.c b/grep.c index 04e3ec6..4db1510 100644 --- a/grep.c +++ b/grep.c @@ -1,7 +1,248 @@ #include "cache.h" +#include "config.h" #include "grep.h" +#include "object-store.h" #include "userdiff.h" #include "xdiff-interface.h" +#include "diff.h" +#include "diffcore.h" +#include "commit.h" +#include "quote.h" +#include "help.h" + +static int grep_source_load(struct grep_source *gs); +static int grep_source_is_binary(struct grep_source *gs, + struct index_state *istate); + +static struct grep_opt grep_defaults; + +static const char *color_grep_slots[] = { + [GREP_COLOR_CONTEXT] = "context", + [GREP_COLOR_FILENAME] = "filename", + [GREP_COLOR_FUNCTION] = "function", + [GREP_COLOR_LINENO] = "lineNumber", + [GREP_COLOR_COLUMNNO] = "column", + [GREP_COLOR_MATCH_CONTEXT] = "matchContext", + [GREP_COLOR_MATCH_SELECTED] = "matchSelected", + [GREP_COLOR_SELECTED] = "selected", + [GREP_COLOR_SEP] = "separator", +}; + +static void std_output(struct grep_opt *opt, const void *buf, size_t size) +{ + fwrite(buf, size, 1, stdout); +} + +static void color_set(char *dst, const char *color_bytes) +{ + xsnprintf(dst, COLOR_MAXLEN, "%s", color_bytes); +} + +/* + * Initialize the grep_defaults template with hardcoded defaults. + * We could let the compiler do this, but without C99 initializers + * the code gets unwieldy and unreadable, so... + */ +void init_grep_defaults(struct repository *repo) +{ + struct grep_opt *opt = &grep_defaults; + static int run_once; + + if (run_once) + return; + run_once++; + + memset(opt, 0, sizeof(*opt)); + opt->repo = repo; + opt->relative = 1; + opt->pathname = 1; + opt->max_depth = -1; + opt->pattern_type_option = GREP_PATTERN_TYPE_UNSPECIFIED; + color_set(opt->colors[GREP_COLOR_CONTEXT], ""); + color_set(opt->colors[GREP_COLOR_FILENAME], ""); + color_set(opt->colors[GREP_COLOR_FUNCTION], ""); + color_set(opt->colors[GREP_COLOR_LINENO], ""); + color_set(opt->colors[GREP_COLOR_COLUMNNO], ""); + color_set(opt->colors[GREP_COLOR_MATCH_CONTEXT], GIT_COLOR_BOLD_RED); + color_set(opt->colors[GREP_COLOR_MATCH_SELECTED], GIT_COLOR_BOLD_RED); + color_set(opt->colors[GREP_COLOR_SELECTED], ""); + color_set(opt->colors[GREP_COLOR_SEP], GIT_COLOR_CYAN); + opt->only_matching = 0; + opt->color = -1; + opt->output = std_output; +} + +static int parse_pattern_type_arg(const char *opt, const char *arg) +{ + if (!strcmp(arg, "default")) + return GREP_PATTERN_TYPE_UNSPECIFIED; + else if (!strcmp(arg, "basic")) + return GREP_PATTERN_TYPE_BRE; + else if (!strcmp(arg, "extended")) + return GREP_PATTERN_TYPE_ERE; + else if (!strcmp(arg, "fixed")) + return GREP_PATTERN_TYPE_FIXED; + else if (!strcmp(arg, "perl")) + return GREP_PATTERN_TYPE_PCRE; + die("bad %s argument: %s", opt, arg); +} + +define_list_config_array_extra(color_grep_slots, {"match"}); + +/* + * Read the configuration file once and store it in + * the grep_defaults template. + */ +int grep_config(const char *var, const char *value, void *cb) +{ + struct grep_opt *opt = &grep_defaults; + const char *slot; + + if (userdiff_config(var, value) < 0) + return -1; + + if (!strcmp(var, "grep.extendedregexp")) { + opt->extended_regexp_option = git_config_bool(var, value); + return 0; + } + + if (!strcmp(var, "grep.patterntype")) { + opt->pattern_type_option = parse_pattern_type_arg(var, value); + return 0; + } + + if (!strcmp(var, "grep.linenumber")) { + opt->linenum = git_config_bool(var, value); + return 0; + } + if (!strcmp(var, "grep.column")) { + opt->columnnum = git_config_bool(var, value); + return 0; + } + + if (!strcmp(var, "grep.fullname")) { + opt->relative = !git_config_bool(var, value); + return 0; + } + + if (!strcmp(var, "color.grep")) + opt->color = git_config_colorbool(var, value); + if (!strcmp(var, "color.grep.match")) { + if (grep_config("color.grep.matchcontext", value, cb) < 0) + return -1; + if (grep_config("color.grep.matchselected", value, cb) < 0) + return -1; + } else if (skip_prefix(var, "color.grep.", &slot)) { + int i = LOOKUP_CONFIG(color_grep_slots, slot); + char *color; + + if (i < 0) + return -1; + color = opt->colors[i]; + if (!value) + return config_error_nonbool(var); + return color_parse(value, color); + } + return 0; +} + +/* + * Initialize one instance of grep_opt and copy the + * default values from the template we read the configuration + * information in an earlier call to git_config(grep_config). + */ +void grep_init(struct grep_opt *opt, struct repository *repo, const char *prefix) +{ + struct grep_opt *def = &grep_defaults; + int i; + + memset(opt, 0, sizeof(*opt)); + opt->repo = repo; + opt->prefix = prefix; + opt->prefix_length = (prefix && *prefix) ? strlen(prefix) : 0; + opt->pattern_tail = &opt->pattern_list; + opt->header_tail = &opt->header_list; + + opt->only_matching = def->only_matching; + opt->color = def->color; + opt->extended_regexp_option = def->extended_regexp_option; + opt->pattern_type_option = def->pattern_type_option; + opt->linenum = def->linenum; + opt->columnnum = def->columnnum; + opt->max_depth = def->max_depth; + opt->pathname = def->pathname; + opt->relative = def->relative; + opt->output = def->output; + + for (i = 0; i < NR_GREP_COLORS; i++) + color_set(opt->colors[i], def->colors[i]); +} + +static void grep_set_pattern_type_option(enum grep_pattern_type pattern_type, struct grep_opt *opt) +{ + /* + * When committing to the pattern type by setting the relevant + * fields in grep_opt it's generally not necessary to zero out + * the fields we're not choosing, since they won't have been + * set by anything. The extended_regexp_option field is the + * only exception to this. + * + * This is because in the process of parsing grep.patternType + * & grep.extendedRegexp we set opt->pattern_type_option and + * opt->extended_regexp_option, respectively. We then + * internally use opt->extended_regexp_option to see if we're + * compiling an ERE. It must be unset if that's not actually + * the case. + */ + if (pattern_type != GREP_PATTERN_TYPE_ERE && + opt->extended_regexp_option) + opt->extended_regexp_option = 0; + + switch (pattern_type) { + case GREP_PATTERN_TYPE_UNSPECIFIED: + /* fall through */ + + case GREP_PATTERN_TYPE_BRE: + break; + + case GREP_PATTERN_TYPE_ERE: + opt->extended_regexp_option = 1; + break; + + case GREP_PATTERN_TYPE_FIXED: + opt->fixed = 1; + break; + + case GREP_PATTERN_TYPE_PCRE: +#ifdef USE_LIBPCRE2 + opt->pcre2 = 1; +#else + /* + * It's important that pcre1 always be assigned to + * even when there's no USE_LIBPCRE* defined. We still + * call the PCRE stub function, it just dies with + * "cannot use Perl-compatible regexes[...]". + */ + opt->pcre1 = 1; +#endif + break; + } +} + +void grep_commit_pattern_type(enum grep_pattern_type pattern_type, struct grep_opt *opt) +{ + if (pattern_type != GREP_PATTERN_TYPE_UNSPECIFIED) + grep_set_pattern_type_option(pattern_type, opt); + else if (opt->pattern_type_option != GREP_PATTERN_TYPE_UNSPECIFIED) + grep_set_pattern_type_option(opt->pattern_type_option, opt); + else if (opt->extended_regexp_option) + /* + * This branch *must* happen after setting from the + * opt->pattern_type_option above, we don't want + * grep.extendedRegexp to override grep.patternType! + */ + grep_set_pattern_type_option(GREP_PATTERN_TYPE_ERE, opt); +} static struct grep_pat *create_grep_pat(const char *pat, size_t patlen, const char *origin, int no, @@ -60,6 +301,8 @@ void append_header_grep_pattern(struct grep_opt *opt, { struct grep_pat *p = create_grep_pat(pat, strlen(pat), "header", 0, GREP_PATTERN_HEAD, field); + if (field == GREP_HEADER_REFLOG) + opt->use_reflog_filter = 1; do_append_grep_pat(&opt->header_tail, p); } @@ -104,36 +347,78 @@ static NORETURN void compile_regexp_failed(const struct grep_pat *p, char where[1024]; if (p->no) - sprintf(where, "In '%s' at %d, ", p->origin, p->no); + xsnprintf(where, sizeof(where), "In '%s' at %d, ", p->origin, p->no); else if (p->origin) - sprintf(where, "%s, ", p->origin); + xsnprintf(where, sizeof(where), "%s, ", p->origin); else where[0] = 0; die("%s'%s': %s", where, p->pattern, error); } -#ifdef USE_LIBPCRE -static void compile_pcre_regexp(struct grep_pat *p, const struct grep_opt *opt) +static int is_fixed(const char *s, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) { + if (is_regex_special(s[i])) + return 0; + } + + return 1; +} + +static int has_null(const char *s, size_t len) +{ + /* + * regcomp cannot accept patterns with NULs so when using it + * we consider any pattern containing a NUL fixed. + */ + if (memchr(s, 0, len)) + return 1; + + return 0; +} + +#ifdef USE_LIBPCRE1 +static void compile_pcre1_regexp(struct grep_pat *p, const struct grep_opt *opt) { const char *error; int erroffset; int options = PCRE_MULTILINE; - if (opt->ignore_case) + if (opt->ignore_case) { + if (has_non_ascii(p->pattern)) + p->pcre1_tables = pcre_maketables(); options |= PCRE_CASELESS; + } + if (is_utf8_locale() && has_non_ascii(p->pattern)) + options |= PCRE_UTF8; - p->pcre_regexp = pcre_compile(p->pattern, options, &error, &erroffset, - NULL); - if (!p->pcre_regexp) + p->pcre1_regexp = pcre_compile(p->pattern, options, &error, &erroffset, + p->pcre1_tables); + if (!p->pcre1_regexp) compile_regexp_failed(p, error); - p->pcre_extra_info = pcre_study(p->pcre_regexp, 0, &error); - if (!p->pcre_extra_info && error) + p->pcre1_extra_info = pcre_study(p->pcre1_regexp, GIT_PCRE_STUDY_JIT_COMPILE, &error); + if (!p->pcre1_extra_info && error) die("%s", error); + +#ifdef GIT_PCRE1_USE_JIT + pcre_config(PCRE_CONFIG_JIT, &p->pcre1_jit_on); + if (p->pcre1_jit_on == 1) { + p->pcre1_jit_stack = pcre_jit_stack_alloc(1, 1024 * 1024); + if (!p->pcre1_jit_stack) + die("Couldn't allocate PCRE JIT stack"); + pcre_assign_jit_stack(p->pcre1_extra_info, NULL, p->pcre1_jit_stack); + } else if (p->pcre1_jit_on != 0) { + BUG("The pcre1_jit_on variable should be 0 or 1, not %d", + p->pcre1_jit_on); + } +#endif } -static int pcrematch(struct grep_pat *p, const char *line, const char *eol, +static int pcre1match(struct grep_pat *p, const char *line, const char *eol, regmatch_t *match, int eflags) { int ovector[30], ret, flags = 0; @@ -141,8 +426,19 @@ static int pcrematch(struct grep_pat *p, const char *line, const char *eol, if (eflags & REG_NOTBOL) flags |= PCRE_NOTBOL; - ret = pcre_exec(p->pcre_regexp, p->pcre_extra_info, line, eol - line, - 0, flags, ovector, ARRAY_SIZE(ovector)); +#ifdef GIT_PCRE1_USE_JIT + if (p->pcre1_jit_on) { + ret = pcre_jit_exec(p->pcre1_regexp, p->pcre1_extra_info, line, + eol - line, 0, flags, ovector, + ARRAY_SIZE(ovector), p->pcre1_jit_stack); + } else +#endif + { + ret = pcre_exec(p->pcre1_regexp, p->pcre1_extra_info, line, + eol - line, 0, flags, ovector, + ARRAY_SIZE(ovector)); + } + if (ret < 0 && ret != PCRE_ERROR_NOMATCH) die("pcre_exec failed with error code %d", ret); if (ret > 0) { @@ -154,78 +450,264 @@ static int pcrematch(struct grep_pat *p, const char *line, const char *eol, return ret; } -static void free_pcre_regexp(struct grep_pat *p) +static void free_pcre1_regexp(struct grep_pat *p) { - pcre_free(p->pcre_regexp); - pcre_free(p->pcre_extra_info); + pcre_free(p->pcre1_regexp); +#ifdef GIT_PCRE1_USE_JIT + if (p->pcre1_jit_on) { + pcre_free_study(p->pcre1_extra_info); + pcre_jit_stack_free(p->pcre1_jit_stack); + } else +#endif + { + pcre_free(p->pcre1_extra_info); + } + pcre_free((void *)p->pcre1_tables); } -#else /* !USE_LIBPCRE */ -static void compile_pcre_regexp(struct grep_pat *p, const struct grep_opt *opt) +#else /* !USE_LIBPCRE1 */ +static void compile_pcre1_regexp(struct grep_pat *p, const struct grep_opt *opt) { die("cannot use Perl-compatible regexes when not compiled with USE_LIBPCRE"); } -static int pcrematch(struct grep_pat *p, const char *line, const char *eol, +static int pcre1match(struct grep_pat *p, const char *line, const char *eol, regmatch_t *match, int eflags) { return 1; } -static void free_pcre_regexp(struct grep_pat *p) +static void free_pcre1_regexp(struct grep_pat *p) { } -#endif /* !USE_LIBPCRE */ +#endif /* !USE_LIBPCRE1 */ -static int is_fixed(const char *s, size_t len) +#ifdef USE_LIBPCRE2 +static void compile_pcre2_pattern(struct grep_pat *p, const struct grep_opt *opt) { - size_t i; + int error; + PCRE2_UCHAR errbuf[256]; + PCRE2_SIZE erroffset; + int options = PCRE2_MULTILINE; + const uint8_t *character_tables = NULL; + int jitret; + int patinforet; + size_t jitsizearg; + + assert(opt->pcre2); + + p->pcre2_compile_context = NULL; + + if (opt->ignore_case) { + if (has_non_ascii(p->pattern)) { + character_tables = pcre2_maketables(NULL); + p->pcre2_compile_context = pcre2_compile_context_create(NULL); + pcre2_set_character_tables(p->pcre2_compile_context, character_tables); + } + options |= PCRE2_CASELESS; + } + if (is_utf8_locale() && has_non_ascii(p->pattern)) + options |= PCRE2_UTF; - /* regcomp cannot accept patterns with NULs so we - * consider any pattern containing a NUL fixed. - */ - if (memchr(s, 0, len)) - return 1; + p->pcre2_pattern = pcre2_compile((PCRE2_SPTR)p->pattern, + p->patternlen, options, &error, &erroffset, + p->pcre2_compile_context); - for (i = 0; i < len; i++) { - if (is_regex_special(s[i])) - return 0; + if (p->pcre2_pattern) { + p->pcre2_match_data = pcre2_match_data_create_from_pattern(p->pcre2_pattern, NULL); + if (!p->pcre2_match_data) + die("Couldn't allocate PCRE2 match data"); + } else { + pcre2_get_error_message(error, errbuf, sizeof(errbuf)); + compile_regexp_failed(p, (const char *)&errbuf); + } + + pcre2_config(PCRE2_CONFIG_JIT, &p->pcre2_jit_on); + if (p->pcre2_jit_on == 1) { + jitret = pcre2_jit_compile(p->pcre2_pattern, PCRE2_JIT_COMPLETE); + if (jitret) + die("Couldn't JIT the PCRE2 pattern '%s', got '%d'\n", p->pattern, jitret); + + /* + * The pcre2_config(PCRE2_CONFIG_JIT, ...) call just + * tells us whether the library itself supports JIT, + * but to see whether we're going to be actually using + * JIT we need to extract PCRE2_INFO_JITSIZE from the + * pattern *after* we do pcre2_jit_compile() above. + * + * This is because if the pattern contains the + * (*NO_JIT) verb (see pcre2syntax(3)) + * pcre2_jit_compile() will exit early with 0. If we + * then proceed to call pcre2_jit_match() further down + * the line instead of pcre2_match() we'll either + * segfault (pre PCRE 10.31) or run into a fatal error + * (post PCRE2 10.31) + */ + patinforet = pcre2_pattern_info(p->pcre2_pattern, PCRE2_INFO_JITSIZE, &jitsizearg); + if (patinforet) + BUG("pcre2_pattern_info() failed: %d", patinforet); + if (jitsizearg == 0) { + p->pcre2_jit_on = 0; + return; + } + + p->pcre2_jit_stack = pcre2_jit_stack_create(1, 1024 * 1024, NULL); + if (!p->pcre2_jit_stack) + die("Couldn't allocate PCRE2 JIT stack"); + p->pcre2_match_context = pcre2_match_context_create(NULL); + if (!p->pcre2_match_context) + die("Couldn't allocate PCRE2 match context"); + pcre2_jit_stack_assign(p->pcre2_match_context, NULL, p->pcre2_jit_stack); + } else if (p->pcre2_jit_on != 0) { + BUG("The pcre2_jit_on variable should be 0 or 1, not %d", + p->pcre1_jit_on); + } +} + +static int pcre2match(struct grep_pat *p, const char *line, const char *eol, + regmatch_t *match, int eflags) +{ + int ret, flags = 0; + PCRE2_SIZE *ovector; + PCRE2_UCHAR errbuf[256]; + + if (eflags & REG_NOTBOL) + flags |= PCRE2_NOTBOL; + + if (p->pcre2_jit_on) + ret = pcre2_jit_match(p->pcre2_pattern, (unsigned char *)line, + eol - line, 0, flags, p->pcre2_match_data, + NULL); + else + ret = pcre2_match(p->pcre2_pattern, (unsigned char *)line, + eol - line, 0, flags, p->pcre2_match_data, + NULL); + + if (ret < 0 && ret != PCRE2_ERROR_NOMATCH) { + pcre2_get_error_message(ret, errbuf, sizeof(errbuf)); + die("%s failed with error code %d: %s", + (p->pcre2_jit_on ? "pcre2_jit_match" : "pcre2_match"), ret, + errbuf); + } + if (ret > 0) { + ovector = pcre2_get_ovector_pointer(p->pcre2_match_data); + ret = 0; + match->rm_so = (int)ovector[0]; + match->rm_eo = (int)ovector[1]; } + return ret; +} + +static void free_pcre2_pattern(struct grep_pat *p) +{ + pcre2_compile_context_free(p->pcre2_compile_context); + pcre2_code_free(p->pcre2_pattern); + pcre2_match_data_free(p->pcre2_match_data); + pcre2_jit_stack_free(p->pcre2_jit_stack); + pcre2_match_context_free(p->pcre2_match_context); +} +#else /* !USE_LIBPCRE2 */ +static void compile_pcre2_pattern(struct grep_pat *p, const struct grep_opt *opt) +{ + /* + * Unreachable until USE_LIBPCRE2 becomes synonymous with + * USE_LIBPCRE. See the sibling comment in + * grep_set_pattern_type_option(). + */ + die("cannot use Perl-compatible regexes when not compiled with USE_LIBPCRE"); +} + +static int pcre2match(struct grep_pat *p, const char *line, const char *eol, + regmatch_t *match, int eflags) +{ return 1; } +static void free_pcre2_pattern(struct grep_pat *p) +{ +} +#endif /* !USE_LIBPCRE2 */ + +static void compile_fixed_regexp(struct grep_pat *p, struct grep_opt *opt) +{ + struct strbuf sb = STRBUF_INIT; + int err; + int regflags = 0; + + basic_regex_quote_buf(&sb, p->pattern); + if (opt->ignore_case) + regflags |= REG_ICASE; + err = regcomp(&p->regexp, sb.buf, regflags); + if (opt->debug) + fprintf(stderr, "fixed %s\n", sb.buf); + strbuf_release(&sb); + if (err) { + char errbuf[1024]; + regerror(err, &p->regexp, errbuf, sizeof(errbuf)); + compile_regexp_failed(p, errbuf); + } +} + static void compile_regexp(struct grep_pat *p, struct grep_opt *opt) { + int ascii_only; int err; + int regflags = REG_NEWLINE; p->word_regexp = opt->word_regexp; p->ignore_case = opt->ignore_case; + ascii_only = !has_non_ascii(p->pattern); - if (opt->fixed || is_fixed(p->pattern, p->patternlen)) - p->fixed = 1; - else - p->fixed = 0; + /* + * Even when -F (fixed) asks us to do a non-regexp search, we + * may not be able to correctly case-fold when -i + * (ignore-case) is asked (in which case, we'll synthesize a + * regexp to match the pattern that matches regexp special + * characters literally, while ignoring case differences). On + * the other hand, even without -F, if the pattern does not + * have any regexp special characters and there is no need for + * case-folding search, we can internally turn it into a + * simple string match using kws. p->fixed tells us if we + * want to use kws. + */ + if (opt->fixed || + has_null(p->pattern, p->patternlen) || + is_fixed(p->pattern, p->patternlen)) + p->fixed = !p->ignore_case || ascii_only; if (p->fixed) { - if (opt->regflags & REG_ICASE || p->ignore_case) - p->kws = kwsalloc(tolower_trans_tbl); - else - p->kws = kwsalloc(NULL); + p->kws = kwsalloc(p->ignore_case ? tolower_trans_tbl : NULL); kwsincr(p->kws, p->pattern, p->patternlen); kwsprep(p->kws); return; + } else if (opt->fixed) { + /* + * We come here when the pattern has the non-ascii + * characters we cannot case-fold, and asked to + * ignore-case. + */ + compile_fixed_regexp(p, opt); + return; } - if (opt->pcre) { - compile_pcre_regexp(p, opt); + if (opt->pcre2) { + compile_pcre2_pattern(p, opt); return; } - err = regcomp(&p->regexp, p->pattern, opt->regflags); + if (opt->pcre1) { + compile_pcre1_regexp(p, opt); + return; + } + + if (p->ignore_case) + regflags |= REG_ICASE; + if (opt->extended_regexp_option) + regflags |= REG_EXTENDED; + err = regcomp(&p->regexp, p->pattern, regflags); if (err) { char errbuf[1024]; regerror(err, &p->regexp, errbuf, 1024); - regfree(&p->regexp); compile_regexp_failed(p, errbuf); } } @@ -332,6 +814,87 @@ static struct grep_expr *compile_pattern_expr(struct grep_pat **list) return compile_pattern_or(list); } +static void indent(int in) +{ + while (in-- > 0) + fputc(' ', stderr); +} + +static void dump_grep_pat(struct grep_pat *p) +{ + switch (p->token) { + case GREP_AND: fprintf(stderr, "*and*"); break; + case GREP_OPEN_PAREN: fprintf(stderr, "*(*"); break; + case GREP_CLOSE_PAREN: fprintf(stderr, "*)*"); break; + case GREP_NOT: fprintf(stderr, "*not*"); break; + case GREP_OR: fprintf(stderr, "*or*"); break; + + case GREP_PATTERN: fprintf(stderr, "pattern"); break; + case GREP_PATTERN_HEAD: fprintf(stderr, "pattern_head"); break; + case GREP_PATTERN_BODY: fprintf(stderr, "pattern_body"); break; + } + + switch (p->token) { + default: break; + case GREP_PATTERN_HEAD: + fprintf(stderr, "", p->field); break; + case GREP_PATTERN_BODY: + fprintf(stderr, ""); break; + } + switch (p->token) { + default: break; + case GREP_PATTERN_HEAD: + case GREP_PATTERN_BODY: + case GREP_PATTERN: + fprintf(stderr, "%.*s", (int)p->patternlen, p->pattern); + break; + } + fputc('\n', stderr); +} + +static void dump_grep_expression_1(struct grep_expr *x, int in) +{ + indent(in); + switch (x->node) { + case GREP_NODE_TRUE: + fprintf(stderr, "true\n"); + break; + case GREP_NODE_ATOM: + dump_grep_pat(x->u.atom); + break; + case GREP_NODE_NOT: + fprintf(stderr, "(not\n"); + dump_grep_expression_1(x->u.unary, in+1); + indent(in); + fprintf(stderr, ")\n"); + break; + case GREP_NODE_AND: + fprintf(stderr, "(and\n"); + dump_grep_expression_1(x->u.binary.left, in+1); + dump_grep_expression_1(x->u.binary.right, in+1); + indent(in); + fprintf(stderr, ")\n"); + break; + case GREP_NODE_OR: + fprintf(stderr, "(or\n"); + dump_grep_expression_1(x->u.binary.left, in+1); + dump_grep_expression_1(x->u.binary.right, in+1); + indent(in); + fprintf(stderr, ")\n"); + break; + } +} + +static void dump_grep_expression(struct grep_opt *opt) +{ + struct grep_expr *x = opt->pattern_expression; + + if (opt->all_match) + fprintf(stderr, "[all-match]\n"); + dump_grep_expression_1(x, 0); + fflush(NULL); +} + static struct grep_expr *grep_true_expr(void) { struct grep_expr *z = xcalloc(1, sizeof(*z)); @@ -360,9 +923,10 @@ static struct grep_expr *prep_header_patterns(struct grep_opt *opt) for (p = opt->header_list; p; p = p->next) { if (p->token != GREP_PATTERN_HEAD) - die("bug: a non-header pattern in grep header list."); - if (p->field < 0 || GREP_HEADER_FIELD_MAX <= p->field) - die("bug: unknown header field %d", p->field); + BUG("a non-header pattern in grep header list."); + if (p->field < GREP_HEADER_FIELD_MIN || + GREP_HEADER_FIELD_MAX <= p->field) + BUG("unknown header field %d", p->field); compile_regexp(p, opt); } @@ -375,7 +939,7 @@ static struct grep_expr *prep_header_patterns(struct grep_opt *opt) h = compile_pattern_atom(&pp); if (!h || pp != p->next) - die("bug: malformed header expr"); + BUG("malformed header expr"); if (!header_group[p->field]) { header_group[p->field] = h; continue; @@ -395,7 +959,23 @@ static struct grep_expr *prep_header_patterns(struct grep_opt *opt) return header_expr; } -void compile_grep_patterns(struct grep_opt *opt) +static struct grep_expr *grep_splice_or(struct grep_expr *x, struct grep_expr *y) +{ + struct grep_expr *z = x; + + while (x) { + assert(x->node == GREP_NODE_OR); + if (x->u.binary.right && + x->u.binary.right->node == GREP_NODE_TRUE) { + x->u.binary.right = y; + break; + } + x = x->u.binary.right; + } + return z; +} + +static void compile_grep_patterns_real(struct grep_opt *opt) { struct grep_pat *p; struct grep_expr *header_expr = prep_header_patterns(opt); @@ -415,7 +995,7 @@ void compile_grep_patterns(struct grep_opt *opt) if (opt->all_match || header_expr) opt->extended = 1; - else if (!opt->extended) + else if (!opt->extended && !opt->debug) return; p = opt->pattern_list; @@ -429,12 +1009,22 @@ void compile_grep_patterns(struct grep_opt *opt) if (!opt->pattern_expression) opt->pattern_expression = header_expr; + else if (opt->all_match) + opt->pattern_expression = grep_splice_or(header_expr, + opt->pattern_expression); else opt->pattern_expression = grep_or_expr(opt->pattern_expression, header_expr); opt->all_match = 1; } +void compile_grep_patterns(struct grep_opt *opt) +{ + compile_grep_patterns_real(opt); + if (opt->debug) + dump_grep_expression(opt); +} + static void free_pattern_expr(struct grep_expr *x) { switch (x->node) { @@ -465,8 +1055,10 @@ void free_grep_patterns(struct grep_opt *opt) case GREP_PATTERN_BODY: if (p->kws) kwsfree(p->kws); - else if (p->pcre_regexp) - free_pcre_regexp(p); + else if (p->pcre1_regexp) + free_pcre1_regexp(p); + else if (p->pcre2_pattern) + free_pcre2_pattern(p); else regfree(&p->regexp); free(p->pattern); @@ -514,12 +1106,12 @@ static void output_sep(struct grep_opt *opt, char sign) if (opt->null_following_name) opt->output(opt, "\0", 1); else - output_color(opt, &sign, 1, opt->color_sep); + output_color(opt, &sign, 1, opt->colors[GREP_COLOR_SEP]); } static void show_name(struct grep_opt *opt, const char *name) { - output_color(opt, name, strlen(name), opt->color_filename); + output_color(opt, name, strlen(name), opt->colors[GREP_COLOR_FILENAME]); opt->output(opt, opt->null_following_name ? "\0" : "\n", 1); } @@ -538,17 +1130,6 @@ static int fixmatch(struct grep_pat *p, char *line, char *eol, } } -static int regmatch(const regex_t *preg, char *line, char *eol, - regmatch_t *match, int eflags) -{ -#ifdef REG_STARTEND - match->rm_so = 0; - match->rm_eo = eol - line; - eflags |= REG_STARTEND; -#endif - return regexec(preg, line, 1, match, eflags); -} - static int patmatch(struct grep_pat *p, char *line, char *eol, regmatch_t *match, int eflags) { @@ -556,10 +1137,13 @@ static int patmatch(struct grep_pat *p, char *line, char *eol, if (p->fixed) hit = !fixmatch(p, line, eol, match); - else if (p->pcre_regexp) - hit = !pcrematch(p, line, eol, match, eflags); + else if (p->pcre1_regexp) + hit = !pcre1match(p, line, eol, match, eflags); + else if (p->pcre2_pattern) + hit = !pcre2match(p, line, eol, match, eflags); else - hit = !regmatch(&p->regexp, line, eol, match, eflags); + hit = !regexec_buf(&p->regexp, line, eol - line, 1, match, + eflags); return hit; } @@ -586,6 +1170,7 @@ static struct { } header_field[] = { { "author ", 7 }, { "committer ", 10 }, + { "reflog ", 7 }, }; static int match_one_pattern(struct grep_pat *p, char *bol, char *eol, @@ -609,7 +1194,14 @@ static int match_one_pattern(struct grep_pat *p, char *bol, char *eol, if (strncmp(bol, field, len)) return 0; bol += len; - saved_ch = strip_timestamp(bol, &eol); + switch (p->field) { + case GREP_HEADER_AUTHOR: + case GREP_HEADER_COMMITTER: + saved_ch = strip_timestamp(bol, &eol); + break; + default: + break; + } } again: @@ -664,11 +1256,11 @@ static int match_one_pattern(struct grep_pat *p, char *bol, char *eol, return hit; } -static int match_expr_eval(struct grep_expr *x, char *bol, char *eol, - enum grep_context ctx, int collect_hits) +static int match_expr_eval(struct grep_opt *opt, struct grep_expr *x, char *bol, + char *eol, enum grep_context ctx, ssize_t *col, + ssize_t *icol, int collect_hits) { int h = 0; - regmatch_t match; if (!x) die("Not a valid grep expression"); @@ -677,25 +1269,52 @@ static int match_expr_eval(struct grep_expr *x, char *bol, char *eol, h = 1; break; case GREP_NODE_ATOM: - h = match_one_pattern(x->u.atom, bol, eol, ctx, &match, 0); + { + regmatch_t tmp; + h = match_one_pattern(x->u.atom, bol, eol, ctx, + &tmp, 0); + if (h && (*col < 0 || tmp.rm_so < *col)) + *col = tmp.rm_so; + } break; case GREP_NODE_NOT: - h = !match_expr_eval(x->u.unary, bol, eol, ctx, 0); + /* + * Upon visiting a GREP_NODE_NOT, col and icol become swapped. + */ + h = !match_expr_eval(opt, x->u.unary, bol, eol, ctx, icol, col, + 0); break; case GREP_NODE_AND: - if (!match_expr_eval(x->u.binary.left, bol, eol, ctx, 0)) - return 0; - h = match_expr_eval(x->u.binary.right, bol, eol, ctx, 0); + h = match_expr_eval(opt, x->u.binary.left, bol, eol, ctx, col, + icol, 0); + if (h || opt->columnnum) { + /* + * Don't short-circuit AND when given --column, since a + * NOT earlier in the tree may turn this into an OR. In + * this case, see the below comment. + */ + h &= match_expr_eval(opt, x->u.binary.right, bol, eol, + ctx, col, icol, 0); + } break; case GREP_NODE_OR: - if (!collect_hits) - return (match_expr_eval(x->u.binary.left, - bol, eol, ctx, 0) || - match_expr_eval(x->u.binary.right, - bol, eol, ctx, 0)); - h = match_expr_eval(x->u.binary.left, bol, eol, ctx, 0); - x->u.binary.left->hit |= h; - h |= match_expr_eval(x->u.binary.right, bol, eol, ctx, 1); + if (!(collect_hits || opt->columnnum)) { + /* + * Don't short-circuit OR when given --column (or + * collecting hits) to ensure we don't skip a later + * child that would produce an earlier match. + */ + return (match_expr_eval(opt, x->u.binary.left, bol, eol, + ctx, col, icol, 0) || + match_expr_eval(opt, x->u.binary.right, bol, + eol, ctx, col, icol, 0)); + } + h = match_expr_eval(opt, x->u.binary.left, bol, eol, ctx, col, + icol, 0); + if (collect_hits) + x->u.binary.left->hit |= h; + h |= match_expr_eval(opt, x->u.binary.right, bol, eol, ctx, col, + icol, collect_hits); break; default: die("Unexpected node type (internal error) %d", x->node); @@ -706,27 +1325,43 @@ static int match_expr_eval(struct grep_expr *x, char *bol, char *eol, } static int match_expr(struct grep_opt *opt, char *bol, char *eol, - enum grep_context ctx, int collect_hits) + enum grep_context ctx, ssize_t *col, + ssize_t *icol, int collect_hits) { struct grep_expr *x = opt->pattern_expression; - return match_expr_eval(x, bol, eol, ctx, collect_hits); + return match_expr_eval(opt, x, bol, eol, ctx, col, icol, collect_hits); } static int match_line(struct grep_opt *opt, char *bol, char *eol, + ssize_t *col, ssize_t *icol, enum grep_context ctx, int collect_hits) { struct grep_pat *p; - regmatch_t match; + int hit = 0; if (opt->extended) - return match_expr(opt, bol, eol, ctx, collect_hits); + return match_expr(opt, bol, eol, ctx, col, icol, + collect_hits); /* we do not call with collect_hits without being extended */ for (p = opt->pattern_list; p; p = p->next) { - if (match_one_pattern(p, bol, eol, ctx, &match, 0)) - return 1; + regmatch_t tmp; + if (match_one_pattern(p, bol, eol, ctx, &tmp, 0)) { + hit |= 1; + if (!opt->columnnum) { + /* + * Without --column, any single match on a line + * is enough to know that it needs to be + * printed. With --column, scan _all_ patterns + * to find the earliest. + */ + break; + } + if (*col < 0 || tmp.rm_so < *col) + *col = tmp.rm_so; + } } - return 0; + return hit; } static int match_next_pattern(struct grep_pat *p, char *bol, char *eol, @@ -774,11 +1409,44 @@ static int next_match(struct grep_opt *opt, char *bol, char *eol, return hit; } +static void show_line_header(struct grep_opt *opt, const char *name, + unsigned lno, ssize_t cno, char sign) +{ + if (opt->heading && opt->last_shown == 0) { + output_color(opt, name, strlen(name), opt->colors[GREP_COLOR_FILENAME]); + opt->output(opt, "\n", 1); + } + opt->last_shown = lno; + + if (!opt->heading && opt->pathname) { + output_color(opt, name, strlen(name), opt->colors[GREP_COLOR_FILENAME]); + output_sep(opt, sign); + } + if (opt->linenum) { + char buf[32]; + xsnprintf(buf, sizeof(buf), "%d", lno); + output_color(opt, buf, strlen(buf), opt->colors[GREP_COLOR_LINENO]); + output_sep(opt, sign); + } + /* + * Treat 'cno' as the 1-indexed offset from the start of a non-context + * line to its first match. Otherwise, 'cno' is 0 indicating that we are + * being called with a context line. + */ + if (opt->columnnum && cno) { + char buf[32]; + xsnprintf(buf, sizeof(buf), "%"PRIuMAX, (uintmax_t)cno); + output_color(opt, buf, strlen(buf), opt->colors[GREP_COLOR_COLUMNNO]); + output_sep(opt, sign); + } +} + static void show_line(struct grep_opt *opt, char *bol, char *eol, - const char *name, unsigned lno, char sign) + const char *name, unsigned lno, ssize_t cno, char sign) { int rest = eol - bol; - char *line_color = NULL; + const char *match_color = NULL; + const char *line_color = NULL; if (opt->file_break && opt->last_shown == 0) { if (opt->show_hunk_mark) @@ -786,62 +1454,65 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol, } else if (opt->pre_context || opt->post_context || opt->funcbody) { if (opt->last_shown == 0) { if (opt->show_hunk_mark) { - output_color(opt, "--", 2, opt->color_sep); + output_color(opt, "--", 2, opt->colors[GREP_COLOR_SEP]); opt->output(opt, "\n", 1); } } else if (lno > opt->last_shown + 1) { - output_color(opt, "--", 2, opt->color_sep); + output_color(opt, "--", 2, opt->colors[GREP_COLOR_SEP]); opt->output(opt, "\n", 1); } } - if (opt->heading && opt->last_shown == 0) { - output_color(opt, name, strlen(name), opt->color_filename); - opt->output(opt, "\n", 1); - } - opt->last_shown = lno; - - if (!opt->heading && opt->pathname) { - output_color(opt, name, strlen(name), opt->color_filename); - output_sep(opt, sign); - } - if (opt->linenum) { - char buf[32]; - snprintf(buf, sizeof(buf), "%d", lno); - output_color(opt, buf, strlen(buf), opt->color_lineno); - output_sep(opt, sign); + if (!opt->only_matching) { + /* + * In case the line we're being called with contains more than + * one match, leave printing each header to the loop below. + */ + show_line_header(opt, name, lno, cno, sign); } - if (opt->color) { + if (opt->color || opt->only_matching) { regmatch_t match; enum grep_context ctx = GREP_CONTEXT_BODY; int ch = *eol; int eflags = 0; - if (sign == ':') - line_color = opt->color_selected; - else if (sign == '-') - line_color = opt->color_context; - else if (sign == '=') - line_color = opt->color_function; + if (opt->color) { + if (sign == ':') + match_color = opt->colors[GREP_COLOR_MATCH_SELECTED]; + else + match_color = opt->colors[GREP_COLOR_MATCH_CONTEXT]; + if (sign == ':') + line_color = opt->colors[GREP_COLOR_SELECTED]; + else if (sign == '-') + line_color = opt->colors[GREP_COLOR_CONTEXT]; + else if (sign == '=') + line_color = opt->colors[GREP_COLOR_FUNCTION]; + } *eol = '\0'; while (next_match(opt, bol, eol, ctx, &match, eflags)) { if (match.rm_so == match.rm_eo) break; - output_color(opt, bol, match.rm_so, line_color); + if (opt->only_matching) + show_line_header(opt, name, lno, cno, sign); + else + output_color(opt, bol, match.rm_so, line_color); output_color(opt, bol + match.rm_so, - match.rm_eo - match.rm_so, - opt->color_match); + match.rm_eo - match.rm_so, match_color); + if (opt->only_matching) + opt->output(opt, "\n", 1); bol += match.rm_eo; + cno += match.rm_eo; rest -= match.rm_eo; eflags = REG_NOTBOL; } *eol = ch; } - output_color(opt, bol, rest, line_color); - opt->output(opt, "\n", 1); + if (!opt->only_matching) { + output_color(opt, bol, rest, line_color); + opt->output(opt, "\n", 1); + } } -#ifndef NO_PTHREADS int grep_use_locks; /* @@ -867,16 +1538,11 @@ static inline void grep_attr_unlock(void) */ pthread_mutex_t grep_read_mutex; -#else -#define grep_attr_lock() -#define grep_attr_unlock() -#endif - static int match_funcname(struct grep_opt *opt, struct grep_source *gs, char *bol, char *eol) { xdemitconf_t *xecfg = opt->priv; if (xecfg && !xecfg->find_func) { - grep_source_load_driver(gs); + grep_source_load_driver(gs, opt->repo->index); if (gs->driver->funcname.pattern) { const struct userdiff_funcname *pe = &gs->driver->funcname; xdiff_set_find_func(xecfg, pe->pattern, pe->cflags); @@ -912,37 +1578,58 @@ static void show_funcname_line(struct grep_opt *opt, struct grep_source *gs, break; if (match_funcname(opt, gs, bol, eol)) { - show_line(opt, bol, eol, gs->name, lno, '='); + show_line(opt, bol, eol, gs->name, lno, 0, '='); break; } } } +static int is_empty_line(const char *bol, const char *eol); + static void show_pre_context(struct grep_opt *opt, struct grep_source *gs, char *bol, char *end, unsigned lno) { - unsigned cur = lno, from = 1, funcname_lno = 0; - int funcname_needed = !!opt->funcname; - - if (opt->funcbody && !match_funcname(opt, gs, bol, end)) - funcname_needed = 2; + unsigned cur = lno, from = 1, funcname_lno = 0, orig_from; + int funcname_needed = !!opt->funcname, comment_needed = 0; if (opt->pre_context < lno) from = lno - opt->pre_context; if (from <= opt->last_shown) from = opt->last_shown + 1; + orig_from = from; + if (opt->funcbody) { + if (match_funcname(opt, gs, bol, end)) + comment_needed = 1; + else + funcname_needed = 1; + from = opt->last_shown + 1; + } /* Rewind. */ - while (bol > gs->buf && - cur > (funcname_needed == 2 ? opt->last_shown + 1 : from)) { + while (bol > gs->buf && cur > from) { + char *next_bol = bol; char *eol = --bol; while (bol > gs->buf && bol[-1] != '\n') bol--; cur--; + if (comment_needed && (is_empty_line(bol, eol) || + match_funcname(opt, gs, bol, eol))) { + comment_needed = 0; + from = orig_from; + if (cur < from) { + cur++; + bol = next_bol; + break; + } + } if (funcname_needed && match_funcname(opt, gs, bol, eol)) { funcname_lno = cur; funcname_needed = 0; + if (opt->funcbody) + comment_needed = 1; + else + from = orig_from; } } @@ -956,7 +1643,7 @@ static void show_pre_context(struct grep_opt *opt, struct grep_source *gs, while (*eol != '\n') eol++; - show_line(opt, bol, eol, gs->name, cur, sign); + show_line(opt, bol, eol, gs->name, cur, 0, sign); bol = eol + 1; cur++; } @@ -1018,14 +1705,70 @@ static int look_ahead(struct grep_opt *opt, return 0; } -static void std_output(struct grep_opt *opt, const void *buf, size_t size) +static int fill_textconv_grep(struct repository *r, + struct userdiff_driver *driver, + struct grep_source *gs) { - fwrite(buf, size, 1, stdout); + struct diff_filespec *df; + char *buf; + size_t size; + + if (!driver || !driver->textconv) + return grep_source_load(gs); + + /* + * The textconv interface is intimately tied to diff_filespecs, so we + * have to pretend to be one. If we could unify the grep_source + * and diff_filespec structs, this mess could just go away. + */ + df = alloc_filespec(gs->path); + switch (gs->type) { + case GREP_SOURCE_OID: + fill_filespec(df, gs->identifier, 1, 0100644); + break; + case GREP_SOURCE_FILE: + fill_filespec(df, &null_oid, 0, 0100644); + break; + default: + BUG("attempt to textconv something without a path?"); + } + + /* + * fill_textconv is not remotely thread-safe; it may load objects + * behind the scenes, and it modifies the global diff tempfile + * structure. + */ + grep_read_lock(); + size = fill_textconv(r, driver, df, &buf); + grep_read_unlock(); + free_filespec(df); + + /* + * The normal fill_textconv usage by the diff machinery would just keep + * the textconv'd buf separate from the diff_filespec. But much of the + * grep code passes around a grep_source and assumes that its "buf" + * pointer is the beginning of the thing we are searching. So let's + * install our textconv'd version into the grep_source, taking care not + * to leak any existing buffer. + */ + grep_source_clear_data(gs); + gs->buf = buf; + gs->size = size; + + return 0; +} + +static int is_empty_line(const char *bol, const char *eol) +{ + while (bol < eol && isspace(*bol)) + bol++; + return bol == eol; } static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int collect_hits) { char *bol; + char *peek_bol = NULL; unsigned long left; unsigned lno = 1; unsigned last_hit = 0; @@ -1033,6 +1776,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle unsigned count = 0; int try_lookahead = 0; int show_function = 0; + struct userdiff_driver *textconv = NULL; enum grep_context ctx = GREP_CONTEXT_HEAD; xdemitconf_t xecfg; @@ -1054,19 +1798,36 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle } opt->last_shown = 0; - switch (opt->binary) { - case GREP_BINARY_DEFAULT: - if (grep_source_is_binary(gs)) - binary_match_only = 1; - break; - case GREP_BINARY_NOMATCH: - if (grep_source_is_binary(gs)) - return 0; /* Assume unmatch */ - break; - case GREP_BINARY_TEXT: - break; - default: - die("bug: unknown binary handling mode"); + if (opt->allow_textconv) { + grep_source_load_driver(gs, opt->repo->index); + /* + * We might set up the shared textconv cache data here, which + * is not thread-safe. + */ + grep_attr_lock(); + textconv = userdiff_get_textconv(gs->driver); + grep_attr_unlock(); + } + + /* + * We know the result of a textconv is text, so we only have to care + * about binary handling if we are not using it. + */ + if (!textconv) { + switch (opt->binary) { + case GREP_BINARY_DEFAULT: + if (grep_source_is_binary(gs, opt->repo->index)) + binary_match_only = 1; + break; + case GREP_BINARY_NOMATCH: + if (grep_source_is_binary(gs, opt->repo->index)) + return 0; /* Assume unmatch */ + break; + case GREP_BINARY_TEXT: + break; + default: + BUG("unknown binary handling mode"); + } } memset(&xecfg, 0, sizeof(xecfg)); @@ -1074,7 +1835,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle try_lookahead = should_lookahead(opt); - if (grep_source_load(gs) < 0) + if (fill_textconv_grep(opt->repo, textconv, gs) < 0) return 0; bol = gs->buf; @@ -1082,6 +1843,8 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle while (left) { char *eol, ch; int hit; + ssize_t cno; + ssize_t col = -1, icol = -1; /* * look_ahead() skips quickly to the line that possibly @@ -1105,7 +1868,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle if ((ctx == GREP_CONTEXT_HEAD) && (eol == bol)) ctx = GREP_CONTEXT_BODY; - hit = match_line(opt, bol, eol, ctx, collect_hits); + hit = match_line(opt, bol, eol, &col, &icol, ctx, collect_hits); *eol = ch; if (collect_hits) @@ -1135,7 +1898,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle if (binary_match_only) { opt->output(opt, "Binary file ", 12); output_color(opt, gs->name, strlen(gs->name), - opt->color_filename); + opt->colors[GREP_COLOR_FILENAME]); opt->output(opt, " matches\n", 9); return 1; } @@ -1146,20 +1909,47 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle show_pre_context(opt, gs, bol, eol, lno); else if (opt->funcname) show_funcname_line(opt, gs, bol, lno); - show_line(opt, bol, eol, gs->name, lno, ':'); + cno = opt->invert ? icol : col; + if (cno < 0) { + /* + * A negative cno indicates that there was no + * match on the line. We are thus inverted and + * being asked to show all lines that _don't_ + * match a given expression. Therefore, set cno + * to 0 to suggest the whole line matches. + */ + cno = 0; + } + show_line(opt, bol, eol, gs->name, lno, cno + 1, ':'); last_hit = lno; if (opt->funcbody) show_function = 1; goto next_line; } - if (show_function && match_funcname(opt, gs, bol, eol)) - show_function = 0; + if (show_function && (!peek_bol || peek_bol < bol)) { + unsigned long peek_left = left; + char *peek_eol = eol; + + /* + * Trailing empty lines are not interesting. + * Peek past them to see if they belong to the + * body of the current function. + */ + peek_bol = bol; + while (is_empty_line(peek_bol, peek_eol)) { + peek_bol = peek_eol + 1; + peek_eol = end_of_line(peek_bol, &peek_left); + } + + if (match_funcname(opt, gs, peek_bol, peek_eol)) + show_function = 0; + } if (show_function || (last_hit && lno <= last_hit + opt->post_context)) { /* If the last hit is within the post context, * we need to show this line. */ - show_line(opt, bol, eol, gs->name, lno, '-'); + show_line(opt, bol, eol, gs->name, lno, col + 1, '-'); } next_line: @@ -1174,7 +1964,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle return 0; if (opt->status_only) - return 0; + return opt->unmatch_name_only; if (opt->unmatch_name_only) { /* We did not see any hit, so we want to show this */ show_name(opt, gs->name); @@ -1191,9 +1981,12 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle */ if (opt->count && count) { char buf[32]; - output_color(opt, gs->name, strlen(gs->name), opt->color_filename); - output_sep(opt, ':'); - snprintf(buf, sizeof(buf), "%u\n", count); + if (opt->pathname) { + output_color(opt, gs->name, strlen(gs->name), + opt->colors[GREP_COLOR_FILENAME]); + output_sep(opt, ':'); + } + xsnprintf(buf, sizeof(buf), "%u\n", count); opt->output(opt, buf, strlen(buf)); return 1; } @@ -1252,7 +2045,7 @@ int grep_buffer(struct grep_opt *opt, char *buf, unsigned long size) struct grep_source gs; int r; - grep_source_init(&gs, GREP_SOURCE_BUF, NULL, NULL); + grep_source_init(&gs, GREP_SOURCE_BUF, NULL, NULL, NULL); gs.buf = buf; gs.size = size; @@ -1263,10 +2056,12 @@ int grep_buffer(struct grep_opt *opt, char *buf, unsigned long size) } void grep_source_init(struct grep_source *gs, enum grep_source_type type, - const char *name, const void *identifier) + const char *name, const char *path, + const void *identifier) { gs->type = type; - gs->name = name ? xstrdup(name) : NULL; + gs->name = xstrdup_or_null(name); + gs->path = xstrdup_or_null(path); gs->buf = NULL; gs->size = 0; gs->driver = NULL; @@ -1275,21 +2070,20 @@ void grep_source_init(struct grep_source *gs, enum grep_source_type type, case GREP_SOURCE_FILE: gs->identifier = xstrdup(identifier); break; - case GREP_SOURCE_SHA1: - gs->identifier = xmalloc(20); - memcpy(gs->identifier, identifier, 20); + case GREP_SOURCE_OID: + gs->identifier = oiddup(identifier); break; case GREP_SOURCE_BUF: gs->identifier = NULL; + break; } } void grep_source_clear(struct grep_source *gs) { - free(gs->name); - gs->name = NULL; - free(gs->identifier); - gs->identifier = NULL; + FREE_AND_NULL(gs->name); + FREE_AND_NULL(gs->path); + FREE_AND_NULL(gs->identifier); grep_source_clear_data(gs); } @@ -1297,9 +2091,8 @@ void grep_source_clear_data(struct grep_source *gs) { switch (gs->type) { case GREP_SOURCE_FILE: - case GREP_SOURCE_SHA1: - free(gs->buf); - gs->buf = NULL; + case GREP_SOURCE_OID: + FREE_AND_NULL(gs->buf); gs->size = 0; break; case GREP_SOURCE_BUF: @@ -1308,18 +2101,18 @@ void grep_source_clear_data(struct grep_source *gs) } } -static int grep_source_load_sha1(struct grep_source *gs) +static int grep_source_load_oid(struct grep_source *gs) { enum object_type type; grep_read_lock(); - gs->buf = read_sha1_file(gs->identifier, &type, &gs->size); + gs->buf = read_object_file(gs->identifier, &type, &gs->size); grep_read_unlock(); if (!gs->buf) return error(_("'%s': unable to read %s"), gs->name, - sha1_to_hex(gs->identifier)); + oid_to_hex(gs->identifier)); return 0; } @@ -1334,7 +2127,7 @@ static int grep_source_load_file(struct grep_source *gs) if (lstat(filename, &st) < 0) { err_ret: if (errno != ENOENT) - error(_("'%s': %s"), filename, strerror(errno)); + error_errno(_("failed to stat '%s'"), filename); return -1; } if (!S_ISREG(st.st_mode)) @@ -1343,22 +2136,21 @@ static int grep_source_load_file(struct grep_source *gs) i = open(filename, O_RDONLY); if (i < 0) goto err_ret; - data = xmalloc(size + 1); + data = xmallocz(size); if (st.st_size != read_in_full(i, data, size)) { - error(_("'%s': short read %s"), filename, strerror(errno)); + error_errno(_("'%s': short read"), filename); close(i); free(data); return -1; } close(i); - data[size] = 0; gs->buf = data; gs->size = size; return 0; } -int grep_source_load(struct grep_source *gs) +static int grep_source_load(struct grep_source *gs) { if (gs->buf) return 0; @@ -1366,29 +2158,32 @@ int grep_source_load(struct grep_source *gs) switch (gs->type) { case GREP_SOURCE_FILE: return grep_source_load_file(gs); - case GREP_SOURCE_SHA1: - return grep_source_load_sha1(gs); + case GREP_SOURCE_OID: + return grep_source_load_oid(gs); case GREP_SOURCE_BUF: return gs->buf ? 0 : -1; } - die("BUG: invalid grep_source type"); + BUG("invalid grep_source type to load"); } -void grep_source_load_driver(struct grep_source *gs) +void grep_source_load_driver(struct grep_source *gs, + struct index_state *istate) { if (gs->driver) return; grep_attr_lock(); - gs->driver = userdiff_find_by_path(gs->name); + if (gs->path) + gs->driver = userdiff_find_by_path(istate, gs->path); if (!gs->driver) gs->driver = userdiff_find_by_name("default"); grep_attr_unlock(); } -int grep_source_is_binary(struct grep_source *gs) +static int grep_source_is_binary(struct grep_source *gs, + struct index_state *istate) { - grep_source_load_driver(gs); + grep_source_load_driver(gs, istate); if (gs->driver->binary != -1) return gs->driver->binary;