xref: /freebsd/usr.bin/grep/grep.c (revision 3fc36ee018bb836bd1796067cf4ef8683f166ebc)
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 		add_pattern(line, line[0] == '\n' ? 0 : (size_t)rlen);
319 	free(line);
320 	if (ferror(f))
321 		err(2, "%s", fn);
322 	fclose(f);
323 }
324 
325 static inline const char *
326 init_color(const char *d)
327 {
328 	char *c;
329 
330 	c = getenv("GREP_COLOR");
331 	return (c != NULL && c[0] != '\0' ? c : d);
332 }
333 
334 int
335 main(int argc, char *argv[])
336 {
337 	char **aargv, **eargv, *eopts;
338 	char *ep;
339 	const char *pn;
340 	unsigned long long l;
341 	unsigned int aargc, eargc, i;
342 	int c, lastc, needpattern, newarg, prevoptind;
343 
344 	setlocale(LC_ALL, "");
345 
346 #ifndef WITHOUT_NLS
347 	catalog = catopen("grep", NL_CAT_LOCALE);
348 #endif
349 
350 	/* Check what is the program name of the binary.  In this
351 	   way we can have all the funcionalities in one binary
352 	   without the need of scripting and using ugly hacks. */
353 	pn = getprogname();
354 	if (pn[0] == 'b' && pn[1] == 'z') {
355 		filebehave = FILE_BZIP;
356 		pn += 2;
357 	} else if (pn[0] == 'x' && pn[1] == 'z') {
358 		filebehave = FILE_XZ;
359 		pn += 2;
360 	} else if (pn[0] == 'l' && pn[1] == 'z') {
361 		filebehave = FILE_LZMA;
362 		pn += 2;
363 	} else if (pn[0] == 'z') {
364 		filebehave = FILE_GZIP;
365 		pn += 1;
366 	}
367 	switch (pn[0]) {
368 	case 'e':
369 		grepbehave = GREP_EXTENDED;
370 		break;
371 	case 'f':
372 		grepbehave = GREP_FIXED;
373 		break;
374 	}
375 
376 	lastc = '\0';
377 	newarg = 1;
378 	prevoptind = 1;
379 	needpattern = 1;
380 
381 	eopts = getenv("GREP_OPTIONS");
382 
383 	/* support for extra arguments in GREP_OPTIONS */
384 	eargc = 0;
385 	if (eopts != NULL && eopts[0] != '\0') {
386 		char *str;
387 
388 		/* make an estimation of how many extra arguments we have */
389 		for (unsigned int j = 0; j < strlen(eopts); j++)
390 			if (eopts[j] == ' ')
391 				eargc++;
392 
393 		eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1));
394 
395 		eargc = 0;
396 		/* parse extra arguments */
397 		while ((str = strsep(&eopts, " ")) != NULL)
398 			if (str[0] != '\0')
399 				eargv[eargc++] = grep_strdup(str);
400 
401 		aargv = (char **)grep_calloc(eargc + argc + 1,
402 		    sizeof(char *));
403 
404 		aargv[0] = argv[0];
405 		for (i = 0; i < eargc; i++)
406 			aargv[i + 1] = eargv[i];
407 		for (int j = 1; j < argc; j++, i++)
408 			aargv[i + 1] = argv[j];
409 
410 		aargc = eargc + argc;
411 	} else {
412 		aargv = argv;
413 		aargc = argc;
414 	}
415 
416 	while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) !=
417 	    -1)) {
418 		switch (c) {
419 		case '0': case '1': case '2': case '3': case '4':
420 		case '5': case '6': case '7': case '8': case '9':
421 			if (newarg || !isdigit(lastc))
422 				Aflag = 0;
423 			else if (Aflag > LLONG_MAX / 10) {
424 				errno = ERANGE;
425 				err(2, NULL);
426 			}
427 			Aflag = Bflag = (Aflag * 10) + (c - '0');
428 			break;
429 		case 'C':
430 			if (optarg == NULL) {
431 				Aflag = Bflag = 2;
432 				break;
433 			}
434 			/* FALLTHROUGH */
435 		case 'A':
436 			/* FALLTHROUGH */
437 		case 'B':
438 			errno = 0;
439 			l = strtoull(optarg, &ep, 10);
440 			if (((errno == ERANGE) && (l == ULLONG_MAX)) ||
441 			    ((errno == EINVAL) && (l == 0)))
442 				err(2, NULL);
443 			else if (ep[0] != '\0') {
444 				errno = EINVAL;
445 				err(2, NULL);
446 			}
447 			if (c == 'A')
448 				Aflag = l;
449 			else if (c == 'B')
450 				Bflag = l;
451 			else
452 				Aflag = Bflag = l;
453 			break;
454 		case 'a':
455 			binbehave = BINFILE_TEXT;
456 			break;
457 		case 'b':
458 			bflag = true;
459 			break;
460 		case 'c':
461 			cflag = true;
462 			break;
463 		case 'D':
464 			if (strcasecmp(optarg, "skip") == 0)
465 				devbehave = DEV_SKIP;
466 			else if (strcasecmp(optarg, "read") == 0)
467 				devbehave = DEV_READ;
468 			else
469 				errx(2, getstr(3), "--devices");
470 			break;
471 		case 'd':
472 			if (strcasecmp("recurse", optarg) == 0) {
473 				Hflag = true;
474 				dirbehave = DIR_RECURSE;
475 			} else if (strcasecmp("skip", optarg) == 0)
476 				dirbehave = DIR_SKIP;
477 			else if (strcasecmp("read", optarg) == 0)
478 				dirbehave = DIR_READ;
479 			else
480 				errx(2, getstr(3), "--directories");
481 			break;
482 		case 'E':
483 			grepbehave = GREP_EXTENDED;
484 			break;
485 		case 'e':
486 			{
487 				char *token;
488 				char *string = optarg;
489 
490 				while ((token = strsep(&string, "\n")) != NULL)
491 					add_pattern(token, strlen(token));
492 			}
493 			needpattern = 0;
494 			break;
495 		case 'F':
496 			grepbehave = GREP_FIXED;
497 			break;
498 		case 'f':
499 			read_patterns(optarg);
500 			needpattern = 0;
501 			break;
502 		case 'G':
503 			grepbehave = GREP_BASIC;
504 			break;
505 		case 'H':
506 			Hflag = true;
507 			break;
508 		case 'h':
509 			Hflag = false;
510 			hflag = true;
511 			break;
512 		case 'I':
513 			binbehave = BINFILE_SKIP;
514 			break;
515 		case 'i':
516 		case 'y':
517 			iflag =  true;
518 			cflags |= REG_ICASE;
519 			break;
520 		case 'J':
521 #ifdef WITHOUT_BZIP2
522 			errno = EOPNOTSUPP;
523 			err(2, "bzip2 support was disabled at compile-time");
524 #endif
525 			filebehave = FILE_BZIP;
526 			break;
527 		case 'L':
528 			lflag = false;
529 			Lflag = true;
530 			break;
531 		case 'l':
532 			Lflag = false;
533 			lflag = true;
534 			break;
535 		case 'm':
536 			mflag = true;
537 			errno = 0;
538 			mlimit = mcount = strtoll(optarg, &ep, 10);
539 			if (((errno == ERANGE) && (mcount == LLONG_MAX)) ||
540 			    ((errno == EINVAL) && (mcount == 0)))
541 				err(2, NULL);
542 			else if (ep[0] != '\0') {
543 				errno = EINVAL;
544 				err(2, NULL);
545 			}
546 			break;
547 		case 'M':
548 			filebehave = FILE_LZMA;
549 			break;
550 		case 'n':
551 			nflag = true;
552 			break;
553 		case 'O':
554 			linkbehave = LINK_EXPLICIT;
555 			break;
556 		case 'o':
557 			oflag = true;
558 			cflags &= ~REG_NOSUB;
559 			break;
560 		case 'p':
561 			linkbehave = LINK_SKIP;
562 			break;
563 		case 'q':
564 			qflag = true;
565 			break;
566 		case 'S':
567 			linkbehave = LINK_READ;
568 			break;
569 		case 'R':
570 		case 'r':
571 			dirbehave = DIR_RECURSE;
572 			Hflag = true;
573 			break;
574 		case 's':
575 			sflag = true;
576 			break;
577 		case 'U':
578 			binbehave = BINFILE_BIN;
579 			break;
580 		case 'u':
581 		case MMAP_OPT:
582 			filebehave = FILE_MMAP;
583 			break;
584 		case 'V':
585 			printf(getstr(9), getprogname(), VERSION);
586 			exit(0);
587 		case 'v':
588 			vflag = true;
589 			break;
590 		case 'w':
591 			wflag = true;
592 			cflags &= ~REG_NOSUB;
593 			break;
594 		case 'x':
595 			xflag = true;
596 			cflags &= ~REG_NOSUB;
597 			break;
598 		case 'X':
599 			filebehave = FILE_XZ;
600 			break;
601 		case 'Z':
602 			filebehave = FILE_GZIP;
603 			break;
604 		case BIN_OPT:
605 			if (strcasecmp("binary", optarg) == 0)
606 				binbehave = BINFILE_BIN;
607 			else if (strcasecmp("without-match", optarg) == 0)
608 				binbehave = BINFILE_SKIP;
609 			else if (strcasecmp("text", optarg) == 0)
610 				binbehave = BINFILE_TEXT;
611 			else
612 				errx(2, getstr(3), "--binary-files");
613 			break;
614 		case COLOR_OPT:
615 			color = NULL;
616 			if (optarg == NULL || strcasecmp("auto", optarg) == 0 ||
617 			    strcasecmp("tty", optarg) == 0 ||
618 			    strcasecmp("if-tty", optarg) == 0) {
619 				char *term;
620 
621 				term = getenv("TERM");
622 				if (isatty(STDOUT_FILENO) && term != NULL &&
623 				    strcasecmp(term, "dumb") != 0)
624 					color = init_color("01;31");
625 			} else if (strcasecmp("always", optarg) == 0 ||
626 			    strcasecmp("yes", optarg) == 0 ||
627 			    strcasecmp("force", optarg) == 0) {
628 				color = init_color("01;31");
629 			} else if (strcasecmp("never", optarg) != 0 &&
630 			    strcasecmp("none", optarg) != 0 &&
631 			    strcasecmp("no", optarg) != 0)
632 				errx(2, getstr(3), "--color");
633 			cflags &= ~REG_NOSUB;
634 			break;
635 		case LABEL_OPT:
636 			label = optarg;
637 			break;
638 		case LINEBUF_OPT:
639 			lbflag = true;
640 			break;
641 		case NULL_OPT:
642 			nullflag = true;
643 			break;
644 		case R_INCLUDE_OPT:
645 			finclude = true;
646 			add_fpattern(optarg, INCL_PAT);
647 			break;
648 		case R_EXCLUDE_OPT:
649 			fexclude = true;
650 			add_fpattern(optarg, EXCL_PAT);
651 			break;
652 		case R_DINCLUDE_OPT:
653 			dinclude = true;
654 			add_dpattern(optarg, INCL_PAT);
655 			break;
656 		case R_DEXCLUDE_OPT:
657 			dexclude = true;
658 			add_dpattern(optarg, EXCL_PAT);
659 			break;
660 		case HELP_OPT:
661 		default:
662 			usage();
663 		}
664 		lastc = c;
665 		newarg = optind != prevoptind;
666 		prevoptind = optind;
667 	}
668 	aargc -= optind;
669 	aargv += optind;
670 
671 	/* Empty pattern file matches nothing */
672 	if (!needpattern && (patterns == 0))
673 		exit(1);
674 
675 	/* Fail if we don't have any pattern */
676 	if (aargc == 0 && needpattern)
677 		usage();
678 
679 	/* Process patterns from command line */
680 	if (aargc != 0 && needpattern) {
681 		char *token;
682 		char *string = *aargv;
683 
684 		while ((token = strsep(&string, "\n")) != NULL)
685 			add_pattern(token, strlen(token));
686 		--aargc;
687 		++aargv;
688 	}
689 
690 	switch (grepbehave) {
691 	case GREP_BASIC:
692 		break;
693 	case GREP_FIXED:
694 		/* XXX: header mess, REG_LITERAL not defined in gnu/regex.h */
695 		cflags |= 0020;
696 		break;
697 	case GREP_EXTENDED:
698 		cflags |= REG_EXTENDED;
699 		break;
700 	default:
701 		/* NOTREACHED */
702 		usage();
703 	}
704 
705 	fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern));
706 	r_pattern = grep_calloc(patterns, sizeof(*r_pattern));
707 
708 	/* Check if cheating is allowed (always is for fgrep). */
709 	for (i = 0; i < patterns; ++i) {
710 		if (fastncomp(&fg_pattern[i], pattern[i].pat,
711 		    pattern[i].len, cflags) != 0) {
712 			/* Fall back to full regex library */
713 			c = regcomp(&r_pattern[i], pattern[i].pat, cflags);
714 			if (c != 0) {
715 				regerror(c, &r_pattern[i], re_error,
716 				    RE_ERROR_BUF);
717 				errx(2, "%s", re_error);
718 			}
719 		}
720 	}
721 
722 	if (lbflag)
723 		setlinebuf(stdout);
724 
725 	if ((aargc == 0 || aargc == 1) && !Hflag)
726 		hflag = true;
727 
728 	if (aargc == 0)
729 		exit(!procfile("-"));
730 
731 	if (dirbehave == DIR_RECURSE)
732 		c = grep_tree(aargv);
733 	else
734 		for (c = 0; aargc--; ++aargv) {
735 			if ((finclude || fexclude) && !file_matching(*aargv))
736 				continue;
737 			c+= procfile(*aargv);
738 		}
739 
740 #ifndef WITHOUT_NLS
741 	catclose(catalog);
742 #endif
743 
744 	/* Find out the correct return value according to the
745 	   results and the command line option. */
746 	exit(c ? (file_err ? (qflag ? 0 : 2) : 0) : (file_err ? 2 : 1));
747 }
748