xref: /freebsd/usr.bin/grep/grep.c (revision 42249ef2)
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 	/*
347 	 * Check how we've bene invoked to determine the behavior we should
348 	 * exhibit. In this way we can have all the functionalities in one
349 	 * binary without the need of scripting and using ugly hacks.
350 	 */
351 	pn = getprogname();
352 	switch (pn[0]) {
353 	case 'e':
354 		grepbehave = GREP_EXTENDED;
355 		break;
356 	case 'f':
357 		grepbehave = GREP_FIXED;
358 		break;
359 	case 'r':
360 		dirbehave = DIR_RECURSE;
361 		Hflag = true;
362 		break;
363 	}
364 
365 	lastc = '\0';
366 	newarg = 1;
367 	prevoptind = 1;
368 	needpattern = 1;
369 	fileeol = '\n';
370 
371 	eopts = getenv("GREP_OPTIONS");
372 
373 	/* support for extra arguments in GREP_OPTIONS */
374 	eargc = 0;
375 	if (eopts != NULL && eopts[0] != '\0') {
376 		char *str;
377 
378 		/* make an estimation of how many extra arguments we have */
379 		for (unsigned int j = 0; j < strlen(eopts); j++)
380 			if (eopts[j] == ' ')
381 				eargc++;
382 
383 		eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1));
384 
385 		eargc = 0;
386 		/* parse extra arguments */
387 		while ((str = strsep(&eopts, " ")) != NULL)
388 			if (str[0] != '\0')
389 				eargv[eargc++] = grep_strdup(str);
390 
391 		aargv = (char **)grep_calloc(eargc + argc + 1,
392 		    sizeof(char *));
393 
394 		aargv[0] = argv[0];
395 		for (i = 0; i < eargc; i++)
396 			aargv[i + 1] = eargv[i];
397 		for (int j = 1; j < argc; j++, i++)
398 			aargv[i + 1] = argv[j];
399 
400 		aargc = eargc + argc;
401 	} else {
402 		aargv = argv;
403 		aargc = argc;
404 	}
405 
406 	while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) !=
407 	    -1)) {
408 		switch (c) {
409 		case '0': case '1': case '2': case '3': case '4':
410 		case '5': case '6': case '7': case '8': case '9':
411 			if (newarg || !isdigit(lastc))
412 				Aflag = 0;
413 			else if (Aflag > LLONG_MAX / 10 - 1) {
414 				errno = ERANGE;
415 				err(2, NULL);
416 			}
417 
418 			Aflag = Bflag = (Aflag * 10) + (c - '0');
419 			break;
420 		case 'C':
421 			if (optarg == NULL) {
422 				Aflag = Bflag = 2;
423 				break;
424 			}
425 			/* FALLTHROUGH */
426 		case 'A':
427 			/* FALLTHROUGH */
428 		case 'B':
429 			errno = 0;
430 			l = strtoll(optarg, &ep, 10);
431 			if (errno == ERANGE || errno == EINVAL)
432 				err(2, NULL);
433 			else if (ep[0] != '\0') {
434 				errno = EINVAL;
435 				err(2, NULL);
436 			} else if (l < 0) {
437 				errno = EINVAL;
438 				err(2, "context argument must be non-negative");
439 			}
440 
441 			if (c == 'A')
442 				Aflag = l;
443 			else if (c == 'B')
444 				Bflag = l;
445 			else
446 				Aflag = Bflag = l;
447 			break;
448 		case 'a':
449 			binbehave = BINFILE_TEXT;
450 			break;
451 		case 'b':
452 			bflag = true;
453 			break;
454 		case 'c':
455 			cflag = true;
456 			break;
457 		case 'D':
458 			if (strcasecmp(optarg, "skip") == 0)
459 				devbehave = DEV_SKIP;
460 			else if (strcasecmp(optarg, "read") == 0)
461 				devbehave = DEV_READ;
462 			else
463 				errx(2, errstr[2], "--devices");
464 			break;
465 		case 'd':
466 			if (strcasecmp("recurse", optarg) == 0) {
467 				Hflag = true;
468 				dirbehave = DIR_RECURSE;
469 			} else if (strcasecmp("skip", optarg) == 0)
470 				dirbehave = DIR_SKIP;
471 			else if (strcasecmp("read", optarg) == 0)
472 				dirbehave = DIR_READ;
473 			else
474 				errx(2, errstr[2], "--directories");
475 			break;
476 		case 'E':
477 			grepbehave = GREP_EXTENDED;
478 			break;
479 		case 'e':
480 			{
481 				char *token;
482 				char *string = optarg;
483 
484 				while ((token = strsep(&string, "\n")) != NULL)
485 					add_pattern(token, strlen(token));
486 			}
487 			needpattern = 0;
488 			break;
489 		case 'F':
490 			grepbehave = GREP_FIXED;
491 			break;
492 		case 'f':
493 			read_patterns(optarg);
494 			needpattern = 0;
495 			break;
496 		case 'G':
497 			grepbehave = GREP_BASIC;
498 			break;
499 		case 'H':
500 			Hflag = true;
501 			break;
502 		case 'h':
503 			Hflag = false;
504 			hflag = true;
505 			break;
506 		case 'I':
507 			binbehave = BINFILE_SKIP;
508 			break;
509 		case 'i':
510 		case 'y':
511 			iflag =  true;
512 			cflags |= REG_ICASE;
513 			break;
514 		case 'L':
515 			lflag = false;
516 			Lflag = true;
517 			break;
518 		case 'l':
519 			Lflag = false;
520 			lflag = true;
521 			break;
522 		case 'm':
523 			mflag = true;
524 			errno = 0;
525 			mlimit = mcount = strtoll(optarg, &ep, 10);
526 			if (((errno == ERANGE) && (mcount == LLONG_MAX)) ||
527 			    ((errno == EINVAL) && (mcount == 0)))
528 				err(2, NULL);
529 			else if (ep[0] != '\0') {
530 				errno = EINVAL;
531 				err(2, NULL);
532 			}
533 			break;
534 		case 'n':
535 			nflag = true;
536 			break;
537 		case 'O':
538 			linkbehave = LINK_EXPLICIT;
539 			break;
540 		case 'o':
541 			oflag = true;
542 			cflags &= ~REG_NOSUB;
543 			break;
544 		case 'p':
545 			linkbehave = LINK_SKIP;
546 			break;
547 		case 'q':
548 			qflag = true;
549 			break;
550 		case 'S':
551 			linkbehave = LINK_READ;
552 			break;
553 		case 'R':
554 		case 'r':
555 			dirbehave = DIR_RECURSE;
556 			Hflag = true;
557 			break;
558 		case 's':
559 			sflag = true;
560 			break;
561 		case 'U':
562 			binbehave = BINFILE_BIN;
563 			break;
564 		case 'u':
565 		case MMAP_OPT:
566 			filebehave = FILE_MMAP;
567 			break;
568 		case 'V':
569 #ifdef WITH_GNU
570 			printf(errstr[9], getprogname(), VERSION);
571 #else
572 			printf(errstr[8], getprogname(), VERSION);
573 #endif
574 			exit(0);
575 		case 'v':
576 			vflag = true;
577 			break;
578 		case 'w':
579 			wflag = true;
580 			cflags &= ~REG_NOSUB;
581 			break;
582 		case 'x':
583 			xflag = true;
584 			cflags &= ~REG_NOSUB;
585 			break;
586 		case 'z':
587 			fileeol = '\0';
588 			break;
589 		case BIN_OPT:
590 			if (strcasecmp("binary", optarg) == 0)
591 				binbehave = BINFILE_BIN;
592 			else if (strcasecmp("without-match", optarg) == 0)
593 				binbehave = BINFILE_SKIP;
594 			else if (strcasecmp("text", optarg) == 0)
595 				binbehave = BINFILE_TEXT;
596 			else
597 				errx(2, errstr[2], "--binary-files");
598 			break;
599 		case COLOR_OPT:
600 			color = NULL;
601 			if (optarg == NULL || strcasecmp("auto", optarg) == 0 ||
602 			    strcasecmp("tty", optarg) == 0 ||
603 			    strcasecmp("if-tty", optarg) == 0) {
604 				char *term;
605 
606 				term = getenv("TERM");
607 				if (isatty(STDOUT_FILENO) && term != NULL &&
608 				    strcasecmp(term, "dumb") != 0)
609 					color = init_color("01;31");
610 			} else if (strcasecmp("always", optarg) == 0 ||
611 			    strcasecmp("yes", optarg) == 0 ||
612 			    strcasecmp("force", optarg) == 0) {
613 				color = init_color("01;31");
614 			} else if (strcasecmp("never", optarg) != 0 &&
615 			    strcasecmp("none", optarg) != 0 &&
616 			    strcasecmp("no", optarg) != 0)
617 				errx(2, errstr[2], "--color");
618 			cflags &= ~REG_NOSUB;
619 			break;
620 		case LABEL_OPT:
621 			label = optarg;
622 			break;
623 		case LINEBUF_OPT:
624 			lbflag = true;
625 			break;
626 		case NULL_OPT:
627 			nullflag = true;
628 			break;
629 		case R_INCLUDE_OPT:
630 			finclude = true;
631 			add_fpattern(optarg, INCL_PAT);
632 			break;
633 		case R_EXCLUDE_OPT:
634 			fexclude = true;
635 			add_fpattern(optarg, EXCL_PAT);
636 			break;
637 		case R_DINCLUDE_OPT:
638 			dinclude = true;
639 			add_dpattern(optarg, INCL_PAT);
640 			break;
641 		case R_DEXCLUDE_OPT:
642 			dexclude = true;
643 			add_dpattern(optarg, EXCL_PAT);
644 			break;
645 		case HELP_OPT:
646 		default:
647 			usage();
648 		}
649 		lastc = c;
650 		newarg = optind != prevoptind;
651 		prevoptind = optind;
652 	}
653 	aargc -= optind;
654 	aargv += optind;
655 
656 	/* Empty pattern file matches nothing */
657 	if (!needpattern && (patterns == 0))
658 		exit(1);
659 
660 	/* Fail if we don't have any pattern */
661 	if (aargc == 0 && needpattern)
662 		usage();
663 
664 	/* Process patterns from command line */
665 	if (aargc != 0 && needpattern) {
666 		char *token;
667 		char *string = *aargv;
668 
669 		while ((token = strsep(&string, "\n")) != NULL)
670 			add_pattern(token, strlen(token));
671 		--aargc;
672 		++aargv;
673 	}
674 
675 	switch (grepbehave) {
676 	case GREP_BASIC:
677 		break;
678 	case GREP_FIXED:
679 		/*
680 		 * regex(3) implementations that support fixed-string searches generally
681 		 * define either REG_NOSPEC or REG_LITERAL. Set the appropriate flag
682 		 * here. If neither are defined, GREP_FIXED later implies that the
683 		 * internal literal matcher should be used. Other cflags that have
684 		 * the same interpretation as REG_NOSPEC and REG_LITERAL should be
685 		 * similarly added here, and grep.h should be amended to take this into
686 		 * consideration when defining WITH_INTERNAL_NOSPEC.
687 		 */
688 #if defined(REG_NOSPEC)
689 		cflags |= REG_NOSPEC;
690 #elif defined(REG_LITERAL)
691 		cflags |= REG_LITERAL;
692 #endif
693 		break;
694 	case GREP_EXTENDED:
695 		cflags |= REG_EXTENDED;
696 		break;
697 	default:
698 		/* NOTREACHED */
699 		usage();
700 	}
701 
702 	r_pattern = grep_calloc(patterns, sizeof(*r_pattern));
703 
704 	/* Don't process any patterns if we have a blank one */
705 #ifdef WITH_INTERNAL_NOSPEC
706 	if (!matchall && grepbehave != GREP_FIXED) {
707 #else
708 	if (!matchall) {
709 #endif
710 		/* Check if cheating is allowed (always is for fgrep). */
711 		for (i = 0; i < patterns; ++i) {
712 			c = regcomp(&r_pattern[i], pattern[i].pat, cflags);
713 			if (c != 0) {
714 				regerror(c, &r_pattern[i], re_error,
715 				    RE_ERROR_BUF);
716 				errx(2, "%s", re_error);
717 			}
718 		}
719 	}
720 
721 	if (lbflag)
722 		setlinebuf(stdout);
723 
724 	if ((aargc == 0 || aargc == 1) && !Hflag)
725 		hflag = true;
726 
727 	if (aargc == 0 && dirbehave != DIR_RECURSE)
728 		exit(!procfile("-"));
729 
730 	if (dirbehave == DIR_RECURSE)
731 		matched = grep_tree(aargv);
732 	else
733 		for (matched = false; aargc--; ++aargv) {
734 			if ((finclude || fexclude) && !file_matching(*aargv))
735 				continue;
736 			if (procfile(*aargv))
737 				matched = true;
738 		}
739 
740 	/* Find out the correct return value according to the
741 	   results and the command line option. */
742 	exit(matched ? (file_err ? (qflag ? 0 : 2) : 0) : (file_err ? 2 : 1));
743 }
744