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