]> git.ipfire.org Git - thirdparty/git.git/blobdiff - grep.c
Merge branch 'jt/fetch-pack-record-refs-in-the-dot-promisor'
[thirdparty/git.git] / grep.c
diff --git a/grep.c b/grep.c
index 9556d13dc1dd5f5d7f83e31421846c4f43fbb39d..b7ae5a442a624810471cc4dc8ab84cd1a35c963a 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -405,31 +405,20 @@ static int is_fixed(const char *s, size_t len)
        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;
+       int study_options = 0;
 
        if (opt->ignore_case) {
-               if (has_non_ascii(p->pattern))
+               if (!opt->ignore_locale && has_non_ascii(p->pattern))
                        p->pcre1_tables = pcre_maketables();
                options |= PCRE_CASELESS;
        }
-       if (is_utf8_locale() && has_non_ascii(p->pattern))
+       if (!opt->ignore_locale && is_utf8_locale() && has_non_ascii(p->pattern))
                options |= PCRE_UTF8;
 
        p->pcre1_regexp = pcre_compile(p->pattern, options, &error, &erroffset,
@@ -437,44 +426,31 @@ static void compile_pcre1_regexp(struct grep_pat *p, const struct grep_opt *opt)
        if (!p->pcre1_regexp)
                compile_regexp_failed(p, 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
+#if defined(PCRE_CONFIG_JIT) && !defined(NO_LIBPCRE1_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);
-       }
+       if (opt->debug)
+               fprintf(stderr, "pcre1_jit_on=%d\n", p->pcre1_jit_on);
+
+       if (p->pcre1_jit_on)
+               study_options = PCRE_STUDY_JIT_COMPILE;
 #endif
+
+       p->pcre1_extra_info = pcre_study(p->pcre1_regexp, study_options, &error);
+       if (!p->pcre1_extra_info && error)
+               die("%s", error);
 }
 
 static int pcre1match(struct grep_pat *p, const char *line, const char *eol,
                regmatch_t *match, int eflags)
 {
-       int ovector[30], ret, flags = 0;
+       int ovector[30], ret, flags = PCRE_NO_UTF8_CHECK;
 
        if (eflags & REG_NOTBOL)
                flags |= PCRE_NOTBOL;
 
-#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));
-       }
+       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);
@@ -490,15 +466,12 @@ static int pcre1match(struct grep_pat *p, const char *line, const char *eol,
 static void free_pcre1_regexp(struct grep_pat *p)
 {
        pcre_free(p->pcre1_regexp);
-#ifdef GIT_PCRE1_USE_JIT
-       if (p->pcre1_jit_on) {
+#ifdef PCRE_CONFIG_JIT
+       if (p->pcre1_jit_on)
                pcre_free_study(p->pcre1_extra_info);
-               pcre_jit_stack_free(p->pcre1_jit_stack);
-       } else
+       else
 #endif
-       {
                pcre_free(p->pcre1_extra_info);
-       }
        pcre_free((void *)p->pcre1_tables);
 }
 #else /* !USE_LIBPCRE1 */
@@ -535,7 +508,7 @@ static void compile_pcre2_pattern(struct grep_pat *p, const struct grep_opt *opt
 
        /* pcre2_global_context is initialized in append_grep_pattern */
        if (opt->ignore_case) {
-               if (has_non_ascii(p->pattern)) {
+               if (!opt->ignore_locale && has_non_ascii(p->pattern)) {
                        if (!pcre2_global_context)
                                BUG("pcre2_global_context uninitialized");
                        p->pcre2_tables = pcre2_maketables(pcre2_global_context);
@@ -545,7 +518,8 @@ static void compile_pcre2_pattern(struct grep_pat *p, const struct grep_opt *opt
                }
                options |= PCRE2_CASELESS;
        }
-       if (is_utf8_locale() && has_non_ascii(p->pattern))
+       if (!opt->ignore_locale && is_utf8_locale() && has_non_ascii(p->pattern) &&
+           !(!opt->ignore_case && (p->fixed || p->is_fixed)))
                options |= PCRE2_UTF;
 
        p->pcre2_pattern = pcre2_compile((PCRE2_SPTR)p->pattern,
@@ -562,7 +536,9 @@ static void compile_pcre2_pattern(struct grep_pat *p, const struct grep_opt *opt
        }
 
        pcre2_config(PCRE2_CONFIG_JIT, &p->pcre2_jit_on);
-       if (p->pcre2_jit_on == 1) {
+       if (opt->debug)
+               fprintf(stderr, "pcre2_jit_on=%d\n", p->pcre2_jit_on);
+       if (p->pcre2_jit_on) {
                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);
@@ -587,19 +563,11 @@ static void compile_pcre2_pattern(struct grep_pat *p, const struct grep_opt *opt
                        BUG("pcre2_pattern_info() failed: %d", patinforet);
                if (jitsizearg == 0) {
                        p->pcre2_jit_on = 0;
+                       if (opt->debug)
+                               fprintf(stderr, "pcre2_jit_on=%d: (*NO_JIT) in regex\n",
+                                       p->pcre2_jit_on);
                        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->pcre2_jit_on);
        }
 }
 
@@ -643,8 +611,6 @@ 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);
        free((void *)p->pcre2_tables);
 }
 #else /* !USE_LIBPCRE2 */
@@ -667,7 +633,6 @@ static int pcre2match(struct grep_pat *p, const char *line, const char *eol,
 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)
 {
@@ -688,46 +653,66 @@ static void compile_fixed_regexp(struct grep_pat *p, struct grep_opt *opt)
                compile_regexp_failed(p, errbuf);
        }
 }
+#endif /* !USE_LIBPCRE2 */
 
 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);
+       p->fixed = opt->fixed;
 
-       /*
-        * 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) {
-               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.
-                */
+       if (memchr(p->pattern, 0, p->patternlen) && !opt->pcre2)
+               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);
+#ifdef USE_LIBPCRE2
+       if (!p->fixed && !p->is_fixed) {
+              const char *no_jit = "(*NO_JIT)";
+              const int no_jit_len = strlen(no_jit);
+              if (starts_with(p->pattern, no_jit) &&
+                  is_fixed(p->pattern + no_jit_len,
+                           p->patternlen - no_jit_len))
+                      p->is_fixed = 1;
+       }
+#endif
+       if (p->fixed || p->is_fixed) {
+#ifdef USE_LIBPCRE2
+               opt->pcre2 = 1;
+               if (p->is_fixed) {
+                       compile_pcre2_pattern(p, opt);
+               } else {
+                       /*
+                        * E.g. t7811-grep-open.sh relies on the
+                        * pattern being restored.
+                        */
+                       char *old_pattern = p->pattern;
+                       size_t old_patternlen = p->patternlen;
+                       struct strbuf sb = STRBUF_INIT;
+
+                       /*
+                        * There is the PCRE2_LITERAL flag, but it's
+                        * only in PCRE v2 10.30 and later. Needing to
+                        * ifdef our way around that and dealing with
+                        * it + PCRE2_MULTILINE being an error is more
+                        * complex than just quoting this ourselves.
+                       */
+                       strbuf_add(&sb, "\\Q", 2);
+                       strbuf_add(&sb, p->pattern, p->patternlen);
+                       strbuf_add(&sb, "\\E", 2);
+
+                       p->pattern = sb.buf;
+                       p->patternlen = sb.len;
+                       compile_pcre2_pattern(p, opt);
+                       p->pattern = old_pattern;
+                       p->patternlen = old_patternlen;
+                       strbuf_release(&sb);
+               }
+#else /* !USE_LIBPCRE2 */
                compile_fixed_regexp(p, opt);
+#endif /* !USE_LIBPCRE2 */
                return;
        }
 
