Code

Merge branch 'jc/grep' into next
[git.git] / builtin-grep.c
index 14471db7cb0de059c4e50648c100a20f6da892dd..36512d8a17abccb67654b5bcfcd74a441b3e4586 100644 (file)
@@ -437,24 +437,78 @@ static int exec_grep(int argc, const char **argv)
 }
 
 #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;
+       int i, nr, argc, hit, len;
        const char *argv[MAXARGS+1];
+       char randarg[ARGBUF];
+       char *argptr = randarg;
        struct grep_pat *p;
 
-       nr = 0;
-       argv[nr++] = "grep";
+       len = nr = 0;
+       push_arg("grep");
+       if (opt->fixed)
+               push_arg("-F");
+       if (opt->linenum)
+               push_arg("-n");
+       if (opt->regflags & REG_EXTENDED)
+               push_arg("-E");
        if (opt->word_regexp)
-               argv[nr++] = "-w";
+               push_arg("-w");
        if (opt->name_only)
-               argv[nr++] = "-l";
+               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) {
-               argv[nr++] = "-e";
-               argv[nr++] = p->pattern;
+               push_arg("-e");
+               push_arg(p->pattern);
+       }
+
+       if (NO_H_OPTION_IN_GREP)
+               push_arg("/dev/null");
+       else {
+               push_arg("-H");
+               push_arg("--");
        }
-       argv[nr++] = "--";
 
        hit = 0;
        argc = nr;
@@ -486,8 +540,19 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
         * Use the external "grep" command for the case where
         * we grep through the checked-out files. It tends to
         * be a lot more optimized
+        *
+        * Some grep implementations do not understand -H nor --
+        * but /dev/null can be used as a substitution in most
+        * cases.
+        *
+        * However -L and -c would slightly misbehave (-L would
+        * list /dev/null as a hit, and -c would report 0 hits
+        * from /dev/null); so do not use the external one on
+        * such platforms.
         */
-       if (!cached) {
+       if (!cached &&
+           (!NO_H_OPTION_IN_GREP ||
+            (!opt->count && !opt->unmatch_name_only))) {
                hit = external_grep(opt, paths, cached);
                if (hit >= 0)
                        return hit;