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