@@ -1094,9 +1079,7 @@ void free_grep_patterns(struct grep_opt *opt)
                case GREP_PATTERN: /* atom */
                case GREP_PATTERN_HEAD:
                case GREP_PATTERN_BODY:
-                       if (p->kws)
-                               kwsfree(p->kws);
-                       else if (p->pcre1_regexp)
+                       if (p->pcre1_regexp)
                                free_pcre1_regexp(p);
                        else if (p->pcre2_pattern)
                                free_pcre2_pattern(p);
@@ -1156,29 +1139,12 @@ static void show_name(struct grep_opt *opt, const char *name)
        opt->output(opt, opt->null_following_name ? "\0" : "\n", 1);
 }
 
-static int fixmatch(struct grep_pat *p, char *line, char *eol,
-                   regmatch_t *match)
-{
-       struct kwsmatch kwsm;
-       size_t offset = kwsexec(p->kws, line, eol - line, &kwsm);
-       if (offset == -1) {
-               match->rm_so = match->rm_eo = -1;
-               return REG_NOMATCH;
-       } else {
-               match->rm_so = offset;
-               match->rm_eo = match->rm_so + kwsm.size[0];
-               return 0;
-       }
-}
-
 static int patmatch(struct grep_pat *p, char *line, char *eol,
                    regmatch_t *match, int eflags)
 {
        int hit;
 
-       if (p->fixed)
-               hit = !fixmatch(p, line, eol, match);
-       else if (p->pcre1_regexp)
+       if (p->pcre1_regexp)
                hit = !pcre1match(p, line, eol, match, eflags);
        else if (p->pcre2_pattern)
                hit = !pcre2match(p, line, eol, match, eflags);