grep.c revision 226261
1219888Sed/*	$NetBSD: grep.c,v 1.4 2011/02/16 01:31:33 joerg Exp $	*/
2219888Sed/* 	$FreeBSD: stable/9/usr.bin/grep/grep.c 226261 2011-10-11 15:03:42Z gabor $	*/
3219888Sed/*	$OpenBSD: grep.c,v 1.42 2010/07/02 22:18:03 tedu Exp $	*/
4219888Sed
5219888Sed/*-
6219888Sed * Copyright (c) 1999 James Howard and Dag-Erling Co��dan Sm��rgrav
7219888Sed * Copyright (C) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org>
8219888Sed * All rights reserved.
9219888Sed *
10219888Sed * Redistribution and use in source and binary forms, with or without
11219888Sed * modification, are permitted provided that the following conditions
12219888Sed * are met:
13219888Sed * 1. Redistributions of source code must retain the above copyright
14219888Sed *    notice, this list of conditions and the following disclaimer.
15219888Sed * 2. Redistributions in binary form must reproduce the above copyright
16219888Sed *    notice, this list of conditions and the following disclaimer in the
17219888Sed *    documentation and/or other materials provided with the distribution.
18219888Sed *
19219888Sed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20219888Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21219888Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22219888Sed * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23219888Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24219888Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25219888Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26219888Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27219888Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28219888Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29219888Sed * SUCH DAMAGE.
30219888Sed */
31219888Sed
32219888Sed#include <sys/cdefs.h>
33219888Sed__FBSDID("$FreeBSD: stable/9/usr.bin/grep/grep.c 226261 2011-10-11 15:03:42Z gabor $");
34219888Sed
35219888Sed#include <sys/stat.h>
36219888Sed#include <sys/types.h>
37219888Sed
38219888Sed#include <ctype.h>
39219888Sed#include <err.h>
40219888Sed#include <errno.h>
41219888Sed#include <fcntl.h>
42219888Sed#include <getopt.h>
43219888Sed#include <limits.h>
44263817Sray#include <libgen.h>
45263817Sray#include <locale.h>
46219888Sed#include <stdbool.h>
47219888Sed#include <stdio.h>
48219888Sed#include <stdlib.h>
49219888Sed#include <string.h>
50219888Sed#include <unistd.h>
51219888Sed
52219888Sed#include "fastmatch.h"
53219888Sed#include "grep.h"
54219888Sed
55219888Sed#ifndef WITHOUT_NLS
56219888Sed#include <nl_types.h>
57219888Sednl_catd	 catalog;
58219888Sed#endif
59219888Sed
60219888Sed/*
61219888Sed * Default messags to use when NLS is disabled or no catalogue
62219888Sed * is found.
63219888Sed */
64219888Sedconst char	*errstr[] = {
65219888Sed	"",
66219888Sed/* 1*/	"(standard input)",
67219888Sed/* 2*/	"cannot read bzip2 compressed file",
68219888Sed/* 3*/	"unknown %s option",
69219888Sed/* 4*/	"usage: %s [-abcDEFGHhIiJLlmnOoPqRSsUVvwxZ] [-A num] [-B num] [-C[num]]\n",
70219888Sed/* 5*/	"\t[-e pattern] [-f file] [--binary-files=value] [--color=when]\n",
71219888Sed/* 6*/	"\t[--context[=num]] [--directories=action] [--label] [--line-buffered]\n",
72219888Sed/* 7*/	"\t[--null] [pattern] [file ...]\n",
73219888Sed/* 8*/	"Binary file %s matches\n",
74219888Sed/* 9*/	"%s (BSD grep) %s\n",
75219888Sed};
76219888Sed
77219888Sed/* Flags passed to regcomp() and regexec() */
78219888Sedint		 cflags = REG_NOSUB;
79219888Sedint		 eflags = REG_STARTEND;
80219888Sed
81219888Sed/* Shortcut for matching all cases like empty regex */
82219888Sedbool		 matchall;
83219888Sed
84219888Sed/* Searching patterns */
85219888Sedunsigned int	 patterns, pattern_sz;
86219888Sedstruct pat	*pattern;
87219888Sedregex_t		*r_pattern;
88219888Sedfastmatch_t	*fg_pattern;
89263817Sray
90263817Sray/* Filename exclusion/inclusion patterns */
91219888Sedunsigned int	 fpatterns, fpattern_sz;
92263817Srayunsigned int	 dpatterns, dpattern_sz;
93263817Sraystruct epat	*dpattern, *fpattern;
94263817Sray
95263817Sray/* For regex errors  */
96263817Sraychar	 re_error[RE_ERROR_BUF + 1];
97219888Sed
98263817Sray/* Command-line flags */
99263817Srayunsigned long long Aflag;	/* -A x: print x lines trailing each match */
100263817Srayunsigned long long Bflag;	/* -B x: print x lines leading each match */
101263817Sraybool	 Hflag;		/* -H: always print file name */
102263817Sraybool	 Lflag;		/* -L: only show names of files with no matches */
103263817Sraybool	 bflag;		/* -b: show block numbers for each match */
104263817Sraybool	 cflag;		/* -c: only show a count of matching lines */
105263817Sraybool	 hflag;		/* -h: don't print filename headers */
106219888Sedbool	 iflag;		/* -i: ignore case */
107263817Sraybool	 lflag;		/* -l: only show names of files with matches */
108263817Sraybool	 mflag;		/* -m x: stop reading the files after x matches */
109219888Sedlong long mcount;	/* count for -m */
110219888Sedbool	 nflag;		/* -n: show line numbers in front of matching lines */
111219888Sedbool	 oflag;		/* -o: print only matching part */
112263817Sraybool	 qflag;		/* -q: quiet mode (don't output anything) */
113263817Sraybool	 sflag;		/* -s: silent mode (ignore errors) */
114219888Sedbool	 vflag;		/* -v: only show non-matching lines */
115219888Sedbool	 wflag;		/* -w: pattern must start and end on word boundaries */
116219888Sedbool	 xflag;		/* -x: pattern must match entire line */
117219888Sedbool	 lbflag;	/* --line-buffered */
118219888Sedbool	 nullflag;	/* --null */
119219888Sedchar	*label;		/* --label */
120219888Sedconst char *color;	/* --color */
121219888Sedint	 grepbehave = GREP_BASIC;	/* -EFGP: type of the regex */
122219888Sedint	 binbehave = BINFILE_BIN;	/* -aIU: handling of binary files */
123219888Sedint	 filebehave = FILE_STDIO;	/* -JZ: normal, gzip or bzip2 file */
124219888Sedint	 devbehave = DEV_READ;		/* -D: handling of devices */
125219888Sedint	 dirbehave = DIR_READ;		/* -dRr: handling of directories */
126219888Sedint	 linkbehave = LINK_READ;	/* -OpS: handling of symlinks */
127219888Sed
128219888Sedbool	 dexclude, dinclude;	/* --exclude-dir and --include-dir */
129219888Sedbool	 fexclude, finclude;	/* --exclude and --include */
130219888Sed
131263817Srayenum {
132219888Sed	BIN_OPT = CHAR_MAX + 1,
133219888Sed	COLOR_OPT,
134263817Sray	HELP_OPT,
135263817Sray	MMAP_OPT,
136219888Sed	LINEBUF_OPT,
137219888Sed	LABEL_OPT,
138219888Sed	NULL_OPT,
139219888Sed	R_EXCLUDE_OPT,
140219888Sed	R_INCLUDE_OPT,
141219888Sed	R_DEXCLUDE_OPT,
142219888Sed	R_DINCLUDE_OPT
143263817Sray};
144219888Sed
145219888Sedstatic inline const char	*init_color(const char *);
146219888Sed
147219888Sed/* Housekeeping */
148219888Sedbool	 first = true;	/* flag whether we are processing the first match */
149219888Sedbool	 prev;		/* flag whether or not the previous line matched */
150219888Sedint	 tail;		/* lines left to print */
151219888Sedbool	 notfound;	/* file not found */
152219888Sed
153219888Sedextern char	*__progname;
154263817Sray
155263817Sray/*
156219888Sed * Prints usage information and returns 2.
157219888Sed */
158219888Sedstatic void
159219888Sedusage(void)
160219888Sed{
161219888Sed	fprintf(stderr, getstr(4), __progname);
162219888Sed	fprintf(stderr, "%s", getstr(5));
163219888Sed	fprintf(stderr, "%s", getstr(5));
164219888Sed	fprintf(stderr, "%s", getstr(6));
165219888Sed	fprintf(stderr, "%s", getstr(7));
166263817Sray	exit(2);
167219888Sed}
168219888Sed
169263817Sraystatic const char	*optstr = "0123456789A:B:C:D:EFGHIJMLOPSRUVZabcd:e:f:hilm:nopqrsuvwxXy";
170219888Sed
171219888Sedstruct option long_options[] =
172219888Sed{
173219888Sed	{"binary-files",	required_argument,	NULL, BIN_OPT},
174219888Sed	{"help",		no_argument,		NULL, HELP_OPT},
175219888Sed	{"mmap",		no_argument,		NULL, MMAP_OPT},
176219888Sed	{"line-buffered",	no_argument,		NULL, LINEBUF_OPT},
177219888Sed	{"label",		required_argument,	NULL, LABEL_OPT},
178263817Sray	{"null",		no_argument,		NULL, NULL_OPT},
179263817Sray	{"color",		optional_argument,	NULL, COLOR_OPT},
180263817Sray	{"colour",		optional_argument,	NULL, COLOR_OPT},
181256145Sray	{"exclude",		required_argument,	NULL, R_EXCLUDE_OPT},
182219888Sed	{"include",		required_argument,	NULL, R_INCLUDE_OPT},
183263817Sray	{"exclude-dir",		required_argument,	NULL, R_DEXCLUDE_OPT},
184219888Sed	{"include-dir",		required_argument,	NULL, R_DINCLUDE_OPT},
185219888Sed	{"after-context",	required_argument,	NULL, 'A'},
186263817Sray	{"text",		no_argument,		NULL, 'a'},
187219888Sed	{"before-context",	required_argument,	NULL, 'B'},
188219888Sed	{"byte-offset",		no_argument,		NULL, 'b'},
189219888Sed	{"context",		optional_argument,	NULL, 'C'},
190219888Sed	{"count",		no_argument,		NULL, 'c'},
191263817Sray	{"devices",		required_argument,	NULL, 'D'},
192219888Sed        {"directories",		required_argument,	NULL, 'd'},
193219888Sed	{"extended-regexp",	no_argument,		NULL, 'E'},
194219888Sed	{"regexp",		required_argument,	NULL, 'e'},
195219888Sed	{"fixed-strings",	no_argument,		NULL, 'F'},
196219888Sed	{"file",		required_argument,	NULL, 'f'},
197263817Sray	{"basic-regexp",	no_argument,		NULL, 'G'},
198263817Sray	{"no-filename",		no_argument,		NULL, 'h'},
199263817Sray	{"with-filename",	no_argument,		NULL, 'H'},
200263817Sray	{"ignore-case",		no_argument,		NULL, 'i'},
201263817Sray	{"bz2decompress",	no_argument,		NULL, 'J'},
202263817Sray	{"files-with-matches",	no_argument,		NULL, 'l'},
203263817Sray	{"files-without-match", no_argument,            NULL, 'L'},
204263817Sray	{"max-count",		required_argument,	NULL, 'm'},
205263817Sray	{"lzma",		no_argument,		NULL, 'M'},
206263817Sray	{"line-number",		no_argument,		NULL, 'n'},
207263817Sray	{"only-matching",	no_argument,		NULL, 'o'},
208263817Sray	{"quiet",		no_argument,		NULL, 'q'},
209263817Sray	{"silent",		no_argument,		NULL, 'q'},
210263817Sray	{"recursive",		no_argument,		NULL, 'r'},
211263817Sray	{"no-messages",		no_argument,		NULL, 's'},
212263817Sray	{"binary",		no_argument,		NULL, 'U'},
213263817Sray	{"unix-byte-offsets",	no_argument,		NULL, 'u'},
214219888Sed	{"invert-match",	no_argument,		NULL, 'v'},
215219888Sed	{"version",		no_argument,		NULL, 'V'},
216219888Sed	{"word-regexp",		no_argument,		NULL, 'w'},
217219888Sed	{"line-regexp",		no_argument,		NULL, 'x'},
218219888Sed	{"xz",			no_argument,		NULL, 'X'},
219219888Sed	{"decompress",          no_argument,            NULL, 'Z'},
220219888Sed	{NULL,			no_argument,		NULL, 0}
221219888Sed};
222219888Sed
223219888Sed/*
224219888Sed * Adds a searching pattern to the internal array.
225 */
226static void
227add_pattern(char *pat, size_t len)
228{
229
230	/* Do not add further pattern is we already match everything */
231	if (matchall)
232	  return;
233
234	/* Check if we can do a shortcut */
235	if (len == 0) {
236		matchall = true;
237		for (unsigned int i = 0; i < patterns; i++) {
238			free(pattern[i].pat);
239		}
240		pattern = grep_realloc(pattern, sizeof(struct pat));
241		pattern[0].pat = NULL;
242		pattern[0].len = 0;
243		patterns = 1;
244		return;
245	}
246	/* Increase size if necessary */
247	if (patterns == pattern_sz) {
248		pattern_sz *= 2;
249		pattern = grep_realloc(pattern, ++pattern_sz *
250		    sizeof(struct pat));
251	}
252	if (len > 0 && pat[len - 1] == '\n')
253		--len;
254	/* pat may not be NUL-terminated */
255	pattern[patterns].pat = grep_malloc(len + 1);
256	memcpy(pattern[patterns].pat, pat, len);
257	pattern[patterns].len = len;
258	pattern[patterns].pat[len] = '\0';
259	++patterns;
260}
261
262/*
263 * Adds a file include/exclude pattern to the internal array.
264 */
265static void
266add_fpattern(const char *pat, int mode)
267{
268
269	/* Increase size if necessary */
270	if (fpatterns == fpattern_sz) {
271		fpattern_sz *= 2;
272		fpattern = grep_realloc(fpattern, ++fpattern_sz *
273		    sizeof(struct epat));
274	}
275	fpattern[fpatterns].pat = grep_strdup(pat);
276	fpattern[fpatterns].mode = mode;
277	++fpatterns;
278}
279
280/*
281 * Adds a directory include/exclude pattern to the internal array.
282 */
283static void
284add_dpattern(const char *pat, int mode)
285{
286
287	/* Increase size if necessary */
288	if (dpatterns == dpattern_sz) {
289		dpattern_sz *= 2;
290		dpattern = grep_realloc(dpattern, ++dpattern_sz *
291		    sizeof(struct epat));
292	}
293	dpattern[dpatterns].pat = grep_strdup(pat);
294	dpattern[dpatterns].mode = mode;
295	++dpatterns;
296}
297
298/*
299 * Reads searching patterns from a file and adds them with add_pattern().
300 */
301static void
302read_patterns(const char *fn)
303{
304	struct stat st;
305	FILE *f;
306	char *line;
307	size_t len;
308
309	if ((f = fopen(fn, "r")) == NULL)
310		err(2, "%s", fn);
311	if ((fstat(fileno(f), &st) == -1) || (S_ISDIR(st.st_mode))) {
312		fclose(f);
313		return;
314	}
315        while ((line = fgetln(f, &len)) != NULL)
316		add_pattern(line, line[0] == '\n' ? 0 : len);
317	if (ferror(f))
318		err(2, "%s", fn);
319	fclose(f);
320}
321
322static inline const char *
323init_color(const char *d)
324{
325	char *c;
326
327	c = getenv("GREP_COLOR");
328	return (c != NULL && c[0] != '\0' ? c : d);
329}
330
331int
332main(int argc, char *argv[])
333{
334	char **aargv, **eargv, *eopts;
335	char *pn, *ep;
336	unsigned long long l;
337	unsigned int aargc, eargc, i;
338	int c, lastc, needpattern, newarg, prevoptind;
339
340	setlocale(LC_ALL, "");
341
342#ifndef WITHOUT_NLS
343	catalog = catopen("grep", NL_CAT_LOCALE);
344#endif
345
346	/* Check what is the program name of the binary.  In this
347	   way we can have all the funcionalities in one binary
348	   without the need of scripting and using ugly hacks. */
349	pn = __progname;
350	if (pn[0] == 'b' && pn[1] == 'z') {
351		filebehave = FILE_BZIP;
352		pn += 2;
353	} else if (pn[0] == 'x' && pn[1] == 'z') {
354		filebehave = FILE_XZ;
355		pn += 2;
356	} else if (pn[0] == 'l' && pn[1] == 'z') {
357		filebehave = FILE_LZMA;
358		pn += 2;
359	} else if (pn[0] == 'z') {
360		filebehave = FILE_GZIP;
361		pn += 1;
362	}
363	switch (pn[0]) {
364	case 'e':
365		grepbehave = GREP_EXTENDED;
366		break;
367	case 'f':
368		grepbehave = GREP_FIXED;
369		break;
370	}
371
372	lastc = '\0';
373	newarg = 1;
374	prevoptind = 1;
375	needpattern = 1;
376
377	eopts = getenv("GREP_OPTIONS");
378
379	/* support for extra arguments in GREP_OPTIONS */
380	eargc = 0;
381	if (eopts != NULL && eopts[0] != '\0') {
382		char *str;
383
384		/* make an estimation of how many extra arguments we have */
385		for (unsigned int j = 0; j < strlen(eopts); j++)
386			if (eopts[j] == ' ')
387				eargc++;
388
389		eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1));
390
391		eargc = 0;
392		/* parse extra arguments */
393		while ((str = strsep(&eopts, " ")) != NULL)
394			if (str[0] != '\0')
395				eargv[eargc++] = grep_strdup(str);
396
397		aargv = (char **)grep_calloc(eargc + argc + 1,
398		    sizeof(char *));
399
400		aargv[0] = argv[0];
401		for (i = 0; i < eargc; i++)
402			aargv[i + 1] = eargv[i];
403		for (int j = 1; j < argc; j++, i++)
404			aargv[i + 1] = argv[j];
405
406		aargc = eargc + argc;
407	} else {
408		aargv = argv;
409		aargc = argc;
410	}
411
412	while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) !=
413	    -1)) {
414		switch (c) {
415		case '0': case '1': case '2': case '3': case '4':
416		case '5': case '6': case '7': case '8': case '9':
417			if (newarg || !isdigit(lastc))
418				Aflag = 0;
419			else if (Aflag > LLONG_MAX / 10) {
420				errno = ERANGE;
421				err(2, NULL);
422			}
423			Aflag = Bflag = (Aflag * 10) + (c - '0');
424			break;
425		case 'C':
426			if (optarg == NULL) {
427				Aflag = Bflag = 2;
428				break;
429			}
430			/* FALLTHROUGH */
431		case 'A':
432			/* FALLTHROUGH */
433		case 'B':
434			errno = 0;
435			l = strtoull(optarg, &ep, 10);
436			if (((errno == ERANGE) && (l == ULLONG_MAX)) ||
437			    ((errno == EINVAL) && (l == 0)))
438				err(2, NULL);
439			else if (ep[0] != '\0') {
440				errno = EINVAL;
441				err(2, NULL);
442			}
443			if (c == 'A')
444				Aflag = l;
445			else if (c == 'B')
446				Bflag = l;
447			else
448				Aflag = Bflag = l;
449			break;
450		case 'a':
451			binbehave = BINFILE_TEXT;
452			break;
453		case 'b':
454			bflag = true;
455			break;
456		case 'c':
457			cflag = true;
458			break;
459		case 'D':
460			if (strcasecmp(optarg, "skip") == 0)
461				devbehave = DEV_SKIP;
462			else if (strcasecmp(optarg, "read") == 0)
463				devbehave = DEV_READ;
464			else
465				errx(2, getstr(3), "--devices");
466			break;
467		case 'd':
468			if (strcasecmp("recurse", optarg) == 0) {
469				Hflag = true;
470				dirbehave = DIR_RECURSE;
471			} else if (strcasecmp("skip", optarg) == 0)
472				dirbehave = DIR_SKIP;
473			else if (strcasecmp("read", optarg) == 0)
474				dirbehave = DIR_READ;
475			else
476				errx(2, getstr(3), "--directories");
477			break;
478		case 'E':
479			grepbehave = GREP_EXTENDED;
480			break;
481		case 'e':
482			add_pattern(optarg, strlen(optarg));
483			needpattern = 0;
484			break;
485		case 'F':
486			grepbehave = GREP_FIXED;
487			break;
488		case 'f':
489			read_patterns(optarg);
490			needpattern = 0;
491			break;
492		case 'G':
493			grepbehave = GREP_BASIC;
494			break;
495		case 'H':
496			Hflag = true;
497			break;
498		case 'h':
499			Hflag = false;
500			hflag = true;
501			break;
502		case 'I':
503			binbehave = BINFILE_SKIP;
504			break;
505		case 'i':
506		case 'y':
507			iflag =  true;
508			cflags |= REG_ICASE;
509			break;
510		case 'J':
511			filebehave = FILE_BZIP;
512			break;
513		case 'L':
514			lflag = false;
515			Lflag = true;
516			break;
517		case 'l':
518			Lflag = false;
519			lflag = true;
520			break;
521		case 'm':
522			mflag = true;
523			errno = 0;
524			mcount = strtoll(optarg, &ep, 10);
525			if (((errno == ERANGE) && (mcount == LLONG_MAX)) ||
526			    ((errno == EINVAL) && (mcount == 0)))
527				err(2, NULL);
528			else if (ep[0] != '\0') {
529				errno = EINVAL;
530				err(2, NULL);
531			}
532			break;
533		case 'M':
534			filebehave = FILE_LZMA;
535			break;
536		case 'n':
537			nflag = true;
538			break;
539		case 'O':
540			linkbehave = LINK_EXPLICIT;
541			break;
542		case 'o':
543			oflag = true;
544			cflags &= ~REG_NOSUB;
545			break;
546		case 'p':
547			linkbehave = LINK_SKIP;
548			break;
549		case 'q':
550			qflag = true;
551			break;
552		case 'S':
553			linkbehave = LINK_READ;
554			break;
555		case 'R':
556		case 'r':
557			dirbehave = DIR_RECURSE;
558			Hflag = true;
559			break;
560		case 's':
561			sflag = true;
562			break;
563		case 'U':
564			binbehave = BINFILE_BIN;
565			break;
566		case 'u':
567		case MMAP_OPT:
568			filebehave = FILE_MMAP;
569			break;
570		case 'V':
571			printf(getstr(9), __progname, VERSION);
572			exit(0);
573		case 'v':
574			vflag = true;
575			break;
576		case 'w':
577			wflag = true;
578			cflags &= ~REG_NOSUB;
579			break;
580		case 'x':
581			xflag = true;
582			cflags &= ~REG_NOSUB;
583			break;
584		case 'X':
585			filebehave = FILE_XZ;
586			break;
587		case 'Z':
588			filebehave = FILE_GZIP;
589			break;
590		case BIN_OPT:
591			if (strcasecmp("binary", optarg) == 0)
592				binbehave = BINFILE_BIN;
593			else if (strcasecmp("without-match", optarg) == 0)
594				binbehave = BINFILE_SKIP;
595			else if (strcasecmp("text", optarg) == 0)
596				binbehave = BINFILE_TEXT;
597			else
598				errx(2, getstr(3), "--binary-files");
599			break;
600		case COLOR_OPT:
601			color = NULL;
602			if (optarg == NULL || strcasecmp("auto", optarg) == 0 ||
603			    strcasecmp("tty", optarg) == 0 ||
604			    strcasecmp("if-tty", optarg) == 0) {
605				char *term;
606
607				term = getenv("TERM");
608				if (isatty(STDOUT_FILENO) && term != NULL &&
609				    strcasecmp(term, "dumb") != 0)
610					color = init_color("01;31");
611			} else if (strcasecmp("always", optarg) == 0 ||
612			    strcasecmp("yes", optarg) == 0 ||
613			    strcasecmp("force", optarg) == 0) {
614				color = init_color("01;31");
615			} else if (strcasecmp("never", optarg) != 0 &&
616			    strcasecmp("none", optarg) != 0 &&
617			    strcasecmp("no", optarg) != 0)
618				errx(2, getstr(3), "--color");
619			cflags &= ~REG_NOSUB;
620			break;
621		case LABEL_OPT:
622			label = optarg;
623			break;
624		case LINEBUF_OPT:
625			lbflag = true;
626			break;
627		case NULL_OPT:
628			nullflag = true;
629			break;
630		case R_INCLUDE_OPT:
631			finclude = true;
632			add_fpattern(optarg, INCL_PAT);
633			break;
634		case R_EXCLUDE_OPT:
635			fexclude = true;
636			add_fpattern(optarg, EXCL_PAT);
637			break;
638		case R_DINCLUDE_OPT:
639			dinclude = true;
640			add_dpattern(optarg, INCL_PAT);
641			break;
642		case R_DEXCLUDE_OPT:
643			dexclude = true;
644			add_dpattern(optarg, EXCL_PAT);
645			break;
646		case HELP_OPT:
647		default:
648			usage();
649		}
650		lastc = c;
651		newarg = optind != prevoptind;
652		prevoptind = optind;
653	}
654	aargc -= optind;
655	aargv += optind;
656
657	/* Empty pattern file matches nothing */
658	if (!needpattern && (patterns == 0))
659		exit(1);
660
661	/* Fail if we don't have any pattern */
662	if (aargc == 0 && needpattern)
663		usage();
664
665	/* Process patterns from command line */
666	if (aargc != 0 && needpattern) {
667		add_pattern(*aargv, strlen(*aargv));
668		--aargc;
669		++aargv;
670	}
671
672	switch (grepbehave) {
673	case GREP_BASIC:
674		break;
675	case GREP_FIXED:
676		/* XXX: header mess, REG_LITERAL not defined in gnu/regex.h */
677		cflags |= 0020;
678		break;
679	case GREP_EXTENDED:
680		cflags |= REG_EXTENDED;
681		break;
682	default:
683		/* NOTREACHED */
684		usage();
685	}
686
687	fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern));
688	r_pattern = grep_calloc(patterns, sizeof(*r_pattern));
689
690	/* Check if cheating is allowed (always is for fgrep). */
691	for (i = 0; i < patterns; ++i) {
692		if (fastncomp(&fg_pattern[i], pattern[i].pat,
693		    pattern[i].len, cflags) != 0) {
694			/* Fall back to full regex library */
695			c = regcomp(&r_pattern[i], pattern[i].pat, cflags);
696			if (c != 0) {
697				regerror(c, &r_pattern[i], re_error,
698				    RE_ERROR_BUF);
699				errx(2, "%s", re_error);
700			}
701		}
702	}
703
704	if (lbflag)
705		setlinebuf(stdout);
706
707	if ((aargc == 0 || aargc == 1) && !Hflag)
708		hflag = true;
709
710	if (aargc == 0)
711		exit(!procfile("-"));
712
713	if (dirbehave == DIR_RECURSE)
714		c = grep_tree(aargv);
715	else
716		for (c = 0; aargc--; ++aargv) {
717			if ((finclude || fexclude) && !file_matching(*aargv))
718				continue;
719			c+= procfile(*aargv);
720		}
721
722#ifndef WITHOUT_NLS
723	catclose(catalog);
724#endif
725
726	/* Find out the correct return value according to the
727	   results and the command line option. */
728	exit(c ? (notfound ? (qflag ? 0 : 2) : 0) : (notfound ? 2 : 1));
729}
730