1/*	$NetBSD: grep.c,v 1.6 2011/04/18 03:48:23 joerg Exp $	*/
2/* 	$FreeBSD$	*/
3/*	$OpenBSD: grep.c,v 1.42 2010/07/02 22:18:03 tedu Exp $	*/
4
5/*-
6 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
7 *
8 * Copyright (c) 1999 James Howard and Dag-Erling Co��dan Sm��rgrav
9 * Copyright (C) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org>
10 * All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 *    notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 *    notice, this list of conditions and the following disclaimer in the
19 *    documentation and/or other materials provided with the distribution.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#include <sys/cdefs.h>
35__FBSDID("$FreeBSD$");
36
37#include <sys/stat.h>
38#include <sys/types.h>
39
40#include <ctype.h>
41#include <err.h>
42#include <errno.h>
43#include <fcntl.h>
44#include <getopt.h>
45#include <limits.h>
46#include <libgen.h>
47#include <locale.h>
48#include <stdbool.h>
49#include <stdio.h>
50#include <stdlib.h>
51#include <string.h>
52#include <unistd.h>
53
54#include "grep.h"
55
56const char	*errstr[] = {
57	"",
58/* 1*/	"(standard input)",
59/* 2*/	"unknown %s option",
60/* 3*/	"usage: %s [-abcDEFGHhIiLlmnOoPqRSsUVvwxz] [-A num] [-B num] [-C[num]]\n",
61/* 4*/	"\t[-e pattern] [-f file] [--binary-files=value] [--color=when]\n",
62/* 5*/	"\t[--context[=num]] [--directories=action] [--label] [--line-buffered]\n",
63/* 6*/	"\t[--null] [pattern] [file ...]\n",
64/* 7*/	"Binary file %s matches\n",
65/* 8*/	"%s (BSD grep, GNU compatible) %s\n",
66};
67
68/* Flags passed to regcomp() and regexec() */
69int		 cflags = REG_NOSUB | REG_NEWLINE;
70int		 eflags = REG_STARTEND;
71
72bool		 matchall;
73
74/* Searching patterns */
75unsigned int	 patterns;
76static unsigned int pattern_sz;
77struct pat	*pattern;
78regex_t		*r_pattern;
79
80/* Filename exclusion/inclusion patterns */
81unsigned int	fpatterns, dpatterns;
82static unsigned int fpattern_sz, dpattern_sz;
83struct epat	*dpattern, *fpattern;
84
85/* For regex errors  */
86char	 re_error[RE_ERROR_BUF + 1];
87
88/* Command-line flags */
89long long Aflag;	/* -A x: print x lines trailing each match */
90long long Bflag;	/* -B x: print x lines leading each match */
91bool	 Hflag;		/* -H: always print file name */
92bool	 Lflag;		/* -L: only show names of files with no matches */
93bool	 bflag;		/* -b: show block numbers for each match */
94bool	 cflag;		/* -c: only show a count of matching lines */
95bool	 hflag;		/* -h: don't print filename headers */
96bool	 iflag;		/* -i: ignore case */
97bool	 lflag;		/* -l: only show names of files with matches */
98bool	 mflag;		/* -m x: stop reading the files after x matches */
99long long mcount;	/* count for -m */
100long long mlimit;	/* requested value for -m */
101char	 fileeol;	/* indicator for eol */
102bool	 nflag;		/* -n: show line numbers in front of matching lines */
103bool	 oflag;		/* -o: print only matching part */
104bool	 qflag;		/* -q: quiet mode (don't output anything) */
105bool	 sflag;		/* -s: silent mode (ignore errors) */
106bool	 vflag;		/* -v: only show non-matching lines */
107bool	 wflag;		/* -w: pattern must start and end on word boundaries */
108bool	 xflag;		/* -x: pattern must match entire line */
109bool	 lbflag;	/* --line-buffered */
110bool	 nullflag;	/* --null */
111char	*label;		/* --label */
112const char *color;	/* --color */
113int	 grepbehave = GREP_BASIC;	/* -EFGP: type of the regex */
114int	 binbehave = BINFILE_BIN;	/* -aIU: handling of binary files */
115int	 filebehave = FILE_STDIO;
116int	 devbehave = DEV_READ;		/* -D: handling of devices */
117int	 dirbehave = DIR_READ;		/* -dRr: handling of directories */
118int	 linkbehave = LINK_READ;	/* -OpS: handling of symlinks */
119
120bool	 dexclude, dinclude;	/* --exclude-dir and --include-dir */
121bool	 fexclude, finclude;	/* --exclude and --include */
122
123enum {
124	BIN_OPT = CHAR_MAX + 1,
125	COLOR_OPT,
126	HELP_OPT,
127	MMAP_OPT,
128	LINEBUF_OPT,
129	LABEL_OPT,
130	NULL_OPT,
131	R_EXCLUDE_OPT,
132	R_INCLUDE_OPT,
133	R_DEXCLUDE_OPT,
134	R_DINCLUDE_OPT
135};
136
137static inline const char	*init_color(const char *);
138
139/* Housekeeping */
140bool	 file_err;	/* file reading error */
141
142/*
143 * Prints usage information and returns 2.
144 */
145static void
146usage(void)
147{
148	fprintf(stderr, errstr[3], getprogname());
149	fprintf(stderr, "%s", errstr[4]);
150	fprintf(stderr, "%s", errstr[5]);
151	fprintf(stderr, "%s", errstr[6]);
152	exit(2);
153}
154
155static const char	*optstr = "0123456789A:B:C:D:EFGHILOPSRUVabcd:e:f:hilm:nopqrsuvwxyz";
156
157static const struct option long_options[] =
158{
159	{"binary-files",	required_argument,	NULL, BIN_OPT},
160	{"help",		no_argument,		NULL, HELP_OPT},
161	{"mmap",		no_argument,		NULL, MMAP_OPT},
162	{"line-buffered",	no_argument,		NULL, LINEBUF_OPT},
163	{"label",		required_argument,	NULL, LABEL_OPT},
164	{"null",		no_argument,		NULL, NULL_OPT},
165	{"color",		optional_argument,	NULL, COLOR_OPT},
166	{"colour",		optional_argument,	NULL, COLOR_OPT},
167	{"exclude",		required_argument,	NULL, R_EXCLUDE_OPT},
168	{"include",		required_argument,	NULL, R_INCLUDE_OPT},
169	{"exclude-dir",		required_argument,	NULL, R_DEXCLUDE_OPT},
170	{"include-dir",		required_argument,	NULL, R_DINCLUDE_OPT},
171	{"after-context",	required_argument,	NULL, 'A'},
172	{"text",		no_argument,		NULL, 'a'},
173	{"before-context",	required_argument,	NULL, 'B'},
174	{"byte-offset",		no_argument,		NULL, 'b'},
175	{"context",		optional_argument,	NULL, 'C'},
176	{"count",		no_argument,		NULL, 'c'},
177	{"devices",		required_argument,	NULL, 'D'},
178        {"directories",		required_argument,	NULL, 'd'},
179	{"extended-regexp",	no_argument,		NULL, 'E'},
180	{"regexp",		required_argument,	NULL, 'e'},
181	{"fixed-strings",	no_argument,		NULL, 'F'},
182	{"file",		required_argument,	NULL, 'f'},
183	{"basic-regexp",	no_argument,		NULL, 'G'},
184	{"no-filename",		no_argument,		NULL, 'h'},
185	{"with-filename",	no_argument,		NULL, 'H'},
186	{"ignore-case",		no_argument,		NULL, 'i'},
187	{"files-with-matches",	no_argument,		NULL, 'l'},
188	{"files-without-match", no_argument,            NULL, 'L'},
189	{"max-count",		required_argument,	NULL, 'm'},
190	{"line-number",		no_argument,		NULL, 'n'},
191	{"only-matching",	no_argument,		NULL, 'o'},
192	{"quiet",		no_argument,		NULL, 'q'},
193	{"silent",		no_argument,		NULL, 'q'},
194	{"recursive",		no_argument,		NULL, 'r'},
195	{"no-messages",		no_argument,		NULL, 's'},
196	{"binary",		no_argument,		NULL, 'U'},
197	{"unix-byte-offsets",	no_argument,		NULL, 'u'},
198	{"invert-match",	no_argument,		NULL, 'v'},
199	{"version",		no_argument,		NULL, 'V'},
200	{"word-regexp",		no_argument,		NULL, 'w'},
201	{"line-regexp",		no_argument,		NULL, 'x'},
202	{"null-data",		no_argument,		NULL, 'z'},
203	{NULL,			no_argument,		NULL, 0}
204};
205
206/*
207 * Adds a searching pattern to the internal array.
208 */
209static void
210add_pattern(char *pat, size_t len)
211{
212
213	/* Check if we can do a shortcut */
214	if (len == 0) {
215		matchall = true;
216		return;
217	}
218	/* Increase size if necessary */
219	if (patterns == pattern_sz) {
220		pattern_sz *= 2;
221		pattern = grep_realloc(pattern, ++pattern_sz *
222		    sizeof(struct pat));
223	}
224	if (len > 0 && pat[len - 1] == '\n')
225		--len;
226	/* pat may not be NUL-terminated */
227	pattern[patterns].pat = grep_malloc(len + 1);
228	memcpy(pattern[patterns].pat, pat, len);
229	pattern[patterns].len = len;
230	pattern[patterns].pat[len] = '\0';
231	++patterns;
232}
233
234/*
235 * Adds a file include/exclude pattern to the internal array.
236 */
237static void
238add_fpattern(const char *pat, int mode)
239{
240
241	/* Increase size if necessary */
242	if (fpatterns == fpattern_sz) {
243		fpattern_sz *= 2;
244		fpattern = grep_realloc(fpattern, ++fpattern_sz *
245		    sizeof(struct epat));
246	}
247	fpattern[fpatterns].pat = grep_strdup(pat);
248	fpattern[fpatterns].mode = mode;
249	++fpatterns;
250}
251
252/*
253 * Adds a directory include/exclude pattern to the internal array.
254 */
255static void
256add_dpattern(const char *pat, int mode)
257{
258
259	/* Increase size if necessary */
260	if (dpatterns == dpattern_sz) {
261		dpattern_sz *= 2;
262		dpattern = grep_realloc(dpattern, ++dpattern_sz *
263		    sizeof(struct epat));
264	}
265	dpattern[dpatterns].pat = grep_strdup(pat);
266	dpattern[dpatterns].mode = mode;
267	++dpatterns;
268}
269
270/*
271 * Reads searching patterns from a file and adds them with add_pattern().
272 */
273static void
274read_patterns(const char *fn)
275{
276	struct stat st;
277	FILE *f;
278	char *line;
279	size_t len;
280	ssize_t rlen;
281
282	if (strcmp(fn, "-") == 0)
283		f = stdin;
284	else if ((f = fopen(fn, "r")) == NULL)
285		err(2, "%s", fn);
286	if ((fstat(fileno(f), &st) == -1) || (S_ISDIR(st.st_mode))) {
287		fclose(f);
288		return;
289	}
290	len = 0;
291	line = NULL;
292	while ((rlen = getline(&line, &len, f)) != -1) {
293		if (line[0] == '\0')
294			continue;
295		add_pattern(line, line[0] == '\n' ? 0 : (size_t)rlen);
296	}
297
298	free(line);
299	if (ferror(f))
300		err(2, "%s", fn);
301	if (strcmp(fn, "-") != 0)
302		fclose(f);
303}
304
305static inline const char *
306init_color(const char *d)
307{
308	char *c;
309
310	c = getenv("GREP_COLOR");
311	return (c != NULL && c[0] != '\0' ? c : d);
312}
313
314int
315main(int argc, char *argv[])
316{
317	char **aargv, **eargv, *eopts;
318	char *ep;
319	const char *pn;
320	long long l;
321	unsigned int aargc, eargc, i;
322	int c, lastc, needpattern, newarg, prevoptind;
323	bool matched;
324
325	setlocale(LC_ALL, "");
326
327	/*
328	 * Check how we've bene invoked to determine the behavior we should
329	 * exhibit. In this way we can have all the functionalities in one
330	 * binary without the need of scripting and using ugly hacks.
331	 */
332	pn = getprogname();
333	switch (pn[0]) {
334	case 'e':
335		grepbehave = GREP_EXTENDED;
336		break;
337	case 'f':
338		grepbehave = GREP_FIXED;
339		break;
340	case 'r':
341		dirbehave = DIR_RECURSE;
342		Hflag = true;
343		break;
344	}
345
346	lastc = '\0';
347	newarg = 1;
348	prevoptind = 1;
349	needpattern = 1;
350	fileeol = '\n';
351
352	eopts = getenv("GREP_OPTIONS");
353
354	/* support for extra arguments in GREP_OPTIONS */
355	eargc = 0;
356	if (eopts != NULL && eopts[0] != '\0') {
357		char *str;
358
359		/* make an estimation of how many extra arguments we have */
360		for (unsigned int j = 0; j < strlen(eopts); j++)
361			if (eopts[j] == ' ')
362				eargc++;
363
364		eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1));
365
366		eargc = 0;
367		/* parse extra arguments */
368		while ((str = strsep(&eopts, " ")) != NULL)
369			if (str[0] != '\0')
370				eargv[eargc++] = grep_strdup(str);
371
372		aargv = (char **)grep_calloc(eargc + argc + 1,
373		    sizeof(char *));
374
375		aargv[0] = argv[0];
376		for (i = 0; i < eargc; i++)
377			aargv[i + 1] = eargv[i];
378		for (int j = 1; j < argc; j++, i++)
379			aargv[i + 1] = argv[j];
380
381		aargc = eargc + argc;
382	} else {
383		aargv = argv;
384		aargc = argc;
385	}
386
387	while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) !=
388	    -1)) {
389		switch (c) {
390		case '0': case '1': case '2': case '3': case '4':
391		case '5': case '6': case '7': case '8': case '9':
392			if (newarg || !isdigit(lastc))
393				Aflag = 0;
394			else if (Aflag > LLONG_MAX / 10 - 1) {
395				errno = ERANGE;
396				err(2, NULL);
397			}
398
399			Aflag = Bflag = (Aflag * 10) + (c - '0');
400			break;
401		case 'C':
402			if (optarg == NULL) {
403				Aflag = Bflag = 2;
404				break;
405			}
406			/* FALLTHROUGH */
407		case 'A':
408			/* FALLTHROUGH */
409		case 'B':
410			errno = 0;
411			l = strtoll(optarg, &ep, 10);
412			if (errno == ERANGE || errno == EINVAL)
413				err(2, NULL);
414			else if (ep[0] != '\0') {
415				errno = EINVAL;
416				err(2, NULL);
417			} else if (l < 0) {
418				errno = EINVAL;
419				err(2, "context argument must be non-negative");
420			}
421
422			if (c == 'A')
423				Aflag = l;
424			else if (c == 'B')
425				Bflag = l;
426			else
427				Aflag = Bflag = l;
428			break;
429		case 'a':
430			binbehave = BINFILE_TEXT;
431			break;
432		case 'b':
433			bflag = true;
434			break;
435		case 'c':
436			cflag = true;
437			break;
438		case 'D':
439			if (strcasecmp(optarg, "skip") == 0)
440				devbehave = DEV_SKIP;
441			else if (strcasecmp(optarg, "read") == 0)
442				devbehave = DEV_READ;
443			else
444				errx(2, errstr[2], "--devices");
445			break;
446		case 'd':
447			if (strcasecmp("recurse", optarg) == 0) {
448				Hflag = true;
449				dirbehave = DIR_RECURSE;
450			} else if (strcasecmp("skip", optarg) == 0)
451				dirbehave = DIR_SKIP;
452			else if (strcasecmp("read", optarg) == 0)
453				dirbehave = DIR_READ;
454			else
455				errx(2, errstr[2], "--directories");
456			break;
457		case 'E':
458			grepbehave = GREP_EXTENDED;
459			break;
460		case 'e':
461			{
462				char *token;
463				char *string = optarg;
464
465				while ((token = strsep(&string, "\n")) != NULL)
466					add_pattern(token, strlen(token));
467			}
468			needpattern = 0;
469			break;
470		case 'F':
471			grepbehave = GREP_FIXED;
472			break;
473		case 'f':
474			read_patterns(optarg);
475			needpattern = 0;
476			break;
477		case 'G':
478			grepbehave = GREP_BASIC;
479			break;
480		case 'H':
481			Hflag = true;
482			break;
483		case 'h':
484			Hflag = false;
485			hflag = true;
486			break;
487		case 'I':
488			binbehave = BINFILE_SKIP;
489			break;
490		case 'i':
491		case 'y':
492			iflag =  true;
493			cflags |= REG_ICASE;
494			break;
495		case 'L':
496			lflag = false;
497			Lflag = true;
498			break;
499		case 'l':
500			Lflag = false;
501			lflag = true;
502			break;
503		case 'm':
504			mflag = true;
505			errno = 0;
506			mlimit = mcount = strtoll(optarg, &ep, 10);
507			if (((errno == ERANGE) && (mcount == LLONG_MAX)) ||
508			    ((errno == EINVAL) && (mcount == 0)))
509				err(2, NULL);
510			else if (ep[0] != '\0') {
511				errno = EINVAL;
512				err(2, NULL);
513			}
514			break;
515		case 'n':
516			nflag = true;
517			break;
518		case 'O':
519			linkbehave = LINK_EXPLICIT;
520			break;
521		case 'o':
522			oflag = true;
523			cflags &= ~REG_NOSUB;
524			break;
525		case 'p':
526			linkbehave = LINK_SKIP;
527			break;
528		case 'q':
529			qflag = true;
530			break;
531		case 'S':
532			linkbehave = LINK_READ;
533			break;
534		case 'R':
535		case 'r':
536			dirbehave = DIR_RECURSE;
537			Hflag = true;
538			break;
539		case 's':
540			sflag = true;
541			break;
542		case 'U':
543			binbehave = BINFILE_BIN;
544			break;
545		case 'u':
546		case MMAP_OPT:
547			filebehave = FILE_MMAP;
548			break;
549		case 'V':
550			printf(errstr[8], getprogname(), VERSION);
551			exit(0);
552		case 'v':
553			vflag = true;
554			break;
555		case 'w':
556			wflag = true;
557			cflags &= ~REG_NOSUB;
558			break;
559		case 'x':
560			xflag = true;
561			cflags &= ~REG_NOSUB;
562			break;
563		case 'z':
564			fileeol = '\0';
565			break;
566		case BIN_OPT:
567			if (strcasecmp("binary", optarg) == 0)
568				binbehave = BINFILE_BIN;
569			else if (strcasecmp("without-match", optarg) == 0)
570				binbehave = BINFILE_SKIP;
571			else if (strcasecmp("text", optarg) == 0)
572				binbehave = BINFILE_TEXT;
573			else
574				errx(2, errstr[2], "--binary-files");
575			break;
576		case COLOR_OPT:
577			color = NULL;
578			if (optarg == NULL || strcasecmp("auto", optarg) == 0 ||
579			    strcasecmp("tty", optarg) == 0 ||
580			    strcasecmp("if-tty", optarg) == 0) {
581				char *term;
582
583				term = getenv("TERM");
584				if (isatty(STDOUT_FILENO) && term != NULL &&
585				    strcasecmp(term, "dumb") != 0)
586					color = init_color("01;31");
587			} else if (strcasecmp("always", optarg) == 0 ||
588			    strcasecmp("yes", optarg) == 0 ||
589			    strcasecmp("force", optarg) == 0) {
590				color = init_color("01;31");
591			} else if (strcasecmp("never", optarg) != 0 &&
592			    strcasecmp("none", optarg) != 0 &&
593			    strcasecmp("no", optarg) != 0)
594				errx(2, errstr[2], "--color");
595			cflags &= ~REG_NOSUB;
596			break;
597		case LABEL_OPT:
598			label = optarg;
599			break;
600		case LINEBUF_OPT:
601			lbflag = true;
602			break;
603		case NULL_OPT:
604			nullflag = true;
605			break;
606		case R_INCLUDE_OPT:
607			finclude = true;
608			add_fpattern(optarg, INCL_PAT);
609			break;
610		case R_EXCLUDE_OPT:
611			fexclude = true;
612			add_fpattern(optarg, EXCL_PAT);
613			break;
614		case R_DINCLUDE_OPT:
615			dinclude = true;
616			add_dpattern(optarg, INCL_PAT);
617			break;
618		case R_DEXCLUDE_OPT:
619			dexclude = true;
620			add_dpattern(optarg, EXCL_PAT);
621			break;
622		case HELP_OPT:
623		default:
624			usage();
625		}
626		lastc = c;
627		newarg = optind != prevoptind;
628		prevoptind = optind;
629	}
630	aargc -= optind;
631	aargv += optind;
632
633	/* xflag takes precedence, don't confuse the matching bits. */
634	if (wflag && xflag)
635		wflag = false;
636
637	/* Fail if we don't have any pattern */
638	if (aargc == 0 && needpattern)
639		usage();
640
641	/* Process patterns from command line */
642	if (aargc != 0 && needpattern) {
643		char *token;
644		char *string = *aargv;
645
646		while ((token = strsep(&string, "\n")) != NULL)
647			add_pattern(token, strlen(token));
648		--aargc;
649		++aargv;
650	}
651
652	switch (grepbehave) {
653	case GREP_BASIC:
654		break;
655	case GREP_FIXED:
656		/*
657		 * regex(3) implementations that support fixed-string searches generally
658		 * define either REG_NOSPEC or REG_LITERAL. Set the appropriate flag
659		 * here. If neither are defined, GREP_FIXED later implies that the
660		 * internal literal matcher should be used. Other cflags that have
661		 * the same interpretation as REG_NOSPEC and REG_LITERAL should be
662		 * similarly added here, and grep.h should be amended to take this into
663		 * consideration when defining WITH_INTERNAL_NOSPEC.
664		 */
665#if defined(REG_NOSPEC)
666		cflags |= REG_NOSPEC;
667#elif defined(REG_LITERAL)
668		cflags |= REG_LITERAL;
669#endif
670		break;
671	case GREP_EXTENDED:
672		cflags |= REG_EXTENDED;
673		break;
674	default:
675		/* NOTREACHED */
676		usage();
677	}
678
679	r_pattern = grep_calloc(patterns, sizeof(*r_pattern));
680
681#ifdef WITH_INTERNAL_NOSPEC
682	if (grepbehave != GREP_FIXED) {
683#else
684	{
685#endif
686		/* Check if cheating is allowed (always is for fgrep). */
687		for (i = 0; i < patterns; ++i) {
688			c = regcomp(&r_pattern[i], pattern[i].pat, cflags);
689			if (c != 0) {
690				regerror(c, &r_pattern[i], re_error,
691				    RE_ERROR_BUF);
692				errx(2, "%s", re_error);
693			}
694		}
695	}
696
697	if (lbflag)
698		setlinebuf(stdout);
699
700	if ((aargc == 0 || aargc == 1) && !Hflag)
701		hflag = true;
702
703	initqueue();
704
705	if (aargc == 0 && dirbehave != DIR_RECURSE)
706		exit(!procfile("-"));
707
708	if (dirbehave == DIR_RECURSE)
709		matched = grep_tree(aargv);
710	else
711		for (matched = false; aargc--; ++aargv) {
712			if ((finclude || fexclude) && !file_matching(*aargv))
713				continue;
714			if (procfile(*aargv))
715				matched = true;
716		}
717
718	if (Lflag)
719		matched = !matched;
720
721	/*
722	 * Calculate the correct return value according to the
723	 * results and the command line option.
724	 */
725	exit(matched ? (file_err ? (qflag ? 0 : 2) : 0) : (file_err ? 2 : 1));
726}
727