1 /*        $NetBSD: grep.c,v 1.16 2022/10/15 21:53:22 andvar 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.16 2022/10/15 21:53:22 andvar 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 messages 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 functionalities 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