1 /* $NetBSD: grep.c,v 1.15 2018/08/12 09:03:21 christos Exp $ */
2 /* $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>
37 __RCSID("$NetBSD: grep.c,v 1.15 2018/08/12 09:03:21 christos Exp $");
38
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>
59 nl_catd catalog;
60 #endif
61
62 /*
63 * Default messags to use when NLS is disabled or no catalogue
64 * is found.
65 */
66 const 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() */
80 int cflags = 0;
81 int eflags = REG_STARTEND;
82
83 /* Searching patterns */
84 unsigned int patterns, pattern_sz;
85 char **pattern;
86 regex_t *r_pattern;
87 fastgrep_t *fg_pattern;
88
89 /* Filename exclusion/inclusion patterns */
90 unsigned int fpatterns, fpattern_sz;
91 unsigned int dpatterns, dpattern_sz;
92 struct epat *dpattern, *fpattern;
93
94 /* For regex errors */
95 char re_error[RE_ERROR_BUF + 1];
96
97 /* Command-line flags */
98 unsigned long long Aflag; /* -A x: print x lines trailing each match */
99 unsigned long long Bflag; /* -B x: print x lines leading each match */
100 bool Hflag; /* -H: always print file name */
101 bool Lflag; /* -L: only show names of files with no matches */
102 bool bflag; /* -b: show block numbers for each match */
103 bool cflag; /* -c: only show a count of matching lines */
104 bool hflag; /* -h: don't print filename headers */
105 bool iflag; /* -i: ignore case */
106 bool lflag; /* -l: only show names of files with matches */
107 bool mflag; /* -m x: stop reading the files after x matches */
108 unsigned long long mcount; /* count for -m */
109 bool nflag; /* -n: show line numbers in front of matching lines */
110 bool oflag; /* -o: print only matching part */
111 bool qflag; /* -q: quiet mode (don't output anything) */
112 bool sflag; /* -s: silent mode (ignore errors) */
113 bool vflag; /* -v: only show non-matching lines */
114 bool wflag; /* -w: pattern must start and end on word boundaries */
115 bool xflag; /* -x: pattern must match entire line */
116 bool lbflag; /* --line-buffered */
117 bool nullflag; /* --null */
118 bool nulldataflag; /* --null-data */
119 unsigned char line_sep = '\n'; /* 0 for --null-data */
120 char *label; /* --label */
121 const char *color; /* --color */
122 int grepbehave = GREP_BASIC; /* -EFGP: type of the regex */
123 int binbehave = BINFILE_BIN; /* -aIU: handling of binary files */
124 int filebehave = FILE_STDIO; /* -JZ: normal, gzip or bzip2 file */
125 int devbehave = DEV_READ; /* -D: handling of devices */
126 int dirbehave = DIR_READ; /* -dRr: handling of directories */
127 int linkbehave = LINK_READ; /* -OpS: handling of symlinks */
128
129 bool dexclude, dinclude; /* --exclude-dir and --include-dir */
130 bool fexclude, finclude; /* --exclude and --include */
131
132 enum {
133 BIN_OPT = CHAR_MAX + 1,
134 COLOR_OPT,
135 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
146 static inline const char *init_color(const char *);
147
148 /* Housekeeping */
149 int tail; /* lines left to print */
150 bool notfound; /* file not found */
151
152 extern char *__progname;
153
154 /*
155 * Prints usage information and returns 2.
156 */
157 __dead static void
usage(void)158 usage(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
167 static const char optstr[] =
168 "0123456789A:B:C:D:EFGHIJLOPSRUVZabcd:e:f:hilm:nopqrsuvwxyz";
169
170 struct option long_options[] =
171 {
172 {"binary-files", required_argument, NULL, BIN_OPT},
173 #ifndef WITHOUT_GZIP
174 {"decompress", no_argument, NULL, DECOMPRESS_OPT},
175 #endif
176 {"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'},
202 #ifndef WITHOUT_BZ2
203 {"bz2decompress", no_argument, NULL, 'J'},
204 #endif
205 {"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 */
228 static void
add_pattern(char * pat,size_t len)229 add_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 */
252 static void
add_fpattern(const char * pat,int mode)253 add_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 */
270 static void
add_dpattern(const char * pat,int mode)271 add_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 */
288 static void
read_patterns(const char * fn)289 read_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
308 static inline const char *
init_color(const char * d)309 init_color(const char *d)
310 {
311 char *c;
312
313 c = getenv("GREP_COLOR");
314 return (c != NULL ? c : d);
315 }
316
317 int
main(int argc,char * argv[])318 main(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;
345 #ifndef WITHOUT_GZIP
346 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;
360 #endif
361 }
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;
500 #ifndef WITHOUT_BZ2
501 case 'J':
502 filebehave = FILE_BZIP;
503 break;
504 #endif
505 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;
607 #ifndef WITHOUT_GZIP
608 case DECOMPRESS_OPT:
609 filebehave = FILE_GZIP;
610 break;
611 #endif
612 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
692 if (lbflag) {
693 #ifdef _IOLBF
694 setvbuf(stdout, NULL, _IOLBF, 0);
695 #else
696 setlinebuf(stdout);
697 #endif
698 }
699
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 }
723