grep.c revision 244493
1220422Sgabor/*	$NetBSD: grep.c,v 1.4 2011/02/16 01:31:33 joerg Exp $	*/
2220422Sgabor/* 	$FreeBSD: head/usr.bin/grep/grep.c 244493 2012-12-20 17:38:14Z eadler $	*/
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 244493 2012-12-20 17:38:14Z eadler $");
34210389Sgabor
35210389Sgabor#include <sys/stat.h>
36210389Sgabor#include <sys/types.h>
37210389Sgabor
38210389Sgabor#include <ctype.h>
39210389Sgabor#include <err.h>
40210389Sgabor#include <errno.h>
41226035Sgabor#include <fcntl.h>
42210389Sgabor#include <getopt.h>
43210389Sgabor#include <limits.h>
44210389Sgabor#include <libgen.h>
45210389Sgabor#include <locale.h>
46210389Sgabor#include <stdbool.h>
47210389Sgabor#include <stdio.h>
48210389Sgabor#include <stdlib.h>
49210389Sgabor#include <string.h>
50210389Sgabor#include <unistd.h>
51210389Sgabor
52226035Sgabor#include "fastmatch.h"
53210389Sgabor#include "grep.h"
54210389Sgabor
55210389Sgabor#ifndef WITHOUT_NLS
56210389Sgabor#include <nl_types.h>
57210389Sgabornl_catd	 catalog;
58210389Sgabor#endif
59210389Sgabor
60210389Sgabor/*
61210389Sgabor * Default messags to use when NLS is disabled or no catalogue
62210389Sgabor * is found.
63210389Sgabor */
64210389Sgaborconst char	*errstr[] = {
65210389Sgabor	"",
66210389Sgabor/* 1*/	"(standard input)",
67210389Sgabor/* 2*/	"cannot read bzip2 compressed file",
68210622Sgabor/* 3*/	"unknown %s option",
69210389Sgabor/* 4*/	"usage: %s [-abcDEFGHhIiJLlmnOoPqRSsUVvwxZ] [-A num] [-B num] [-C[num]]\n",
70210389Sgabor/* 5*/	"\t[-e pattern] [-f file] [--binary-files=value] [--color=when]\n",
71210389Sgabor/* 6*/	"\t[--context[=num]] [--directories=action] [--label] [--line-buffered]\n",
72210389Sgabor/* 7*/	"\t[--null] [pattern] [file ...]\n",
73210622Sgabor/* 8*/	"Binary file %s matches\n",
74210622Sgabor/* 9*/	"%s (BSD grep) %s\n",
75210389Sgabor};
76210389Sgabor
77210389Sgabor/* Flags passed to regcomp() and regexec() */
78223009Sgaborint		 cflags = REG_NOSUB;
79210389Sgaborint		 eflags = REG_STARTEND;
80210389Sgabor
81210389Sgabor/* Shortcut for matching all cases like empty regex */
82210389Sgaborbool		 matchall;
83210389Sgabor
84210389Sgabor/* Searching patterns */
85241737Sedunsigned int	 patterns;
86241737Sedstatic unsigned int pattern_sz;
87226035Sgaborstruct pat	*pattern;
88210389Sgaborregex_t		*r_pattern;
89226035Sgaborfastmatch_t	*fg_pattern;
90210389Sgabor
91210389Sgabor/* Filename exclusion/inclusion patterns */
92241737Sedunsigned int	fpatterns, dpatterns;
93241737Sedstatic unsigned int fpattern_sz, dpattern_sz;
94210578Sgaborstruct epat	*dpattern, *fpattern;
95210389Sgabor
96210389Sgabor/* For regex errors  */
97210389Sgaborchar	 re_error[RE_ERROR_BUF + 1];
98210389Sgabor
99210389Sgabor/* Command-line flags */
100210389Sgaborunsigned long long Aflag;	/* -A x: print x lines trailing each match */
101210389Sgaborunsigned long long Bflag;	/* -B x: print x lines leading each match */
102210389Sgaborbool	 Hflag;		/* -H: always print file name */
103210389Sgaborbool	 Lflag;		/* -L: only show names of files with no matches */
104210389Sgaborbool	 bflag;		/* -b: show block numbers for each match */
105210389Sgaborbool	 cflag;		/* -c: only show a count of matching lines */
106210389Sgaborbool	 hflag;		/* -h: don't print filename headers */
107210389Sgaborbool	 iflag;		/* -i: ignore case */
108210389Sgaborbool	 lflag;		/* -l: only show names of files with matches */
109210389Sgaborbool	 mflag;		/* -m x: stop reading the files after x matches */
110226035Sgaborlong long mcount;	/* count for -m */
111244493Seadlerlong long mlimit;	/* requested value for -m */
112210389Sgaborbool	 nflag;		/* -n: show line numbers in front of matching lines */
113210389Sgaborbool	 oflag;		/* -o: print only matching part */
114210389Sgaborbool	 qflag;		/* -q: quiet mode (don't output anything) */
115210389Sgaborbool	 sflag;		/* -s: silent mode (ignore errors) */
116210389Sgaborbool	 vflag;		/* -v: only show non-matching lines */
117210389Sgaborbool	 wflag;		/* -w: pattern must start and end on word boundaries */
118210389Sgaborbool	 xflag;		/* -x: pattern must match entire line */
119210389Sgaborbool	 lbflag;	/* --line-buffered */
120210389Sgaborbool	 nullflag;	/* --null */
121210389Sgaborchar	*label;		/* --label */
122210461Sgaborconst char *color;	/* --color */
123210389Sgaborint	 grepbehave = GREP_BASIC;	/* -EFGP: type of the regex */
124210389Sgaborint	 binbehave = BINFILE_BIN;	/* -aIU: handling of binary files */
125210389Sgaborint	 filebehave = FILE_STDIO;	/* -JZ: normal, gzip or bzip2 file */
126210461Sgaborint	 devbehave = DEV_READ;		/* -D: handling of devices */
127210461Sgaborint	 dirbehave = DIR_READ;		/* -dRr: handling of directories */
128210461Sgaborint	 linkbehave = LINK_READ;	/* -OpS: handling of symlinks */
129210389Sgabor
130211364Sgaborbool	 dexclude, dinclude;	/* --exclude-dir and --include-dir */
131211364Sgaborbool	 fexclude, finclude;	/* --exclude and --include */
132210578Sgabor
133210389Sgaborenum {
134210389Sgabor	BIN_OPT = CHAR_MAX + 1,
135210389Sgabor	COLOR_OPT,
136210389Sgabor	HELP_OPT,
137210389Sgabor	MMAP_OPT,
138210389Sgabor	LINEBUF_OPT,
139210389Sgabor	LABEL_OPT,
140210389Sgabor	NULL_OPT,
141210389Sgabor	R_EXCLUDE_OPT,
142210389Sgabor	R_INCLUDE_OPT,
143210389Sgabor	R_DEXCLUDE_OPT,
144210389Sgabor	R_DINCLUDE_OPT
145210389Sgabor};
146210389Sgabor
147210461Sgaborstatic inline const char	*init_color(const char *);
148210461Sgabor
149210389Sgabor/* Housekeeping */
150210389Sgaborbool	 first = true;	/* flag whether we are processing the first match */
151210389Sgaborbool	 prev;		/* flag whether or not the previous line matched */
152210389Sgaborint	 tail;		/* lines left to print */
153228319Sgaborbool	 file_err;	/* file reading error */
154210389Sgabor
155210389Sgabor/*
156210389Sgabor * Prints usage information and returns 2.
157210389Sgabor */
158210389Sgaborstatic void
159210389Sgaborusage(void)
160210389Sgabor{
161226271Sgabor	fprintf(stderr, getstr(4), getprogname());
162210389Sgabor	fprintf(stderr, "%s", getstr(5));
163210389Sgabor	fprintf(stderr, "%s", getstr(6));
164210389Sgabor	fprintf(stderr, "%s", getstr(7));
165210389Sgabor	exit(2);
166210389Sgabor}
167210389Sgabor
168226035Sgaborstatic const char	*optstr = "0123456789A:B:C:D:EFGHIJMLOPSRUVZabcd:e:f:hilm:nopqrsuvwxXy";
169210389Sgabor
170228395Sedstatic const struct option long_options[] =
171210389Sgabor{
172210389Sgabor	{"binary-files",	required_argument,	NULL, BIN_OPT},
173210389Sgabor	{"help",		no_argument,		NULL, HELP_OPT},
174210389Sgabor	{"mmap",		no_argument,		NULL, MMAP_OPT},
175210389Sgabor	{"line-buffered",	no_argument,		NULL, LINEBUF_OPT},
176210389Sgabor	{"label",		required_argument,	NULL, LABEL_OPT},
177210389Sgabor	{"null",		no_argument,		NULL, NULL_OPT},
178210389Sgabor	{"color",		optional_argument,	NULL, COLOR_OPT},
179210389Sgabor	{"colour",		optional_argument,	NULL, COLOR_OPT},
180210389Sgabor	{"exclude",		required_argument,	NULL, R_EXCLUDE_OPT},
181210389Sgabor	{"include",		required_argument,	NULL, R_INCLUDE_OPT},
182210389Sgabor	{"exclude-dir",		required_argument,	NULL, R_DEXCLUDE_OPT},
183210389Sgabor	{"include-dir",		required_argument,	NULL, R_DINCLUDE_OPT},
184210389Sgabor	{"after-context",	required_argument,	NULL, 'A'},
185210389Sgabor	{"text",		no_argument,		NULL, 'a'},
186210389Sgabor	{"before-context",	required_argument,	NULL, 'B'},
187210389Sgabor	{"byte-offset",		no_argument,		NULL, 'b'},
188210389Sgabor	{"context",		optional_argument,	NULL, 'C'},
189210389Sgabor	{"count",		no_argument,		NULL, 'c'},
190210389Sgabor	{"devices",		required_argument,	NULL, 'D'},
191210389Sgabor        {"directories",		required_argument,	NULL, 'd'},
192210389Sgabor	{"extended-regexp",	no_argument,		NULL, 'E'},
193210389Sgabor	{"regexp",		required_argument,	NULL, 'e'},
194210389Sgabor	{"fixed-strings",	no_argument,		NULL, 'F'},
195210389Sgabor	{"file",		required_argument,	NULL, 'f'},
196210389Sgabor	{"basic-regexp",	no_argument,		NULL, 'G'},
197210389Sgabor	{"no-filename",		no_argument,		NULL, 'h'},
198210389Sgabor	{"with-filename",	no_argument,		NULL, 'H'},
199210389Sgabor	{"ignore-case",		no_argument,		NULL, 'i'},
200210389Sgabor	{"bz2decompress",	no_argument,		NULL, 'J'},
201210389Sgabor	{"files-with-matches",	no_argument,		NULL, 'l'},
202210389Sgabor	{"files-without-match", no_argument,            NULL, 'L'},
203210389Sgabor	{"max-count",		required_argument,	NULL, 'm'},
204226035Sgabor	{"lzma",		no_argument,		NULL, 'M'},
205210389Sgabor	{"line-number",		no_argument,		NULL, 'n'},
206210389Sgabor	{"only-matching",	no_argument,		NULL, 'o'},
207210389Sgabor	{"quiet",		no_argument,		NULL, 'q'},
208210389Sgabor	{"silent",		no_argument,		NULL, 'q'},
209210389Sgabor	{"recursive",		no_argument,		NULL, 'r'},
210210389Sgabor	{"no-messages",		no_argument,		NULL, 's'},
211210389Sgabor	{"binary",		no_argument,		NULL, 'U'},
212210389Sgabor	{"unix-byte-offsets",	no_argument,		NULL, 'u'},
213210389Sgabor	{"invert-match",	no_argument,		NULL, 'v'},
214210389Sgabor	{"version",		no_argument,		NULL, 'V'},
215210389Sgabor	{"word-regexp",		no_argument,		NULL, 'w'},
216210389Sgabor	{"line-regexp",		no_argument,		NULL, 'x'},
217226035Sgabor	{"xz",			no_argument,		NULL, 'X'},
218210389Sgabor	{"decompress",          no_argument,            NULL, 'Z'},
219210389Sgabor	{NULL,			no_argument,		NULL, 0}
220210389Sgabor};
221210389Sgabor
222210389Sgabor/*
223210389Sgabor * Adds a searching pattern to the internal array.
224210389Sgabor */
225210389Sgaborstatic void
226210389Sgaboradd_pattern(char *pat, size_t len)
227210389Sgabor{
228210389Sgabor
229226035Sgabor	/* Do not add further pattern is we already match everything */
230226035Sgabor	if (matchall)
231226035Sgabor	  return;
232226035Sgabor
233210389Sgabor	/* Check if we can do a shortcut */
234226035Sgabor	if (len == 0) {
235210389Sgabor		matchall = true;
236226035Sgabor		for (unsigned int i = 0; i < patterns; i++) {
237226035Sgabor			free(pattern[i].pat);
238226035Sgabor		}
239226035Sgabor		pattern = grep_realloc(pattern, sizeof(struct pat));
240226035Sgabor		pattern[0].pat = NULL;
241226035Sgabor		pattern[0].len = 0;
242226035Sgabor		patterns = 1;
243210389Sgabor		return;
244210389Sgabor	}
245210389Sgabor	/* Increase size if necessary */
246210389Sgabor	if (patterns == pattern_sz) {
247210389Sgabor		pattern_sz *= 2;
248210389Sgabor		pattern = grep_realloc(pattern, ++pattern_sz *
249226035Sgabor		    sizeof(struct pat));
250210389Sgabor	}
251210389Sgabor	if (len > 0 && pat[len - 1] == '\n')
252210389Sgabor		--len;
253210389Sgabor	/* pat may not be NUL-terminated */
254226035Sgabor	pattern[patterns].pat = grep_malloc(len + 1);
255226035Sgabor	memcpy(pattern[patterns].pat, pat, len);
256226035Sgabor	pattern[patterns].len = len;
257226035Sgabor	pattern[patterns].pat[len] = '\0';
258210389Sgabor	++patterns;
259210389Sgabor}
260210389Sgabor
261210389Sgabor/*
262210578Sgabor * Adds a file include/exclude pattern to the internal array.
263210389Sgabor */
264210389Sgaborstatic void
265210578Sgaboradd_fpattern(const char *pat, int mode)
266210389Sgabor{
267210389Sgabor
268210389Sgabor	/* Increase size if necessary */
269210578Sgabor	if (fpatterns == fpattern_sz) {
270210578Sgabor		fpattern_sz *= 2;
271210578Sgabor		fpattern = grep_realloc(fpattern, ++fpattern_sz *
272210389Sgabor		    sizeof(struct epat));
273210389Sgabor	}
274210578Sgabor	fpattern[fpatterns].pat = grep_strdup(pat);
275210578Sgabor	fpattern[fpatterns].mode = mode;
276210578Sgabor	++fpatterns;
277210389Sgabor}
278210389Sgabor
279210389Sgabor/*
280210578Sgabor * Adds a directory include/exclude pattern to the internal array.
281210578Sgabor */
282210578Sgaborstatic void
283210578Sgaboradd_dpattern(const char *pat, int mode)
284210578Sgabor{
285210578Sgabor
286210578Sgabor	/* Increase size if necessary */
287210578Sgabor	if (dpatterns == dpattern_sz) {
288210578Sgabor		dpattern_sz *= 2;
289210578Sgabor		dpattern = grep_realloc(dpattern, ++dpattern_sz *
290210578Sgabor		    sizeof(struct epat));
291210578Sgabor	}
292210578Sgabor	dpattern[dpatterns].pat = grep_strdup(pat);
293210578Sgabor	dpattern[dpatterns].mode = mode;
294210578Sgabor	++dpatterns;
295210578Sgabor}
296210578Sgabor
297210578Sgabor/*
298210389Sgabor * Reads searching patterns from a file and adds them with add_pattern().
299210389Sgabor */
300210389Sgaborstatic void
301210389Sgaborread_patterns(const char *fn)
302210389Sgabor{
303226035Sgabor	struct stat st;
304210389Sgabor	FILE *f;
305210389Sgabor	char *line;
306210389Sgabor	size_t len;
307210389Sgabor
308210389Sgabor	if ((f = fopen(fn, "r")) == NULL)
309210389Sgabor		err(2, "%s", fn);
310226035Sgabor	if ((fstat(fileno(f), &st) == -1) || (S_ISDIR(st.st_mode))) {
311226035Sgabor		fclose(f);
312226035Sgabor		return;
313226035Sgabor	}
314226035Sgabor        while ((line = fgetln(f, &len)) != NULL)
315226035Sgabor		add_pattern(line, line[0] == '\n' ? 0 : len);
316210389Sgabor	if (ferror(f))
317210389Sgabor		err(2, "%s", fn);
318210389Sgabor	fclose(f);
319210389Sgabor}
320210389Sgabor
321210461Sgaborstatic inline const char *
322210461Sgaborinit_color(const char *d)
323210461Sgabor{
324210461Sgabor	char *c;
325210461Sgabor
326210461Sgabor	c = getenv("GREP_COLOR");
327224937Sgabor	return (c != NULL && c[0] != '\0' ? c : d);
328210461Sgabor}
329210461Sgabor
330210389Sgaborint
331210389Sgabormain(int argc, char *argv[])
332210389Sgabor{
333210389Sgabor	char **aargv, **eargv, *eopts;
334226271Sgabor	char *ep;
335226271Sgabor	const char *pn;
336210389Sgabor	unsigned long long l;
337210389Sgabor	unsigned int aargc, eargc, i;
338210389Sgabor	int c, lastc, needpattern, newarg, prevoptind;
339210389Sgabor
340210389Sgabor	setlocale(LC_ALL, "");
341210389Sgabor
342210389Sgabor#ifndef WITHOUT_NLS
343210389Sgabor	catalog = catopen("grep", NL_CAT_LOCALE);
344210389Sgabor#endif
345210389Sgabor
346210389Sgabor	/* Check what is the program name of the binary.  In this
347210389Sgabor	   way we can have all the funcionalities in one binary
348210389Sgabor	   without the need of scripting and using ugly hacks. */
349226271Sgabor	pn = getprogname();
350226035Sgabor	if (pn[0] == 'b' && pn[1] == 'z') {
351226035Sgabor		filebehave = FILE_BZIP;
352226035Sgabor		pn += 2;
353226035Sgabor	} else if (pn[0] == 'x' && pn[1] == 'z') {
354226035Sgabor		filebehave = FILE_XZ;
355226035Sgabor		pn += 2;
356226035Sgabor	} else if (pn[0] == 'l' && pn[1] == 'z') {
357226035Sgabor		filebehave = FILE_LZMA;
358226035Sgabor		pn += 2;
359226035Sgabor	} else if (pn[0] == 'z') {
360226035Sgabor		filebehave = FILE_GZIP;
361226035Sgabor		pn += 1;
362226035Sgabor	}
363226035Sgabor	switch (pn[0]) {
364210389Sgabor	case 'e':
365210389Sgabor		grepbehave = GREP_EXTENDED;
366210389Sgabor		break;
367210389Sgabor	case 'f':
368210389Sgabor		grepbehave = GREP_FIXED;
369210389Sgabor		break;
370210389Sgabor	}
371210389Sgabor
372210389Sgabor	lastc = '\0';
373210389Sgabor	newarg = 1;
374210389Sgabor	prevoptind = 1;
375210389Sgabor	needpattern = 1;
376210389Sgabor
377210389Sgabor	eopts = getenv("GREP_OPTIONS");
378210389Sgabor
379211364Sgabor	/* support for extra arguments in GREP_OPTIONS */
380211364Sgabor	eargc = 0;
381224937Sgabor	if (eopts != NULL && eopts[0] != '\0') {
382210389Sgabor		char *str;
383210389Sgabor
384211364Sgabor		/* make an estimation of how many extra arguments we have */
385211364Sgabor		for (unsigned int j = 0; j < strlen(eopts); j++)
386211364Sgabor			if (eopts[j] == ' ')
387210389Sgabor				eargc++;
388210389Sgabor
389210389Sgabor		eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1));
390210389Sgabor
391210389Sgabor		eargc = 0;
392211364Sgabor		/* parse extra arguments */
393211364Sgabor		while ((str = strsep(&eopts, " ")) != NULL)
394224937Sgabor			if (str[0] != '\0')
395224937Sgabor				eargv[eargc++] = grep_strdup(str);
396210389Sgabor
397210430Sdelphij		aargv = (char **)grep_calloc(eargc + argc + 1,
398210430Sdelphij		    sizeof(char *));
399211364Sgabor
400210389Sgabor		aargv[0] = argv[0];
401211364Sgabor		for (i = 0; i < eargc; i++)
402211364Sgabor			aargv[i + 1] = eargv[i];
403211364Sgabor		for (int j = 1; j < argc; j++, i++)
404211364Sgabor			aargv[i + 1] = argv[j];
405210389Sgabor
406211364Sgabor		aargc = eargc + argc;
407210389Sgabor	} else {
408210389Sgabor		aargv = argv;
409210389Sgabor		aargc = argc;
410210389Sgabor	}
411210389Sgabor
412210389Sgabor	while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) !=
413210389Sgabor	    -1)) {
414210389Sgabor		switch (c) {
415210389Sgabor		case '0': case '1': case '2': case '3': case '4':
416210389Sgabor		case '5': case '6': case '7': case '8': case '9':
417210389Sgabor			if (newarg || !isdigit(lastc))
418210389Sgabor				Aflag = 0;
419210389Sgabor			else if (Aflag > LLONG_MAX / 10) {
420210389Sgabor				errno = ERANGE;
421210389Sgabor				err(2, NULL);
422210389Sgabor			}
423210389Sgabor			Aflag = Bflag = (Aflag * 10) + (c - '0');
424210389Sgabor			break;
425210389Sgabor		case 'C':
426210389Sgabor			if (optarg == NULL) {
427210389Sgabor				Aflag = Bflag = 2;
428210389Sgabor				break;
429210389Sgabor			}
430210389Sgabor			/* FALLTHROUGH */
431210389Sgabor		case 'A':
432210389Sgabor			/* FALLTHROUGH */
433210389Sgabor		case 'B':
434210389Sgabor			errno = 0;
435210389Sgabor			l = strtoull(optarg, &ep, 10);
436210389Sgabor			if (((errno == ERANGE) && (l == ULLONG_MAX)) ||
437210389Sgabor			    ((errno == EINVAL) && (l == 0)))
438210389Sgabor				err(2, NULL);
439210389Sgabor			else if (ep[0] != '\0') {
440210389Sgabor				errno = EINVAL;
441210389Sgabor				err(2, NULL);
442210389Sgabor			}
443210389Sgabor			if (c == 'A')
444210389Sgabor				Aflag = l;
445210389Sgabor			else if (c == 'B')
446210389Sgabor				Bflag = l;
447210389Sgabor			else
448210389Sgabor				Aflag = Bflag = l;
449210389Sgabor			break;
450210389Sgabor		case 'a':
451210389Sgabor			binbehave = BINFILE_TEXT;
452210389Sgabor			break;
453210389Sgabor		case 'b':
454210389Sgabor			bflag = true;
455210389Sgabor			break;
456210389Sgabor		case 'c':
457210389Sgabor			cflag = true;
458210389Sgabor			break;
459210389Sgabor		case 'D':
460210461Sgabor			if (strcasecmp(optarg, "skip") == 0)
461210389Sgabor				devbehave = DEV_SKIP;
462210461Sgabor			else if (strcasecmp(optarg, "read") == 0)
463210461Sgabor				devbehave = DEV_READ;
464210622Sgabor			else
465210622Sgabor				errx(2, getstr(3), "--devices");
466210389Sgabor			break;
467210389Sgabor		case 'd':
468210461Sgabor			if (strcasecmp("recurse", optarg) == 0) {
469210389Sgabor				Hflag = true;
470210389Sgabor				dirbehave = DIR_RECURSE;
471210461Sgabor			} else if (strcasecmp("skip", optarg) == 0)
472210389Sgabor				dirbehave = DIR_SKIP;
473210461Sgabor			else if (strcasecmp("read", optarg) == 0)
474210461Sgabor				dirbehave = DIR_READ;
475210622Sgabor			else
476210622Sgabor				errx(2, getstr(3), "--directories");
477210389Sgabor			break;
478210389Sgabor		case 'E':
479210389Sgabor			grepbehave = GREP_EXTENDED;
480210389Sgabor			break;
481210389Sgabor		case 'e':
482210389Sgabor			add_pattern(optarg, strlen(optarg));
483210389Sgabor			needpattern = 0;
484210389Sgabor			break;
485210389Sgabor		case 'F':
486210389Sgabor			grepbehave = GREP_FIXED;
487210389Sgabor			break;
488210389Sgabor		case 'f':
489210389Sgabor			read_patterns(optarg);
490210389Sgabor			needpattern = 0;
491210389Sgabor			break;
492210389Sgabor		case 'G':
493210389Sgabor			grepbehave = GREP_BASIC;
494210389Sgabor			break;
495210389Sgabor		case 'H':
496210389Sgabor			Hflag = true;
497210389Sgabor			break;
498210389Sgabor		case 'h':
499210389Sgabor			Hflag = false;
500210389Sgabor			hflag = true;
501210389Sgabor			break;
502210389Sgabor		case 'I':
503210389Sgabor			binbehave = BINFILE_SKIP;
504210389Sgabor			break;
505210389Sgabor		case 'i':
506210389Sgabor		case 'y':
507210389Sgabor			iflag =  true;
508210389Sgabor			cflags |= REG_ICASE;
509210389Sgabor			break;
510210389Sgabor		case 'J':
511226271Sgabor#ifdef WITHOUT_BZIP2
512226271Sgabor			errno = EOPNOTSUPP;
513226271Sgabor			err(2, "bzip2 support was disabled at compile-time");
514226271Sgabor#endif
515210389Sgabor			filebehave = FILE_BZIP;
516210389Sgabor			break;
517210389Sgabor		case 'L':
518210389Sgabor			lflag = false;
519210461Sgabor			Lflag = true;
520210389Sgabor			break;
521210389Sgabor		case 'l':
522210389Sgabor			Lflag = false;
523210461Sgabor			lflag = true;
524210389Sgabor			break;
525210389Sgabor		case 'm':
526210389Sgabor			mflag = true;
527210389Sgabor			errno = 0;
528244493Seadler			mlimit = mcount = strtoll(optarg, &ep, 10);
529226035Sgabor			if (((errno == ERANGE) && (mcount == LLONG_MAX)) ||
530210389Sgabor			    ((errno == EINVAL) && (mcount == 0)))
531210389Sgabor				err(2, NULL);
532210389Sgabor			else if (ep[0] != '\0') {
533210389Sgabor				errno = EINVAL;
534210389Sgabor				err(2, NULL);
535210389Sgabor			}
536210389Sgabor			break;
537226035Sgabor		case 'M':
538226035Sgabor			filebehave = FILE_LZMA;
539226035Sgabor			break;
540210389Sgabor		case 'n':
541210389Sgabor			nflag = true;
542210389Sgabor			break;
543210389Sgabor		case 'O':
544210389Sgabor			linkbehave = LINK_EXPLICIT;
545210389Sgabor			break;
546210389Sgabor		case 'o':
547210389Sgabor			oflag = true;
548223009Sgabor			cflags &= ~REG_NOSUB;
549210389Sgabor			break;
550210389Sgabor		case 'p':
551210389Sgabor			linkbehave = LINK_SKIP;
552210389Sgabor			break;
553210389Sgabor		case 'q':
554210389Sgabor			qflag = true;
555210389Sgabor			break;
556210389Sgabor		case 'S':
557210461Sgabor			linkbehave = LINK_READ;
558210389Sgabor			break;
559210389Sgabor		case 'R':
560210389Sgabor		case 'r':
561210389Sgabor			dirbehave = DIR_RECURSE;
562210389Sgabor			Hflag = true;
563210389Sgabor			break;
564210389Sgabor		case 's':
565210389Sgabor			sflag = true;
566210389Sgabor			break;
567210389Sgabor		case 'U':
568210389Sgabor			binbehave = BINFILE_BIN;
569210389Sgabor			break;
570210389Sgabor		case 'u':
571210389Sgabor		case MMAP_OPT:
572226035Sgabor			filebehave = FILE_MMAP;
573210389Sgabor			break;
574210389Sgabor		case 'V':
575226271Sgabor			printf(getstr(9), getprogname(), VERSION);
576210389Sgabor			exit(0);
577210389Sgabor		case 'v':
578210389Sgabor			vflag = true;
579210389Sgabor			break;
580210389Sgabor		case 'w':
581210389Sgabor			wflag = true;
582223009Sgabor			cflags &= ~REG_NOSUB;
583210389Sgabor			break;
584210389Sgabor		case 'x':
585210389Sgabor			xflag = true;
586223009Sgabor			cflags &= ~REG_NOSUB;
587210389Sgabor			break;
588226035Sgabor		case 'X':
589226035Sgabor			filebehave = FILE_XZ;
590226035Sgabor			break;
591210389Sgabor		case 'Z':
592210389Sgabor			filebehave = FILE_GZIP;
593210389Sgabor			break;
594210389Sgabor		case BIN_OPT:
595210461Sgabor			if (strcasecmp("binary", optarg) == 0)
596210389Sgabor				binbehave = BINFILE_BIN;
597210461Sgabor			else if (strcasecmp("without-match", optarg) == 0)
598210389Sgabor				binbehave = BINFILE_SKIP;
599210461Sgabor			else if (strcasecmp("text", optarg) == 0)
600210389Sgabor				binbehave = BINFILE_TEXT;
601210389Sgabor			else
602210622Sgabor				errx(2, getstr(3), "--binary-files");
603210389Sgabor			break;
604210389Sgabor		case COLOR_OPT:
605210461Sgabor			color = NULL;
606210461Sgabor			if (optarg == NULL || strcasecmp("auto", optarg) == 0 ||
607210461Sgabor			    strcasecmp("tty", optarg) == 0 ||
608210461Sgabor			    strcasecmp("if-tty", optarg) == 0) {
609210461Sgabor				char *term;
610210461Sgabor
611210461Sgabor				term = getenv("TERM");
612210461Sgabor				if (isatty(STDOUT_FILENO) && term != NULL &&
613210461Sgabor				    strcasecmp(term, "dumb") != 0)
614210461Sgabor					color = init_color("01;31");
615210461Sgabor			} else if (strcasecmp("always", optarg) == 0 ||
616210461Sgabor			    strcasecmp("yes", optarg) == 0 ||
617210461Sgabor			    strcasecmp("force", optarg) == 0) {
618210461Sgabor				color = init_color("01;31");
619210461Sgabor			} else if (strcasecmp("never", optarg) != 0 &&
620210461Sgabor			    strcasecmp("none", optarg) != 0 &&
621210461Sgabor			    strcasecmp("no", optarg) != 0)
622210622Sgabor				errx(2, getstr(3), "--color");
623223009Sgabor			cflags &= ~REG_NOSUB;
624210389Sgabor			break;
625210389Sgabor		case LABEL_OPT:
626210389Sgabor			label = optarg;
627210389Sgabor			break;
628210389Sgabor		case LINEBUF_OPT:
629210389Sgabor			lbflag = true;
630210389Sgabor			break;
631210389Sgabor		case NULL_OPT:
632210389Sgabor			nullflag = true;
633210389Sgabor			break;
634210389Sgabor		case R_INCLUDE_OPT:
635210578Sgabor			finclude = true;
636210578Sgabor			add_fpattern(optarg, INCL_PAT);
637210389Sgabor			break;
638210389Sgabor		case R_EXCLUDE_OPT:
639210578Sgabor			fexclude = true;
640210578Sgabor			add_fpattern(optarg, EXCL_PAT);
641210389Sgabor			break;
642210389Sgabor		case R_DINCLUDE_OPT:
643211364Sgabor			dinclude = true;
644210578Sgabor			add_dpattern(optarg, INCL_PAT);
645210389Sgabor			break;
646210389Sgabor		case R_DEXCLUDE_OPT:
647211364Sgabor			dexclude = true;
648210578Sgabor			add_dpattern(optarg, EXCL_PAT);
649210389Sgabor			break;
650210389Sgabor		case HELP_OPT:
651210389Sgabor		default:
652210389Sgabor			usage();
653210389Sgabor		}
654210389Sgabor		lastc = c;
655210389Sgabor		newarg = optind != prevoptind;
656210389Sgabor		prevoptind = optind;
657210389Sgabor	}
658210389Sgabor	aargc -= optind;
659210389Sgabor	aargv += optind;
660210389Sgabor
661226035Sgabor	/* Empty pattern file matches nothing */
662226035Sgabor	if (!needpattern && (patterns == 0))
663226035Sgabor		exit(1);
664226035Sgabor
665210389Sgabor	/* Fail if we don't have any pattern */
666210389Sgabor	if (aargc == 0 && needpattern)
667210389Sgabor		usage();
668210389Sgabor
669210389Sgabor	/* Process patterns from command line */
670210389Sgabor	if (aargc != 0 && needpattern) {
671210389Sgabor		add_pattern(*aargv, strlen(*aargv));
672210389Sgabor		--aargc;
673210389Sgabor		++aargv;
674210389Sgabor	}
675210389Sgabor
676210389Sgabor	switch (grepbehave) {
677210389Sgabor	case GREP_BASIC:
678210389Sgabor		break;
679226035Sgabor	case GREP_FIXED:
680226035Sgabor		/* XXX: header mess, REG_LITERAL not defined in gnu/regex.h */
681226035Sgabor		cflags |= 0020;
682226035Sgabor		break;
683210389Sgabor	case GREP_EXTENDED:
684210389Sgabor		cflags |= REG_EXTENDED;
685210389Sgabor		break;
686210389Sgabor	default:
687210389Sgabor		/* NOTREACHED */
688210389Sgabor		usage();
689210389Sgabor	}
690210389Sgabor
691210389Sgabor	fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern));
692210389Sgabor	r_pattern = grep_calloc(patterns, sizeof(*r_pattern));
693226035Sgabor
694226035Sgabor	/* Check if cheating is allowed (always is for fgrep). */
695226035Sgabor	for (i = 0; i < patterns; ++i) {
696226035Sgabor		if (fastncomp(&fg_pattern[i], pattern[i].pat,
697226035Sgabor		    pattern[i].len, cflags) != 0) {
698226035Sgabor			/* Fall back to full regex library */
699226035Sgabor			c = regcomp(&r_pattern[i], pattern[i].pat, cflags);
700226035Sgabor			if (c != 0) {
701226035Sgabor				regerror(c, &r_pattern[i], re_error,
702226035Sgabor				    RE_ERROR_BUF);
703226035Sgabor				errx(2, "%s", re_error);
704210389Sgabor			}
705210389Sgabor		}
706210389Sgabor	}
707210389Sgabor
708210389Sgabor	if (lbflag)
709210389Sgabor		setlinebuf(stdout);
710210389Sgabor
711210389Sgabor	if ((aargc == 0 || aargc == 1) && !Hflag)
712210389Sgabor		hflag = true;
713210389Sgabor
714210389Sgabor	if (aargc == 0)
715210389Sgabor		exit(!procfile("-"));
716210389Sgabor
717210389Sgabor	if (dirbehave == DIR_RECURSE)
718210389Sgabor		c = grep_tree(aargv);
719211519Sdelphij	else
720210578Sgabor		for (c = 0; aargc--; ++aargv) {
721210578Sgabor			if ((finclude || fexclude) && !file_matching(*aargv))
722210578Sgabor				continue;
723210389Sgabor			c+= procfile(*aargv);
724210578Sgabor		}
725210389Sgabor
726210389Sgabor#ifndef WITHOUT_NLS
727210389Sgabor	catclose(catalog);
728210389Sgabor#endif
729210389Sgabor
730210389Sgabor	/* Find out the correct return value according to the
731210389Sgabor	   results and the command line option. */
732228319Sgabor	exit(c ? (file_err ? (qflag ? 0 : 2) : 0) : (file_err ? 2 : 1));
733210389Sgabor}
734