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