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