grep.c revision 210622
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 210622 2010-07-29 18:02:57Z 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",
64210622Sgabor/* 3*/	"unknown %s 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",
69210622Sgabor/* 8*/	"Binary file %s matches\n",
70210622Sgabor/* 9*/	"%s (BSD grep) %s\n",
71210389Sgabor};
72210389Sgabor
73210389Sgabor/* Flags passed to regcomp() and regexec() */
74210389Sgaborint		 cflags = 0;
75210389Sgaborint		 eflags = REG_STARTEND;
76210389Sgabor
77210389Sgabor/* Shortcut for matching all cases like empty regex */
78210389Sgaborbool		 matchall;
79210389Sgabor
80210389Sgabor/* Searching patterns */
81210389Sgaborunsigned int	 patterns, pattern_sz;
82210389Sgaborchar		**pattern;
83210389Sgaborregex_t		*r_pattern;
84210389Sgaborfastgrep_t	*fg_pattern;
85210389Sgabor
86210389Sgabor/* Filename exclusion/inclusion patterns */
87210578Sgaborunsigned int	 fpatterns, fpattern_sz;
88210578Sgaborunsigned int	 dpatterns, dpattern_sz;
89210578Sgaborstruct epat	*dpattern, *fpattern;
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 */
115210389Sgaborchar	*label;		/* --label */
116210461Sgaborconst char *color;	/* --color */
117210389Sgaborint	 grepbehave = GREP_BASIC;	/* -EFGP: type of the regex */
118210389Sgaborint	 binbehave = BINFILE_BIN;	/* -aIU: handling of binary files */
119210389Sgaborint	 filebehave = FILE_STDIO;	/* -JZ: normal, gzip or bzip2 file */
120210461Sgaborint	 devbehave = DEV_READ;		/* -D: handling of devices */
121210461Sgaborint	 dirbehave = DIR_READ;		/* -dRr: handling of directories */
122210461Sgaborint	 linkbehave = LINK_READ;	/* -OpS: handling of symlinks */
123210389Sgabor
124210578Sgaborbool	 dexclude, dinclude;	/* --exclude amd --include */
125210578Sgaborbool	 fexclude, finclude;	/* --exclude-dir and --include-dir */
126210578Sgabor
127210389Sgaborenum {
128210389Sgabor	BIN_OPT = CHAR_MAX + 1,
129210389Sgabor	COLOR_OPT,
130210389Sgabor	HELP_OPT,
131210389Sgabor	MMAP_OPT,
132210389Sgabor	LINEBUF_OPT,
133210389Sgabor	LABEL_OPT,
134210389Sgabor	NULL_OPT,
135210389Sgabor	R_EXCLUDE_OPT,
136210389Sgabor	R_INCLUDE_OPT,
137210389Sgabor	R_DEXCLUDE_OPT,
138210389Sgabor	R_DINCLUDE_OPT
139210389Sgabor};
140210389Sgabor
141210461Sgaborstatic inline const char	*init_color(const char *);
142210461Sgabor
143210389Sgabor/* Housekeeping */
144210389Sgaborbool	 first = true;	/* flag whether we are processing the first match */
145210389Sgaborbool	 prev;		/* flag whether or not the previous line matched */
146210389Sgaborint	 tail;		/* lines left to print */
147210389Sgaborbool	 notfound;	/* file not found */
148210389Sgabor
149210389Sgaborextern char	*__progname;
150210389Sgabor
151210389Sgabor/*
152210389Sgabor * Prints usage information and returns 2.
153210389Sgabor */
154210389Sgaborstatic void
155210389Sgaborusage(void)
156210389Sgabor{
157210389Sgabor	fprintf(stderr, getstr(4), __progname);
158210389Sgabor	fprintf(stderr, "%s", getstr(5));
159210389Sgabor	fprintf(stderr, "%s", getstr(5));
160210389Sgabor	fprintf(stderr, "%s", getstr(6));
161210389Sgabor	fprintf(stderr, "%s", getstr(7));
162210389Sgabor	exit(2);
163210389Sgabor}
164210389Sgabor
165210389Sgaborstatic const char	*optstr = "0123456789A:B:C:D:EFGHIJLOPSRUVZabcd:e:f:hilm:nopqrsuvwxy";
166210389Sgabor
167210389Sgaborstruct option long_options[] =
168210389Sgabor{
169210389Sgabor	{"binary-files",	required_argument,	NULL, BIN_OPT},
170210389Sgabor	{"help",		no_argument,		NULL, HELP_OPT},
171210389Sgabor	{"mmap",		no_argument,		NULL, MMAP_OPT},
172210389Sgabor	{"line-buffered",	no_argument,		NULL, LINEBUF_OPT},
173210389Sgabor	{"label",		required_argument,	NULL, LABEL_OPT},
174210389Sgabor	{"null",		no_argument,		NULL, NULL_OPT},
175210389Sgabor	{"color",		optional_argument,	NULL, COLOR_OPT},
176210389Sgabor	{"colour",		optional_argument,	NULL, COLOR_OPT},
177210389Sgabor	{"exclude",		required_argument,	NULL, R_EXCLUDE_OPT},
178210389Sgabor	{"include",		required_argument,	NULL, R_INCLUDE_OPT},
179210389Sgabor	{"exclude-dir",		required_argument,	NULL, R_DEXCLUDE_OPT},
180210389Sgabor	{"include-dir",		required_argument,	NULL, R_DINCLUDE_OPT},
181210389Sgabor	{"after-context",	required_argument,	NULL, 'A'},
182210389Sgabor	{"text",		no_argument,		NULL, 'a'},
183210389Sgabor	{"before-context",	required_argument,	NULL, 'B'},
184210389Sgabor	{"byte-offset",		no_argument,		NULL, 'b'},
185210389Sgabor	{"context",		optional_argument,	NULL, 'C'},
186210389Sgabor	{"count",		no_argument,		NULL, 'c'},
187210389Sgabor	{"devices",		required_argument,	NULL, 'D'},
188210389Sgabor        {"directories",		required_argument,	NULL, 'd'},
189210389Sgabor	{"extended-regexp",	no_argument,		NULL, 'E'},
190210389Sgabor	{"regexp",		required_argument,	NULL, 'e'},
191210389Sgabor	{"fixed-strings",	no_argument,		NULL, 'F'},
192210389Sgabor	{"file",		required_argument,	NULL, 'f'},
193210389Sgabor	{"basic-regexp",	no_argument,		NULL, 'G'},
194210389Sgabor	{"no-filename",		no_argument,		NULL, 'h'},
195210389Sgabor	{"with-filename",	no_argument,		NULL, 'H'},
196210389Sgabor	{"ignore-case",		no_argument,		NULL, 'i'},
197210389Sgabor	{"bz2decompress",	no_argument,		NULL, 'J'},
198210389Sgabor	{"files-with-matches",	no_argument,		NULL, 'l'},
199210389Sgabor	{"files-without-match", no_argument,            NULL, 'L'},
200210389Sgabor	{"max-count",		required_argument,	NULL, 'm'},
201210389Sgabor	{"line-number",		no_argument,		NULL, 'n'},
202210389Sgabor	{"only-matching",	no_argument,		NULL, 'o'},
203210389Sgabor	{"quiet",		no_argument,		NULL, 'q'},
204210389Sgabor	{"silent",		no_argument,		NULL, 'q'},
205210389Sgabor	{"recursive",		no_argument,		NULL, 'r'},
206210389Sgabor	{"no-messages",		no_argument,		NULL, 's'},
207210389Sgabor	{"binary",		no_argument,		NULL, 'U'},
208210389Sgabor	{"unix-byte-offsets",	no_argument,		NULL, 'u'},
209210389Sgabor	{"invert-match",	no_argument,		NULL, 'v'},
210210389Sgabor	{"version",		no_argument,		NULL, 'V'},
211210389Sgabor	{"word-regexp",		no_argument,		NULL, 'w'},
212210389Sgabor	{"line-regexp",		no_argument,		NULL, 'x'},
213210389Sgabor	{"decompress",          no_argument,            NULL, 'Z'},
214210389Sgabor	{NULL,			no_argument,		NULL, 0}
215210389Sgabor};
216210389Sgabor
217210389Sgabor/*
218210389Sgabor * Adds a searching pattern to the internal array.
219210389Sgabor */
220210389Sgaborstatic void
221210389Sgaboradd_pattern(char *pat, size_t len)
222210389Sgabor{
223210389Sgabor
224210389Sgabor	/* Check if we can do a shortcut */
225210389Sgabor	if (len == 0 || matchall) {
226210389Sgabor		matchall = true;
227210389Sgabor		return;
228210389Sgabor	}
229210389Sgabor	/* Increase size if necessary */
230210389Sgabor	if (patterns == pattern_sz) {
231210389Sgabor		pattern_sz *= 2;
232210389Sgabor		pattern = grep_realloc(pattern, ++pattern_sz *
233210389Sgabor		    sizeof(*pattern));
234210389Sgabor	}
235210389Sgabor	if (len > 0 && pat[len - 1] == '\n')
236210389Sgabor		--len;
237210389Sgabor	/* pat may not be NUL-terminated */
238210389Sgabor	pattern[patterns] = grep_malloc(len + 1);
239210578Sgabor	strlcpy(pattern[patterns], pat, len + 1);
240210389Sgabor	++patterns;
241210389Sgabor}
242210389Sgabor
243210389Sgabor/*
244210578Sgabor * Adds a file include/exclude pattern to the internal array.
245210389Sgabor */
246210389Sgaborstatic void
247210578Sgaboradd_fpattern(const char *pat, int mode)
248210389Sgabor{
249210389Sgabor
250210389Sgabor	/* Increase size if necessary */
251210578Sgabor	if (fpatterns == fpattern_sz) {
252210578Sgabor		fpattern_sz *= 2;
253210578Sgabor		fpattern = grep_realloc(fpattern, ++fpattern_sz *
254210389Sgabor		    sizeof(struct epat));
255210389Sgabor	}
256210578Sgabor	fpattern[fpatterns].pat = grep_strdup(pat);
257210578Sgabor	fpattern[fpatterns].mode = mode;
258210578Sgabor	++fpatterns;
259210389Sgabor}
260210389Sgabor
261210389Sgabor/*
262210578Sgabor * Adds a directory include/exclude pattern to the internal array.
263210578Sgabor */
264210578Sgaborstatic void
265210578Sgaboradd_dpattern(const char *pat, int mode)
266210578Sgabor{
267210578Sgabor
268210578Sgabor	/* Increase size if necessary */
269210578Sgabor	if (dpatterns == dpattern_sz) {
270210578Sgabor		dpattern_sz *= 2;
271210578Sgabor		dpattern = grep_realloc(dpattern, ++dpattern_sz *
272210578Sgabor		    sizeof(struct epat));
273210578Sgabor	}
274210578Sgabor	dpattern[dpatterns].pat = grep_strdup(pat);
275210578Sgabor	dpattern[dpatterns].mode = mode;
276210578Sgabor	++dpatterns;
277210578Sgabor}
278210578Sgabor
279210578Sgabor/*
280210389Sgabor * Reads searching patterns from a file and adds them with add_pattern().
281210389Sgabor */
282210389Sgaborstatic void
283210389Sgaborread_patterns(const char *fn)
284210389Sgabor{
285210389Sgabor	FILE *f;
286210389Sgabor	char *line;
287210389Sgabor	size_t len;
288210389Sgabor
289210389Sgabor	if ((f = fopen(fn, "r")) == NULL)
290210389Sgabor		err(2, "%s", fn);
291210389Sgabor	while ((line = fgetln(f, &len)) != NULL)
292210389Sgabor		add_pattern(line, *line == '\n' ? 0 : len);
293210389Sgabor	if (ferror(f))
294210389Sgabor		err(2, "%s", fn);
295210389Sgabor	fclose(f);
296210389Sgabor}
297210389Sgabor
298210461Sgaborstatic inline const char *
299210461Sgaborinit_color(const char *d)
300210461Sgabor{
301210461Sgabor	char *c;
302210461Sgabor
303210461Sgabor	c = getenv("GREP_COLOR");
304210461Sgabor	return (c != NULL ? c : d);
305210461Sgabor}
306210461Sgabor
307210389Sgaborint
308210389Sgabormain(int argc, char *argv[])
309210389Sgabor{
310210389Sgabor	char **aargv, **eargv, *eopts;
311210389Sgabor	char *ep;
312210389Sgabor	unsigned long long l;
313210389Sgabor	unsigned int aargc, eargc, i;
314210389Sgabor	int c, lastc, needpattern, newarg, prevoptind;
315210389Sgabor
316210389Sgabor	setlocale(LC_ALL, "");
317210389Sgabor
318210389Sgabor#ifndef WITHOUT_NLS
319210389Sgabor	catalog = catopen("grep", NL_CAT_LOCALE);
320210389Sgabor#endif
321210389Sgabor
322210389Sgabor	/* Check what is the program name of the binary.  In this
323210389Sgabor	   way we can have all the funcionalities in one binary
324210389Sgabor	   without the need of scripting and using ugly hacks. */
325210389Sgabor	switch (__progname[0]) {
326210389Sgabor	case 'e':
327210389Sgabor		grepbehave = GREP_EXTENDED;
328210389Sgabor		break;
329210389Sgabor	case 'f':
330210389Sgabor		grepbehave = GREP_FIXED;
331210389Sgabor		break;
332210389Sgabor	case 'g':
333210389Sgabor		grepbehave = GREP_BASIC;
334210389Sgabor		break;
335210389Sgabor	case 'z':
336210389Sgabor		filebehave = FILE_GZIP;
337210389Sgabor		switch(__progname[1]) {
338210389Sgabor		case 'e':
339210389Sgabor			grepbehave = GREP_EXTENDED;
340210389Sgabor			break;
341210389Sgabor		case 'f':
342210389Sgabor			grepbehave = GREP_FIXED;
343210389Sgabor			break;
344210389Sgabor		case 'g':
345210389Sgabor			grepbehave = GREP_BASIC;
346210389Sgabor			break;
347210389Sgabor		}
348210389Sgabor		break;
349210389Sgabor	}
350210389Sgabor
351210389Sgabor	lastc = '\0';
352210389Sgabor	newarg = 1;
353210389Sgabor	prevoptind = 1;
354210389Sgabor	needpattern = 1;
355210389Sgabor
356210389Sgabor	eopts = getenv("GREP_OPTIONS");
357210389Sgabor
358210389Sgabor	eargc = 1;
359210389Sgabor	if (eopts != NULL) {
360210389Sgabor		char *str;
361210389Sgabor
362210389Sgabor		for(i = 0; i < strlen(eopts); i++)
363210389Sgabor			if (eopts[i] == ' ')
364210389Sgabor				eargc++;
365210389Sgabor
366210389Sgabor		eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1));
367210389Sgabor
368210389Sgabor		str = strtok(eopts, " ");
369210389Sgabor		eargc = 0;
370210389Sgabor
371210389Sgabor		while(str != NULL) {
372210389Sgabor			eargv[++eargc] = (char *)grep_malloc(sizeof(char) *
373210389Sgabor			    (strlen(str) + 1));
374210389Sgabor			strlcpy(eargv[eargc], str, strlen(str) + 1);
375210389Sgabor			str = strtok(NULL, " ");
376210389Sgabor		}
377210389Sgabor		eargv[++eargc] = NULL;
378210389Sgabor
379210430Sdelphij		aargv = (char **)grep_calloc(eargc + argc + 1,
380210430Sdelphij		    sizeof(char *));
381210389Sgabor		aargv[0] = argv[0];
382210389Sgabor
383210389Sgabor		for(i = 1; i < eargc; i++)
384210389Sgabor			aargv[i] = eargv[i];
385210389Sgabor		for(int j = 1; j < argc; j++)
386210389Sgabor			aargv[i++] = argv[j];
387210389Sgabor
388210389Sgabor		aargc = eargc + argc - 1;
389210389Sgabor
390210389Sgabor	} else {
391210389Sgabor		aargv = argv;
392210389Sgabor		aargc = argc;
393210389Sgabor	}
394210389Sgabor
395210389Sgabor	while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) !=
396210389Sgabor	    -1)) {
397210389Sgabor		switch (c) {
398210389Sgabor		case '0': case '1': case '2': case '3': case '4':
399210389Sgabor		case '5': case '6': case '7': case '8': case '9':
400210389Sgabor			if (newarg || !isdigit(lastc))
401210389Sgabor				Aflag = 0;
402210389Sgabor			else if (Aflag > LLONG_MAX / 10) {
403210389Sgabor				errno = ERANGE;
404210389Sgabor				err(2, NULL);
405210389Sgabor			}
406210389Sgabor			Aflag = Bflag = (Aflag * 10) + (c - '0');
407210389Sgabor			break;
408210389Sgabor		case 'C':
409210389Sgabor			if (optarg == NULL) {
410210389Sgabor				Aflag = Bflag = 2;
411210389Sgabor				break;
412210389Sgabor			}
413210389Sgabor			/* FALLTHROUGH */
414210389Sgabor		case 'A':
415210389Sgabor			/* FALLTHROUGH */
416210389Sgabor		case 'B':
417210389Sgabor			errno = 0;
418210389Sgabor			l = strtoull(optarg, &ep, 10);
419210389Sgabor			if (((errno == ERANGE) && (l == ULLONG_MAX)) ||
420210389Sgabor			    ((errno == EINVAL) && (l == 0)))
421210389Sgabor				err(2, NULL);
422210389Sgabor			else if (ep[0] != '\0') {
423210389Sgabor				errno = EINVAL;
424210389Sgabor				err(2, NULL);
425210389Sgabor			}
426210389Sgabor			if (c == 'A')
427210389Sgabor				Aflag = l;
428210389Sgabor			else if (c == 'B')
429210389Sgabor				Bflag = l;
430210389Sgabor			else
431210389Sgabor				Aflag = Bflag = l;
432210389Sgabor			break;
433210389Sgabor		case 'a':
434210389Sgabor			binbehave = BINFILE_TEXT;
435210389Sgabor			break;
436210389Sgabor		case 'b':
437210389Sgabor			bflag = true;
438210389Sgabor			break;
439210389Sgabor		case 'c':
440210389Sgabor			cflag = true;
441210389Sgabor			break;
442210389Sgabor		case 'D':
443210461Sgabor			if (strcasecmp(optarg, "skip") == 0)
444210389Sgabor				devbehave = DEV_SKIP;
445210461Sgabor			else if (strcasecmp(optarg, "read") == 0)
446210461Sgabor				devbehave = DEV_READ;
447210622Sgabor			else
448210622Sgabor				errx(2, getstr(3), "--devices");
449210389Sgabor			break;
450210389Sgabor		case 'd':
451210461Sgabor			if (strcasecmp("recurse", optarg) == 0) {
452210389Sgabor				Hflag = true;
453210389Sgabor				dirbehave = DIR_RECURSE;
454210461Sgabor			} else if (strcasecmp("skip", optarg) == 0)
455210389Sgabor				dirbehave = DIR_SKIP;
456210461Sgabor			else if (strcasecmp("read", optarg) == 0)
457210461Sgabor				dirbehave = DIR_READ;
458210622Sgabor			else
459210622Sgabor				errx(2, getstr(3), "--directories");
460210389Sgabor			break;
461210389Sgabor		case 'E':
462210389Sgabor			grepbehave = GREP_EXTENDED;
463210389Sgabor			break;
464210389Sgabor		case 'e':
465210389Sgabor			add_pattern(optarg, strlen(optarg));
466210389Sgabor			needpattern = 0;
467210389Sgabor			break;
468210389Sgabor		case 'F':
469210389Sgabor			grepbehave = GREP_FIXED;
470210389Sgabor			break;
471210389Sgabor		case 'f':
472210389Sgabor			read_patterns(optarg);
473210389Sgabor			needpattern = 0;
474210389Sgabor			break;
475210389Sgabor		case 'G':
476210389Sgabor			grepbehave = GREP_BASIC;
477210389Sgabor			break;
478210389Sgabor		case 'H':
479210389Sgabor			Hflag = true;
480210389Sgabor			break;
481210389Sgabor		case 'h':
482210389Sgabor			Hflag = false;
483210389Sgabor			hflag = true;
484210389Sgabor			break;
485210389Sgabor		case 'I':
486210389Sgabor			binbehave = BINFILE_SKIP;
487210389Sgabor			break;
488210389Sgabor		case 'i':
489210389Sgabor		case 'y':
490210389Sgabor			iflag =  true;
491210389Sgabor			cflags |= REG_ICASE;
492210389Sgabor			break;
493210389Sgabor		case 'J':
494210389Sgabor			filebehave = FILE_BZIP;
495210389Sgabor			break;
496210389Sgabor		case 'L':
497210389Sgabor			lflag = false;
498210461Sgabor			Lflag = true;
499210389Sgabor			break;
500210389Sgabor		case 'l':
501210389Sgabor			Lflag = false;
502210461Sgabor			lflag = true;
503210389Sgabor			break;
504210389Sgabor		case 'm':
505210389Sgabor			mflag = true;
506210389Sgabor			errno = 0;
507210389Sgabor			mcount = strtoull(optarg, &ep, 10);
508210389Sgabor			if (((errno == ERANGE) && (mcount == ULLONG_MAX)) ||
509210389Sgabor			    ((errno == EINVAL) && (mcount == 0)))
510210389Sgabor				err(2, NULL);
511210389Sgabor			else if (ep[0] != '\0') {
512210389Sgabor				errno = EINVAL;
513210389Sgabor				err(2, NULL);
514210389Sgabor			}
515210389Sgabor			break;
516210389Sgabor		case 'n':
517210389Sgabor			nflag = true;
518210389Sgabor			break;
519210389Sgabor		case 'O':
520210389Sgabor			linkbehave = LINK_EXPLICIT;
521210389Sgabor			break;
522210389Sgabor		case 'o':
523210389Sgabor			oflag = true;
524210389Sgabor			break;
525210389Sgabor		case 'p':
526210389Sgabor			linkbehave = LINK_SKIP;
527210389Sgabor			break;
528210389Sgabor		case 'q':
529210389Sgabor			qflag = true;
530210389Sgabor			break;
531210389Sgabor		case 'S':
532210461Sgabor			linkbehave = LINK_READ;
533210389Sgabor			break;
534210389Sgabor		case 'R':
535210389Sgabor		case 'r':
536210389Sgabor			dirbehave = DIR_RECURSE;
537210389Sgabor			Hflag = true;
538210389Sgabor			break;
539210389Sgabor		case 's':
540210389Sgabor			sflag = true;
541210389Sgabor			break;
542210389Sgabor		case 'U':
543210389Sgabor			binbehave = BINFILE_BIN;
544210389Sgabor			break;
545210389Sgabor		case 'u':
546210389Sgabor		case MMAP_OPT:
547210389Sgabor			/* noop, compatibility */
548210389Sgabor			break;
549210389Sgabor		case 'V':
550210622Sgabor			printf(getstr(9), __progname, VERSION);
551210389Sgabor			exit(0);
552210389Sgabor		case 'v':
553210389Sgabor			vflag = true;
554210389Sgabor			break;
555210389Sgabor		case 'w':
556210389Sgabor			wflag = true;
557210389Sgabor			break;
558210389Sgabor		case 'x':
559210389Sgabor			xflag = true;
560210389Sgabor			break;
561210389Sgabor		case 'Z':
562210389Sgabor			filebehave = FILE_GZIP;
563210389Sgabor			break;
564210389Sgabor		case BIN_OPT:
565210461Sgabor			if (strcasecmp("binary", optarg) == 0)
566210389Sgabor				binbehave = BINFILE_BIN;
567210461Sgabor			else if (strcasecmp("without-match", optarg) == 0)
568210389Sgabor				binbehave = BINFILE_SKIP;
569210461Sgabor			else if (strcasecmp("text", optarg) == 0)
570210389Sgabor				binbehave = BINFILE_TEXT;
571210389Sgabor			else
572210622Sgabor				errx(2, getstr(3), "--binary-files");
573210389Sgabor			break;
574210389Sgabor		case COLOR_OPT:
575210461Sgabor			color = NULL;
576210461Sgabor			if (optarg == NULL || strcasecmp("auto", optarg) == 0 ||
577210461Sgabor			    strcasecmp("tty", optarg) == 0 ||
578210461Sgabor			    strcasecmp("if-tty", optarg) == 0) {
579210461Sgabor				char *term;
580210461Sgabor
581210461Sgabor				term = getenv("TERM");
582210461Sgabor				if (isatty(STDOUT_FILENO) && term != NULL &&
583210461Sgabor				    strcasecmp(term, "dumb") != 0)
584210461Sgabor					color = init_color("01;31");
585210461Sgabor			} else if (strcasecmp("always", optarg) == 0 ||
586210461Sgabor			    strcasecmp("yes", optarg) == 0 ||
587210461Sgabor			    strcasecmp("force", optarg) == 0) {
588210461Sgabor				color = init_color("01;31");
589210461Sgabor			} else if (strcasecmp("never", optarg) != 0 &&
590210461Sgabor			    strcasecmp("none", optarg) != 0 &&
591210461Sgabor			    strcasecmp("no", optarg) != 0)
592210622Sgabor				errx(2, getstr(3), "--color");
593210389Sgabor			break;
594210389Sgabor		case LABEL_OPT:
595210389Sgabor			label = optarg;
596210389Sgabor			break;
597210389Sgabor		case LINEBUF_OPT:
598210389Sgabor			lbflag = true;
599210389Sgabor			break;
600210389Sgabor		case NULL_OPT:
601210389Sgabor			nullflag = true;
602210389Sgabor			break;
603210389Sgabor		case R_INCLUDE_OPT:
604210578Sgabor			finclude = true;
605210578Sgabor			add_fpattern(optarg, INCL_PAT);
606210389Sgabor			break;
607210389Sgabor		case R_EXCLUDE_OPT:
608210578Sgabor			fexclude = true;
609210578Sgabor			add_fpattern(optarg, EXCL_PAT);
610210389Sgabor			break;
611210389Sgabor		case R_DINCLUDE_OPT:
612210578Sgabor			dexclude = true;
613210578Sgabor			add_dpattern(optarg, INCL_PAT);
614210389Sgabor			break;
615210389Sgabor		case R_DEXCLUDE_OPT:
616210578Sgabor			dinclude = true;
617210578Sgabor			add_dpattern(optarg, EXCL_PAT);
618210389Sgabor			break;
619210389Sgabor		case HELP_OPT:
620210389Sgabor		default:
621210389Sgabor			usage();
622210389Sgabor		}
623210389Sgabor		lastc = c;
624210389Sgabor		newarg = optind != prevoptind;
625210389Sgabor		prevoptind = optind;
626210389Sgabor	}
627210389Sgabor	aargc -= optind;
628210389Sgabor	aargv += optind;
629210389Sgabor
630210389Sgabor	/* Fail if we don't have any pattern */
631210389Sgabor	if (aargc == 0 && needpattern)
632210389Sgabor		usage();
633210389Sgabor
634210389Sgabor	/* Process patterns from command line */
635210389Sgabor	if (aargc != 0 && needpattern) {
636210389Sgabor		add_pattern(*aargv, strlen(*aargv));
637210389Sgabor		--aargc;
638210389Sgabor		++aargv;
639210389Sgabor	}
640210389Sgabor
641210389Sgabor	switch (grepbehave) {
642210389Sgabor	case GREP_FIXED:
643210389Sgabor	case GREP_BASIC:
644210389Sgabor		break;
645210389Sgabor	case GREP_EXTENDED:
646210389Sgabor		cflags |= REG_EXTENDED;
647210389Sgabor		break;
648210389Sgabor	default:
649210389Sgabor		/* NOTREACHED */
650210389Sgabor		usage();
651210389Sgabor	}
652210389Sgabor
653210389Sgabor	fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern));
654210389Sgabor	r_pattern = grep_calloc(patterns, sizeof(*r_pattern));
655210389Sgabor/*
656210389Sgabor * XXX: fgrepcomp() and fastcomp() are workarounds for regexec() performance.
657210389Sgabor * Optimizations should be done there.
658210389Sgabor */
659210389Sgabor		/* Check if cheating is allowed (always is for fgrep). */
660210389Sgabor	if (grepbehave == GREP_FIXED) {
661210389Sgabor		for (i = 0; i < patterns; ++i)
662210389Sgabor			fgrepcomp(&fg_pattern[i], pattern[i]);
663210389Sgabor	} else {
664210389Sgabor		for (i = 0; i < patterns; ++i) {
665210389Sgabor			if (fastcomp(&fg_pattern[i], pattern[i])) {
666210389Sgabor				/* Fall back to full regex library */
667210389Sgabor				c = regcomp(&r_pattern[i], pattern[i], cflags);
668210389Sgabor				if (c != 0) {
669210389Sgabor					regerror(c, &r_pattern[i], re_error,
670210389Sgabor					    RE_ERROR_BUF);
671210389Sgabor					errx(2, "%s", re_error);
672210389Sgabor				}
673210389Sgabor			}
674210389Sgabor		}
675210389Sgabor	}
676210389Sgabor
677210389Sgabor	if (lbflag)
678210389Sgabor		setlinebuf(stdout);
679210389Sgabor
680210389Sgabor	if ((aargc == 0 || aargc == 1) && !Hflag)
681210389Sgabor		hflag = true;
682210389Sgabor
683210389Sgabor	if (aargc == 0)
684210389Sgabor		exit(!procfile("-"));
685210389Sgabor
686210389Sgabor	if (dirbehave == DIR_RECURSE)
687210389Sgabor		c = grep_tree(aargv);
688210389Sgabor	else
689210578Sgabor		for (c = 0; aargc--; ++aargv) {
690210578Sgabor			if ((finclude || fexclude) && !file_matching(*aargv))
691210578Sgabor				continue;
692210389Sgabor			c+= procfile(*aargv);
693210578Sgabor		}
694210389Sgabor
695210389Sgabor#ifndef WITHOUT_NLS
696210389Sgabor	catclose(catalog);
697210389Sgabor#endif
698210389Sgabor
699210389Sgabor	/* Find out the correct return value according to the
700210389Sgabor	   results and the command line option. */
701210389Sgabor	exit(c ? (notfound ? (qflag ? 0 : 2) : 0) : (notfound ? 2 : 1));
702210389Sgabor}
703