@@ -187,6 +187,78 @@ static int exec_grep(int argc, const char **argv)
187187 else die("maximum number of args exceeded"); \
188188 } while (0)
189189
190+ /*
191+ * If you send a singleton filename to grep, it does not give
192+ * the name of the file. GNU grep has "-H" but we would want
193+ * that behaviour in a portable way.
194+ *
195+ * So we keep two pathnames in argv buffer unsent to grep in
196+ * the main loop if we need to do more than one grep.
197+ */
198+ static int flush_grep (struct grep_opt * opt ,
199+ int argc , int arg0 , const char * * argv , int * kept )
200+ {
201+ int status ;
202+ int count = argc - arg0 ;
203+ const char * kept_0 = NULL ;
204+
205+ if (count <= 2 ) {
206+ /*
207+ * Because we keep at least 2 paths in the call from
208+ * the main loop (i.e. kept != NULL), and MAXARGS is
209+ * far greater than 2, this usually is a call to
210+ * conclude the grep. However, the user could attempt
211+ * to overflow the argv buffer by giving too many
212+ * options to leave very small number of real
213+ * arguments even for the call in the main loop.
214+ */
215+ if (kept )
216+ die ("insanely many options to grep" );
217+
218+ /*
219+ * If we have two or more paths, we do not have to do
220+ * anything special, but we need to push /dev/null to
221+ * get "-H" behaviour of GNU grep portably but when we
222+ * are not doing "-l" nor "-L" nor "-c".
223+ */
224+ if (count == 1 &&
225+ !opt -> name_only &&
226+ !opt -> unmatch_name_only &&
227+ !opt -> count ) {
228+ argv [argc ++ ] = "/dev/null" ;
229+ argv [argc ] = NULL ;
230+ }
231+ }
232+
233+ else if (kept ) {
234+ /*
235+ * Called because we found many paths and haven't finished
236+ * iterating over the cache yet. We keep two paths
237+ * for the concluding call. argv[argc-2] and argv[argc-1]
238+ * has the last two paths, so save the first one away,
239+ * replace it with NULL while sending the list to grep,
240+ * and recover them after we are done.
241+ */
242+ * kept = 2 ;
243+ kept_0 = argv [argc - 2 ];
244+ argv [argc - 2 ] = NULL ;
245+ argc -= 2 ;
246+ }
247+
248+ status = exec_grep (argc , argv );
249+
250+ if (kept_0 ) {
251+ /*
252+ * Then recover them. Now the last arg is beyond the
253+ * terminating NULL which is at argc, and the second
254+ * from the last is what we saved away in kept_0
255+ */
256+ argv [arg0 ++ ] = kept_0 ;
257+ argv [arg0 ] = argv [argc + 1 ];
258+ }
259+ return status ;
260+ }
261+
190262static int external_grep (struct grep_opt * opt , const char * * paths , int cached )
191263{
192264 int i , nr , argc , hit , len , status ;
@@ -253,22 +325,12 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
253325 push_arg (p -> pattern );
254326 }
255327
256- /*
257- * To make sure we get the header printed out when we want it,
258- * add /dev/null to the paths to grep. This is unnecessary
259- * (and wrong) with "-l" or "-L", which always print out the
260- * name anyway.
261- *
262- * GNU grep has "-H", but this is portable.
263- */
264- if (!opt -> name_only && !opt -> unmatch_name_only )
265- push_arg ("/dev/null" );
266-
267328 hit = 0 ;
268329 argc = nr ;
269330 for (i = 0 ; i < active_nr ; i ++ ) {
270331 struct cache_entry * ce = active_cache [i ];
271332 char * name ;
333+ int kept ;
272334 if (!S_ISREG (ntohl (ce -> ce_mode )))
273335 continue ;
274336 if (!pathspec_matches (paths , ce -> name ))
@@ -283,10 +345,10 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
283345 argv [argc ++ ] = name ;
284346 if (argc < MAXARGS && !ce_stage (ce ))
285347 continue ;
286- status = exec_grep ( argc , argv );
348+ status = flush_grep ( opt , argc , nr , argv , & kept );
287349 if (0 < status )
288350 hit = 1 ;
289- argc = nr ;
351+ argc = nr + kept ;
290352 if (ce_stage (ce )) {
291353 do {
292354 i ++ ;
@@ -296,7 +358,7 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
296358 }
297359 }
298360 if (argc > nr ) {
299- status = exec_grep ( argc , argv );
361+ status = flush_grep ( opt , argc , nr , argv , NULL );
300362 if (0 < status )
301363 hit = 1 ;
302364 }
0 commit comments