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