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