[PATCH] Split grep arguments in a way that does not requires to add /dev/null.

!MAILaRCHIVE_VOTE_RePLACE
Previous message: [thread] [date] [author]
Next message: [thread] [date] [author]
To: <git@...>
Cc: Pierre Habouzit <madcoder@...>
Date: Tuesday, September 11, 2007 - 7:36 pm

As adding /dev/null may create spurious output, and that grep applied to a
singleton does not works the same way than when it's applied to more than
one file, split the arguments in a way that the last batch has never a
singleton. Though, when there is only one file in the repository, force
adding /dev/null as we cannot do anything about it.

  Also, as we "spare" one argument from the batch before the last
(supposedly a full batch) we must be sure this batch with one element less
has at least 2 elements. Meaning that batches must have at least 3 available
slots for the algorithm to work. As it's a very unreasonable case (user
passing more than MAXARGS - 3 option flags !!!) die loudly.

Signed-off-by: Pierre Habouzit <madcoder@debian.org>
---
 builtin-grep.c  |   25 ++++++++++++++++++++++---
 t/t7002-grep.sh |    4 ++++
 2 files changed, 26 insertions(+), 3 deletions(-)

diff --git a/builtin-grep.c b/builtin-grep.c
index e13cb31..8f25020 100644
--- a/builtin-grep.c
+++ b/builtin-grep.c
@@ -189,7 +189,7 @@ static int exec_grep(int argc, const char **argv)
 
 static int external_grep(struct grep_opt *opt, const char **paths, int cached)
 {
-	int i, nr, argc, hit, len, status;
+	int i, nr, nbargs, argc, hit, len, status;
 	const char *argv[MAXARGS+1];
 	char randarg[ARGBUF];
 	char *argptr = randarg;
@@ -253,6 +253,15 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
 		push_arg(p->pattern);
 	}
 
+	for (nbargs = i = 0; i < active_nr; i++) {
+		struct cache_entry *ce = active_cache[i];
+		if (!S_ISREG(ntohl(ce->ce_mode)))
+			continue;
+		if (!pathspec_matches(paths, ce->name))
+			continue;
+		nbargs++;
+	}
+
 	/*
 	 * To make sure we get the header printed out when we want it,
 	 * add /dev/null to the paths to grep.  This is unnecessary
@@ -261,8 +270,16 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
 	 *
 	 * GNU grep has "-H", but this is portable.
 	 */
-	if (!opt->name_only && !opt->unmatch_name_only)
+	if (!opt->name_only && !opt->unmatch_name_only && nbargs == 1) {
 		push_arg("/dev/null");
+	}
+
+	/* for the algorithm that spares one file name if possible, we must have
+	 * at least 3 file name slots, so that sparing one, doesn't make a new
+	 * singleton, so rather than incorect output, die loudly.
+	 */
+	if (nr >= MAXARGS - 2)
+		die("You have an insanely long number of options");
 
 	hit = 0;
 	argc = nr;
@@ -281,12 +298,14 @@ 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 `nbargs + nr' overflows MAXARGS of one, spare one split now */
+		if (argc < MAXARGS - (nbargs + nr == MAXARGS + 1) && !ce_stage(ce))
 			continue;
 		status = exec_grep(argc, argv);
 		if (0 < status)
 			hit = 1;
 		argc = nr;
+		nbargs -= argc;
 		if (ce_stage(ce)) {
 			do {
 				i++;
diff --git a/t/t7002-grep.sh b/t/t7002-grep.sh
index 6bfb899..68b2b92 100755
--- a/t/t7002-grep.sh
+++ b/t/t7002-grep.sh
@@ -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
-- 
1.5.3.1

-
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Previous message: [thread] [date] [author]
Next message: [thread] [date] [author]

Messages in current thread:
[BUG] Funny output from git grep -c, Martin Langhoff, (Tue Sep 11, 6:14 pm)
[PATCH] Split grep arguments in a way that does not requires..., Pierre Habouzit, (Tue Sep 11, 7:36 pm)