1/*	$NetBSD: grep.c,v 1.4 2011/02/16 01:31:33 joerg Exp $	*/
2/* 	$FreeBSD$	*/
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#include <sys/cdefs.h>
33__FBSDID("$FreeBSD$");
34
35#include <sys/stat.h>
36#include <sys/types.h>
37
38#include <ctype.h>
39#include <err.h>
40#include <errno.h>
41#include <fcntl.h>
42#include <getopt.h>
43#include <limits.h>
44#include <libgen.h>
45#include <locale.h>
46#include <stdbool.h>
47#include <stdio.h>
48#include <stdlib.h>
49#include <string.h>
50#include <unistd.h>
51
52#include "fastmatch.h"
53#include "grep.h"
54
55#ifndef WITHOUT_NLS
56#include <nl_types.h>
57nl_catd	 catalog;
58#endif
59
60/*
61 * Default messags to use when NLS is disabled or no catalogue
62 * is found.
63 */
64const char	*errstr[] = {
65	"",
66/* 1*/	"(standard input)",
67/* 2*/	"cannot read bzip2 compressed file",
68/* 3*/	"unknown %s option",
69/* 4*/	"usage: %s [-abcDEFGHhIiJLlmnOoPqRSsUVvwxZ] [-A num] [-B num] [-C[num]]\n",
70/* 5*/	"\t[-e pattern] [-f file] [--binary-files=value] [--color=when]\n",
71/* 6*/	"\t[--context[=num]] [--directories=action] [--label] [--line-buffered]\n",
72/* 7*/	"\t[--null] [pattern] [file ...]\n",
73/* 8*/	"Binary file %s matches\n",
74/* 9*/	"%s (BSD grep) %s\n",
75};
76
77/* Flags passed to regcomp() and regexec() */
78int		 cflags = REG_NOSUB;
79int		 eflags = REG_STARTEND;
80
81/* Shortcut for matching all cases like empty regex */
82bool		 matchall;
83
84/* Searching patterns */
85unsigned int	 patterns, pattern_sz;
86struct pat	*pattern;
87regex_t		*r_pattern;
88fastmatch_t	*fg_pattern;
89
90/* Filename exclusion/inclusion patterns */
91unsigned int	 fpatterns, fpattern_sz;
92unsigned int	 dpatterns, dpattern_sz;
93struct epat	*dpattern, *fpattern;
94
95/* For regex errors  */
96char	 re_error[RE_ERROR_BUF + 1];
97
98/* Command-line flags */
99unsigned long long Aflag;	/* -A x: print x lines trailing each match */
100unsigned long long Bflag;	/* -B x: print x lines leading each match */
101bool	 Hflag;		/* -H: always print file name */
102bool	 Lflag;		/* -L: only show names of files with no matches */
103bool	 bflag;		/* -b: show block numbers for each match */
104bool	 cflag;		/* -c: only show a count of matching lines */
105bool	 hflag;		/* -h: don't print filename headers */
106bool	 iflag;		/* -i: ignore case */
107bool	 lflag;		/* -l: only show names of files with matches */
108bool	 mflag;		/* -m x: stop reading the files after x matches */
109long long mcount;	/* count for -m */
110long long mlimit;	/* requested value for -m */
111bool	 nflag;		/* -n: show line numbers in front of matching lines */
112bool	 oflag;		/* -o: print only matching part */
113bool	 qflag;		/* -q: quiet mode (don't output anything) */
114bool	 sflag;		/* -s: silent mode (ignore errors) */
115bool	 vflag;		/* -v: only show non-matching lines */
116bool	 wflag;		/* -w: pattern must start and end on word boundaries */
117bool	 xflag;		/* -x: pattern must match entire line */
118bool	 lbflag;	/* --line-buffered */
119bool	 nullflag;	/* --null */
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	HELP_OPT,
136	MMAP_OPT,
137	LINEBUF_OPT,
138	LABEL_OPT,
139	NULL_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 */
149bool	 first = true;	/* flag whether we are processing the first match */
150bool	 prev;		/* flag whether or not the previous line matched */
151int	 tail;		/* lines left to print */
152bool	 file_err;	/* file reading error */
153
154/*
155 * Prints usage information and returns 2.
156 */
157static void
158usage(void)
159{
160	fprintf(stderr, getstr(4), getprogname());
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 = "0123456789A:B:C:D:EFGHIJMLOPSRUVZabcd:e:f:hilm:nopqrsuvwxXy";
168
169struct option long_options[] =
170{
171	{"binary-files",	required_argument,	NULL, BIN_OPT},
172	{"help",		no_argument,		NULL, HELP_OPT},
173	{"mmap",		no_argument,		NULL, MMAP_OPT},
174	{"line-buffered",	no_argument,		NULL, LINEBUF_OPT},
175	{"label",		required_argument,	NULL, LABEL_OPT},
176	{"null",		no_argument,		NULL, NULL_OPT},
177	{"color",		optional_argument,	NULL, COLOR_OPT},
178	{"colour",		optional_argument,	NULL, COLOR_OPT},
179	{"exclude",		required_argument,	NULL, R_EXCLUDE_OPT},
180	{"include",		required_argument,	NULL, R_INCLUDE_OPT},
181	{"exclude-dir",		required_argument,	NULL, R_DEXCLUDE_OPT},
182	{"include-dir",		required_argument,	NULL, R_DINCLUDE_OPT},
183	{"after-context",	required_argument,	NULL, 'A'},
184	{"text",		no_argument,		NULL, 'a'},
185	{"before-context",	required_argument,	NULL, 'B'},
186	{"byte-offset",		no_argument,		NULL, 'b'},
187	{"context",		optional_argument,	NULL, 'C'},
188	{"count",		no_argument,		NULL, 'c'},
189	{"devices",		required_argument,	NULL, 'D'},
190        {"directories",		required_argument,	NULL, 'd'},
191	{"extended-regexp",	no_argument,		NULL, 'E'},
192	{"regexp",		required_argument,	NULL, 'e'},
193	{"fixed-strings",	no_argument,		NULL, 'F'},
194	{"file",		required_argument,	NULL, 'f'},
195	{"basic-regexp",	no_argument,		NULL, 'G'},
196	{"no-filename",		no_argument,		NULL, 'h'},
197	{"with-filename",	no_argument,		NULL, 'H'},
198	{"ignore-case",		no_argument,		NULL, 'i'},
199	{"bz2decompress",	no_argument,		NULL, 'J'},
200	{"files-with-matches",	no_argument,		NULL, 'l'},
201	{"files-without-match", no_argument,            NULL, 'L'},
202	{"max-count",		required_argument,	NULL, 'm'},
203	{"lzma",		no_argument,		NULL, 'M'},
204	{"line-number",		no_argument,		NULL, 'n'},
205	{"only-matching",	no_argument,		NULL, 'o'},
206	{"quiet",		no_argument,		NULL, 'q'},
207	{"silent",		no_argument,		NULL, 'q'},
208	{"recursive",		no_argument,		NULL, 'r'},
209	{"no-messages",		no_argument,		NULL, 's'},
210	{"binary",		no_argument,		NULL, 'U'},
211	{"unix-byte-offsets",	no_argument,		NULL, 'u'},
212	{"invert-match",	no_argument,		NULL, 'v'},
213	{"version",		no_argument,		NULL, 'V'},
214	{"word-regexp",		no_argument,		NULL, 'w'},
215	{"line-regexp",		no_argument,		NULL, 'x'},
216	{"xz",			no_argument,		NULL, 'X'},
217	{"decompress",          no_argument,            NULL, 'Z'},
218	{NULL,			no_argument,		NULL, 0}
219};
220
221/*
222 * Adds a searching pattern to the internal array.
223 */
224static void
225add_pattern(char *pat, size_t len)
226{
227
228	/* Do not add further pattern is we already match everything */
229	if (matchall)
230	  return;
231
232	/* Check if we can do a shortcut */
233	if (len == 0) {
234		matchall = true;
235		for (unsigned int i = 0; i < patterns; i++) {
236			free(pattern[i].pat);
237		}
238		pattern = grep_realloc(pattern, sizeof(struct pat));
239		pattern[0].pat = NULL;
240		pattern[0].len = 0;
241		patterns = 1;
242		return;
243	}
244	/* Increase size if necessary */
245	if (patterns == pattern_sz) {
246		pattern_sz *= 2;
247		pattern = grep_realloc(pattern, ++pattern_sz *
248		    sizeof(struct pat));
249	}
250	if (len > 0 && pat[len - 1] == '\n')
251		--len;
252	/* pat may not be NUL-terminated */
253	pattern[patterns].pat = grep_malloc(len + 1);
254	memcpy(pattern[patterns].pat, pat, len);
255	pattern[patterns].len = len;
256	pattern[patterns].pat[len] = '\0';
257	++patterns;
258}
259
260/*
261 * Adds a file include/exclude pattern to the internal array.
262 */
263static void
264add_fpattern(const char *pat, int mode)
265{
266
267	/* Increase size if necessary */
268	if (fpatterns == fpattern_sz) {
269		fpattern_sz *= 2;
270		fpattern = grep_realloc(fpattern, ++fpattern_sz *
271		    sizeof(struct epat));
272	}
273	fpattern[fpatterns].pat = grep_strdup(pat);
274	fpattern[fpatterns].mode = mode;
275	++fpatterns;
276}
277
278/*
279 * Adds a directory include/exclude pattern to the internal array.
280 */
281static void
282add_dpattern(const char *pat, int mode)
283{
284
285	/* Increase size if necessary */
286	if (dpatterns == dpattern_sz) {
287		dpattern_sz *= 2;
288		dpattern = grep_realloc(dpattern, ++dpattern_sz *
289		    sizeof(struct epat));
290	}
291	dpattern[dpatterns].pat = grep_strdup(pat);
292	dpattern[dpatterns].mode = mode;
293	++dpatterns;
294}
295
296/*
297 * Reads searching patterns from a file and adds them with add_pattern().
298 */
299static void
300read_patterns(const char *fn)
301{
302	struct stat st;
303	FILE *f;
304	char *line;
305	size_t len;
306
307	if ((f = fopen(fn, "r")) == NULL)
308		err(2, "%s", fn);
309	if ((fstat(fileno(f), &st) == -1) || (S_ISDIR(st.st_mode))) {
310		fclose(f);
311		return;
312	}
313	while ((line = fgetln(f, &len)) != NULL)
314		add_pattern(line, line[0] == '\n' ? 0 : len);
315	if (ferror(f))
316		err(2, "%s", fn);
317	fclose(f);
318}
319
320static inline const char *
321init_color(const char *d)
322{
323	char *c;
324
325	c = getenv("GREP_COLOR");
326	return (c != NULL && c[0] != '\0' ? c : d);
327}
328
329int
330main(int argc, char *argv[])
331{
332	char **aargv, **eargv, *eopts;
333	char *ep;
334	const char *pn;
335	unsigned long long l;
336	unsigned int aargc, eargc, i;
337	int c, lastc, needpattern, newarg, prevoptind;
338
339	setlocale(LC_ALL, "");
340
341#ifndef WITHOUT_NLS
342	catalog = catopen("grep", NL_CAT_LOCALE);
343#endif
344
345	/* Check what is the program name of the binary.  In this
346	   way we can have all the funcionalities in one binary
347	   without the need of scripting and using ugly hacks. */
348	pn = getprogname();
349	if (pn[0] == 'b' && pn[1] == 'z') {
350		filebehave = FILE_BZIP;
351		pn += 2;
352	} else if (pn[0] == 'x' && pn[1] == 'z') {
353		filebehave = FILE_XZ;
354		pn += 2;
355	} else if (pn[0] == 'l' && pn[1] == 'z') {
356		filebehave = FILE_LZMA;
357		pn += 2;
358	} else if (pn[0] == 'z') {
359		filebehave = FILE_GZIP;
360		pn += 1;
361	}
362	switch (pn[0]) {
363	case 'e':
364		grepbehave = GREP_EXTENDED;
365		break;
366	case 'f':
367		grepbehave = GREP_FIXED;
368		break;
369	}
370
371	lastc = '\0';
372	newarg = 1;
373	prevoptind = 1;
374	needpattern = 1;
375
376	eopts = getenv("GREP_OPTIONS");
377
378	/* support for extra arguments in GREP_OPTIONS */
379	eargc = 0;
380	if (eopts != NULL && eopts[0] != '\0') {
381		char *str;
382
383		/* make an estimation of how many extra arguments we have */
384		for (unsigned int j = 0; j < strlen(eopts); j++)
385			if (eopts[j] == ' ')
386				eargc++;
387
388		eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1));
389
390		eargc = 0;
391		/* parse extra arguments */
392		while ((str = strsep(&eopts, " ")) != NULL)
393			if (str[0] != '\0')
394				eargv[eargc++] = grep_strdup(str);
395
396		aargv = (char **)grep_calloc(eargc + argc + 1,
397		    sizeof(char *));
398
399		aargv[0] = argv[0];
400		for (i = 0; i < eargc; i++)
401			aargv[i + 1] = eargv[i];
402		for (int j = 1; j < argc; j++, i++)
403			aargv[i + 1] = argv[j];
404
405		aargc = eargc + argc;
406	} else {
407		aargv = argv;
408		aargc = argc;
409	}
410
411	while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) !=
412	    -1)) {
413		switch (c) {
414		case '0': case '1': case '2': case '3': case '4':
415		case '5': case '6': case '7': case '8': case '9':
416			if (newarg || !isdigit(lastc))
417				Aflag = 0;
418			else if (Aflag > LLONG_MAX / 10) {
419				errno = ERANGE;
420				err(2, NULL);
421			}
422			Aflag = Bflag = (Aflag * 10) + (c - '0');
423			break;
424		case 'C':
425			if (optarg == NULL) {
426				Aflag = Bflag = 2;
427				break;
428			}
429			/* FALLTHROUGH */
430		case 'A':
431			/* FALLTHROUGH */
432		case 'B':
433			errno = 0;
434			l = strtoull(optarg, &ep, 10);
435			if (((errno == ERANGE) && (l == ULLONG_MAX)) ||
436			    ((errno == EINVAL) && (l == 0)))
437				err(2, NULL);
438			else if (ep[0] != '\0') {
439				errno = EINVAL;
440				err(2, NULL);
441			}
442			if (c == 'A')
443				Aflag = l;
444			else if (c == 'B')
445				Bflag = l;
446			else
447				Aflag = Bflag = l;
448			break;
449		case 'a':
450			binbehave = BINFILE_TEXT;
451			break;
452		case 'b':
453			bflag = true;
454			break;
455		case 'c':
456			cflag = true;
457			break;
458		case 'D':
459			if (strcasecmp(optarg, "skip") == 0)
460				devbehave = DEV_SKIP;
461			else if (strcasecmp(optarg, "read") == 0)
462				devbehave = DEV_READ;
463			else
464				errx(2, getstr(3), "--devices");
465			break;
466		case 'd':
467			if (strcasecmp("recurse", optarg) == 0) {
468				Hflag = true;
469				dirbehave = DIR_RECURSE;
470			} else if (strcasecmp("skip", optarg) == 0)
471				dirbehave = DIR_SKIP;
472			else if (strcasecmp("read", optarg) == 0)
473				dirbehave = DIR_READ;
474			else
475				errx(2, getstr(3), "--directories");
476			break;
477		case 'E':
478			grepbehave = GREP_EXTENDED;
479			break;
480		case 'e':
481			{
482				char *token;
483				char *string = optarg;
484
485				while ((token = strsep(&string, "\n")) != NULL)
486					add_pattern(token, strlen(token));
487			}
488			needpattern = 0;
489			break;
490		case 'F':
491			grepbehave = GREP_FIXED;
492			break;
493		case 'f':
494			read_patterns(optarg);
495			needpattern = 0;
496			break;
497		case 'G':
498			grepbehave = GREP_BASIC;
499			break;
500		case 'H':
501			Hflag = true;
502			break;
503		case 'h':
504			Hflag = false;
505			hflag = true;
506			break;
507		case 'I':
508			binbehave = BINFILE_SKIP;
509			break;
510		case 'i':
511		case 'y':
512			iflag =  true;
513			cflags |= REG_ICASE;
514			break;
515		case 'J':
516#ifdef WITHOUT_BZIP2
517			errno = EOPNOTSUPP;
518			err(2, "bzip2 support was disabled at compile-time");
519#endif
520			filebehave = FILE_BZIP;
521			break;
522		case 'L':
523			lflag = false;
524			Lflag = true;
525			break;
526		case 'l':
527			Lflag = false;
528			lflag = true;
529			break;
530		case 'm':
531			mflag = true;
532			errno = 0;
533			mlimit = mcount = strtoll(optarg, &ep, 10);
534			if (((errno == ERANGE) && (mcount == LLONG_MAX)) ||
535			    ((errno == EINVAL) && (mcount == 0)))
536				err(2, NULL);
537			else if (ep[0] != '\0') {
538				errno = EINVAL;
539				err(2, NULL);
540			}
541			break;
542		case 'M':
543			filebehave = FILE_LZMA;
544			break;
545		case 'n':
546			nflag = true;
547			break;
548		case 'O':
549			linkbehave = LINK_EXPLICIT;
550			break;
551		case 'o':
552			oflag = true;
553			cflags &= ~REG_NOSUB;
554			break;
555		case 'p':
556			linkbehave = LINK_SKIP;
557			break;
558		case 'q':
559			qflag = true;
560			break;
561		case 'S':
562			linkbehave = LINK_READ;
563			break;
564		case 'R':
565		case 'r':
566			dirbehave = DIR_RECURSE;
567			Hflag = true;
568			break;
569		case 's':
570			sflag = true;
571			break;
572		case 'U':
573			binbehave = BINFILE_BIN;
574			break;
575		case 'u':
576		case MMAP_OPT:
577			filebehave = FILE_MMAP;
578			break;
579		case 'V':
580			printf(getstr(9), getprogname(), VERSION);
581			exit(0);
582		case 'v':
583			vflag = true;
584			break;
585		case 'w':
586			wflag = true;
587			cflags &= ~REG_NOSUB;
588			break;
589		case 'x':
590			xflag = true;
591			cflags &= ~REG_NOSUB;
592			break;
593		case 'X':
594			filebehave = FILE_XZ;
595			break;
596		case 'Z':
597			filebehave = FILE_GZIP;
598			break;
599		case BIN_OPT:
600			if (strcasecmp("binary", optarg) == 0)
601				binbehave = BINFILE_BIN;
602			else if (strcasecmp("without-match", optarg) == 0)
603				binbehave = BINFILE_SKIP;
604			else if (strcasecmp("text", optarg) == 0)
605				binbehave = BINFILE_TEXT;
606			else
607				errx(2, getstr(3), "--binary-files");
608			break;
609		case COLOR_OPT:
610			color = NULL;
611			if (optarg == NULL || strcasecmp("auto", optarg) == 0 ||
612			    strcasecmp("tty", optarg) == 0 ||
613			    strcasecmp("if-tty", optarg) == 0) {
614				char *term;
615
616				term = getenv("TERM");
617				if (isatty(STDOUT_FILENO) && term != NULL &&
618				    strcasecmp(term, "dumb") != 0)
619					color = init_color("01;31");
620			} else if (strcasecmp("always", optarg) == 0 ||
621			    strcasecmp("yes", optarg) == 0 ||
622			    strcasecmp("force", optarg) == 0) {
623				color = init_color("01;31");
624			} else if (strcasecmp("never", optarg) != 0 &&
625			    strcasecmp("none", optarg) != 0 &&
626			    strcasecmp("no", optarg) != 0)
627				errx(2, getstr(3), "--color");
628			cflags &= ~REG_NOSUB;
629			break;
630		case LABEL_OPT:
631			label = optarg;
632			break;
633		case LINEBUF_OPT:
634			lbflag = true;
635			break;
636		case NULL_OPT:
637			nullflag = true;
638			break;
639		case R_INCLUDE_OPT:
640			finclude = true;
641			add_fpattern(optarg, INCL_PAT);
642			break;
643		case R_EXCLUDE_OPT:
644			fexclude = true;
645			add_fpattern(optarg, EXCL_PAT);
646			break;
647		case R_DINCLUDE_OPT:
648			dinclude = true;
649			add_dpattern(optarg, INCL_PAT);
650			break;
651		case R_DEXCLUDE_OPT:
652			dexclude = true;
653			add_dpattern(optarg, EXCL_PAT);
654			break;
655		case HELP_OPT:
656		default:
657			usage();
658		}
659		lastc = c;
660		newarg = optind != prevoptind;
661		prevoptind = optind;
662	}
663	aargc -= optind;
664	aargv += optind;
665
666	/* Empty pattern file matches nothing */
667	if (!needpattern && (patterns == 0))
668		exit(1);
669
670	/* Fail if we don't have any pattern */
671	if (aargc == 0 && needpattern)
672		usage();
673
674	/* Process patterns from command line */
675	if (aargc != 0 && needpattern) {
676		char *token;
677		char *string = *aargv;
678
679		while ((token = strsep(&string, "\n")) != NULL)
680			add_pattern(token, strlen(token));
681		--aargc;
682		++aargv;
683	}
684
685	switch (grepbehave) {
686	case GREP_BASIC:
687		break;
688	case GREP_FIXED:
689		/* XXX: header mess, REG_LITERAL not defined in gnu/regex.h */
690		cflags |= 0020;
691		break;
692	case GREP_EXTENDED:
693		cflags |= REG_EXTENDED;
694		break;
695	default:
696		/* NOTREACHED */
697		usage();
698	}
699
700	fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern));
701	r_pattern = grep_calloc(patterns, sizeof(*r_pattern));
702
703	/* Check if cheating is allowed (always is for fgrep). */
704	for (i = 0; i < patterns; ++i) {
705		if (fastncomp(&fg_pattern[i], pattern[i].pat,
706		    pattern[i].len, cflags) != 0) {
707			/* Fall back to full regex library */
708			c = regcomp(&r_pattern[i], pattern[i].pat, cflags);
709			if (c != 0) {
710				regerror(c, &r_pattern[i], re_error,
711				    RE_ERROR_BUF);
712				errx(2, "%s", re_error);
713			}
714		}
715	}
716
717	if (lbflag)
718		setlinebuf(stdout);
719
720	if ((aargc == 0 || aargc == 1) && !Hflag)
721		hflag = true;
722
723	if (aargc == 0)
724		exit(!procfile("-"));
725
726	if (dirbehave == DIR_RECURSE)
727		c = grep_tree(aargv);
728	else
729		for (c = 0; aargc--; ++aargv) {
730			if ((finclude || fexclude) && !file_matching(*aargv))
731				continue;
732			c+= procfile(*aargv);
733		}
734
735#ifndef WITHOUT_NLS
736	catclose(catalog);
737#endif
738
739	/* Find out the correct return value according to the
740	   results and the command line option. */
741	exit(c ? (file_err ? (qflag ? 0 : 2) : 0) : (file_err ? 2 : 1));
742}
743