grep.c revision 226261
1219888Sed/* $NetBSD: grep.c,v 1.4 2011/02/16 01:31:33 joerg Exp $ */ 2219888Sed/* $FreeBSD: stable/9/usr.bin/grep/grep.c 226261 2011-10-11 15:03:42Z gabor $ */ 3219888Sed/* $OpenBSD: grep.c,v 1.42 2010/07/02 22:18:03 tedu Exp $ */ 4219888Sed 5219888Sed/*- 6219888Sed * Copyright (c) 1999 James Howard and Dag-Erling Co��dan Sm��rgrav 7219888Sed * Copyright (C) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org> 8219888Sed * All rights reserved. 9219888Sed * 10219888Sed * Redistribution and use in source and binary forms, with or without 11219888Sed * modification, are permitted provided that the following conditions 12219888Sed * are met: 13219888Sed * 1. Redistributions of source code must retain the above copyright 14219888Sed * notice, this list of conditions and the following disclaimer. 15219888Sed * 2. Redistributions in binary form must reproduce the above copyright 16219888Sed * notice, this list of conditions and the following disclaimer in the 17219888Sed * documentation and/or other materials provided with the distribution. 18219888Sed * 19219888Sed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20219888Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21219888Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22219888Sed * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23219888Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24219888Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25219888Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26219888Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27219888Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28219888Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29219888Sed * SUCH DAMAGE. 30219888Sed */ 31219888Sed 32219888Sed#include <sys/cdefs.h> 33219888Sed__FBSDID("$FreeBSD: stable/9/usr.bin/grep/grep.c 226261 2011-10-11 15:03:42Z gabor $"); 34219888Sed 35219888Sed#include <sys/stat.h> 36219888Sed#include <sys/types.h> 37219888Sed 38219888Sed#include <ctype.h> 39219888Sed#include <err.h> 40219888Sed#include <errno.h> 41219888Sed#include <fcntl.h> 42219888Sed#include <getopt.h> 43219888Sed#include <limits.h> 44263817Sray#include <libgen.h> 45263817Sray#include <locale.h> 46219888Sed#include <stdbool.h> 47219888Sed#include <stdio.h> 48219888Sed#include <stdlib.h> 49219888Sed#include <string.h> 50219888Sed#include <unistd.h> 51219888Sed 52219888Sed#include "fastmatch.h" 53219888Sed#include "grep.h" 54219888Sed 55219888Sed#ifndef WITHOUT_NLS 56219888Sed#include <nl_types.h> 57219888Sednl_catd catalog; 58219888Sed#endif 59219888Sed 60219888Sed/* 61219888Sed * Default messags to use when NLS is disabled or no catalogue 62219888Sed * is found. 63219888Sed */ 64219888Sedconst char *errstr[] = { 65219888Sed "", 66219888Sed/* 1*/ "(standard input)", 67219888Sed/* 2*/ "cannot read bzip2 compressed file", 68219888Sed/* 3*/ "unknown %s option", 69219888Sed/* 4*/ "usage: %s [-abcDEFGHhIiJLlmnOoPqRSsUVvwxZ] [-A num] [-B num] [-C[num]]\n", 70219888Sed/* 5*/ "\t[-e pattern] [-f file] [--binary-files=value] [--color=when]\n", 71219888Sed/* 6*/ "\t[--context[=num]] [--directories=action] [--label] [--line-buffered]\n", 72219888Sed/* 7*/ "\t[--null] [pattern] [file ...]\n", 73219888Sed/* 8*/ "Binary file %s matches\n", 74219888Sed/* 9*/ "%s (BSD grep) %s\n", 75219888Sed}; 76219888Sed 77219888Sed/* Flags passed to regcomp() and regexec() */ 78219888Sedint cflags = REG_NOSUB; 79219888Sedint eflags = REG_STARTEND; 80219888Sed 81219888Sed/* Shortcut for matching all cases like empty regex */ 82219888Sedbool matchall; 83219888Sed 84219888Sed/* Searching patterns */ 85219888Sedunsigned int patterns, pattern_sz; 86219888Sedstruct pat *pattern; 87219888Sedregex_t *r_pattern; 88219888Sedfastmatch_t *fg_pattern; 89263817Sray 90263817Sray/* Filename exclusion/inclusion patterns */ 91219888Sedunsigned int fpatterns, fpattern_sz; 92263817Srayunsigned int dpatterns, dpattern_sz; 93263817Sraystruct epat *dpattern, *fpattern; 94263817Sray 95263817Sray/* For regex errors */ 96263817Sraychar re_error[RE_ERROR_BUF + 1]; 97219888Sed 98263817Sray/* Command-line flags */ 99263817Srayunsigned long long Aflag; /* -A x: print x lines trailing each match */ 100263817Srayunsigned long long Bflag; /* -B x: print x lines leading each match */ 101263817Sraybool Hflag; /* -H: always print file name */ 102263817Sraybool Lflag; /* -L: only show names of files with no matches */ 103263817Sraybool bflag; /* -b: show block numbers for each match */ 104263817Sraybool cflag; /* -c: only show a count of matching lines */ 105263817Sraybool hflag; /* -h: don't print filename headers */ 106219888Sedbool iflag; /* -i: ignore case */ 107263817Sraybool lflag; /* -l: only show names of files with matches */ 108263817Sraybool mflag; /* -m x: stop reading the files after x matches */ 109219888Sedlong long mcount; /* count for -m */ 110219888Sedbool nflag; /* -n: show line numbers in front of matching lines */ 111219888Sedbool oflag; /* -o: print only matching part */ 112263817Sraybool qflag; /* -q: quiet mode (don't output anything) */ 113263817Sraybool sflag; /* -s: silent mode (ignore errors) */ 114219888Sedbool vflag; /* -v: only show non-matching lines */ 115219888Sedbool wflag; /* -w: pattern must start and end on word boundaries */ 116219888Sedbool xflag; /* -x: pattern must match entire line */ 117219888Sedbool lbflag; /* --line-buffered */ 118219888Sedbool nullflag; /* --null */ 119219888Sedchar *label; /* --label */ 120219888Sedconst char *color; /* --color */ 121219888Sedint grepbehave = GREP_BASIC; /* -EFGP: type of the regex */ 122219888Sedint binbehave = BINFILE_BIN; /* -aIU: handling of binary files */ 123219888Sedint filebehave = FILE_STDIO; /* -JZ: normal, gzip or bzip2 file */ 124219888Sedint devbehave = DEV_READ; /* -D: handling of devices */ 125219888Sedint dirbehave = DIR_READ; /* -dRr: handling of directories */ 126219888Sedint linkbehave = LINK_READ; /* -OpS: handling of symlinks */ 127219888Sed 128219888Sedbool dexclude, dinclude; /* --exclude-dir and --include-dir */ 129219888Sedbool fexclude, finclude; /* --exclude and --include */ 130219888Sed 131263817Srayenum { 132219888Sed BIN_OPT = CHAR_MAX + 1, 133219888Sed COLOR_OPT, 134263817Sray HELP_OPT, 135263817Sray MMAP_OPT, 136219888Sed LINEBUF_OPT, 137219888Sed LABEL_OPT, 138219888Sed NULL_OPT, 139219888Sed R_EXCLUDE_OPT, 140219888Sed R_INCLUDE_OPT, 141219888Sed R_DEXCLUDE_OPT, 142219888Sed R_DINCLUDE_OPT 143263817Sray}; 144219888Sed 145219888Sedstatic inline const char *init_color(const char *); 146219888Sed 147219888Sed/* Housekeeping */ 148219888Sedbool first = true; /* flag whether we are processing the first match */ 149219888Sedbool prev; /* flag whether or not the previous line matched */ 150219888Sedint tail; /* lines left to print */ 151219888Sedbool notfound; /* file not found */ 152219888Sed 153219888Sedextern char *__progname; 154263817Sray 155263817Sray/* 156219888Sed * Prints usage information and returns 2. 157219888Sed */ 158219888Sedstatic void 159219888Sedusage(void) 160219888Sed{ 161219888Sed fprintf(stderr, getstr(4), __progname); 162219888Sed fprintf(stderr, "%s", getstr(5)); 163219888Sed fprintf(stderr, "%s", getstr(5)); 164219888Sed fprintf(stderr, "%s", getstr(6)); 165219888Sed fprintf(stderr, "%s", getstr(7)); 166263817Sray exit(2); 167219888Sed} 168219888Sed 169263817Sraystatic const char *optstr = "0123456789A:B:C:D:EFGHIJMLOPSRUVZabcd:e:f:hilm:nopqrsuvwxXy"; 170219888Sed 171219888Sedstruct option long_options[] = 172219888Sed{ 173219888Sed {"binary-files", required_argument, NULL, BIN_OPT}, 174219888Sed {"help", no_argument, NULL, HELP_OPT}, 175219888Sed {"mmap", no_argument, NULL, MMAP_OPT}, 176219888Sed {"line-buffered", no_argument, NULL, LINEBUF_OPT}, 177219888Sed {"label", required_argument, NULL, LABEL_OPT}, 178263817Sray {"null", no_argument, NULL, NULL_OPT}, 179263817Sray {"color", optional_argument, NULL, COLOR_OPT}, 180263817Sray {"colour", optional_argument, NULL, COLOR_OPT}, 181256145Sray {"exclude", required_argument, NULL, R_EXCLUDE_OPT}, 182219888Sed {"include", required_argument, NULL, R_INCLUDE_OPT}, 183263817Sray {"exclude-dir", required_argument, NULL, R_DEXCLUDE_OPT}, 184219888Sed {"include-dir", required_argument, NULL, R_DINCLUDE_OPT}, 185219888Sed {"after-context", required_argument, NULL, 'A'}, 186263817Sray {"text", no_argument, NULL, 'a'}, 187219888Sed {"before-context", required_argument, NULL, 'B'}, 188219888Sed {"byte-offset", no_argument, NULL, 'b'}, 189219888Sed {"context", optional_argument, NULL, 'C'}, 190219888Sed {"count", no_argument, NULL, 'c'}, 191263817Sray {"devices", required_argument, NULL, 'D'}, 192219888Sed {"directories", required_argument, NULL, 'd'}, 193219888Sed {"extended-regexp", no_argument, NULL, 'E'}, 194219888Sed {"regexp", required_argument, NULL, 'e'}, 195219888Sed {"fixed-strings", no_argument, NULL, 'F'}, 196219888Sed {"file", required_argument, NULL, 'f'}, 197263817Sray {"basic-regexp", no_argument, NULL, 'G'}, 198263817Sray {"no-filename", no_argument, NULL, 'h'}, 199263817Sray {"with-filename", no_argument, NULL, 'H'}, 200263817Sray {"ignore-case", no_argument, NULL, 'i'}, 201263817Sray {"bz2decompress", no_argument, NULL, 'J'}, 202263817Sray {"files-with-matches", no_argument, NULL, 'l'}, 203263817Sray {"files-without-match", no_argument, NULL, 'L'}, 204263817Sray {"max-count", required_argument, NULL, 'm'}, 205263817Sray {"lzma", no_argument, NULL, 'M'}, 206263817Sray {"line-number", no_argument, NULL, 'n'}, 207263817Sray {"only-matching", no_argument, NULL, 'o'}, 208263817Sray {"quiet", no_argument, NULL, 'q'}, 209263817Sray {"silent", no_argument, NULL, 'q'}, 210263817Sray {"recursive", no_argument, NULL, 'r'}, 211263817Sray {"no-messages", no_argument, NULL, 's'}, 212263817Sray {"binary", no_argument, NULL, 'U'}, 213263817Sray {"unix-byte-offsets", no_argument, NULL, 'u'}, 214219888Sed {"invert-match", no_argument, NULL, 'v'}, 215219888Sed {"version", no_argument, NULL, 'V'}, 216219888Sed {"word-regexp", no_argument, NULL, 'w'}, 217219888Sed {"line-regexp", no_argument, NULL, 'x'}, 218219888Sed {"xz", no_argument, NULL, 'X'}, 219219888Sed {"decompress", no_argument, NULL, 'Z'}, 220219888Sed {NULL, no_argument, NULL, 0} 221219888Sed}; 222219888Sed 223219888Sed/* 224219888Sed * 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