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