diff --git a/builtin-grep.c b/builtin-grep.c
index 26a3fc387ff0def55ce29494840b4e3437f6bbc2..14471db7cb0de059c4e50648c100a20f6da892dd 100644 (file)
--- a/builtin-grep.c
+++ b/builtin-grep.c
#include "builtin.h"
#include <regex.h>
#include <fnmatch.h>
#include "builtin.h"
#include <regex.h>
#include <fnmatch.h>
+#include <sys/wait.h>
/*
* git grep pathspecs are somewhat different from diff-tree pathspecs;
/*
* git grep pathspecs are somewhat different from diff-tree pathspecs;
unsigned unmatch_name_only:1;
unsigned count:1;
unsigned word_regexp:1;
unsigned unmatch_name_only:1;
unsigned count:1;
unsigned word_regexp:1;
+ unsigned fixed:1;
#define GREP_BINARY_DEFAULT 0
#define GREP_BINARY_NOMATCH 1
#define GREP_BINARY_TEXT 2
#define GREP_BINARY_DEFAULT 0
#define GREP_BINARY_NOMATCH 1
#define GREP_BINARY_TEXT 2
return 0;
}
return 0;
}
+static int fixmatch(const char *pattern, char *line, regmatch_t *match)
+{
+ char *hit = strstr(line, pattern);
+ if (!hit) {
+ match->rm_so = match->rm_eo = -1;
+ return REG_NOMATCH;
+ }
+ else {
+ match->rm_so = hit - line;
+ match->rm_eo = match->rm_so + strlen(pattern);
+ return 0;
+ }
+}
+
static int grep_buffer(struct grep_opt *opt, const char *name,
char *buf, unsigned long size)
{
static int grep_buffer(struct grep_opt *opt, const char *name,
char *buf, unsigned long size)
{
*eol = 0;
for (p = opt->pattern_list; p; p = p->next) {
*eol = 0;
for (p = opt->pattern_list; p; p = p->next) {
- regex_t *exp = &p->regexp;
- hit = !regexec(exp, bol, ARRAY_SIZE(pmatch),
- pmatch, 0);
+ if (!opt->fixed) {
+ regex_t *exp = &p->regexp;
+ hit = !regexec(exp, bol, ARRAY_SIZE(pmatch),
+ pmatch, 0);
+ }
+ else {
+ hit = !fixmatch(p->pattern, bol, pmatch);
+ }
if (hit && opt->word_regexp) {
/* Match beginning must be either
if (hit && opt->word_regexp) {
/* Match beginning must be either
die("regexp returned nonsense");
if (pmatch[0].rm_so != 0 &&
word_char(bol[pmatch[0].rm_so-1]))
die("regexp returned nonsense");
if (pmatch[0].rm_so != 0 &&
word_char(bol[pmatch[0].rm_so-1]))
- continue; /* not a word boundary */
- if ((eol-bol) < pmatch[0].rm_eo &&
+ hit = 0;
+ if (pmatch[0].rm_eo != (eol-bol) &&
word_char(bol[pmatch[0].rm_eo]))
word_char(bol[pmatch[0].rm_eo]))
- continue; /* not a word boundary */
+ hit = 0;
}
if (hit)
break;
}
if (hit)
break;
return i;
}
return i;
}
+static int exec_grep(int argc, const char **argv)
+{
+ pid_t pid;
+ int status;
+
+ argv[argc] = NULL;
+ pid = fork();
+ if (pid < 0)
+ return pid;
+ if (!pid) {
+ execvp("grep", (char **) argv);
+ exit(255);
+ }
+ while (waitpid(pid, &status, 0) < 0) {
+ if (errno == EINTR)
+ continue;
+ return -1;
+ }
+ if (WIFEXITED(status)) {
+ if (!WEXITSTATUS(status))
+ return 1;
+ return 0;
+ }
+ return -1;
+}
+
+#define MAXARGS 1000
+
+static int external_grep(struct grep_opt *opt, const char **paths, int cached)
+{
+ int i, nr, argc, hit;
+ const char *argv[MAXARGS+1];
+ struct grep_pat *p;
+
+ nr = 0;
+ argv[nr++] = "grep";
+ if (opt->word_regexp)
+ argv[nr++] = "-w";
+ if (opt->name_only)
+ argv[nr++] = "-l";
+ for (p = opt->pattern_list; p; p = p->next) {
+ argv[nr++] = "-e";
+ argv[nr++] = p->pattern;
+ }
+ argv[nr++] = "--";
+
+ hit = 0;
+ argc = nr;
+ for (i = 0; i < active_nr; i++) {
+ struct cache_entry *ce = active_cache[i];
+ if (ce_stage(ce) || !S_ISREG(ntohl(ce->ce_mode)))
+ continue;
+ if (!pathspec_matches(paths, ce->name))
+ continue;
+ argv[argc++] = ce->name;
+ if (argc < MAXARGS)
+ continue;
+ hit += exec_grep(argc, argv);
+ argc = nr;
+ }
+ if (argc > nr)
+ hit += exec_grep(argc, argv);
+ return 0;
+}
+
static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
{
int hit = 0;
int nr;
read_cache();
static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
{
int hit = 0;
int nr;
read_cache();
+#ifdef __unix__
+ /*
+ * Use the external "grep" command for the case where
+ * we grep through the checked-out files. It tends to
+ * be a lot more optimized
+ */
+ if (!cached) {
+ hit = external_grep(opt, paths, cached);
+ if (hit >= 0)
+ return hit;
+ }
+#endif
+
for (nr = 0; nr < active_nr; nr++) {
struct cache_entry *ce = active_cache[nr];
if (ce_stage(ce) || !S_ISREG(ntohl(ce->ce_mode)))
for (nr = 0; nr < active_nr; nr++) {
struct cache_entry *ce = active_cache[nr];
if (ce_stage(ce) || !S_ISREG(ntohl(ce->ce_mode)))
opt.regflags |= REG_EXTENDED;
continue;
}
opt.regflags |= REG_EXTENDED;
continue;
}
+ if (!strcmp("-F", arg) ||
+ !strcmp("--fixed-strings", arg)) {
+ opt.fixed = 1;
+ continue;
+ }
if (!strcmp("-G", arg) ||
!strcmp("--basic-regexp", arg)) {
opt.regflags &= ~REG_EXTENDED;
if (!strcmp("-G", arg) ||
!strcmp("--basic-regexp", arg)) {
opt.regflags &= ~REG_EXTENDED;
if (!opt.pattern_list)
die("no pattern given.");
if (!opt.pattern_list)
die("no pattern given.");
- compile_patterns(&opt);
+ if ((opt.regflags != REG_NEWLINE) && opt.fixed)
+ die("cannot mix --fixed-strings and regexp");
+ if (!opt.fixed)
+ compile_patterns(&opt);
/* Check revs and then paths */
for (i = 1; i < argc; i++) {
/* Check revs and then paths */
for (i = 1; i < argc; i++) {
/* The rest are paths */
if (!seen_dashdash) {
int j;
/* The rest are paths */
if (!seen_dashdash) {
int j;
- for (j = i; j < argc; i++)
+ for (j = i; j < argc; j++)
verify_filename(prefix, argv[j]);
}
verify_filename(prefix, argv[j]);
}