t0302 & t3900: add forgotten quotes
[gitweb.git] / grep.c
diff --git a/grep.c b/grep.c
index 528b652f713d2b6db5f48e3829448212cc3837bf..47cee4506703f5dd2b7db84e09dae93adf99fbaf 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -4,12 +4,19 @@
 #include "xdiff-interface.h"
 #include "diff.h"
 #include "diffcore.h"
+#include "commit.h"
+#include "quote.h"
 
 static int grep_source_load(struct grep_source *gs);
 static int grep_source_is_binary(struct grep_source *gs);
 
 static struct grep_opt grep_defaults;
 
+static void std_output(struct grep_opt *opt, const void *buf, size_t size)
+{
+       fwrite(buf, size, 1, stdout);
+}
+
 /*
  * Initialize the grep_defaults template with hardcoded defaults.
  * We could let the compiler do this, but without C99 initializers
@@ -40,6 +47,7 @@ void init_grep_defaults(void)
        color_set(opt->color_selected, "");
        color_set(opt->color_sep, GIT_COLOR_CYAN);
        opt->color = -1;
+       opt->output = std_output;
 }
 
 static int parse_pattern_type_arg(const char *opt, const char *arg)
@@ -150,6 +158,7 @@ void grep_init(struct grep_opt *opt, const char *prefix)
        opt->pathname = def->pathname;
        opt->regflags = def->regflags;
        opt->relative = def->relative;
+       opt->output = def->output;
 
        color_set(opt->color_context, def->color_context);
        color_set(opt->color_filename, def->color_filename);
@@ -161,17 +170,7 @@ void grep_init(struct grep_opt *opt, const char *prefix)
        color_set(opt->color_sep, def->color_sep);
 }
 
-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)
-               grep_set_pattern_type_option(GREP_PATTERN_TYPE_ERE, opt);
-}
-
-void grep_set_pattern_type_option(enum grep_pattern_type pattern_type, struct grep_opt *opt)
+static void grep_set_pattern_type_option(enum grep_pattern_type pattern_type, struct grep_opt *opt)
 {
        switch (pattern_type) {
        case GREP_PATTERN_TYPE_UNSPECIFIED:
@@ -203,6 +202,16 @@ void grep_set_pattern_type_option(enum grep_pattern_type pattern_type, struct gr
        }
 }
 
+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)
+               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,
@@ -322,11 +331,16 @@ static void compile_pcre_regexp(struct grep_pat *p, const struct grep_opt *opt)
        int erroffset;
        int options = PCRE_MULTILINE;
 
-       if (opt->ignore_case)
+       if (opt->ignore_case) {
+               if (has_non_ascii(p->pattern))
+                       p->pcre_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);
+                                     p->pcre_tables);
        if (!p->pcre_regexp)
                compile_regexp_failed(p, error);
 
@@ -360,6 +374,7 @@ static void free_pcre_regexp(struct grep_pat *p)
 {
        pcre_free(p->pcre_regexp);
        pcre_free(p->pcre_extra_info);
+       pcre_free((void *)p->pcre_tables);
 }
 #else /* !USE_LIBPCRE */
 static void compile_pcre_regexp(struct grep_pat *p, const struct grep_opt *opt)
@@ -396,26 +411,68 @@ static int is_fixed(const char *s, size_t len)
        return 1;
 }
 
