grep.c revision 210578
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 210578 2010-07-29 00:11:14Z 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 */
88210578Sgaborunsigned int	 fpatterns, fpattern_sz;
89210578Sgaborunsigned int	 dpatterns, dpattern_sz;
90210578Sgaborstruct epat	*dpattern, *fpattern;
91210389Sgabor
92210389Sgabor/* For regex errors  */
93210389Sgaborchar	 re_error[RE_ERROR_BUF + 1];
94210389Sgabor
95210389Sgabor/* Command-line flags */
96210389Sgaborunsigned long long Aflag;	/* -A x: print x lines trailing each match */
97210389Sgaborunsigned long long Bflag;	/* -B x: print x lines leading each match */
98210389Sgaborbool	 Hflag;		/* -H: always print file name */
99210389Sgaborbool	 Lflag;		/* -L: only show names of files with no matches */
100210389Sgaborbool	 bflag;		/* -b: show block numbers for each match */
101210389Sgaborbool	 cflag;		/* -c: only show a count of matching lines */
102210389Sgaborbool	 hflag;		/* -h: don't print filename headers */
103210389Sgaborbool	 iflag;		/* -i: ignore case */
104210389Sgaborbool	 lflag;		/* -l: only show names of files with matches */
105210389Sgaborbool	 mflag;		/* -m x: stop reading the files after x matches */
106210389Sgaborunsigned long long mcount;	/* count for -m */
107210389Sgaborbool	 nflag;		/* -n: show line numbers in front of matching lines */
108210389Sgaborbool	 oflag;		/* -o: print only matching part */
109210389Sgaborbool	 qflag;		/* -q: quiet mode (don't output anything) */
110210389Sgaborbool	 sflag;		/* -s: silent mode (ignore errors) */
111210389Sgaborbool	 vflag;		/* -v: only show non-matching lines */
112210389Sgaborbool	 wflag;		/* -w: pattern must start and end on word boundaries */
113210389Sgaborbool	 xflag;		/* -x: pattern must match entire line */
114210389Sgaborbool	 lbflag;	/* --line-buffered */
115210389Sgaborbool	 nullflag;	/* --null */
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
125210578Sgaborbool	 dexclude, dinclude;	/* --exclude amd --include */
126210578Sgaborbool	 fexclude, finclude;	/* --exclude-dir and --include-dir */
127210578Sgabor
128210389Sgaborenum {
129210389Sgabor	BIN_OPT = CHAR_MAX + 1,
130210389Sgabor	COLOR_OPT,
131210389Sgabor	HELP_OPT,
132210389Sgabor	MMAP_OPT,
133210389Sgabor	LINEBUF_OPT,
134210389Sgabor	LABEL_OPT,
135210389Sgabor	NULL_OPT,
136210389Sgabor	R_EXCLUDE_OPT,
137210389Sgabor	R_INCLUDE_OPT,
138210389Sgabor	R_DEXCLUDE_OPT,
139210389Sgabor	R_DINCLUDE_OPT
140210389Sgabor};
141210389Sgabor
142210461Sgaborstatic inline const char	*init_color(const char *);
143210461Sgabor
144210389Sgabor/* Housekeeping */
145210389Sgaborbool	 first = true;	/* flag whether we are processing the first match */
146210389Sgaborbool	 prev;		/* flag whether or not the previous line matched */
147210389Sgaborint	 tail;		/* lines left to print */
148210389Sgaborbool	 notfound;	/* file not found */
149210389Sgabor
150210389Sgaborextern char	*__progname;
151210389Sgabor
152210389Sgabor/*
153210389Sgabor * Prints usage information and returns 2.
154210389Sgabor */
155210389Sgaborstatic void
156210389Sgaborusage(void)
157210389Sgabor{
158210389Sgabor	fprintf(stderr, getstr(4), __progname);
159210389Sgabor	fprintf(stderr, "%s", getstr(5));
160210389Sgabor	fprintf(stderr, "%s", getstr(5));
161210389Sgabor	fprintf(stderr, "%s", getstr(6));
162210389Sgabor	fprintf(stderr, "%s", getstr(7));
163210389Sgabor	exit(2);
164210389Sgabor}
165210389Sgabor
166210389Sgaborstatic const char	*optstr = "0123456789A:B:C:D:EFGHIJLOPSRUVZabcd:e:f:hilm:nopqrsuvwxy";
167210389Sgabor
168210389Sgaborstruct option long_options[] =
169210389Sgabor{
170210389Sgabor	{"binary-files",	required_argument,	NULL, BIN_OPT},
171210389Sgabor	{"help",		no_argument,		NULL, HELP_OPT},
172210389Sgabor	{"mmap",		no_argument,		NULL, MMAP_OPT},
173210389Sgabor	{"line-buffered",	no_argument,		NULL, LINEBUF_OPT},
174210389Sgabor	{"label",		required_argument,	NULL, LABEL_OPT},
175210389Sgabor	{"null",		no_argument,		NULL, NULL_OPT},
176210389Sgabor	{"color",		optional_argument,	NULL, COLOR_OPT},
177210389Sgabor	{"colour",		optional_argument,	NULL, COLOR_OPT},
178210389Sgabor	{"exclude",		required_argument,	NULL, R_EXCLUDE_OPT},
179210389Sgabor	{"include",		required_argument,	NULL, R_INCLUDE_OPT},
180210389Sgabor	{"exclude-dir",		required_argument,	NULL, R_DEXCLUDE_OPT},
181210389Sgabor	{"include-dir",		required_argument,	NULL, R_DINCLUDE_OPT},
182210389Sgabor	{"after-context",	required_argument,	NULL, 'A'},
183210389Sgabor	{"text",		no_argument,		NULL, 'a'},
184210389Sgabor	{"before-context",	required_argument,	NULL, 'B'},
185210389Sgabor	{"byte-offset",		no_argument,		NULL, 'b'},
186210389Sgabor	{"context",		optional_argument,	NULL, 'C'},
187210389Sgabor	{"count",		no_argument,		NULL, 'c'},
188210389Sgabor	{"devices",		required_argument,	NULL, 'D'},
189210389Sgabor        {"directories",		required_argument,	NULL, 'd'},
190210389Sgabor	{"extended-regexp",	no_argument,		NULL, 'E'},
191210389Sgabor	{"regexp",		required_argument,	NULL, 'e'},
192210389Sgabor	{"fixed-strings",	no_argument,		NULL, 'F'},
193210389Sgabor	{"file",		required_argument,	NULL, 'f'},
194210389Sgabor	{"basic-regexp",	no_argument,		NULL, 'G'},
195210389Sgabor	{"no-filename",		no_argument,		NULL, 'h'},
196210389Sgabor	{"with-filename",	no_argument,		NULL, 'H'},
197210389Sgabor	{"ignore-case",		no_argument,		NULL, 'i'},
198210389Sgabor	{"bz2decompress",	no_argument,		NULL, 'J'},
199210389Sgabor	{"files-with-matches",	no_argument,		NULL, 'l'},
200210389Sgabor	{"files-without-match", no_argument,            NULL, 'L'},
201210389Sgabor	{"max-count",		required_argument,	NULL, 'm'},
202210389Sgabor	{"line-number",		no_argument,		NULL, 'n'},
203210389Sgabor	{"only-matching",	no_argument,		NULL, 'o'},
204210389Sgabor	{"quiet",		no_argument,		NULL, 'q'},
205210389Sgabor	{"silent",		no_argument,		NULL, 'q'},
206210389Sgabor	{"recursive",		no_argument,		NULL, 'r'},
207210389Sgabor	{"no-messages",		no_argument,		NULL, 's'},
208210389Sgabor	{"binary",		no_argument,		NULL, 'U'},
209210389Sgabor	{"unix-byte-offsets",	no_argument,		NULL, 'u'},
210210389Sgabor	{"invert-match",	no_argument,		NULL, 'v'},
211210389Sgabor	{"version",		no_argument,		NULL, 'V'},
212210389Sgabor	{"word-regexp",		no_argument,		NULL, 'w'},
213210389Sgabor	{"line-regexp",		no_argument,		NULL, 'x'},
214210389Sgabor	{"decompress",          no_argument,            NULL, 'Z'},
215210389Sgabor	{NULL,			no_argument,		NULL, 0}
216210389Sgabor};
217210389Sgabor
218210389Sgabor/*
219210389Sgabor * Adds a searching pattern to the internal array.
220210389Sgabor */
221210389Sgaborstatic void
222210389Sgaboradd_pattern(char *pat, size_t len)
223210389Sgabor{
224210389Sgabor
225210389Sgabor	/* Check if we can do a shortcut */
226210389Sgabor	if (len == 0 || matchall) {
227210389Sgabor		matchall = true;
228210389Sgabor		return;
229210389Sgabor	}
230210389Sgabor	/* Increase size if necessary */
231210389Sgabor	if (patterns == pattern_sz) {
232210389Sgabor		pattern_sz *= 2;
233210389Sgabor		pattern = grep_realloc(pattern, ++pattern_sz *
234210389Sgabor		    sizeof(*pattern));
235210389Sgabor	}
236210389Sgabor	if (len > 0 && pat[len - 1] == '\n')
237210389Sgabor		--len;
238210389Sgabor	/* pat may not be NUL-terminated */
239210389Sgabor	pattern[patterns] = grep_malloc(len + 1);
240210578Sgabor	strlcpy(pattern[patterns], pat, len + 1);
241210389Sgabor	++patterns;
242210389Sgabor}
243210389Sgabor
244210389Sgabor/*
245210578Sgabor * Adds a file include/exclude pattern to the internal array.
246210389Sgabor */
247210389Sgaborstatic void
248210578Sgaboradd_fpattern(const char *pat, int mode)
249210389Sgabor{
250210389Sgabor
251210389Sgabor	/* Increase size if necessary */
252210578Sgabor	if (fpatterns == fpattern_sz) {
253210578Sgabor		fpattern_sz *= 2;
254210578Sgabor		fpattern = grep_realloc(fpattern, ++fpattern_sz *
255210389Sgabor		    sizeof(struct epat));
256210389Sgabor	}
257210578Sgabor	fpattern[fpatterns].pat = grep_strdup(pat);
258210578Sgabor	fpattern[fpatterns].mode = mode;
259210578Sgabor	++fpatterns;
260210389Sgabor}
261210389Sgabor
262210389Sgabor/*
263210578Sgabor * Adds a directory include/exclude pattern to the internal array.
264210578Sgabor */
265210578Sgaborstatic void
266210578Sgaboradd_dpattern(const char *pat, int mode)
267210578Sgabor{
268210578Sgabor
269210578Sgabor	/* Increase size if necessary */
270210578Sgabor	if (dpatterns == dpattern_sz) {
271210578Sgabor		dpattern_sz *= 2;
272210578Sgabor		dpattern = grep_realloc(dpattern, ++dpattern_sz *
273210578Sgabor		    sizeof(struct epat));
274210578Sgabor	}
275210578Sgabor	dpattern[dpatterns].pat = grep_strdup(pat);
276210578Sgabor	dpattern[dpatterns].mode = mode;
277210578Sgabor	++dpatterns;
278210578Sgabor}
279210578Sgabor
280210578Sgabor/*
281210389Sgabor * Reads searching patterns from a file and adds them with add_pattern().
282210389Sgabor */
283210389Sgaborstatic void
284210389Sgaborread_patterns(const char *fn)
285210389Sgabor{
286210389Sgabor	FILE *f;
287210389Sgabor	char *line;
288210389Sgabor	size_t len;
289210389Sgabor
290210389Sgabor	if ((f = fopen(fn, "r")) == NULL)
291210389Sgabor		err(2, "%s", fn);
292210389Sgabor	while ((line = fgetln(f, &len)) != NULL)
293210389Sgabor		add_pattern(line, *line == '\n' ? 0 : len);
294210389Sgabor	if (ferror(f))
295210389Sgabor		err(2, "%s", fn);
296210389Sgabor	fclose(f);
297210389Sgabor}
298210389Sgabor
299210461Sgaborstatic inline const char *
300210461Sgaborinit_color(const char *d)
301210461Sgabor{
302210461Sgabor	char *c;
303210461Sgabor
304210461Sgabor	c = getenv("GREP_COLOR");
305210461Sgabor	return (c != NULL ? c : d);
306210461Sgabor}
307210461Sgabor
308210389Sgaborint
309210389Sgabormain(int argc, char *argv[])
310210389Sgabor{
311210389Sgabor	char **aargv, **eargv, *eopts;
312210389Sgabor	char *ep;
313210389Sgabor	unsigned long long l;
314210389Sgabor	unsigned int aargc, eargc, i;
315210389Sgabor	int c, lastc, needpattern, newarg, prevoptind;
316210389Sgabor
317210389Sgabor	setlocale(LC_ALL, "");
318210389Sgabor
319210389Sgabor#ifndef WITHOUT_NLS
320210389Sgabor	catalog = catopen("grep", NL_CAT_LOCALE);
321210389Sgabor#endif
322210389Sgabor
323210389Sgabor	/* Check what is the program name of the binary.  In this
324210389Sgabor	   way we can have all the funcionalities in one binary
325210389Sgabor	   without the need of scripting and using ugly hacks. */
326210389Sgabor	switch (__progname[0]) {
327210389Sgabor	case 'e':
328210389Sgabor		grepbehave = GREP_EXTENDED;
329210389Sgabor		break;
330210389Sgabor	case 'f':
331210389Sgabor		grepbehave = GREP_FIXED;
332210389Sgabor		break;
333210389Sgabor	case 'g':
334210389Sgabor		grepbehave = GREP_BASIC;
335210389Sgabor		break;
336210389Sgabor	case 'z':
337210389Sgabor		filebehave = FILE_GZIP;
338210389Sgabor		switch(__progname[1]) {
339210389Sgabor		case 'e':
340210389Sgabor			grepbehave = GREP_EXTENDED;
341210389Sgabor			break;
342210389Sgabor		case 'f':
343210389Sgabor			grepbehave = GREP_FIXED;
344210389Sgabor			break;
345210389Sgabor		case 'g':
346210389Sgabor			grepbehave = GREP_BASIC;
347210389Sgabor			break;
348210389Sgabor		}
349210389Sgabor		break;
350210389Sgabor	}
351210389Sgabor
352210389Sgabor	lastc = '\0';
353210389Sgabor	newarg = 1;
354210389Sgabor	prevoptind = 1;
355210389Sgabor	needpattern = 1;
356210389Sgabor
357210389Sgabor	eopts = getenv("GREP_OPTIONS");
358210389Sgabor
359210389Sgabor	eargc = 1;
360210389Sgabor	if (eopts != NULL) {
361210389Sgabor		char *str;
362210389Sgabor
363210389Sgabor		for(i = 0; i < strlen(eopts); i++)
364210389Sgabor			if (eopts[i] == ' ')
365210389Sgabor				eargc++;
366210389Sgabor
367210389Sgabor		eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1));
368210389Sgabor
369210389Sgabor		str = strtok(eopts, " ");
370210389Sgabor		eargc = 0;
371210389Sgabor
372210389Sgabor		while(str != NULL) {
373210389Sgabor			eargv[++eargc] = (char *)grep_malloc(sizeof(char) *
374210389Sgabor			    (strlen(str) + 1));
375210389Sgabor			strlcpy(eargv[eargc], str, strlen(str) + 1);
376210389Sgabor			str = strtok(NULL, " ");
377210389Sgabor		}
378210389Sgabor		eargv[++eargc] = NULL;
379210389Sgabor
380210430Sdelphij		aargv = (char **)grep_calloc(eargc + argc + 1,
381210430Sdelphij		    sizeof(char *));
382210389Sgabor		aargv[0] = argv[0];
383210389Sgabor
384210389Sgabor		for(i = 1; i < eargc; i++)
385210389Sgabor			aargv[i] = eargv[i];
386210389Sgabor		for(int j = 1; j < argc; j++)
387210389Sgabor			aargv[i++] = argv[j];
388210389Sgabor
389210389Sgabor		aargc = eargc + argc - 1;
390210389Sgabor
391210389Sgabor	} else {
392210389Sgabor		aargv = argv;
393210389Sgabor		aargc = argc;
394210389Sgabor	}
395210389Sgabor
396210389Sgabor	while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) !=
397210389Sgabor	    -1)) {
398210389Sgabor		switch (c) {
399210389Sgabor		case '0': case '1': case '2': case '3': case '4':
400210389Sgabor		case '5': case '6': case '7': case '8': case '9':
401210389Sgabor			if (newarg || !isdigit(lastc))
402210389Sgabor				Aflag = 0;
403210389Sgabor			else if (Aflag > LLONG_MAX / 10) {
404210389Sgabor				errno = ERANGE;
405210389Sgabor				err(2, NULL);
406210389Sgabor			}
407210389Sgabor			Aflag = Bflag = (Aflag * 10) + (c - '0');
408210389Sgabor			break;
409210389Sgabor		case 'C':
410210389Sgabor			if (optarg == NULL) {
411210389Sgabor				Aflag = Bflag = 2;
412210389Sgabor				break;
413210389Sgabor			}
414210389Sgabor			/* FALLTHROUGH */
415210389Sgabor		case 'A':
416210389Sgabor			/* FALLTHROUGH */
417210389Sgabor		case 'B':
418210389Sgabor			errno = 0;
419210389Sgabor			l = strtoull(optarg, &ep, 10);
420210389Sgabor			if (((errno == ERANGE) && (l == ULLONG_MAX)) ||
421210389Sgabor			    ((errno == EINVAL) && (l == 0)))
422210389Sgabor				err(2, NULL);
423210389Sgabor			else if (ep[0] != '\0') {
424210389Sgabor				errno = EINVAL;
425210389Sgabor				err(2, NULL);
426210389Sgabor			}
427210389Sgabor			if (c == 'A')
428210389Sgabor				Aflag = l;
429210389Sgabor			else if (c == 'B')
430210389Sgabor				Bflag = l;
431210389Sgabor			else
432210389Sgabor				Aflag = Bflag = l;
433210389Sgabor			break;
434210389Sgabor		case 'a':
435210389Sgabor			binbehave = BINFILE_TEXT;
436210389Sgabor			break;
437210389Sgabor		case 'b':
438210389Sgabor			bflag = true;
439210389Sgabor			break;
440210389Sgabor		case 'c':
441210389Sgabor			cflag = true;
442210389Sgabor			break;
443210389Sgabor		case 'D':
444210461Sgabor			if (strcasecmp(optarg, "skip") == 0)
445210389Sgabor				devbehave = DEV_SKIP;
446210461Sgabor			else if (strcasecmp(optarg, "read") == 0)
447210461Sgabor				devbehave = DEV_READ;
448210461Sgabor			else {
449210461Sgabor				errno = EINVAL;
450210461Sgabor				err(2, NULL);
451210461Sgabor			}
452210389Sgabor			break;
453210389Sgabor		case 'd':
454210461Sgabor			if (strcasecmp("recurse", optarg) == 0) {
455210389Sgabor				Hflag = true;
456210389Sgabor				dirbehave = DIR_RECURSE;
457210461Sgabor			} else if (strcasecmp("skip", optarg) == 0)
458210389Sgabor				dirbehave = DIR_SKIP;
459210461Sgabor			else if (strcasecmp("read", optarg) == 0)
460210461Sgabor				dirbehave = DIR_READ;
461210461Sgabor			else {
462210389Sgabor				errno = EINVAL;
463210389Sgabor				err(2, NULL);
464210389Sgabor			}
465210389Sgabor			break;
466210389Sgabor		case 'E':
467210389Sgabor			grepbehave = GREP_EXTENDED;
468210389Sgabor			break;
469210389Sgabor		case 'e':
470210389Sgabor			add_pattern(optarg, strlen(optarg));
471210389Sgabor			needpattern = 0;
472210389Sgabor			break;
473210389Sgabor		case 'F':
474210389Sgabor			grepbehave = GREP_FIXED;
475210389Sgabor			break;
476210389Sgabor		case 'f':
477210389Sgabor			read_patterns(optarg);
478210389Sgabor			needpattern = 0;
479210389Sgabor			break;
480210389Sgabor		case 'G':
481210389Sgabor			grepbehave = GREP_BASIC;
482210389Sgabor			break;
483210389Sgabor		case 'H':
484210389Sgabor			Hflag = true;
485210389Sgabor			break;
486210389Sgabor		case 'h':
487210389Sgabor			Hflag = false;
488210389Sgabor			hflag = true;
489210389Sgabor			break;
490210389Sgabor		case 'I':
491210389Sgabor			binbehave = BINFILE_SKIP;
492210389Sgabor			break;
493210389Sgabor		case 'i':
494210389Sgabor		case 'y':
495210389Sgabor			iflag =  true;
496210389Sgabor			cflags |= REG_ICASE;
497210389Sgabor			break;
498210389Sgabor		case 'J':
499210389Sgabor			filebehave = FILE_BZIP;
500210389Sgabor			break;
501210389Sgabor		case 'L':
502210389Sgabor			lflag = false;
503210461Sgabor			Lflag = true;
504210389Sgabor			break;
505210389Sgabor		case 'l':
506210389Sgabor			Lflag = false;
507210461Sgabor			lflag = true;
508210389Sgabor			break;
509210389Sgabor		case 'm':
510210389Sgabor			mflag = true;
511210389Sgabor			errno = 0;
512210389Sgabor			mcount = strtoull(optarg, &ep, 10);
513210389Sgabor			if (((errno == ERANGE) && (mcount == ULLONG_MAX)) ||
514210389Sgabor			    ((errno == EINVAL) && (mcount == 0)))
515210389Sgabor				err(2, NULL);
516210389Sgabor			else if (ep[0] != '\0') {
517210389Sgabor				errno = EINVAL;
518210389Sgabor				err(2, NULL);
519210389Sgabor			}
520210389Sgabor			break;
521210389Sgabor		case 'n':
522210389Sgabor			nflag = true;
523210389Sgabor			break;
524210389Sgabor		case 'O':
525210389Sgabor			linkbehave = LINK_EXPLICIT;
526210389Sgabor			break;
527210389Sgabor		case 'o':
528210389Sgabor			oflag = true;
529210389Sgabor			break;
530210389Sgabor		case 'p':
531210389Sgabor			linkbehave = LINK_SKIP;
532210389Sgabor			break;
533210389Sgabor		case 'q':
534210389Sgabor			qflag = true;
535210389Sgabor			break;
536210389Sgabor		case 'S':
537210461Sgabor			linkbehave = LINK_READ;
538210389Sgabor			break;
539210389Sgabor		case 'R':
540210389Sgabor		case 'r':
541210389Sgabor			dirbehave = DIR_RECURSE;
542210389Sgabor			Hflag = true;
543210389Sgabor			break;
544210389Sgabor		case 's':
545210389Sgabor			sflag = true;
546210389Sgabor			break;
547210389Sgabor		case 'U':
548210389Sgabor			binbehave = BINFILE_BIN;
549210389Sgabor			break;
550210389Sgabor		case 'u':
551210389Sgabor		case MMAP_OPT:
552210389Sgabor			/* noop, compatibility */
553210389Sgabor			break;
554210389Sgabor		case 'V':
555210389Sgabor			printf(getstr(10), __progname, VERSION);
556210389Sgabor			exit(0);
557210389Sgabor		case 'v':
558210389Sgabor			vflag = true;
559210389Sgabor			break;
560210389Sgabor		case 'w':
561210389Sgabor			wflag = true;
562210389Sgabor			break;
563210389Sgabor		case 'x':
564210389Sgabor			xflag = true;
565210389Sgabor			break;
566210389Sgabor		case 'Z':
567210389Sgabor			filebehave = FILE_GZIP;
568210389Sgabor			break;
569210389Sgabor		case BIN_OPT:
570210461Sgabor			if (strcasecmp("binary", optarg) == 0)
571210389Sgabor				binbehave = BINFILE_BIN;
572210461Sgabor			else if (strcasecmp("without-match", optarg) == 0)
573210389Sgabor				binbehave = BINFILE_SKIP;
574210461Sgabor			else if (strcasecmp("text", optarg) == 0)
575210389Sgabor				binbehave = BINFILE_TEXT;
576210389Sgabor			else
577210389Sgabor				errx(2, "%s", getstr(8));
578210389Sgabor			break;
579210389Sgabor		case COLOR_OPT:
580210461Sgabor			color = NULL;
581210461Sgabor			if (optarg == NULL || strcasecmp("auto", optarg) == 0 ||
582210461Sgabor			    strcasecmp("tty", optarg) == 0 ||
583210461Sgabor			    strcasecmp("if-tty", optarg) == 0) {
584210461Sgabor				char *term;
585210461Sgabor
586210461Sgabor				term = getenv("TERM");
587210461Sgabor				if (isatty(STDOUT_FILENO) && term != NULL &&
588210461Sgabor				    strcasecmp(term, "dumb") != 0)
589210461Sgabor					color = init_color("01;31");
590210461Sgabor			} else if (strcasecmp("always", optarg) == 0 ||
591210461Sgabor			    strcasecmp("yes", optarg) == 0 ||
592210461Sgabor			    strcasecmp("force", optarg) == 0) {
593210461Sgabor				color = init_color("01;31");
594210461Sgabor			} else if (strcasecmp("never", optarg) != 0 &&
595210461Sgabor			    strcasecmp("none", optarg) != 0 &&
596210461Sgabor			    strcasecmp("no", optarg) != 0)
597210389Sgabor				errx(2, "%s", getstr(3));
598210389Sgabor			break;
599210389Sgabor		case LABEL_OPT:
600210389Sgabor			label = optarg;
601210389Sgabor			break;
602210389Sgabor		case LINEBUF_OPT:
603210389Sgabor			lbflag = true;
604210389Sgabor			break;
605210389Sgabor		case NULL_OPT:
606210389Sgabor			nullflag = true;
607210389Sgabor			break;
608210389Sgabor		case R_INCLUDE_OPT:
609210578Sgabor			finclude = true;
610210578Sgabor			add_fpattern(optarg, INCL_PAT);
611210389Sgabor			break;
612210389Sgabor		case R_EXCLUDE_OPT:
613210578Sgabor			fexclude = true;
614210578Sgabor			add_fpattern(optarg, EXCL_PAT);
615210389Sgabor			break;
616210389Sgabor		case R_DINCLUDE_OPT:
617210578Sgabor			dexclude = true;
618210578Sgabor			add_dpattern(optarg, INCL_PAT);
619210389Sgabor			break;
620210389Sgabor		case R_DEXCLUDE_OPT:
621210578Sgabor			dinclude = true;
622210578Sgabor			add_dpattern(optarg, EXCL_PAT);
623210389Sgabor			break;
624210389Sgabor		case HELP_OPT:
625210389Sgabor		default:
626210389Sgabor			usage();
627210389Sgabor		}
628210389Sgabor		lastc = c;
629210389Sgabor		newarg = optind != prevoptind;
630210389Sgabor		prevoptind = optind;
631210389Sgabor	}
632210389Sgabor	aargc -= optind;
633210389Sgabor	aargv += optind;
634210389Sgabor
635210389Sgabor	/* Fail if we don't have any pattern */
636210389Sgabor	if (aargc == 0 && needpattern)
637210389Sgabor		usage();
638210389Sgabor
639210389Sgabor	/* Process patterns from command line */
640210389Sgabor	if (aargc != 0 && needpattern) {
641210389Sgabor		add_pattern(*aargv, strlen(*aargv));
642210389Sgabor		--aargc;
643210389Sgabor		++aargv;
644210389Sgabor	}
645210389Sgabor
646210389Sgabor	switch (grepbehave) {
647210389Sgabor	case GREP_FIXED:
648210389Sgabor	case GREP_BASIC:
649210389Sgabor		break;
650210389Sgabor	case GREP_EXTENDED:
651210389Sgabor		cflags |= REG_EXTENDED;
652210389Sgabor		break;
653210389Sgabor	default:
654210389Sgabor		/* NOTREACHED */
655210389Sgabor		usage();
656210389Sgabor	}
657210389Sgabor
658210389Sgabor	fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern));
659210389Sgabor	r_pattern = grep_calloc(patterns, sizeof(*r_pattern));
660210389Sgabor/*
661210389Sgabor * XXX: fgrepcomp() and fastcomp() are workarounds for regexec() performance.
662210389Sgabor * Optimizations should be done there.
663210389Sgabor */
664210389Sgabor		/* Check if cheating is allowed (always is for fgrep). */
665210389Sgabor	if (grepbehave == GREP_FIXED) {
666210389Sgabor		for (i = 0; i < patterns; ++i)
667210389Sgabor			fgrepcomp(&fg_pattern[i], pattern[i]);
668210389Sgabor	} else {
669210389Sgabor		for (i = 0; i < patterns; ++i) {
670210389Sgabor			if (fastcomp(&fg_pattern[i], pattern[i])) {
671210389Sgabor				/* Fall back to full regex library */
672210389Sgabor				c = regcomp(&r_pattern[i], pattern[i], cflags);
673210389Sgabor				if (c != 0) {
674210389Sgabor					regerror(c, &r_pattern[i], re_error,
675210389Sgabor					    RE_ERROR_BUF);
676210389Sgabor					errx(2, "%s", re_error);
677210389Sgabor				}
678210389Sgabor			}
679210389Sgabor		}
680210389Sgabor	}
681210389Sgabor
682210389Sgabor	if (lbflag)
683210389Sgabor		setlinebuf(stdout);
684210389Sgabor
685210389Sgabor	if ((aargc == 0 || aargc == 1) && !Hflag)
686210389Sgabor		hflag = true;
687210389Sgabor
688210389Sgabor	if (aargc == 0)
689210389Sgabor		exit(!procfile("-"));
690210389Sgabor
691210389Sgabor	if (dirbehave == DIR_RECURSE)
692210389Sgabor		c = grep_tree(aargv);
693210389Sgabor	else
694210578Sgabor		for (c = 0; aargc--; ++aargv) {
695210578Sgabor			if ((finclude || fexclude) && !file_matching(*aargv))
696210578Sgabor				continue;
697210389Sgabor			c+= procfile(*aargv);
698210578Sgabor		}
699210389Sgabor
700210389Sgabor#ifndef WITHOUT_NLS
701210389Sgabor	catclose(catalog);
702210389Sgabor#endif
703210389Sgabor
704210389Sgabor	/* Find out the correct return value according to the
705210389Sgabor	   results and the command line option. */
706210389Sgabor	exit(c ? (notfound ? (qflag ? 0 : 2) : 0) : (notfound ? 2 : 1));
707210389Sgabor}
708