grep.c revision 268966
1/*	$NetBSD: grep.c,v 1.6 2011/04/18 03:48:23 joerg Exp $	*/
2/* 	$FreeBSD: stable/10/usr.bin/grep/grep.c 268966 2014-07-21 22:59:40Z pfg $	*/
3/*	$OpenBSD: grep.c,v 1.42 2010/07/02 22:18:03 tedu Exp $	*/
4
5/*-
6 * Copyright (c) 1999 James Howard and Dag-Erling Co��dan Sm��rgrav
7 * Copyright (C) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org>
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33__FBSDID("$FreeBSD: stable/10/usr.bin/grep/grep.c 268966 2014-07-21 22:59:40Z pfg $");
34
35#include <sys/stat.h>
36#include <sys/types.h>
37
38#include <ctype.h>
39#include <err.h>
40#include <errno.h>
41#include <fcntl.h>
42#include <getopt.h>
43#include <limits.h>
44#include <libgen.h>
45#include <locale.h>
46#include <stdbool.h>
47#define _WITH_GETLINE
48#include <stdio.h>
49#include <stdlib.h>
50#include <string.h>
51#include <unistd.h>
52
53#include "fastmatch.h"
54#include "grep.h"
55
56#ifndef WITHOUT_NLS
57#include <nl_types.h>
58nl_catd	 catalog;
59#endif
60
61/*
62 * Default messags to use when NLS is disabled or no catalogue
63 * is found.
64 */
65const char	*errstr[] = {
66	"",
67/* 1*/	"(standard input)",
68/* 2*/	"cannot read bzip2 compressed file",
69/* 3*/	"unknown %s option",
70/* 4*/	"usage: %s [-abcDEFGHhIiJLlmnOoPqRSsUVvwxZ] [-A num] [-B num] [-C[num]]\n",
71/* 5*/	"\t[-e pattern] [-f file] [--binary-files=value] [--color=when]\n",
72/* 6*/	"\t[--context[=num]] [--directories=action] [--label] [--line-buffered]\n",
73/* 7*/	"\t[--null] [pattern] [file ...]\n",
74/* 8*/	"Binary file %s matches\n",
75/* 9*/	"%s (BSD grep) %s\n",
76};
77
78/* Flags passed to regcomp() and regexec() */
79int		 cflags = REG_NOSUB;
80int		 eflags = REG_STARTEND;
81
82/* Shortcut for matching all cases like empty regex */
83bool		 matchall;
84
85/* Searching patterns */
86unsigned int	 patterns;
87static unsigned int pattern_sz;
88struct pat	*pattern;
89regex_t		*r_pattern;
90fastmatch_t	*fg_pattern;
91
92/* Filename exclusion/inclusion patterns */
93unsigned int	fpatterns, dpatterns;
94static unsigned int fpattern_sz, dpattern_sz;
95struct epat	*dpattern, *fpattern;
96
97/* For regex errors  */
98char	 re_error[RE_ERROR_BUF + 1];
99
100/* Command-line flags */
101unsigned long long Aflag;	/* -A x: print x lines trailing each match */
102unsigned long long Bflag;	/* -B x: print x lines leading each match */
103bool	 Hflag;		/* -H: always print file name */
104bool	 Lflag;		/* -L: only show names of files with no matches */
105bool	 bflag;		/* -b: show block numbers for each match */
106bool	 cflag;		/* -c: only show a count of matching lines */
107bool	 hflag;		/* -h: don't print filename headers */
108bool	 iflag;		/* -i: ignore case */
109bool	 lflag;		/* -l: only show names of files with matches */
110bool	 mflag;		/* -m x: stop reading the files after x matches */
111long long mcount;	/* count for -m */
112long long mlimit;	/* requested value for -m */
113bool	 nflag;		/* -n: show line numbers in front of matching lines */
114bool	 oflag;		/* -o: print only matching part */
115bool	 qflag;		/* -q: quiet mode (don't output anything) */
116bool	 sflag;		/* -s: silent mode (ignore errors) */
117bool	 vflag;		/* -v: only show non-matching lines */
118bool	 wflag;		/* -w: pattern must start and end on word boundaries */
119bool	 xflag;		/* -x: pattern must match entire line */
120bool	 lbflag;	/* --line-buffered */
121bool	 nullflag;	/* --null */
122char	*label;		/* --label */
123const char *color;	/* --color */
124int	 grepbehave = GREP_BASIC;	/* -EFGP: type of the regex */
125int	 binbehave = BINFILE_BIN;	/* -aIU: handling of binary files */
126int	 filebehave = FILE_STDIO;	/* -JZ: normal, gzip or bzip2 file */
127int	 devbehave = DEV_READ;		/* -D: handling of devices */
128int	 dirbehave = DIR_READ;		/* -dRr: handling of directories */
129int	 linkbehave = LINK_READ;	/* -OpS: handling of symlinks */
130
131bool	 dexclude, dinclude;	/* --exclude-dir and --include-dir */
132bool	 fexclude, finclude;	/* --exclude and --include */
133
134enum {
135	BIN_OPT = CHAR_MAX + 1,
136	COLOR_OPT,
137	HELP_OPT,
138	MMAP_OPT,
139	LINEBUF_OPT,
140	LABEL_OPT,
141	NULL_OPT,
142	R_EXCLUDE_OPT,
143	R_INCLUDE_OPT,
144	R_DEXCLUDE_OPT,
145	R_DINCLUDE_OPT
146};
147
148static inline const char	*init_color(const char *);
149
150/* Housekeeping */
151bool	 first = true;	/* flag whether we are processing the first match */
152bool	 prev;		/* flag whether or not the previous line matched */
153int	 tail;		/* lines left to print */
154bool	 file_err;	/* file reading error */
155
156/*
157 * Prints usage information and returns 2.
158 */
159static void
160usage(void)
161{
162	fprintf(stderr, getstr(4), getprogname());
163	fprintf(stderr, "%s", getstr(5));
164	fprintf(stderr, "%s", getstr(6));
165	fprintf(stderr, "%s", getstr(7));
166	exit(2);
167}
168
169static const char	*optstr = "0123456789A:B:C:D:EFGHIJMLOPSRUVZabcd:e:f:hilm:nopqrsuvwxXy";
170
171static const struct option long_options[] =
172{
173	{"binary-files",	required_argument,	NULL, BIN_OPT},
174	{"help",		no_argument,		NULL, HELP_OPT},
175	{"mmap",		no_argument,		NULL, MMAP_OPT},
176	{"line-buffered",	no_argument,		NULL, LINEBUF_OPT},
177	{"label",		required_argument,	NULL, LABEL_OPT},
178	{"null",		no_argument,		NULL, NULL_OPT},
179	{"color",		optional_argument,	NULL, COLOR_OPT},
180	{"colour",		optional_argument,	NULL, COLOR_OPT},
181	{"exclude",		required_argument,	NULL, R_EXCLUDE_OPT},
182	{"include",		required_argument,	NULL, R_INCLUDE_OPT},
183	{"exclude-dir",		required_argument,	NULL, R_DEXCLUDE_OPT},
184	{"include-dir",		required_argument,	NULL, R_DINCLUDE_OPT},
185	{"after-context",	required_argument,	NULL, 'A'},
186	{"text",		no_argument,		NULL, 'a'},
187	{"before-context",	required_argument,	NULL, 'B'},
188	{"byte-offset",		no_argument,		NULL, 'b'},
189	{"context",		optional_argument,	NULL, 'C'},
190	{"count",		no_argument,		NULL, 'c'},
191	{"devices",		required_argument,	NULL, 'D'},
192        {"directories",		required_argument,	NULL, 'd'},
193	{"extended-regexp",	no_argument,		NULL, 'E'},
194	{"regexp",		required_argument,	NULL, 'e'},
195	{"fixed-strings",	no_argument,		NULL, 'F'},
196	{"file",		required_argument,	NULL, 'f'},
197	{"basic-regexp",	no_argument,		NULL, 'G'},
198	{"no-filename",		no_argument,		NULL, 'h'},
199	{"with-filename",	no_argument,		NULL, 'H'},
200	{"ignore-case",		no_argument,		NULL, 'i'},
201	{"bz2decompress",	no_argument,		NULL, 'J'},
202	{"files-with-matches",	no_argument,		NULL, 'l'},
203	{"files-without-match", no_argument,            NULL, 'L'},
204	{"max-count",		required_argument,	NULL, 'm'},
205	{"lzma",		no_argument,		NULL, 'M'},
206	{"line-number",		no_argument,		NULL, 'n'},
207	{"only-matching",	no_argument,		NULL, 'o'},
208	{"quiet",		no_argument,		NULL, 'q'},
209	{"silent",		no_argument,		NULL, 'q'},
210	{"recursive",		no_argument,		NULL, 'r'},
211	{"no-messages",		no_argument,		NULL, 's'},
212	{"binary",		no_argument,		NULL, 'U'},
213	{"unix-byte-offsets",	no_argument,		NULL, 'u'},
214	{"invert-match",	no_argument,		NULL, 'v'},
215	{"version",		no_argument,		NULL, 'V'},
216	{"word-regexp",		no_argument,		NULL, 'w'},
217	{"line-regexp",		no_argument,		NULL, 'x'},
218	{"xz",			no_argument,		NULL, 'X'},
219	{"decompress",          no_argument,            NULL, 'Z'},
220	{NULL,			no_argument,		NULL, 0}
221};
222
223/*
224 * Adds a searching pattern to the internal array.
225 */
226static void
227add_pattern(char *pat, size_t len)
228{
229
230	/* Do not add further pattern is we already match everything */
231	if (matchall)
232	  return;
233
234	/* Check if we can do a shortcut */
235	if (len == 0) {
236		matchall = true;
237		for (unsigned int i = 0; i < patterns; i++) {
238			free(pattern[i].pat);
239		}
240		pattern = grep_realloc(pattern, sizeof(struct pat));
241		pattern[0].pat = NULL;
242		pattern[0].len = 0;
243		patterns = 1;
244		return;
245	}
246	/* Increase size if necessary */
247	if (patterns == pattern_sz) {
248		pattern_sz *= 2;
249		pattern = grep_realloc(pattern, ++pattern_sz *
250		    sizeof(struct pat));
251	}
252	if (len > 0 && pat[len - 1] == '\n')
253		--len;
254	/* pat may not be NUL-terminated */
255	pattern[patterns].pat = grep_malloc(len + 1);
256	memcpy(pattern[patterns].pat, pat, len);
257	pattern[patterns].len = len;
258	pattern[patterns].pat[len] = '\0';
259	++patterns;
260}
261
262/*
263 * Adds a file include/exclude pattern to the internal array.
264 */
265static void
266add_fpattern(const char *pat, int mode)
267{
268
269	/* Increase size if necessary */
270	if (fpatterns == fpattern_sz) {
271		fpattern_sz *= 2;
272		fpattern = grep_realloc(fpattern, ++fpattern_sz *
273		    sizeof(struct epat));
274	}
275	fpattern[fpatterns].pat = grep_strdup(pat);
276	fpattern[fpatterns].mode = mode;
277	++fpatterns;
278}
279
280/*
281 * Adds a directory include/exclude pattern to the internal array.
282 */
283static void
284add_dpattern(const char *pat, int mode)
285{
286
287	/* Increase size if necessary */
288	if (dpatterns == dpattern_sz) {
289		dpattern_sz *= 2;
290		dpattern = grep_realloc(dpattern, ++dpattern_sz *
291		    sizeof(struct epat));
292	}
293	dpattern[dpatterns].pat = grep_strdup(pat);
294	dpattern[dpatterns].mode = mode;
295	++dpatterns;
296}
297
298/*
299 * Reads searching patterns from a file and adds them with add_pattern().
300 */
301static void
302read_patterns(const char *fn)
303{
304	struct stat st;
305	FILE *f;
306	char *line;
307	size_t len;
308	ssize_t rlen;
309
310	if ((f = fopen(fn, "r")) == NULL)
311		err(2, "%s", fn);
312	if ((fstat(fileno(f), &st) == -1) || (S_ISDIR(st.st_mode))) {
313		fclose(f);
314		return;
315	}
316	len = 0;
317	line = NULL;
318	while ((rlen = getline(&line, &len, f)) != -1)
319		add_pattern(line, line[0] == '\n' ? 0 : len);
320	free(line);
321	if (ferror(f))
322		err(2, "%s", fn);
323	fclose(f);
324}
325
326static inline const char *
327init_color(const char *d)
328{
329	char *c;
330
331	c = getenv("GREP_COLOR");
332	return (c != NULL && c[0] != '\0' ? c : d);
333}
334
335int
336main(int argc, char *argv[])
337{
338	char **aargv, **eargv, *eopts;
339	char *ep;
340	const char *pn;
341	unsigned long long l;
342	unsigned int aargc, eargc, i;
343	int c, lastc, needpattern, newarg, prevoptind;
344
345	setlocale(LC_ALL, "");
346
347#ifndef WITHOUT_NLS
348	catalog = catopen("grep", NL_CAT_LOCALE);
349#endif
350
351	/* Check what is the program name of the binary.  In this
352	   way we can have all the funcionalities in one binary
353	   without the need of scripting and using ugly hacks. */
354	pn = getprogname();
355	if (pn[0] == 'b' && pn[1] == 'z') {
356		filebehave = FILE_BZIP;
357		pn += 2;
358	} else if (pn[0] == 'x' && pn[1] == 'z') {
359		filebehave = FILE_XZ;
360		pn += 2;
361	} else if (pn[0] == 'l' && pn[1] == 'z') {
362		filebehave = FILE_LZMA;
363		pn += 2;
364	} else if (pn[0] == 'z') {
365		filebehave = FILE_GZIP;
366		pn += 1;
367	}
368	switch (pn[0]) {
369	case 'e':
370		grepbehave = GREP_EXTENDED;
371		break;
372	case 'f':
373		grepbehave = GREP_FIXED;
374		break;
375	}
376
377	lastc = '\0';
378	newarg = 1;
379	prevoptind = 1;
380	needpattern = 1;
381
382	eopts = getenv("GREP_OPTIONS");
383
384	/* support for extra arguments in GREP_OPTIONS */
385	eargc = 0;
386	if (eopts != NULL && eopts[0] != '\0') {
387		char *str;
388
389		/* make an estimation of how many extra arguments we have */
390		for (unsigned int j = 0; j < strlen(eopts); j++)
391			if (eopts[j] == ' ')
392				eargc++;
393
394		eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1));
395
396		eargc = 0;
397		/* parse extra arguments */
398		while ((str = strsep(&eopts, " ")) != NULL)
399			if (str[0] != '\0')
400				eargv[eargc++] = grep_strdup(str);
401
402		aargv = (char **)grep_calloc(eargc + argc + 1,
403		    sizeof(char *));
404
405		aargv[0] = argv[0];
406		for (i = 0; i < eargc; i++)
407			aargv[i + 1] = eargv[i];
408		for (int j = 1; j < argc; j++, i++)
409			aargv[i + 1] = argv[j];
410
411		aargc = eargc + argc;
412	} else {
413		aargv = argv;
414		aargc = argc;
415	}
416
417	while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) !=
418	    -1)) {
419		switch (c) {
420		case '0': case '1': case '2': case '3': case '4':
421		case '5': case '6': case '7': case '8': case '9':
422			if (newarg || !isdigit(lastc))
423				Aflag = 0;
424			else if (Aflag > LLONG_MAX / 10) {
425				errno = ERANGE;
426				err(2, NULL);
427			}
428			Aflag = Bflag = (Aflag * 10) + (c - '0');
429			break;
430		case 'C':
431			if (optarg == NULL) {
432				Aflag = Bflag = 2;
433				break;
434			}
435			/* FALLTHROUGH */
436		case 'A':
437			/* FALLTHROUGH */
438		case 'B':
439			errno = 0;
440			l = strtoull(optarg, &ep, 10);
441			if (((errno == ERANGE) && (l == ULLONG_MAX)) ||
442			    ((errno == EINVAL) && (l == 0)))
443				err(2, NULL);
444			else if (ep[0] != '\0') {
445				errno = EINVAL;
446				err(2, NULL);
447			}
448			if (c == 'A')
449				Aflag = l;
450			else if (c == 'B')
451				Bflag = l;
452			else
453				Aflag = Bflag = l;
454			break;
455		case 'a':
456			binbehave = BINFILE_TEXT;
457			break;
458		case 'b':
459			bflag = true;
460			break;
461		case 'c':
462			cflag = true;
463			break;
464		case 'D':
465			if (strcasecmp(optarg, "skip") == 0)
466				devbehave = DEV_SKIP;
467			else if (strcasecmp(optarg, "read") == 0)
468				devbehave = DEV_READ;
469			else
470				errx(2, getstr(3), "--devices");
471			break;
472		case 'd':
473			if (strcasecmp("recurse", optarg) == 0) {
474				Hflag = true;
475				dirbehave = DIR_RECURSE;
476			} else if (strcasecmp("skip", optarg) == 0)
477				dirbehave = DIR_SKIP;
478			else if (strcasecmp("read", optarg) == 0)
479				dirbehave = DIR_READ;
480			else
481				errx(2, getstr(3), "--directories");
482			break;
483		case 'E':
484			grepbehave = GREP_EXTENDED;
485			break;
486		case 'e':
487			{
488				char *token;
489				char *string = optarg;
490
491				while ((token = strsep(&string, "\n")) != NULL)
492					add_pattern(token, strlen(token));
493			}
494			needpattern = 0;
495			break;
496		case 'F':
497			grepbehave = GREP_FIXED;
498			break;
499		case 'f':
500			read_patterns(optarg);
501			needpattern = 0;
502			break;
503		case 'G':
504			grepbehave = GREP_BASIC;
505			break;
506		case 'H':
507			Hflag = true;
508			break;
509		case 'h':
510			Hflag = false;
511			hflag = true;
512			break;
513		case 'I':
514			binbehave = BINFILE_SKIP;
515			break;
516		case 'i':
517		case 'y':
518			iflag =  true;
519			cflags |= REG_ICASE;
520			break;
521		case 'J':
522#ifdef WITHOUT_BZIP2
523			errno = EOPNOTSUPP;
524			err(2, "bzip2 support was disabled at compile-time");
525#endif
526			filebehave = FILE_BZIP;
527			break;
528		case 'L':
529			lflag = false;
530			Lflag = true;
531			break;
532		case 'l':
533			Lflag = false;
534			lflag = true;
535			break;
536		case 'm':
537			mflag = true;
538			errno = 0;
539			mlimit = mcount = strtoll(optarg, &ep, 10);
540			if (((errno == ERANGE) && (mcount == LLONG_MAX)) ||
541			    ((errno == EINVAL) && (mcount == 0)))
542				err(2, NULL);
543			else if (ep[0] != '\0') {
544				errno = EINVAL;
545				err(2, NULL);
546			}
547			break;
548		case 'M':
549			filebehave = FILE_LZMA;
550			break;
551		case 'n':
552			nflag = true;
553			break;
554		case 'O':
555			linkbehave = LINK_EXPLICIT;
556			break;
557		case 'o':
558			oflag = true;
559			cflags &= ~REG_NOSUB;
560			break;
561		case 'p':
562			linkbehave = LINK_SKIP;
563			break;
564		case 'q':
565			qflag = true;
566			break;
567		case 'S':
568			linkbehave = LINK_READ;
569			break;
570		case 'R':
571		case 'r':
572			dirbehave = DIR_RECURSE;
573			Hflag = true;
574			break;
575		case 's':
576			sflag = true;
577			break;
578		case 'U':
579			binbehave = BINFILE_BIN;
580			break;
581		case 'u':
582		case MMAP_OPT:
583			filebehave = FILE_MMAP;
584			break;
585		case 'V':
586			printf(getstr(9), getprogname(), VERSION);
587			exit(0);
588		case 'v':
589			vflag = true;
590			break;
591		case 'w':
592			wflag = true;
593			cflags &= ~REG_NOSUB;
594			break;
595		case 'x':
596			xflag = true;
597			cflags &= ~REG_NOSUB;
598			break;
599		case 'X':
600			filebehave = FILE_XZ;
601			break;
602		case 'Z':
603			filebehave = FILE_GZIP;
604			break;
605		case BIN_OPT:
606			if (strcasecmp("binary", optarg) == 0)
607				binbehave = BINFILE_BIN;
608			else if (strcasecmp("without-match", optarg) == 0)
609				binbehave = BINFILE_SKIP;
610			else if (strcasecmp("text", optarg) == 0)
611				binbehave = BINFILE_TEXT;
612			else
613				errx(2, getstr(3), "--binary-files");
614			break;
615		case COLOR_OPT:
616			color = NULL;
617			if (optarg == NULL || strcasecmp("auto", optarg) == 0 ||
618			    strcasecmp("tty", optarg) == 0 ||
619			    strcasecmp("if-tty", optarg) == 0) {
620				char *term;
621
622				term = getenv("TERM");
623				if (isatty(STDOUT_FILENO) && term != NULL &&
624				    strcasecmp(term, "dumb") != 0)
625					color = init_color("01;31");
626			} else if (strcasecmp("always", optarg) == 0 ||
627			    strcasecmp("yes", optarg) == 0 ||
628			    strcasecmp("force", optarg) == 0) {
629				color = init_color("01;31");
630			} else if (strcasecmp("never", optarg) != 0 &&
631			    strcasecmp("none", optarg) != 0 &&
632			    strcasecmp("no", optarg) != 0)
633				errx(2, getstr(3), "--color");
634			cflags &= ~REG_NOSUB;
635			break;
636		case LABEL_OPT:
637			label = optarg;
638			break;
639		case LINEBUF_OPT:
640			lbflag = true;
641			break;
642		case NULL_OPT:
643			nullflag = true;
644			break;
645		case R_INCLUDE_OPT:
646			finclude = true;
647			add_fpattern(optarg, INCL_PAT);
648			break;
649		case R_EXCLUDE_OPT:
650			fexclude = true;
651			add_fpattern(optarg, EXCL_PAT);
652			break;
653		case R_DINCLUDE_OPT:
654			dinclude = true;
655			add_dpattern(optarg, INCL_PAT);
656			break;
657		case R_DEXCLUDE_OPT:
658			dexclude = true;
659			add_dpattern(optarg, EXCL_PAT);
660			break;
661		case HELP_OPT:
662		default:
663			usage();
664		}
665		lastc = c;
666		newarg = optind != prevoptind;
667		prevoptind = optind;
668	}
669	aargc -= optind;
670	aargv += optind;
671
672	/* Empty pattern file matches nothing */
673	if (!needpattern && (patterns == 0))
674		exit(1);
675
676	/* Fail if we don't have any pattern */
677	if (aargc == 0 && needpattern)
678		usage();
679
680	/* Process patterns from command line */
681	if (aargc != 0 && needpattern) {
682		char *token;
683		char *string = *aargv;
684
685		while ((token = strsep(&string, "\n")) != NULL)
686			add_pattern(token, strlen(token));
687		--aargc;
688		++aargv;
689	}
690
691	switch (grepbehave) {
692	case GREP_BASIC:
693		break;
694	case GREP_FIXED:
695		/* XXX: header mess, REG_LITERAL not defined in gnu/regex.h */
696		cflags |= 0020;
697		break;
698	case GREP_EXTENDED:
699		cflags |= REG_EXTENDED;
700		break;
701	default:
702		/* NOTREACHED */
703		usage();
704	}
705
706	fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern));
707	r_pattern = grep_calloc(patterns, sizeof(*r_pattern));
708
709	/* Check if cheating is allowed (always is for fgrep). */
710	for (i = 0; i < patterns; ++i) {
711		if (fastncomp(&fg_pattern[i], pattern[i].pat,
712		    pattern[i].len, cflags) != 0) {
713			/* Fall back to full regex library */
714			c = regcomp(&r_pattern[i], pattern[i].pat, cflags);
715			if (c != 0) {
716				regerror(c, &r_pattern[i], re_error,
717				    RE_ERROR_BUF);
718				errx(2, "%s", re_error);
719			}
720		}
721	}
722
723	if (lbflag)
724		setlinebuf(stdout);
725
726	if ((aargc == 0 || aargc == 1) && !Hflag)
727		hflag = true;
728
729	if (aargc == 0)
730		exit(!procfile("-"));
731
732	if (dirbehave == DIR_RECURSE)
733		c = grep_tree(aargv);
734	else
735		for (c = 0; aargc--; ++aargv) {
736			if ((finclude || fexclude) && !file_matching(*aargv))
737				continue;
738			c+= procfile(*aargv);
739		}
740
741#ifndef WITHOUT_NLS
742	catclose(catalog);
743#endif
744
745	/* Find out the correct return value according to the
746	   results and the command line option. */
747	exit(c ? (file_err ? (qflag ? 0 : 2) : 0) : (file_err ? 2 : 1));
748}
749