grep.c revision 226261
1/*	$NetBSD: grep.c,v 1.4 2011/02/16 01:31:33 joerg Exp $	*/
2/* 	$FreeBSD: stable/9/usr.bin/grep/grep.c 226261 2011-10-11 15:03:42Z gabor $	*/
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: stable/9/usr.bin/grep/grep.c 226261 2011-10-11 15:03:42Z gabor $");
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 */
110bool	 nflag;		/* -n: show line numbers in front of matching lines */
111bool	 oflag;		/* -o: print only matching part */
112bool	 qflag;		/* -q: quiet mode (don't output anything) */
113bool	 sflag;		/* -s: silent mode (ignore errors) */
114bool	 vflag;		/* -v: only show non-matching lines */
115bool	 wflag;		/* -w: pattern must start and end on word boundaries */
116bool	 xflag;		/* -x: pattern must match entire line */
117bool	 lbflag;	/* --line-buffered */
118bool	 nullflag;	/* --null */
119char	*label;		/* --label */
120const char *color;	/* --color */
121int	 grepbehave = GREP_BASIC;	/* -EFGP: type of the regex */
122int	 binbehave = BINFILE_BIN;	/* -aIU: handling of binary files */
123int	 filebehave = FILE_STDIO;	/* -JZ: normal, gzip or bzip2 file */
124int	 devbehave = DEV_READ;		/* -D: handling of devices */
125int	 dirbehave = DIR_READ;		/* -dRr: handling of directories */
126int	 linkbehave = LINK_READ;	/* -OpS: handling of symlinks */
127
128bool	 dexclude, dinclude;	/* --exclude-dir and --include-dir */
129bool	 fexclude, finclude;	/* --exclude and --include */
130
131enum {
132	BIN_OPT = CHAR_MAX + 1,
133	COLOR_OPT,
134	HELP_OPT,
135	MMAP_OPT,
136	LINEBUF_OPT,
137	LABEL_OPT,
138	NULL_OPT,
139	R_EXCLUDE_OPT,
140	R_INCLUDE_OPT,
141	R_DEXCLUDE_OPT,
142	R_DINCLUDE_OPT
143};
144
145static inline const char	*init_color(const char *);
146
147/* Housekeeping */
148bool	 first = true;	/* flag whether we are processing the first match */
149bool	 prev;		/* flag whether or not the previous line matched */
150int	 tail;		/* lines left to print */
151bool	 notfound;	/* file not found */
152
153extern char	*__progname;
154
155/*
156 * Prints usage information and returns 2.
157 */
158static void
159usage(void)
160{
161	fprintf(stderr, getstr(4), __progname);
162	fprintf(stderr, "%s", getstr(5));
163	fprintf(stderr, "%s", getstr(5));
164	fprintf(stderr, "%s", getstr(6));
165	fprintf(stderr, "%s", getstr(7));
166	exit(2);
167}
168
169static const char	*optstr = "0123456789A:B:C:D:EFGHIJMLOPSRUVZabcd:e:f:hilm:nopqrsuvwxXy";
170
171struct option long_options[] =
172{
173	{"binary-files",	required_argument,	NULL, BIN_OPT},
174	{"help",		no_argument,		NULL, HELP_OPT},
175	{"mmap",		no_argument,		NULL, MMAP_OPT},
176	{"line-buffered",	no_argument,		NULL, LINEBUF_OPT},
177	{"label",		required_argument,	NULL, LABEL_OPT},
178	{"null",		no_argument,		NULL, NULL_OPT},
179	{"color",		optional_argument,	NULL, COLOR_OPT},
180	{"colour",		optional_argument,	NULL, COLOR_OPT},
181	{"exclude",		required_argument,	NULL, R_EXCLUDE_OPT},
182	{"include",		required_argument,	NULL, R_INCLUDE_OPT},
183	{"exclude-dir",		required_argument,	NULL, R_DEXCLUDE_OPT},
184	{"include-dir",		required_argument,	NULL, R_DINCLUDE_OPT},
185	{"after-context",	required_argument,	NULL, 'A'},
186	{"text",		no_argument,		NULL, 'a'},
187	{"before-context",	required_argument,	NULL, 'B'},
188	{"byte-offset",		no_argument,		NULL, 'b'},
189	{"context",		optional_argument,	NULL, 'C'},
190	{"count",		no_argument,		NULL, 'c'},
191	{"devices",		required_argument,	NULL, 'D'},
192        {"directories",		required_argument,	NULL, 'd'},
193	{"extended-regexp",	no_argument,		NULL, 'E'},
194	{"regexp",		required_argument,	NULL, 'e'},
195	{"fixed-strings",	no_argument,		NULL, 'F'},
196	{"file",		required_argument,	NULL, 'f'},
197	{"basic-regexp",	no_argument,		NULL, 'G'},
198	{"no-filename",		no_argument,		NULL, 'h'},
199	{"with-filename",	no_argument,		NULL, 'H'},
200	{"ignore-case",		no_argument,		NULL, 'i'},
201	{"bz2decompress",	no_argument,		NULL, 'J'},
202	{"files-with-matches",	no_argument,		NULL, 'l'},
203	{"files-without-match", no_argument,            NULL, 'L'},
204	{"max-count",		required_argument,	NULL, 'm'},
205	{"lzma",		no_argument,		NULL, 'M'},
206	{"line-number",		no_argument,		NULL, 'n'},
207	{"only-matching",	no_argument,		NULL, 'o'},
208	{"quiet",		no_argument,		NULL, 'q'},
209	{"silent",		no_argument,		NULL, 'q'},
210	{"recursive",		no_argument,		NULL, 'r'},
211	{"no-messages",		no_argument,		NULL, 's'},
212	{"binary",		no_argument,		NULL, 'U'},
213	{"unix-byte-offsets",	no_argument,		NULL, 'u'},
214	{"invert-match",	no_argument,		NULL, 'v'},
215	{"version",		no_argument,		NULL, 'V'},
216	{"word-regexp",		no_argument,		NULL, 'w'},
217	{"line-regexp",		no_argument,		NULL, 'x'},
218	{"xz",			no_argument,		NULL, 'X'},
219	{"decompress",          no_argument,            NULL, 'Z'},
220	{NULL,			no_argument,		NULL, 0}
221};
222
223/*
224 * Adds a searching pattern to the internal array.
225 */
226static void
227add_pattern(char *pat, size_t len)
228{
229
230	/* Do not add further pattern is we already match everything */
231	if (matchall)
232	  return;
233
234	/* Check if we can do a shortcut */
235	if (len == 0) {
236		matchall = true;
237		for (unsigned int i = 0; i < patterns; i++) {
238			free(pattern[i].pat);
239		}
240		pattern = grep_realloc(pattern, sizeof(struct pat));
241		pattern[0].pat = NULL;
242		pattern[0].len = 0;
243		patterns = 1;
244		return;
245	}
246	/* Increase size if necessary */
247	if (patterns == pattern_sz) {
248		pattern_sz *= 2;
249		pattern = grep_realloc(pattern, ++pattern_sz *
250		    sizeof(struct pat));
251	}
252	if (len > 0 && pat[len - 1] == '\n')
253		--len;
254	/* pat may not be NUL-terminated */
255	pattern[patterns].pat = grep_malloc(len + 1);
256	memcpy(pattern[patterns].pat, pat, len);
257	pattern[patterns].len = len;
258	pattern[patterns].pat[len] = '\0';
259	++patterns;
260}
261
262/*
263 * Adds a file include/exclude pattern to the internal array.
264 */
265static void
266add_fpattern(const char *pat, int mode)
267{
268
269	/* Increase size if necessary */
270	if (fpatterns == fpattern_sz) {
271		fpattern_sz *= 2;
272		fpattern = grep_realloc(fpattern, ++fpattern_sz *
273		    sizeof(struct epat));
274	}
275	fpattern[fpatterns].pat = grep_strdup(pat);
276	fpattern[fpatterns].mode = mode;
277	++fpatterns;
278}
279
280/*
281 * Adds a directory include/exclude pattern to the internal array.
282 */
283static void
284add_dpattern(const char *pat, int mode)
285{
286
287	/* Increase size if necessary */
288	if (dpatterns == dpattern_sz) {
289		dpattern_sz *= 2;
290		dpattern = grep_realloc(dpattern, ++dpattern_sz *
291		    sizeof(struct epat));
292	}
293	dpattern[dpatterns].pat = grep_strdup(pat);
294	dpattern[dpatterns].mode = mode;
295	++dpatterns;
296}
297
298/*
299 * Reads searching patterns from a file and adds them with add_pattern().
300 */
301static void
302read_patterns(const char *fn)
303{
304	struct stat st;
305	FILE *f;
306	char *line;
307	size_t len;
308
309	if ((f = fopen(fn, "r")) == NULL)
310		err(2, "%s", fn);
311	if ((fstat(fileno(f), &st) == -1) || (S_ISDIR(st.st_mode))) {
312		fclose(f);
313		return;
314	}
315        while ((line = fgetln(f, &len)) != NULL)
316		add_pattern(line, line[0] == '\n' ? 0 : len);
317	if (ferror(f))
318		err(2, "%s", fn);
319	fclose(f);
320}
321
322static inline const char *
323init_color(const char *d)
324{
325	char *c;
326
327	c = getenv("GREP_COLOR");
328	return (c != NULL && c[0] != '\0' ? c : d);
329}
330
331int
332main(int argc, char *argv[])
333{
334	char **aargv, **eargv, *eopts;
335	char *pn, *ep;
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 = __progname;
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			filebehave = FILE_BZIP;
512			break;
513		case 'L':
514			lflag = false;
515			Lflag = true;
516			break;
517		case 'l':
518			Lflag = false;
519			lflag = true;
520			break;
521		case 'm':
522			mflag = true;
523			errno = 0;
524			mcount = strtoll(optarg, &ep, 10);
525			if (((errno == ERANGE) && (mcount == LLONG_MAX)) ||
526			    ((errno == EINVAL) && (mcount == 0)))
527				err(2, NULL);
528			else if (ep[0] != '\0') {
529				errno = EINVAL;
530				err(2, NULL);
531			}
532			break;
533		case 'M':
534			filebehave = FILE_LZMA;
535			break;
536		case 'n':
537			nflag = true;
538			break;
539		case 'O':
540			linkbehave = LINK_EXPLICIT;
541			break;
542		case 'o':
543			oflag = true;
544			cflags &= ~REG_NOSUB;
545			break;
546		case 'p':
547			linkbehave = LINK_SKIP;
548			break;
549		case 'q':
550			qflag = true;
551			break;
552		case 'S':
553			linkbehave = LINK_READ;
554			break;
555		case 'R':
556		case 'r':
557			dirbehave = DIR_RECURSE;
558			Hflag = true;
559			break;
560		case 's':
561			sflag = true;
562			break;
563		case 'U':
564			binbehave = BINFILE_BIN;
565			break;
566		case 'u':
567		case MMAP_OPT:
568			filebehave = FILE_MMAP;
569			break;
570		case 'V':
571			printf(getstr(9), __progname, VERSION);
572			exit(0);
573		case 'v':
574			vflag = true;
575			break;
576		case 'w':
577			wflag = true;
578			cflags &= ~REG_NOSUB;
579			break;
580		case 'x':
581			xflag = true;
582			cflags &= ~REG_NOSUB;
583			break;
584		case 'X':
585			filebehave = FILE_XZ;
586			break;
587		case 'Z':
588			filebehave = FILE_GZIP;
589			break;
590		case BIN_OPT:
591			if (strcasecmp("binary", optarg) == 0)
592				binbehave = BINFILE_BIN;
593			else if (strcasecmp("without-match", optarg) == 0)
594				binbehave = BINFILE_SKIP;
595			else if (strcasecmp("text", optarg) == 0)
596				binbehave = BINFILE_TEXT;
597			else
598				errx(2, getstr(3), "--binary-files");
599			break;
600		case COLOR_OPT:
601			color = NULL;
602			if (optarg == NULL || strcasecmp("auto", optarg) == 0 ||
603			    strcasecmp("tty", optarg) == 0 ||
604			    strcasecmp("if-tty", optarg) == 0) {
605				char *term;
606
607				term = getenv("TERM");
608				if (isatty(STDOUT_FILENO) && term != NULL &&
609				    strcasecmp(term, "dumb") != 0)
610					color = init_color("01;31");
611			} else if (strcasecmp("always", optarg) == 0 ||
612			    strcasecmp("yes", optarg) == 0 ||
613			    strcasecmp("force", optarg) == 0) {
614				color = init_color("01;31");
615			} else if (strcasecmp("never", optarg) != 0 &&
616			    strcasecmp("none", optarg) != 0 &&
617			    strcasecmp("no", optarg) != 0)
618				errx(2, getstr(3), "--color");
619			cflags &= ~REG_NOSUB;
620			break;
621		case LABEL_OPT:
622			label = optarg;
623			break;
624		case LINEBUF_OPT:
625			lbflag = true;
626			break;
627		case NULL_OPT:
628			nullflag = true;
629			break;
630		case R_INCLUDE_OPT:
631			finclude = true;
632			add_fpattern(optarg, INCL_PAT);
633			break;
634		case R_EXCLUDE_OPT:
635			fexclude = true;
636			add_fpattern(optarg, EXCL_PAT);
637			break;
638		case R_DINCLUDE_OPT:
639			dinclude = true;
640			add_dpattern(optarg, INCL_PAT);
641			break;
642		case R_DEXCLUDE_OPT:
643			dexclude = true;
644			add_dpattern(optarg, EXCL_PAT);
645			break;
646		case HELP_OPT:
647		default:
648			usage();
649		}
650		lastc = c;
651		newarg = optind != prevoptind;
652		prevoptind = optind;
653	}
654	aargc -= optind;
655	aargv += optind;
656
657	/* Empty pattern file matches nothing */
658	if (!needpattern && (patterns == 0))
659		exit(1);
660
661	/* Fail if we don't have any pattern */
662	if (aargc == 0 && needpattern)
663		usage();
664
665	/* Process patterns from command line */
666	if (aargc != 0 && needpattern) {
667		add_pattern(*aargv, strlen(*aargv));
668		--aargc;
669		++aargv;
670	}
671
672	switch (grepbehave) {
673	case GREP_BASIC:
674		break;
675	case GREP_FIXED:
676		/* XXX: header mess, REG_LITERAL not defined in gnu/regex.h */
677		cflags |= 0020;
678		break;
679	case GREP_EXTENDED:
680		cflags |= REG_EXTENDED;
681		break;
682	default:
683		/* NOTREACHED */
684		usage();
685	}
686
687	fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern));
688	r_pattern = grep_calloc(patterns, sizeof(*r_pattern));
689
690	/* Check if cheating is allowed (always is for fgrep). */
691	for (i = 0; i < patterns; ++i) {
692		if (fastncomp(&fg_pattern[i], pattern[i].pat,
693		    pattern[i].len, cflags) != 0) {
694			/* Fall back to full regex library */
695			c = regcomp(&r_pattern[i], pattern[i].pat, cflags);
696			if (c != 0) {
697				regerror(c, &r_pattern[i], re_error,
698				    RE_ERROR_BUF);
699				errx(2, "%s", re_error);
700			}
701		}
702	}
703
704	if (lbflag)
705		setlinebuf(stdout);
706
707	if ((aargc == 0 || aargc == 1) && !Hflag)
708		hflag = true;
709
710	if (aargc == 0)
711		exit(!procfile("-"));
712
713	if (dirbehave == DIR_RECURSE)
714		c = grep_tree(aargv);
715	else
716		for (c = 0; aargc--; ++aargv) {
717			if ((finclude || fexclude) && !file_matching(*aargv))
718				continue;
719			c+= procfile(*aargv);
720		}
721
722#ifndef WITHOUT_NLS
723	catclose(catalog);
724#endif
725
726	/* Find out the correct return value according to the
727	   results and the command line option. */
728	exit(c ? (notfound ? (qflag ? 0 : 2) : 0) : (notfound ? 2 : 1));
729}
730