Skip to content

Commit 63e7e9d

Browse files
mkiedrowiczgitster
authored andcommitted
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent a30c148 commit 63e7e9d

File tree

6 files changed

+107
-1
lines changed

6 files changed

+107
-1
lines changed

Documentation/git-grep.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ SYNOPSIS
1212
'git grep' [-a | --text] [-I] [-i | --ignore-case] [-w | --word-regexp]
1313
[-v | --invert-match] [-h|-H] [--full-name]
1414
[-E | --extended-regexp] [-G | --basic-regexp]
15+
[-P | --perl-regexp]
1516
[-F | --fixed-strings] [-n | --line-number]
1617
[-l | --files-with-matches] [-L | --files-without-match]
1718
[(-O | --open-files-in-pager) [<pager>]]
@@ -97,6 +98,11 @@ OPTIONS
9798
Use POSIX extended/basic regexp for patterns. Default
9899
is to use basic regexp.
99100

101+
-P::
102+
--perl-regexp::
103+
Use Perl-compatible regexp for patterns. Requires libpcre to be
104+
compiled in.
105+
100106
-F::
101107
--fixed-strings::
102108
Use fixed strings for patterns (don't interpret pattern

Makefile

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ all::
2424
# Define NO_OPENSSL environment variable if you do not have OpenSSL.
2525
# This also implies BLK_SHA1.
2626
#
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+
#
2733
# Define NO_CURL if you do not have libcurl installed. git-http-pull and
2834
# git-http-push are not built, and you cannot use http:// and https://
2935
# transports.
@@ -1248,6 +1254,15 @@ ifdef NO_LIBGEN_H
12481254
COMPAT_OBJS += compat/basename.o
12491255
endif
12501256

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+
12511266
ifdef NO_CURL
12521267
BASIC_CFLAGS += -DNO_CURL
12531268
REMOTE_CURL_PRIMARY =

builtin/grep.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -781,6 +781,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
781781
REG_EXTENDED),
782782
OPT_BOOLEAN('F', "fixed-strings", &opt.fixed,
783783
"interpret patterns as fixed strings"),
784+
OPT_BOOLEAN('P', "perl-regexp", &opt.pcre,
785+
"use Perl-compatible regular expressions"),
784786
OPT_GROUP(""),
785787
OPT_BOOLEAN('n', "line-number", &opt.linenum, "show line numbers"),
786788
OPT_NEGBIT('h', NULL, &opt.pathname, "don't show filenames", 1),

contrib/completion/git-completion.bash

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1487,6 +1487,7 @@ _git_grep ()
14871487
--text --ignore-case --word-regexp --invert-match
14881488
--full-name --line-number
14891489
--extended-regexp --basic-regexp --fixed-strings
1490+
--perl-regexp
14901491
--files-with-matches --name-only
14911492
--files-without-match
14921493
--max-depth

grep.c

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,69 @@ static NORETURN void compile_regexp_failed(const struct grep_pat *p,
7474
die("%s'%s': %s", where, p->pattern, error);
7575
}
7676

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+
77140
static void compile_regexp(struct grep_pat *p, struct grep_opt *opt)
78141
{
79142
int err;
@@ -85,6 +148,11 @@ static void compile_regexp(struct grep_pat *p, struct grep_opt *opt)
85148
if (p->fixed)
86149
return;
87150

151+
if (opt->pcre) {
152+
compile_pcre_regexp(p, opt);
153+
return;
154+
}
155+
88156
err = regcomp(&p->regexp, p->pattern, opt->regflags);
89157
if (err) {
90158
char errbuf[1024];
@@ -327,7 +395,10 @@ void free_grep_patterns(struct grep_opt *opt)
327395
case GREP_PATTERN: /* atom */
328396
case GREP_PATTERN_HEAD:
329397
case GREP_PATTERN_BODY:
330-
regfree(&p->regexp);
398+
if (p->pcre_regexp)
399+
free_pcre_regexp(p);
400+
else
401+
regfree(&p->regexp);
331402
break;
332403
default:
333404
break;
@@ -426,6 +497,8 @@ static int patmatch(struct grep_pat *p, char *line, char *eol,
426497

427498
if (p->fixed)
428499
hit = !fixmatch(p, line, eol, match);
500+
else if (p->pcre_regexp)
501+
hit = !pcrematch(p, line, eol, match, eflags);
429502
else
430503
hit = !regmatch(&p->regexp, line, eol, match, eflags);
431504

grep.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
#ifndef GREP_H
22
#define GREP_H
33
#include "color.h"
4+
#ifdef USE_LIBPCRE
5+
#include <pcre.h>
6+
#else
7+
typedef int pcre;
8+
typedef int pcre_extra;
9+
#endif
410

511
enum grep_pat_token {
612
GREP_PATTERN,
@@ -33,6 +39,8 @@ struct grep_pat {
3339
size_t patternlen;
3440
enum grep_header_field field;
3541
regex_t regexp;
42+
pcre *pcre_regexp;
43+
pcre_extra *pcre_extra_info;
3644
unsigned fixed:1;
3745
unsigned ignore_case:1;
3846
unsigned word_regexp:1;
@@ -83,6 +91,7 @@ struct grep_opt {
8391
#define GREP_BINARY_TEXT 2
8492
int binary;
8593
int extended;
94+
int pcre;
8695
int relative;
8796
int pathname;
8897
int null_following_name;

0 commit comments

Comments
 (0)