Merge branch 'jc/grep-c' into maint
* jc/grep-c: Split grep arguments in a way that does not requires to add /dev/null.
This commit is contained in:
commit
a017f27dcb
@ -187,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;
|
||||
@ -253,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))
|
||||
@ -283,10 +345,10 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
|
||||
argv[argc++] = name;
|
||||
if (argc < MAXARGS && !ce_stage(ce))
|
||||
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++;
|
||||
@ -296,7 +358,7 @@ 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;
|
||||
}
|
||||
|
@ -107,6 +107,10 @@ do
|
||||
diff expected actual
|
||||
'
|
||||
|
||||
test_expect_failure "grep -c $L (no /dev/null)" '
|
||||
git grep -c test $H | grep -q "/dev/null"
|
||||
'
|
||||
|
||||
done
|
||||
|
||||
test_done
|
||||
|
Loading…
Reference in New Issue
Block a user