1220422Sgabor/*	$NetBSD: grep.c,v 1.4 2011/02/16 01:31:33 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>
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 */
110246279Seadlerlong long mlimit;	/* requested value for -m */
111210389Sgaborbool	 nflag;		/* -n: show line numbers in front of matching lines */
112210389Sgaborbool	 oflag;		/* -o: print only matching part */
113210389Sgaborbool	 qflag;		/* -q: quiet mode (don't output anything) */
114210389Sgaborbool	 sflag;		/* -s: silent mode (ignore errors) */
115210389Sgaborbool	 vflag;		/* -v: only show non-matching lines */
116210389Sgaborbool	 wflag;		/* -w: pattern must start and end on word boundaries */
117210389Sgaborbool	 xflag;		/* -x: pattern must match entire line */
118210389Sgaborbool	 lbflag;	/* --line-buffered */
119210389Sgaborbool	 nullflag;	/* --null */
120210389Sgaborchar	*label;		/* --label */
121210461Sgaborconst char *color;	/* --color */
122210389Sgaborint	 grepbehave = GREP_BASIC;	/* -EFGP: type of the regex */
123210389Sgaborint	 binbehave = BINFILE_BIN;	/* -aIU: handling of binary files */
124210389Sgaborint	 filebehave = FILE_STDIO;	/* -JZ: normal, gzip or bzip2 file */
125210461Sgaborint	 devbehave = DEV_READ;		/* -D: handling of devices */
126210461Sgaborint	 dirbehave = DIR_READ;		/* -dRr: handling of directories */
127210461Sgaborint	 linkbehave = LINK_READ;	/* -OpS: handling of symlinks */
128210389Sgabor
129211364Sgaborbool	 dexclude, dinclude;	/* --exclude-dir and --include-dir */
130211364Sgaborbool	 fexclude, finclude;	/* --exclude and --include */
131210578Sgabor
132210389Sgaborenum {
133210389Sgabor	BIN_OPT = CHAR_MAX + 1,
134210389Sgabor	COLOR_OPT,
135210389Sgabor	HELP_OPT,
136210389Sgabor	MMAP_OPT,
137210389Sgabor	LINEBUF_OPT,
138210389Sgabor	LABEL_OPT,
139210389Sgabor	NULL_OPT,
140210389Sgabor	R_EXCLUDE_OPT,
141210389Sgabor	R_INCLUDE_OPT,
142210389Sgabor	R_DEXCLUDE_OPT,
143210389Sgabor	R_DINCLUDE_OPT
144210389Sgabor};
145210389Sgabor
146210461Sgaborstatic inline const char	*init_color(const char *);
147210461Sgabor
148210389Sgabor/* Housekeeping */
149210389Sgaborbool	 first = true;	/* flag whether we are processing the first match */
150210389Sgaborbool	 prev;		/* flag whether or not the previous line matched */
151210389Sgaborint	 tail;		/* lines left to print */
152229081Sgaborbool	 file_err;	/* file reading error */
153210389Sgabor
154210389Sgabor/*
155210389Sgabor * Prints usage information and returns 2.
156210389Sgabor */
157210389Sgaborstatic void
158210389Sgaborusage(void)
159210389Sgabor{
160226573Sgabor	fprintf(stderr, getstr(4), getprogname());
161210389Sgabor	fprintf(stderr, "%s", getstr(5));
162210389Sgabor	fprintf(stderr, "%s", getstr(6));
163210389Sgabor	fprintf(stderr, "%s", getstr(7));
164210389Sgabor	exit(2);
165210389Sgabor}
166210389Sgabor
167226261Sgaborstatic const char	*optstr = "0123456789A:B:C:D:EFGHIJMLOPSRUVZabcd:e:f:hilm:nopqrsuvwxXy";
168210389Sgabor
169210389Sgaborstruct option long_options[] =
170210389Sgabor{
171210389Sgabor	{"binary-files",	required_argument,	NULL, BIN_OPT},
172210389Sgabor	{"help",		no_argument,		NULL, HELP_OPT},
173210389Sgabor	{"mmap",		no_argument,		NULL, MMAP_OPT},
174210389Sgabor	{"line-buffered",	no_argument,		NULL, LINEBUF_OPT},
175210389Sgabor	{"label",		required_argument,	NULL, LABEL_OPT},
176210389Sgabor	{"null",		no_argument,		NULL, NULL_OPT},
177210389Sgabor	{"color",		optional_argument,	NULL, COLOR_OPT},
178210389Sgabor	{"colour",		optional_argument,	NULL, COLOR_OPT},
179210389Sgabor	{"exclude",		required_argument,	NULL, R_EXCLUDE_OPT},
180210389Sgabor	{"include",		required_argument,	NULL, R_INCLUDE_OPT},
181210389Sgabor	{"exclude-dir",		required_argument,	NULL, R_DEXCLUDE_OPT},
182210389Sgabor	{"include-dir",		required_argument,	NULL, R_DINCLUDE_OPT},
183210389Sgabor	{"after-context",	required_argument,	NULL, 'A'},
184210389Sgabor	{"text",		no_argument,		NULL, 'a'},
185210389Sgabor	{"before-context",	required_argument,	NULL, 'B'},
186210389Sgabor	{"byte-offset",		no_argument,		NULL, 'b'},
187210389Sgabor	{"context",		optional_argument,	NULL, 'C'},
188210389Sgabor	{"count",		no_argument,		NULL, 'c'},
189210389Sgabor	{"devices",		required_argument,	NULL, 'D'},
190210389Sgabor        {"directories",		required_argument,	NULL, 'd'},
191210389Sgabor	{"extended-regexp",	no_argument,		NULL, 'E'},
192210389Sgabor	{"regexp",		required_argument,	NULL, 'e'},
193210389Sgabor	{"fixed-strings",	no_argument,		NULL, 'F'},
194210389Sgabor	{"file",		required_argument,	NULL, 'f'},
195210389Sgabor	{"basic-regexp",	no_argument,		NULL, 'G'},
196210389Sgabor	{"no-filename",		no_argument,		NULL, 'h'},
197210389Sgabor	{"with-filename",	no_argument,		NULL, 'H'},
198210389Sgabor	{"ignore-case",		no_argument,		NULL, 'i'},
199210389Sgabor	{"bz2decompress",	no_argument,		NULL, 'J'},
200210389Sgabor	{"files-with-matches",	no_argument,		NULL, 'l'},
201210389Sgabor	{"files-without-match", no_argument,            NULL, 'L'},
202210389Sgabor	{"max-count",		required_argument,	NULL, 'm'},
203226261Sgabor	{"lzma",		no_argument,		NULL, 'M'},
204210389Sgabor	{"line-number",		no_argument,		NULL, 'n'},
205210389Sgabor	{"only-matching",	no_argument,		NULL, 'o'},
206210389Sgabor	{"quiet",		no_argument,		NULL, 'q'},
207210389Sgabor	{"silent",		no_argument,		NULL, 'q'},
208210389Sgabor	{"recursive",		no_argument,		NULL, 'r'},
209210389Sgabor	{"no-messages",		no_argument,		NULL, 's'},
210210389Sgabor	{"binary",		no_argument,		NULL, 'U'},
211210389Sgabor	{"unix-byte-offsets",	no_argument,		NULL, 'u'},
212210389Sgabor	{"invert-match",	no_argument,		NULL, 'v'},
213210389Sgabor	{"version",		no_argument,		NULL, 'V'},
214210389Sgabor	{"word-regexp",		no_argument,		NULL, 'w'},
215210389Sgabor	{"line-regexp",		no_argument,		NULL, 'x'},
216226261Sgabor	{"xz",			no_argument,		NULL, 'X'},
217210389Sgabor	{"decompress",          no_argument,            NULL, 'Z'},
218210389Sgabor	{NULL,			no_argument,		NULL, 0}
219210389Sgabor};
220210389Sgabor
221210389Sgabor/*
222210389Sgabor * Adds a searching pattern to the internal array.
223210389Sgabor */
224210389Sgaborstatic void
225210389Sgaboradd_pattern(char *pat, size_t len)
226210389Sgabor{
227210389Sgabor
228226261Sgabor	/* Do not add further pattern is we already match everything */
229226261Sgabor	if (matchall)
230226261Sgabor	  return;
231226261Sgabor
232210389Sgabor	/* Check if we can do a shortcut */
233226261Sgabor	if (len == 0) {
234210389Sgabor		matchall = true;
235226261Sgabor		for (unsigned int i = 0; i < patterns; i++) {
236226261Sgabor			free(pattern[i].pat);
237226261Sgabor		}
238226261Sgabor		pattern = grep_realloc(pattern, sizeof(struct pat));
239226261Sgabor		pattern[0].pat = NULL;
240226261Sgabor		pattern[0].len = 0;
241226261Sgabor		patterns = 1;
242210389Sgabor		return;
243210389Sgabor	}
244210389Sgabor	/* Increase size if necessary */
245210389Sgabor	if (patterns == pattern_sz) {
246210389Sgabor		pattern_sz *= 2;
247210389Sgabor		pattern = grep_realloc(pattern, ++pattern_sz *
248226261Sgabor		    sizeof(struct pat));
249210389Sgabor	}
250210389Sgabor	if (len > 0 && pat[len - 1] == '\n')
251210389Sgabor		--len;
252210389Sgabor	/* pat may not be NUL-terminated */
253226261Sgabor	pattern[patterns].pat = grep_malloc(len + 1);
254226261Sgabor	memcpy(pattern[patterns].pat, pat, len);
255226261Sgabor	pattern[patterns].len = len;
256226261Sgabor	pattern[patterns].pat[len] = '\0';
257210389Sgabor	++patterns;
258210389Sgabor}
259210389Sgabor
260210389Sgabor/*
261210578Sgabor * Adds a file include/exclude pattern to the internal array.
262210389Sgabor */
263210389Sgaborstatic void
264210578Sgaboradd_fpattern(const char *pat, int mode)
265210389Sgabor{
266210389Sgabor
267210389Sgabor	/* Increase size if necessary */
268210578Sgabor	if (fpatterns == fpattern_sz) {
269210578Sgabor		fpattern_sz *= 2;
270210578Sgabor		fpattern = grep_realloc(fpattern, ++fpattern_sz *
271210389Sgabor		    sizeof(struct epat));
272210389Sgabor	}
273210578Sgabor	fpattern[fpatterns].pat = grep_strdup(pat);
274210578Sgabor	fpattern[fpatterns].mode = mode;
275210578Sgabor	++fpatterns;
276210389Sgabor}
277210389Sgabor
278210389Sgabor/*
279210578Sgabor * Adds a directory include/exclude pattern to the internal array.
280210578Sgabor */
281210578Sgaborstatic void
282210578Sgaboradd_dpattern(const char *pat, int mode)
283210578Sgabor{
284210578Sgabor
285210578Sgabor	/* Increase size if necessary */
286210578Sgabor	if (dpatterns == dpattern_sz) {
287210578Sgabor		dpattern_sz *= 2;
288210578Sgabor		dpattern = grep_realloc(dpattern, ++dpattern_sz *
289210578Sgabor		    sizeof(struct epat));
290210578Sgabor	}
291210578Sgabor	dpattern[dpatterns].pat = grep_strdup(pat);
292210578Sgabor	dpattern[dpatterns].mode = mode;
293210578Sgabor	++dpatterns;
294210578Sgabor}
295210578Sgabor
296210578Sgabor/*
297210389Sgabor * Reads searching patterns from a file and adds them with add_pattern().
298210389Sgabor */
299210389Sgaborstatic void
300210389Sgaborread_patterns(const char *fn)
301210389Sgabor{
302226261Sgabor	struct stat st;
303210389Sgabor	FILE *f;
304210389Sgabor	char *line;
305210389Sgabor	size_t len;
306210389Sgabor
307210389Sgabor	if ((f = fopen(fn, "r")) == NULL)
308210389Sgabor		err(2, "%s", fn);
309226261Sgabor	if ((fstat(fileno(f), &st) == -1) || (S_ISDIR(st.st_mode))) {
310226261Sgabor		fclose(f);
311226261Sgabor		return;
312226261Sgabor	}
313265161Spfg	while ((line = fgetln(f, &len)) != NULL)
314226261Sgabor		add_pattern(line, line[0] == '\n' ? 0 : len);
315210389Sgabor	if (ferror(f))
316210389Sgabor		err(2, "%s", fn);
317210389Sgabor	fclose(f);
318210389Sgabor}
319210389Sgabor
320210461Sgaborstatic inline const char *
321210461Sgaborinit_color(const char *d)
322210461Sgabor{
323210461Sgabor	char *c;
324210461Sgabor
325210461Sgabor	c = getenv("GREP_COLOR");
326224937Sgabor	return (c != NULL && c[0] != '\0' ? c : d);
327210461Sgabor}
328210461Sgabor
329210389Sgaborint
330210389Sgabormain(int argc, char *argv[])
331210389Sgabor{
332210389Sgabor	char **aargv, **eargv, *eopts;
333226573Sgabor	char *ep;
334226573Sgabor	const char *pn;
335210389Sgabor	unsigned long long l;
336210389Sgabor	unsigned int aargc, eargc, i;
337210389Sgabor	int c, lastc, needpattern, newarg, prevoptind;
338210389Sgabor
339210389Sgabor	setlocale(LC_ALL, "");
340210389Sgabor
341210389Sgabor#ifndef WITHOUT_NLS
342210389Sgabor	catalog = catopen("grep", NL_CAT_LOCALE);
343210389Sgabor#endif
344210389Sgabor
345210389Sgabor	/* Check what is the program name of the binary.  In this
346210389Sgabor	   way we can have all the funcionalities in one binary
347210389Sgabor	   without the need of scripting and using ugly hacks. */
348226573Sgabor	pn = getprogname();
349226261Sgabor	if (pn[0] == 'b' && pn[1] == 'z') {
350226261Sgabor		filebehave = FILE_BZIP;
351226261Sgabor		pn += 2;
352226261Sgabor	} else if (pn[0] == 'x' && pn[1] == 'z') {
353226261Sgabor		filebehave = FILE_XZ;
354226261Sgabor		pn += 2;
355226261Sgabor	} else if (pn[0] == 'l' && pn[1] == 'z') {
356226261Sgabor		filebehave = FILE_LZMA;
357226261Sgabor		pn += 2;
358226261Sgabor	} else if (pn[0] == 'z') {
359226261Sgabor		filebehave = FILE_GZIP;
360226261Sgabor		pn += 1;
361226261Sgabor	}
362226261Sgabor	switch (pn[0]) {
363210389Sgabor	case 'e':
364210389Sgabor		grepbehave = GREP_EXTENDED;
365210389Sgabor		break;
366210389Sgabor	case 'f':
367210389Sgabor		grepbehave = GREP_FIXED;
368210389Sgabor		break;
369210389Sgabor	}
370210389Sgabor
371210389Sgabor	lastc = '\0';
372210389Sgabor	newarg = 1;
373210389Sgabor	prevoptind = 1;
374210389Sgabor	needpattern = 1;
375210389Sgabor
376210389Sgabor	eopts = getenv("GREP_OPTIONS");
377210389Sgabor
378211364Sgabor	/* support for extra arguments in GREP_OPTIONS */
379211364Sgabor	eargc = 0;
380224937Sgabor	if (eopts != NULL && eopts[0] != '\0') {
381210389Sgabor		char *str;
382210389Sgabor
383211364Sgabor		/* make an estimation of how many extra arguments we have */
384211364Sgabor		for (unsigned int j = 0; j < strlen(eopts); j++)
385211364Sgabor			if (eopts[j] == ' ')
386210389Sgabor				eargc++;
387210389Sgabor
388210389Sgabor		eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1));
389210389Sgabor
390210389Sgabor		eargc = 0;
391211364Sgabor		/* parse extra arguments */
392211364Sgabor		while ((str = strsep(&eopts, " ")) != NULL)
393224937Sgabor			if (str[0] != '\0')
394224937Sgabor				eargv[eargc++] = grep_strdup(str);
395210389Sgabor
396210430Sdelphij		aargv = (char **)grep_calloc(eargc + argc + 1,
397210430Sdelphij		    sizeof(char *));
398211364Sgabor
399210389Sgabor		aargv[0] = argv[0];
400211364Sgabor		for (i = 0; i < eargc; i++)
401211364Sgabor			aargv[i + 1] = eargv[i];
402211364Sgabor		for (int j = 1; j < argc; j++, i++)
403211364Sgabor			aargv[i + 1] = argv[j];
404210389Sgabor
405211364Sgabor		aargc = eargc + argc;
406210389Sgabor	} else {
407210389Sgabor		aargv = argv;
408210389Sgabor		aargc = argc;
409210389Sgabor	}
410210389Sgabor
411210389Sgabor	while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) !=
412210389Sgabor	    -1)) {
413210389Sgabor		switch (c) {
414210389Sgabor		case '0': case '1': case '2': case '3': case '4':
415210389Sgabor		case '5': case '6': case '7': case '8': case '9':
416210389Sgabor			if (newarg || !isdigit(lastc))
417210389Sgabor				Aflag = 0;
418210389Sgabor			else if (Aflag > LLONG_MAX / 10) {
419210389Sgabor				errno = ERANGE;
420210389Sgabor				err(2, NULL);
421210389Sgabor			}
422210389Sgabor			Aflag = Bflag = (Aflag * 10) + (c - '0');
423210389Sgabor			break;
424210389Sgabor		case 'C':
425210389Sgabor			if (optarg == NULL) {
426210389Sgabor				Aflag = Bflag = 2;
427210389Sgabor				break;
428210389Sgabor			}
429210389Sgabor			/* FALLTHROUGH */
430210389Sgabor		case 'A':
431210389Sgabor			/* FALLTHROUGH */
432210389Sgabor		case 'B':
433210389Sgabor			errno = 0;
434210389Sgabor			l = strtoull(optarg, &ep, 10);
435210389Sgabor			if (((errno == ERANGE) && (l == ULLONG_MAX)) ||
436210389Sgabor			    ((errno == EINVAL) && (l == 0)))
437210389Sgabor				err(2, NULL);
438210389Sgabor			else if (ep[0] != '\0') {
439210389Sgabor				errno = EINVAL;
440210389Sgabor				err(2, NULL);
441210389Sgabor			}
442210389Sgabor			if (c == 'A')
443210389Sgabor				Aflag = l;
444210389Sgabor			else if (c == 'B')
445210389Sgabor				Bflag = l;
446210389Sgabor			else
447210389Sgabor				Aflag = Bflag = l;
448210389Sgabor			break;
449210389Sgabor		case 'a':
450210389Sgabor			binbehave = BINFILE_TEXT;
451210389Sgabor			break;
452210389Sgabor		case 'b':
453210389Sgabor			bflag = true;
454210389Sgabor			break;
455210389Sgabor		case 'c':
456210389Sgabor			cflag = true;
457210389Sgabor			break;
458210389Sgabor		case 'D':
459210461Sgabor			if (strcasecmp(optarg, "skip") == 0)
460210389Sgabor				devbehave = DEV_SKIP;
461210461Sgabor			else if (strcasecmp(optarg, "read") == 0)
462210461Sgabor				devbehave = DEV_READ;
463210622Sgabor			else
464210622Sgabor				errx(2, getstr(3), "--devices");
465210389Sgabor			break;
466210389Sgabor		case 'd':
467210461Sgabor			if (strcasecmp("recurse", optarg) == 0) {
468210389Sgabor				Hflag = true;
469210389Sgabor				dirbehave = DIR_RECURSE;
470210461Sgabor			} else if (strcasecmp("skip", optarg) == 0)
471210389Sgabor				dirbehave = DIR_SKIP;
472210461Sgabor			else if (strcasecmp("read", optarg) == 0)
473210461Sgabor				dirbehave = DIR_READ;
474210622Sgabor			else
475210622Sgabor				errx(2, getstr(3), "--directories");
476210389Sgabor			break;
477210389Sgabor		case 'E':
478210389Sgabor			grepbehave = GREP_EXTENDED;
479210389Sgabor			break;
480210389Sgabor		case 'e':
481245996Sgabor			{
482245996Sgabor				char *token;
483245996Sgabor				char *string = optarg;
484245996Sgabor
485245996Sgabor				while ((token = strsep(&string, "\n")) != NULL)
486245996Sgabor					add_pattern(token, strlen(token));
487245996Sgabor			}
488210389Sgabor			needpattern = 0;
489210389Sgabor			break;
490210389Sgabor		case 'F':
491210389Sgabor			grepbehave = GREP_FIXED;
492210389Sgabor			break;
493210389Sgabor		case 'f':
494210389Sgabor			read_patterns(optarg);
495210389Sgabor			needpattern = 0;
496210389Sgabor			break;
497210389Sgabor		case 'G':
498210389Sgabor			grepbehave = GREP_BASIC;
499210389Sgabor			break;
500210389Sgabor		case 'H':
501210389Sgabor			Hflag = true;
502210389Sgabor			break;
503210389Sgabor		case 'h':
504210389Sgabor			Hflag = false;
505210389Sgabor			hflag = true;
506210389Sgabor			break;
507210389Sgabor		case 'I':
508210389Sgabor			binbehave = BINFILE_SKIP;
509210389Sgabor			break;
510210389Sgabor		case 'i':
511210389Sgabor		case 'y':
512210389Sgabor			iflag =  true;
513210389Sgabor			cflags |= REG_ICASE;
514210389Sgabor			break;
515210389Sgabor		case 'J':
516226573Sgabor#ifdef WITHOUT_BZIP2
517226573Sgabor			errno = EOPNOTSUPP;
518226573Sgabor			err(2, "bzip2 support was disabled at compile-time");
519226573Sgabor#endif
520210389Sgabor			filebehave = FILE_BZIP;
521210389Sgabor			break;
522210389Sgabor		case 'L':
523210389Sgabor			lflag = false;
524210461Sgabor			Lflag = true;
525210389Sgabor			break;
526210389Sgabor		case 'l':
527210389Sgabor			Lflag = false;
528210461Sgabor			lflag = true;
529210389Sgabor			break;
530210389Sgabor		case 'm':
531210389Sgabor			mflag = true;
532210389Sgabor			errno = 0;
533246279Seadler			mlimit = mcount = strtoll(optarg, &ep, 10);
534226261Sgabor			if (((errno == ERANGE) && (mcount == LLONG_MAX)) ||
535210389Sgabor			    ((errno == EINVAL) && (mcount == 0)))
536210389Sgabor				err(2, NULL);
537210389Sgabor			else if (ep[0] != '\0') {
538210389Sgabor				errno = EINVAL;
539210389Sgabor				err(2, NULL);
540210389Sgabor			}
541210389Sgabor			break;
542226261Sgabor		case 'M':
543226261Sgabor			filebehave = FILE_LZMA;
544226261Sgabor			break;
545210389Sgabor		case 'n':
546210389Sgabor			nflag = true;
547210389Sgabor			break;
548210389Sgabor		case 'O':
549210389Sgabor			linkbehave = LINK_EXPLICIT;
550210389Sgabor			break;
551210389Sgabor		case 'o':
552210389Sgabor			oflag = true;
553223009Sgabor			cflags &= ~REG_NOSUB;
554210389Sgabor			break;
555210389Sgabor		case 'p':
556210389Sgabor			linkbehave = LINK_SKIP;
557210389Sgabor			break;
558210389Sgabor		case 'q':
559210389Sgabor			qflag = true;
560210389Sgabor			break;
561210389Sgabor		case 'S':
562210461Sgabor			linkbehave = LINK_READ;
563210389Sgabor			break;
564210389Sgabor		case 'R':
565210389Sgabor		case 'r':
566210389Sgabor			dirbehave = DIR_RECURSE;
567210389Sgabor			Hflag = true;
568210389Sgabor			break;
569210389Sgabor		case 's':
570210389Sgabor			sflag = true;
571210389Sgabor			break;
572210389Sgabor		case 'U':
573210389Sgabor			binbehave = BINFILE_BIN;
574210389Sgabor			break;
575210389Sgabor		case 'u':
576210389Sgabor		case MMAP_OPT:
577226261Sgabor			filebehave = FILE_MMAP;
578210389Sgabor			break;
579210389Sgabor		case 'V':
580226573Sgabor			printf(getstr(9), getprogname(), VERSION);
581210389Sgabor			exit(0);
582210389Sgabor		case 'v':
583210389Sgabor			vflag = true;
584210389Sgabor			break;
585210389Sgabor		case 'w':
586210389Sgabor			wflag = true;
587223009Sgabor			cflags &= ~REG_NOSUB;
588210389Sgabor			break;
589210389Sgabor		case 'x':
590210389Sgabor			xflag = true;
591223009Sgabor			cflags &= ~REG_NOSUB;
592210389Sgabor			break;
593226261Sgabor		case 'X':
594226261Sgabor			filebehave = FILE_XZ;
595226261Sgabor			break;
596210389Sgabor		case 'Z':
597210389Sgabor			filebehave = FILE_GZIP;
598210389Sgabor			break;
599210389Sgabor		case BIN_OPT:
600210461Sgabor			if (strcasecmp("binary", optarg) == 0)
601210389Sgabor				binbehave = BINFILE_BIN;
602210461Sgabor			else if (strcasecmp("without-match", optarg) == 0)
603210389Sgabor				binbehave = BINFILE_SKIP;
604210461Sgabor			else if (strcasecmp("text", optarg) == 0)
605210389Sgabor				binbehave = BINFILE_TEXT;
606210389Sgabor			else
607210622Sgabor				errx(2, getstr(3), "--binary-files");
608210389Sgabor			break;
609210389Sgabor		case COLOR_OPT:
610210461Sgabor			color = NULL;
611210461Sgabor			if (optarg == NULL || strcasecmp("auto", optarg) == 0 ||
612210461Sgabor			    strcasecmp("tty", optarg) == 0 ||
613210461Sgabor			    strcasecmp("if-tty", optarg) == 0) {
614210461Sgabor				char *term;
615210461Sgabor
616210461Sgabor				term = getenv("TERM");
617210461Sgabor				if (isatty(STDOUT_FILENO) && term != NULL &&
618210461Sgabor				    strcasecmp(term, "dumb") != 0)
619210461Sgabor					color = init_color("01;31");
620210461Sgabor			} else if (strcasecmp("always", optarg) == 0 ||
621210461Sgabor			    strcasecmp("yes", optarg) == 0 ||
622210461Sgabor			    strcasecmp("force", optarg) == 0) {
623210461Sgabor				color = init_color("01;31");
624210461Sgabor			} else if (strcasecmp("never", optarg) != 0 &&
625210461Sgabor			    strcasecmp("none", optarg) != 0 &&
626210461Sgabor			    strcasecmp("no", optarg) != 0)
627210622Sgabor				errx(2, getstr(3), "--color");
628223009Sgabor			cflags &= ~REG_NOSUB;
629210389Sgabor			break;
630210389Sgabor		case LABEL_OPT:
631210389Sgabor			label = optarg;
632210389Sgabor			break;
633210389Sgabor		case LINEBUF_OPT:
634210389Sgabor			lbflag = true;
635210389Sgabor			break;
636210389Sgabor		case NULL_OPT:
637210389Sgabor			nullflag = true;
638210389Sgabor			break;
639210389Sgabor		case R_INCLUDE_OPT:
640210578Sgabor			finclude = true;
641210578Sgabor			add_fpattern(optarg, INCL_PAT);
642210389Sgabor			break;
643210389Sgabor		case R_EXCLUDE_OPT:
644210578Sgabor			fexclude = true;
645210578Sgabor			add_fpattern(optarg, EXCL_PAT);
646210389Sgabor			break;
647210389Sgabor		case R_DINCLUDE_OPT:
648211364Sgabor			dinclude = true;
649210578Sgabor			add_dpattern(optarg, INCL_PAT);
650210389Sgabor			break;
651210389Sgabor		case R_DEXCLUDE_OPT:
652211364Sgabor			dexclude = true;
653210578Sgabor			add_dpattern(optarg, EXCL_PAT);
654210389Sgabor			break;
655210389Sgabor		case HELP_OPT:
656210389Sgabor		default:
657210389Sgabor			usage();
658210389Sgabor		}
659210389Sgabor		lastc = c;
660210389Sgabor		newarg = optind != prevoptind;
661210389Sgabor		prevoptind = optind;
662210389Sgabor	}
663210389Sgabor	aargc -= optind;
664210389Sgabor	aargv += optind;
665210389Sgabor
666226261Sgabor	/* Empty pattern file matches nothing */
667226261Sgabor	if (!needpattern && (patterns == 0))
668226261Sgabor		exit(1);
669226261Sgabor
670210389Sgabor	/* Fail if we don't have any pattern */
671210389Sgabor	if (aargc == 0 && needpattern)
672210389Sgabor		usage();
673210389Sgabor
674210389Sgabor	/* Process patterns from command line */
675210389Sgabor	if (aargc != 0 && needpattern) {
676245996Sgabor		char *token;
677245996Sgabor		char *string = *aargv;
678245996Sgabor
679245996Sgabor		while ((token = strsep(&string, "\n")) != NULL)
680245996Sgabor			add_pattern(token, strlen(token));
681210389Sgabor		--aargc;
682210389Sgabor		++aargv;
683210389Sgabor	}
684210389Sgabor
685210389Sgabor	switch (grepbehave) {
686210389Sgabor	case GREP_BASIC:
687210389Sgabor		break;
688226261Sgabor	case GREP_FIXED:
689226261Sgabor		/* XXX: header mess, REG_LITERAL not defined in gnu/regex.h */
690226261Sgabor		cflags |= 0020;
691226261Sgabor		break;
692210389Sgabor	case GREP_EXTENDED:
693210389Sgabor		cflags |= REG_EXTENDED;
694210389Sgabor		break;
695210389Sgabor	default:
696210389Sgabor		/* NOTREACHED */
697210389Sgabor		usage();
698210389Sgabor	}
699210389Sgabor
700210389Sgabor	fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern));
701210389Sgabor	r_pattern = grep_calloc(patterns, sizeof(*r_pattern));
702226261Sgabor
703226261Sgabor	/* Check if cheating is allowed (always is for fgrep). */
704226261Sgabor	for (i = 0; i < patterns; ++i) {
705226261Sgabor		if (fastncomp(&fg_pattern[i], pattern[i].pat,
706226261Sgabor		    pattern[i].len, cflags) != 0) {
707226261Sgabor			/* Fall back to full regex library */
708226261Sgabor			c = regcomp(&r_pattern[i], pattern[i].pat, cflags);
709226261Sgabor			if (c != 0) {
710226261Sgabor				regerror(c, &r_pattern[i], re_error,
711226261Sgabor				    RE_ERROR_BUF);
712226261Sgabor				errx(2, "%s", re_error);
713210389Sgabor			}
714210389Sgabor		}
715210389Sgabor	}
716210389Sgabor
717210389Sgabor	if (lbflag)
718210389Sgabor		setlinebuf(stdout);
719210389Sgabor
720210389Sgabor	if ((aargc == 0 || aargc == 1) && !Hflag)
721210389Sgabor		hflag = true;
722210389Sgabor
723210389Sgabor	if (aargc == 0)
724210389Sgabor		exit(!procfile("-"));
725210389Sgabor
726210389Sgabor	if (dirbehave == DIR_RECURSE)
727210389Sgabor		c = grep_tree(aargv);
728211519Sdelphij	else
729210578Sgabor		for (c = 0; aargc--; ++aargv) {
730210578Sgabor			if ((finclude || fexclude) && !file_matching(*aargv))
731210578Sgabor				continue;
732210389Sgabor			c+= procfile(*aargv);
733210578Sgabor		}
734210389Sgabor
735210389Sgabor#ifndef WITHOUT_NLS
736210389Sgabor	catclose(catalog);
737210389Sgabor#endif
738210389Sgabor
739210389Sgabor	/* Find out the correct return value according to the
740210389Sgabor	   results and the command line option. */
741229081Sgabor	exit(c ? (file_err ? (qflag ? 0 : 2) : 0) : (file_err ? 2 : 1));
742210389Sgabor}
743