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