git-grep: Learn PCRE

This patch teaches git-grep the --perl-regexp/-P options (naming borrowed from GNU grep) in order to allow specifying PCRE regexes on the command line. PCRE has a number of features which make them more handy to use than POSIX regexes, like consistent escaping rules, extended character classes, ungreedy matching etc. git isn't build with PCRE support automatically. USE_LIBPCRE environment variable must be enabled (like `make USE_LIBPCRE=YesPlease`). Signed-off-by: Michał Kiedrowicz <michal.kiedrowicz@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Michał Kiedrowicz committed May 9, 2011 at 23:52 UTC 63e7e9d8b6483fed555ebed1c79a4820b2ba2558
6 files changed +107 -1
Documentation/git-grep.txt
+6
@@ -12,6 +12,7 @@ SYNOPSIS
12 'git grep' [-a | --text] [-I] [-i | --ignore-case] [-w | --word-regexp]
13 [-v | --invert-match] [-h|-H] [--full-name]
14 [-E | --extended-regexp] [-G | --basic-regexp]
15 + [-P | --perl-regexp]
16 [-F | --fixed-strings] [-n | --line-number]
17 [-l | --files-with-matches] [-L | --files-without-match]
18 [(-O | --open-files-in-pager) [<pager>]]
@@ -97,6 +98,11 @@ OPTIONS
98 Use POSIX extended/basic regexp for patterns. Default
99 is to use basic regexp.
100
101 +-P::
102 +--perl-regexp::
103 + Use Perl-compatible regexp for patterns. Requires libpcre to be
104 + compiled in.
105 +
106 -F::
107 --fixed-strings::
108 Use fixed strings for patterns (don't interpret pattern
Makefile
+15
@@ -24,6 +24,12 @@ all::
24 # Define NO_OPENSSL environment variable if you do not have OpenSSL.
25 # This also implies BLK_SHA1.
26 #
27 +# Define USE_LIBPCRE if you have and want to use libpcre. git-grep will be
28 +# able to use Perl-compatible regular expressions.
29 +#
30 +# Define LIBPCREDIR=/foo/bar if your libpcre header and library files are in
31 +# /foo/bar/include and /foo/bar/lib directories.
32 +#
33 # Define NO_CURL if you do not have libcurl installed. git-http-pull and
34 # git-http-push are not built, and you cannot use http:// and https://
35 # transports.
@@ -1248,6 +1254,15 @@ ifdef NO_LIBGEN_H
1254 COMPAT_OBJS += compat/basename.o
1255 endif
1256
1257 +ifdef USE_LIBPCRE
1258 + BASIC_CFLAGS += -DUSE_LIBPCRE
1259 + ifdef LIBPCREDIR
1260 + BASIC_CFLAGS += -I$(LIBPCREDIR)/include
1261 + EXTLIBS += -L$(LIBPCREDIR)/$(lib) $(CC_LD_DYNPATH)$(LIBPCREDIR)/$(lib)
1262 + endif
1263 + EXTLIBS += -lpcre
1264 +endif
1265 +
1266 ifdef NO_CURL
1267 BASIC_CFLAGS += -DNO_CURL
1268 REMOTE_CURL_PRIMARY =
builtin/grep.c
+2
@@ -781,6 +781,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
781 REG_EXTENDED),
782 OPT_BOOLEAN('F', "fixed-strings", &opt.fixed,
783 "interpret patterns as fixed strings"),
784 + OPT_BOOLEAN('P', "perl-regexp", &opt.pcre,
785 + "use Perl-compatible regular expressions"),
786 OPT_GROUP(""),
787 OPT_BOOLEAN('n', "line-number", &opt.linenum, "show line numbers"),
788 OPT_NEGBIT('h', NULL, &opt.pathname, "don't show filenames", 1),
contrib/completion/git-completion.bash
+1
@@ -1487,6 +1487,7 @@ _git_grep ()
1487 --text --ignore-case --word-regexp --invert-match
1488 --full-name --line-number
1489 --extended-regexp --basic-regexp --fixed-strings
1490 + --perl-regexp
1491 --files-with-matches --name-only
1492 --files-without-match
1493 --max-depth
grep.c
+74 -1
@@ -74,6 +74,69 @@ static NORETURN void compile_regexp_failed(const struct grep_pat *p,
74 die("%s'%s': %s", where, p->pattern, error);
75 }
76
77 +#ifdef USE_LIBPCRE
78 +static void compile_pcre_regexp(struct grep_pat *p, const struct grep_opt *opt)
79 +{
80 + const char *error;
81 + int erroffset;
82 + int options = 0;
83 +
84 + if (opt->ignore_case)
85 + options |= PCRE_CASELESS;
86 +
87 + p->pcre_regexp = pcre_compile(p->pattern, options, &error, &erroffset,
88 + NULL);
89 + if (!p->pcre_regexp)
90 + compile_regexp_failed(p, error);
91 +
92 + p->pcre_extra_info = pcre_study(p->pcre_regexp, 0, &error);
93 + if (!p->pcre_extra_info && error)
94 + die("%s", error);
95 +}
96 +
97 +static int pcrematch(struct grep_pat *p, const char *line, const char *eol,
98 + regmatch_t *match, int eflags)
99 +{
100 + int ovector[30], ret, flags = 0;
101 +
102 + if (eflags & REG_NOTBOL)
103 + flags |= PCRE_NOTBOL;
104 +
105 + ret = pcre_exec(p->pcre_regexp, p->pcre_extra_info, line, eol - line,
106 + 0, flags, ovector, ARRAY_SIZE(ovector));
107 + if (ret < 0 && ret != PCRE_ERROR_NOMATCH)
108 + die("pcre_exec failed with error code %d", ret);
109 + if (ret > 0) {
110 + ret = 0;
111 + match->rm_so = ovector[0];
112 + match->rm_eo = ovector[1];
113 + }
114 +
115 + return ret;
116 +}
117 +
118 +static void free_pcre_regexp(struct grep_pat *p)
119 +{
120 + pcre_free(p->pcre_regexp);
121 + pcre_free(p->pcre_extra_info);
122 +}
123 +#else /* !USE_LIBPCRE */
124 +static void compile_pcre_regexp(struct grep_pat *p, const struct grep_opt *opt)
125 +{
126 + die("cannot use Perl-compatible regexes when not compiled with USE_LIBPCRE");
127 +}
128 +
129 +static int pcrematch(struct grep_pat *p, const char *line, const char *eol,
130 + regmatch_t *match, int eflags)
131 +{
132 + return 1;
133 +}
134 +
135 +static void free_pcre_regexp(struct grep_pat *p)
136 +{
137 +}
138 +#endif /* !USE_LIBPCRE */
139 +
140 static void compile_regexp(struct grep_pat *p, struct grep_opt *opt)
141 {
142 int err;
@@ -85,6 +148,11 @@ static void compile_regexp(struct grep_pat *p, struct grep_opt *opt)
148 if (p->fixed)
149 return;
150
151 + if (opt->pcre) {
152 + compile_pcre_regexp(p, opt);
153 + return;
154 + }
155 +
156 err = regcomp(&p->regexp, p->pattern, opt->regflags);
157 if (err) {
158 char errbuf[1024];
@@ -327,7 +395,10 @@ void free_grep_patterns(struct grep_opt *opt)
395 case GREP_PATTERN: /* atom */
396 case GREP_PATTERN_HEAD:
397 case GREP_PATTERN_BODY:
330 - regfree(&p->regexp);
398 + if (p->pcre_regexp)
399 + free_pcre_regexp(p);
400 + else
401 + regfree(&p->regexp);
402 break;
403 default:
404 break;
@@ -426,6 +497,8 @@ static int patmatch(struct grep_pat *p, char *line, char *eol,
497
498 if (p->fixed)
499 hit = !fixmatch(p, line, eol, match);
500 + else if (p->pcre_regexp)
501 + hit = !pcrematch(p, line, eol, match, eflags);
502 else
503 hit = !regmatch(&p->regexp, line, eol, match, eflags);
504
grep.h
+9
@@ -1,6 +1,12 @@
1 #ifndef GREP_H
2 #define GREP_H
3 #include "color.h"
4 +#ifdef USE_LIBPCRE
5 +#include <pcre.h>
6 +#else
7 +typedef int pcre;
8 +typedef int pcre_extra;
9 +#endif
10
11 enum grep_pat_token {
12 GREP_PATTERN,
@@ -33,6 +39,8 @@ struct grep_pat {
39 size_t patternlen;
40 enum grep_header_field field;
41 regex_t regexp;
42 + pcre *pcre_regexp;
43 + pcre_extra *pcre_extra_info;
44 unsigned fixed:1;
45 unsigned ignore_case:1;
46 unsigned word_regexp:1;
@@ -83,6 +91,7 @@ struct grep_opt {
91 #define GREP_BINARY_TEXT 2
92 int binary;
93 int extended;
94 + int pcre;
95 int relative;
96 int pathname;
97 int null_following_name;