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