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