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;
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			{
483				char *token;
484				char *string = optarg;
485
486				while ((token = strsep(&string, "\n")) != NULL)
487					add_pattern(token, strlen(token));
488			}
489			needpattern = 0;
490			break;
491		case 'F':
492			grepbehave = GREP_FIXED;
493			break;
494		case 'f':
495			read_patterns(optarg);
496			needpattern = 0;
497			break;
498		case 'G':
499			grepbehave = GREP_BASIC;
500			break;
501		case 'H':
502			Hflag = true;
503			break;
504		case 'h':
505			Hflag = false;
506			hflag = true;
507			break;
508		case 'I':
509			binbehave = BINFILE_SKIP;
510			break;
511		case 'i':
512		case 'y':
513			iflag =  true;
514			cflags |= REG_ICASE;
515			break;
516		case 'J':
517#ifdef WITHOUT_BZIP2
518			errno = EOPNOTSUPP;
519			err(2, "bzip2 support was disabled at compile-time");
520#endif
521			filebehave = FILE_BZIP;
522			break;
523		case 'L':
524			lflag = false;
525			Lflag = true;
526			break;
527		case 'l':
528			Lflag = false;
529			lflag = true;
530			break;
531		case 'm':
532			mflag = true;
533			errno = 0;
534			mlimit = mcount = strtoll(optarg, &ep, 10);
535			if (((errno == ERANGE) && (mcount == LLONG_MAX)) ||
536			    ((errno == EINVAL) && (mcount == 0)))
537				err(2, NULL);
538			else if (ep[0] != '\0') {
539				errno = EINVAL;
540				err(2, NULL);
541			}
542			break;
543		case 'M':
544			filebehave = FILE_LZMA;
545			break;
546		case 'n':
547			nflag = true;
548			break;
549		case 'O':
550			linkbehave = LINK_EXPLICIT;
551			break;
552		case 'o':
553			oflag = true;
554			cflags &= ~REG_NOSUB;
555			break;
556		case 'p':
557			linkbehave = LINK_SKIP;
558			break;
559		case 'q':
560			qflag = true;
561			break;
562		case 'S':
563			linkbehave = LINK_READ;
564			break;
565		case 'R':
566		case 'r':
567			dirbehave = DIR_RECURSE;
568			Hflag = true;
569			break;
570		case 's':
571			sflag = true;
572			break;
573		case 'U':
574			binbehave = BINFILE_BIN;
575			break;
576		case 'u':
577		case MMAP_OPT:
578			filebehave = FILE_MMAP;
579			break;
580		case 'V':
581			printf(getstr(9), getprogname(), VERSION);
582			exit(0);
583		case 'v':
584			vflag = true;
585			break;
586		case 'w':
587			wflag = true;
588			cflags &= ~REG_NOSUB;
589			break;
590		case 'x':
591			xflag = true;
592			cflags &= ~REG_NOSUB;
593			break;
594		case 'X':
595			filebehave = FILE_XZ;
596			break;
597		case 'Z':
598			filebehave = FILE_GZIP;
599			break;
600		case BIN_OPT:
601			if (strcasecmp("binary", optarg) == 0)
602				binbehave = BINFILE_BIN;
603			else if (strcasecmp("without-match", optarg) == 0)
604				binbehave = BINFILE_SKIP;
605			else if (strcasecmp("text", optarg) == 0)
606				binbehave = BINFILE_TEXT;
607			else
608				errx(2, getstr(3), "--binary-files");
609			break;
610		case COLOR_OPT:
611			color = NULL;
612			if (optarg == NULL || strcasecmp("auto", optarg) == 0 ||
613			    strcasecmp("tty", optarg) == 0 ||
614			    strcasecmp("if-tty", optarg) == 0) {
615				char *term;
616
617				term = getenv("TERM");
618				if (isatty(STDOUT_FILENO) && term != NULL &&
619				    strcasecmp(term, "dumb") != 0)
620					color = init_color("01;31");
621			} else if (strcasecmp("always", optarg) == 0 ||
622			    strcasecmp("yes", optarg) == 0 ||
623			    strcasecmp("force", optarg) == 0) {
624				color = init_color("01;31");
625			} else if (strcasecmp("never", optarg) != 0 &&
626			    strcasecmp("none", optarg) != 0 &&
627			    strcasecmp("no", optarg) != 0)
628				errx(2, getstr(3), "--color");
629			cflags &= ~REG_NOSUB;
630			break;
631		case LABEL_OPT:
632			label = optarg;
633			break;
634		case LINEBUF_OPT:
635			lbflag = true;
636			break;
637		case NULL_OPT:
638			nullflag = true;
639			break;
640		case R_INCLUDE_OPT:
641			finclude = true;
642			add_fpattern(optarg, INCL_PAT);
643			break;
644		case R_EXCLUDE_OPT:
645			fexclude = true;
646			add_fpattern(optarg, EXCL_PAT);
647			break;
648		case R_DINCLUDE_OPT:
649			dinclude = true;
650			add_dpattern(optarg, INCL_PAT);
651			break;
652		case R_DEXCLUDE_OPT:
653			dexclude = true;
654			add_dpattern(optarg, EXCL_PAT);
655			break;
656		case HELP_OPT:
657		default:
658			usage();
659		}
660		lastc = c;
661		newarg = optind != prevoptind;
662		prevoptind = optind;
663	}
664	aargc -= optind;
665	aargv += optind;
666
667	/* Empty pattern file matches nothing */
668	if (!needpattern && (patterns == 0))
669		exit(1);
670
671	/* Fail if we don't have any pattern */
672	if (aargc == 0 && needpattern)
673		usage();
674
675	/* Process patterns from command line */
676	if (aargc != 0 && needpattern) {
677		char *token;
678		char *string = *aargv;
679
680		while ((token = strsep(&string, "\n")) != NULL)
681			add_pattern(token, strlen(token));
682		--aargc;
683		++aargv;
684	}
685
686	switch (grepbehave) {
687	case GREP_BASIC:
688		break;
689	case GREP_FIXED:
690		/* XXX: header mess, REG_LITERAL not defined in gnu/regex.h */
691		cflags |= 0020;
692		break;
693	case GREP_EXTENDED:
694		cflags |= REG_EXTENDED;
695		break;
696	default:
697		/* NOTREACHED */
698		usage();
699	}
700
701	fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern));
702	r_pattern = grep_calloc(patterns, sizeof(*r_pattern));
703
704	/* Check if cheating is allowed (always is for fgrep). */
705	for (i = 0; i < patterns; ++i) {
706		if (fastncomp(&fg_pattern[i], pattern[i].pat,
707		    pattern[i].len, cflags) != 0) {
708			/* Fall back to full regex library */
709			c = regcomp(&r_pattern[i], pattern[i].pat, cflags);
710			if (c != 0) {
711				regerror(c, &r_pattern[i], re_error,
712				    RE_ERROR_BUF);
713				errx(2, "%s", re_error);
714			}
715		}
716	}
717
718	if (lbflag)
719		setlinebuf(stdout);
720
721	if ((aargc == 0 || aargc == 1) && !Hflag)
722		hflag = true;
723
724	if (aargc == 0)
725		exit(!procfile("-"));
726
727	if (dirbehave == DIR_RECURSE)
728		c = grep_tree(aargv);
729	else
730		for (c = 0; aargc--; ++aargv) {
731			if ((finclude || fexclude) && !file_matching(*aargv))
732				continue;
733			c+= procfile(*aargv);
734		}
735
736#ifndef WITHOUT_NLS
737	catclose(catalog);
738#endif
739
740	/* Find out the correct return value according to the
741	   results and the command line option. */
742	exit(c ? (file_err ? (qflag ? 0 : 2) : 0) : (file_err ? 2 : 1));
743}
744