1/* $NetBSD: grep.c,v 1.4 2011/02/16 01:31:33 joerg Exp $ */ 2/* $FreeBSD$ */ 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$"); 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 */ 110long long mlimit; /* requested value for -m */ 111bool nflag; /* -n: show line numbers in front of matching lines */ 112bool oflag; /* -o: print only matching part */ 113bool qflag; /* -q: quiet mode (don't output anything) */ 114bool sflag; /* -s: silent mode (ignore errors) */ 115bool vflag; /* -v: only show non-matching lines */ 116bool wflag; /* -w: pattern must start and end on word boundaries */ 117bool xflag; /* -x: pattern must match entire line */ 118bool lbflag; /* --line-buffered */ 119bool nullflag; /* --null */ 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 HELP_OPT, 136 MMAP_OPT, 137 LINEBUF_OPT, 138 LABEL_OPT, 139 NULL_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 */ 149bool first = true; /* flag whether we are processing the first match */ 150bool prev; /* flag whether or not the previous line matched */ 151int tail; /* lines left to print */ 152bool file_err; /* file reading error */ 153 154/* 155 * Prints usage information and returns 2. 156 */ 157static void 158usage(void) 159{ 160 fprintf(stderr, getstr(4), getprogname()); 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 = "0123456789A:B:C:D:EFGHIJMLOPSRUVZabcd:e:f:hilm:nopqrsuvwxXy"; 168 169struct option long_options[] = 170{ 171 {"binary-files", required_argument, NULL, BIN_OPT}, 172 {"help", no_argument, NULL, HELP_OPT}, 173 {"mmap", no_argument, NULL, MMAP_OPT}, 174 {"line-buffered", no_argument, NULL, LINEBUF_OPT}, 175 {"label", required_argument, NULL, LABEL_OPT}, 176 {"null", no_argument, NULL, NULL_OPT}, 177 {"color", optional_argument, NULL, COLOR_OPT}, 178 {"colour", optional_argument, NULL, COLOR_OPT}, 179 {"exclude", required_argument, NULL, R_EXCLUDE_OPT}, 180 {"include", required_argument, NULL, R_INCLUDE_OPT}, 181 {"exclude-dir", required_argument, NULL, R_DEXCLUDE_OPT}, 182 {"include-dir", required_argument, NULL, R_DINCLUDE_OPT}, 183 {"after-context", required_argument, NULL, 'A'}, 184 {"text", no_argument, NULL, 'a'}, 185 {"before-context", required_argument, NULL, 'B'}, 186 {"byte-offset", no_argument, NULL, 'b'}, 187 {"context", optional_argument, NULL, 'C'}, 188 {"count", no_argument, NULL, 'c'}, 189 {"devices", required_argument, NULL, 'D'}, 190 {"directories", required_argument, NULL, 'd'}, 191 {"extended-regexp", no_argument, NULL, 'E'}, 192 {"regexp", required_argument, NULL, 'e'}, 193 {"fixed-strings", no_argument, NULL, 'F'}, 194 {"file", required_argument, NULL, 'f'}, 195 {"basic-regexp", no_argument, NULL, 'G'}, 196 {"no-filename", no_argument, NULL, 'h'}, 197 {"with-filename", no_argument, NULL, 'H'}, 198 {"ignore-case", no_argument, NULL, 'i'}, 199 {"bz2decompress", no_argument, NULL, 'J'}, 200 {"files-with-matches", no_argument, NULL, 'l'}, 201 {"files-without-match", no_argument, NULL, 'L'}, 202 {"max-count", required_argument, NULL, 'm'}, 203 {"lzma", no_argument, NULL, 'M'}, 204 {"line-number", no_argument, NULL, 'n'}, 205 {"only-matching", no_argument, NULL, 'o'}, 206 {"quiet", no_argument, NULL, 'q'}, 207 {"silent", no_argument, NULL, 'q'}, 208 {"recursive", no_argument, NULL, 'r'}, 209 {"no-messages", no_argument, NULL, 's'}, 210 {"binary", no_argument, NULL, 'U'}, 211 {"unix-byte-offsets", no_argument, NULL, 'u'}, 212 {"invert-match", no_argument, NULL, 'v'}, 213 {"version", no_argument, NULL, 'V'}, 214 {"word-regexp", no_argument, NULL, 'w'}, 215 {"line-regexp", no_argument, NULL, 'x'}, 216 {"xz", no_argument, NULL, 'X'}, 217 {"decompress", no_argument, NULL, 'Z'}, 218 {NULL, no_argument, NULL, 0} 219}; 220 221/* 222 * Adds a searching pattern to the internal array. 223 */ 224static void 225add_pattern(char *pat, size_t len) 226{ 227 228 /* Do not add further pattern is we already match everything */ 229 if (matchall) 230 return; 231 232 /* Check if we can do a shortcut */ 233 if (len == 0) { 234 matchall = true; 235 for (unsigned int i = 0; i < patterns; i++) { 236 free(pattern[i].pat); 237 } 238 pattern = grep_realloc(pattern, sizeof(struct pat)); 239 pattern[0].pat = NULL; 240 pattern[0].len = 0; 241 patterns = 1; 242 return; 243 } 244 /* Increase size if necessary */ 245 if (patterns == pattern_sz) { 246 pattern_sz *= 2; 247 pattern = grep_realloc(pattern, ++pattern_sz * 248 sizeof(struct pat)); 249 } 250 if (len > 0 && pat[len - 1] == '\n') 251 --len; 252 /* pat may not be NUL-terminated */ 253 pattern[patterns].pat = grep_malloc(len + 1); 254 memcpy(pattern[patterns].pat, pat, len); 255 pattern[patterns].len = len; 256 pattern[patterns].pat[len] = '\0'; 257 ++patterns; 258} 259 260/* 261 * Adds a file include/exclude pattern to the internal array. 262 */ 263static void 264add_fpattern(const char *pat, int mode) 265{ 266 267 /* Increase size if necessary */ 268 if (fpatterns == fpattern_sz) { 269 fpattern_sz *= 2; 270 fpattern = grep_realloc(fpattern, ++fpattern_sz * 271 sizeof(struct epat)); 272 } 273 fpattern[fpatterns].pat = grep_strdup(pat); 274 fpattern[fpatterns].mode = mode; 275 ++fpatterns; 276} 277 278/* 279 * Adds a directory include/exclude pattern to the internal array. 280 */ 281static void 282add_dpattern(const char *pat, int mode) 283{ 284 285 /* Increase size if necessary */ 286 if (dpatterns == dpattern_sz) { 287 dpattern_sz *= 2; 288 dpattern = grep_realloc(dpattern, ++dpattern_sz * 289 sizeof(struct epat)); 290 } 291 dpattern[dpatterns].pat = grep_strdup(pat); 292 dpattern[dpatterns].mode = mode; 293 ++dpatterns; 294} 295 296/* 297 * Reads searching patterns from a file and adds them with add_pattern(). 298 */ 299static void 300read_patterns(const char *fn) 301{ 302 struct stat st; 303 FILE *f; 304 char *line; 305 size_t len; 306 307 if ((f = fopen(fn, "r")) == NULL) 308 err(2, "%s", fn); 309 if ((fstat(fileno(f), &st) == -1) || (S_ISDIR(st.st_mode))) { 310 fclose(f); 311 return; 312 } 313 while ((line = fgetln(f, &len)) != NULL) 314 add_pattern(line, line[0] == '\n' ? 0 : len); 315 if (ferror(f)) 316 err(2, "%s", fn); 317 fclose(f); 318} 319 320static inline const char * 321init_color(const char *d) 322{ 323 char *c; 324 325 c = getenv("GREP_COLOR"); 326 return (c != NULL && c[0] != '\0' ? c : d); 327} 328 329int 330main(int argc, char *argv[]) 331{ 332 char **aargv, **eargv, *eopts; 333 char *ep; 334 const char *pn; 335 unsigned long long l; 336 unsigned int aargc, eargc, i; 337 int c, lastc, needpattern, newarg, prevoptind; 338 339 setlocale(LC_ALL, ""); 340 341#ifndef WITHOUT_NLS 342 catalog = catopen("grep", NL_CAT_LOCALE); 343#endif 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] == 'b' && pn[1] == 'z') { 350 filebehave = FILE_BZIP; 351 pn += 2; 352 } else if (pn[0] == 'x' && pn[1] == 'z') { 353 filebehave = FILE_XZ; 354 pn += 2; 355 } else if (pn[0] == 'l' && pn[1] == 'z') { 356 filebehave = FILE_LZMA; 357 pn += 2; 358 } else if (pn[0] == 'z') { 359 filebehave = FILE_GZIP; 360 pn += 1; 361 } 362 switch (pn[0]) { 363 case 'e': 364 grepbehave = GREP_EXTENDED; 365 break; 366 case 'f': 367 grepbehave = GREP_FIXED; 368 break; 369 } 370 371 lastc = '\0'; 372 newarg = 1; 373 prevoptind = 1; 374 needpattern = 1; 375 376 eopts = getenv("GREP_OPTIONS"); 377 378 /* support for extra arguments in GREP_OPTIONS */ 379 eargc = 0; 380 if (eopts != NULL && eopts[0] != '\0') { 381 char *str; 382 383 /* make an estimation of how many extra arguments we have */ 384 for (unsigned int j = 0; j < strlen(eopts); j++) 385 if (eopts[j] == ' ') 386 eargc++; 387 388 eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1)); 389 390 eargc = 0; 391 /* parse extra arguments */ 392 while ((str = strsep(&eopts, " ")) != NULL) 393 if (str[0] != '\0') 394 eargv[eargc++] = grep_strdup(str); 395 396 aargv = (char **)grep_calloc(eargc + argc + 1, 397 sizeof(char *)); 398 399 aargv[0] = argv[0]; 400 for (i = 0; i < eargc; i++) 401 aargv[i + 1] = eargv[i]; 402 for (int j = 1; j < argc; j++, i++) 403 aargv[i + 1] = argv[j]; 404 405 aargc = eargc + argc; 406 } else { 407 aargv = argv; 408 aargc = argc; 409 } 410 411 while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) != 412 -1)) { 413 switch (c) { 414 case '0': case '1': case '2': case '3': case '4': 415 case '5': case '6': case '7': case '8': case '9': 416 if (newarg || !isdigit(lastc)) 417 Aflag = 0; 418 else if (Aflag > LLONG_MAX / 10) { 419 errno = ERANGE; 420 err(2, NULL); 421 } 422 Aflag = Bflag = (Aflag * 10) + (c - '0'); 423 break; 424 case 'C': 425 if (optarg == NULL) { 426 Aflag = Bflag = 2; 427 break; 428 } 429 /* FALLTHROUGH */ 430 case 'A': 431 /* FALLTHROUGH */ 432 case 'B': 433 errno = 0; 434 l = strtoull(optarg, &ep, 10); 435 if (((errno == ERANGE) && (l == ULLONG_MAX)) || 436 ((errno == EINVAL) && (l == 0))) 437 err(2, NULL); 438 else if (ep[0] != '\0') { 439 errno = EINVAL; 440 err(2, NULL); 441 } 442 if (c == 'A') 443 Aflag = l; 444 else if (c == 'B') 445 Bflag = l; 446 else 447 Aflag = Bflag = l; 448 break; 449 case 'a': 450 binbehave = BINFILE_TEXT; 451 break; 452 case 'b': 453 bflag = true; 454 break; 455 case 'c': 456 cflag = true; 457 break; 458 case 'D': 459 if (strcasecmp(optarg, "skip") == 0) 460 devbehave = DEV_SKIP; 461 else if (strcasecmp(optarg, "read") == 0) 462 devbehave = DEV_READ; 463 else 464 errx(2, getstr(3), "--devices"); 465 break; 466 case 'd': 467 if (strcasecmp("recurse", optarg) == 0) { 468 Hflag = true; 469 dirbehave = DIR_RECURSE; 470 } else if (strcasecmp("skip", optarg) == 0) 471 dirbehave = DIR_SKIP; 472 else if (strcasecmp("read", optarg) == 0) 473 dirbehave = DIR_READ; 474 else 475 errx(2, getstr(3), "--directories"); 476 break; 477 case 'E': 478 grepbehave = GREP_EXTENDED; 479 break; 480 case 'e': 481 { 482 char *token; 483 char *string = optarg; 484 485 while ((token = strsep(&string, "\n")) != NULL) 486 add_pattern(token, strlen(token)); 487 } 488 needpattern = 0; 489 break; 490 case 'F': 491 grepbehave = GREP_FIXED; 492 break; 493 case 'f': 494 read_patterns(optarg); 495 needpattern = 0; 496 break; 497 case 'G': 498 grepbehave = GREP_BASIC; 499 break; 500 case 'H': 501 Hflag = true; 502 break; 503 case 'h': 504 Hflag = false; 505 hflag = true; 506 break; 507 case 'I': 508 binbehave = BINFILE_SKIP; 509 break; 510 case 'i': 511 case 'y': 512 iflag = true; 513 cflags |= REG_ICASE; 514 break; 515 case 'J': 516#ifdef WITHOUT_BZIP2 517 errno = EOPNOTSUPP; 518 err(2, "bzip2 support was disabled at compile-time"); 519#endif 520 filebehave = FILE_BZIP; 521 break; 522 case 'L': 523 lflag = false; 524 Lflag = true; 525 break; 526 case 'l': 527 Lflag = false; 528 lflag = true; 529 break; 530 case 'm': 531 mflag = true; 532 errno = 0; 533 mlimit = mcount = strtoll(optarg, &ep, 10); 534 if (((errno == ERANGE) && (mcount == LLONG_MAX)) || 535 ((errno == EINVAL) && (mcount == 0))) 536 err(2, NULL); 537 else if (ep[0] != '\0') { 538 errno = EINVAL; 539 err(2, NULL); 540 } 541 break; 542 case 'M': 543 filebehave = FILE_LZMA; 544 break; 545 case 'n': 546 nflag = true; 547 break; 548 case 'O': 549 linkbehave = LINK_EXPLICIT; 550 break; 551 case 'o': 552 oflag = true; 553 cflags &= ~REG_NOSUB; 554 break; 555 case 'p': 556 linkbehave = LINK_SKIP; 557 break; 558 case 'q': 559 qflag = true; 560 break; 561 case 'S': 562 linkbehave = LINK_READ; 563 break; 564 case 'R': 565 case 'r': 566 dirbehave = DIR_RECURSE; 567 Hflag = true; 568 break; 569 case 's': 570 sflag = true; 571 break; 572 case 'U': 573 binbehave = BINFILE_BIN; 574 break; 575 case 'u': 576 case MMAP_OPT: 577 filebehave = FILE_MMAP; 578 break; 579 case 'V': 580 printf(getstr(9), getprogname(), VERSION); 581 exit(0); 582 case 'v': 583 vflag = true; 584 break; 585 case 'w': 586 wflag = true; 587 cflags &= ~REG_NOSUB; 588 break; 589 case 'x': 590 xflag = true; 591 cflags &= ~REG_NOSUB; 592 break; 593 case 'X': 594 filebehave = FILE_XZ; 595 break; 596 case 'Z': 597 filebehave = FILE_GZIP; 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(3), "--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(3), "--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 /* XXX: header mess, REG_LITERAL not defined in gnu/regex.h */ 690 cflags |= 0020; 691 break; 692 case GREP_EXTENDED: 693 cflags |= REG_EXTENDED; 694 break; 695 default: 696 /* NOTREACHED */ 697 usage(); 698 } 699 700 fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern)); 701 r_pattern = grep_calloc(patterns, sizeof(*r_pattern)); 702 703 /* Check if cheating is allowed (always is for fgrep). */ 704 for (i = 0; i < patterns; ++i) { 705 if (fastncomp(&fg_pattern[i], pattern[i].pat, 706 pattern[i].len, cflags) != 0) { 707 /* Fall back to full regex library */ 708 c = regcomp(&r_pattern[i], pattern[i].pat, cflags); 709 if (c != 0) { 710 regerror(c, &r_pattern[i], re_error, 711 RE_ERROR_BUF); 712 errx(2, "%s", re_error); 713 } 714 } 715 } 716 717 if (lbflag) 718 setlinebuf(stdout); 719 720 if ((aargc == 0 || aargc == 1) && !Hflag) 721 hflag = true; 722 723 if (aargc == 0) 724 exit(!procfile("-")); 725 726 if (dirbehave == DIR_RECURSE) 727 c = grep_tree(aargv); 728 else 729 for (c = 0; aargc--; ++aargv) { 730 if ((finclude || fexclude) && !file_matching(*aargv)) 731 continue; 732 c+= procfile(*aargv); 733 } 734 735#ifndef WITHOUT_NLS 736 catclose(catalog); 737#endif 738 739 /* Find out the correct return value according to the 740 results and the command line option. */ 741 exit(c ? (file_err ? (qflag ? 0 : 2) : 0) : (file_err ? 2 : 1)); 742} 743