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