grep.c revision 210389
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 210389 2010-07-22 19:11: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",
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 */
117210389Sgaborchar	*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 */
121210389Sgaborint	 devbehave = DEV_GREP;		/* -D: handling of devices */
122210389Sgaborint	 dirbehave = DIR_GREP;		/* -dRr: handling of directories */
123210389Sgaborint	 linkbehave = LINK_GREP;	/* -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
139210389Sgabor/* Housekeeping */
140210389Sgaborbool	 first = true;	/* flag whether we are processing the first match */
141210389Sgaborbool	 prev;		/* flag whether or not the previous line matched */
142210389Sgaborint	 tail;		/* lines left to print */
143210389Sgaborbool	 notfound;	/* file not found */
144210389Sgabor
145210389Sgaborextern char	*__progname;
146210389Sgabor
147210389Sgabor/*
148210389Sgabor * Prints usage information and returns 2.
149210389Sgabor */
150210389Sgaborstatic void
151210389Sgaborusage(void)
152210389Sgabor{
153210389Sgabor	fprintf(stderr, getstr(4), __progname);
154210389Sgabor	fprintf(stderr, "%s", getstr(5));
155210389Sgabor	fprintf(stderr, "%s", getstr(5));
156210389Sgabor	fprintf(stderr, "%s", getstr(6));
157210389Sgabor	fprintf(stderr, "%s", getstr(7));
158210389Sgabor	exit(2);
159210389Sgabor}
160210389Sgabor
161210389Sgaborstatic const char	*optstr = "0123456789A:B:C:D:EFGHIJLOPSRUVZabcd:e:f:hilm:nopqrsuvwxy";
162210389Sgabor
163210389Sgaborstruct option long_options[] =
164210389Sgabor{
165210389Sgabor	{"binary-files",	required_argument,	NULL, BIN_OPT},
166210389Sgabor	{"help",		no_argument,		NULL, HELP_OPT},
167210389Sgabor	{"mmap",		no_argument,		NULL, MMAP_OPT},
168210389Sgabor	{"line-buffered",	no_argument,		NULL, LINEBUF_OPT},
169210389Sgabor	{"label",		required_argument,	NULL, LABEL_OPT},
170210389Sgabor	{"null",		no_argument,		NULL, NULL_OPT},
171210389Sgabor	{"color",		optional_argument,	NULL, COLOR_OPT},
172210389Sgabor	{"colour",		optional_argument,	NULL, COLOR_OPT},
173210389Sgabor	{"exclude",		required_argument,	NULL, R_EXCLUDE_OPT},
174210389Sgabor	{"include",		required_argument,	NULL, R_INCLUDE_OPT},
175210389Sgabor	{"exclude-dir",		required_argument,	NULL, R_DEXCLUDE_OPT},
176210389Sgabor	{"include-dir",		required_argument,	NULL, R_DINCLUDE_OPT},
177210389Sgabor	{"after-context",	required_argument,	NULL, 'A'},
178210389Sgabor	{"text",		no_argument,		NULL, 'a'},
179210389Sgabor	{"before-context",	required_argument,	NULL, 'B'},
180210389Sgabor	{"byte-offset",		no_argument,		NULL, 'b'},
181210389Sgabor	{"context",		optional_argument,	NULL, 'C'},
182210389Sgabor	{"count",		no_argument,		NULL, 'c'},
183210389Sgabor	{"devices",		required_argument,	NULL, 'D'},
184210389Sgabor        {"directories",		required_argument,	NULL, 'd'},
185210389Sgabor	{"extended-regexp",	no_argument,		NULL, 'E'},
186210389Sgabor	{"regexp",		required_argument,	NULL, 'e'},
187210389Sgabor	{"fixed-strings",	no_argument,		NULL, 'F'},
188210389Sgabor	{"file",		required_argument,	NULL, 'f'},
189210389Sgabor	{"basic-regexp",	no_argument,		NULL, 'G'},
190210389Sgabor	{"no-filename",		no_argument,		NULL, 'h'},
191210389Sgabor	{"with-filename",	no_argument,		NULL, 'H'},
192210389Sgabor	{"ignore-case",		no_argument,		NULL, 'i'},
193210389Sgabor	{"bz2decompress",	no_argument,		NULL, 'J'},
194210389Sgabor	{"files-with-matches",	no_argument,		NULL, 'l'},
195210389Sgabor	{"files-without-match", no_argument,            NULL, 'L'},
196210389Sgabor	{"max-count",		required_argument,	NULL, 'm'},
197210389Sgabor	{"line-number",		no_argument,		NULL, 'n'},
198210389Sgabor	{"only-matching",	no_argument,		NULL, 'o'},
199210389Sgabor	{"quiet",		no_argument,		NULL, 'q'},
200210389Sgabor	{"silent",		no_argument,		NULL, 'q'},
201210389Sgabor	{"recursive",		no_argument,		NULL, 'r'},
202210389Sgabor	{"no-messages",		no_argument,		NULL, 's'},
203210389Sgabor	{"binary",		no_argument,		NULL, 'U'},
204210389Sgabor	{"unix-byte-offsets",	no_argument,		NULL, 'u'},
205210389Sgabor	{"invert-match",	no_argument,		NULL, 'v'},
206210389Sgabor	{"version",		no_argument,		NULL, 'V'},
207210389Sgabor	{"word-regexp",		no_argument,		NULL, 'w'},
208210389Sgabor	{"line-regexp",		no_argument,		NULL, 'x'},
209210389Sgabor	{"decompress",          no_argument,            NULL, 'Z'},
210210389Sgabor	{NULL,			no_argument,		NULL, 0}
211210389Sgabor};
212210389Sgabor
213210389Sgabor/*
214210389Sgabor * Adds a searching pattern to the internal array.
215210389Sgabor */
216210389Sgaborstatic void
217210389Sgaboradd_pattern(char *pat, size_t len)
218210389Sgabor{
219210389Sgabor
220210389Sgabor	/* Check if we can do a shortcut */
221210389Sgabor	if (len == 0 || matchall) {
222210389Sgabor		matchall = true;
223210389Sgabor		return;
224210389Sgabor	}
225210389Sgabor	/* Increase size if necessary */
226210389Sgabor	if (patterns == pattern_sz) {
227210389Sgabor		pattern_sz *= 2;
228210389Sgabor		pattern = grep_realloc(pattern, ++pattern_sz *
229210389Sgabor		    sizeof(*pattern));
230210389Sgabor	}
231210389Sgabor	if (len > 0 && pat[len - 1] == '\n')
232210389Sgabor		--len;
233210389Sgabor	/* pat may not be NUL-terminated */
234210389Sgabor	pattern[patterns] = grep_malloc(len + 1);
235210389Sgabor	memcpy(pattern[patterns], pat, len);
236210389Sgabor	pattern[patterns][len] = '\0';
237210389Sgabor	++patterns;
238210389Sgabor}
239210389Sgabor
240210389Sgabor/*
241210389Sgabor * Adds an include/exclude pattern to the internal array.
242210389Sgabor */
243210389Sgaborstatic void
244210389Sgaboradd_epattern(char *pat, size_t len, int type, int mode)
245210389Sgabor{
246210389Sgabor
247210389Sgabor	/* Increase size if necessary */
248210389Sgabor	if (epatterns == epattern_sz) {
249210389Sgabor		epattern_sz *= 2;
250210389Sgabor		epattern = grep_realloc(epattern, ++epattern_sz *
251210389Sgabor		    sizeof(struct epat));
252210389Sgabor	}
253210389Sgabor	if (len > 0 && pat[len - 1] == '\n')
254210389Sgabor		 --len;
255210389Sgabor	epattern[epatterns].pat = grep_malloc(len + 1);
256210389Sgabor	memcpy(epattern[epatterns].pat, pat, len);
257210389Sgabor	epattern[epatterns].pat[len] = '\0';
258210389Sgabor	epattern[epatterns].type = type;
259210389Sgabor	epattern[epatterns].mode = mode;
260210389Sgabor	++epatterns;
261210389Sgabor}
262210389Sgabor
263210389Sgabor/*
264210389Sgabor * Reads searching patterns from a file and adds them with add_pattern().
265210389Sgabor */
266210389Sgaborstatic void
267210389Sgaborread_patterns(const char *fn)
268210389Sgabor{
269210389Sgabor	FILE *f;
270210389Sgabor	char *line;
271210389Sgabor	size_t len;
272210389Sgabor
273210389Sgabor	if ((f = fopen(fn, "r")) == NULL)
274210389Sgabor		err(2, "%s", fn);
275210389Sgabor	while ((line = fgetln(f, &len)) != NULL)
276210389Sgabor		add_pattern(line, *line == '\n' ? 0 : len);
277210389Sgabor	if (ferror(f))
278210389Sgabor		err(2, "%s", fn);
279210389Sgabor	fclose(f);
280210389Sgabor}
281210389Sgabor
282210389Sgaborint
283210389Sgabormain(int argc, char *argv[])
284210389Sgabor{
285210389Sgabor	char **aargv, **eargv, *eopts;
286210389Sgabor	char *ep;
287210389Sgabor	unsigned long long l;
288210389Sgabor	unsigned int aargc, eargc, i;
289210389Sgabor	int c, lastc, needpattern, newarg, prevoptind;
290210389Sgabor
291210389Sgabor	setlocale(LC_ALL, "");
292210389Sgabor
293210389Sgabor#ifndef WITHOUT_NLS
294210389Sgabor	catalog = catopen("grep", NL_CAT_LOCALE);
295210389Sgabor#endif
296210389Sgabor
297210389Sgabor	/* Check what is the program name of the binary.  In this
298210389Sgabor	   way we can have all the funcionalities in one binary
299210389Sgabor	   without the need of scripting and using ugly hacks. */
300210389Sgabor	switch (__progname[0]) {
301210389Sgabor	case 'e':
302210389Sgabor		grepbehave = GREP_EXTENDED;
303210389Sgabor		break;
304210389Sgabor	case 'f':
305210389Sgabor		grepbehave = GREP_FIXED;
306210389Sgabor		break;
307210389Sgabor	case 'g':
308210389Sgabor		grepbehave = GREP_BASIC;
309210389Sgabor		break;
310210389Sgabor	case 'z':
311210389Sgabor		filebehave = FILE_GZIP;
312210389Sgabor		switch(__progname[1]) {
313210389Sgabor		case 'e':
314210389Sgabor			grepbehave = GREP_EXTENDED;
315210389Sgabor			break;
316210389Sgabor		case 'f':
317210389Sgabor			grepbehave = GREP_FIXED;
318210389Sgabor			break;
319210389Sgabor		case 'g':
320210389Sgabor			grepbehave = GREP_BASIC;
321210389Sgabor			break;
322210389Sgabor		}
323210389Sgabor		break;
324210389Sgabor	}
325210389Sgabor
326210389Sgabor	lastc = '\0';
327210389Sgabor	newarg = 1;
328210389Sgabor	prevoptind = 1;
329210389Sgabor	needpattern = 1;
330210389Sgabor
331210389Sgabor	eopts = getenv("GREP_OPTIONS");
332210389Sgabor
333210389Sgabor	eargc = 1;
334210389Sgabor	if (eopts != NULL) {
335210389Sgabor		char *str;
336210389Sgabor
337210389Sgabor		for(i = 0; i < strlen(eopts); i++)
338210389Sgabor			if (eopts[i] == ' ')
339210389Sgabor				eargc++;
340210389Sgabor
341210389Sgabor		eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1));
342210389Sgabor
343210389Sgabor		str = strtok(eopts, " ");
344210389Sgabor		eargc = 0;
345210389Sgabor
346210389Sgabor		while(str != NULL) {
347210389Sgabor			eargv[++eargc] = (char *)grep_malloc(sizeof(char) *
348210389Sgabor			    (strlen(str) + 1));
349210389Sgabor			strlcpy(eargv[eargc], str, strlen(str) + 1);
350210389Sgabor			str = strtok(NULL, " ");
351210389Sgabor		}
352210389Sgabor		eargv[++eargc] = NULL;
353210389Sgabor
354210389Sgabor		aargv = (char **)grep_malloc(sizeof(char *) *
355210389Sgabor		    (eargc + argc + 1));
356210389Sgabor		aargv[0] = argv[0];
357210389Sgabor
358210389Sgabor		for(i = 1; i < eargc; i++)
359210389Sgabor			aargv[i] = eargv[i];
360210389Sgabor		for(int j = 1; j < argc; j++)
361210389Sgabor			aargv[i++] = argv[j];
362210389Sgabor
363210389Sgabor		aargc = eargc + argc - 1;
364210389Sgabor
365210389Sgabor	} else {
366210389Sgabor		aargv = argv;
367210389Sgabor		aargc = argc;
368210389Sgabor	}
369210389Sgabor
370210389Sgabor	while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) !=
371210389Sgabor	    -1)) {
372210389Sgabor		switch (c) {
373210389Sgabor		case '0': case '1': case '2': case '3': case '4':
374210389Sgabor		case '5': case '6': case '7': case '8': case '9':
375210389Sgabor			if (newarg || !isdigit(lastc))
376210389Sgabor				Aflag = 0;
377210389Sgabor			else if (Aflag > LLONG_MAX / 10) {
378210389Sgabor				errno = ERANGE;
379210389Sgabor				err(2, NULL);
380210389Sgabor			}
381210389Sgabor			Aflag = Bflag = (Aflag * 10) + (c - '0');
382210389Sgabor			break;
383210389Sgabor		case 'C':
384210389Sgabor			if (optarg == NULL) {
385210389Sgabor				Aflag = Bflag = 2;
386210389Sgabor				break;
387210389Sgabor			}
388210389Sgabor			/* FALLTHROUGH */
389210389Sgabor		case 'A':
390210389Sgabor			/* FALLTHROUGH */
391210389Sgabor		case 'B':
392210389Sgabor			errno = 0;
393210389Sgabor			l = strtoull(optarg, &ep, 10);
394210389Sgabor			if (((errno == ERANGE) && (l == ULLONG_MAX)) ||
395210389Sgabor			    ((errno == EINVAL) && (l == 0)))
396210389Sgabor				err(2, NULL);
397210389Sgabor			else if (ep[0] != '\0') {
398210389Sgabor				errno = EINVAL;
399210389Sgabor				err(2, NULL);
400210389Sgabor			}
401210389Sgabor			if (c == 'A')
402210389Sgabor				Aflag = l;
403210389Sgabor			else if (c == 'B')
404210389Sgabor				Bflag = l;
405210389Sgabor			else
406210389Sgabor				Aflag = Bflag = l;
407210389Sgabor			break;
408210389Sgabor		case 'a':
409210389Sgabor			binbehave = BINFILE_TEXT;
410210389Sgabor			break;
411210389Sgabor		case 'b':
412210389Sgabor			bflag = true;
413210389Sgabor			break;
414210389Sgabor		case 'c':
415210389Sgabor			cflag = true;
416210389Sgabor			break;
417210389Sgabor		case 'D':
418210389Sgabor			if (strcmp(optarg, "skip") == 0)
419210389Sgabor				devbehave = DEV_SKIP;
420210389Sgabor			break;
421210389Sgabor		case 'd':
422210389Sgabor			if (strcmp("recurse", optarg) == 0) {
423210389Sgabor				Hflag = true;
424210389Sgabor				dirbehave = DIR_RECURSE;
425210389Sgabor			} else if (strcmp("skip", optarg) == 0)
426210389Sgabor				dirbehave = DIR_SKIP;
427210389Sgabor			else if (strcmp("read", optarg) != 0) {
428210389Sgabor				errno = EINVAL;
429210389Sgabor				err(2, NULL);
430210389Sgabor			}
431210389Sgabor			break;
432210389Sgabor		case 'E':
433210389Sgabor			grepbehave = GREP_EXTENDED;
434210389Sgabor			break;
435210389Sgabor		case 'e':
436210389Sgabor			add_pattern(optarg, strlen(optarg));
437210389Sgabor			needpattern = 0;
438210389Sgabor			break;
439210389Sgabor		case 'F':
440210389Sgabor			grepbehave = GREP_FIXED;
441210389Sgabor			break;
442210389Sgabor		case 'f':
443210389Sgabor			read_patterns(optarg);
444210389Sgabor			needpattern = 0;
445210389Sgabor			break;
446210389Sgabor		case 'G':
447210389Sgabor			grepbehave = GREP_BASIC;
448210389Sgabor			break;
449210389Sgabor		case 'H':
450210389Sgabor			Hflag = true;
451210389Sgabor			break;
452210389Sgabor		case 'h':
453210389Sgabor			Hflag = false;
454210389Sgabor			hflag = true;
455210389Sgabor			break;
456210389Sgabor		case 'I':
457210389Sgabor			binbehave = BINFILE_SKIP;
458210389Sgabor			break;
459210389Sgabor		case 'i':
460210389Sgabor		case 'y':
461210389Sgabor			iflag =  true;
462210389Sgabor			cflags |= REG_ICASE;
463210389Sgabor			break;
464210389Sgabor		case 'J':
465210389Sgabor			filebehave = FILE_BZIP;
466210389Sgabor			break;
467210389Sgabor		case 'L':
468210389Sgabor			lflag = false;
469210389Sgabor			Lflag = qflag = true;
470210389Sgabor			break;
471210389Sgabor		case 'l':
472210389Sgabor			Lflag = false;
473210389Sgabor			lflag = qflag = true;
474210389Sgabor			break;
475210389Sgabor		case 'm':
476210389Sgabor			mflag = true;
477210389Sgabor			errno = 0;
478210389Sgabor			mcount = strtoull(optarg, &ep, 10);
479210389Sgabor			if (((errno == ERANGE) && (mcount == ULLONG_MAX)) ||
480210389Sgabor			    ((errno == EINVAL) && (mcount == 0)))
481210389Sgabor				err(2, NULL);
482210389Sgabor			else if (ep[0] != '\0') {
483210389Sgabor				errno = EINVAL;
484210389Sgabor				err(2, NULL);
485210389Sgabor			}
486210389Sgabor			break;
487210389Sgabor		case 'n':
488210389Sgabor			nflag = true;
489210389Sgabor			break;
490210389Sgabor		case 'O':
491210389Sgabor			linkbehave = LINK_EXPLICIT;
492210389Sgabor			break;
493210389Sgabor		case 'o':
494210389Sgabor			oflag = true;
495210389Sgabor			break;
496210389Sgabor		case 'p':
497210389Sgabor			linkbehave = LINK_SKIP;
498210389Sgabor			break;
499210389Sgabor		case 'q':
500210389Sgabor			qflag = true;
501210389Sgabor			break;
502210389Sgabor		case 'S':
503210389Sgabor			linkbehave = LINK_GREP;
504210389Sgabor			break;
505210389Sgabor		case 'R':
506210389Sgabor		case 'r':
507210389Sgabor			dirbehave = DIR_RECURSE;
508210389Sgabor			Hflag = true;
509210389Sgabor			break;
510210389Sgabor		case 's':
511210389Sgabor			sflag = true;
512210389Sgabor			break;
513210389Sgabor		case 'U':
514210389Sgabor			binbehave = BINFILE_BIN;
515210389Sgabor			break;
516210389Sgabor		case 'u':
517210389Sgabor		case MMAP_OPT:
518210389Sgabor			/* noop, compatibility */
519210389Sgabor			break;
520210389Sgabor		case 'V':
521210389Sgabor			printf(getstr(10), __progname, VERSION);
522210389Sgabor			exit(0);
523210389Sgabor		case 'v':
524210389Sgabor			vflag = true;
525210389Sgabor			break;
526210389Sgabor		case 'w':
527210389Sgabor			wflag = true;
528210389Sgabor			break;
529210389Sgabor		case 'x':
530210389Sgabor			xflag = true;
531210389Sgabor			break;
532210389Sgabor		case 'Z':
533210389Sgabor			filebehave = FILE_GZIP;
534210389Sgabor			break;
535210389Sgabor		case BIN_OPT:
536210389Sgabor			if (strcmp("binary", optarg) == 0)
537210389Sgabor				binbehave = BINFILE_BIN;
538210389Sgabor			else if (strcmp("without-match", optarg) == 0)
539210389Sgabor				binbehave = BINFILE_SKIP;
540210389Sgabor			else if (strcmp("text", optarg) == 0)
541210389Sgabor				binbehave = BINFILE_TEXT;
542210389Sgabor			else
543210389Sgabor				errx(2, "%s", getstr(8));
544210389Sgabor			break;
545210389Sgabor		case COLOR_OPT:
546210389Sgabor			if (optarg == NULL || strcmp("auto", optarg) == 0 ||
547210389Sgabor			    strcmp("always", optarg) == 0 ) {
548210389Sgabor				color = getenv("GREP_COLOR");
549210389Sgabor				if (color == NULL) {
550210389Sgabor					color = grep_malloc(sizeof(char) * 6);
551210389Sgabor					strcpy(color, "01;31");
552210389Sgabor				}
553210389Sgabor			} else if (strcmp("never", optarg) == 0)
554210389Sgabor				color = NULL;
555210389Sgabor			else
556210389Sgabor				errx(2, "%s", getstr(3));
557210389Sgabor			break;
558210389Sgabor		case LABEL_OPT:
559210389Sgabor			label = optarg;
560210389Sgabor			break;
561210389Sgabor		case LINEBUF_OPT:
562210389Sgabor			lbflag = true;
563210389Sgabor			break;
564210389Sgabor		case NULL_OPT:
565210389Sgabor			nullflag = true;
566210389Sgabor			break;
567210389Sgabor		case R_INCLUDE_OPT:
568210389Sgabor			exclflag = true;
569210389Sgabor			add_epattern(basename(optarg), strlen(basename(optarg)),
570210389Sgabor			    FILE_PAT, INCL_PAT);
571210389Sgabor			break;
572210389Sgabor		case R_EXCLUDE_OPT:
573210389Sgabor			exclflag = true;
574210389Sgabor			add_epattern(basename(optarg), strlen(basename(optarg)),
575210389Sgabor			    FILE_PAT, EXCL_PAT);
576210389Sgabor			break;
577210389Sgabor		case R_DINCLUDE_OPT:
578210389Sgabor			exclflag = true;
579210389Sgabor			add_epattern(basename(optarg), strlen(basename(optarg)),
580210389Sgabor			    DIR_PAT, INCL_PAT);
581210389Sgabor			break;
582210389Sgabor		case R_DEXCLUDE_OPT:
583210389Sgabor			exclflag = true;
584210389Sgabor			add_epattern(basename(optarg), strlen(basename(optarg)),
585210389Sgabor			    DIR_PAT, EXCL_PAT);
586210389Sgabor			break;
587210389Sgabor		case HELP_OPT:
588210389Sgabor		default:
589210389Sgabor			usage();
590210389Sgabor		}
591210389Sgabor		lastc = c;
592210389Sgabor		newarg = optind != prevoptind;
593210389Sgabor		prevoptind = optind;
594210389Sgabor	}
595210389Sgabor	aargc -= optind;
596210389Sgabor	aargv += optind;
597210389Sgabor
598210389Sgabor	/* Fail if we don't have any pattern */
599210389Sgabor	if (aargc == 0 && needpattern)
600210389Sgabor		usage();
601210389Sgabor
602210389Sgabor	/* Process patterns from command line */
603210389Sgabor	if (aargc != 0 && needpattern) {
604210389Sgabor		add_pattern(*aargv, strlen(*aargv));
605210389Sgabor		--aargc;
606210389Sgabor		++aargv;
607210389Sgabor	}
608210389Sgabor
609210389Sgabor	switch (grepbehave) {
610210389Sgabor	case GREP_FIXED:
611210389Sgabor	case GREP_BASIC:
612210389Sgabor		break;
613210389Sgabor	case GREP_EXTENDED:
614210389Sgabor		cflags |= REG_EXTENDED;
615210389Sgabor		break;
616210389Sgabor	default:
617210389Sgabor		/* NOTREACHED */
618210389Sgabor		usage();
619210389Sgabor	}
620210389Sgabor
621210389Sgabor	fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern));
622210389Sgabor	r_pattern = grep_calloc(patterns, sizeof(*r_pattern));
623210389Sgabor/*
624210389Sgabor * XXX: fgrepcomp() and fastcomp() are workarounds for regexec() performance.
625210389Sgabor * Optimizations should be done there.
626210389Sgabor */
627210389Sgabor		/* Check if cheating is allowed (always is for fgrep). */
628210389Sgabor	if (grepbehave == GREP_FIXED) {
629210389Sgabor		for (i = 0; i < patterns; ++i)
630210389Sgabor			fgrepcomp(&fg_pattern[i], pattern[i]);
631210389Sgabor	} else {
632210389Sgabor		for (i = 0; i < patterns; ++i) {
633210389Sgabor			if (fastcomp(&fg_pattern[i], pattern[i])) {
634210389Sgabor				/* Fall back to full regex library */
635210389Sgabor				c = regcomp(&r_pattern[i], pattern[i], cflags);
636210389Sgabor				if (c != 0) {
637210389Sgabor					regerror(c, &r_pattern[i], re_error,
638210389Sgabor					    RE_ERROR_BUF);
639210389Sgabor					errx(2, "%s", re_error);
640210389Sgabor				}
641210389Sgabor			}
642210389Sgabor		}
643210389Sgabor	}
644210389Sgabor
645210389Sgabor	if (lbflag)
646210389Sgabor		setlinebuf(stdout);
647210389Sgabor
648210389Sgabor	if ((aargc == 0 || aargc == 1) && !Hflag)
649210389Sgabor		hflag = true;
650210389Sgabor
651210389Sgabor	if (aargc == 0)
652210389Sgabor		exit(!procfile("-"));
653210389Sgabor
654210389Sgabor	if (dirbehave == DIR_RECURSE)
655210389Sgabor		c = grep_tree(aargv);
656210389Sgabor	else
657210389Sgabor		for (c = 0; aargc--; ++aargv)
658210389Sgabor			c+= procfile(*aargv);
659210389Sgabor
660210389Sgabor#ifndef WITHOUT_NLS
661210389Sgabor	catclose(catalog);
662210389Sgabor#endif
663210389Sgabor
664210389Sgabor	/* Find out the correct return value according to the
665210389Sgabor	   results and the command line option. */
666210389Sgabor	exit(c ? (notfound ? (qflag ? 0 : 2) : 0) : (notfound ? 2 : 1));
667210389Sgabor}
668