grep.c revision 210461
1210389Sgabor/*	$OpenBSD: grep.c,v 1.42 2010/07/02 22:18:03 tedu Exp $	*/
2210389Sgabor
3210389Sgabor/*-
4210389Sgabor * Copyright (c) 1999 James Howard and Dag-Erling Co�dan Sm�rgrav
5210389Sgabor * Copyright (C) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org>
6210389Sgabor * All rights reserved.
7210389Sgabor *
8210389Sgabor * Redistribution and use in source and binary forms, with or without
9210389Sgabor * modification, are permitted provided that the following conditions
10210389Sgabor * are met:
11210389Sgabor * 1. Redistributions of source code must retain the above copyright
12210389Sgabor *    notice, this list of conditions and the following disclaimer.
13210389Sgabor * 2. Redistributions in binary form must reproduce the above copyright
14210389Sgabor *    notice, this list of conditions and the following disclaimer in the
15210389Sgabor *    documentation and/or other materials provided with the distribution.
16210389Sgabor *
17210389Sgabor * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18210389Sgabor * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19210389Sgabor * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20210389Sgabor * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21210389Sgabor * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22210389Sgabor * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23210389Sgabor * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24210389Sgabor * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25210389Sgabor * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26210389Sgabor * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27210389Sgabor * SUCH DAMAGE.
28210389Sgabor */
29210389Sgabor
30210389Sgabor#include <sys/cdefs.h>
31210389Sgabor__FBSDID("$FreeBSD: head/usr.bin/grep/grep.c 210461 2010-07-25 08:42:18Z gabor $");
32210389Sgabor
33210389Sgabor#include <sys/stat.h>
34210389Sgabor#include <sys/types.h>
35210389Sgabor
36210389Sgabor#include <ctype.h>
37210389Sgabor#include <err.h>
38210389Sgabor#include <errno.h>
39210389Sgabor#include <getopt.h>
40210389Sgabor#include <limits.h>
41210389Sgabor#include <libgen.h>
42210389Sgabor#include <locale.h>
43210389Sgabor#include <stdbool.h>
44210389Sgabor#include <stdio.h>
45210389Sgabor#include <stdlib.h>
46210389Sgabor#include <string.h>
47210389Sgabor#include <unistd.h>
48210389Sgabor
49210389Sgabor#include "grep.h"
50210389Sgabor
51210389Sgabor#ifndef WITHOUT_NLS
52210389Sgabor#include <nl_types.h>
53210389Sgabornl_catd	 catalog;
54210389Sgabor#endif
55210389Sgabor
56210389Sgabor/*
57210389Sgabor * Default messags to use when NLS is disabled or no catalogue
58210389Sgabor * is found.
59210389Sgabor */
60210389Sgaborconst char	*errstr[] = {
61210389Sgabor	"",
62210389Sgabor/* 1*/	"(standard input)",
63210389Sgabor/* 2*/	"cannot read bzip2 compressed file",
64210389Sgabor/* 3*/	"unknown --color option",
65210389Sgabor/* 4*/	"usage: %s [-abcDEFGHhIiJLlmnOoPqRSsUVvwxZ] [-A num] [-B num] [-C[num]]\n",
66210389Sgabor/* 5*/	"\t[-e pattern] [-f file] [--binary-files=value] [--color=when]\n",
67210389Sgabor/* 6*/	"\t[--context[=num]] [--directories=action] [--label] [--line-buffered]\n",
68210389Sgabor/* 7*/	"\t[--null] [pattern] [file ...]\n",
69210389Sgabor/* 8*/	"unknown --binary-files option",
70210389Sgabor/* 9*/	"Binary file %s matches\n",
71210389Sgabor/*10*/	"%s (BSD grep) %s\n",
72210389Sgabor};
73210389Sgabor
74210389Sgabor/* Flags passed to regcomp() and regexec() */
75210389Sgaborint		 cflags = 0;
76210389Sgaborint		 eflags = REG_STARTEND;
77210389Sgabor
78210389Sgabor/* Shortcut for matching all cases like empty regex */
79210389Sgaborbool		 matchall;
80210389Sgabor
81210389Sgabor/* Searching patterns */
82210389Sgaborunsigned int	 patterns, pattern_sz;
83210389Sgaborchar		**pattern;
84210389Sgaborregex_t		*r_pattern;
85210389Sgaborfastgrep_t	*fg_pattern;
86210389Sgabor
87210389Sgabor/* Filename exclusion/inclusion patterns */
88210389Sgaborunsigned int	 epatterns, epattern_sz;
89210389Sgaborstruct epat	*epattern;
90210389Sgabor
91210389Sgabor/* For regex errors  */
92210389Sgaborchar	 re_error[RE_ERROR_BUF + 1];
93210389Sgabor
94210389Sgabor/* Command-line flags */
95210389Sgaborunsigned long long Aflag;	/* -A x: print x lines trailing each match */
96210389Sgaborunsigned long long Bflag;	/* -B x: print x lines leading each match */
97210389Sgaborbool	 Hflag;		/* -H: always print file name */
98210389Sgaborbool	 Lflag;		/* -L: only show names of files with no matches */
99210389Sgaborbool	 bflag;		/* -b: show block numbers for each match */
100210389Sgaborbool	 cflag;		/* -c: only show a count of matching lines */
101210389Sgaborbool	 hflag;		/* -h: don't print filename headers */
102210389Sgaborbool	 iflag;		/* -i: ignore case */
103210389Sgaborbool	 lflag;		/* -l: only show names of files with matches */
104210389Sgaborbool	 mflag;		/* -m x: stop reading the files after x matches */
105210389Sgaborunsigned long long mcount;	/* count for -m */
106210389Sgaborbool	 nflag;		/* -n: show line numbers in front of matching lines */
107210389Sgaborbool	 oflag;		/* -o: print only matching part */
108210389Sgaborbool	 qflag;		/* -q: quiet mode (don't output anything) */
109210389Sgaborbool	 sflag;		/* -s: silent mode (ignore errors) */
110210389Sgaborbool	 vflag;		/* -v: only show non-matching lines */
111210389Sgaborbool	 wflag;		/* -w: pattern must start and end on word boundaries */
112210389Sgaborbool	 xflag;		/* -x: pattern must match entire line */
113210389Sgaborbool	 lbflag;	/* --line-buffered */
114210389Sgaborbool	 nullflag;	/* --null */
115210389Sgaborbool	 exclflag;	/* --exclude */
116210389Sgaborchar	*label;		/* --label */
117210461Sgaborconst char *color;	/* --color */
118210389Sgaborint	 grepbehave = GREP_BASIC;	/* -EFGP: type of the regex */
119210389Sgaborint	 binbehave = BINFILE_BIN;	/* -aIU: handling of binary files */
120210389Sgaborint	 filebehave = FILE_STDIO;	/* -JZ: normal, gzip or bzip2 file */
121210461Sgaborint	 devbehave = DEV_READ;		/* -D: handling of devices */
122210461Sgaborint	 dirbehave = DIR_READ;		/* -dRr: handling of directories */
123210461Sgaborint	 linkbehave = LINK_READ;	/* -OpS: handling of symlinks */
124210389Sgabor
125210389Sgaborenum {
126210389Sgabor	BIN_OPT = CHAR_MAX + 1,
127210389Sgabor	COLOR_OPT,
128210389Sgabor	HELP_OPT,
129210389Sgabor	MMAP_OPT,
130210389Sgabor	LINEBUF_OPT,
131210389Sgabor	LABEL_OPT,
132210389Sgabor	NULL_OPT,
133210389Sgabor	R_EXCLUDE_OPT,
134210389Sgabor	R_INCLUDE_OPT,
135210389Sgabor	R_DEXCLUDE_OPT,
136210389Sgabor	R_DINCLUDE_OPT
137210389Sgabor};
138210389Sgabor
139210461Sgaborstatic inline const char	*init_color(const char *);
140210461Sgabor
141210389Sgabor/* Housekeeping */
142210389Sgaborbool	 first = true;	/* flag whether we are processing the first match */
143210389Sgaborbool	 prev;		/* flag whether or not the previous line matched */
144210389Sgaborint	 tail;		/* lines left to print */
145210389Sgaborbool	 notfound;	/* file not found */
146210389Sgabor
147210389Sgaborextern char	*__progname;
148210389Sgabor
149210389Sgabor/*
150210389Sgabor * Prints usage information and returns 2.
151210389Sgabor */
152210389Sgaborstatic void
153210389Sgaborusage(void)
154210389Sgabor{
155210389Sgabor	fprintf(stderr, getstr(4), __progname);
156210389Sgabor	fprintf(stderr, "%s", getstr(5));
157210389Sgabor	fprintf(stderr, "%s", getstr(5));
158210389Sgabor	fprintf(stderr, "%s", getstr(6));
159210389Sgabor	fprintf(stderr, "%s", getstr(7));
160210389Sgabor	exit(2);
161210389Sgabor}
162210389Sgabor
163210389Sgaborstatic const char	*optstr = "0123456789A:B:C:D:EFGHIJLOPSRUVZabcd:e:f:hilm:nopqrsuvwxy";
164210389Sgabor
165210389Sgaborstruct option long_options[] =
166210389Sgabor{
167210389Sgabor	{"binary-files",	required_argument,	NULL, BIN_OPT},
168210389Sgabor	{"help",		no_argument,		NULL, HELP_OPT},
169210389Sgabor	{"mmap",		no_argument,		NULL, MMAP_OPT},
170210389Sgabor	{"line-buffered",	no_argument,		NULL, LINEBUF_OPT},
171210389Sgabor	{"label",		required_argument,	NULL, LABEL_OPT},
172210389Sgabor	{"null",		no_argument,		NULL, NULL_OPT},
173210389Sgabor	{"color",		optional_argument,	NULL, COLOR_OPT},
174210389Sgabor	{"colour",		optional_argument,	NULL, COLOR_OPT},
175210389Sgabor	{"exclude",		required_argument,	NULL, R_EXCLUDE_OPT},
176210389Sgabor	{"include",		required_argument,	NULL, R_INCLUDE_OPT},
177210389Sgabor	{"exclude-dir",		required_argument,	NULL, R_DEXCLUDE_OPT},
178210389Sgabor	{"include-dir",		required_argument,	NULL, R_DINCLUDE_OPT},
179210389Sgabor	{"after-context",	required_argument,	NULL, 'A'},
180210389Sgabor	{"text",		no_argument,		NULL, 'a'},
181210389Sgabor	{"before-context",	required_argument,	NULL, 'B'},
182210389Sgabor	{"byte-offset",		no_argument,		NULL, 'b'},
183210389Sgabor	{"context",		optional_argument,	NULL, 'C'},
184210389Sgabor	{"count",		no_argument,		NULL, 'c'},
185210389Sgabor	{"devices",		required_argument,	NULL, 'D'},
186210389Sgabor        {"directories",		required_argument,	NULL, 'd'},
187210389Sgabor	{"extended-regexp",	no_argument,		NULL, 'E'},
188210389Sgabor	{"regexp",		required_argument,	NULL, 'e'},
189210389Sgabor	{"fixed-strings",	no_argument,		NULL, 'F'},
190210389Sgabor	{"file",		required_argument,	NULL, 'f'},
191210389Sgabor	{"basic-regexp",	no_argument,		NULL, 'G'},
192210389Sgabor	{"no-filename",		no_argument,		NULL, 'h'},
193210389Sgabor	{"with-filename",	no_argument,		NULL, 'H'},
194210389Sgabor	{"ignore-case",		no_argument,		NULL, 'i'},
195210389Sgabor	{"bz2decompress",	no_argument,		NULL, 'J'},
196210389Sgabor	{"files-with-matches",	no_argument,		NULL, 'l'},
197210389Sgabor	{"files-without-match", no_argument,            NULL, 'L'},
198210389Sgabor	{"max-count",		required_argument,	NULL, 'm'},
199210389Sgabor	{"line-number",		no_argument,		NULL, 'n'},
200210389Sgabor	{"only-matching",	no_argument,		NULL, 'o'},
201210389Sgabor	{"quiet",		no_argument,		NULL, 'q'},
202210389Sgabor	{"silent",		no_argument,		NULL, 'q'},
203210389Sgabor	{"recursive",		no_argument,		NULL, 'r'},
204210389Sgabor	{"no-messages",		no_argument,		NULL, 's'},
205210389Sgabor	{"binary",		no_argument,		NULL, 'U'},
206210389Sgabor	{"unix-byte-offsets",	no_argument,		NULL, 'u'},
207210389Sgabor	{"invert-match",	no_argument,		NULL, 'v'},
208210389Sgabor	{"version",		no_argument,		NULL, 'V'},
209210389Sgabor	{"word-regexp",		no_argument,		NULL, 'w'},
210210389Sgabor	{"line-regexp",		no_argument,		NULL, 'x'},
211210389Sgabor	{"decompress",          no_argument,            NULL, 'Z'},
212210389Sgabor	{NULL,			no_argument,		NULL, 0}
213210389Sgabor};
214210389Sgabor
215210389Sgabor/*
216210389Sgabor * Adds a searching pattern to the internal array.
217210389Sgabor */
218210389Sgaborstatic void
219210389Sgaboradd_pattern(char *pat, size_t len)
220210389Sgabor{
221210389Sgabor
222210389Sgabor	/* Check if we can do a shortcut */
223210389Sgabor	if (len == 0 || matchall) {
224210389Sgabor		matchall = true;
225210389Sgabor		return;
226210389Sgabor	}
227210389Sgabor	/* Increase size if necessary */
228210389Sgabor	if (patterns == pattern_sz) {
229210389Sgabor		pattern_sz *= 2;
230210389Sgabor		pattern = grep_realloc(pattern, ++pattern_sz *
231210389Sgabor		    sizeof(*pattern));
232210389Sgabor	}
233210389Sgabor	if (len > 0 && pat[len - 1] == '\n')
234210389Sgabor		--len;
235210389Sgabor	/* pat may not be NUL-terminated */
236210389Sgabor	pattern[patterns] = grep_malloc(len + 1);
237210389Sgabor	memcpy(pattern[patterns], pat, len);
238210389Sgabor	pattern[patterns][len] = '\0';
239210389Sgabor	++patterns;
240210389Sgabor}
241210389Sgabor
242210389Sgabor/*
243210389Sgabor * Adds an include/exclude pattern to the internal array.
244210389Sgabor */
245210389Sgaborstatic void
246210389Sgaboradd_epattern(char *pat, size_t len, int type, int mode)
247210389Sgabor{
248210389Sgabor
249210389Sgabor	/* Increase size if necessary */
250210389Sgabor	if (epatterns == epattern_sz) {
251210389Sgabor		epattern_sz *= 2;
252210389Sgabor		epattern = grep_realloc(epattern, ++epattern_sz *
253210389Sgabor		    sizeof(struct epat));
254210389Sgabor	}
255210389Sgabor	if (len > 0 && pat[len - 1] == '\n')
256210389Sgabor		 --len;
257210389Sgabor	epattern[epatterns].pat = grep_malloc(len + 1);
258210389Sgabor	memcpy(epattern[epatterns].pat, pat, len);
259210389Sgabor	epattern[epatterns].pat[len] = '\0';
260210389Sgabor	epattern[epatterns].type = type;
261210389Sgabor	epattern[epatterns].mode = mode;
262210389Sgabor	++epatterns;
263210389Sgabor}
264210389Sgabor
265210389Sgabor/*
266210389Sgabor * Reads searching patterns from a file and adds them with add_pattern().
267210389Sgabor */
268210389Sgaborstatic void
269210389Sgaborread_patterns(const char *fn)
270210389Sgabor{
271210389Sgabor	FILE *f;
272210389Sgabor	char *line;
273210389Sgabor	size_t len;
274210389Sgabor
275210389Sgabor	if ((f = fopen(fn, "r")) == NULL)
276210389Sgabor		err(2, "%s", fn);
277210389Sgabor	while ((line = fgetln(f, &len)) != NULL)
278210389Sgabor		add_pattern(line, *line == '\n' ? 0 : len);
279210389Sgabor	if (ferror(f))
280210389Sgabor		err(2, "%s", fn);
281210389Sgabor	fclose(f);
282210389Sgabor}
283210389Sgabor
284210461Sgaborstatic inline const char *
285210461Sgaborinit_color(const char *d)
286210461Sgabor{
287210461Sgabor	char *c;
288210461Sgabor
289210461Sgabor	c = getenv("GREP_COLOR");
290210461Sgabor	return (c != NULL ? c : d);
291210461Sgabor}
292210461Sgabor
293210389Sgaborint
294210389Sgabormain(int argc, char *argv[])
295210389Sgabor{
296210389Sgabor	char **aargv, **eargv, *eopts;
297210389Sgabor	char *ep;
298210389Sgabor	unsigned long long l;
299210389Sgabor	unsigned int aargc, eargc, i;
300210389Sgabor	int c, lastc, needpattern, newarg, prevoptind;
301210389Sgabor
302210389Sgabor	setlocale(LC_ALL, "");
303210389Sgabor
304210389Sgabor#ifndef WITHOUT_NLS
305210389Sgabor	catalog = catopen("grep", NL_CAT_LOCALE);
306210389Sgabor#endif
307210389Sgabor
308210389Sgabor	/* Check what is the program name of the binary.  In this
309210389Sgabor	   way we can have all the funcionalities in one binary
310210389Sgabor	   without the need of scripting and using ugly hacks. */
311210389Sgabor	switch (__progname[0]) {
312210389Sgabor	case 'e':
313210389Sgabor		grepbehave = GREP_EXTENDED;
314210389Sgabor		break;
315210389Sgabor	case 'f':
316210389Sgabor		grepbehave = GREP_FIXED;
317210389Sgabor		break;
318210389Sgabor	case 'g':
319210389Sgabor		grepbehave = GREP_BASIC;
320210389Sgabor		break;
321210389Sgabor	case 'z':
322210389Sgabor		filebehave = FILE_GZIP;
323210389Sgabor		switch(__progname[1]) {
324210389Sgabor		case 'e':
325210389Sgabor			grepbehave = GREP_EXTENDED;
326210389Sgabor			break;
327210389Sgabor		case 'f':
328210389Sgabor			grepbehave = GREP_FIXED;
329210389Sgabor			break;
330210389Sgabor		case 'g':
331210389Sgabor			grepbehave = GREP_BASIC;
332210389Sgabor			break;
333210389Sgabor		}
334210389Sgabor		break;
335210389Sgabor	}
336210389Sgabor
337210389Sgabor	lastc = '\0';
338210389Sgabor	newarg = 1;
339210389Sgabor	prevoptind = 1;
340210389Sgabor	needpattern = 1;
341210389Sgabor
342210389Sgabor	eopts = getenv("GREP_OPTIONS");
343210389Sgabor
344210389Sgabor	eargc = 1;
345210389Sgabor	if (eopts != NULL) {
346210389Sgabor		char *str;
347210389Sgabor
348210389Sgabor		for(i = 0; i < strlen(eopts); i++)
349210389Sgabor			if (eopts[i] == ' ')
350210389Sgabor				eargc++;
351210389Sgabor
352210389Sgabor		eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1));
353210389Sgabor
354210389Sgabor		str = strtok(eopts, " ");
355210389Sgabor		eargc = 0;
356210389Sgabor
357210389Sgabor		while(str != NULL) {
358210389Sgabor			eargv[++eargc] = (char *)grep_malloc(sizeof(char) *
359210389Sgabor			    (strlen(str) + 1));
360210389Sgabor			strlcpy(eargv[eargc], str, strlen(str) + 1);
361210389Sgabor			str = strtok(NULL, " ");
362210389Sgabor		}
363210389Sgabor		eargv[++eargc] = NULL;
364210389Sgabor
365210430Sdelphij		aargv = (char **)grep_calloc(eargc + argc + 1,
366210430Sdelphij		    sizeof(char *));
367210389Sgabor		aargv[0] = argv[0];
368210389Sgabor
369210389Sgabor		for(i = 1; i < eargc; i++)
370210389Sgabor			aargv[i] = eargv[i];
371210389Sgabor		for(int j = 1; j < argc; j++)
372210389Sgabor			aargv[i++] = argv[j];
373210389Sgabor
374210389Sgabor		aargc = eargc + argc - 1;
375210389Sgabor
376210389Sgabor	} else {
377210389Sgabor		aargv = argv;
378210389Sgabor		aargc = argc;
379210389Sgabor	}
380210389Sgabor
381210389Sgabor	while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) !=
382210389Sgabor	    -1)) {
383210389Sgabor		switch (c) {
384210389Sgabor		case '0': case '1': case '2': case '3': case '4':
385210389Sgabor		case '5': case '6': case '7': case '8': case '9':
386210389Sgabor			if (newarg || !isdigit(lastc))
387210389Sgabor				Aflag = 0;
388210389Sgabor			else if (Aflag > LLONG_MAX / 10) {
389210389Sgabor				errno = ERANGE;
390210389Sgabor				err(2, NULL);
391210389Sgabor			}
392210389Sgabor			Aflag = Bflag = (Aflag * 10) + (c - '0');
393210389Sgabor			break;
394210389Sgabor		case 'C':
395210389Sgabor			if (optarg == NULL) {
396210389Sgabor				Aflag = Bflag = 2;
397210389Sgabor				break;
398210389Sgabor			}
399210389Sgabor			/* FALLTHROUGH */
400210389Sgabor		case 'A':
401210389Sgabor			/* FALLTHROUGH */
402210389Sgabor		case 'B':
403210389Sgabor			errno = 0;
404210389Sgabor			l = strtoull(optarg, &ep, 10);
405210389Sgabor			if (((errno == ERANGE) && (l == ULLONG_MAX)) ||
406210389Sgabor			    ((errno == EINVAL) && (l == 0)))
407210389Sgabor				err(2, NULL);
408210389Sgabor			else if (ep[0] != '\0') {
409210389Sgabor				errno = EINVAL;
410210389Sgabor				err(2, NULL);
411210389Sgabor			}
412210389Sgabor			if (c == 'A')
413210389Sgabor				Aflag = l;
414210389Sgabor			else if (c == 'B')
415210389Sgabor				Bflag = l;
416210389Sgabor			else
417210389Sgabor				Aflag = Bflag = l;
418210389Sgabor			break;
419210389Sgabor		case 'a':
420210389Sgabor			binbehave = BINFILE_TEXT;
421210389Sgabor			break;
422210389Sgabor		case 'b':
423210389Sgabor			bflag = true;
424210389Sgabor			break;
425210389Sgabor		case 'c':
426210389Sgabor			cflag = true;
427210389Sgabor			break;
428210389Sgabor		case 'D':
429210461Sgabor			if (strcasecmp(optarg, "skip") == 0)
430210389Sgabor				devbehave = DEV_SKIP;
431210461Sgabor			else if (strcasecmp(optarg, "read") == 0)
432210461Sgabor				devbehave = DEV_READ;
433210461Sgabor			else {
434210461Sgabor				errno = EINVAL;
435210461Sgabor				err(2, NULL);
436210461Sgabor			}
437210389Sgabor			break;
438210389Sgabor		case 'd':
439210461Sgabor			if (strcasecmp("recurse", optarg) == 0) {
440210389Sgabor				Hflag = true;
441210389Sgabor				dirbehave = DIR_RECURSE;
442210461Sgabor			} else if (strcasecmp("skip", optarg) == 0)
443210389Sgabor				dirbehave = DIR_SKIP;
444210461Sgabor			else if (strcasecmp("read", optarg) == 0)
445210461Sgabor				dirbehave = DIR_READ;
446210461Sgabor			else {
447210389Sgabor				errno = EINVAL;
448210389Sgabor				err(2, NULL);
449210389Sgabor			}
450210389Sgabor			break;
451210389Sgabor		case 'E':
452210389Sgabor			grepbehave = GREP_EXTENDED;
453210389Sgabor			break;
454210389Sgabor		case 'e':
455210389Sgabor			add_pattern(optarg, strlen(optarg));
456210389Sgabor			needpattern = 0;
457210389Sgabor			break;
458210389Sgabor		case 'F':
459210389Sgabor			grepbehave = GREP_FIXED;
460210389Sgabor			break;
461210389Sgabor		case 'f':
462210389Sgabor			read_patterns(optarg);
463210389Sgabor			needpattern = 0;
464210389Sgabor			break;
465210389Sgabor		case 'G':
466210389Sgabor			grepbehave = GREP_BASIC;
467210389Sgabor			break;
468210389Sgabor		case 'H':
469210389Sgabor			Hflag = true;
470210389Sgabor			break;
471210389Sgabor		case 'h':
472210389Sgabor			Hflag = false;
473210389Sgabor			hflag = true;
474210389Sgabor			break;
475210389Sgabor		case 'I':
476210389Sgabor			binbehave = BINFILE_SKIP;
477210389Sgabor			break;
478210389Sgabor		case 'i':
479210389Sgabor		case 'y':
480210389Sgabor			iflag =  true;
481210389Sgabor			cflags |= REG_ICASE;
482210389Sgabor			break;
483210389Sgabor		case 'J':
484210389Sgabor			filebehave = FILE_BZIP;
485210389Sgabor			break;
486210389Sgabor		case 'L':
487210389Sgabor			lflag = false;
488210461Sgabor			Lflag = true;
489210389Sgabor			break;
490210389Sgabor		case 'l':
491210389Sgabor			Lflag = false;
492210461Sgabor			lflag = true;
493210389Sgabor			break;
494210389Sgabor		case 'm':
495210389Sgabor			mflag = true;
496210389Sgabor			errno = 0;
497210389Sgabor			mcount = strtoull(optarg, &ep, 10);
498210389Sgabor			if (((errno == ERANGE) && (mcount == ULLONG_MAX)) ||
499210389Sgabor			    ((errno == EINVAL) && (mcount == 0)))
500210389Sgabor				err(2, NULL);
501210389Sgabor			else if (ep[0] != '\0') {
502210389Sgabor				errno = EINVAL;
503210389Sgabor				err(2, NULL);
504210389Sgabor			}
505210389Sgabor			break;
506210389Sgabor		case 'n':
507210389Sgabor			nflag = true;
508210389Sgabor			break;
509210389Sgabor		case 'O':
510210389Sgabor			linkbehave = LINK_EXPLICIT;
511210389Sgabor			break;
512210389Sgabor		case 'o':
513210389Sgabor			oflag = true;
514210389Sgabor			break;
515210389Sgabor		case 'p':
516210389Sgabor			linkbehave = LINK_SKIP;
517210389Sgabor			break;
518210389Sgabor		case 'q':
519210389Sgabor			qflag = true;
520210389Sgabor			break;
521210389Sgabor		case 'S':
522210461Sgabor			linkbehave = LINK_READ;
523210389Sgabor			break;
524210389Sgabor		case 'R':
525210389Sgabor		case 'r':
526210389Sgabor			dirbehave = DIR_RECURSE;
527210389Sgabor			Hflag = true;
528210389Sgabor			break;
529210389Sgabor		case 's':
530210389Sgabor			sflag = true;
531210389Sgabor			break;
532210389Sgabor		case 'U':
533210389Sgabor			binbehave = BINFILE_BIN;
534210389Sgabor			break;
535210389Sgabor		case 'u':
536210389Sgabor		case MMAP_OPT:
537210389Sgabor			/* noop, compatibility */
538210389Sgabor			break;
539210389Sgabor		case 'V':
540210389Sgabor			printf(getstr(10), __progname, VERSION);
541210389Sgabor			exit(0);
542210389Sgabor		case 'v':
543210389Sgabor			vflag = true;
544210389Sgabor			break;
545210389Sgabor		case 'w':
546210389Sgabor			wflag = true;
547210389Sgabor			break;
548210389Sgabor		case 'x':
549210389Sgabor			xflag = true;
550210389Sgabor			break;
551210389Sgabor		case 'Z':
552210389Sgabor			filebehave = FILE_GZIP;
553210389Sgabor			break;
554210389Sgabor		case BIN_OPT:
555210461Sgabor			if (strcasecmp("binary", optarg) == 0)
556210389Sgabor				binbehave = BINFILE_BIN;
557210461Sgabor			else if (strcasecmp("without-match", optarg) == 0)
558210389Sgabor				binbehave = BINFILE_SKIP;
559210461Sgabor			else if (strcasecmp("text", optarg) == 0)
560210389Sgabor				binbehave = BINFILE_TEXT;
561210389Sgabor			else
562210389Sgabor				errx(2, "%s", getstr(8));
563210389Sgabor			break;
564210389Sgabor		case COLOR_OPT:
565210461Sgabor			color = NULL;
566210461Sgabor			if (optarg == NULL || strcasecmp("auto", optarg) == 0 ||
567210461Sgabor			    strcasecmp("tty", optarg) == 0 ||
568210461Sgabor			    strcasecmp("if-tty", optarg) == 0) {
569210461Sgabor				char *term;
570210461Sgabor
571210461Sgabor				term = getenv("TERM");
572210461Sgabor				if (isatty(STDOUT_FILENO) && term != NULL &&
573210461Sgabor				    strcasecmp(term, "dumb") != 0)
574210461Sgabor					color = init_color("01;31");
575210461Sgabor			} else if (strcasecmp("always", optarg) == 0 ||
576210461Sgabor			    strcasecmp("yes", optarg) == 0 ||
577210461Sgabor			    strcasecmp("force", optarg) == 0) {
578210461Sgabor				color = init_color("01;31");
579210461Sgabor			} else if (strcasecmp("never", optarg) != 0 &&
580210461Sgabor			    strcasecmp("none", optarg) != 0 &&
581210461Sgabor			    strcasecmp("no", optarg) != 0)
582210389Sgabor				errx(2, "%s", getstr(3));
583210389Sgabor			break;
584210389Sgabor		case LABEL_OPT:
585210389Sgabor			label = optarg;
586210389Sgabor			break;
587210389Sgabor		case LINEBUF_OPT:
588210389Sgabor			lbflag = true;
589210389Sgabor			break;
590210389Sgabor		case NULL_OPT:
591210389Sgabor			nullflag = true;
592210389Sgabor			break;
593210389Sgabor		case R_INCLUDE_OPT:
594210389Sgabor			exclflag = true;
595210389Sgabor			add_epattern(basename(optarg), strlen(basename(optarg)),
596210389Sgabor			    FILE_PAT, INCL_PAT);
597210389Sgabor			break;
598210389Sgabor		case R_EXCLUDE_OPT:
599210389Sgabor			exclflag = true;
600210389Sgabor			add_epattern(basename(optarg), strlen(basename(optarg)),
601210389Sgabor			    FILE_PAT, EXCL_PAT);
602210389Sgabor			break;
603210389Sgabor		case R_DINCLUDE_OPT:
604210389Sgabor			exclflag = true;
605210389Sgabor			add_epattern(basename(optarg), strlen(basename(optarg)),
606210389Sgabor			    DIR_PAT, INCL_PAT);
607210389Sgabor			break;
608210389Sgabor		case R_DEXCLUDE_OPT:
609210389Sgabor			exclflag = true;
610210389Sgabor			add_epattern(basename(optarg), strlen(basename(optarg)),
611210389Sgabor			    DIR_PAT, EXCL_PAT);
612210389Sgabor			break;
613210389Sgabor		case HELP_OPT:
614210389Sgabor		default:
615210389Sgabor			usage();
616210389Sgabor		}
617210389Sgabor		lastc = c;
618210389Sgabor		newarg = optind != prevoptind;
619210389Sgabor		prevoptind = optind;
620210389Sgabor	}
621210389Sgabor	aargc -= optind;
622210389Sgabor	aargv += optind;
623210389Sgabor
624210389Sgabor	/* Fail if we don't have any pattern */
625210389Sgabor	if (aargc == 0 && needpattern)
626210389Sgabor		usage();
627210389Sgabor
628210389Sgabor	/* Process patterns from command line */
629210389Sgabor	if (aargc != 0 && needpattern) {
630210389Sgabor		add_pattern(*aargv, strlen(*aargv));
631210389Sgabor		--aargc;
632210389Sgabor		++aargv;
633210389Sgabor	}
634210389Sgabor
635210389Sgabor	switch (grepbehave) {
636210389Sgabor	case GREP_FIXED:
637210389Sgabor	case GREP_BASIC:
638210389Sgabor		break;
639210389Sgabor	case GREP_EXTENDED:
640210389Sgabor		cflags |= REG_EXTENDED;
641210389Sgabor		break;
642210389Sgabor	default:
643210389Sgabor		/* NOTREACHED */
644210389Sgabor		usage();
645210389Sgabor	}
646210389Sgabor
647210389Sgabor	fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern));
648210389Sgabor	r_pattern = grep_calloc(patterns, sizeof(*r_pattern));
649210389Sgabor/*
650210389Sgabor * XXX: fgrepcomp() and fastcomp() are workarounds for regexec() performance.
651210389Sgabor * Optimizations should be done there.
652210389Sgabor */
653210389Sgabor		/* Check if cheating is allowed (always is for fgrep). */
654210389Sgabor	if (grepbehave == GREP_FIXED) {
655210389Sgabor		for (i = 0; i < patterns; ++i)
656210389Sgabor			fgrepcomp(&fg_pattern[i], pattern[i]);
657210389Sgabor	} else {
658210389Sgabor		for (i = 0; i < patterns; ++i) {
659210389Sgabor			if (fastcomp(&fg_pattern[i], pattern[i])) {
660210389Sgabor				/* Fall back to full regex library */
661210389Sgabor				c = regcomp(&r_pattern[i], pattern[i], cflags);
662210389Sgabor				if (c != 0) {
663210389Sgabor					regerror(c, &r_pattern[i], re_error,
664210389Sgabor					    RE_ERROR_BUF);
665210389Sgabor					errx(2, "%s", re_error);
666210389Sgabor				}
667210389Sgabor			}
668210389Sgabor		}
669210389Sgabor	}
670210389Sgabor
671210389Sgabor	if (lbflag)
672210389Sgabor		setlinebuf(stdout);
673210389Sgabor
674210389Sgabor	if ((aargc == 0 || aargc == 1) && !Hflag)
675210389Sgabor		hflag = true;
676210389Sgabor
677210389Sgabor	if (aargc == 0)
678210389Sgabor		exit(!procfile("-"));
679210389Sgabor
680210389Sgabor	if (dirbehave == DIR_RECURSE)
681210389Sgabor		c = grep_tree(aargv);
682210389Sgabor	else
683210389Sgabor		for (c = 0; aargc--; ++aargv)
684210389Sgabor			c+= procfile(*aargv);
685210389Sgabor
686210389Sgabor#ifndef WITHOUT_NLS
687210389Sgabor	catclose(catalog);
688210389Sgabor#endif
689210389Sgabor
690210389Sgabor	/* Find out the correct return value according to the
691210389Sgabor	   results and the command line option. */
692210389Sgabor	exit(c ? (notfound ? (qflag ? 0 : 2) : 0) : (notfound ? 2 : 1));
693210389Sgabor}
694