grep.c revision 210578
1329462Skib/*	$OpenBSD: grep.c,v 1.42 2010/07/02 22:18:03 tedu Exp $	*/
21817Sdg
31817Sdg/*-
41817Sdg * Copyright (c) 1999 James Howard and Dag-Erling Co�dan Sm�rgrav
51817Sdg * Copyright (C) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org>
6329462Skib * All rights reserved.
7329462Skib *
8329462Skib * Redistribution and use in source and binary forms, with or without
9329462Skib * modification, are permitted provided that the following conditions
10329462Skib * are met:
11329462Skib * 1. Redistributions of source code must retain the above copyright
12329462Skib *    notice, this list of conditions and the following disclaimer.
131817Sdg * 2. Redistributions in binary form must reproduce the above copyright
141817Sdg *    notice, this list of conditions and the following disclaimer in the
151817Sdg *    documentation and/or other materials provided with the distribution.
161817Sdg *
171817Sdg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
181817Sdg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191817Sdg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201817Sdg * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
211817Sdg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221817Sdg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231817Sdg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241817Sdg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251817Sdg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261817Sdg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271817Sdg * SUCH DAMAGE.
281817Sdg */
291817Sdg
301817Sdg#include <sys/cdefs.h>
311817Sdg__FBSDID("$FreeBSD: head/usr.bin/grep/grep.c 210578 2010-07-29 00:11:14Z gabor $");
321817Sdg
331817Sdg#include <sys/stat.h>
341817Sdg#include <sys/types.h>
351817Sdg
361817Sdg#include <ctype.h>
3750477Speter#include <err.h>
381817Sdg#include <errno.h>
391817Sdg#include <getopt.h>
402579Sbde#include <limits.h>
412579Sbde#include <libgen.h>
422123Sjkh#include <locale.h>
4316029Speter#include <stdbool.h>
442579Sbde#include <stdio.h>
4518961Sbde#include <stdlib.h>
4613107Sbde#include <string.h>
4725083Sjdp#include <unistd.h>
48163726Sbde
4925083Sjdp#include "grep.h"
5025083Sjdp
51163726Sbde#ifndef WITHOUT_NLS
5225083Sjdp#include <nl_types.h>
5325083Sjdpnl_catd	 catalog;
5425083Sjdp#endif
55114349Speter
5622636Sbde/*
5725083Sjdp * Default messags to use when NLS is disabled or no catalogue
5822636Sbde * is found.
59114349Speter */
6022636Sbdeconst char	*errstr[] = {
6125083Sjdp	"",
62757Sdg/* 1*/	"(standard input)",
6325083Sjdp/* 2*/	"cannot read bzip2 compressed file",
6446548Sbde/* 3*/	"unknown --color option",
6513107Sbde/* 4*/	"usage: %s [-abcDEFGHhIiJLlmnOoPqRSsUVvwxZ] [-A num] [-B num] [-C[num]]\n",
6618961Sbde/* 5*/	"\t[-e pattern] [-f file] [--binary-files=value] [--color=when]\n",
67757Sdg/* 6*/	"\t[--context[=num]] [--directories=action] [--label] [--line-buffered]\n",
68171914Sjkoshy/* 7*/	"\t[--null] [pattern] [file ...]\n",
69171914Sjkoshy/* 8*/	"unknown --binary-files option",
70757Sdg/* 9*/	"Binary file %s matches\n",
71757Sdg/*10*/	"%s (BSD grep) %s\n",
7246548Sbde};
7313107Sbde
7413107Sbde/* Flags passed to regcomp() and regexec() */
7513107Sbdeint		 cflags = 0;
7613107Sbdeint		 eflags = REG_STARTEND;
7713107Sbde
7813107Sbde/* Shortcut for matching all cases like empty regex */
7913107Sbdebool		 matchall;
8013107Sbde
8146548Sbde/* Searching patterns */
8218961Sbdeunsigned int	 patterns, pattern_sz;
8318961Sbdechar		**pattern;
8446548Sbderegex_t		*r_pattern;
8518961Sbdefastgrep_t	*fg_pattern;
8618961Sbde
8713107Sbde/* Filename exclusion/inclusion patterns */
8846548Sbdeunsigned int	 fpatterns, fpattern_sz;
8946548Sbdeunsigned int	 dpatterns, dpattern_sz;
9013107Sbdestruct epat	*dpattern, *fpattern;
9118961Sbde
9246548Sbde/* For regex errors  */
9346548Sbdechar	 re_error[RE_ERROR_BUF + 1];
9418961Sbde
9518961Sbde/* Command-line flags */
9613107Sbdeunsigned long long Aflag;	/* -A x: print x lines trailing each match */
9713107Sbdeunsigned long long Bflag;	/* -B x: print x lines leading each match */
9813107Sbdebool	 Hflag;		/* -H: always print file name */
9913107Sbdebool	 Lflag;		/* -L: only show names of files with no matches */
10013107Sbdebool	 bflag;		/* -b: show block numbers for each match */
10113107Sbdebool	 cflag;		/* -c: only show a count of matching lines */
10213107Sbdebool	 hflag;		/* -h: don't print filename headers */
10313107Sbdebool	 iflag;		/* -i: ignore case */
10413107Sbdebool	 lflag;		/* -l: only show names of files with matches */
10518961Sbdebool	 mflag;		/* -m x: stop reading the files after x matches */
10618961Sbdeunsigned long long mcount;	/* count for -m */
10718961Sbdebool	 nflag;		/* -n: show line numbers in front of matching lines */
10818961Sbdebool	 oflag;		/* -o: print only matching part */
109757Sdgbool	 qflag;		/* -q: quiet mode (don't output anything) */
11013107Sbdebool	 sflag;		/* -s: silent mode (ignore errors) */
11118961Sbdebool	 vflag;		/* -v: only show non-matching lines */
11218961Sbdebool	 wflag;		/* -w: pattern must start and end on word boundaries */
11318961Sbdebool	 xflag;		/* -x: pattern must match entire line */
11418961Sbdebool	 lbflag;	/* --line-buffered */
11513107Sbdebool	 nullflag;	/* --null */
116122940Speterchar	*label;		/* --label */
11713107Sbdeconst char *color;	/* --color */
11813107Sbdeint	 grepbehave = GREP_BASIC;	/* -EFGP: type of the regex */
119163722Sbdeint	 binbehave = BINFILE_BIN;	/* -aIU: handling of binary files */
120163726Sbdeint	 filebehave = FILE_STDIO;	/* -JZ: normal, gzip or bzip2 file */
12118961Sbdeint	 devbehave = DEV_READ;		/* -D: handling of devices */
122163722Sbdeint	 dirbehave = DIR_READ;		/* -dRr: handling of directories */
123163722Sbdeint	 linkbehave = LINK_READ;	/* -OpS: handling of symlinks */
124163722Sbde
12518961Sbdebool	 dexclude, dinclude;	/* --exclude amd --include */
12618961Sbdebool	 fexclude, finclude;	/* --exclude-dir and --include-dir */
127757Sdg
128757Sdgenum {
129757Sdg	BIN_OPT = CHAR_MAX + 1,
13013107Sbde	COLOR_OPT,
13113107Sbde	HELP_OPT,
132757Sdg	MMAP_OPT,
13313107Sbde	LINEBUF_OPT,
13418961Sbde	LABEL_OPT,
13518961Sbde	NULL_OPT,
13613107Sbde	R_EXCLUDE_OPT,
13713107Sbde	R_INCLUDE_OPT,
1381321Sdg	R_DEXCLUDE_OPT,
13913107Sbde	R_DINCLUDE_OPT
14013107Sbde};
14113107Sbde
142757Sdgstatic inline const char	*init_color(const char *);
143274489Sscottl
144274489Sscottl/* Housekeeping */
145274489Sscottlbool	 first = true;	/* flag whether we are processing the first match */
146274489Sscottlbool	 prev;		/* flag whether or not the previous line matched */
147274489Sscottlint	 tail;		/* lines left to print */
148274489Sscottlbool	 notfound;	/* file not found */
149274489Sscottl
150274489Sscottlextern char	*__progname;
151274489Sscottl
152274489Sscottl/*
153122849Speter * Prints usage information and returns 2.
154122849Speter */
155329462Skibstatic void
156329462Skibusage(void)
157329462Skib{
158329462Skib	fprintf(stderr, getstr(4), __progname);
159329462Skib	fprintf(stderr, "%s", getstr(5));
160329462Skib	fprintf(stderr, "%s", getstr(5));
161329462Skib	fprintf(stderr, "%s", getstr(6));
162329462Skib	fprintf(stderr, "%s", getstr(7));
163156699Speter	exit(2);
164122849Speter}
165122849Speter
166122849Speterstatic const char	*optstr = "0123456789A:B:C:D:EFGHIJLOPSRUVZabcd:e:f:hilm:nopqrsuvwxy";
167122849Speter
168329462Skibstruct option long_options[] =
169329462Skib{
170329462Skib	{"binary-files",	required_argument,	NULL, BIN_OPT},
171329462Skib	{"help",		no_argument,		NULL, HELP_OPT},
172329462Skib	{"mmap",		no_argument,		NULL, MMAP_OPT},
173329462Skib	{"line-buffered",	no_argument,		NULL, LINEBUF_OPT},
174153241Sjhb	{"label",		required_argument,	NULL, LABEL_OPT},
175329462Skib	{"null",		no_argument,		NULL, NULL_OPT},
176329462Skib	{"color",		optional_argument,	NULL, COLOR_OPT},
177329462Skib	{"colour",		optional_argument,	NULL, COLOR_OPT},
178329462Skib	{"exclude",		required_argument,	NULL, R_EXCLUDE_OPT},
179329462Skib	{"include",		required_argument,	NULL, R_INCLUDE_OPT},
180329462Skib	{"exclude-dir",		required_argument,	NULL, R_DEXCLUDE_OPT},
181329462Skib	{"include-dir",		required_argument,	NULL, R_DINCLUDE_OPT},
182329462Skib	{"after-context",	required_argument,	NULL, 'A'},
183153241Sjhb	{"text",		no_argument,		NULL, 'a'},
184329462Skib	{"before-context",	required_argument,	NULL, 'B'},
185329462Skib	{"byte-offset",		no_argument,		NULL, 'b'},
186329462Skib	{"context",		optional_argument,	NULL, 'C'},
187329462Skib	{"count",		no_argument,		NULL, 'c'},
188329462Skib	{"devices",		required_argument,	NULL, 'D'},
189329462Skib        {"directories",		required_argument,	NULL, 'd'},
190329462Skib	{"extended-regexp",	no_argument,		NULL, 'E'},
191329462Skib	{"regexp",		required_argument,	NULL, 'e'},
192329462Skib	{"fixed-strings",	no_argument,		NULL, 'F'},
193329462Skib	{"file",		required_argument,	NULL, 'f'},
194153241Sjhb	{"basic-regexp",	no_argument,		NULL, 'G'},
195329462Skib	{"no-filename",		no_argument,		NULL, 'h'},
196329462Skib	{"with-filename",	no_argument,		NULL, 'H'},
197335570Skib	{"ignore-case",		no_argument,		NULL, 'i'},
198335570Skib	{"bz2decompress",	no_argument,		NULL, 'J'},
199329462Skib	{"files-with-matches",	no_argument,		NULL, 'l'},
200329462Skib	{"files-without-match", no_argument,            NULL, 'L'},
201329462Skib	{"max-count",		required_argument,	NULL, 'm'},
202335570Skib	{"line-number",		no_argument,		NULL, 'n'},
203329462Skib	{"only-matching",	no_argument,		NULL, 'o'},
204329462Skib	{"quiet",		no_argument,		NULL, 'q'},
205329462Skib	{"silent",		no_argument,		NULL, 'q'},
206329462Skib	{"recursive",		no_argument,		NULL, 'r'},
207329462Skib	{"no-messages",		no_argument,		NULL, 's'},
208329462Skib	{"binary",		no_argument,		NULL, 'U'},
209329462Skib	{"unix-byte-offsets",	no_argument,		NULL, 'u'},
210329462Skib	{"invert-match",	no_argument,		NULL, 'v'},
211329462Skib	{"version",		no_argument,		NULL, 'V'},
212329462Skib	{"word-regexp",		no_argument,		NULL, 'w'},
213329462Skib	{"line-regexp",		no_argument,		NULL, 'x'},
214329462Skib	{"decompress",          no_argument,            NULL, 'Z'},
215329462Skib	{NULL,			no_argument,		NULL, 0}
216329462Skib};
217329462Skib
218329462Skib/*
219329462Skib * Adds a searching pattern to the internal array.
220329462Skib */
221329462Skibstatic void
222329462Skibadd_pattern(char *pat, size_t len)
223329462Skib{
224334330Sjhb
225329462Skib	/* Check if we can do a shortcut */
226334330Sjhb	if (len == 0 || matchall) {
227329462Skib		matchall = true;
228329462Skib		return;
229329462Skib	}
230329462Skib	/* Increase size if necessary */
231329462Skib	if (patterns == pattern_sz) {
232329462Skib		pattern_sz *= 2;
233329462Skib		pattern = grep_realloc(pattern, ++pattern_sz *
234329462Skib		    sizeof(*pattern));
235334330Sjhb	}
236329462Skib	if (len > 0 && pat[len - 1] == '\n')
237334330Sjhb		--len;
238329462Skib	/* pat may not be NUL-terminated */
239329462Skib	pattern[patterns] = grep_malloc(len + 1);
240329462Skib	strlcpy(pattern[patterns], pat, len + 1);
241329462Skib	++patterns;
242329462Skib}
243329462Skib
244329462Skib/*
245329462Skib * Adds a file include/exclude pattern to the internal array.
246329462Skib */
247329462Skibstatic void
248329462Skibadd_fpattern(const char *pat, int mode)
249329462Skib{
250329462Skib
251329462Skib	/* Increase size if necessary */
252329462Skib	if (fpatterns == fpattern_sz) {
253329462Skib		fpattern_sz *= 2;
254329462Skib		fpattern = grep_realloc(fpattern, ++fpattern_sz *
255329462Skib		    sizeof(struct epat));
256329462Skib	}
257329462Skib	fpattern[fpatterns].pat = grep_strdup(pat);
258329462Skib	fpattern[fpatterns].mode = mode;
259329462Skib	++fpatterns;
260329462Skib}
261334327Skib
262329462Skib/*
263329462Skib * Adds a directory include/exclude pattern to the internal array.
264329462Skib */
265329462Skibstatic void
266329462Skibadd_dpattern(const char *pat, int mode)
267329462Skib{
268329462Skib
269329462Skib	/* Increase size if necessary */
270329462Skib	if (dpatterns == dpattern_sz) {
271329462Skib		dpattern_sz *= 2;
272329462Skib		dpattern = grep_realloc(dpattern, ++dpattern_sz *
273329462Skib		    sizeof(struct epat));
274329462Skib	}
275329462Skib	dpattern[dpatterns].pat = grep_strdup(pat);
276329462Skib	dpattern[dpatterns].mode = mode;
277329462Skib	++dpatterns;
278329462Skib}
279329462Skib
280329462Skib/*
281329462Skib * Reads searching patterns from a file and adds them with add_pattern().
282329462Skib */
283329462Skibstatic void
284329462Skibread_patterns(const char *fn)
285329462Skib{
286329462Skib	FILE *f;
287329462Skib	char *line;
288329462Skib	size_t len;
289122849Speter
290122849Speter	if ((f = fopen(fn, "r")) == NULL)
291263002Sroyger		err(2, "%s", fn);
292263002Sroyger	while ((line = fgetln(f, &len)) != NULL)
293263002Sroyger		add_pattern(line, *line == '\n' ? 0 : len);
294263002Sroyger	if (ferror(f))
295263002Sroyger		err(2, "%s", fn);
296263002Sroyger	fclose(f);
297263002Sroyger}
298263002Sroyger
299263002Sroygerstatic inline const char *
300263002Sroygerinit_color(const char *d)
301263002Sroyger{
302263002Sroyger	char *c;
303263002Sroyger
304263002Sroyger	c = getenv("GREP_COLOR");
305263002Sroyger	return (c != NULL ? c : d);
306263002Sroyger}
307263002Sroyger
308263002Sroygerint
309263002Sroygermain(int argc, char *argv[])
310263002Sroyger{
311263002Sroyger	char **aargv, **eargv, *eopts;
312263002Sroyger	char *ep;
313263002Sroyger	unsigned long long l;
314263002Sroyger	unsigned int aargc, eargc, i;
315263002Sroyger	int c, lastc, needpattern, newarg, prevoptind;
316263002Sroyger
3172579Sbde	setlocale(LC_ALL, "");
318
319#ifndef WITHOUT_NLS
320	catalog = catopen("grep", NL_CAT_LOCALE);
321#endif
322
323	/* Check what is the program name of the binary.  In this
324	   way we can have all the funcionalities in one binary
325	   without the need of scripting and using ugly hacks. */
326	switch (__progname[0]) {
327	case 'e':
328		grepbehave = GREP_EXTENDED;
329		break;
330	case 'f':
331		grepbehave = GREP_FIXED;
332		break;
333	case 'g':
334		grepbehave = GREP_BASIC;
335		break;
336	case 'z':
337		filebehave = FILE_GZIP;
338		switch(__progname[1]) {
339		case 'e':
340			grepbehave = GREP_EXTENDED;
341			break;
342		case 'f':
343			grepbehave = GREP_FIXED;
344			break;
345		case 'g':
346			grepbehave = GREP_BASIC;
347			break;
348		}
349		break;
350	}
351
352	lastc = '\0';
353	newarg = 1;
354	prevoptind = 1;
355	needpattern = 1;
356
357	eopts = getenv("GREP_OPTIONS");
358
359	eargc = 1;
360	if (eopts != NULL) {
361		char *str;
362
363		for(i = 0; i < strlen(eopts); i++)
364			if (eopts[i] == ' ')
365				eargc++;
366
367		eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1));
368
369		str = strtok(eopts, " ");
370		eargc = 0;
371
372		while(str != NULL) {
373			eargv[++eargc] = (char *)grep_malloc(sizeof(char) *
374			    (strlen(str) + 1));
375			strlcpy(eargv[eargc], str, strlen(str) + 1);
376			str = strtok(NULL, " ");
377		}
378		eargv[++eargc] = NULL;
379
380		aargv = (char **)grep_calloc(eargc + argc + 1,
381		    sizeof(char *));
382		aargv[0] = argv[0];
383
384		for(i = 1; i < eargc; i++)
385			aargv[i] = eargv[i];
386		for(int j = 1; j < argc; j++)
387			aargv[i++] = argv[j];
388
389		aargc = eargc + argc - 1;
390
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				errno = EINVAL;
450				err(2, NULL);
451			}
452			break;
453		case 'd':
454			if (strcasecmp("recurse", optarg) == 0) {
455				Hflag = true;
456				dirbehave = DIR_RECURSE;
457			} else if (strcasecmp("skip", optarg) == 0)
458				dirbehave = DIR_SKIP;
459			else if (strcasecmp("read", optarg) == 0)
460				dirbehave = DIR_READ;
461			else {
462				errno = EINVAL;
463				err(2, NULL);
464			}
465			break;
466		case 'E':
467			grepbehave = GREP_EXTENDED;
468			break;
469		case 'e':
470			add_pattern(optarg, strlen(optarg));
471			needpattern = 0;
472			break;
473		case 'F':
474			grepbehave = GREP_FIXED;
475			break;
476		case 'f':
477			read_patterns(optarg);
478			needpattern = 0;
479			break;
480		case 'G':
481			grepbehave = GREP_BASIC;
482			break;
483		case 'H':
484			Hflag = true;
485			break;
486		case 'h':
487			Hflag = false;
488			hflag = true;
489			break;
490		case 'I':
491			binbehave = BINFILE_SKIP;
492			break;
493		case 'i':
494		case 'y':
495			iflag =  true;
496			cflags |= REG_ICASE;
497			break;
498		case 'J':
499			filebehave = FILE_BZIP;
500			break;
501		case 'L':
502			lflag = false;
503			Lflag = true;
504			break;
505		case 'l':
506			Lflag = false;
507			lflag = true;
508			break;
509		case 'm':
510			mflag = true;
511			errno = 0;
512			mcount = strtoull(optarg, &ep, 10);
513			if (((errno == ERANGE) && (mcount == ULLONG_MAX)) ||
514			    ((errno == EINVAL) && (mcount == 0)))
515				err(2, NULL);
516			else if (ep[0] != '\0') {
517				errno = EINVAL;
518				err(2, NULL);
519			}
520			break;
521		case 'n':
522			nflag = true;
523			break;
524		case 'O':
525			linkbehave = LINK_EXPLICIT;
526			break;
527		case 'o':
528			oflag = true;
529			break;
530		case 'p':
531			linkbehave = LINK_SKIP;
532			break;
533		case 'q':
534			qflag = true;
535			break;
536		case 'S':
537			linkbehave = LINK_READ;
538			break;
539		case 'R':
540		case 'r':
541			dirbehave = DIR_RECURSE;
542			Hflag = true;
543			break;
544		case 's':
545			sflag = true;
546			break;
547		case 'U':
548			binbehave = BINFILE_BIN;
549			break;
550		case 'u':
551		case MMAP_OPT:
552			/* noop, compatibility */
553			break;
554		case 'V':
555			printf(getstr(10), __progname, VERSION);
556			exit(0);
557		case 'v':
558			vflag = true;
559			break;
560		case 'w':
561			wflag = true;
562			break;
563		case 'x':
564			xflag = true;
565			break;
566		case 'Z':
567			filebehave = FILE_GZIP;
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, "%s", getstr(8));
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, "%s", getstr(3));
598			break;
599		case LABEL_OPT:
600			label = optarg;
601			break;
602		case LINEBUF_OPT:
603			lbflag = true;
604			break;
605		case NULL_OPT:
606			nullflag = 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			dexclude = true;
618			add_dpattern(optarg, INCL_PAT);
619			break;
620		case R_DEXCLUDE_OPT:
621			dinclude = 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