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