1140074Spjd/*	$NetBSD: grep.c,v 1.16 2022/10/15 21:53:22 andvar Exp $	*/
2140074Spjd/* 	$FreeBSD: head/usr.bin/grep/grep.c 211519 2010-08-19 22:55:17Z delphij $	*/
3140074Spjd/*	$OpenBSD: grep.c,v 1.42 2010/07/02 22:18:03 tedu Exp $	*/
4140074Spjd
5216468Sobrien/*-
6140074Spjd * Copyright (c) 1999 James Howard and Dag-Erling Co��dan Sm��rgrav
7140074Spjd * 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.16 2022/10/15 21:53:22 andvar 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 messages 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/* Searching patterns */
84unsigned int	 patterns, pattern_sz;
85char		**pattern;
86regex_t		*r_pattern;
87fastgrep_t	*fg_pattern;
88
89/* Filename exclusion/inclusion patterns */
90unsigned int	 fpatterns, fpattern_sz;
91unsigned int	 dpatterns, dpattern_sz;
92struct epat	*dpattern, *fpattern;
93
94/* For regex errors  */
95char	 re_error[RE_ERROR_BUF + 1];
96
97/* Command-line flags */
98unsigned long long Aflag;	/* -A x: print x lines trailing each match */
99unsigned long long Bflag;	/* -B x: print x lines leading each match */
100bool	 Hflag;		/* -H: always print file name */
101bool	 Lflag;		/* -L: only show names of files with no matches */
102bool	 bflag;		/* -b: show block numbers for each match */
103bool	 cflag;		/* -c: only show a count of matching lines */
104bool	 hflag;		/* -h: don't print filename headers */
105bool	 iflag;		/* -i: ignore case */
106bool	 lflag;		/* -l: only show names of files with matches */
107bool	 mflag;		/* -m x: stop reading the files after x matches */
108unsigned long long mcount;	/* count for -m */
109bool	 nflag;		/* -n: show line numbers in front of matching lines */
110bool	 oflag;		/* -o: print only matching part */
111bool	 qflag;		/* -q: quiet mode (don't output anything) */
112bool	 sflag;		/* -s: silent mode (ignore errors) */
113bool	 vflag;		/* -v: only show non-matching lines */
114bool	 wflag;		/* -w: pattern must start and end on word boundaries */
115bool	 xflag;		/* -x: pattern must match entire line */
116bool	 lbflag;	/* --line-buffered */
117bool	 nullflag;	/* --null */
118bool	 nulldataflag;	/* --null-data */
119unsigned char line_sep = '\n';	/* 0 for --null-data */
120char	*label;		/* --label */
121const char *color;	/* --color */
122int	 grepbehave = GREP_BASIC;	/* -EFGP: type of the regex */
123int	 binbehave = BINFILE_BIN;	/* -aIU: handling of binary files */
124int	 filebehave = FILE_STDIO;	/* -JZ: normal, gzip or bzip2 file */
125int	 devbehave = DEV_READ;		/* -D: handling of devices */
126int	 dirbehave = DIR_READ;		/* -dRr: handling of directories */
127int	 linkbehave = LINK_READ;	/* -OpS: handling of symlinks */
128
129bool	 dexclude, dinclude;	/* --exclude-dir and --include-dir */
130bool	 fexclude, finclude;	/* --exclude and --include */
131
132enum {
133	BIN_OPT = CHAR_MAX + 1,
134	COLOR_OPT,
135	DECOMPRESS_OPT,
136	HELP_OPT,
137	MMAP_OPT,
138	LINEBUF_OPT,
139	LABEL_OPT,
140	R_EXCLUDE_OPT,
141	R_INCLUDE_OPT,
142	R_DEXCLUDE_OPT,
143	R_DINCLUDE_OPT
144};
145
146static inline const char	*init_color(const char *);
147
148/* Housekeeping */
149int	 tail;		/* lines left to print */
150bool	 notfound;	/* file not found */
151
152extern char	*__progname;
153
154/*
155 * Prints usage information and returns 2.
156 */
157__dead static void
158usage(void)
159{
160	fprintf(stderr, getstr(4), __progname);
161	fprintf(stderr, "%s", getstr(5));
162	fprintf(stderr, "%s", getstr(6));
163	fprintf(stderr, "%s", getstr(7));
164	exit(2);
165}
166
167static const char optstr[] =
168    "0123456789A:B:C:D:EFGHIJLOPSRUVZabcd:e:f:hilm:nopqrsuvwxyz";
169
170struct option long_options[] =
171{
172	{"binary-files",	required_argument,	NULL, BIN_OPT},
173#ifndef WITHOUT_GZIP
174	{"decompress",          no_argument,            NULL, DECOMPRESS_OPT},
175#endif
176	{"help",		no_argument,		NULL, HELP_OPT},
177	{"mmap",		no_argument,		NULL, MMAP_OPT},
178	{"line-buffered",	no_argument,		NULL, LINEBUF_OPT},
179	{"label",		required_argument,	NULL, LABEL_OPT},
180	{"color",		optional_argument,	NULL, COLOR_OPT},
181	{"colour",		optional_argument,	NULL, COLOR_OPT},
182	{"exclude",		required_argument,	NULL, R_EXCLUDE_OPT},
183	{"include",		required_argument,	NULL, R_INCLUDE_OPT},
184	{"exclude-dir",		required_argument,	NULL, R_DEXCLUDE_OPT},
185	{"include-dir",		required_argument,	NULL, R_DINCLUDE_OPT},
186	{"after-context",	required_argument,	NULL, 'A'},
187	{"text",		no_argument,		NULL, 'a'},
188	{"before-context",	required_argument,	NULL, 'B'},
189	{"byte-offset",		no_argument,		NULL, 'b'},
190	{"context",		optional_argument,	NULL, 'C'},
191	{"count",		no_argument,		NULL, 'c'},
192	{"devices",		required_argument,	NULL, 'D'},
193        {"directories",		required_argument,	NULL, 'd'},
194	{"extended-regexp",	no_argument,		NULL, 'E'},
195	{"regexp",		required_argument,	NULL, 'e'},
196	{"fixed-strings",	no_argument,		NULL, 'F'},
197	{"file",		required_argument,	NULL, 'f'},
198	{"basic-regexp",	no_argument,		NULL, 'G'},
199	{"no-filename",		no_argument,		NULL, 'h'},
200	{"with-filename",	no_argument,		NULL, 'H'},
201	{"ignore-case",		no_argument,		NULL, 'i'},
202#ifndef WITHOUT_BZ2
203	{"bz2decompress",	no_argument,		NULL, 'J'},
204#endif
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	/* TODO: Check for empty patterns and shortcut */
233
234	/* Increase size if necessary */
235	if (patterns == pattern_sz) {
236		pattern_sz *= 2;
237		pattern = grep_realloc(pattern, ++pattern_sz *
238		    sizeof(*pattern));
239	}
240	if (len > 0 && pat[len - 1] == '\n')
241		--len;
242	/* pat may not be NUL-terminated */
243	pattern[patterns] = grep_malloc(len + 1);
244	memcpy(pattern[patterns], pat, len);
245	pattern[patterns][len] = '\0';
246	++patterns;
247}
248
249/*
250 * Adds a file include/exclude pattern to the internal array.
251 */
252static void
253add_fpattern(const char *pat, int mode)
254{
255
256	/* Increase size if necessary */
257	if (fpatterns == fpattern_sz) {
258		fpattern_sz *= 2;
259		fpattern = grep_realloc(fpattern, ++fpattern_sz *
260		    sizeof(struct epat));
261	}
262	fpattern[fpatterns].pat = grep_strdup(pat);
263	fpattern[fpatterns].mode = mode;
264	++fpatterns;
265}
266
267/*
268 * Adds a directory include/exclude pattern to the internal array.
269 */
270static void
271add_dpattern(const char *pat, int mode)
272{
273
274	/* Increase size if necessary */
275	if (dpatterns == dpattern_sz) {
276		dpattern_sz *= 2;
277		dpattern = grep_realloc(dpattern, ++dpattern_sz *
278		    sizeof(struct epat));
279	}
280	dpattern[dpatterns].pat = grep_strdup(pat);
281	dpattern[dpatterns].mode = mode;
282	++dpatterns;
283}
284
285/*
286 * Reads searching patterns from a file and adds them with add_pattern().
287 */
288static void
289read_patterns(const char *fn)
290{
291	FILE *f;
292	char *line;
293	size_t len;
294	ssize_t rlen;
295
296	if ((f = fopen(fn, "r")) == NULL)
297		err(2, "%s", fn);
298	line = NULL;
299	len = 0;
300	while ((rlen = getline(&line, &len, f)) != -1)
301		add_pattern(line, *line == '\n' ? 0 : (size_t)rlen);
302	free(line);
303	if (ferror(f))
304		err(2, "%s", fn);
305	fclose(f);
306}
307
308static inline const char *
309init_color(const char *d)
310{
311	char *c;
312
313	c = getenv("GREP_COLOR");
314	return (c != NULL ? c : d);
315}
316
317int
318main(int argc, char *argv[])
319{
320	char **aargv, **eargv, *eopts;
321	char *ep;
322	unsigned long long l;
323	unsigned int aargc, eargc, i, j;
324	int c, lastc, needpattern, newarg, prevoptind;
325
326	setlocale(LC_ALL, "");
327
328#ifndef WITHOUT_NLS
329	catalog = catopen("grep", NL_CAT_LOCALE);
330#endif
331
332	/* Check what is the program name of the binary.  In this
333	   way we can have all the functionalities in one binary
334	   without the need of scripting and using ugly hacks. */
335	switch (__progname[0]) {
336	case 'e':
337		grepbehave = GREP_EXTENDED;
338		break;
339	case 'f':
340		grepbehave = GREP_FIXED;
341		break;
342	case 'g':
343		grepbehave = GREP_BASIC;
344		break;
345#ifndef WITHOUT_GZIP
346	case 'z':
347		filebehave = FILE_GZIP;
348		switch(__progname[1]) {
349		case 'e':
350			grepbehave = GREP_EXTENDED;
351			break;
352		case 'f':
353			grepbehave = GREP_FIXED;
354			break;
355		case 'g':
356			grepbehave = GREP_BASIC;
357			break;
358		}
359		break;
360#endif
361	}
362
363	lastc = '\0';
364	newarg = 1;
365	prevoptind = 1;
366	needpattern = 1;
367
368	eopts = getenv("GREP_OPTIONS");
369
370	/* support for extra arguments in GREP_OPTIONS */
371	eargc = 0;
372	if (eopts != NULL) {
373		char *str;
374
375		/* make an estimation of how many extra arguments we have */
376		for (j = 0; j < strlen(eopts); j++)
377			if (eopts[j] == ' ')
378				eargc++;
379
380		eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1));
381
382		eargc = 0;
383		/* parse extra arguments */
384		while ((str = strsep(&eopts, " ")) != NULL)
385			eargv[eargc++] = grep_strdup(str);
386
387		aargv = (char **)grep_calloc(eargc + argc + 1,
388		    sizeof(char *));
389
390		aargv[0] = argv[0];
391		for (i = 0; i < eargc; i++)
392			aargv[i + 1] = eargv[i];
393		for (j = 1; j < (unsigned int)argc; j++, i++)
394			aargv[i + 1] = argv[j];
395
396		aargc = eargc + argc;
397	} else {
398		aargv = argv;
399		aargc = argc;
400	}
401
402	while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) !=
403	    -1)) {
404		switch (c) {
405		case '0': case '1': case '2': case '3': case '4':
406		case '5': case '6': case '7': case '8': case '9':
407			if (newarg || !isdigit(lastc))
408				Aflag = 0;
409			else if (Aflag > LLONG_MAX / 10) {
410				errno = ERANGE;
411				err(2, NULL);
412			}
413			Aflag = Bflag = (Aflag * 10) + (c - '0');
414			break;
415		case 'C':
416			if (optarg == NULL) {
417				Aflag = Bflag = 2;
418				break;
419			}
420			/* FALLTHROUGH */
421		case 'A':
422			/* FALLTHROUGH */
423		case 'B':
424			errno = 0;
425			l = strtoull(optarg, &ep, 10);
426			if (((errno == ERANGE) && (l == ULLONG_MAX)) ||
427			    ((errno == EINVAL) && (l == 0)))
428				err(2, NULL);
429			else if (ep[0] != '\0') {
430				errno = EINVAL;
431				err(2, NULL);
432			}
433			if (c == 'A')
434				Aflag = l;
435			else if (c == 'B')
436				Bflag = l;
437			else
438				Aflag = Bflag = l;
439			break;
440		case 'a':
441			binbehave = BINFILE_TEXT;
442			break;
443		case 'b':
444			bflag = true;
445			break;
446		case 'c':
447			cflag = true;
448			break;
449		case 'D':
450			if (strcasecmp(optarg, "skip") == 0)
451				devbehave = DEV_SKIP;
452			else if (strcasecmp(optarg, "read") == 0)
453				devbehave = DEV_READ;
454			else
455				errx(2, getstr(3), "--devices");
456			break;
457		case 'd':
458			if (strcasecmp("recurse", optarg) == 0) {
459				Hflag = true;
460				dirbehave = DIR_RECURSE;
461			} else if (strcasecmp("skip", optarg) == 0)
462				dirbehave = DIR_SKIP;
463			else if (strcasecmp("read", optarg) == 0)
464				dirbehave = DIR_READ;
465			else
466				errx(2, getstr(3), "--directories");
467			break;
468		case 'E':
469			grepbehave = GREP_EXTENDED;
470			break;
471		case 'e':
472			add_pattern(optarg, strlen(optarg));
473			needpattern = 0;
474			break;
475		case 'F':
476			grepbehave = GREP_FIXED;
477			break;
478		case 'f':
479			read_patterns(optarg);
480			needpattern = 0;
481			break;
482		case 'G':
483			grepbehave = GREP_BASIC;
484			break;
485		case 'H':
486			Hflag = true;
487			break;
488		case 'h':
489			Hflag = false;
490			hflag = true;
491			break;
492		case 'I':
493			binbehave = BINFILE_SKIP;
494			break;
495		case 'i':
496		case 'y':
497			iflag =  true;
498			cflags |= REG_ICASE;
499			break;
500#ifndef WITHOUT_BZ2
501		case 'J':
502			filebehave = FILE_BZIP;
503			break;
504#endif
505		case 'L':
506			lflag = false;
507			Lflag = true;
508			break;
509		case 'l':
510			Lflag = false;
511			lflag = true;
512			break;
513		case 'm':
514			mflag = true;
515			errno = 0;
516			mcount = strtoull(optarg, &ep, 10);
517			if (((errno == ERANGE) && (mcount == ULLONG_MAX)) ||
518			    ((errno == EINVAL) && (mcount == 0)))
519				err(2, NULL);
520			else if (ep[0] != '\0') {
521				errno = EINVAL;
522				err(2, NULL);
523			}
524			break;
525		case 'n':
526			nflag = true;
527			break;
528		case 'O':
529			linkbehave = LINK_EXPLICIT;
530			break;
531		case 'o':
532			oflag = true;
533			break;
534		case 'p':
535			linkbehave = LINK_SKIP;
536			break;
537		case 'q':
538			qflag = true;
539			break;
540		case 'S':
541			linkbehave = LINK_READ;
542			break;
543		case 'R':
544		case 'r':
545			dirbehave = DIR_RECURSE;
546			Hflag = true;
547			break;
548		case 's':
549			sflag = true;
550			break;
551		case 'U':
552			binbehave = BINFILE_BIN;
553			break;
554		case 'u':
555		case MMAP_OPT:
556			/* noop, compatibility */
557			break;
558		case 'V':
559			printf(getstr(9), __progname, VERSION);
560			exit(0);
561		case 'v':
562			vflag = true;
563			break;
564		case 'w':
565			wflag = true;
566			break;
567		case 'x':
568			xflag = true;
569			break;
570		case 'Z':
571			nullflag = true;
572			break;
573		case 'z':
574			nulldataflag = true;
575			line_sep = '\0';
576			break;
577		case BIN_OPT:
578			if (strcasecmp("binary", optarg) == 0)
579				binbehave = BINFILE_BIN;
580			else if (strcasecmp("without-match", optarg) == 0)
581				binbehave = BINFILE_SKIP;
582			else if (strcasecmp("text", optarg) == 0)
583				binbehave = BINFILE_TEXT;
584			else
585				errx(2, getstr(3), "--binary-files");
586			break;
587		case COLOR_OPT:
588			color = NULL;
589			if (optarg == NULL || strcasecmp("auto", optarg) == 0 ||
590			    strcasecmp("tty", optarg) == 0 ||
591			    strcasecmp("if-tty", optarg) == 0) {
592				char *term;
593
594				term = getenv("TERM");
595				if (isatty(STDOUT_FILENO) && term != NULL &&
596				    strcasecmp(term, "dumb") != 0)
597					color = init_color("01;31");
598			} else if (strcasecmp("always", optarg) == 0 ||
599			    strcasecmp("yes", optarg) == 0 ||
600			    strcasecmp("force", optarg) == 0) {
601				color = init_color("01;31");
602			} else if (strcasecmp("never", optarg) != 0 &&
603			    strcasecmp("none", optarg) != 0 &&
604			    strcasecmp("no", optarg) != 0)
605				errx(2, getstr(3), "--color");
606			break;
607#ifndef WITHOUT_GZIP
608		case DECOMPRESS_OPT:
609			filebehave = FILE_GZIP;
610			break;
611#endif
612		case LABEL_OPT:
613			label = optarg;
614			break;
615		case LINEBUF_OPT:
616			lbflag = true;
617			break;
618		case R_INCLUDE_OPT:
619			finclude = true;
620			add_fpattern(optarg, INCL_PAT);
621			break;
622		case R_EXCLUDE_OPT:
623			fexclude = true;
624			add_fpattern(optarg, EXCL_PAT);
625			break;
626		case R_DINCLUDE_OPT:
627			dinclude = true;
628			add_dpattern(optarg, INCL_PAT);
629			break;
630		case R_DEXCLUDE_OPT:
631			dexclude = true;
632			add_dpattern(optarg, EXCL_PAT);
633			break;
634		case HELP_OPT:
635		default:
636			usage();
637		}
638		lastc = c;
639		newarg = optind != prevoptind;
640		prevoptind = optind;
641	}
642	aargc -= optind;
643	aargv += optind;
644
645	/* Fail if we don't have any pattern */
646	if (aargc == 0 && needpattern)
647		usage();
648
649	/* Process patterns from command line */
650	if (aargc != 0 && needpattern) {
651		add_pattern(*aargv, strlen(*aargv));
652		--aargc;
653		++aargv;
654	}
655
656	switch (grepbehave) {
657	case GREP_FIXED:
658	case GREP_BASIC:
659		break;
660	case GREP_EXTENDED:
661		cflags |= REG_EXTENDED;
662		break;
663	default:
664		/* NOTREACHED */
665		usage();
666	}
667
668	fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern));
669	r_pattern = grep_calloc(patterns, sizeof(*r_pattern));
670/*
671 * XXX: fgrepcomp() and fastcomp() are workarounds for regexec() performance.
672 * Optimizations should be done there.
673 */
674		/* Check if cheating is allowed (always is for fgrep). */
675	if (grepbehave == GREP_FIXED) {
676		for (i = 0; i < patterns; ++i)
677			fgrepcomp(&fg_pattern[i], pattern[i]);
678	} else {
679		for (i = 0; i < patterns; ++i) {
680			if (fastcomp(&fg_pattern[i], pattern[i])) {
681				/* Fall back to full regex library */
682				c = regcomp(&r_pattern[i], pattern[i], cflags);
683				if (c != 0) {
684					regerror(c, &r_pattern[i], re_error,
685					    RE_ERROR_BUF);
686					errx(2, "%s", re_error);
687				}
688			}
689		}
690	}
691
692	if (lbflag) {
693#ifdef _IOLBF
694		setvbuf(stdout, NULL, _IOLBF, 0);
695#else
696		setlinebuf(stdout);
697#endif
698	}
699
700	if ((aargc == 0 || aargc == 1) && !Hflag)
701		hflag = true;
702
703	if (aargc == 0)
704		exit(!procfile("-"));
705
706	if (dirbehave == DIR_RECURSE)
707		c = grep_tree(aargv);
708	else
709		for (c = 0; aargc--; ++aargv) {
710			if ((finclude || fexclude) && !file_matching(*aargv))
711				continue;
712			c+= procfile(*aargv);
713		}
714
715#ifndef WITHOUT_NLS
716	catclose(catalog);
717#endif
718
719	/* Find out the correct return value according to the
720	   results and the command line option. */
721	exit(c ? (notfound ? (qflag ? 0 : 2) : 0) : (notfound ? 2 : 1));
722}
723