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