1268966Spfg/*	$NetBSD: grep.c,v 1.6 2011/04/18 03:48:23 joerg Exp $	*/
2220422Sgabor/* 	$FreeBSD$	*/
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$");
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>
47268966Spfg#define _WITH_GETLINE
48210389Sgabor#include <stdio.h>
49210389Sgabor#include <stdlib.h>
50210389Sgabor#include <string.h>
51210389Sgabor#include <unistd.h>
52210389Sgabor
53226035Sgabor#include "fastmatch.h"
54210389Sgabor#include "grep.h"
55210389Sgabor
56210389Sgabor#ifndef WITHOUT_NLS
57210389Sgabor#include <nl_types.h>
58210389Sgabornl_catd	 catalog;
59210389Sgabor#endif
60210389Sgabor
61210389Sgabor/*
62210389Sgabor * Default messags to use when NLS is disabled or no catalogue
63210389Sgabor * is found.
64210389Sgabor */
65210389Sgaborconst char	*errstr[] = {
66210389Sgabor	"",
67210389Sgabor/* 1*/	"(standard input)",
68210389Sgabor/* 2*/	"cannot read bzip2 compressed file",
69210622Sgabor/* 3*/	"unknown %s option",
70210389Sgabor/* 4*/	"usage: %s [-abcDEFGHhIiJLlmnOoPqRSsUVvwxZ] [-A num] [-B num] [-C[num]]\n",
71210389Sgabor/* 5*/	"\t[-e pattern] [-f file] [--binary-files=value] [--color=when]\n",
72210389Sgabor/* 6*/	"\t[--context[=num]] [--directories=action] [--label] [--line-buffered]\n",
73210389Sgabor/* 7*/	"\t[--null] [pattern] [file ...]\n",
74210622Sgabor/* 8*/	"Binary file %s matches\n",
75210622Sgabor/* 9*/	"%s (BSD grep) %s\n",
76210389Sgabor};
77210389Sgabor
78210389Sgabor/* Flags passed to regcomp() and regexec() */
79223009Sgaborint		 cflags = REG_NOSUB;
80210389Sgaborint		 eflags = REG_STARTEND;
81210389Sgabor
82210389Sgabor/* Shortcut for matching all cases like empty regex */
83210389Sgaborbool		 matchall;
84210389Sgabor
85210389Sgabor/* Searching patterns */
86241737Sedunsigned int	 patterns;
87241737Sedstatic unsigned int pattern_sz;
88226035Sgaborstruct pat	*pattern;
89210389Sgaborregex_t		*r_pattern;
90226035Sgaborfastmatch_t	*fg_pattern;
91210389Sgabor
92210389Sgabor/* Filename exclusion/inclusion patterns */
93241737Sedunsigned int	fpatterns, dpatterns;
94241737Sedstatic unsigned int fpattern_sz, dpattern_sz;
95210578Sgaborstruct epat	*dpattern, *fpattern;
96210389Sgabor
97210389Sgabor/* For regex errors  */
98210389Sgaborchar	 re_error[RE_ERROR_BUF + 1];
99210389Sgabor
100210389Sgabor/* Command-line flags */
101210389Sgaborunsigned long long Aflag;	/* -A x: print x lines trailing each match */
102210389Sgaborunsigned long long Bflag;	/* -B x: print x lines leading each match */
103210389Sgaborbool	 Hflag;		/* -H: always print file name */
104210389Sgaborbool	 Lflag;		/* -L: only show names of files with no matches */
105210389Sgaborbool	 bflag;		/* -b: show block numbers for each match */
106210389Sgaborbool	 cflag;		/* -c: only show a count of matching lines */
107210389Sgaborbool	 hflag;		/* -h: don't print filename headers */
108210389Sgaborbool	 iflag;		/* -i: ignore case */
109210389Sgaborbool	 lflag;		/* -l: only show names of files with matches */
110210389Sgaborbool	 mflag;		/* -m x: stop reading the files after x matches */
111226035Sgaborlong long mcount;	/* count for -m */
112244493Seadlerlong long mlimit;	/* requested value for -m */
113210389Sgaborbool	 nflag;		/* -n: show line numbers in front of matching lines */
114210389Sgaborbool	 oflag;		/* -o: print only matching part */
115210389Sgaborbool	 qflag;		/* -q: quiet mode (don't output anything) */
116210389Sgaborbool	 sflag;		/* -s: silent mode (ignore errors) */
117210389Sgaborbool	 vflag;		/* -v: only show non-matching lines */
118210389Sgaborbool	 wflag;		/* -w: pattern must start and end on word boundaries */
119210389Sgaborbool	 xflag;		/* -x: pattern must match entire line */
120210389Sgaborbool	 lbflag;	/* --line-buffered */
121210389Sgaborbool	 nullflag;	/* --null */
122210389Sgaborchar	*label;		/* --label */
123210461Sgaborconst char *color;	/* --color */
124210389Sgaborint	 grepbehave = GREP_BASIC;	/* -EFGP: type of the regex */
125210389Sgaborint	 binbehave = BINFILE_BIN;	/* -aIU: handling of binary files */
126210389Sgaborint	 filebehave = FILE_STDIO;	/* -JZ: normal, gzip or bzip2 file */
127210461Sgaborint	 devbehave = DEV_READ;		/* -D: handling of devices */
128210461Sgaborint	 dirbehave = DIR_READ;		/* -dRr: handling of directories */
129210461Sgaborint	 linkbehave = LINK_READ;	/* -OpS: handling of symlinks */
130210389Sgabor
131211364Sgaborbool	 dexclude, dinclude;	/* --exclude-dir and --include-dir */
132211364Sgaborbool	 fexclude, finclude;	/* --exclude and --include */
133210578Sgabor
134210389Sgaborenum {
135210389Sgabor	BIN_OPT = CHAR_MAX + 1,
136210389Sgabor	COLOR_OPT,
137210389Sgabor	HELP_OPT,
138210389Sgabor	MMAP_OPT,
139210389Sgabor	LINEBUF_OPT,
140210389Sgabor	LABEL_OPT,
141210389Sgabor	NULL_OPT,
142210389Sgabor	R_EXCLUDE_OPT,
143210389Sgabor	R_INCLUDE_OPT,
144210389Sgabor	R_DEXCLUDE_OPT,
145210389Sgabor	R_DINCLUDE_OPT
146210389Sgabor};
147210389Sgabor
148210461Sgaborstatic inline const char	*init_color(const char *);
149210461Sgabor
150210389Sgabor/* Housekeeping */
151210389Sgaborbool	 first = true;	/* flag whether we are processing the first match */
152210389Sgaborbool	 prev;		/* flag whether or not the previous line matched */
153210389Sgaborint	 tail;		/* lines left to print */
154228319Sgaborbool	 file_err;	/* file reading error */
155210389Sgabor
156210389Sgabor/*
157210389Sgabor * Prints usage information and returns 2.
158210389Sgabor */
159210389Sgaborstatic void
160210389Sgaborusage(void)
161210389Sgabor{
162226271Sgabor	fprintf(stderr, getstr(4), getprogname());
163210389Sgabor	fprintf(stderr, "%s", getstr(5));
164210389Sgabor	fprintf(stderr, "%s", getstr(6));
165210389Sgabor	fprintf(stderr, "%s", getstr(7));
166210389Sgabor	exit(2);
167210389Sgabor}
168210389Sgabor
169226035Sgaborstatic const char	*optstr = "0123456789A:B:C:D:EFGHIJMLOPSRUVZabcd:e:f:hilm:nopqrsuvwxXy";
170210389Sgabor
171228395Sedstatic const struct option long_options[] =
172210389Sgabor{
173210389Sgabor	{"binary-files",	required_argument,	NULL, BIN_OPT},
174210389Sgabor	{"help",		no_argument,		NULL, HELP_OPT},
175210389Sgabor	{"mmap",		no_argument,		NULL, MMAP_OPT},
176210389Sgabor	{"line-buffered",	no_argument,		NULL, LINEBUF_OPT},
177210389Sgabor	{"label",		required_argument,	NULL, LABEL_OPT},
178210389Sgabor	{"null",		no_argument,		NULL, NULL_OPT},
179210389Sgabor	{"color",		optional_argument,	NULL, COLOR_OPT},
180210389Sgabor	{"colour",		optional_argument,	NULL, COLOR_OPT},
181210389Sgabor	{"exclude",		required_argument,	NULL, R_EXCLUDE_OPT},
182210389Sgabor	{"include",		required_argument,	NULL, R_INCLUDE_OPT},
183210389Sgabor	{"exclude-dir",		required_argument,	NULL, R_DEXCLUDE_OPT},
184210389Sgabor	{"include-dir",		required_argument,	NULL, R_DINCLUDE_OPT},
185210389Sgabor	{"after-context",	required_argument,	NULL, 'A'},
186210389Sgabor	{"text",		no_argument,		NULL, 'a'},
187210389Sgabor	{"before-context",	required_argument,	NULL, 'B'},
188210389Sgabor	{"byte-offset",		no_argument,		NULL, 'b'},
189210389Sgabor	{"context",		optional_argument,	NULL, 'C'},
190210389Sgabor	{"count",		no_argument,		NULL, 'c'},
191210389Sgabor	{"devices",		required_argument,	NULL, 'D'},
192210389Sgabor        {"directories",		required_argument,	NULL, 'd'},
193210389Sgabor	{"extended-regexp",	no_argument,		NULL, 'E'},
194210389Sgabor	{"regexp",		required_argument,	NULL, 'e'},
195210389Sgabor	{"fixed-strings",	no_argument,		NULL, 'F'},
196210389Sgabor	{"file",		required_argument,	NULL, 'f'},
197210389Sgabor	{"basic-regexp",	no_argument,		NULL, 'G'},
198210389Sgabor	{"no-filename",		no_argument,		NULL, 'h'},
199210389Sgabor	{"with-filename",	no_argument,		NULL, 'H'},
200210389Sgabor	{"ignore-case",		no_argument,		NULL, 'i'},
201210389Sgabor	{"bz2decompress",	no_argument,		NULL, 'J'},
202210389Sgabor	{"files-with-matches",	no_argument,		NULL, 'l'},
203210389Sgabor	{"files-without-match", no_argument,            NULL, 'L'},
204210389Sgabor	{"max-count",		required_argument,	NULL, 'm'},
205226035Sgabor	{"lzma",		no_argument,		NULL, 'M'},
206210389Sgabor	{"line-number",		no_argument,		NULL, 'n'},
207210389Sgabor	{"only-matching",	no_argument,		NULL, 'o'},
208210389Sgabor	{"quiet",		no_argument,		NULL, 'q'},
209210389Sgabor	{"silent",		no_argument,		NULL, 'q'},
210210389Sgabor	{"recursive",		no_argument,		NULL, 'r'},
211210389Sgabor	{"no-messages",		no_argument,		NULL, 's'},
212210389Sgabor	{"binary",		no_argument,		NULL, 'U'},
213210389Sgabor	{"unix-byte-offsets",	no_argument,		NULL, 'u'},
214210389Sgabor	{"invert-match",	no_argument,		NULL, 'v'},
215210389Sgabor	{"version",		no_argument,		NULL, 'V'},
216210389Sgabor	{"word-regexp",		no_argument,		NULL, 'w'},
217210389Sgabor	{"line-regexp",		no_argument,		NULL, 'x'},
218226035Sgabor	{"xz",			no_argument,		NULL, 'X'},
219210389Sgabor	{"decompress",          no_argument,            NULL, 'Z'},
220210389Sgabor	{NULL,			no_argument,		NULL, 0}
221210389Sgabor};
222210389Sgabor
223210389Sgabor/*
224210389Sgabor * Adds a searching pattern to the internal array.
225210389Sgabor */
226210389Sgaborstatic void
227210389Sgaboradd_pattern(char *pat, size_t len)
228210389Sgabor{
229210389Sgabor
230226035Sgabor	/* Do not add further pattern is we already match everything */
231226035Sgabor	if (matchall)
232226035Sgabor	  return;
233226035Sgabor
234210389Sgabor	/* Check if we can do a shortcut */
235226035Sgabor	if (len == 0) {
236210389Sgabor		matchall = true;
237226035Sgabor		for (unsigned int i = 0; i < patterns; i++) {
238226035Sgabor			free(pattern[i].pat);
239226035Sgabor		}
240226035Sgabor		pattern = grep_realloc(pattern, sizeof(struct pat));
241226035Sgabor		pattern[0].pat = NULL;
242226035Sgabor		pattern[0].len = 0;
243226035Sgabor		patterns = 1;
244210389Sgabor		return;
245210389Sgabor	}
246210389Sgabor	/* Increase size if necessary */
247210389Sgabor	if (patterns == pattern_sz) {
248210389Sgabor		pattern_sz *= 2;
249210389Sgabor		pattern = grep_realloc(pattern, ++pattern_sz *
250226035Sgabor		    sizeof(struct pat));
251210389Sgabor	}
252210389Sgabor	if (len > 0 && pat[len - 1] == '\n')
253210389Sgabor		--len;
254210389Sgabor	/* pat may not be NUL-terminated */
255226035Sgabor	pattern[patterns].pat = grep_malloc(len + 1);
256226035Sgabor	memcpy(pattern[patterns].pat, pat, len);
257226035Sgabor	pattern[patterns].len = len;
258226035Sgabor	pattern[patterns].pat[len] = '\0';
259210389Sgabor	++patterns;
260210389Sgabor}
261210389Sgabor
262210389Sgabor/*
263210578Sgabor * Adds a file include/exclude pattern to the internal array.
264210389Sgabor */
265210389Sgaborstatic void
266210578Sgaboradd_fpattern(const char *pat, int mode)
267210389Sgabor{
268210389Sgabor
269210389Sgabor	/* Increase size if necessary */
270210578Sgabor	if (fpatterns == fpattern_sz) {
271210578Sgabor		fpattern_sz *= 2;
272210578Sgabor		fpattern = grep_realloc(fpattern, ++fpattern_sz *
273210389Sgabor		    sizeof(struct epat));
274210389Sgabor	}
275210578Sgabor	fpattern[fpatterns].pat = grep_strdup(pat);
276210578Sgabor	fpattern[fpatterns].mode = mode;
277210578Sgabor	++fpatterns;
278210389Sgabor}
279210389Sgabor
280210389Sgabor/*
281210578Sgabor * Adds a directory include/exclude pattern to the internal array.
282210578Sgabor */
283210578Sgaborstatic void
284210578Sgaboradd_dpattern(const char *pat, int mode)
285210578Sgabor{
286210578Sgabor
287210578Sgabor	/* Increase size if necessary */
288210578Sgabor	if (dpatterns == dpattern_sz) {
289210578Sgabor		dpattern_sz *= 2;
290210578Sgabor		dpattern = grep_realloc(dpattern, ++dpattern_sz *
291210578Sgabor		    sizeof(struct epat));
292210578Sgabor	}
293210578Sgabor	dpattern[dpatterns].pat = grep_strdup(pat);
294210578Sgabor	dpattern[dpatterns].mode = mode;
295210578Sgabor	++dpatterns;
296210578Sgabor}
297210578Sgabor
298210578Sgabor/*
299210389Sgabor * Reads searching patterns from a file and adds them with add_pattern().
300210389Sgabor */
301210389Sgaborstatic void
302210389Sgaborread_patterns(const char *fn)
303210389Sgabor{
304226035Sgabor	struct stat st;
305210389Sgabor	FILE *f;
306210389Sgabor	char *line;
307210389Sgabor	size_t len;
308268966Spfg	ssize_t rlen;
309210389Sgabor
310210389Sgabor	if ((f = fopen(fn, "r")) == NULL)
311210389Sgabor		err(2, "%s", fn);
312226035Sgabor	if ((fstat(fileno(f), &st) == -1) || (S_ISDIR(st.st_mode))) {
313226035Sgabor		fclose(f);
314226035Sgabor		return;
315226035Sgabor	}
316268966Spfg	len = 0;
317268966Spfg	line = NULL;
318268966Spfg	while ((rlen = getline(&line, &len, f)) != -1)
319226035Sgabor		add_pattern(line, line[0] == '\n' ? 0 : len);
320268966Spfg	free(line);
321210389Sgabor	if (ferror(f))
322210389Sgabor		err(2, "%s", fn);
323210389Sgabor	fclose(f);
324210389Sgabor}
325210389Sgabor
326210461Sgaborstatic inline const char *
327210461Sgaborinit_color(const char *d)
328210461Sgabor{
329210461Sgabor	char *c;
330210461Sgabor
331210461Sgabor	c = getenv("GREP_COLOR");
332224937Sgabor	return (c != NULL && c[0] != '\0' ? c : d);
333210461Sgabor}
334210461Sgabor
335210389Sgaborint
336210389Sgabormain(int argc, char *argv[])
337210389Sgabor{
338210389Sgabor	char **aargv, **eargv, *eopts;
339226271Sgabor	char *ep;
340226271Sgabor	const char *pn;
341210389Sgabor	unsigned long long l;
342210389Sgabor	unsigned int aargc, eargc, i;
343210389Sgabor	int c, lastc, needpattern, newarg, prevoptind;
344210389Sgabor
345210389Sgabor	setlocale(LC_ALL, "");
346210389Sgabor
347210389Sgabor#ifndef WITHOUT_NLS
348210389Sgabor	catalog = catopen("grep", NL_CAT_LOCALE);
349210389Sgabor#endif
350210389Sgabor
351210389Sgabor	/* Check what is the program name of the binary.  In this
352210389Sgabor	   way we can have all the funcionalities in one binary
353210389Sgabor	   without the need of scripting and using ugly hacks. */
354226271Sgabor	pn = getprogname();
355226035Sgabor	if (pn[0] == 'b' && pn[1] == 'z') {
356226035Sgabor		filebehave = FILE_BZIP;
357226035Sgabor		pn += 2;
358226035Sgabor	} else if (pn[0] == 'x' && pn[1] == 'z') {
359226035Sgabor		filebehave = FILE_XZ;
360226035Sgabor		pn += 2;
361226035Sgabor	} else if (pn[0] == 'l' && pn[1] == 'z') {
362226035Sgabor		filebehave = FILE_LZMA;
363226035Sgabor		pn += 2;
364226035Sgabor	} else if (pn[0] == 'z') {
365226035Sgabor		filebehave = FILE_GZIP;
366226035Sgabor		pn += 1;
367226035Sgabor	}
368226035Sgabor	switch (pn[0]) {
369210389Sgabor	case 'e':
370210389Sgabor		grepbehave = GREP_EXTENDED;
371210389Sgabor		break;
372210389Sgabor	case 'f':
373210389Sgabor		grepbehave = GREP_FIXED;
374210389Sgabor		break;
375210389Sgabor	}
376210389Sgabor
377210389Sgabor	lastc = '\0';
378210389Sgabor	newarg = 1;
379210389Sgabor	prevoptind = 1;
380210389Sgabor	needpattern = 1;
381210389Sgabor
382210389Sgabor	eopts = getenv("GREP_OPTIONS");
383210389Sgabor
384211364Sgabor	/* support for extra arguments in GREP_OPTIONS */
385211364Sgabor	eargc = 0;
386224937Sgabor	if (eopts != NULL && eopts[0] != '\0') {
387210389Sgabor		char *str;
388210389Sgabor
389211364Sgabor		/* make an estimation of how many extra arguments we have */
390211364Sgabor		for (unsigned int j = 0; j < strlen(eopts); j++)
391211364Sgabor			if (eopts[j] == ' ')
392210389Sgabor				eargc++;
393210389Sgabor
394210389Sgabor		eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1));
395210389Sgabor
396210389Sgabor		eargc = 0;
397211364Sgabor		/* parse extra arguments */
398211364Sgabor		while ((str = strsep(&eopts, " ")) != NULL)
399224937Sgabor			if (str[0] != '\0')
400224937Sgabor				eargv[eargc++] = grep_strdup(str);
401210389Sgabor
402210430Sdelphij		aargv = (char **)grep_calloc(eargc + argc + 1,
403210430Sdelphij		    sizeof(char *));
404211364Sgabor
405210389Sgabor		aargv[0] = argv[0];
406211364Sgabor		for (i = 0; i < eargc; i++)
407211364Sgabor			aargv[i + 1] = eargv[i];
408211364Sgabor		for (int j = 1; j < argc; j++, i++)
409211364Sgabor			aargv[i + 1] = argv[j];
410210389Sgabor
411211364Sgabor		aargc = eargc + argc;
412210389Sgabor	} else {
413210389Sgabor		aargv = argv;
414210389Sgabor		aargc = argc;
415210389Sgabor	}
416210389Sgabor
417210389Sgabor	while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) !=
418210389Sgabor	    -1)) {
419210389Sgabor		switch (c) {
420210389Sgabor		case '0': case '1': case '2': case '3': case '4':
421210389Sgabor		case '5': case '6': case '7': case '8': case '9':
422210389Sgabor			if (newarg || !isdigit(lastc))
423210389Sgabor				Aflag = 0;
424210389Sgabor			else if (Aflag > LLONG_MAX / 10) {
425210389Sgabor				errno = ERANGE;
426210389Sgabor				err(2, NULL);
427210389Sgabor			}
428210389Sgabor			Aflag = Bflag = (Aflag * 10) + (c - '0');
429210389Sgabor			break;
430210389Sgabor		case 'C':
431210389Sgabor			if (optarg == NULL) {
432210389Sgabor				Aflag = Bflag = 2;
433210389Sgabor				break;
434210389Sgabor			}
435210389Sgabor			/* FALLTHROUGH */
436210389Sgabor		case 'A':
437210389Sgabor			/* FALLTHROUGH */
438210389Sgabor		case 'B':
439210389Sgabor			errno = 0;
440210389Sgabor			l = strtoull(optarg, &ep, 10);
441210389Sgabor			if (((errno == ERANGE) && (l == ULLONG_MAX)) ||
442210389Sgabor			    ((errno == EINVAL) && (l == 0)))
443210389Sgabor				err(2, NULL);
444210389Sgabor			else if (ep[0] != '\0') {
445210389Sgabor				errno = EINVAL;
446210389Sgabor				err(2, NULL);
447210389Sgabor			}
448210389Sgabor			if (c == 'A')
449210389Sgabor				Aflag = l;
450210389Sgabor			else if (c == 'B')
451210389Sgabor				Bflag = l;
452210389Sgabor			else
453210389Sgabor				Aflag = Bflag = l;
454210389Sgabor			break;
455210389Sgabor		case 'a':
456210389Sgabor			binbehave = BINFILE_TEXT;
457210389Sgabor			break;
458210389Sgabor		case 'b':
459210389Sgabor			bflag = true;
460210389Sgabor			break;
461210389Sgabor		case 'c':
462210389Sgabor			cflag = true;
463210389Sgabor			break;
464210389Sgabor		case 'D':
465210461Sgabor			if (strcasecmp(optarg, "skip") == 0)
466210389Sgabor				devbehave = DEV_SKIP;
467210461Sgabor			else if (strcasecmp(optarg, "read") == 0)
468210461Sgabor				devbehave = DEV_READ;
469210622Sgabor			else
470210622Sgabor				errx(2, getstr(3), "--devices");
471210389Sgabor			break;
472210389Sgabor		case 'd':
473210461Sgabor			if (strcasecmp("recurse", optarg) == 0) {
474210389Sgabor				Hflag = true;
475210389Sgabor				dirbehave = DIR_RECURSE;
476210461Sgabor			} else if (strcasecmp("skip", optarg) == 0)
477210389Sgabor				dirbehave = DIR_SKIP;
478210461Sgabor			else if (strcasecmp("read", optarg) == 0)
479210461Sgabor				dirbehave = DIR_READ;
480210622Sgabor			else
481210622Sgabor				errx(2, getstr(3), "--directories");
482210389Sgabor			break;
483210389Sgabor		case 'E':
484210389Sgabor			grepbehave = GREP_EXTENDED;
485210389Sgabor			break;
486210389Sgabor		case 'e':
487245057Sgabor			{
488245057Sgabor				char *token;
489245688Sgabor				char *string = optarg;
490245057Sgabor
491245057Sgabor				while ((token = strsep(&string, "\n")) != NULL)
492245057Sgabor					add_pattern(token, strlen(token));
493245057Sgabor			}
494210389Sgabor			needpattern = 0;
495210389Sgabor			break;
496210389Sgabor		case 'F':
497210389Sgabor			grepbehave = GREP_FIXED;
498210389Sgabor			break;
499210389Sgabor		case 'f':
500210389Sgabor			read_patterns(optarg);
501210389Sgabor			needpattern = 0;
502210389Sgabor			break;
503210389Sgabor		case 'G':
504210389Sgabor			grepbehave = GREP_BASIC;
505210389Sgabor			break;
506210389Sgabor		case 'H':
507210389Sgabor			Hflag = true;
508210389Sgabor			break;
509210389Sgabor		case 'h':
510210389Sgabor			Hflag = false;
511210389Sgabor			hflag = true;
512210389Sgabor			break;
513210389Sgabor		case 'I':
514210389Sgabor			binbehave = BINFILE_SKIP;
515210389Sgabor			break;
516210389Sgabor		case 'i':
517210389Sgabor		case 'y':
518210389Sgabor			iflag =  true;
519210389Sgabor			cflags |= REG_ICASE;
520210389Sgabor			break;
521210389Sgabor		case 'J':
522226271Sgabor#ifdef WITHOUT_BZIP2
523226271Sgabor			errno = EOPNOTSUPP;
524226271Sgabor			err(2, "bzip2 support was disabled at compile-time");
525226271Sgabor#endif
526210389Sgabor			filebehave = FILE_BZIP;
527210389Sgabor			break;
528210389Sgabor		case 'L':
529210389Sgabor			lflag = false;
530210461Sgabor			Lflag = true;
531210389Sgabor			break;
532210389Sgabor		case 'l':
533210389Sgabor			Lflag = false;
534210461Sgabor			lflag = true;
535210389Sgabor			break;
536210389Sgabor		case 'm':
537210389Sgabor			mflag = true;
538210389Sgabor			errno = 0;
539244493Seadler			mlimit = mcount = strtoll(optarg, &ep, 10);
540226035Sgabor			if (((errno == ERANGE) && (mcount == LLONG_MAX)) ||
541210389Sgabor			    ((errno == EINVAL) && (mcount == 0)))
542210389Sgabor				err(2, NULL);
543210389Sgabor			else if (ep[0] != '\0') {
544210389Sgabor				errno = EINVAL;
545210389Sgabor				err(2, NULL);
546210389Sgabor			}
547210389Sgabor			break;
548226035Sgabor		case 'M':
549226035Sgabor			filebehave = FILE_LZMA;
550226035Sgabor			break;
551210389Sgabor		case 'n':
552210389Sgabor			nflag = true;
553210389Sgabor			break;
554210389Sgabor		case 'O':
555210389Sgabor			linkbehave = LINK_EXPLICIT;
556210389Sgabor			break;
557210389Sgabor		case 'o':
558210389Sgabor			oflag = true;
559223009Sgabor			cflags &= ~REG_NOSUB;
560210389Sgabor			break;
561210389Sgabor		case 'p':
562210389Sgabor			linkbehave = LINK_SKIP;
563210389Sgabor			break;
564210389Sgabor		case 'q':
565210389Sgabor			qflag = true;
566210389Sgabor			break;
567210389Sgabor		case 'S':
568210461Sgabor			linkbehave = LINK_READ;
569210389Sgabor			break;
570210389Sgabor		case 'R':
571210389Sgabor		case 'r':
572210389Sgabor			dirbehave = DIR_RECURSE;
573210389Sgabor			Hflag = true;
574210389Sgabor			break;
575210389Sgabor		case 's':
576210389Sgabor			sflag = true;
577210389Sgabor			break;
578210389Sgabor		case 'U':
579210389Sgabor			binbehave = BINFILE_BIN;
580210389Sgabor			break;
581210389Sgabor		case 'u':
582210389Sgabor		case MMAP_OPT:
583226035Sgabor			filebehave = FILE_MMAP;
584210389Sgabor			break;
585210389Sgabor		case 'V':
586226271Sgabor			printf(getstr(9), getprogname(), VERSION);
587210389Sgabor			exit(0);
588210389Sgabor		case 'v':
589210389Sgabor			vflag = true;
590210389Sgabor			break;
591210389Sgabor		case 'w':
592210389Sgabor			wflag = true;
593223009Sgabor			cflags &= ~REG_NOSUB;
594210389Sgabor			break;
595210389Sgabor		case 'x':
596210389Sgabor			xflag = true;
597223009Sgabor			cflags &= ~REG_NOSUB;
598210389Sgabor			break;
599226035Sgabor		case 'X':
600226035Sgabor			filebehave = FILE_XZ;
601226035Sgabor			break;
602210389Sgabor		case 'Z':
603210389Sgabor			filebehave = FILE_GZIP;
604210389Sgabor			break;
605210389Sgabor		case BIN_OPT:
606210461Sgabor			if (strcasecmp("binary", optarg) == 0)
607210389Sgabor				binbehave = BINFILE_BIN;
608210461Sgabor			else if (strcasecmp("without-match", optarg) == 0)
609210389Sgabor				binbehave = BINFILE_SKIP;
610210461Sgabor			else if (strcasecmp("text", optarg) == 0)
611210389Sgabor				binbehave = BINFILE_TEXT;
612210389Sgabor			else
613210622Sgabor				errx(2, getstr(3), "--binary-files");
614210389Sgabor			break;
615210389Sgabor		case COLOR_OPT:
616210461Sgabor			color = NULL;
617210461Sgabor			if (optarg == NULL || strcasecmp("auto", optarg) == 0 ||
618210461Sgabor			    strcasecmp("tty", optarg) == 0 ||
619210461Sgabor			    strcasecmp("if-tty", optarg) == 0) {
620210461Sgabor				char *term;
621210461Sgabor
622210461Sgabor				term = getenv("TERM");
623210461Sgabor				if (isatty(STDOUT_FILENO) && term != NULL &&
624210461Sgabor				    strcasecmp(term, "dumb") != 0)
625210461Sgabor					color = init_color("01;31");
626210461Sgabor			} else if (strcasecmp("always", optarg) == 0 ||
627210461Sgabor			    strcasecmp("yes", optarg) == 0 ||
628210461Sgabor			    strcasecmp("force", optarg) == 0) {
629210461Sgabor				color = init_color("01;31");
630210461Sgabor			} else if (strcasecmp("never", optarg) != 0 &&
631210461Sgabor			    strcasecmp("none", optarg) != 0 &&
632210461Sgabor			    strcasecmp("no", optarg) != 0)
633210622Sgabor				errx(2, getstr(3), "--color");
634223009Sgabor			cflags &= ~REG_NOSUB;
635210389Sgabor			break;
636210389Sgabor		case LABEL_OPT:
637210389Sgabor			label = optarg;
638210389Sgabor			break;
639210389Sgabor		case LINEBUF_OPT:
640210389Sgabor			lbflag = true;
641210389Sgabor			break;
642210389Sgabor		case NULL_OPT:
643210389Sgabor			nullflag = true;
644210389Sgabor			break;
645210389Sgabor		case R_INCLUDE_OPT:
646210578Sgabor			finclude = true;
647210578Sgabor			add_fpattern(optarg, INCL_PAT);
648210389Sgabor			break;
649210389Sgabor		case R_EXCLUDE_OPT:
650210578Sgabor			fexclude = true;
651210578Sgabor			add_fpattern(optarg, EXCL_PAT);
652210389Sgabor			break;
653210389Sgabor		case R_DINCLUDE_OPT:
654211364Sgabor			dinclude = true;
655210578Sgabor			add_dpattern(optarg, INCL_PAT);
656210389Sgabor			break;
657210389Sgabor		case R_DEXCLUDE_OPT:
658211364Sgabor			dexclude = true;
659210578Sgabor			add_dpattern(optarg, EXCL_PAT);
660210389Sgabor			break;
661210389Sgabor		case HELP_OPT:
662210389Sgabor		default:
663210389Sgabor			usage();
664210389Sgabor		}
665210389Sgabor		lastc = c;
666210389Sgabor		newarg = optind != prevoptind;
667210389Sgabor		prevoptind = optind;
668210389Sgabor	}
669210389Sgabor	aargc -= optind;
670210389Sgabor	aargv += optind;
671210389Sgabor
672226035Sgabor	/* Empty pattern file matches nothing */
673226035Sgabor	if (!needpattern && (patterns == 0))
674226035Sgabor		exit(1);
675226035Sgabor
676210389Sgabor	/* Fail if we don't have any pattern */
677210389Sgabor	if (aargc == 0 && needpattern)
678210389Sgabor		usage();
679210389Sgabor
680210389Sgabor	/* Process patterns from command line */
681210389Sgabor	if (aargc != 0 && needpattern) {
682245057Sgabor		char *token;
683245688Sgabor		char *string = *aargv;
684245057Sgabor
685245057Sgabor		while ((token = strsep(&string, "\n")) != NULL)
686245057Sgabor			add_pattern(token, strlen(token));
687210389Sgabor		--aargc;
688210389Sgabor		++aargv;
689210389Sgabor	}
690210389Sgabor
691210389Sgabor	switch (grepbehave) {
692210389Sgabor	case GREP_BASIC:
693210389Sgabor		break;
694226035Sgabor	case GREP_FIXED:
695226035Sgabor		/* XXX: header mess, REG_LITERAL not defined in gnu/regex.h */
696226035Sgabor		cflags |= 0020;
697226035Sgabor		break;
698210389Sgabor	case GREP_EXTENDED:
699210389Sgabor		cflags |= REG_EXTENDED;
700210389Sgabor		break;
701210389Sgabor	default:
702210389Sgabor		/* NOTREACHED */
703210389Sgabor		usage();
704210389Sgabor	}
705210389Sgabor
706210389Sgabor	fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern));
707210389Sgabor	r_pattern = grep_calloc(patterns, sizeof(*r_pattern));
708226035Sgabor
709226035Sgabor	/* Check if cheating is allowed (always is for fgrep). */
710226035Sgabor	for (i = 0; i < patterns; ++i) {
711226035Sgabor		if (fastncomp(&fg_pattern[i], pattern[i].pat,
712226035Sgabor		    pattern[i].len, cflags) != 0) {
713226035Sgabor			/* Fall back to full regex library */
714226035Sgabor			c = regcomp(&r_pattern[i], pattern[i].pat, cflags);
715226035Sgabor			if (c != 0) {
716226035Sgabor				regerror(c, &r_pattern[i], re_error,
717226035Sgabor				    RE_ERROR_BUF);
718226035Sgabor				errx(2, "%s", re_error);
719210389Sgabor			}
720210389Sgabor		}
721210389Sgabor	}
722210389Sgabor
723210389Sgabor	if (lbflag)
724210389Sgabor		setlinebuf(stdout);
725210389Sgabor
726210389Sgabor	if ((aargc == 0 || aargc == 1) && !Hflag)
727210389Sgabor		hflag = true;
728210389Sgabor
729210389Sgabor	if (aargc == 0)
730210389Sgabor		exit(!procfile("-"));
731210389Sgabor
732210389Sgabor	if (dirbehave == DIR_RECURSE)
733210389Sgabor		c = grep_tree(aargv);
734211519Sdelphij	else
735210578Sgabor		for (c = 0; aargc--; ++aargv) {
736210578Sgabor			if ((finclude || fexclude) && !file_matching(*aargv))
737210578Sgabor				continue;
738210389Sgabor			c+= procfile(*aargv);
739210578Sgabor		}
740210389Sgabor
741210389Sgabor#ifndef WITHOUT_NLS
742210389Sgabor	catclose(catalog);
743210389Sgabor#endif
744210389Sgabor
745210389Sgabor	/* Find out the correct return value according to the
746210389Sgabor	   results and the command line option. */
747228319Sgabor	exit(c ? (file_err ? (qflag ? 0 : 2) : 0) : (file_err ? 2 : 1));
748210389Sgabor}
749