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