grep.c revision 245996
1220422Sgabor/*	$NetBSD: grep.c,v 1.4 2011/02/16 01:31:33 joerg Exp $	*/
2220422Sgabor/* 	$FreeBSD: stable/9/usr.bin/grep/grep.c 245996 2013-01-27 19:44:41Z 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: stable/9/usr.bin/grep/grep.c 245996 2013-01-27 19:44:41Z 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>
41226261Sgabor#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
52226261Sgabor#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 */
85210389Sgaborunsigned int	 patterns, pattern_sz;
86226261Sgaborstruct pat	*pattern;
87210389Sgaborregex_t		*r_pattern;
88226261Sgaborfastmatch_t	*fg_pattern;
89210389Sgabor
90210389Sgabor/* Filename exclusion/inclusion patterns */
91210578Sgaborunsigned int	 fpatterns, fpattern_sz;
92210578Sgaborunsigned int	 dpatterns, dpattern_sz;
93210578Sgaborstruct epat	*dpattern, *fpattern;
94210389Sgabor
95210389Sgabor/* For regex errors  */
96210389Sgaborchar	 re_error[RE_ERROR_BUF + 1];
97210389Sgabor
98210389Sgabor/* Command-line flags */
99210389Sgaborunsigned long long Aflag;	/* -A x: print x lines trailing each match */
100210389Sgaborunsigned long long Bflag;	/* -B x: print x lines leading each match */
101210389Sgaborbool	 Hflag;		/* -H: always print file name */
102210389Sgaborbool	 Lflag;		/* -L: only show names of files with no matches */
103210389Sgaborbool	 bflag;		/* -b: show block numbers for each match */
104210389Sgaborbool	 cflag;		/* -c: only show a count of matching lines */
105210389Sgaborbool	 hflag;		/* -h: don't print filename headers */
106210389Sgaborbool	 iflag;		/* -i: ignore case */
107210389Sgaborbool	 lflag;		/* -l: only show names of files with matches */
108210389Sgaborbool	 mflag;		/* -m x: stop reading the files after x matches */
109226261Sgaborlong long mcount;	/* count for -m */
110210389Sgaborbool	 nflag;		/* -n: show line numbers in front of matching lines */
111210389Sgaborbool	 oflag;		/* -o: print only matching part */
112210389Sgaborbool	 qflag;		/* -q: quiet mode (don't output anything) */
113210389Sgaborbool	 sflag;		/* -s: silent mode (ignore errors) */
114210389Sgaborbool	 vflag;		/* -v: only show non-matching lines */
115210389Sgaborbool	 wflag;		/* -w: pattern must start and end on word boundaries */
116210389Sgaborbool	 xflag;		/* -x: pattern must match entire line */
117210389Sgaborbool	 lbflag;	/* --line-buffered */
118210389Sgaborbool	 nullflag;	/* --null */
119210389Sgaborchar	*label;		/* --label */
120210461Sgaborconst char *color;	/* --color */
121210389Sgaborint	 grepbehave = GREP_BASIC;	/* -EFGP: type of the regex */
122210389Sgaborint	 binbehave = BINFILE_BIN;	/* -aIU: handling of binary files */
123210389Sgaborint	 filebehave = FILE_STDIO;	/* -JZ: normal, gzip or bzip2 file */
124210461Sgaborint	 devbehave = DEV_READ;		/* -D: handling of devices */
125210461Sgaborint	 dirbehave = DIR_READ;		/* -dRr: handling of directories */
126210461Sgaborint	 linkbehave = LINK_READ;	/* -OpS: handling of symlinks */
127210389Sgabor
128211364Sgaborbool	 dexclude, dinclude;	/* --exclude-dir and --include-dir */
129211364Sgaborbool	 fexclude, finclude;	/* --exclude and --include */
130210578Sgabor
131210389Sgaborenum {
132210389Sgabor	BIN_OPT = CHAR_MAX + 1,
133210389Sgabor	COLOR_OPT,
134210389Sgabor	HELP_OPT,
135210389Sgabor	MMAP_OPT,
136210389Sgabor	LINEBUF_OPT,
137210389Sgabor	LABEL_OPT,
138210389Sgabor	NULL_OPT,
139210389Sgabor	R_EXCLUDE_OPT,
140210389Sgabor	R_INCLUDE_OPT,
141210389Sgabor	R_DEXCLUDE_OPT,
142210389Sgabor	R_DINCLUDE_OPT
143210389Sgabor};
144210389Sgabor
145210461Sgaborstatic inline const char	*init_color(const char *);
146210461Sgabor
147210389Sgabor/* Housekeeping */
148210389Sgaborbool	 first = true;	/* flag whether we are processing the first match */
149210389Sgaborbool	 prev;		/* flag whether or not the previous line matched */
150210389Sgaborint	 tail;		/* lines left to print */
151229081Sgaborbool	 file_err;	/* file reading error */
152210389Sgabor
153210389Sgabor/*
154210389Sgabor * Prints usage information and returns 2.
155210389Sgabor */
156210389Sgaborstatic void
157210389Sgaborusage(void)
158210389Sgabor{
159226573Sgabor	fprintf(stderr, getstr(4), getprogname());
160210389Sgabor	fprintf(stderr, "%s", getstr(5));
161210389Sgabor	fprintf(stderr, "%s", getstr(6));
162210389Sgabor	fprintf(stderr, "%s", getstr(7));
163210389Sgabor	exit(2);
164210389Sgabor}
165210389Sgabor
166226261Sgaborstatic const char	*optstr = "0123456789A:B:C:D:EFGHIJMLOPSRUVZabcd:e:f:hilm:nopqrsuvwxXy";
167210389Sgabor
168210389Sgaborstruct option long_options[] =
169210389Sgabor{
170210389Sgabor	{"binary-files",	required_argument,	NULL, BIN_OPT},
171210389Sgabor	{"help",		no_argument,		NULL, HELP_OPT},
172210389Sgabor	{"mmap",		no_argument,		NULL, MMAP_OPT},
173210389Sgabor	{"line-buffered",	no_argument,		NULL, LINEBUF_OPT},
174210389Sgabor	{"label",		required_argument,	NULL, LABEL_OPT},
175210389Sgabor	{"null",		no_argument,		NULL, NULL_OPT},
176210389Sgabor	{"color",		optional_argument,	NULL, COLOR_OPT},
177210389Sgabor	{"colour",		optional_argument,	NULL, COLOR_OPT},
178210389Sgabor	{"exclude",		required_argument,	NULL, R_EXCLUDE_OPT},
179210389Sgabor	{"include",		required_argument,	NULL, R_INCLUDE_OPT},
180210389Sgabor	{"exclude-dir",		required_argument,	NULL, R_DEXCLUDE_OPT},
181210389Sgabor	{"include-dir",		required_argument,	NULL, R_DINCLUDE_OPT},
182210389Sgabor	{"after-context",	required_argument,	NULL, 'A'},
183210389Sgabor	{"text",		no_argument,		NULL, 'a'},
184210389Sgabor	{"before-context",	required_argument,	NULL, 'B'},
185210389Sgabor	{"byte-offset",		no_argument,		NULL, 'b'},
186210389Sgabor	{"context",		optional_argument,	NULL, 'C'},
187210389Sgabor	{"count",		no_argument,		NULL, 'c'},
188210389Sgabor	{"devices",		required_argument,	NULL, 'D'},
189210389Sgabor        {"directories",		required_argument,	NULL, 'd'},
190210389Sgabor	{"extended-regexp",	no_argument,		NULL, 'E'},
191210389Sgabor	{"regexp",		required_argument,	NULL, 'e'},
192210389Sgabor	{"fixed-strings",	no_argument,		NULL, 'F'},
193210389Sgabor	{"file",		required_argument,	NULL, 'f'},
194210389Sgabor	{"basic-regexp",	no_argument,		NULL, 'G'},
195210389Sgabor	{"no-filename",		no_argument,		NULL, 'h'},
196210389Sgabor	{"with-filename",	no_argument,		NULL, 'H'},
197210389Sgabor	{"ignore-case",		no_argument,		NULL, 'i'},
198210389Sgabor	{"bz2decompress",	no_argument,		NULL, 'J'},
199210389Sgabor	{"files-with-matches",	no_argument,		NULL, 'l'},
200210389Sgabor	{"files-without-match", no_argument,            NULL, 'L'},
201210389Sgabor	{"max-count",		required_argument,	NULL, 'm'},
202226261Sgabor	{"lzma",		no_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'},
215226261Sgabor	{"xz",			no_argument,		NULL, 'X'},
216210389Sgabor	{"decompress",          no_argument,            NULL, 'Z'},
217210389Sgabor	{NULL,			no_argument,		NULL, 0}
218210389Sgabor};
219210389Sgabor
220210389Sgabor/*
221210389Sgabor * Adds a searching pattern to the internal array.
222210389Sgabor */
223210389Sgaborstatic void
224210389Sgaboradd_pattern(char *pat, size_t len)
225210389Sgabor{
226210389Sgabor
227226261Sgabor	/* Do not add further pattern is we already match everything */
228226261Sgabor	if (matchall)
229226261Sgabor	  return;
230226261Sgabor
231210389Sgabor	/* Check if we can do a shortcut */
232226261Sgabor	if (len == 0) {
233210389Sgabor		matchall = true;
234226261Sgabor		for (unsigned int i = 0; i < patterns; i++) {
235226261Sgabor			free(pattern[i].pat);
236226261Sgabor		}
237226261Sgabor		pattern = grep_realloc(pattern, sizeof(struct pat));
238226261Sgabor		pattern[0].pat = NULL;
239226261Sgabor		pattern[0].len = 0;
240226261Sgabor		patterns = 1;
241210389Sgabor		return;
242210389Sgabor	}
243210389Sgabor	/* Increase size if necessary */
244210389Sgabor	if (patterns == pattern_sz) {
245210389Sgabor		pattern_sz *= 2;
246210389Sgabor		pattern = grep_realloc(pattern, ++pattern_sz *
247226261Sgabor		    sizeof(struct pat));
248210389Sgabor	}
249210389Sgabor	if (len > 0 && pat[len - 1] == '\n')
250210389Sgabor		--len;
251210389Sgabor	/* pat may not be NUL-terminated */
252226261Sgabor	pattern[patterns].pat = grep_malloc(len + 1);
253226261Sgabor	memcpy(pattern[patterns].pat, pat, len);
254226261Sgabor	pattern[patterns].len = len;
255226261Sgabor	pattern[patterns].pat[len] = '\0';
256210389Sgabor	++patterns;
257210389Sgabor}
258210389Sgabor
259210389Sgabor/*
260210578Sgabor * Adds a file include/exclude pattern to the internal array.
261210389Sgabor */
262210389Sgaborstatic void
263210578Sgaboradd_fpattern(const char *pat, int mode)
264210389Sgabor{
265210389Sgabor
266210389Sgabor	/* Increase size if necessary */
267210578Sgabor	if (fpatterns == fpattern_sz) {
268210578Sgabor		fpattern_sz *= 2;
269210578Sgabor		fpattern = grep_realloc(fpattern, ++fpattern_sz *
270210389Sgabor		    sizeof(struct epat));
271210389Sgabor	}
272210578Sgabor	fpattern[fpatterns].pat = grep_strdup(pat);
273210578Sgabor	fpattern[fpatterns].mode = mode;
274210578Sgabor	++fpatterns;
275210389Sgabor}
276210389Sgabor
277210389Sgabor/*
278210578Sgabor * Adds a directory include/exclude pattern to the internal array.
279210578Sgabor */
280210578Sgaborstatic void
281210578Sgaboradd_dpattern(const char *pat, int mode)
282210578Sgabor{
283210578Sgabor
284210578Sgabor	/* Increase size if necessary */
285210578Sgabor	if (dpatterns == dpattern_sz) {
286210578Sgabor		dpattern_sz *= 2;
287210578Sgabor		dpattern = grep_realloc(dpattern, ++dpattern_sz *
288210578Sgabor		    sizeof(struct epat));
289210578Sgabor	}
290210578Sgabor	dpattern[dpatterns].pat = grep_strdup(pat);
291210578Sgabor	dpattern[dpatterns].mode = mode;
292210578Sgabor	++dpatterns;
293210578Sgabor}
294210578Sgabor
295210578Sgabor/*
296210389Sgabor * Reads searching patterns from a file and adds them with add_pattern().
297210389Sgabor */
298210389Sgaborstatic void
299210389Sgaborread_patterns(const char *fn)
300210389Sgabor{
301226261Sgabor	struct stat st;
302210389Sgabor	FILE *f;
303210389Sgabor	char *line;
304210389Sgabor	size_t len;
305210389Sgabor
306210389Sgabor	if ((f = fopen(fn, "r")) == NULL)
307210389Sgabor		err(2, "%s", fn);
308226261Sgabor	if ((fstat(fileno(f), &st) == -1) || (S_ISDIR(st.st_mode))) {
309226261Sgabor		fclose(f);
310226261Sgabor		return;
311226261Sgabor	}
312226261Sgabor        while ((line = fgetln(f, &len)) != NULL)
313226261Sgabor		add_pattern(line, line[0] == '\n' ? 0 : len);
314210389Sgabor	if (ferror(f))
315210389Sgabor		err(2, "%s", fn);
316210389Sgabor	fclose(f);
317210389Sgabor}
318210389Sgabor
319210461Sgaborstatic inline const char *
320210461Sgaborinit_color(const char *d)
321210461Sgabor{
322210461Sgabor	char *c;
323210461Sgabor
324210461Sgabor	c = getenv("GREP_COLOR");
325224937Sgabor	return (c != NULL && c[0] != '\0' ? c : d);
326210461Sgabor}
327210461Sgabor
328210389Sgaborint
329210389Sgabormain(int argc, char *argv[])
330210389Sgabor{
331210389Sgabor	char **aargv, **eargv, *eopts;
332226573Sgabor	char *ep;
333226573Sgabor	const char *pn;
334210389Sgabor	unsigned long long l;
335210389Sgabor	unsigned int aargc, eargc, i;
336210389Sgabor	int c, lastc, needpattern, newarg, prevoptind;
337210389Sgabor
338210389Sgabor	setlocale(LC_ALL, "");
339210389Sgabor
340210389Sgabor#ifndef WITHOUT_NLS
341210389Sgabor	catalog = catopen("grep", NL_CAT_LOCALE);
342210389Sgabor#endif
343210389Sgabor
344210389Sgabor	/* Check what is the program name of the binary.  In this
345210389Sgabor	   way we can have all the funcionalities in one binary
346210389Sgabor	   without the need of scripting and using ugly hacks. */
347226573Sgabor	pn = getprogname();
348226261Sgabor	if (pn[0] == 'b' && pn[1] == 'z') {
349226261Sgabor		filebehave = FILE_BZIP;
350226261Sgabor		pn += 2;
351226261Sgabor	} else if (pn[0] == 'x' && pn[1] == 'z') {
352226261Sgabor		filebehave = FILE_XZ;
353226261Sgabor		pn += 2;
354226261Sgabor	} else if (pn[0] == 'l' && pn[1] == 'z') {
355226261Sgabor		filebehave = FILE_LZMA;
356226261Sgabor		pn += 2;
357226261Sgabor	} else if (pn[0] == 'z') {
358226261Sgabor		filebehave = FILE_GZIP;
359226261Sgabor		pn += 1;
360226261Sgabor	}
361226261Sgabor	switch (pn[0]) {
362210389Sgabor	case 'e':
363210389Sgabor		grepbehave = GREP_EXTENDED;
364210389Sgabor		break;
365210389Sgabor	case 'f':
366210389Sgabor		grepbehave = GREP_FIXED;
367210389Sgabor		break;
368210389Sgabor	}
369210389Sgabor
370210389Sgabor	lastc = '\0';
371210389Sgabor	newarg = 1;
372210389Sgabor	prevoptind = 1;
373210389Sgabor	needpattern = 1;
374210389Sgabor
375210389Sgabor	eopts = getenv("GREP_OPTIONS");
376210389Sgabor
377211364Sgabor	/* support for extra arguments in GREP_OPTIONS */
378211364Sgabor	eargc = 0;
379224937Sgabor	if (eopts != NULL && eopts[0] != '\0') {
380210389Sgabor		char *str;
381210389Sgabor
382211364Sgabor		/* make an estimation of how many extra arguments we have */
383211364Sgabor		for (unsigned int j = 0; j < strlen(eopts); j++)
384211364Sgabor			if (eopts[j] == ' ')
385210389Sgabor				eargc++;
386210389Sgabor
387210389Sgabor		eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1));
388210389Sgabor
389210389Sgabor		eargc = 0;
390211364Sgabor		/* parse extra arguments */
391211364Sgabor		while ((str = strsep(&eopts, " ")) != NULL)
392224937Sgabor			if (str[0] != '\0')
393224937Sgabor				eargv[eargc++] = grep_strdup(str);
394210389Sgabor
395210430Sdelphij		aargv = (char **)grep_calloc(eargc + argc + 1,
396210430Sdelphij		    sizeof(char *));
397211364Sgabor
398210389Sgabor		aargv[0] = argv[0];
399211364Sgabor		for (i = 0; i < eargc; i++)
400211364Sgabor			aargv[i + 1] = eargv[i];
401211364Sgabor		for (int j = 1; j < argc; j++, i++)
402211364Sgabor			aargv[i + 1] = argv[j];
403210389Sgabor
404211364Sgabor		aargc = eargc + argc;
405210389Sgabor	} else {
406210389Sgabor		aargv = argv;
407210389Sgabor		aargc = argc;
408210389Sgabor	}
409210389Sgabor
410210389Sgabor	while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) !=
411210389Sgabor	    -1)) {
412210389Sgabor		switch (c) {
413210389Sgabor		case '0': case '1': case '2': case '3': case '4':
414210389Sgabor		case '5': case '6': case '7': case '8': case '9':
415210389Sgabor			if (newarg || !isdigit(lastc))
416210389Sgabor				Aflag = 0;
417210389Sgabor			else if (Aflag > LLONG_MAX / 10) {
418210389Sgabor				errno = ERANGE;
419210389Sgabor				err(2, NULL);
420210389Sgabor			}
421210389Sgabor			Aflag = Bflag = (Aflag * 10) + (c - '0');
422210389Sgabor			break;
423210389Sgabor		case 'C':
424210389Sgabor			if (optarg == NULL) {
425210389Sgabor				Aflag = Bflag = 2;
426210389Sgabor				break;
427210389Sgabor			}
428210389Sgabor			/* FALLTHROUGH */
429210389Sgabor		case 'A':
430210389Sgabor			/* FALLTHROUGH */
431210389Sgabor		case 'B':
432210389Sgabor			errno = 0;
433210389Sgabor			l = strtoull(optarg, &ep, 10);
434210389Sgabor			if (((errno == ERANGE) && (l == ULLONG_MAX)) ||
435210389Sgabor			    ((errno == EINVAL) && (l == 0)))
436210389Sgabor				err(2, NULL);
437210389Sgabor			else if (ep[0] != '\0') {
438210389Sgabor				errno = EINVAL;
439210389Sgabor				err(2, NULL);
440210389Sgabor			}
441210389Sgabor			if (c == 'A')
442210389Sgabor				Aflag = l;
443210389Sgabor			else if (c == 'B')
444210389Sgabor				Bflag = l;
445210389Sgabor			else
446210389Sgabor				Aflag = Bflag = l;
447210389Sgabor			break;
448210389Sgabor		case 'a':
449210389Sgabor			binbehave = BINFILE_TEXT;
450210389Sgabor			break;
451210389Sgabor		case 'b':
452210389Sgabor			bflag = true;
453210389Sgabor			break;
454210389Sgabor		case 'c':
455210389Sgabor			cflag = true;
456210389Sgabor			break;
457210389Sgabor		case 'D':
458210461Sgabor			if (strcasecmp(optarg, "skip") == 0)
459210389Sgabor				devbehave = DEV_SKIP;
460210461Sgabor			else if (strcasecmp(optarg, "read") == 0)
461210461Sgabor				devbehave = DEV_READ;
462210622Sgabor			else
463210622Sgabor				errx(2, getstr(3), "--devices");
464210389Sgabor			break;
465210389Sgabor		case 'd':
466210461Sgabor			if (strcasecmp("recurse", optarg) == 0) {
467210389Sgabor				Hflag = true;
468210389Sgabor				dirbehave = DIR_RECURSE;
469210461Sgabor			} else if (strcasecmp("skip", optarg) == 0)
470210389Sgabor				dirbehave = DIR_SKIP;
471210461Sgabor			else if (strcasecmp("read", optarg) == 0)
472210461Sgabor				dirbehave = DIR_READ;
473210622Sgabor			else
474210622Sgabor				errx(2, getstr(3), "--directories");
475210389Sgabor			break;
476210389Sgabor		case 'E':
477210389Sgabor			grepbehave = GREP_EXTENDED;
478210389Sgabor			break;
479210389Sgabor		case 'e':
480245996Sgabor			{
481245996Sgabor				char *token;
482245996Sgabor				char *string = optarg;
483245996Sgabor
484245996Sgabor				while ((token = strsep(&string, "\n")) != NULL)
485245996Sgabor					add_pattern(token, strlen(token));
486245996Sgabor			}
487210389Sgabor			needpattern = 0;
488210389Sgabor			break;
489210389Sgabor		case 'F':
490210389Sgabor			grepbehave = GREP_FIXED;
491210389Sgabor			break;
492210389Sgabor		case 'f':
493210389Sgabor			read_patterns(optarg);
494210389Sgabor			needpattern = 0;
495210389Sgabor			break;
496210389Sgabor		case 'G':
497210389Sgabor			grepbehave = GREP_BASIC;
498210389Sgabor			break;
499210389Sgabor		case 'H':
500210389Sgabor			Hflag = true;
501210389Sgabor			break;
502210389Sgabor		case 'h':
503210389Sgabor			Hflag = false;
504210389Sgabor			hflag = true;
505210389Sgabor			break;
506210389Sgabor		case 'I':
507210389Sgabor			binbehave = BINFILE_SKIP;
508210389Sgabor			break;
509210389Sgabor		case 'i':
510210389Sgabor		case 'y':
511210389Sgabor			iflag =  true;
512210389Sgabor			cflags |= REG_ICASE;
513210389Sgabor			break;
514210389Sgabor		case 'J':
515226573Sgabor#ifdef WITHOUT_BZIP2
516226573Sgabor			errno = EOPNOTSUPP;
517226573Sgabor			err(2, "bzip2 support was disabled at compile-time");
518226573Sgabor#endif
519210389Sgabor			filebehave = FILE_BZIP;
520210389Sgabor			break;
521210389Sgabor		case 'L':
522210389Sgabor			lflag = false;
523210461Sgabor			Lflag = true;
524210389Sgabor			break;
525210389Sgabor		case 'l':
526210389Sgabor			Lflag = false;
527210461Sgabor			lflag = true;
528210389Sgabor			break;
529210389Sgabor		case 'm':
530210389Sgabor			mflag = true;
531210389Sgabor			errno = 0;
532226261Sgabor			mcount = strtoll(optarg, &ep, 10);
533226261Sgabor			if (((errno == ERANGE) && (mcount == LLONG_MAX)) ||
534210389Sgabor			    ((errno == EINVAL) && (mcount == 0)))
535210389Sgabor				err(2, NULL);
536210389Sgabor			else if (ep[0] != '\0') {
537210389Sgabor				errno = EINVAL;
538210389Sgabor				err(2, NULL);
539210389Sgabor			}
540210389Sgabor			break;
541226261Sgabor		case 'M':
542226261Sgabor			filebehave = FILE_LZMA;
543226261Sgabor			break;
544210389Sgabor		case 'n':
545210389Sgabor			nflag = true;
546210389Sgabor			break;
547210389Sgabor		case 'O':
548210389Sgabor			linkbehave = LINK_EXPLICIT;
549210389Sgabor			break;
550210389Sgabor		case 'o':
551210389Sgabor			oflag = true;
552223009Sgabor			cflags &= ~REG_NOSUB;
553210389Sgabor			break;
554210389Sgabor		case 'p':
555210389Sgabor			linkbehave = LINK_SKIP;
556210389Sgabor			break;
557210389Sgabor		case 'q':
558210389Sgabor			qflag = true;
559210389Sgabor			break;
560210389Sgabor		case 'S':
561210461Sgabor			linkbehave = LINK_READ;
562210389Sgabor			break;
563210389Sgabor		case 'R':
564210389Sgabor		case 'r':
565210389Sgabor			dirbehave = DIR_RECURSE;
566210389Sgabor			Hflag = true;
567210389Sgabor			break;
568210389Sgabor		case 's':
569210389Sgabor			sflag = true;
570210389Sgabor			break;
571210389Sgabor		case 'U':
572210389Sgabor			binbehave = BINFILE_BIN;
573210389Sgabor			break;
574210389Sgabor		case 'u':
575210389Sgabor		case MMAP_OPT:
576226261Sgabor			filebehave = FILE_MMAP;
577210389Sgabor			break;
578210389Sgabor		case 'V':
579226573Sgabor			printf(getstr(9), getprogname(), VERSION);
580210389Sgabor			exit(0);
581210389Sgabor		case 'v':
582210389Sgabor			vflag = true;
583210389Sgabor			break;
584210389Sgabor		case 'w':
585210389Sgabor			wflag = true;
586223009Sgabor			cflags &= ~REG_NOSUB;
587210389Sgabor			break;
588210389Sgabor		case 'x':
589210389Sgabor			xflag = true;
590223009Sgabor			cflags &= ~REG_NOSUB;
591210389Sgabor			break;
592226261Sgabor		case 'X':
593226261Sgabor			filebehave = FILE_XZ;
594226261Sgabor			break;
595210389Sgabor		case 'Z':
596210389Sgabor			filebehave = FILE_GZIP;
597210389Sgabor			break;
598210389Sgabor		case BIN_OPT:
599210461Sgabor			if (strcasecmp("binary", optarg) == 0)
600210389Sgabor				binbehave = BINFILE_BIN;
601210461Sgabor			else if (strcasecmp("without-match", optarg) == 0)
602210389Sgabor				binbehave = BINFILE_SKIP;
603210461Sgabor			else if (strcasecmp("text", optarg) == 0)
604210389Sgabor				binbehave = BINFILE_TEXT;
605210389Sgabor			else
606210622Sgabor				errx(2, getstr(3), "--binary-files");
607210389Sgabor			break;
608210389Sgabor		case COLOR_OPT:
609210461Sgabor			color = NULL;
610210461Sgabor			if (optarg == NULL || strcasecmp("auto", optarg) == 0 ||
611210461Sgabor			    strcasecmp("tty", optarg) == 0 ||
612210461Sgabor			    strcasecmp("if-tty", optarg) == 0) {
613210461Sgabor				char *term;
614210461Sgabor
615210461Sgabor				term = getenv("TERM");
616210461Sgabor				if (isatty(STDOUT_FILENO) && term != NULL &&
617210461Sgabor				    strcasecmp(term, "dumb") != 0)
618210461Sgabor					color = init_color("01;31");
619210461Sgabor			} else if (strcasecmp("always", optarg) == 0 ||
620210461Sgabor			    strcasecmp("yes", optarg) == 0 ||
621210461Sgabor			    strcasecmp("force", optarg) == 0) {
622210461Sgabor				color = init_color("01;31");
623210461Sgabor			} else if (strcasecmp("never", optarg) != 0 &&
624210461Sgabor			    strcasecmp("none", optarg) != 0 &&
625210461Sgabor			    strcasecmp("no", optarg) != 0)
626210622Sgabor				errx(2, getstr(3), "--color");
627223009Sgabor			cflags &= ~REG_NOSUB;
628210389Sgabor			break;
629210389Sgabor		case LABEL_OPT:
630210389Sgabor			label = optarg;
631210389Sgabor			break;
632210389Sgabor		case LINEBUF_OPT:
633210389Sgabor			lbflag = true;
634210389Sgabor			break;
635210389Sgabor		case NULL_OPT:
636210389Sgabor			nullflag = true;
637210389Sgabor			break;
638210389Sgabor		case R_INCLUDE_OPT:
639210578Sgabor			finclude = true;
640210578Sgabor			add_fpattern(optarg, INCL_PAT);
641210389Sgabor			break;
642210389Sgabor		case R_EXCLUDE_OPT:
643210578Sgabor			fexclude = true;
644210578Sgabor			add_fpattern(optarg, EXCL_PAT);
645210389Sgabor			break;
646210389Sgabor		case R_DINCLUDE_OPT:
647211364Sgabor			dinclude = true;
648210578Sgabor			add_dpattern(optarg, INCL_PAT);
649210389Sgabor			break;
650210389Sgabor		case R_DEXCLUDE_OPT:
651211364Sgabor			dexclude = true;
652210578Sgabor			add_dpattern(optarg, EXCL_PAT);
653210389Sgabor			break;
654210389Sgabor		case HELP_OPT:
655210389Sgabor		default:
656210389Sgabor			usage();
657210389Sgabor		}
658210389Sgabor		lastc = c;
659210389Sgabor		newarg = optind != prevoptind;
660210389Sgabor		prevoptind = optind;
661210389Sgabor	}
662210389Sgabor	aargc -= optind;
663210389Sgabor	aargv += optind;
664210389Sgabor
665226261Sgabor	/* Empty pattern file matches nothing */
666226261Sgabor	if (!needpattern && (patterns == 0))
667226261Sgabor		exit(1);
668226261Sgabor
669210389Sgabor	/* Fail if we don't have any pattern */
670210389Sgabor	if (aargc == 0 && needpattern)
671210389Sgabor		usage();
672210389Sgabor
673210389Sgabor	/* Process patterns from command line */
674210389Sgabor	if (aargc != 0 && needpattern) {
675245996Sgabor		char *token;
676245996Sgabor		char *string = *aargv;
677245996Sgabor
678245996Sgabor		while ((token = strsep(&string, "\n")) != NULL)
679245996Sgabor			add_pattern(token, strlen(token));
680210389Sgabor		--aargc;
681210389Sgabor		++aargv;
682210389Sgabor	}
683210389Sgabor
684210389Sgabor	switch (grepbehave) {
685210389Sgabor	case GREP_BASIC:
686210389Sgabor		break;
687226261Sgabor	case GREP_FIXED:
688226261Sgabor		/* XXX: header mess, REG_LITERAL not defined in gnu/regex.h */
689226261Sgabor		cflags |= 0020;
690226261Sgabor		break;
691210389Sgabor	case GREP_EXTENDED:
692210389Sgabor		cflags |= REG_EXTENDED;
693210389Sgabor		break;
694210389Sgabor	default:
695210389Sgabor		/* NOTREACHED */
696210389Sgabor		usage();
697210389Sgabor	}
698210389Sgabor
699210389Sgabor	fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern));
700210389Sgabor	r_pattern = grep_calloc(patterns, sizeof(*r_pattern));
701226261Sgabor
702226261Sgabor	/* Check if cheating is allowed (always is for fgrep). */
703226261Sgabor	for (i = 0; i < patterns; ++i) {
704226261Sgabor		if (fastncomp(&fg_pattern[i], pattern[i].pat,
705226261Sgabor		    pattern[i].len, cflags) != 0) {
706226261Sgabor			/* Fall back to full regex library */
707226261Sgabor			c = regcomp(&r_pattern[i], pattern[i].pat, cflags);
708226261Sgabor			if (c != 0) {
709226261Sgabor				regerror(c, &r_pattern[i], re_error,
710226261Sgabor				    RE_ERROR_BUF);
711226261Sgabor				errx(2, "%s", re_error);
712210389Sgabor			}
713210389Sgabor		}
714210389Sgabor	}
715210389Sgabor
716210389Sgabor	if (lbflag)
717210389Sgabor		setlinebuf(stdout);
718210389Sgabor
719210389Sgabor	if ((aargc == 0 || aargc == 1) && !Hflag)
720210389Sgabor		hflag = true;
721210389Sgabor
722210389Sgabor	if (aargc == 0)
723210389Sgabor		exit(!procfile("-"));
724210389Sgabor
725210389Sgabor	if (dirbehave == DIR_RECURSE)
726210389Sgabor		c = grep_tree(aargv);
727211519Sdelphij	else
728210578Sgabor		for (c = 0; aargc--; ++aargv) {
729210578Sgabor			if ((finclude || fexclude) && !file_matching(*aargv))
730210578Sgabor				continue;
731210389Sgabor			c+= procfile(*aargv);
732210578Sgabor		}
733210389Sgabor
734210389Sgabor#ifndef WITHOUT_NLS
735210389Sgabor	catclose(catalog);
736210389Sgabor#endif
737210389Sgabor
738210389Sgabor	/* Find out the correct return value according to the
739210389Sgabor	   results and the command line option. */
740229081Sgabor	exit(c ? (file_err ? (qflag ? 0 : 2) : 0) : (file_err ? 2 : 1));
741210389Sgabor}
742