X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=builtin-grep.c;h=bbf747fc7b66f6b4f19d9dc62a9ba10965199917;hb=e5d3de5c457d1a82c921b9bf2cd9a962970ce30e;hp=96b70227cf073517c309dbc8b042085f1040dab5;hpb=784b11cd05e7862b0fbcc1251a65669c7004a8df;p=git.git diff --git a/builtin-grep.c b/builtin-grep.c index 96b70227c..bbf747fc7 100644 --- a/builtin-grep.c +++ b/builtin-grep.c @@ -122,6 +122,8 @@ static int grep_file(struct grep_opt *opt, const char *filename) struct stat st; int i; char *data; + size_t sz; + if (lstat(filename, &st) < 0) { err_ret: if (errno != ENOENT) @@ -132,11 +134,12 @@ static int grep_file(struct grep_opt *opt, const char *filename) return 0; /* empty file -- no grep hit */ if (!S_ISREG(st.st_mode)) return 0; + sz = xsize_t(st.st_size); i = open(filename, O_RDONLY); if (i < 0) goto err_ret; - data = xmalloc(st.st_size + 1); - if (st.st_size != read_in_full(i, data, st.st_size)) { + data = xmalloc(sz + 1); + if (st.st_size != read_in_full(i, data, sz)) { error("'%s': short read %s", filename, strerror(errno)); close(i); free(data); @@ -145,11 +148,12 @@ static int grep_file(struct grep_opt *opt, const char *filename) close(i); if (opt->relative && opt->prefix_length) filename += opt->prefix_length; - i = grep_buffer(opt, filename, data, st.st_size); + i = grep_buffer(opt, filename, data, sz); free(data); return i; } +#ifdef __unix__ static int exec_grep(int argc, const char **argv) { pid_t pid; @@ -183,6 +187,78 @@ static int exec_grep(int argc, const char **argv) else die("maximum number of args exceeded"); \ } while (0) +/* + * If you send a singleton filename to grep, it does not give + * the name of the file. GNU grep has "-H" but we would want + * that behaviour in a portable way. + * + * So we keep two pathnames in argv buffer unsent to grep in + * the main loop if we need to do more than one grep. + */ +static int flush_grep(struct grep_opt *opt, + int argc, int arg0, const char **argv, int *kept) +{ + int status; + int count = argc - arg0; + const char *kept_0 = NULL; + + if (count <= 2) { + /* + * Because we keep at least 2 paths in the call from + * the main loop (i.e. kept != NULL), and MAXARGS is + * far greater than 2, this usually is a call to + * conclude the grep. However, the user could attempt + * to overflow the argv buffer by giving too many + * options to leave very small number of real + * arguments even for the call in the main loop. + */ + if (kept) + die("insanely many options to grep"); + + /* + * If we have two or more paths, we do not have to do + * anything special, but we need to push /dev/null to + * get "-H" behaviour of GNU grep portably but when we + * are not doing "-l" nor "-L" nor "-c". + */ + if (count == 1 && + !opt->name_only && + !opt->unmatch_name_only && + !opt->count) { + argv[argc++] = "/dev/null"; + argv[argc] = NULL; + } + } + + else if (kept) { + /* + * Called because we found many paths and haven't finished + * iterating over the cache yet. We keep two paths + * for the concluding call. argv[argc-2] and argv[argc-1] + * has the last two paths, so save the first one away, + * replace it with NULL while sending the list to grep, + * and recover them after we are done. + */ + *kept = 2; + kept_0 = argv[argc-2]; + argv[argc-2] = NULL; + argc -= 2; + } + + status = exec_grep(argc, argv); + + if (kept_0) { + /* + * Then recover them. Now the last arg is beyond the + * terminating NULL which is at argc, and the second + * from the last is what we saved away in kept_0 + */ + argv[arg0++] = kept_0; + argv[arg0] = argv[argc+1]; + } + return status; +} + static int external_grep(struct grep_opt *opt, const char **paths, int cached) { int i, nr, argc, hit, len, status; @@ -218,7 +294,7 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached) if (opt->pre_context) { push_arg("-B"); len += snprintf(argptr, sizeof(randarg)-len, - "%u", opt->pre_context); + "%u", opt->pre_context) + 1; if (sizeof(randarg) <= len) die("maximum length of args exceeded"); push_arg(argptr); @@ -227,7 +303,7 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached) if (opt->post_context) { push_arg("-A"); len += snprintf(argptr, sizeof(randarg)-len, - "%u", opt->post_context); + "%u", opt->post_context) + 1; if (sizeof(randarg) <= len) die("maximum length of args exceeded"); push_arg(argptr); @@ -237,7 +313,7 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached) else { push_arg("-C"); len += snprintf(argptr, sizeof(randarg)-len, - "%u", opt->post_context); + "%u", opt->post_context) + 1; if (sizeof(randarg) <= len) die("maximum length of args exceeded"); push_arg(argptr); @@ -249,22 +325,12 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached) 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; + int kept; if (!S_ISREG(ntohl(ce->ce_mode))) continue; if (!pathspec_matches(paths, ce->name)) @@ -277,12 +343,12 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached) memcpy(name + 2, ce->name, len + 1); } argv[argc++] = name; - if (argc < MAXARGS && !ce_stage(ce)) + if (argc < MAXARGS) continue; - status = exec_grep(argc, argv); + status = flush_grep(opt, argc, nr, argv, &kept); if (0 < status) hit = 1; - argc = nr; + argc = nr + kept; if (ce_stage(ce)) { do { i++; @@ -292,12 +358,13 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached) } } if (argc > nr) { - status = exec_grep(argc, argv); + status = flush_grep(opt, argc, nr, argv, NULL); if (0 < status) hit = 1; } return hit; } +#endif static int grep_cache(struct grep_opt *opt, const char **paths, int cached) { @@ -373,7 +440,7 @@ static int grep_tree(struct grep_opt *opt, const char **paths, * decide if we want to descend into "abc" * directory. */ - strcpy(path_buf + len + entry.pathlen, "/"); + strcpy(path_buf + len + tree_entry_len(entry.path, entry.sha1), "/"); if (!pathspec_matches(paths, down)) ; @@ -383,11 +450,13 @@ static int grep_tree(struct grep_opt *opt, const char **paths, enum object_type type; struct tree_desc sub; void *data; - data = read_sha1_file(entry.sha1, &type, &sub.size); + unsigned long size; + + data = read_sha1_file(entry.sha1, &type, &size); if (!data) die("unable to read tree (%s)", sha1_to_hex(entry.sha1)); - sub.buf = data; + init_tree_desc(&sub, data, size); hit |= grep_tree(opt, paths, &sub, tree_name, down); free(data); } @@ -403,12 +472,13 @@ static int grep_object(struct grep_opt *opt, const char **paths, if (obj->type == OBJ_COMMIT || obj->type == OBJ_TREE) { struct tree_desc tree; void *data; + unsigned long size; int hit; data = read_object_with_reference(obj->sha1, tree_type, - &tree.size, NULL); + &size, NULL); if (!data) die("unable to read tree (%s)", sha1_to_hex(obj->sha1)); - tree.buf = data; + init_tree_desc(&tree, data, size); hit = grep_tree(opt, paths, &tree, name, ""); free(data); return hit; @@ -548,7 +618,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) scan = arg + 1; break; } - if (sscanf(scan, "%u", &num) != 1) + if (strtoul_ui(scan, 10, &num)) die(emsg_invalid_context_len, scan); switch (arg[1]) { case 'A':