xref: /freebsd/usr.bin/grep/grep.c (revision 7778ab7e0cc22f0824eb1d1047a7ef8b4785267a)
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[0] != '\0' ? 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 && eopts[0] != '\0') {
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 			if (str[0] != '\0')
377 				eargv[eargc++] = grep_strdup(str);
378 
379 		aargv = (char **)grep_calloc(eargc + argc + 1,
380 		    sizeof(char *));
381 
382 		aargv[0] = argv[0];
383 		for (i = 0; i < eargc; i++)
384 			aargv[i + 1] = eargv[i];
385 		for (int j = 1; j < argc; j++, i++)
386 			aargv[i + 1] = argv[j];
387 
388 		aargc = eargc + argc;
389 	} else {
390 		aargv = argv;
391 		aargc = argc;
392 	}
393 
394 	while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) !=
395 	    -1)) {
396 		switch (c) {
397 		case '0': case '1': case '2': case '3': case '4':
398 		case '5': case '6': case '7': case '8': case '9':
399 			if (newarg || !isdigit(lastc))
400 				Aflag = 0;
401 			else if (Aflag > LLONG_MAX / 10) {
402 				errno = ERANGE;
403 				err(2, NULL);
404 			}
405 			Aflag = Bflag = (Aflag * 10) + (c - '0');
406 			break;
407 		case 'C':
408 			if (optarg == NULL) {
409 				Aflag = Bflag = 2;
410 				break;
411 			}
412 			/* FALLTHROUGH */
413 		case 'A':
414 			/* FALLTHROUGH */
415 		case 'B':
416 			errno = 0;
417 			l = strtoull(optarg, &ep, 10);
418 			if (((errno == ERANGE) && (l == ULLONG_MAX)) ||
419 			    ((errno == EINVAL) && (l == 0)))
420 				err(2, NULL);
421 			else if (ep[0] != '\0') {
422 				errno = EINVAL;
423 				err(2, NULL);
424 			}
425 			if (c == 'A')
426 				Aflag = l;
427 			else if (c == 'B')
428 				Bflag = l;
429 			else
430 				Aflag = Bflag = l;
431 			break;
432 		case 'a':
433 			binbehave = BINFILE_TEXT;
434 			break;
435 		case 'b':
436 			bflag = true;
437 			break;
438 		case 'c':
439 			cflag = true;
440 			break;
441 		case 'D':
442 			if (strcasecmp(optarg, "skip") == 0)
443 				devbehave = DEV_SKIP;
444 			else if (strcasecmp(optarg, "read") == 0)
445 				devbehave = DEV_READ;
446 			else
447 				errx(2, getstr(3), "--devices");
448 			break;
449 		case 'd':
450 			if (strcasecmp("recurse", optarg) == 0) {
451 				Hflag = true;
452 				dirbehave = DIR_RECURSE;
453 			} else if (strcasecmp("skip", optarg) == 0)
454 				dirbehave = DIR_SKIP;
455 			else if (strcasecmp("read", optarg) == 0)
456 				dirbehave = DIR_READ;
457 			else
458 				errx(2, getstr(3), "--directories");
459 			break;
460 		case 'E':
461 			grepbehave = GREP_EXTENDED;
462 			break;
463 		case 'e':
464 			add_pattern(optarg, strlen(optarg));
465 			needpattern = 0;
466 			break;
467 		case 'F':
468 			grepbehave = GREP_FIXED;
469 			break;
470 		case 'f':
471 			read_patterns(optarg);
472 			needpattern = 0;
473 			break;
474 		case 'G':
475 			grepbehave = GREP_BASIC;
476 			break;
477 		case 'H':
478 			Hflag = true;
479 			break;
480 		case 'h':
481 			Hflag = false;
482 			hflag = true;
483 			break;
484 		case 'I':
485 			binbehave = BINFILE_SKIP;
486 			break;
487 		case 'i':
488 		case 'y':
489 			iflag =  true;
490 			cflags |= REG_ICASE;
491 			break;
492 		case 'J':
493 			filebehave = FILE_BZIP;
494 			break;
495 		case 'L':
496 			lflag = false;
497 			Lflag = true;
498 			break;
499 		case 'l':
500 			Lflag = false;
501 			lflag = true;
502 			break;
503 		case 'm':
504 			mflag = true;
505 			errno = 0;
506 			mcount = strtoull(optarg, &ep, 10);
507 			if (((errno == ERANGE) && (mcount == ULLONG_MAX)) ||
508 			    ((errno == EINVAL) && (mcount == 0)))
509 				err(2, NULL);
510 			else if (ep[0] != '\0') {
511 				errno = EINVAL;
512 				err(2, NULL);
513 			}
514 			break;
515 		case 'n':
516 			nflag = true;
517 			break;
518 		case 'O':
519 			linkbehave = LINK_EXPLICIT;
520 			break;
521 		case 'o':
522 			oflag = true;
523 			cflags &= ~REG_NOSUB;
524 			break;
525 		case 'p':
526 			linkbehave = LINK_SKIP;
527 			break;
528 		case 'q':
529 			qflag = true;
530 			break;
531 		case 'S':
532 			linkbehave = LINK_READ;
533 			break;
534 		case 'R':
535 		case 'r':
536 			dirbehave = DIR_RECURSE;
537 			Hflag = true;
538 			break;
539 		case 's':
540 			sflag = true;
541 			break;
542 		case 'U':
543 			binbehave = BINFILE_BIN;
544 			break;
545 		case 'u':
546 		case MMAP_OPT:
547 			/* noop, compatibility */
548 			break;
549 		case 'V':
550 			printf(getstr(9), __progname, VERSION);
551 			exit(0);
552 		case 'v':
553 			vflag = true;
554 			break;
555 		case 'w':
556 			wflag = true;
557 			cflags &= ~REG_NOSUB;
558 			break;
559 		case 'x':
560 			xflag = true;
561 			cflags &= ~REG_NOSUB;
562 			break;
563 		case 'Z':
564 			filebehave = FILE_GZIP;
565 			break;
566 		case BIN_OPT:
567 			if (strcasecmp("binary", optarg) == 0)
568 				binbehave = BINFILE_BIN;
569 			else if (strcasecmp("without-match", optarg) == 0)
570 				binbehave = BINFILE_SKIP;
571 			else if (strcasecmp("text", optarg) == 0)
572 				binbehave = BINFILE_TEXT;
573 			else
574 				errx(2, getstr(3), "--binary-files");
575 			break;
576 		case COLOR_OPT:
577 			color = NULL;
578 			if (optarg == NULL || strcasecmp("auto", optarg) == 0 ||
579 			    strcasecmp("tty", optarg) == 0 ||
580 			    strcasecmp("if-tty", optarg) == 0) {
581 				char *term;
582 
583 				term = getenv("TERM");
584 				if (isatty(STDOUT_FILENO) && term != NULL &&
585 				    strcasecmp(term, "dumb") != 0)
586 					color = init_color("01;31");
587 			} else if (strcasecmp("always", optarg) == 0 ||
588 			    strcasecmp("yes", optarg) == 0 ||
589 			    strcasecmp("force", optarg) == 0) {
590 				color = init_color("01;31");
591 			} else if (strcasecmp("never", optarg) != 0 &&
592 			    strcasecmp("none", optarg) != 0 &&
593 			    strcasecmp("no", optarg) != 0)
594 				errx(2, getstr(3), "--color");
595 			cflags &= ~REG_NOSUB;
596 			break;
597 		case LABEL_OPT:
598 			label = optarg;
599 			break;
600 		case LINEBUF_OPT:
601 			lbflag = true;
602 			break;
603 		case NULL_OPT:
604 			nullflag = true;
605 			break;
606 		case R_INCLUDE_OPT:
607 			finclude = true;
608 			add_fpattern(optarg, INCL_PAT);
609 			break;
610 		case R_EXCLUDE_OPT:
611 			fexclude = true;
612 			add_fpattern(optarg, EXCL_PAT);
613 			break;
614 		case R_DINCLUDE_OPT:
615 			dinclude = true;
616 			add_dpattern(optarg, INCL_PAT);
617 			break;
618 		case R_DEXCLUDE_OPT:
619 			dexclude = true;
620 			add_dpattern(optarg, EXCL_PAT);
621 			break;
622 		case HELP_OPT:
623 		default:
624 			usage();
625 		}
626 		lastc = c;
627 		newarg = optind != prevoptind;
628 		prevoptind = optind;
629 	}
630 	aargc -= optind;
631 	aargv += optind;
632 
633 	/* Fail if we don't have any pattern */
634 	if (aargc == 0 && needpattern)
635 		usage();
636 
637 	/* Process patterns from command line */
638 	if (aargc != 0 && needpattern) {
639 		add_pattern(*aargv, strlen(*aargv));
640 		--aargc;
641 		++aargv;
642 	}
643 
644 	switch (grepbehave) {
645 	case GREP_FIXED:
646 	case GREP_BASIC:
647 		break;
648 	case GREP_EXTENDED:
649 		cflags |= REG_EXTENDED;
650 		break;
651 	default:
652 		/* NOTREACHED */
653 		usage();
654 	}
655 
656 	fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern));
657 	r_pattern = grep_calloc(patterns, sizeof(*r_pattern));
658 /*
659  * XXX: fgrepcomp() and fastcomp() are workarounds for regexec() performance.
660  * Optimizations should be done there.
661  */
662 		/* Check if cheating is allowed (always is for fgrep). */
663 	if (grepbehave == GREP_FIXED) {
664 		for (i = 0; i < patterns; ++i)
665 			fgrepcomp(&fg_pattern[i], pattern[i]);
666 	} else {
667 		for (i = 0; i < patterns; ++i) {
668 			if (fastcomp(&fg_pattern[i], pattern[i])) {
669 				/* Fall back to full regex library */
670 				c = regcomp(&r_pattern[i], pattern[i], cflags);
671 				if (c != 0) {
672 					regerror(c, &r_pattern[i], re_error,
673 					    RE_ERROR_BUF);
674 					errx(2, "%s", re_error);
675 				}
676 			}
677 		}
678 	}
679 
680 	if (lbflag)
681 		setlinebuf(stdout);
682 
683 	if ((aargc == 0 || aargc == 1) && !Hflag)
684 		hflag = true;
685 
686 	if (aargc == 0)
687 		exit(!procfile("-"));
688 
689 	if (dirbehave == DIR_RECURSE)
690 		c = grep_tree(aargv);
691 	else
692 		for (c = 0; aargc--; ++aargv) {
693 			if ((finclude || fexclude) && !file_matching(*aargv))
694 				continue;
695 			c+= procfile(*aargv);
696 		}
697 
698 #ifndef WITHOUT_NLS
699 	catclose(catalog);
700 #endif
701 
702 	/* Find out the correct return value according to the
703 	   results and the command line option. */
704 	exit(c ? (notfound ? (qflag ? 0 : 2) : 0) : (notfound ? 2 : 1));
705 }
706