+static void compile_fixed_regexp(struct grep_pat *p, struct grep_opt *opt)
+{
+       struct strbuf sb = STRBUF_INIT;
+       int err;
+       int regflags;
+
+       basic_regex_quote_buf(&sb, p->pattern);
+       regflags = opt->regflags & ~REG_EXTENDED;
+       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));
+               regfree(&p->regexp);
+               compile_regexp_failed(p, errbuf);
+       }
+}
+
 static void compile_regexp(struct grep_pat *p, struct grep_opt *opt)
 {
+       int icase, ascii_only;
        int err;
 
        p->word_regexp = opt->word_regexp;
        p->ignore_case = opt->ignore_case;
+       icase          = opt->regflags & REG_ICASE || p->ignore_case;
+       ascii_only     = !has_non_ascii(p->pattern);
 
+       /*
+        * 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 || is_fixed(p->pattern, p->patternlen))
-               p->fixed = 1;
+               p->fixed = !icase || ascii_only;
        else
                p->fixed = 0;
 
        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(icase ? 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) {
@@ -643,10 +700,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.");
+                       die("BUG: a non-header pattern in grep header list.");
                if (p->field < GREP_HEADER_FIELD_MIN ||
                    GREP_HEADER_FIELD_MAX <= p->field)
-                       die("bug: unknown header field %d", p->field);
+                       die("BUG: unknown header field %d", p->field);
                compile_regexp(p, opt);
        }
 
@@ -659,7 +716,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");
+                       die("BUG: malformed header expr");
                if (!header_group[p->field]) {
                        header_group[p->field] = h;
                        continue;
@@ -848,17 +905,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)
 {
@@ -869,7 +915,8 @@ static int patmatch(struct grep_pat *p, char *line, char *eol,
        else if (p->pcre_regexp)
                hit = !pcrematch(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;
 }
@@ -1124,7 +1171,7 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,
        }
        if (opt->linenum) {
                char buf[32];
-               snprintf(buf, sizeof(buf), "%d", lno);
+               xsnprintf(buf, sizeof(buf), "%d", lno);
                output_color(opt, buf, strlen(buf), opt->color_lineno);
                output_sep(opt, sign);
        }
@@ -1339,11 +1386,6 @@ static int look_ahead(struct grep_opt *opt,
        return 0;
 }
 
-static void std_output(struct grep_opt *opt, const void *buf, size_t size)
-{
-       fwrite(buf, size, 1, stdout);
-}
-
 static int fill_textconv_grep(struct userdiff_driver *driver,
                              struct grep_source *gs)
 {
@@ -1396,9 +1438,17 @@ static int fill_textconv_grep(struct userdiff_driver *driver,
        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;
@@ -1456,7 +1506,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle
                case GREP_BINARY_TEXT:
                        break;
                default:
-                       die("bug: unknown binary handling mode");
+                       die("BUG: unknown binary handling mode");
                }
        }
 
@@ -1543,8 +1593,24 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle
                                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,
@@ -1587,7 +1653,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle
                                     opt->color_filename);
                        output_sep(opt, ':');
                }
-               snprintf(buf, sizeof(buf), "%u\n", count);
+               xsnprintf(buf, sizeof(buf), "%u\n", count);
                opt->output(opt, buf, strlen(buf));
                return 1;
        }
@@ -1671,12 +1737,23 @@ 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_SUBMODULE:
+               if (!identifier) {
+                       gs->identifier = NULL;
+                       break;
+               }
+               /*
+                * FALL THROUGH
+                * If the identifier is non-NULL (in the submodule case) it
+                * will be a SHA1 that needs to be copied.
+                */
        case GREP_SOURCE_SHA1:
                gs->identifier = xmalloc(20);
                hashcpy(gs->identifier, identifier);
                break;
        case GREP_SOURCE_BUF:
                gs->identifier = NULL;
+               break;
        }
 }
 
@@ -1696,6 +1773,7 @@ void grep_source_clear_data(struct grep_source *gs)
        switch (gs->type) {
        case GREP_SOURCE_FILE:
        case GREP_SOURCE_SHA1:
+       case GREP_SOURCE_SUBMODULE:
                free(gs->buf);
                gs->buf = NULL;
                gs->size = 0;
@@ -1732,7 +1810,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))
@@ -1743,7 +1821,7 @@ static int grep_source_load_file(struct grep_source *gs)
                goto err_ret;
        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;
@@ -1767,8 +1845,10 @@ static int grep_source_load(struct grep_source *gs)
                return grep_source_load_sha1(gs);
        case GREP_SOURCE_BUF:
                return gs->buf ? 0 : -1;
+       case GREP_SOURCE_SUBMODULE:
+               break;
        }
-       die("BUG: invalid grep_source type");
+       die("BUG: invalid grep_source type to load");
 }
 
 void grep_source_load_driver(struct grep_source *gs)