Documentation/Makefile: create tarballs for the man pages and html files
[gitweb.git] / builtin-grep.c
index 26a3fc387ff0def55ce29494840b4e3437f6bbc2..66111de5148c17a156fdbfbc0d92b0c93c2b2c34 100644 (file)
@@ -12,6 +12,7 @@
 #include "builtin.h"
 #include <regex.h>
 #include <fnmatch.h>
+#include <sys/wait.h>
 
 /*
  * git grep pathspecs are somewhat different from diff-tree pathspecs;
@@ -98,6 +99,7 @@ struct grep_opt {
        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
@@ -179,6 +181,20 @@ static int buffer_is_binary(const char *ptr, unsigned long size)
        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)
 {
@@ -224,9 +240,14 @@ static int grep_buffer(struct grep_opt *opt, const char *name,
                *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
@@ -241,10 +262,10 @@ static int grep_buffer(struct grep_opt *opt, const char *name,
                                        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]))
-                                       continue; /* not a word boundary */
+                                       hit = 0;
                        }
                        if (hit)
                                break;
@@ -389,12 +410,139 @@ static int grep_file(struct grep_opt *opt, const char *filename)
        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
+#define ARGBUF 4096
+#define push_arg(a) do { \
+       if (nr < MAXARGS) argv[nr++] = (a); \
+       else die("maximum number of args exceeded"); \
+       } while (0)
+
+static int external_grep(struct grep_opt *opt, const char **paths, int cached)
+{
+       int i, nr, argc, hit, len;
+       const char *argv[MAXARGS+1];
+       char randarg[ARGBUF];
+       char *argptr = randarg;
+       struct grep_pat *p;
+
+       len = nr = 0;
+       push_arg("grep");
+       push_arg("-H");
+       if (opt->fixed)
+               push_arg("-F");
+       if (opt->linenum)
+               push_arg("-n");
+       if (opt->regflags & REG_EXTENDED)
+               push_arg("-E");
+       if (opt->word_regexp)
+               push_arg("-w");
+       if (opt->name_only)
+               push_arg("-l");
+       if (opt->unmatch_name_only)
+               push_arg("-L");
+       if (opt->count)
+               push_arg("-c");
+       if (opt->post_context || opt->pre_context) {
+               if (opt->post_context != opt->pre_context) {
+                       if (opt->pre_context) {
+                               push_arg("-B");
+                               len += snprintf(argptr, sizeof(randarg)-len,
+                                               "%u", opt->pre_context);
+                               if (sizeof(randarg) <= len)
+                                       die("maximum length of args exceeded");
+                               push_arg(argptr);
+                               argptr += len;
+                       }
+                       if (opt->post_context) {
+                               push_arg("-A");
+                               len += snprintf(argptr, sizeof(randarg)-len,
+                                               "%u", opt->post_context);
+                               if (sizeof(randarg) <= len)
+                                       die("maximum length of args exceeded");
+                               push_arg(argptr);
+                               argptr += len;
+                       }
+               }
+               else {
+                       push_arg("-C");
+                       len += snprintf(argptr, sizeof(randarg)-len,
+                                       "%u", opt->post_context);
+                       if (sizeof(randarg) <= len)
+                               die("maximum length of args exceeded");
+                       push_arg(argptr);
+                       argptr += len;
+               }
+       }
+       for (p = opt->pattern_list; p; p = p->next) {
+               push_arg("-e");
+               push_arg(p->pattern);
+       }
+       push_arg("--");
+
+       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();
 
+#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)))
@@ -549,6 +697,11 @@ int cmd_grep(int argc, const char **argv, char **envp)
                        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;
@@ -674,7 +827,10 @@ int cmd_grep(int argc, const char **argv, char **envp)
 
        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++) {
@@ -701,7 +857,7 @@ int cmd_grep(int argc, const char **argv, char **envp)
        /* 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]);
        }