grep.c revision 220422
1220422Sgabor/*	$NetBSD: grep.c,v 1.4 2011/02/16 01:31:33 joerg Exp $	*/
2220422Sgabor/* 	$FreeBSD: head/usr.bin/grep/grep.c 220422 2011-04-07 13:03:35Z gabor $	*/
3210389Sgabor/*	$OpenBSD: grep.c,v 1.42 2010/07/02 22:18:03 tedu Exp $	*/
4210389Sgabor
5210389Sgabor/*-
6211496Sdes * Copyright (c) 1999 James Howard and Dag-Erling Co��dan Sm��rgrav
7210389Sgabor * Copyright (C) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org>
8210389Sgabor * All rights reserved.
9210389Sgabor *
10210389Sgabor * Redistribution and use in source and binary forms, with or without
11210389Sgabor * modification, are permitted provided that the following conditions
12210389Sgabor * are met:
13210389Sgabor * 1. Redistributions of source code must retain the above copyright
14210389Sgabor *    notice, this list of conditions and the following disclaimer.
15210389Sgabor * 2. Redistributions in binary form must reproduce the above copyright
16210389Sgabor *    notice, this list of conditions and the following disclaimer in the
17210389Sgabor *    documentation and/or other materials provided with the distribution.
18210389Sgabor *
19210389Sgabor * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20210389Sgabor * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21210389Sgabor * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22210389Sgabor * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23210389Sgabor * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24210389Sgabor * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25210389Sgabor * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26210389Sgabor * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27210389Sgabor * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28210389Sgabor * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29210389Sgabor * SUCH DAMAGE.
30210389Sgabor */
31210389Sgabor
32210389Sgabor#include <sys/cdefs.h>
33210389Sgabor__FBSDID("$FreeBSD: head/usr.bin/grep/grep.c 220422 2011-04-07 13:03:35Z gabor $");
34210389Sgabor
35210389Sgabor#include <sys/stat.h>
36210389Sgabor#include <sys/types.h>
37210389Sgabor
38210389Sgabor#include <ctype.h>
39210389Sgabor#include <err.h>
40210389Sgabor#include <errno.h>
41210389Sgabor#include <getopt.h>
42210389Sgabor#include <limits.h>
43210389Sgabor#include <libgen.h>
44210389Sgabor#include <locale.h>
45210389Sgabor#include <stdbool.h>
46210389Sgabor#include <stdio.h>
47210389Sgabor#include <stdlib.h>
48210389Sgabor#include <string.h>
49210389Sgabor#include <unistd.h>
50210389Sgabor
51210389Sgabor#include "grep.h"
52210389Sgabor
53210389Sgabor#ifndef WITHOUT_NLS
54210389Sgabor#include <nl_types.h>
55210389Sgabornl_catd	 catalog;
56210389Sgabor#endif
57210389Sgabor
58210389Sgabor/*
59210389Sgabor * Default messags to use when NLS is disabled or no catalogue
60210389Sgabor * is found.
61210389Sgabor */
62210389Sgaborconst char	*errstr[] = {
63210389Sgabor	"",
64210389Sgabor/* 1*/	"(standard input)",
65210389Sgabor/* 2*/	"cannot read bzip2 compressed file",
66210622Sgabor/* 3*/	"unknown %s option",
67210389Sgabor/* 4*/	"usage: %s [-abcDEFGHhIiJLlmnOoPqRSsUVvwxZ] [-A num] [-B num] [-C[num]]\n",
68210389Sgabor/* 5*/	"\t[-e pattern] [-f file] [--binary-files=value] [--color=when]\n",
69210389Sgabor/* 6*/	"\t[--context[=num]] [--directories=action] [--label] [--line-buffered]\n",
70210389Sgabor/* 7*/	"\t[--null] [pattern] [file ...]\n",
71210622Sgabor/* 8*/	"Binary file %s matches\n",
72210622Sgabor/* 9*/	"%s (BSD grep) %s\n",
73210389Sgabor};
74210389Sgabor
75210389Sgabor/* Flags passed to regcomp() and regexec() */
76210389Sgaborint		 cflags = 0;
77210389Sgaborint		 eflags = REG_STARTEND;
78210389Sgabor
79210389Sgabor/* Shortcut for matching all cases like empty regex */
80210389Sgaborbool		 matchall;
81210389Sgabor
82210389Sgabor/* Searching patterns */
83210389Sgaborunsigned int	 patterns, pattern_sz;
84210389Sgaborchar		**pattern;
85210389Sgaborregex_t		*r_pattern;
86210389Sgaborfastgrep_t	*fg_pattern;
87210389Sgabor
88210389Sgabor/* Filename exclusion/inclusion patterns */
89210578Sgaborunsigned int	 fpatterns, fpattern_sz;
90210578Sgaborunsigned int	 dpatterns, dpattern_sz;
91210578Sgaborstruct epat	*dpattern, *fpattern;
92210389Sgabor
93210389Sgabor/* For regex errors  */
94210389Sgaborchar	 re_error[RE_ERROR_BUF + 1];
95210389Sgabor
96210389Sgabor/* Command-line flags */
97210389Sgaborunsigned long long Aflag;	/* -A x: print x lines trailing each match */
98210389Sgaborunsigned long long Bflag;	/* -B x: print x lines leading each match */
99210389Sgaborbool	 Hflag;		/* -H: always print file name */
100210389Sgaborbool	 Lflag;		/* -L: only show names of files with no matches */
101210389Sgaborbool	 bflag;		/* -b: show block numbers for each match */
102210389Sgaborbool	 cflag;		/* -c: only show a count of matching lines */
103210389Sgaborbool	 hflag;		/* -h: don't print filename headers */
104210389Sgaborbool	 iflag;		/* -i: ignore case */
105210389Sgaborbool	 lflag;		/* -l: only show names of files with matches */
106210389Sgaborbool	 mflag;		/* -m x: stop reading the files after x matches */
107210389Sgaborunsigned long long mcount;	/* count for -m */
108210389Sgaborbool	 nflag;		/* -n: show line numbers in front of matching lines */
109210389Sgaborbool	 oflag;		/* -o: print only matching part */
110210389Sgaborbool	 qflag;		/* -q: quiet mode (don't output anything) */
111210389Sgaborbool	 sflag;		/* -s: silent mode (ignore errors) */
112210389Sgaborbool	 vflag;		/* -v: only show non-matching lines */
113210389Sgaborbool	 wflag;		/* -w: pattern must start and end on word boundaries */
114210389Sgaborbool	 xflag;		/* -x: pattern must match entire line */
115210389Sgaborbool	 lbflag;	/* --line-buffered */
116210389Sgaborbool	 nullflag;	/* --null */
117210389Sgaborchar	*label;		/* --label */
118210461Sgaborconst char *color;	/* --color */
119210389Sgaborint	 grepbehave = GREP_BASIC;	/* -EFGP: type of the regex */
120210389Sgaborint	 binbehave = BINFILE_BIN;	/* -aIU: handling of binary files */
121210389Sgaborint	 filebehave = FILE_STDIO;	/* -JZ: normal, gzip or bzip2 file */
122210461Sgaborint	 devbehave = DEV_READ;		/* -D: handling of devices */
123210461Sgaborint	 dirbehave = DIR_READ;		/* -dRr: handling of directories */
124210461Sgaborint	 linkbehave = LINK_READ;	/* -OpS: handling of symlinks */
125210389Sgabor
126211364Sgaborbool	 dexclude, dinclude;	/* --exclude-dir and --include-dir */
127211364Sgaborbool	 fexclude, finclude;	/* --exclude and --include */
128210578Sgabor
129210389Sgaborenum {
130210389Sgabor	BIN_OPT = CHAR_MAX + 1,
131210389Sgabor	COLOR_OPT,
132210389Sgabor	HELP_OPT,
133210389Sgabor	MMAP_OPT,
134210389Sgabor	LINEBUF_OPT,
135210389Sgabor	LABEL_OPT,
136210389Sgabor	NULL_OPT,
137210389Sgabor	R_EXCLUDE_OPT,
138210389Sgabor	R_INCLUDE_OPT,
139210389Sgabor	R_DEXCLUDE_OPT,
140210389Sgabor	R_DINCLUDE_OPT
141210389Sgabor};
142210389Sgabor
143210461Sgaborstatic inline const char	*init_color(const char *);
144210461Sgabor
145210389Sgabor/* Housekeeping */
146210389Sgaborbool	 first = true;	/* flag whether we are processing the first match */
147210389Sgaborbool	 prev;		/* flag whether or not the previous line matched */
148210389Sgaborint	 tail;		/* lines left to print */
149210389Sgaborbool	 notfound;	/* file not found */
150210389Sgabor
151210389Sgaborextern char	*__progname;
152210389Sgabor
153210389Sgabor/*
154210389Sgabor * Prints usage information and returns 2.
155210389Sgabor */
156210389Sgaborstatic void
157210389Sgaborusage(void)
158210389Sgabor{
159210389Sgabor	fprintf(stderr, getstr(4), __progname);
160210389Sgabor	fprintf(stderr, "%s", getstr(5));
161210389Sgabor	fprintf(stderr, "%s", getstr(5));
162210389Sgabor	fprintf(stderr, "%s", getstr(6));
163210389Sgabor	fprintf(stderr, "%s", getstr(7));
164210389Sgabor	exit(2);
165210389Sgabor}
166210389Sgabor
167210389Sgaborstatic const char	*optstr = "0123456789A:B:C:D:EFGHIJLOPSRUVZabcd:e:f:hilm:nopqrsuvwxy";
168210389Sgabor
169210389Sgaborstruct option long_options[] =
170210389Sgabor{
171210389Sgabor	{"binary-files",	required_argument,	NULL, BIN_OPT},
172210389Sgabor	{"help",		no_argument,		NULL, HELP_OPT},
173210389Sgabor	{"mmap",		no_argument,		NULL, MMAP_OPT},
174210389Sgabor	{"line-buffered",	no_argument,		NULL, LINEBUF_OPT},
175210389Sgabor	{"label",		required_argument,	NULL, LABEL_OPT},
176210389Sgabor	{"null",		no_argument,		NULL, NULL_OPT},
177210389Sgabor	{"color",		optional_argument,	NULL, COLOR_OPT},
178210389Sgabor	{"colour",		optional_argument,	NULL, COLOR_OPT},
179210389Sgabor	{"exclude",		required_argument,	NULL, R_EXCLUDE_OPT},
180210389Sgabor	{"include",		required_argument,	NULL, R_INCLUDE_OPT},
181210389Sgabor	{"exclude-dir",		required_argument,	NULL, R_DEXCLUDE_OPT},
182210389Sgabor	{"include-dir",		required_argument,	NULL, R_DINCLUDE_OPT},
183210389Sgabor	{"after-context",	required_argument,	NULL, 'A'},
184210389Sgabor	{"text",		no_argument,		NULL, 'a'},
185210389Sgabor	{"before-context",	required_argument,	NULL, 'B'},
186210389Sgabor	{"byte-offset",		no_argument,		NULL, 'b'},
187210389Sgabor	{"context",		optional_argument,	NULL, 'C'},
188210389Sgabor	{"count",		no_argument,		NULL, 'c'},
189210389Sgabor	{"devices",		required_argument,	NULL, 'D'},
190210389Sgabor        {"directories",		required_argument,	NULL, 'd'},
191210389Sgabor	{"extended-regexp",	no_argument,		NULL, 'E'},
192210389Sgabor	{"regexp",		required_argument,	NULL, 'e'},
193210389Sgabor	{"fixed-strings",	no_argument,		NULL, 'F'},
194210389Sgabor	{"file",		required_argument,	NULL, 'f'},
195210389Sgabor	{"basic-regexp",	no_argument,		NULL, 'G'},
196210389Sgabor	{"no-filename",		no_argument,		NULL, 'h'},
197210389Sgabor	{"with-filename",	no_argument,		NULL, 'H'},
198210389Sgabor	{"ignore-case",		no_argument,		NULL, 'i'},
199210389Sgabor	{"bz2decompress",	no_argument,		NULL, 'J'},
200210389Sgabor	{"files-with-matches",	no_argument,		NULL, 'l'},
201210389Sgabor	{"files-without-match", no_argument,            NULL, 'L'},
202210389Sgabor	{"max-count",		required_argument,	NULL, 'm'},
203210389Sgabor	{"line-number",		no_argument,		NULL, 'n'},
204210389Sgabor	{"only-matching",	no_argument,		NULL, 'o'},
205210389Sgabor	{"quiet",		no_argument,		NULL, 'q'},
206210389Sgabor	{"silent",		no_argument,		NULL, 'q'},
207210389Sgabor	{"recursive",		no_argument,		NULL, 'r'},
208210389Sgabor	{"no-messages",		no_argument,		NULL, 's'},
209210389Sgabor	{"binary",		no_argument,		NULL, 'U'},
210210389Sgabor	{"unix-byte-offsets",	no_argument,		NULL, 'u'},
211210389Sgabor	{"invert-match",	no_argument,		NULL, 'v'},
212210389Sgabor	{"version",		no_argument,		NULL, 'V'},
213210389Sgabor	{"word-regexp",		no_argument,		NULL, 'w'},
214210389Sgabor	{"line-regexp",		no_argument,		NULL, 'x'},
215210389Sgabor	{"decompress",          no_argument,            NULL, 'Z'},
216210389Sgabor	{NULL,			no_argument,		NULL, 0}
217210389Sgabor};
218210389Sgabor
219210389Sgabor/*
220210389Sgabor * Adds a searching pattern to the internal array.
221210389Sgabor */
222210389Sgaborstatic void
223210389Sgaboradd_pattern(char *pat, size_t len)
224210389Sgabor{
225210389Sgabor
226210389Sgabor	/* Check if we can do a shortcut */
227210389Sgabor	if (len == 0 || matchall) {
228210389Sgabor		matchall = true;
229210389Sgabor		return;
230210389Sgabor	}
231210389Sgabor	/* Increase size if necessary */
232210389Sgabor	if (patterns == pattern_sz) {
233210389Sgabor		pattern_sz *= 2;
234210389Sgabor		pattern = grep_realloc(pattern, ++pattern_sz *
235210389Sgabor		    sizeof(*pattern));
236210389Sgabor	}
237210389Sgabor	if (len > 0 && pat[len - 1] == '\n')
238210389Sgabor		--len;
239210389Sgabor	/* pat may not be NUL-terminated */
240210389Sgabor	pattern[patterns] = grep_malloc(len + 1);
241211364Sgabor	memcpy(pattern[patterns], pat, len);
242211364Sgabor	pattern[patterns][len] = '\0';
243210389Sgabor	++patterns;
244210389Sgabor}
245210389Sgabor
246210389Sgabor/*
247210578Sgabor * Adds a file include/exclude pattern to the internal array.
248210389Sgabor */
249210389Sgaborstatic void
250210578Sgaboradd_fpattern(const char *pat, int mode)
251210389Sgabor{
252210389Sgabor
253210389Sgabor	/* Increase size if necessary */
254210578Sgabor	if (fpatterns == fpattern_sz) {
255210578Sgabor		fpattern_sz *= 2;
256210578Sgabor		fpattern = grep_realloc(fpattern, ++fpattern_sz *
257210389Sgabor		    sizeof(struct epat));
258210389Sgabor	}
259210578Sgabor	fpattern[fpatterns].pat = grep_strdup(pat);
260210578Sgabor	fpattern[fpatterns].mode = mode;
261210578Sgabor	++fpatterns;
262210389Sgabor}
263210389Sgabor
264210389Sgabor/*
265210578Sgabor * Adds a directory include/exclude pattern to the internal array.
266210578Sgabor */
267210578Sgaborstatic void
268210578Sgaboradd_dpattern(const char *pat, int mode)
269210578Sgabor{
270210578Sgabor
271210578Sgabor	/* Increase size if necessary */
272210578Sgabor	if (dpatterns == dpattern_sz) {
273210578Sgabor		dpattern_sz *= 2;
274210578Sgabor		dpattern = grep_realloc(dpattern, ++dpattern_sz *
275210578Sgabor		    sizeof(struct epat));
276210578Sgabor	}
277210578Sgabor	dpattern[dpatterns].pat = grep_strdup(pat);
278210578Sgabor	dpattern[dpatterns].mode = mode;
279210578Sgabor	++dpatterns;
280210578Sgabor}
281210578Sgabor
282210578Sgabor/*
283210389Sgabor * Reads searching patterns from a file and adds them with add_pattern().
284210389Sgabor */
285210389Sgaborstatic void
286210389Sgaborread_patterns(const char *fn)
287210389Sgabor{
288210389Sgabor	FILE *f;
289210389Sgabor	char *line;
290210389Sgabor	size_t len;
291210389Sgabor
292210389Sgabor	if ((f = fopen(fn, "r")) == NULL)
293210389Sgabor		err(2, "%s", fn);
294210389Sgabor	while ((line = fgetln(f, &len)) != NULL)
295210389Sgabor		add_pattern(line, *line == '\n' ? 0 : len);
296210389Sgabor	if (ferror(f))
297210389Sgabor		err(2, "%s", fn);
298210389Sgabor	fclose(f);
299210389Sgabor}
300210389Sgabor
301210461Sgaborstatic inline const char *
302210461Sgaborinit_color(const char *d)
303210461Sgabor{
304210461Sgabor	char *c;
305210461Sgabor
306210461Sgabor	c = getenv("GREP_COLOR");
307210461Sgabor	return (c != NULL ? c : d);
308210461Sgabor}
309210461Sgabor
310210389Sgaborint
311210389Sgabormain(int argc, char *argv[])
312210389Sgabor{
313210389Sgabor	char **aargv, **eargv, *eopts;
314210389Sgabor	char *ep;
315210389Sgabor	unsigned long long l;
316210389Sgabor	unsigned int aargc, eargc, i;
317210389Sgabor	int c, lastc, needpattern, newarg, prevoptind;
318210389Sgabor
319210389Sgabor	setlocale(LC_ALL, "");
320210389Sgabor
321210389Sgabor#ifndef WITHOUT_NLS
322210389Sgabor	catalog = catopen("grep", NL_CAT_LOCALE);
323210389Sgabor#endif
324210389Sgabor
325210389Sgabor	/* Check what is the program name of the binary.  In this
326210389Sgabor	   way we can have all the funcionalities in one binary
327210389Sgabor	   without the need of scripting and using ugly hacks. */
328210389Sgabor	switch (__progname[0]) {
329210389Sgabor	case 'e':
330210389Sgabor		grepbehave = GREP_EXTENDED;
331210389Sgabor		break;
332210389Sgabor	case 'f':
333210389Sgabor		grepbehave = GREP_FIXED;
334210389Sgabor		break;
335210389Sgabor	case 'g':
336210389Sgabor		grepbehave = GREP_BASIC;
337210389Sgabor		break;
338210389Sgabor	case 'z':
339210389Sgabor		filebehave = FILE_GZIP;
340210389Sgabor		switch(__progname[1]) {
341210389Sgabor		case 'e':
342210389Sgabor			grepbehave = GREP_EXTENDED;
343210389Sgabor			break;
344210389Sgabor		case 'f':
345210389Sgabor			grepbehave = GREP_FIXED;
346210389Sgabor			break;
347210389Sgabor		case 'g':
348210389Sgabor			grepbehave = GREP_BASIC;
349210389Sgabor			break;
350210389Sgabor		}
351210389Sgabor		break;
352210389Sgabor	}
353210389Sgabor
354210389Sgabor	lastc = '\0';
355210389Sgabor	newarg = 1;
356210389Sgabor	prevoptind = 1;
357210389Sgabor	needpattern = 1;
358210389Sgabor
359210389Sgabor	eopts = getenv("GREP_OPTIONS");
360210389Sgabor
361211364Sgabor	/* support for extra arguments in GREP_OPTIONS */
362211364Sgabor	eargc = 0;
363210389Sgabor	if (eopts != NULL) {
364210389Sgabor		char *str;
365210389Sgabor
366211364Sgabor		/* make an estimation of how many extra arguments we have */
367211364Sgabor		for (unsigned int j = 0; j < strlen(eopts); j++)
368211364Sgabor			if (eopts[j] == ' ')
369210389Sgabor				eargc++;
370210389Sgabor
371210389Sgabor		eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1));
372210389Sgabor
373210389Sgabor		eargc = 0;
374211364Sgabor		/* parse extra arguments */
375211364Sgabor		while ((str = strsep(&eopts, " ")) != NULL)
376211364Sgabor			eargv[eargc++] = grep_strdup(str);
377210389Sgabor
378210430Sdelphij		aargv = (char **)grep_calloc(eargc + argc + 1,
379210430Sdelphij		    sizeof(char *));
380211364Sgabor
381210389Sgabor		aargv[0] = argv[0];
382211364Sgabor		for (i = 0; i < eargc; i++)
383211364Sgabor			aargv[i + 1] = eargv[i];
384211364Sgabor		for (int j = 1; j < argc; j++, i++)
385211364Sgabor			aargv[i + 1] = argv[j];
386210389Sgabor
387211364Sgabor		aargc = eargc + argc;
388210389Sgabor	} else {
389210389Sgabor		aargv = argv;
390210389Sgabor		aargc = argc;
391210389Sgabor	}
392210389Sgabor
393210389Sgabor	while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) !=
394210389Sgabor	    -1)) {
395210389Sgabor		switch (c) {
396210389Sgabor		case '0': case '1': case '2': case '3': case '4':
397210389Sgabor		case '5': case '6': case '7': case '8': case '9':
398210389Sgabor			if (newarg || !isdigit(lastc))
399210389Sgabor				Aflag = 0;
400210389Sgabor			else if (Aflag > LLONG_MAX / 10) {
401210389Sgabor				errno = ERANGE;
402210389Sgabor				err(2, NULL);
403210389Sgabor			}
404210389Sgabor			Aflag = Bflag = (Aflag * 10) + (c - '0');
405210389Sgabor			break;
406210389Sgabor		case 'C':
407210389Sgabor			if (optarg == NULL) {
408210389Sgabor				Aflag = Bflag = 2;
409210389Sgabor				break;
410210389Sgabor			}
411210389Sgabor			/* FALLTHROUGH */
412210389Sgabor		case 'A':
413210389Sgabor			/* FALLTHROUGH */
414210389Sgabor		case 'B':
415210389Sgabor			errno = 0;
416210389Sgabor			l = strtoull(optarg, &ep, 10);
417210389Sgabor			if (((errno == ERANGE) && (l == ULLONG_MAX)) ||
418210389Sgabor			    ((errno == EINVAL) && (l == 0)))
419210389Sgabor				err(2, NULL);
420210389Sgabor			else if (ep[0] != '\0') {
421210389Sgabor				errno = EINVAL;
422210389Sgabor				err(2, NULL);
423210389Sgabor			}
424210389Sgabor			if (c == 'A')
425210389Sgabor				Aflag = l;
426210389Sgabor			else if (c == 'B')
427210389Sgabor				Bflag = l;
428210389Sgabor			else
429210389Sgabor				Aflag = Bflag = l;
430210389Sgabor			break;
431210389Sgabor		case 'a':
432210389Sgabor			binbehave = BINFILE_TEXT;
433210389Sgabor			break;
434210389Sgabor		case 'b':
435210389Sgabor			bflag = true;
436210389Sgabor			break;
437210389Sgabor		case 'c':
438210389Sgabor			cflag = true;
439210389Sgabor			break;
440210389Sgabor		case 'D':
441210461Sgabor			if (strcasecmp(optarg, "skip") == 0)
442210389Sgabor				devbehave = DEV_SKIP;
443210461Sgabor			else if (strcasecmp(optarg, "read") == 0)
444210461Sgabor				devbehave = DEV_READ;
445210622Sgabor			else
446210622Sgabor				errx(2, getstr(3), "--devices");
447210389Sgabor			break;
448210389Sgabor		case 'd':
449210461Sgabor			if (strcasecmp("recurse", optarg) == 0) {
450210389Sgabor				Hflag = true;
451210389Sgabor				dirbehave = DIR_RECURSE;
452210461Sgabor			} else if (strcasecmp("skip", optarg) == 0)
453210389Sgabor				dirbehave = DIR_SKIP;
454210461Sgabor			else if (strcasecmp("read", optarg) == 0)
455210461Sgabor				dirbehave = DIR_READ;
456210622Sgabor			else
457210622Sgabor				errx(2, getstr(3), "--directories");
458210389Sgabor			break;
459210389Sgabor		case 'E':
460210389Sgabor			grepbehave = GREP_EXTENDED;
461210389Sgabor			break;
462210389Sgabor		case 'e':
463210389Sgabor			add_pattern(optarg, strlen(optarg));
464210389Sgabor			needpattern = 0;
465210389Sgabor			break;
466210389Sgabor		case 'F':
467210389Sgabor			grepbehave = GREP_FIXED;
468210389Sgabor			break;
469210389Sgabor		case 'f':
470210389Sgabor			read_patterns(optarg);
471210389Sgabor			needpattern = 0;
472210389Sgabor			break;
473210389Sgabor		case 'G':
474210389Sgabor			grepbehave = GREP_BASIC;
475210389Sgabor			break;
476210389Sgabor		case 'H':
477210389Sgabor			Hflag = true;
478210389Sgabor			break;
479210389Sgabor		case 'h':
480210389Sgabor			Hflag = false;
481210389Sgabor			hflag = true;
482210389Sgabor			break;
483210389Sgabor		case 'I':
484210389Sgabor			binbehave = BINFILE_SKIP;
485210389Sgabor			break;
486210389Sgabor		case 'i':
487210389Sgabor		case 'y':
488210389Sgabor			iflag =  true;
489210389Sgabor			cflags |= REG_ICASE;
490210389Sgabor			break;
491210389Sgabor		case 'J':
492210389Sgabor			filebehave = FILE_BZIP;
493210389Sgabor			break;
494210389Sgabor		case 'L':
495210389Sgabor			lflag = false;
496210461Sgabor			Lflag = true;
497210389Sgabor			break;
498210389Sgabor		case 'l':
499210389Sgabor			Lflag = false;
500210461Sgabor			lflag = true;
501210389Sgabor			break;
502210389Sgabor		case 'm':
503210389Sgabor			mflag = true;
504210389Sgabor			errno = 0;
505210389Sgabor			mcount = strtoull(optarg, &ep, 10);
506210389Sgabor			if (((errno == ERANGE) && (mcount == ULLONG_MAX)) ||
507210389Sgabor			    ((errno == EINVAL) && (mcount == 0)))
508210389Sgabor				err(2, NULL);
509210389Sgabor			else if (ep[0] != '\0') {
510210389Sgabor				errno = EINVAL;
511210389Sgabor				err(2, NULL);
512210389Sgabor			}
513210389Sgabor			break;
514210389Sgabor		case 'n':
515210389Sgabor			nflag = true;
516210389Sgabor			break;
517210389Sgabor		case 'O':
518210389Sgabor			linkbehave = LINK_EXPLICIT;
519210389Sgabor			break;
520210389Sgabor		case 'o':
521210389Sgabor			oflag = true;
522210389Sgabor			break;
523210389Sgabor		case 'p':
524210389Sgabor			linkbehave = LINK_SKIP;
525210389Sgabor			break;
526210389Sgabor		case 'q':
527210389Sgabor			qflag = true;
528210389Sgabor			break;
529210389Sgabor		case 'S':
530210461Sgabor			linkbehave = LINK_READ;
531210389Sgabor			break;
532210389Sgabor		case 'R':
533210389Sgabor		case 'r':
534210389Sgabor			dirbehave = DIR_RECURSE;
535210389Sgabor			Hflag = true;
536210389Sgabor			break;
537210389Sgabor		case 's':
538210389Sgabor			sflag = true;
539210389Sgabor			break;
540210389Sgabor		case 'U':
541210389Sgabor			binbehave = BINFILE_BIN;
542210389Sgabor			break;
543210389Sgabor		case 'u':
544210389Sgabor		case MMAP_OPT:
545210389Sgabor			/* noop, compatibility */
546210389Sgabor			break;
547210389Sgabor		case 'V':
548210622Sgabor			printf(getstr(9), __progname, VERSION);
549210389Sgabor			exit(0);
550210389Sgabor		case 'v':
551210389Sgabor			vflag = true;
552210389Sgabor			break;
553210389Sgabor		case 'w':
554210389Sgabor			wflag = true;
555210389Sgabor			break;
556210389Sgabor		case 'x':
557210389Sgabor			xflag = true;
558210389Sgabor			break;
559210389Sgabor		case 'Z':
560210389Sgabor			filebehave = FILE_GZIP;
561210389Sgabor			break;
562210389Sgabor		case BIN_OPT:
563210461Sgabor			if (strcasecmp("binary", optarg) == 0)
564210389Sgabor				binbehave = BINFILE_BIN;
565210461Sgabor			else if (strcasecmp("without-match", optarg) == 0)
566210389Sgabor				binbehave = BINFILE_SKIP;
567210461Sgabor			else if (strcasecmp("text", optarg) == 0)
568210389Sgabor				binbehave = BINFILE_TEXT;
569210389Sgabor			else
570210622Sgabor				errx(2, getstr(3), "--binary-files");
571210389Sgabor			break;
572210389Sgabor		case COLOR_OPT:
573210461Sgabor			color = NULL;
574210461Sgabor			if (optarg == NULL || strcasecmp("auto", optarg) == 0 ||
575210461Sgabor			    strcasecmp("tty", optarg) == 0 ||
576210461Sgabor			    strcasecmp("if-tty", optarg) == 0) {
577210461Sgabor				char *term;
578210461Sgabor
579210461Sgabor				term = getenv("TERM");
580210461Sgabor				if (isatty(STDOUT_FILENO) && term != NULL &&
581210461Sgabor				    strcasecmp(term, "dumb") != 0)
582210461Sgabor					color = init_color("01;31");
583210461Sgabor			} else if (strcasecmp("always", optarg) == 0 ||
584210461Sgabor			    strcasecmp("yes", optarg) == 0 ||
585210461Sgabor			    strcasecmp("force", optarg) == 0) {
586210461Sgabor				color = init_color("01;31");
587210461Sgabor			} else if (strcasecmp("never", optarg) != 0 &&
588210461Sgabor			    strcasecmp("none", optarg) != 0 &&
589210461Sgabor			    strcasecmp("no", optarg) != 0)
590210622Sgabor				errx(2, getstr(3), "--color");
591210389Sgabor			break;
592210389Sgabor		case LABEL_OPT:
593210389Sgabor			label = optarg;
594210389Sgabor			break;
595210389Sgabor		case LINEBUF_OPT:
596210389Sgabor			lbflag = true;
597210389Sgabor			break;
598210389Sgabor		case NULL_OPT:
599210389Sgabor			nullflag = true;
600210389Sgabor			break;
601210389Sgabor		case R_INCLUDE_OPT:
602210578Sgabor			finclude = true;
603210578Sgabor			add_fpattern(optarg, INCL_PAT);
604210389Sgabor			break;
605210389Sgabor		case R_EXCLUDE_OPT:
606210578Sgabor			fexclude = true;
607210578Sgabor			add_fpattern(optarg, EXCL_PAT);
608210389Sgabor			break;
609210389Sgabor		case R_DINCLUDE_OPT:
610211364Sgabor			dinclude = true;
611210578Sgabor			add_dpattern(optarg, INCL_PAT);
612210389Sgabor			break;
613210389Sgabor		case R_DEXCLUDE_OPT:
614211364Sgabor			dexclude = true;
615210578Sgabor			add_dpattern(optarg, EXCL_PAT);
616210389Sgabor			break;
617210389Sgabor		case HELP_OPT:
618210389Sgabor		default:
619210389Sgabor			usage();
620210389Sgabor		}
621210389Sgabor		lastc = c;
622210389Sgabor		newarg = optind != prevoptind;
623210389Sgabor		prevoptind = optind;
624210389Sgabor	}
625210389Sgabor	aargc -= optind;
626210389Sgabor	aargv += optind;
627210389Sgabor
628210389Sgabor	/* Fail if we don't have any pattern */
629210389Sgabor	if (aargc == 0 && needpattern)
630210389Sgabor		usage();
631210389Sgabor
632210389Sgabor	/* Process patterns from command line */
633210389Sgabor	if (aargc != 0 && needpattern) {
634210389Sgabor		add_pattern(*aargv, strlen(*aargv));
635210389Sgabor		--aargc;
636210389Sgabor		++aargv;
637210389Sgabor	}
638210389Sgabor
639210389Sgabor	switch (grepbehave) {
640210389Sgabor	case GREP_FIXED:
641210389Sgabor	case GREP_BASIC:
642210389Sgabor		break;
643210389Sgabor	case GREP_EXTENDED:
644210389Sgabor		cflags |= REG_EXTENDED;
645210389Sgabor		break;
646210389Sgabor	default:
647210389Sgabor		/* NOTREACHED */
648210389Sgabor		usage();
649210389Sgabor	}
650210389Sgabor
651210389Sgabor	fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern));
652210389Sgabor	r_pattern = grep_calloc(patterns, sizeof(*r_pattern));
653210389Sgabor/*
654210389Sgabor * XXX: fgrepcomp() and fastcomp() are workarounds for regexec() performance.
655210389Sgabor * Optimizations should be done there.
656210389Sgabor */
657210389Sgabor		/* Check if cheating is allowed (always is for fgrep). */
658210389Sgabor	if (grepbehave == GREP_FIXED) {
659210389Sgabor		for (i = 0; i < patterns; ++i)
660210389Sgabor			fgrepcomp(&fg_pattern[i], pattern[i]);
661210389Sgabor	} else {
662210389Sgabor		for (i = 0; i < patterns; ++i) {
663210389Sgabor			if (fastcomp(&fg_pattern[i], pattern[i])) {
664210389Sgabor				/* Fall back to full regex library */
665210389Sgabor				c = regcomp(&r_pattern[i], pattern[i], cflags);
666210389Sgabor				if (c != 0) {
667210389Sgabor					regerror(c, &r_pattern[i], re_error,
668210389Sgabor					    RE_ERROR_BUF);
669210389Sgabor					errx(2, "%s", re_error);
670210389Sgabor				}
671210389Sgabor			}
672210389Sgabor		}
673210389Sgabor	}
674210389Sgabor
675210389Sgabor	if (lbflag)
676210389Sgabor		setlinebuf(stdout);
677210389Sgabor
678210389Sgabor	if ((aargc == 0 || aargc == 1) && !Hflag)
679210389Sgabor		hflag = true;
680210389Sgabor
681210389Sgabor	if (aargc == 0)
682210389Sgabor		exit(!procfile("-"));
683210389Sgabor
684210389Sgabor	if (dirbehave == DIR_RECURSE)
685210389Sgabor		c = grep_tree(aargv);
686211519Sdelphij	else
687210578Sgabor		for (c = 0; aargc--; ++aargv) {
688210578Sgabor			if ((finclude || fexclude) && !file_matching(*aargv))
689210578Sgabor				continue;
690210389Sgabor			c+= procfile(*aargv);
691210578Sgabor		}
692210389Sgabor
693210389Sgabor#ifndef WITHOUT_NLS
694210389Sgabor	catclose(catalog);
695210389Sgabor#endif
696210389Sgabor
697210389Sgabor	/* Find out the correct return value according to the
698210389Sgabor	   results and the command line option. */
699210389Sgabor	exit(c ? (notfound ? (qflag ? 0 : 2) : 0) : (notfound ? 2 : 1));
700210389Sgabor}
701