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