#include "builtin.h"
#include <regex.h>
#include <fnmatch.h>
+#include <sys/wait.h>
/*
* git grep pathspecs are somewhat different from diff-tree pathspecs;
int matchlen = strlen(match);
const char *cp, *meta;
- if ((matchlen <= namelen) &&
- !strncmp(name, match, matchlen) &&
- (match[matchlen-1] == '/' ||
- name[matchlen] == '\0' || name[matchlen] == '/'))
+ if (!matchlen ||
+ ((matchlen <= namelen) &&
+ !strncmp(name, match, matchlen) &&
+ (match[matchlen-1] == '/' ||
+ name[matchlen] == '\0' || name[matchlen] == '/')))
return 1;
if (!fnmatch(match, name, 0))
return 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
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)
{
*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
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;
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->regflags & REG_ICASE)
+ push_arg("-i");
+ 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)))
struct tree_desc *tree,
const char *tree_name, const char *base)
{
- unsigned mode;
int len;
int hit = 0;
- const char *path;
- const unsigned char *sha1;
+ struct name_entry entry;
char *down;
char *path_buf = xmalloc(PATH_MAX + strlen(tree_name) + 100);
}
len = strlen(path_buf);
- while (tree->size) {
- int pathlen;
- sha1 = tree_entry_extract(tree, &path, &mode);
- pathlen = strlen(path);
- strcpy(path_buf + len, path);
+ while (tree_entry(tree, &entry)) {
+ strcpy(path_buf + len, entry.path);
- if (S_ISDIR(mode))
+ if (S_ISDIR(entry.mode))
/* Match "abc/" against pathspec to
* decide if we want to descend into "abc"
* directory.
*/
- strcpy(path_buf + len + pathlen, "/");
+ strcpy(path_buf + len + entry.pathlen, "/");
if (!pathspec_matches(paths, down))
;
- else if (S_ISREG(mode))
- hit |= grep_sha1(opt, sha1, path_buf);
- else if (S_ISDIR(mode)) {
+ else if (S_ISREG(entry.mode))
+ hit |= grep_sha1(opt, entry.sha1, path_buf);
+ else if (S_ISDIR(entry.mode)) {
char type[20];
struct tree_desc sub;
void *data;
- data = read_sha1_file(sha1, type, &sub.size);
+ data = read_sha1_file(entry.sha1, type, &sub.size);
if (!data)
die("unable to read tree (%s)",
- sha1_to_hex(sha1));
+ sha1_to_hex(entry.sha1));
sub.buf = data;
hit |= grep_tree(opt, paths, &sub, tree_name, down);
free(data);
}
- update_tree_entry(tree);
}
return hit;
}
static int grep_object(struct grep_opt *opt, const char **paths,
struct object *obj, const char *name)
{
- if (!strcmp(obj->type, blob_type))
+ if (obj->type == TYPE_BLOB)
return grep_sha1(opt, obj->sha1, name);
- if (!strcmp(obj->type, commit_type) ||
- !strcmp(obj->type, tree_type)) {
+ if (obj->type == TYPE_COMMIT || obj->type == TYPE_TREE) {
struct tree_desc tree;
void *data;
int hit;
free(data);
return hit;
}
- die("unable to grep from object of type %s", obj->type);
+ die("unable to grep from object of type %s", typename(obj->type));
}
static const char builtin_grep_usage[] =
int cached = 0;
int seen_dashdash = 0;
struct grep_opt opt;
- struct object_list *list, **tail, *object_list = NULL;
+ struct object_array list = { 0, 0, NULL };
const char *prefix = setup_git_directory();
const char **paths = NULL;
int i;
* that continues up to the -- (if exists), and then paths.
*/
- tail = &object_list;
while (1 < argc) {
const char *arg = argv[1];
argc--; argv++;
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 (!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++) {
/* Is it a rev? */
if (!get_sha1(arg, sha1)) {
struct object *object = parse_object(sha1);
- struct object_list *elem;
if (!object)
die("bad object %s", arg);
- elem = object_list_insert(object, tail);
- elem->name = arg;
- tail = &elem->next;
+ add_object_array(object, arg, &list);
continue;
}
if (!strcmp(arg, "--")) {
paths[1] = NULL;
}
- if (!object_list)
+ if (!list.nr)
return !grep_cache(&opt, paths, cached);
if (cached)
die("both --cached and trees are given.");
- for (list = object_list; list; list = list->next) {
+ for (i = 0; i < list.nr; i++) {
struct object *real_obj;
- real_obj = deref_tag(list->item, NULL, 0);
- if (grep_object(&opt, paths, real_obj, list->name))
+ real_obj = deref_tag(list.objects[i].item, NULL, 0);
+ if (grep_object(&opt, paths, real_obj, list.objects[i].name))
hit = 1;
}
return !hit;