xref: /freebsd/usr.bin/grep/grep.c (revision 891b8ed4672a213bbe6f3f10522eeadb34d01b76)
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 = 0;
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 			break;
523 		case 'p':
524 			linkbehave = LINK_SKIP;
525 			break;
526 		case 'q':
527 			qflag = true;
528 			break;
529 		case 'S':
530 			linkbehave = LINK_READ;
531 			break;
532 		case 'R':
533 		case 'r':
534 			dirbehave = DIR_RECURSE;
535 			Hflag = true;
536 			break;
537 		case 's':
538 			sflag = true;
539 			break;
540 		case 'U':
541 			binbehave = BINFILE_BIN;
542 			break;
543 		case 'u':
544 		case MMAP_OPT:
545 			/* noop, compatibility */
546 			break;
547 		case 'V':
548 			printf(getstr(9), __progname, VERSION);
549 			exit(0);
550 		case 'v':
551 			vflag = true;
552 			break;
553 		case 'w':
554 			wflag = true;
555 			break;
556 		case 'x':
557 			xflag = true;
558 			break;
559 		case 'Z':
560 			filebehave = FILE_GZIP;
561 			break;
562 		case BIN_OPT:
563 			if (strcasecmp("binary", optarg) == 0)
564 				binbehave = BINFILE_BIN;
565 			else if (strcasecmp("without-match", optarg) == 0)
566 				binbehave = BINFILE_SKIP;
567 			else if (strcasecmp("text", optarg) == 0)
568 				binbehave = BINFILE_TEXT;
569 			else
570 				errx(2, getstr(3), "--binary-files");
571 			break;
572 		case COLOR_OPT:
573 			color = NULL;
574 			if (optarg == NULL || strcasecmp("auto", optarg) == 0 ||
575 			    strcasecmp("tty", optarg) == 0 ||
576 			    strcasecmp("if-tty", optarg) == 0) {
577 				char *term;
578 
579 				term = getenv("TERM");
580 				if (isatty(STDOUT_FILENO) && term != NULL &&
581 				    strcasecmp(term, "dumb") != 0)
582 					color = init_color("01;31");
583 			} else if (strcasecmp("always", optarg) == 0 ||
584 			    strcasecmp("yes", optarg) == 0 ||
585 			    strcasecmp("force", optarg) == 0) {
586 				color = init_color("01;31");
587 			} else if (strcasecmp("never", optarg) != 0 &&
588 			    strcasecmp("none", optarg) != 0 &&
589 			    strcasecmp("no", optarg) != 0)
590 				errx(2, getstr(3), "--color");
591 			break;
592 		case LABEL_OPT:
593 			label = optarg;
594 			break;
595 		case LINEBUF_OPT:
596 			lbflag = true;
597 			break;
598 		case NULL_OPT:
599 			nullflag = true;
600 			break;
601 		case R_INCLUDE_OPT:
602 			finclude = true;
603 			add_fpattern(optarg, INCL_PAT);
604 			break;
605 		case R_EXCLUDE_OPT:
606 			fexclude = true;
607 			add_fpattern(optarg, EXCL_PAT);
608 			break;
609 		case R_DINCLUDE_OPT:
610 			dinclude = true;
611 			add_dpattern(optarg, INCL_PAT);
612 			break;
613 		case R_DEXCLUDE_OPT:
614 			dexclude = true;
615 			add_dpattern(optarg, EXCL_PAT);
616 			break;
617 		case HELP_OPT:
618 		default:
619 			usage();
620 		}
621 		lastc = c;
622 		newarg = optind != prevoptind;
623 		prevoptind = optind;
624 	}
625 	aargc -= optind;
626 	aargv += optind;
627 
628 	/* Fail if we don't have any pattern */
629 	if (aargc == 0 && needpattern)
630 		usage();
631 
632 	/* Process patterns from command line */
633 	if (aargc != 0 && needpattern) {
634 		add_pattern(*aargv, strlen(*aargv));
635 		--aargc;
636 		++aargv;
637 	}
638 
639 	switch (grepbehave) {
640 	case GREP_FIXED:
641 	case GREP_BASIC:
642 		break;
643 	case GREP_EXTENDED:
644 		cflags |= REG_EXTENDED;
645 		break;
646 	default:
647 		/* NOTREACHED */
648 		usage();
649 	}
650 
651 	fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern));
652 	r_pattern = grep_calloc(patterns, sizeof(*r_pattern));
653 /*
654  * XXX: fgrepcomp() and fastcomp() are workarounds for regexec() performance.
655  * Optimizations should be done there.
656  */
657 		/* Check if cheating is allowed (always is for fgrep). */
658 	if (grepbehave == GREP_FIXED) {
659 		for (i = 0; i < patterns; ++i)
660 			fgrepcomp(&fg_pattern[i], pattern[i]);
661 	} else {
662 		for (i = 0; i < patterns; ++i) {
663 			if (fastcomp(&fg_pattern[i], pattern[i])) {
664 				/* Fall back to full regex library */
665 				c = regcomp(&r_pattern[i], pattern[i], cflags);
666 				if (c != 0) {
667 					regerror(c, &r_pattern[i], re_error,
668 					    RE_ERROR_BUF);
669 					errx(2, "%s", re_error);
670 				}
671 			}
672 		}
673 	}
674 
675 	if (lbflag)
676 		setlinebuf(stdout);
677 
678 	if ((aargc == 0 || aargc == 1) && !Hflag)
679 		hflag = true;
680 
681 	if (aargc == 0)
682 		exit(!procfile("-"));
683 
684 	if (dirbehave == DIR_RECURSE)
685 		c = grep_tree(aargv);
686 	else
687 		for (c = 0; aargc--; ++aargv) {
688 			if ((finclude || fexclude) && !file_matching(*aargv))
689 				continue;
690 			c+= procfile(*aargv);
691 		}
692 
693 #ifndef WITHOUT_NLS
694 	catclose(catalog);
695 #endif
696 
697 	/* Find out the correct return value according to the
698 	   results and the command line option. */
699 	exit(c ? (notfound ? (qflag ? 0 : 2) : 0) : (notfound ? 2 : 1));
700 }
701