X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=builtin-grep.c;h=53de8a883607157c88d02a30f0320aa9e5c0581a;hb=47df096f254ccac5a81da615b3fd25f3cf466bbf;hp=fead35662944070f8502ed0484fe6b6085d7ac86;hpb=07ea91d84f3c7bb075d4716ee40096e3b12a4c86;p=git.git diff --git a/builtin-grep.c b/builtin-grep.c index fead3566..53de8a88 100644 --- a/builtin-grep.c +++ b/builtin-grep.c @@ -12,6 +12,7 @@ #include "builtin.h" #include #include +#include /* * git grep pathspecs are somewhat different from diff-tree pathspecs; @@ -409,12 +410,156 @@ 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"); + 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); + } + + /* + * To make sure we get the header printed out when we want it, + * add /dev/null to the paths to grep. This is unnecessary + * (and wrong) with "-l" or "-L", which always print out the + * name anyway. + * + * GNU grep has "-H", but this is portable. + */ + if (!opt->name_only && !opt->unmatch_name_only) + push_arg("/dev/null"); + + hit = 0; + argc = nr; + for (i = 0; i < active_nr; i++) { + struct cache_entry *ce = active_cache[i]; + char *name; + if (ce_stage(ce) || !S_ISREG(ntohl(ce->ce_mode))) + continue; + if (!pathspec_matches(paths, ce->name)) + continue; + name = ce->name; + if (name[0] == '-') { + int len = ce_namelen(ce); + name = xmalloc(len + 3); + memcpy(name, "./", 2); + memcpy(name + 2, ce->name, len + 1); + } + argv[argc++] = 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)))