]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'rs/pcre-invalid-utf8-fix-fix'
authorJunio C Hamano <gitster@pobox.com>
Fri, 25 Feb 2022 23:47:38 +0000 (15:47 -0800)
committerJunio C Hamano <gitster@pobox.com>
Fri, 25 Feb 2022 23:47:38 +0000 (15:47 -0800)
Workaround we have for versions of PCRE2 before their version 10.36
were in effect only for their versions newer than 10.36 by mistake,
which has been corrected.

* rs/pcre-invalid-utf8-fix-fix:
  grep: fix triggering PCRE2_NO_START_OPTIMIZE workaround

1  2 
grep.c

diff --combined grep.c
index d5ad9617d993a2c4c2e49a840e14893b0637f03d,29deb85904c5118ee6f5772e74a6a23bc17f4724..82eb7da1022ecc0c08a83ca7bc46e18602ff275b
--- 1/grep.c
--- 2/grep.c
+++ b/grep.c
@@@ -19,6 -19,27 +19,6 @@@ static void std_output(struct grep_opt 
        fwrite(buf, size, 1, stdout);
  }
  
 -static struct grep_opt grep_defaults = {
 -      .relative = 1,
 -      .pathname = 1,
 -      .max_depth = -1,
 -      .pattern_type_option = GREP_PATTERN_TYPE_UNSPECIFIED,
 -      .colors = {
 -              [GREP_COLOR_CONTEXT] = "",
 -              [GREP_COLOR_FILENAME] = "",
 -              [GREP_COLOR_FUNCTION] = "",
 -              [GREP_COLOR_LINENO] = "",
 -              [GREP_COLOR_COLUMNNO] = "",
 -              [GREP_COLOR_MATCH_CONTEXT] = GIT_COLOR_BOLD_RED,
 -              [GREP_COLOR_MATCH_SELECTED] = GIT_COLOR_BOLD_RED,
 -              [GREP_COLOR_SELECTED] = "",
 -              [GREP_COLOR_SEP] = GIT_COLOR_CYAN,
 -      },
 -      .only_matching = 0,
 -      .color = -1,
 -      .output = std_output,
 -};
 -
  static const char *color_grep_slots[] = {
        [GREP_COLOR_CONTEXT]        = "context",
        [GREP_COLOR_FILENAME]       = "filename",
@@@ -54,12 -75,20 +54,12 @@@ define_list_config_array_extra(color_gr
   */
  int grep_config(const char *var, const char *value, void *cb)
  {
 -      struct grep_opt *opt = &grep_defaults;
 +      struct grep_opt *opt = cb;
        const char *slot;
  
        if (userdiff_config(var, value) < 0)
                return -1;
  
 -      /*
 -       * The instance of grep_opt that we set up here is copied by
 -       * grep_init() to be used by each individual invocation.
 -       * When populating a new field of this structure here, be
 -       * sure to think about ownership -- e.g., you might need to
 -       * override the shallow copy in grep_init() with a deep copy.
 -       */
 -
        if (!strcmp(var, "grep.extendedregexp")) {
                opt->extended_regexp_option = git_config_bool(var, value);
                return 0;
        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)
 +void grep_init(struct grep_opt *opt, struct repository *repo)
  {
 -      *opt = grep_defaults;
 +      struct grep_opt blank = GREP_OPT_INIT;
 +      memcpy(opt, &blank, 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;
  }
  
 -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:
 -              opt->pcre2 = 1;
 -              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,
                                        enum grep_pat_token t,
@@@ -271,7 -362,6 +271,7 @@@ static void compile_pcre2_pattern(struc
        int jitret;
        int patinforet;
        size_t jitsizearg;
 +      int literal = !opt->ignore_case && (p->fixed || p->is_fixed);
  
        /*
         * Call pcre2_general_context_create() before calling any
                }
                options |= PCRE2_CASELESS;
        }
 -      if (!opt->ignore_locale && is_utf8_locale() && has_non_ascii(p->pattern) &&
 -          !(!opt->ignore_case && (p->fixed || p->is_fixed)))
 +      if (!opt->ignore_locale && is_utf8_locale() && !literal)
                options |= (PCRE2_UTF | PCRE2_MATCH_INVALID_UTF);
  
- #ifdef GIT_PCRE2_VERSION_10_36_OR_HIGHER
+ #ifndef GIT_PCRE2_VERSION_10_36_OR_HIGHER
        /* Work around https://bugs.exim.org/show_bug.cgi?id=2642 fixed in 10.36 */
        if (PCRE2_MATCH_INVALID_UTF && options & (PCRE2_UTF | PCRE2_CASELESS))
                options |= PCRE2_NO_START_OPTIMIZE;
@@@ -432,17 -523,11 +432,17 @@@ static void compile_regexp(struct grep_
        int err;
        int regflags = REG_NEWLINE;
  
 +      if (opt->pattern_type_option == GREP_PATTERN_TYPE_UNSPECIFIED)
 +              opt->pattern_type_option = (opt->extended_regexp_option
 +                                          ? GREP_PATTERN_TYPE_ERE
 +                                          : GREP_PATTERN_TYPE_BRE);
 +
        p->word_regexp = opt->word_regexp;
        p->ignore_case = opt->ignore_case;
 -      p->fixed = opt->fixed;
 +      p->fixed = opt->pattern_type_option == GREP_PATTERN_TYPE_FIXED;
  
 -      if (memchr(p->pattern, 0, p->patternlen) && !opt->pcre2)
 +      if (opt->pattern_type_option != GREP_PATTERN_TYPE_PCRE &&
 +          memchr(p->pattern, 0, p->patternlen))
                die(_("given pattern contains NULL byte (via -f <file>). This is only supported with -P under PCRE v2"));
  
        p->is_fixed = is_fixed(p->pattern, p->patternlen);
                return;
        }
  
 -      if (opt->pcre2) {
 +      if (opt->pattern_type_option == GREP_PATTERN_TYPE_PCRE) {
                compile_pcre2_pattern(p, opt);
                return;
        }
  
        if (p->ignore_case)
                regflags |= REG_ICASE;
 -      if (opt->extended_regexp_option)
 +      if (opt->pattern_type_option == GREP_PATTERN_TYPE_ERE)
                regflags |= REG_EXTENDED;
        err = regcomp(&p->regexp, p->pattern, regflags);
        if (err) {
        }
  }
  
 +static struct grep_expr *grep_not_expr(struct grep_expr *expr)
 +{
 +      struct grep_expr *z = xcalloc(1, sizeof(*z));
 +      z->node = GREP_NODE_NOT;
 +      z->u.unary = expr;
 +      return z;
 +}
 +
 +static struct grep_expr *grep_binexp(enum grep_expr_node kind,
 +                                   struct grep_expr *left,
 +                                   struct grep_expr *right)
 +{
 +      struct grep_expr *z = xcalloc(1, sizeof(*z));
 +      z->node = kind;
 +      z->u.binary.left = left;
 +      z->u.binary.right = right;
 +      return z;
 +}
 +
 +static struct grep_expr *grep_or_expr(struct grep_expr *left, struct grep_expr *right)
 +{
 +      return grep_binexp(GREP_NODE_OR, left, right);
 +}
 +
 +static struct grep_expr *grep_and_expr(struct grep_expr *left, struct grep_expr *right)
 +{
 +      return grep_binexp(GREP_NODE_AND, left, right);
 +}
 +
  static struct grep_expr *compile_pattern_or(struct grep_pat **);
  static struct grep_expr *compile_pattern_atom(struct grep_pat **list)
  {
        case GREP_PATTERN: /* atom */
        case GREP_PATTERN_HEAD:
        case GREP_PATTERN_BODY:
 -              x = xcalloc(1, sizeof (struct grep_expr));
 +              CALLOC_ARRAY(x, 1);
                x->node = GREP_NODE_ATOM;
                x->u.atom = p;
                *list = p->next;
@@@ -582,10 -638,12 +582,10 @@@ static struct grep_expr *compile_patter
                if (!p->next)
                        die("--not not followed by pattern expression");
                *list = p->next;
 -              x = xcalloc(1, sizeof (struct grep_expr));
 -              x->node = GREP_NODE_NOT;
 -              x->u.unary = compile_pattern_not(list);
 -              if (!x->u.unary)
 +              x = compile_pattern_not(list);
 +              if (!x)
                        die("--not followed by non pattern expression");
 -              return x;
 +              return grep_not_expr(x);
        default:
                return compile_pattern_atom(list);
        }
  static struct grep_expr *compile_pattern_and(struct grep_pat **list)
  {
        struct grep_pat *p;
 -      struct grep_expr *x, *y, *z;
 +      struct grep_expr *x, *y;
  
        x = compile_pattern_not(list);
        p = *list;
        if (p && p->token == GREP_AND) {
 +              if (!x)
 +                      die("--and not preceded by pattern expression");
                if (!p->next)
                        die("--and not followed by pattern expression");
                *list = p->next;
                y = compile_pattern_and(list);
                if (!y)
                        die("--and not followed by pattern expression");
 -              z = xcalloc(1, sizeof (struct grep_expr));
 -              z->node = GREP_NODE_AND;
 -              z->u.binary.left = x;
 -              z->u.binary.right = y;
 -              return z;
 +              return grep_and_expr(x, y);
        }
        return x;
  }
  static struct grep_expr *compile_pattern_or(struct grep_pat **list)
  {
        struct grep_pat *p;
 -      struct grep_expr *x, *y, *z;
 +      struct grep_expr *x, *y;
  
        x = compile_pattern_and(list);
        p = *list;
                y = compile_pattern_or(list);
                if (!y)
                        die("not a pattern expression %s", p->pattern);
 -              z = xcalloc(1, sizeof (struct grep_expr));
 -              z->node = GREP_NODE_OR;
 -              z->u.binary.left = x;
 -              z->u.binary.right = y;
 -              return z;
 +              return grep_or_expr(x, y);
        }
        return x;
  }
@@@ -640,6 -704,15 +640,6 @@@ static struct grep_expr *grep_true_expr
        return z;
  }
  
 -static struct grep_expr *grep_or_expr(struct grep_expr *left, struct grep_expr *right)
 -{
 -      struct grep_expr *z = xcalloc(1, sizeof(*z));
 -      z->node = GREP_NODE_OR;
 -      z->u.binary.left = left;
 -      z->u.binary.right = right;
 -      return z;
 -}
 -
  static struct grep_expr *prep_header_patterns(struct grep_opt *opt)
  {
        struct grep_pat *p;
@@@ -722,7 -795,7 +722,7 @@@ void compile_grep_patterns(struct grep_
                }
        }
  
 -      if (opt->all_match || header_expr)
 +      if (opt->all_match || opt->no_body_match || header_expr)
                opt->extended = 1;
        else if (!opt->extended)
                return;
        if (p)
                die("incomplete pattern expression: %s", p->pattern);
  
 +      if (opt->no_body_match && opt->pattern_expression)
 +              opt->pattern_expression = grep_not_expr(opt->pattern_expression);
 +
        if (!header_expr)
                return;
  
@@@ -795,7 -865,7 +795,7 @@@ void free_grep_patterns(struct grep_op
        free_pattern_expr(opt->pattern_expression);
  }
  
 -static char *end_of_line(char *cp, unsigned long *left)
 +static const char *end_of_line(const char *cp, unsigned long *left)
  {
        unsigned long l = *left;
        while (l && *cp != '\n') {
@@@ -836,8 -906,7 +836,8 @@@ static void show_name(struct grep_opt *
        opt->output(opt, opt->null_following_name ? "\0" : "\n", 1);
  }
  
 -static int patmatch(struct grep_pat *p, char *line, char *eol,
 +static int patmatch(struct grep_pat *p,
 +                  const char *line, const char *eol,
                    regmatch_t *match, int eflags)
  {
        int hit;
        return hit;
  }
  
 -static int strip_timestamp(char *bol, char **eol_p)
 +static void strip_timestamp(const char *bol, const char **eol_p)
  {
 -      char *eol = *eol_p;
 -      int ch;
 +      const char *eol = *eol_p;
  
        while (bol < --eol) {
                if (*eol != '>')
                        continue;
                *eol_p = ++eol;
 -              ch = *eol;
 -              *eol = '\0';
 -              return ch;
 +              break;
        }
 -      return 0;
  }
  
  static struct {
        { "reflog ", 7 },
  };
  
 -static int match_one_pattern(struct grep_pat *p, char *bol, char *eol,
 -                           enum grep_context ctx,
 -                           regmatch_t *pmatch, int eflags)
 +static int headerless_match_one_pattern(struct grep_pat *p,
 +                                      const char *bol, const char *eol,
 +                                      enum grep_context ctx,
 +                                      regmatch_t *pmatch, int eflags)
  {
        int hit = 0;
 -      int saved_ch = 0;
        const char *start = bol;
  
        if ((p->token != GREP_PATTERN) &&
            ((p->token == GREP_PATTERN_HEAD) != (ctx == GREP_CONTEXT_HEAD)))
                return 0;
  
 -      if (p->token == GREP_PATTERN_HEAD) {
 -              const char *field;
 -              size_t len;
 -              assert(p->field < ARRAY_SIZE(header_field));
 -              field = header_field[p->field].field;
 -              len = header_field[p->field].len;
 -              if (strncmp(bol, field, len))
 -                      return 0;
 -              bol += len;
 -              switch (p->field) {
 -              case GREP_HEADER_AUTHOR:
 -              case GREP_HEADER_COMMITTER:
 -                      saved_ch = strip_timestamp(bol, &eol);
 -                      break;
 -              default:
 -                      break;
 -              }
 -      }
 -
   again:
        hit = patmatch(p, bol, eol, pmatch, eflags);
  
                                goto again;
                }
        }
 -      if (p->token == GREP_PATTERN_HEAD && saved_ch)
 -              *eol = saved_ch;
        if (hit) {
                pmatch[0].rm_so += bol - start;
                pmatch[0].rm_eo += bol - start;
        return hit;
  }
  
 -static int match_expr_eval(struct grep_opt *opt, struct grep_expr *x, char *bol,
 -                         char *eol, enum grep_context ctx, ssize_t *col,
 +static int match_one_pattern(struct grep_pat *p,
 +                           const char *bol, const char *eol,
 +                           enum grep_context ctx, regmatch_t *pmatch,
 +                           int eflags)
 +{
 +      const char *field;
 +      size_t len;
 +
 +      if (p->token == GREP_PATTERN_HEAD) {
 +              assert(p->field < ARRAY_SIZE(header_field));
 +              field = header_field[p->field].field;
 +              len = header_field[p->field].len;
 +              if (strncmp(bol, field, len))
 +                      return 0;
 +              bol += len;
 +
 +              switch (p->field) {
 +              case GREP_HEADER_AUTHOR:
 +              case GREP_HEADER_COMMITTER:
 +                      strip_timestamp(bol, &eol);
 +                      break;
 +              default:
 +                      break;
 +              }
 +      }
 +
 +      return headerless_match_one_pattern(p, bol, eol, ctx, pmatch, eflags);
 +}
 +
 +
 +static int match_expr_eval(struct grep_opt *opt, struct grep_expr *x,
 +                         const char *bol, const char *eol,
 +                         enum grep_context ctx, ssize_t *col,
                           ssize_t *icol, int collect_hits)
  {
        int h = 0;
                        if (h && (*col < 0 || tmp.rm_so < *col))
                                *col = tmp.rm_so;
                }
 +              if (x->u.atom->token == GREP_PATTERN_BODY)
 +                      opt->body_hit |= h;
                break;
        case GREP_NODE_NOT:
                /*
        return h;
  }
  
 -static int match_expr(struct grep_opt *opt, char *bol, char *eol,
 +static int match_expr(struct grep_opt *opt,
 +                    const char *bol, const char *eol,
                      enum grep_context ctx, ssize_t *col,
                      ssize_t *icol, int 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,
 +static int match_line(struct grep_opt *opt,
 +                    const char *bol, const char *eol,
                      ssize_t *col, ssize_t *icol,
                      enum grep_context ctx, int collect_hits)
  {
        return hit;
  }
  
 -static int match_next_pattern(struct grep_pat *p, char *bol, char *eol,
 +static int match_next_pattern(struct grep_pat *p,
 +                            const char *bol, const char *eol,
                              enum grep_context ctx,
                              regmatch_t *pmatch, int eflags)
  {
        regmatch_t match;
  
 -      if (!match_one_pattern(p, bol, eol, ctx, &match, eflags))
 +      if (!headerless_match_one_pattern(p, bol, eol, ctx, &match, eflags))
                return 0;
        if (match.rm_so < 0 || match.rm_eo < 0)
                return 0;
        return 1;
  }
  
 -static int next_match(struct grep_opt *opt, char *bol, char *eol,
 -                    enum grep_context ctx, regmatch_t *pmatch, int eflags)
 +int grep_next_match(struct grep_opt *opt,
 +                  const char *bol, const char *eol,
 +                  enum grep_context ctx, regmatch_t *pmatch,
 +                  enum grep_header_field field, int eflags)
  {
        struct grep_pat *p;
        int hit = 0;
  
        pmatch->rm_so = pmatch->rm_eo = -1;
        if (bol < eol) {
 -              for (p = opt->pattern_list; p; p = p->next) {
 +              for (p = ((ctx == GREP_CONTEXT_HEAD)
 +                         ? opt->header_list : opt->pattern_list);
 +                        p; p = p->next) {
                        switch (p->token) {
 -                      case GREP_PATTERN: /* atom */
                        case GREP_PATTERN_HEAD:
 +                              if ((field != GREP_HEADER_FIELD_MAX) &&
 +                                  (p->field != field))
 +                                      continue;
 +                              /* fall thru */
 +                      case GREP_PATTERN: /* atom */
                        case GREP_PATTERN_BODY:
                                hit |= match_next_pattern(p, bol, eol, ctx,
                                                          pmatch, eflags);
@@@ -1163,8 -1213,7 +1163,8 @@@ static void show_line_header(struct gre
        }
  }
  
 -static void show_line(struct grep_opt *opt, char *bol, char *eol,
 +static void show_line(struct grep_opt *opt,
 +                    const char *bol, const char *eol,
                      const char *name, unsigned lno, ssize_t cno, char sign)
  {
        int rest = eol - bol;
        if (opt->color || opt->only_matching) {
                regmatch_t match;
                enum grep_context ctx = GREP_CONTEXT_BODY;
 -              int ch = *eol;
                int eflags = 0;
  
                if (opt->color) {
                        else if (sign == '=')
                                line_color = opt->colors[GREP_COLOR_FUNCTION];
                }
 -              *eol = '\0';
 -              while (next_match(opt, bol, eol, ctx, &match, eflags)) {
 +              while (grep_next_match(opt, bol, eol, ctx, &match,
 +                                     GREP_HEADER_FIELD_MAX, eflags)) {
                        if (match.rm_so == match.rm_eo)
                                break;
  
                        rest -= match.rm_eo;
                        eflags = REG_NOTBOL;
                }
 -              *eol = ch;
        }
        if (!opt->only_matching) {
                output_color(opt, bol, rest, line_color);
@@@ -1254,8 -1305,7 +1254,8 @@@ static inline void grep_attr_unlock(voi
                pthread_mutex_unlock(&grep_attr_mutex);
  }
  
 -static int match_funcname(struct grep_opt *opt, struct grep_source *gs, char *bol, char *eol)
 +static int match_funcname(struct grep_opt *opt, struct grep_source *gs,
 +                        const char *bol, const char *eol)
  {
        xdemitconf_t *xecfg = opt->priv;
        if (xecfg && !xecfg->find_func) {
  }
  
  static void show_funcname_line(struct grep_opt *opt, struct grep_source *gs,
 -                             char *bol, unsigned lno)
 +                             const char *bol, unsigned lno)
  {
        while (bol > gs->buf) {
 -              char *eol = --bol;
 +              const char *eol = --bol;
  
                while (bol > gs->buf && bol[-1] != '\n')
                        bol--;
  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)
 +                           const char *bol, const char *end, unsigned lno)
  {
        unsigned cur = lno, from = 1, funcname_lno = 0, orig_from;
        int funcname_needed = !!opt->funcname, comment_needed = 0;
  
        /* Rewind. */
        while (bol > gs->buf && cur > from) {
 -              char *next_bol = bol;
 -              char *eol = --bol;
 +              const char *next_bol = bol;
 +              const char *eol = --bol;
  
                while (bol > gs->buf && bol[-1] != '\n')
                        bol--;
  
        /* Back forward. */
        while (cur < lno) {
 -              char *eol = bol, sign = (cur == funcname_lno) ? '=' : '-';
 +              const char *eol = bol, sign = (cur == funcname_lno) ? '=' : '-';
  
                while (*eol != '\n')
                        eol++;
@@@ -1384,12 -1434,12 +1384,12 @@@ static int should_lookahead(struct grep
  static int look_ahead(struct grep_opt *opt,
                      unsigned long *left_p,
                      unsigned *lno_p,
 -                    char **bol_p)
 +                    const char **bol_p)
  {
        unsigned lno = *lno_p;
 -      char *bol = *bol_p;
 +      const char *bol = *bol_p;
        struct grep_pat *p;
 -      char *sp, *last_bol;
 +      const char *sp, *last_bol;
        regoff_t earliest = -1;
  
        for (p = opt->pattern_list; p; p = p->next) {
@@@ -1444,7 -1494,7 +1444,7 @@@ static int fill_textconv_grep(struct re
                fill_filespec(df, gs->identifier, 1, 0100644);
                break;
        case GREP_SOURCE_FILE:
 -              fill_filespec(df, &null_oid, 0, 0100644);
 +              fill_filespec(df, null_oid(), 0, 0100644);
                break;
        default:
                BUG("attempt to textconv something without a path?");
@@@ -1491,8 -1541,8 +1491,8 @@@ static int is_empty_line(const char *bo
  
  static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int collect_hits)
  {
 -      char *bol;
 -      char *peek_bol = NULL;
 +      const char *bol;
 +      const char *peek_bol = NULL;
        unsigned long left;
        unsigned lno = 1;
        unsigned last_hit = 0;
        bol = gs->buf;
        left = gs->size;
        while (left) {
 -              char *eol, ch;
 +              const char *eol;
                int hit;
                ssize_t cno;
                ssize_t col = -1, icol = -1;
                    && look_ahead(opt, &left, &lno, &bol))
                        break;
                eol = end_of_line(bol, &left);
 -              ch = *eol;
 -              *eol = 0;
  
                if ((ctx == GREP_CONTEXT_HEAD) && (eol == bol))
                        ctx = GREP_CONTEXT_BODY;
  
                hit = match_line(opt, bol, eol, &col, &icol, ctx, collect_hits);
 -              *eol = ch;
  
                if (collect_hits)
                        goto next_line;
                }
                if (show_function && (!peek_bol || peek_bol < bol)) {
                        unsigned long peek_left = left;
 -                      char *peek_eol = eol;
 +                      const char *peek_eol = eol;
  
                        /*
                         * Trailing empty lines are not interesting.
@@@ -1755,43 -1808,29 +1755,43 @@@ int grep_source(struct grep_opt *opt, s
         * we do not have to do the two-pass grep when we do not check
         * buffer-wide "all-match".
         */
 -      if (!opt->all_match)
 +      if (!opt->all_match && !opt->no_body_match)
                return grep_source_1(opt, gs, 0);
  
        /* Otherwise the toplevel "or" terms hit a bit differently.
         * We first clear hit markers from them.
         */
        clr_hit_marker(opt->pattern_expression);
 +      opt->body_hit = 0;
        grep_source_1(opt, gs, 1);
  
 -      if (!chk_hit_marker(opt->pattern_expression))
 +      if (opt->all_match && !chk_hit_marker(opt->pattern_expression))
 +              return 0;
 +      if (opt->no_body_match && opt->body_hit)
                return 0;
  
        return grep_source_1(opt, gs, 0);
  }
  
 -int grep_buffer(struct grep_opt *opt, char *buf, unsigned long size)
 +static void grep_source_init_buf(struct grep_source *gs,
 +                               const char *buf,
 +                               unsigned long size)
 +{
 +      gs->type = GREP_SOURCE_BUF;
 +      gs->name = NULL;
 +      gs->path = NULL;
 +      gs->buf = buf;
 +      gs->size = size;
 +      gs->driver = NULL;
 +      gs->identifier = NULL;
 +}
 +
 +int grep_buffer(struct grep_opt *opt, const char *buf, unsigned long size)
  {
        struct grep_source gs;
        int r;
  
 -      grep_source_init(&gs, GREP_SOURCE_BUF, NULL, NULL, NULL);
 -      gs.buf = buf;
 -      gs.size = size;
 +      grep_source_init_buf(&gs, buf, size);
  
        r = grep_source(opt, &gs);
  
        return r;
  }
  
 -void grep_source_init(struct grep_source *gs, enum grep_source_type type,
 -                    const char *name, const char *path,
 -                    const void *identifier)
 +void grep_source_init_file(struct grep_source *gs, const char *name,
 +                         const char *path)
  {
 -      gs->type = type;
 +      gs->type = GREP_SOURCE_FILE;
        gs->name = xstrdup_or_null(name);
        gs->path = xstrdup_or_null(path);
        gs->buf = NULL;
        gs->size = 0;
        gs->driver = NULL;
 +      gs->identifier = xstrdup(path);
 +}
  
 -      switch (type) {
 -      case GREP_SOURCE_FILE:
 -              gs->identifier = xstrdup(identifier);
 -              break;
 -      case GREP_SOURCE_OID:
 -              gs->identifier = oiddup(identifier);
 -              break;
 -      case GREP_SOURCE_BUF:
 -              gs->identifier = NULL;
 -              break;
 -      }
 +void grep_source_init_oid(struct grep_source *gs, const char *name,
 +                        const char *path, const struct object_id *oid,
 +                        struct repository *repo)
 +{
 +      gs->type = GREP_SOURCE_OID;
 +      gs->name = xstrdup_or_null(name);
 +      gs->path = xstrdup_or_null(path);
 +      gs->buf = NULL;
 +      gs->size = 0;
 +      gs->driver = NULL;
 +      gs->identifier = oiddup(oid);
 +      gs->repo = repo;
  }
  
  void grep_source_clear(struct grep_source *gs)
@@@ -1838,9 -1875,7 +1838,9 @@@ void grep_source_clear_data(struct grep
        switch (gs->type) {
        case GREP_SOURCE_FILE:
        case GREP_SOURCE_OID:
 -              FREE_AND_NULL(gs->buf);
 +              /* these types own the buffer */
 +              free((char *)gs->buf);
 +              gs->buf = NULL;
                gs->size = 0;
                break;
        case GREP_SOURCE_BUF:
@@@ -1853,8 -1888,7 +1853,8 @@@ static int grep_source_load_oid(struct 
  {
        enum object_type type;
  
 -      gs->buf = read_object_file(gs->identifier, &type, &gs->size);
 +      gs->buf = repo_read_object_file(gs->repo, gs->identifier, &type,
 +                                      &gs->size);
        if (!gs->buf)
                return error(_("'%s': unable to read %s"),
                             gs->name,