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