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