Home | History | Annotate | Download | only in grep
      1 /*	$NetBSD: grep.c,v 1.11 2012/05/06 22:27:00 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.11 2012/05/06 22:27:00 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 [-abcDEFGHhIiJLlmnOoPqRSsUVvwxZz] [-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[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 /* Searching patterns */
     84 unsigned int	 patterns, pattern_sz;
     85 char		**pattern;
     86 regex_t		*r_pattern;
     87 fastgrep_t	*fg_pattern;
     88 
     89 /* Filename exclusion/inclusion patterns */
     90 unsigned int	 fpatterns, fpattern_sz;
     91 unsigned int	 dpatterns, dpattern_sz;
     92 struct epat	*dpattern, *fpattern;
     93 
     94 /* For regex errors  */
     95 char	 re_error[RE_ERROR_BUF + 1];
     96 
     97 /* Command-line flags */
     98 unsigned long long Aflag;	/* -A x: print x lines trailing each match */
     99 unsigned long long Bflag;	/* -B x: print x lines leading each match */
    100 bool	 Hflag;		/* -H: always print file name */
    101 bool	 Lflag;		/* -L: only show names of files with no matches */
    102 bool	 bflag;		/* -b: show block numbers for each match */
    103 bool	 cflag;		/* -c: only show a count of matching lines */
    104 bool	 hflag;		/* -h: don't print filename headers */
    105 bool	 iflag;		/* -i: ignore case */
    106 bool	 lflag;		/* -l: only show names of files with matches */
    107 bool	 mflag;		/* -m x: stop reading the files after x matches */
    108 unsigned long long mcount;	/* count for -m */
    109 bool	 nflag;		/* -n: show line numbers in front of matching lines */
    110 bool	 oflag;		/* -o: print only matching part */
    111 bool	 qflag;		/* -q: quiet mode (don't output anything) */
    112 bool	 sflag;		/* -s: silent mode (ignore errors) */
    113 bool	 vflag;		/* -v: only show non-matching lines */
    114 bool	 wflag;		/* -w: pattern must start and end on word boundaries */
    115 bool	 xflag;		/* -x: pattern must match entire line */
    116 bool	 lbflag;	/* --line-buffered */
    117 bool	 nullflag;	/* --null */
    118 bool	 nulldataflag;	/* --null-data */
    119 unsigned char line_sep = '\n';	/* 0 for --null-data */
    120 char	*label;		/* --label */
    121 const char *color;	/* --color */
    122 int	 grepbehave = GREP_BASIC;	/* -EFGP: type of the regex */
    123 int	 binbehave = BINFILE_BIN;	/* -aIU: handling of binary files */
    124 int	 filebehave = FILE_STDIO;	/* -JZ: normal, gzip or bzip2 file */
    125 int	 devbehave = DEV_READ;		/* -D: handling of devices */
    126 int	 dirbehave = DIR_READ;		/* -dRr: handling of directories */
    127 int	 linkbehave = LINK_READ;	/* -OpS: handling of symlinks */
    128 
    129 bool	 dexclude, dinclude;	/* --exclude-dir and --include-dir */
    130 bool	 fexclude, finclude;	/* --exclude and --include */
    131 
    132 enum {
    133 	BIN_OPT = CHAR_MAX + 1,
    134 	COLOR_OPT,
    135 	DECOMPRESS_OPT,
    136 	HELP_OPT,
    137 	MMAP_OPT,
    138 	LINEBUF_OPT,
    139 	LABEL_OPT,
    140 	R_EXCLUDE_OPT,
    141 	R_INCLUDE_OPT,
    142 	R_DEXCLUDE_OPT,
    143 	R_DINCLUDE_OPT
    144 };
    145 
    146 static inline const char	*init_color(const char *);
    147 
    148 /* Housekeeping */
    149 int	 tail;		/* lines left to print */
    150 bool	 notfound;	/* file not found */
    151 
    152 extern char	*__progname;
    153 
    154 /*
    155  * Prints usage information and returns 2.
    156  */
    157 __dead static void
    158 usage(void)
    159 {
    160 	fprintf(stderr, getstr(4), __progname);
    161 	fprintf(stderr, "%s", getstr(5));
    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[] =
    169     "0123456789A:B:C:D:EFGHIJLOPSRUVZabcd:e:f:hilm:nopqrsuvwxyz";
    170 
    171 struct option long_options[] =
    172 {
    173 	{"binary-files",	required_argument,	NULL, BIN_OPT},
    174 	{"decompress",          no_argument,            NULL, DECOMPRESS_OPT},
    175 	{"help",		no_argument,		NULL, HELP_OPT},
    176 	{"mmap",		no_argument,		NULL, MMAP_OPT},
    177 	{"line-buffered",	no_argument,		NULL, LINEBUF_OPT},
    178 	{"label",		required_argument,	NULL, LABEL_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 	{"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 	{"null",		no_argument,		NULL, 'Z'},
    218 	{"null-data",		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 	/* TODO: Check for empty patterns and shortcut */
    230 
    231 	/* Increase size if necessary */
    232 	if (patterns == pattern_sz) {
    233 		pattern_sz *= 2;
    234 		pattern = grep_realloc(pattern, ++pattern_sz *
    235 		    sizeof(*pattern));
    236 	}
    237 	if (len > 0 && pat[len - 1] == '\n')
    238 		--len;
    239 	/* pat may not be NUL-terminated */
    240 	pattern[patterns] = grep_malloc(len + 1);
    241 	memcpy(pattern[patterns], pat, len);
    242 	pattern[patterns][len] = '\0';
    243 	++patterns;
    244 }
    245 
    246 /*
    247  * Adds a file include/exclude pattern to the internal array.
    248  */
    249 static void
    250 add_fpattern(const char *pat, int mode)
    251 {
    252 
    253 	/* Increase size if necessary */
    254 	if (fpatterns == fpattern_sz) {
    255 		fpattern_sz *= 2;
    256 		fpattern = grep_realloc(fpattern, ++fpattern_sz *
    257 		    sizeof(struct epat));
    258 	}
    259 	fpattern[fpatterns].pat = grep_strdup(pat);
    260 	fpattern[fpatterns].mode = mode;
    261 	++fpatterns;
    262 }
    263 
    264 /*
    265  * Adds a directory include/exclude pattern to the internal array.
    266  */
    267 static void
    268 add_dpattern(const char *pat, int mode)
    269 {
    270 
    271 	/* Increase size if necessary */
    272 	if (dpatterns == dpattern_sz) {
    273 		dpattern_sz *= 2;
    274 		dpattern = grep_realloc(dpattern, ++dpattern_sz *
    275 		    sizeof(struct epat));
    276 	}
    277 	dpattern[dpatterns].pat = grep_strdup(pat);
    278 	dpattern[dpatterns].mode = mode;
    279 	++dpatterns;
    280 }
    281 
    282 /*
    283  * Reads searching patterns from a file and adds them with add_pattern().
    284  */
    285 static void
    286 read_patterns(const char *fn)
    287 {
    288 	FILE *f;
    289 	char *line;
    290 	size_t len;
    291 	ssize_t rlen;
    292 
    293 	if ((f = fopen(fn, "r")) == NULL)
    294 		err(2, "%s", fn);
    295 	line = NULL;
    296 	len = 0;
    297 #ifndef ANDROID
    298 	while ((rlen = getline(&line, &len, f)) != -1)
    299 		add_pattern(line, *line == '\n' ? 0 : (size_t)rlen);
    300 #endif
    301 	free(line);
    302 	if (ferror(f))
    303 		err(2, "%s", fn);
    304 	fclose(f);
    305 }
    306 
    307 static inline const char *
    308 init_color(const char *d)
    309 {
    310 	char *c;
    311 
    312 	c = getenv("GREP_COLOR");
    313 	return (c != NULL ? c : d);
    314 }
    315 
    316 int
    317 grep_main(int argc, char *argv[])
    318 {
    319 	char **aargv, **eargv, *eopts;
    320 	char *ep;
    321 	unsigned long long l;
    322 	unsigned int aargc, eargc, i, j;
    323 	int c, lastc, needpattern, newarg, prevoptind;
    324 
    325 	setlocale(LC_ALL, "");
    326 
    327 #ifndef WITHOUT_NLS
    328 	catalog = catopen("grep", NL_CAT_LOCALE);
    329 #endif
    330 
    331 	/* Check what is the program name of the binary.  In this
    332 	   way we can have all the funcionalities in one binary
    333 	   without the need of scripting and using ugly hacks. */
    334 	switch (__progname[0]) {
    335 	case 'e':
    336 		grepbehave = GREP_EXTENDED;
    337 		break;
    338 	case 'f':
    339 		grepbehave = GREP_FIXED;
    340 		break;
    341 	case 'g':
    342 		grepbehave = GREP_BASIC;
    343 		break;
    344 	case 'z':
    345 		filebehave = FILE_GZIP;
    346 		switch(__progname[1]) {
    347 		case 'e':
    348 			grepbehave = GREP_EXTENDED;
    349 			break;
    350 		case 'f':
    351 			grepbehave = GREP_FIXED;
    352 			break;
    353 		case 'g':
    354 			grepbehave = GREP_BASIC;
    355 			break;
    356 		}
    357 		break;
    358 	}
    359 
    360 	lastc = '\0';
    361 	newarg = 1;
    362 	prevoptind = 1;
    363 	needpattern = 1;
    364 
    365 	eopts = getenv("GREP_OPTIONS");
    366 
    367 	/* support for extra arguments in GREP_OPTIONS */
    368 	eargc = 0;
    369 	if (eopts != NULL) {
    370 		char *str;
    371 
    372 		/* make an estimation of how many extra arguments we have */
    373 		for (j = 0; j < strlen(eopts); j++)
    374 			if (eopts[j] == ' ')
    375 				eargc++;
    376 
    377 		eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1));
    378 
    379 		eargc = 0;
    380 		/* parse extra arguments */
    381 		while ((str = strsep(&eopts, " ")) != NULL)
    382 			eargv[eargc++] = grep_strdup(str);
    383 
    384 		aargv = (char **)grep_calloc(eargc + argc + 1,
    385 		    sizeof(char *));
    386 
    387 		aargv[0] = argv[0];
    388 		for (i = 0; i < eargc; i++)
    389 			aargv[i + 1] = eargv[i];
    390 		for (j = 1; j < (unsigned int)argc; j++, i++)
    391 			aargv[i + 1] = argv[j];
    392 
    393 		aargc = eargc + argc;
    394 	} else {
    395 		aargv = argv;
    396 		aargc = argc;
    397 	}
    398 
    399 	while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) !=
    400 	    -1)) {
    401 		switch (c) {
    402 		case '0': case '1': case '2': case '3': case '4':
    403 		case '5': case '6': case '7': case '8': case '9':
    404 			if (newarg || !isdigit(lastc))
    405 				Aflag = 0;
    406 			else if (Aflag > LLONG_MAX / 10) {
    407 				errno = ERANGE;
    408 				err(2, NULL);
    409 			}
    410 			Aflag = Bflag = (Aflag * 10) + (c - '0');
    411 			break;
    412 		case 'C':
    413 			if (optarg == NULL) {
    414 				Aflag = Bflag = 2;
    415 				break;
    416 			}
    417 			/* FALLTHROUGH */
    418 		case 'A':
    419 			/* FALLTHROUGH */
    420 		case 'B':
    421 			errno = 0;
    422 			l = strtoull(optarg, &ep, 10);
    423 			if (((errno == ERANGE) && (l == ULLONG_MAX)) ||
    424 			    ((errno == EINVAL) && (l == 0)))
    425 				err(2, NULL);
    426 			else if (ep[0] != '\0') {
    427 				errno = EINVAL;
    428 				err(2, NULL);
    429 			}
    430 			if (c == 'A')
    431 				Aflag = l;
    432 			else if (c == 'B')
    433 				Bflag = l;
    434 			else
    435 				Aflag = Bflag = l;
    436 			break;
    437 		case 'a':
    438 			binbehave = BINFILE_TEXT;
    439 			break;
    440 		case 'b':
    441 			bflag = true;
    442 			break;
    443 		case 'c':
    444 			cflag = true;
    445 			break;
    446 		case 'D':
    447 			if (strcasecmp(optarg, "skip") == 0)
    448 				devbehave = DEV_SKIP;
    449 			else if (strcasecmp(optarg, "read") == 0)
    450 				devbehave = DEV_READ;
    451 			else
    452 				errx(2, getstr(3), "--devices");
    453 			break;
    454 		case 'd':
    455 			if (strcasecmp("recurse", optarg) == 0) {
    456 				Hflag = true;
    457 				dirbehave = DIR_RECURSE;
    458 			} else if (strcasecmp("skip", optarg) == 0)
    459 				dirbehave = DIR_SKIP;
    460 			else if (strcasecmp("read", optarg) == 0)
    461 				dirbehave = DIR_READ;
    462 			else
    463 				errx(2, getstr(3), "--directories");
    464 			break;
    465 		case 'E':
    466 			grepbehave = GREP_EXTENDED;
    467 			break;
    468 		case 'e':
    469 			add_pattern(optarg, strlen(optarg));
    470 			needpattern = 0;
    471 			break;
    472 		case 'F':
    473 			grepbehave = GREP_FIXED;
    474 			break;
    475 		case 'f':
    476 			read_patterns(optarg);
    477 			needpattern = 0;
    478 			break;
    479 		case 'G':
    480 			grepbehave = GREP_BASIC;
    481 			break;
    482 		case 'H':
    483 			Hflag = true;
    484 			break;
    485 		case 'h':
    486 			Hflag = false;
    487 			hflag = true;
    488 			break;
    489 		case 'I':
    490 			binbehave = BINFILE_SKIP;
    491 			break;
    492 		case 'i':
    493 		case 'y':
    494 			iflag =  true;
    495 			cflags |= REG_ICASE;
    496 			break;
    497 		case 'J':
    498 			filebehave = FILE_BZIP;
    499 			break;
    500 		case 'L':
    501 			lflag = false;
    502 			Lflag = true;
    503 			break;
    504 		case 'l':
    505 			Lflag = false;
    506 			lflag = true;
    507 			break;
    508 		case 'm':
    509 			mflag = true;
    510 			errno = 0;
    511 			mcount = strtoull(optarg, &ep, 10);
    512 			if (((errno == ERANGE) && (mcount == ULLONG_MAX)) ||
    513 			    ((errno == EINVAL) && (mcount == 0)))
    514 				err(2, NULL);
    515 			else if (ep[0] != '\0') {
    516 				errno = EINVAL;
    517 				err(2, NULL);
    518 			}
    519 			break;
    520 		case 'n':
    521 			nflag = true;
    522 			break;
    523 		case 'O':
    524 			linkbehave = LINK_EXPLICIT;
    525 			break;
    526 		case 'o':
    527 			oflag = true;
    528 			break;
    529 		case 'p':
    530 			linkbehave = LINK_SKIP;
    531 			break;
    532 		case 'q':
    533 			qflag = true;
    534 			break;
    535 		case 'S':
    536 			linkbehave = LINK_READ;
    537 			break;
    538 		case 'R':
    539 		case 'r':
    540 			dirbehave = DIR_RECURSE;
    541 			Hflag = true;
    542 			break;
    543 		case 's':
    544 			sflag = true;
    545 			break;
    546 		case 'U':
    547 			binbehave = BINFILE_BIN;
    548 			break;
    549 		case 'u':
    550 		case MMAP_OPT:
    551 			/* noop, compatibility */
    552 			break;
    553 		case 'V':
    554 			printf(getstr(9), __progname, VERSION);
    555 			exit(0);
    556 		case 'v':
    557 			vflag = true;
    558 			break;
    559 		case 'w':
    560 			wflag = true;
    561 			break;
    562 		case 'x':
    563 			xflag = true;
    564 			break;
    565 		case 'Z':
    566 			nullflag = true;
    567 			break;
    568 		case 'z':
    569 			nulldataflag = true;
    570 			line_sep = '\0';
    571 			break;
    572 		case BIN_OPT:
    573 			if (strcasecmp("binary", optarg) == 0)
    574 				binbehave = BINFILE_BIN;
    575 			else if (strcasecmp("without-match", optarg) == 0)
    576 				binbehave = BINFILE_SKIP;
    577 			else if (strcasecmp("text", optarg) == 0)
    578 				binbehave = BINFILE_TEXT;
    579 			else
    580 				errx(2, getstr(3), "--binary-files");
    581 			break;
    582 		case COLOR_OPT:
    583 			color = NULL;
    584 			if (optarg == NULL || strcasecmp("auto", optarg) == 0 ||
    585 			    strcasecmp("tty", optarg) == 0 ||
    586 			    strcasecmp("if-tty", optarg) == 0) {
    587 				char *term;
    588 
    589 				term = getenv("TERM");
    590 				if (isatty(STDOUT_FILENO) && term != NULL &&
    591 				    strcasecmp(term, "dumb") != 0)
    592 					color = init_color("01;31");
    593 			} else if (strcasecmp("always", optarg) == 0 ||
    594 			    strcasecmp("yes", optarg) == 0 ||
    595 			    strcasecmp("force", optarg) == 0) {
    596 				color = init_color("01;31");
    597 			} else if (strcasecmp("never", optarg) != 0 &&
    598 			    strcasecmp("none", optarg) != 0 &&
    599 			    strcasecmp("no", optarg) != 0)
    600 				errx(2, getstr(3), "--color");
    601 			break;
    602 		case DECOMPRESS_OPT:
    603 			filebehave = FILE_GZIP;
    604 			break;
    605 		case LABEL_OPT:
    606 			label = optarg;
    607 			break;
    608 		case LINEBUF_OPT:
    609 			lbflag = true;
    610 			break;
    611 		case R_INCLUDE_OPT:
    612 			finclude = true;
    613 			add_fpattern(optarg, INCL_PAT);
    614 			break;
    615 		case R_EXCLUDE_OPT:
    616 			fexclude = true;
    617 			add_fpattern(optarg, EXCL_PAT);
    618 			break;
    619 		case R_DINCLUDE_OPT:
    620 			dinclude = true;
    621 			add_dpattern(optarg, INCL_PAT);
    622 			break;
    623 		case R_DEXCLUDE_OPT:
    624 			dexclude = true;
    625 			add_dpattern(optarg, EXCL_PAT);
    626 			break;
    627 		case HELP_OPT:
    628 		default:
    629 			usage();
    630 		}
    631 		lastc = c;
    632 		newarg = optind != prevoptind;
    633 		prevoptind = optind;
    634 	}
    635 	aargc -= optind;
    636 	aargv += optind;
    637 
    638 	/* Fail if we don't have any pattern */
    639 	if (aargc == 0 && needpattern)
    640 		usage();
    641 
    642 	/* Process patterns from command line */
    643 	if (aargc != 0 && needpattern) {
    644 		add_pattern(*aargv, strlen(*aargv));
    645 		--aargc;
    646 		++aargv;
    647 	}
    648 
    649 	switch (grepbehave) {
    650 	case GREP_FIXED:
    651 	case GREP_BASIC:
    652 		break;
    653 	case GREP_EXTENDED:
    654 		cflags |= REG_EXTENDED;
    655 		break;
    656 	default:
    657 		/* NOTREACHED */
    658 		usage();
    659 	}
    660 
    661 	fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern));
    662 	r_pattern = grep_calloc(patterns, sizeof(*r_pattern));
    663 /*
    664  * XXX: fgrepcomp() and fastcomp() are workarounds for regexec() performance.
    665  * Optimizations should be done there.
    666  */
    667 		/* Check if cheating is allowed (always is for fgrep). */
    668 	if (grepbehave == GREP_FIXED) {
    669 		for (i = 0; i < patterns; ++i)
    670 			fgrepcomp(&fg_pattern[i], pattern[i]);
    671 	} else {
    672 		for (i = 0; i < patterns; ++i) {
    673 			if (fastcomp(&fg_pattern[i], pattern[i])) {
    674 				/* Fall back to full regex library */
    675 				c = regcomp(&r_pattern[i], pattern[i], cflags);
    676 				if (c != 0) {
    677 					regerror(c, &r_pattern[i], re_error,
    678 					    RE_ERROR_BUF);
    679 					errx(2, "%s", re_error);
    680 				}
    681 			}
    682 		}
    683 	}
    684 
    685 	if (lbflag)
    686 		setlinebuf(stdout);
    687 
    688 	if ((aargc == 0 || aargc == 1) && !Hflag)
    689 		hflag = true;
    690 
    691 	if (aargc == 0)
    692 		exit(!procfile("-"));
    693 
    694 	if (dirbehave == DIR_RECURSE)
    695 		c = grep_tree(aargv);
    696 	else
    697 		for (c = 0; aargc--; ++aargv) {
    698 			if ((finclude || fexclude) && !file_matching(*aargv))
    699 				continue;
    700 			c+= procfile(*aargv);
    701 		}
    702 
    703 #ifndef WITHOUT_NLS
    704 	catclose(catalog);
    705 #endif
    706 
    707 	/* Find out the correct return value according to the
    708 	   results and the command line option. */
    709 	exit(c ? (notfound ? (qflag ? 0 : 2) : 0) : (notfound ? 2 : 1));
    710 }
    711