]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/grep/grep.c
MFV: file 5.33
[FreeBSD/FreeBSD.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  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
7  *
8  * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
9  * Copyright (C) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org>
10  * All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
36
37 #include <sys/stat.h>
38 #include <sys/types.h>
39
40 #include <ctype.h>
41 #include <err.h>
42 #include <errno.h>
43 #include <fcntl.h>
44 #include <getopt.h>
45 #include <limits.h>
46 #include <libgen.h>
47 #include <locale.h>
48 #include <stdbool.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <unistd.h>
53
54 #include "grep.h"
55
56 #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*/  "unknown %s option",
69 /* 3*/  "usage: %s [-abcDEFGHhIiLlmnOoPqRSsUVvwxz] [-A num] [-B num] [-C[num]]\n",
70 /* 4*/  "\t[-e pattern] [-f file] [--binary-files=value] [--color=when]\n",
71 /* 5*/  "\t[--context[=num]] [--directories=action] [--label] [--line-buffered]\n",
72 /* 6*/  "\t[--null] [pattern] [file ...]\n",
73 /* 7*/  "Binary file %s matches\n",
74 /* 8*/  "%s (BSD grep) %s\n",
75 /* 9*/  "%s (BSD grep, GNU compatible) %s\n",
76 };
77
78 /* Flags passed to regcomp() and regexec() */
79 int              cflags = REG_NOSUB | REG_NEWLINE;
80 int              eflags = REG_STARTEND;
81
82 /* XXX TODO: Get rid of this flag.
83  * matchall is a gross hack that means that an empty pattern was passed to us.
84  * It is a necessary evil at the moment because our regex(3) implementation
85  * does not allow for empty patterns, as supported by POSIX's definition of
86  * grammar for BREs/EREs. When libregex becomes available, it would be wise
87  * to remove this and let regex(3) handle the dirty details of empty patterns.
88  */
89 bool             matchall;
90
91 /* Searching patterns */
92 unsigned int     patterns;
93 static unsigned int pattern_sz;
94 struct pat      *pattern;
95 regex_t         *r_pattern;
96
97 /* Filename exclusion/inclusion patterns */
98 unsigned int    fpatterns, dpatterns;
99 static unsigned int fpattern_sz, dpattern_sz;
100 struct epat     *dpattern, *fpattern;
101
102 /* For regex errors  */
103 char     re_error[RE_ERROR_BUF + 1];
104
105 /* Command-line flags */
106 long long Aflag;        /* -A x: print x lines trailing each match */
107 long long Bflag;        /* -B x: print x lines leading each match */
108 bool     Hflag;         /* -H: always print file name */
109 bool     Lflag;         /* -L: only show names of files with no matches */
110 bool     bflag;         /* -b: show block numbers for each match */
111 bool     cflag;         /* -c: only show a count of matching lines */
112 bool     hflag;         /* -h: don't print filename headers */
113 bool     iflag;         /* -i: ignore case */
114 bool     lflag;         /* -l: only show names of files with matches */
115 bool     mflag;         /* -m x: stop reading the files after x matches */
116 long long mcount;       /* count for -m */
117 long long mlimit;       /* requested value for -m */
118 char     fileeol;       /* indicator for eol */
119 bool     nflag;         /* -n: show line numbers in front of matching lines */
120 bool     oflag;         /* -o: print only matching part */
121 bool     qflag;         /* -q: quiet mode (don't output anything) */
122 bool     sflag;         /* -s: silent mode (ignore errors) */
123 bool     vflag;         /* -v: only show non-matching lines */
124 bool     wflag;         /* -w: pattern must start and end on word boundaries */
125 bool     xflag;         /* -x: pattern must match entire line */
126 bool     lbflag;        /* --line-buffered */
127 bool     nullflag;      /* --null */
128 char    *label;         /* --label */
129 const char *color;      /* --color */
130 int      grepbehave = GREP_BASIC;       /* -EFGP: type of the regex */
131 int      binbehave = BINFILE_BIN;       /* -aIU: handling of binary files */
132 int      filebehave = FILE_STDIO;
133 int      devbehave = DEV_READ;          /* -D: handling of devices */
134 int      dirbehave = DIR_READ;          /* -dRr: handling of directories */
135 int      linkbehave = LINK_READ;        /* -OpS: handling of symlinks */
136
137 bool     dexclude, dinclude;    /* --exclude-dir and --include-dir */
138 bool     fexclude, finclude;    /* --exclude and --include */
139
140 enum {
141         BIN_OPT = CHAR_MAX + 1,
142         COLOR_OPT,
143         HELP_OPT,
144         MMAP_OPT,
145         LINEBUF_OPT,
146         LABEL_OPT,
147         NULL_OPT,
148         R_EXCLUDE_OPT,
149         R_INCLUDE_OPT,
150         R_DEXCLUDE_OPT,
151         R_DINCLUDE_OPT
152 };
153
154 static inline const char        *init_color(const char *);
155
156 /* Housekeeping */
157 bool     file_err;      /* file reading error */
158
159 /*
160  * Prints usage information and returns 2.
161  */
162 static void
163 usage(void)
164 {
165         fprintf(stderr, getstr(3), getprogname());
166         fprintf(stderr, "%s", getstr(4));
167         fprintf(stderr, "%s", getstr(5));
168         fprintf(stderr, "%s", getstr(6));
169         exit(2);
170 }
171
172 static const char       *optstr = "0123456789A:B:C:D:EFGHILOPSRUVabcd:e:f:hilm:nopqrsuvwxyz";
173
174 static const struct option long_options[] =
175 {
176         {"binary-files",        required_argument,      NULL, BIN_OPT},
177         {"help",                no_argument,            NULL, HELP_OPT},
178         {"mmap",                no_argument,            NULL, MMAP_OPT},
179         {"line-buffered",       no_argument,            NULL, LINEBUF_OPT},
180         {"label",               required_argument,      NULL, LABEL_OPT},
181         {"null",                no_argument,            NULL, NULL_OPT},
182         {"color",               optional_argument,      NULL, COLOR_OPT},
183         {"colour",              optional_argument,      NULL, COLOR_OPT},
184         {"exclude",             required_argument,      NULL, R_EXCLUDE_OPT},
185         {"include",             required_argument,      NULL, R_INCLUDE_OPT},
186         {"exclude-dir",         required_argument,      NULL, R_DEXCLUDE_OPT},
187         {"include-dir",         required_argument,      NULL, R_DINCLUDE_OPT},
188         {"after-context",       required_argument,      NULL, 'A'},
189         {"text",                no_argument,            NULL, 'a'},
190         {"before-context",      required_argument,      NULL, 'B'},
191         {"byte-offset",         no_argument,            NULL, 'b'},
192         {"context",             optional_argument,      NULL, 'C'},
193         {"count",               no_argument,            NULL, 'c'},
194         {"devices",             required_argument,      NULL, 'D'},
195         {"directories",         required_argument,      NULL, 'd'},
196         {"extended-regexp",     no_argument,            NULL, 'E'},
197         {"regexp",              required_argument,      NULL, 'e'},
198         {"fixed-strings",       no_argument,            NULL, 'F'},
199         {"file",                required_argument,      NULL, 'f'},
200         {"basic-regexp",        no_argument,            NULL, 'G'},
201         {"no-filename",         no_argument,            NULL, 'h'},
202         {"with-filename",       no_argument,            NULL, 'H'},
203         {"ignore-case",         no_argument,            NULL, 'i'},
204         {"files-with-matches",  no_argument,            NULL, 'l'},
205         {"files-without-match", no_argument,            NULL, 'L'},
206         {"max-count",           required_argument,      NULL, 'm'},
207         {"line-number",         no_argument,            NULL, 'n'},
208         {"only-matching",       no_argument,            NULL, 'o'},
209         {"quiet",               no_argument,            NULL, 'q'},
210         {"silent",              no_argument,            NULL, 'q'},
211         {"recursive",           no_argument,            NULL, 'r'},
212         {"no-messages",         no_argument,            NULL, 's'},
213         {"binary",              no_argument,            NULL, 'U'},
214         {"unix-byte-offsets",   no_argument,            NULL, 'u'},
215         {"invert-match",        no_argument,            NULL, 'v'},
216         {"version",             no_argument,            NULL, 'V'},
217         {"word-regexp",         no_argument,            NULL, 'w'},
218         {"line-regexp",         no_argument,            NULL, 'x'},
219         {"null-data",           no_argument,            NULL, 'z'},
220         {NULL,                  no_argument,            NULL, 0}
221 };
222
223 /*
224  * Adds a searching pattern to the internal array.
225  */
226 static void
227 add_pattern(char *pat, size_t len)
228 {
229
230         /* Do not add further pattern is we already match everything */
231         if (matchall)
232           return;
233
234         /* Check if we can do a shortcut */
235         if (len == 0) {
236                 matchall = true;
237                 for (unsigned int i = 0; i < patterns; i++) {
238                         free(pattern[i].pat);
239                 }
240                 pattern = grep_realloc(pattern, sizeof(struct pat));
241                 pattern[0].pat = NULL;
242                 pattern[0].len = 0;
243                 patterns = 1;
244                 return;
245         }
246         /* Increase size if necessary */
247         if (patterns == pattern_sz) {
248                 pattern_sz *= 2;
249                 pattern = grep_realloc(pattern, ++pattern_sz *
250                     sizeof(struct pat));
251         }
252         if (len > 0 && pat[len - 1] == '\n')
253                 --len;
254         /* pat may not be NUL-terminated */
255         pattern[patterns].pat = grep_malloc(len + 1);
256         memcpy(pattern[patterns].pat, pat, len);
257         pattern[patterns].len = len;
258         pattern[patterns].pat[len] = '\0';
259         ++patterns;
260 }
261
262 /*
263  * Adds a file include/exclude pattern to the internal array.
264  */
265 static void
266 add_fpattern(const char *pat, int mode)
267 {
268
269         /* Increase size if necessary */
270         if (fpatterns == fpattern_sz) {
271                 fpattern_sz *= 2;
272                 fpattern = grep_realloc(fpattern, ++fpattern_sz *
273                     sizeof(struct epat));
274         }
275         fpattern[fpatterns].pat = grep_strdup(pat);
276         fpattern[fpatterns].mode = mode;
277         ++fpatterns;
278 }
279
280 /*
281  * Adds a directory include/exclude pattern to the internal array.
282  */
283 static void
284 add_dpattern(const char *pat, int mode)
285 {
286
287         /* Increase size if necessary */
288         if (dpatterns == dpattern_sz) {
289                 dpattern_sz *= 2;
290                 dpattern = grep_realloc(dpattern, ++dpattern_sz *
291                     sizeof(struct epat));
292         }
293         dpattern[dpatterns].pat = grep_strdup(pat);
294         dpattern[dpatterns].mode = mode;
295         ++dpatterns;
296 }
297
298 /*
299  * Reads searching patterns from a file and adds them with add_pattern().
300  */
301 static void
302 read_patterns(const char *fn)
303 {
304         struct stat st;
305         FILE *f;
306         char *line;
307         size_t len;
308         ssize_t rlen;
309
310         if (strcmp(fn, "-") == 0)
311                 f = stdin;
312         else if ((f = fopen(fn, "r")) == NULL)
313                 err(2, "%s", fn);
314         if ((fstat(fileno(f), &st) == -1) || (S_ISDIR(st.st_mode))) {
315                 fclose(f);
316                 return;
317         }
318         len = 0;
319         line = NULL;
320         while ((rlen = getline(&line, &len, f)) != -1) {
321                 if (line[0] == '\0')
322                         continue;
323                 add_pattern(line, line[0] == '\n' ? 0 : (size_t)rlen);
324         }
325
326         free(line);
327         if (ferror(f))
328                 err(2, "%s", fn);
329         if (strcmp(fn, "-") != 0)
330                 fclose(f);
331 }
332
333 static inline const char *
334 init_color(const char *d)
335 {
336         char *c;
337
338         c = getenv("GREP_COLOR");
339         return (c != NULL && c[0] != '\0' ? c : d);
340 }
341
342 int
343 main(int argc, char *argv[])
344 {
345         char **aargv, **eargv, *eopts;
346         char *ep;
347         const char *pn;
348         long long l;
349         unsigned int aargc, eargc, i;
350         int c, lastc, needpattern, newarg, prevoptind;
351
352         setlocale(LC_ALL, "");
353
354 #ifndef WITHOUT_NLS
355         catalog = catopen("grep", NL_CAT_LOCALE);
356 #endif
357
358         /* Check what is the program name of the binary.  In this
359            way we can have all the funcionalities in one binary
360            without the need of scripting and using ugly hacks. */
361         pn = getprogname();
362         if (pn[0] == 'r') {
363                 dirbehave = DIR_RECURSE;
364                 Hflag = true;
365         }
366         switch (pn[0]) {
367         case 'e':
368                 grepbehave = GREP_EXTENDED;
369                 break;
370         case 'f':
371                 grepbehave = GREP_FIXED;
372                 break;
373         }
374
375         lastc = '\0';
376         newarg = 1;
377         prevoptind = 1;
378         needpattern = 1;
379         fileeol = '\n';
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 - 1) {
424                                 errno = ERANGE;
425                                 err(2, NULL);
426                         }
427
428                         Aflag = Bflag = (Aflag * 10) + (c - '0');
429                         break;
430                 case 'C':
431                         if (optarg == NULL) {
432                                 Aflag = Bflag = 2;
433                                 break;
434                         }
435                         /* FALLTHROUGH */
436                 case 'A':
437                         /* FALLTHROUGH */
438                 case 'B':
439                         errno = 0;
440                         l = strtoll(optarg, &ep, 10);
441                         if (errno == ERANGE || errno == EINVAL)
442                                 err(2, NULL);
443                         else if (ep[0] != '\0') {
444                                 errno = EINVAL;
445                                 err(2, NULL);
446                         } else if (l < 0) {
447                                 errno = EINVAL;
448                                 err(2, "context argument must be non-negative");
449                         }
450
451                         if (c == 'A')
452                                 Aflag = l;
453                         else if (c == 'B')
454                                 Bflag = l;
455                         else
456                                 Aflag = Bflag = l;
457                         break;
458                 case 'a':
459                         binbehave = BINFILE_TEXT;
460                         break;
461                 case 'b':
462                         bflag = true;
463                         break;
464                 case 'c':
465                         cflag = true;
466                         break;
467                 case 'D':
468                         if (strcasecmp(optarg, "skip") == 0)
469                                 devbehave = DEV_SKIP;
470                         else if (strcasecmp(optarg, "read") == 0)
471                                 devbehave = DEV_READ;
472                         else
473                                 errx(2, getstr(2), "--devices");
474                         break;
475                 case 'd':
476                         if (strcasecmp("recurse", optarg) == 0) {
477                                 Hflag = true;
478                                 dirbehave = DIR_RECURSE;
479                         } else if (strcasecmp("skip", optarg) == 0)
480                                 dirbehave = DIR_SKIP;
481                         else if (strcasecmp("read", optarg) == 0)
482                                 dirbehave = DIR_READ;
483                         else
484                                 errx(2, getstr(2), "--directories");
485                         break;
486                 case 'E':
487                         grepbehave = GREP_EXTENDED;
488                         break;
489                 case 'e':
490                         {
491                                 char *token;
492                                 char *string = optarg;
493
494                                 while ((token = strsep(&string, "\n")) != NULL)
495                                         add_pattern(token, strlen(token));
496                         }
497                         needpattern = 0;
498                         break;
499                 case 'F':
500                         grepbehave = GREP_FIXED;
501                         break;
502                 case 'f':
503                         read_patterns(optarg);
504                         needpattern = 0;
505                         break;
506                 case 'G':
507                         grepbehave = GREP_BASIC;
508                         break;
509                 case 'H':
510                         Hflag = true;
511                         break;
512                 case 'h':
513                         Hflag = false;
514                         hflag = true;
515                         break;
516                 case 'I':
517                         binbehave = BINFILE_SKIP;
518                         break;
519                 case 'i':
520                 case 'y':
521                         iflag =  true;
522                         cflags |= REG_ICASE;
523                         break;
524                 case 'L':
525                         lflag = false;
526                         Lflag = true;
527                         break;
528                 case 'l':
529                         Lflag = false;
530                         lflag = true;
531                         break;
532                 case 'm':
533                         mflag = true;
534                         errno = 0;
535                         mlimit = mcount = strtoll(optarg, &ep, 10);
536                         if (((errno == ERANGE) && (mcount == LLONG_MAX)) ||
537                             ((errno == EINVAL) && (mcount == 0)))
538                                 err(2, NULL);
539                         else if (ep[0] != '\0') {
540                                 errno = EINVAL;
541                                 err(2, NULL);
542                         }
543                         break;
544                 case 'n':
545                         nflag = true;
546                         break;
547                 case 'O':
548                         linkbehave = LINK_EXPLICIT;
549                         break;
550                 case 'o':
551                         oflag = true;
552                         cflags &= ~REG_NOSUB;
553                         break;
554                 case 'p':
555                         linkbehave = LINK_SKIP;
556                         break;
557                 case 'q':
558                         qflag = true;
559                         break;
560                 case 'S':
561                         linkbehave = LINK_READ;
562                         break;
563                 case 'R':
564                 case 'r':
565                         dirbehave = DIR_RECURSE;
566                         Hflag = true;
567                         break;
568                 case 's':
569                         sflag = true;
570                         break;
571                 case 'U':
572                         binbehave = BINFILE_BIN;
573                         break;
574                 case 'u':
575                 case MMAP_OPT:
576                         filebehave = FILE_MMAP;
577                         break;
578                 case 'V':
579 #ifdef WITH_GNU
580                         printf(getstr(9), getprogname(), VERSION);
581 #else
582                         printf(getstr(8), getprogname(), VERSION);
583 #endif
584                         exit(0);
585                 case 'v':
586                         vflag = true;
587                         break;
588                 case 'w':
589                         wflag = true;
590                         cflags &= ~REG_NOSUB;
591                         break;
592                 case 'x':
593                         xflag = true;
594                         cflags &= ~REG_NOSUB;
595                         break;
596                 case 'z':
597                         fileeol = '\0';
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(2), "--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(2), "--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                 /*
690                  * regex(3) implementations that support fixed-string searches generally
691                  * define either REG_NOSPEC or REG_LITERAL. Set the appropriate flag
692                  * here. If neither are defined, GREP_FIXED later implies that the
693                  * internal literal matcher should be used. Other cflags that have
694                  * the same interpretation as REG_NOSPEC and REG_LITERAL should be
695                  * similarly added here, and grep.h should be amended to take this into
696                  * consideration when defining WITH_INTERNAL_NOSPEC.
697                  */
698 #if defined(REG_NOSPEC)
699                 cflags |= REG_NOSPEC;
700 #elif defined(REG_LITERAL)
701                 cflags |= REG_LITERAL;
702 #endif
703                 break;
704         case GREP_EXTENDED:
705                 cflags |= REG_EXTENDED;
706                 break;
707         default:
708                 /* NOTREACHED */
709                 usage();
710         }
711
712         r_pattern = grep_calloc(patterns, sizeof(*r_pattern));
713
714         /* Don't process any patterns if we have a blank one */
715 #ifdef WITH_INTERNAL_NOSPEC
716         if (!matchall && grepbehave != GREP_FIXED) {
717 #else
718         if (!matchall) {
719 #endif
720                 /* Check if cheating is allowed (always is for fgrep). */
721                 for (i = 0; i < patterns; ++i) {
722                         c = regcomp(&r_pattern[i], pattern[i].pat, cflags);
723                         if (c != 0) {
724                                 regerror(c, &r_pattern[i], re_error,
725                                     RE_ERROR_BUF);
726                                 errx(2, "%s", re_error);
727                         }
728                 }
729         }
730
731         if (lbflag)
732                 setlinebuf(stdout);
733
734         if ((aargc == 0 || aargc == 1) && !Hflag)
735                 hflag = true;
736
737         if (aargc == 0 && dirbehave != DIR_RECURSE)
738                 exit(!procfile("-"));
739
740         if (dirbehave == DIR_RECURSE)
741                 c = grep_tree(aargv);
742         else
743                 for (c = 0; aargc--; ++aargv) {
744                         if ((finclude || fexclude) && !file_matching(*aargv))
745                                 continue;
746                         c+= procfile(*aargv);
747                 }
748
749 #ifndef WITHOUT_NLS
750         catclose(catalog);
751 #endif
752
753         /* Find out the correct return value according to the
754            results and the command line option. */
755         exit(c ? (file_err ? (qflag ? 0 : 2) : 0) : (file_err ? 2 : 1));
756 }