152 153/* 154 * Prints usage information and returns 2. 155 */ 156static void 157usage(void) 158{ 159 fprintf(stderr, getstr(4), getprogname()); 160 fprintf(stderr, "%s", getstr(5)); 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 add_pattern(optarg, strlen(optarg)); 482 needpattern = 0; 483 break; 484 case 'F': 485 grepbehave = GREP_FIXED; 486 break; 487 case 'f': 488 read_patterns(optarg); 489 needpattern = 0; 490 break; 491 case 'G': 492 grepbehave = GREP_BASIC; 493 break; 494 case 'H': 495 Hflag = true; 496 break; 497 case 'h': 498 Hflag = false; 499 hflag = true; 500 break; 501 case 'I': 502 binbehave = BINFILE_SKIP; 503 break; 504 case 'i': 505 case 'y': 506 iflag = true; 507 cflags |= REG_ICASE; 508 break; 509 case 'J': 510#ifdef WITHOUT_BZIP2 511 errno = EOPNOTSUPP; 512 err(2, "bzip2 support was disabled at compile-time"); 513#endif 514 filebehave = FILE_BZIP; 515 break; 516 case 'L': 517 lflag = false; 518 Lflag = true; 519 break; 520 case 'l': 521 Lflag = false; 522 lflag = true; 523 break; 524 case 'm': 525 mflag = true; 526 errno = 0; 527 mcount = strtoll(optarg, &ep, 10); 528 if (((errno == ERANGE) && (mcount == LLONG_MAX)) || 529 ((errno == EINVAL) && (mcount == 0))) 530 err(2, NULL); 531 else if (ep[0] != '\0') { 532 errno = EINVAL; 533 err(2, NULL); 534 } 535 break; 536 case 'M': 537 filebehave = FILE_LZMA; 538 break; 539 case 'n': 540 nflag = true; 541 break; 542 case 'O': 543 linkbehave = LINK_EXPLICIT; 544 break; 545 case 'o': 546 oflag = true; 547 cflags &= ~REG_NOSUB; 548 break; 549 case 'p': 550 linkbehave = LINK_SKIP; 551 break; 552 case 'q': 553 qflag = true; 554 break; 555 case 'S': 556 linkbehave = LINK_READ; 557 break; 558 case 'R': 559 case 'r': 560 dirbehave = DIR_RECURSE; 561 Hflag = true; 562 break; 563 case 's': 564 sflag = true; 565 break; 566 case 'U': 567 binbehave = BINFILE_BIN; 568 break; 569 case 'u': 570 case MMAP_OPT: 571 filebehave = FILE_MMAP; 572 break; 573 case 'V': 574 printf(getstr(9), getprogname(), VERSION); 575 exit(0); 576 case 'v': 577 vflag = true; 578 break; 579 case 'w': 580 wflag = true; 581 cflags &= ~REG_NOSUB; 582 break; 583 case 'x': 584 xflag = true; 585 cflags &= ~REG_NOSUB; 586 break; 587 case 'X': 588 filebehave = FILE_XZ; 589 break; 590 case 'Z': 591 filebehave = FILE_GZIP; 592 break; 593 case BIN_OPT: 594 if (strcasecmp("binary", optarg) == 0) 595 binbehave = BINFILE_BIN; 596 else if (strcasecmp("without-match", optarg) == 0) 597 binbehave = BINFILE_SKIP; 598 else if (strcasecmp("text", optarg) == 0) 599 binbehave = BINFILE_TEXT; 600 else 601 errx(2, getstr(3), "--binary-files"); 602 break; 603 case COLOR_OPT: 604 color = NULL; 605 if (optarg == NULL || strcasecmp("auto", optarg) == 0 || 606 strcasecmp("tty", optarg) == 0 || 607 strcasecmp("if-tty", optarg) == 0) { 608 char *term; 609 610 term = getenv("TERM"); 611 if (isatty(STDOUT_FILENO) && term != NULL && 612 strcasecmp(term, "dumb") != 0) 613 color = init_color("01;31"); 614 } else if (strcasecmp("always", optarg) == 0 || 615 strcasecmp("yes", optarg) == 0 || 616 strcasecmp("force", optarg) == 0) { 617 color = init_color("01;31"); 618 } else if (strcasecmp("never", optarg) != 0 && 619 strcasecmp("none", optarg) != 0 && 620 strcasecmp("no", optarg) != 0) 621 errx(2, getstr(3), "--color"); 622 cflags &= ~REG_NOSUB; 623 break; 624 case LABEL_OPT: 625 label = optarg; 626 break; 627 case LINEBUF_OPT: 628 lbflag = true; 629 break; 630 case NULL_OPT: 631 nullflag = true; 632 break; 633 case R_INCLUDE_OPT: 634 finclude = true; 635 add_fpattern(optarg, INCL_PAT); 636 break; 637 case R_EXCLUDE_OPT: 638 fexclude = true; 639 add_fpattern(optarg, EXCL_PAT); 640 break; 641 case R_DINCLUDE_OPT: 642 dinclude = true; 643 add_dpattern(optarg, INCL_PAT); 644 break; 645 case R_DEXCLUDE_OPT: 646 dexclude = true; 647 add_dpattern(optarg, EXCL_PAT); 648 break; 649 case HELP_OPT: 650 default: 651 usage(); 652 } 653 lastc = c; 654 newarg = optind != prevoptind; 655 prevoptind = optind; 656 } 657 aargc -= optind; 658 aargv += optind; 659 660 /* Empty pattern file matches nothing */ 661 if (!needpattern && (patterns == 0)) 662 exit(1); 663 664 /* Fail if we don't have any pattern */ 665 if (aargc == 0 && needpattern) 666 usage(); 667 668 /* Process patterns from command line */ 669 if (aargc != 0 && needpattern) { 670 add_pattern(*aargv, strlen(*aargv)); 671 --aargc; 672 ++aargv; 673 } 674 675 switch (grepbehave) { 676 case GREP_BASIC: 677 break; 678 case GREP_FIXED: 679 /* XXX: header mess, REG_LITERAL not defined in gnu/regex.h */ 680 cflags |= 0020; 681 break; 682 case GREP_EXTENDED: 683 cflags |= REG_EXTENDED; 684 break; 685 default: 686 /* NOTREACHED */ 687 usage(); 688 } 689 690 fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern)); 691 r_pattern = grep_calloc(patterns, sizeof(*r_pattern)); 692 693 /* Check if cheating is allowed (always is for fgrep). */ 694 for (i = 0; i < patterns; ++i) { 695 if (fastncomp(&fg_pattern[i], pattern[i].pat, 696 pattern[i].len, cflags) != 0) { 697 /* Fall back to full regex library */ 698 c = regcomp(&r_pattern[i], pattern[i].pat, cflags); 699 if (c != 0) { 700 regerror(c, &r_pattern[i], re_error, 701 RE_ERROR_BUF); 702 errx(2, "%s", re_error); 703 } 704 } 705 } 706 707 if (lbflag) 708 setlinebuf(stdout); 709 710 if ((aargc == 0 || aargc == 1) && !Hflag) 711 hflag = true; 712 713 if (aargc == 0) 714 exit(!procfile("-")); 715 716 if (dirbehave == DIR_RECURSE) 717 c = grep_tree(aargv); 718 else 719 for (c = 0; aargc--; ++aargv) { 720 if ((finclude || fexclude) && !file_matching(*aargv)) 721 continue; 722 c+= procfile(*aargv); 723 } 724 725#ifndef WITHOUT_NLS 726 catclose(catalog); 727#endif 728 729 /* Find out the correct return value according to the 730 results and the command line option. */
| 152 153/* 154 * Prints usage information and returns 2. 155 */ 156static void 157usage(void) 158{ 159 fprintf(stderr, getstr(4), getprogname()); 160 fprintf(stderr, "%s", getstr(5)); 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 add_pattern(optarg, strlen(optarg)); 482 needpattern = 0; 483 break; 484 case 'F': 485 grepbehave = GREP_FIXED; 486 break; 487 case 'f': 488 read_patterns(optarg); 489 needpattern = 0; 490 break; 491 case 'G': 492 grepbehave = GREP_BASIC; 493 break; 494 case 'H': 495 Hflag = true; 496 break; 497 case 'h': 498 Hflag = false; 499 hflag = true; 500 break; 501 case 'I': 502 binbehave = BINFILE_SKIP; 503 break; 504 case 'i': 505 case 'y': 506 iflag = true; 507 cflags |= REG_ICASE; 508 break; 509 case 'J': 510#ifdef WITHOUT_BZIP2 511 errno = EOPNOTSUPP; 512 err(2, "bzip2 support was disabled at compile-time"); 513#endif 514 filebehave = FILE_BZIP; 515 break; 516 case 'L': 517 lflag = false; 518 Lflag = true; 519 break; 520 case 'l': 521 Lflag = false; 522 lflag = true; 523 break; 524 case 'm': 525 mflag = true; 526 errno = 0; 527 mcount = strtoll(optarg, &ep, 10); 528 if (((errno == ERANGE) && (mcount == LLONG_MAX)) || 529 ((errno == EINVAL) && (mcount == 0))) 530 err(2, NULL); 531 else if (ep[0] != '\0') { 532 errno = EINVAL; 533 err(2, NULL); 534 } 535 break; 536 case 'M': 537 filebehave = FILE_LZMA; 538 break; 539 case 'n': 540 nflag = true; 541 break; 542 case 'O': 543 linkbehave = LINK_EXPLICIT; 544 break; 545 case 'o': 546 oflag = true; 547 cflags &= ~REG_NOSUB; 548 break; 549 case 'p': 550 linkbehave = LINK_SKIP; 551 break; 552 case 'q': 553 qflag = true; 554 break; 555 case 'S': 556 linkbehave = LINK_READ; 557 break; 558 case 'R': 559 case 'r': 560 dirbehave = DIR_RECURSE; 561 Hflag = true; 562 break; 563 case 's': 564 sflag = true; 565 break; 566 case 'U': 567 binbehave = BINFILE_BIN; 568 break; 569 case 'u': 570 case MMAP_OPT: 571 filebehave = FILE_MMAP; 572 break; 573 case 'V': 574 printf(getstr(9), getprogname(), VERSION); 575 exit(0); 576 case 'v': 577 vflag = true; 578 break; 579 case 'w': 580 wflag = true; 581 cflags &= ~REG_NOSUB; 582 break; 583 case 'x': 584 xflag = true; 585 cflags &= ~REG_NOSUB; 586 break; 587 case 'X': 588 filebehave = FILE_XZ; 589 break; 590 case 'Z': 591 filebehave = FILE_GZIP; 592 break; 593 case BIN_OPT: 594 if (strcasecmp("binary", optarg) == 0) 595 binbehave = BINFILE_BIN; 596 else if (strcasecmp("without-match", optarg) == 0) 597 binbehave = BINFILE_SKIP; 598 else if (strcasecmp("text", optarg) == 0) 599 binbehave = BINFILE_TEXT; 600 else 601 errx(2, getstr(3), "--binary-files"); 602 break; 603 case COLOR_OPT: 604 color = NULL; 605 if (optarg == NULL || strcasecmp("auto", optarg) == 0 || 606 strcasecmp("tty", optarg) == 0 || 607 strcasecmp("if-tty", optarg) == 0) { 608 char *term; 609 610 term = getenv("TERM"); 611 if (isatty(STDOUT_FILENO) && term != NULL && 612 strcasecmp(term, "dumb") != 0) 613 color = init_color("01;31"); 614 } else if (strcasecmp("always", optarg) == 0 || 615 strcasecmp("yes", optarg) == 0 || 616 strcasecmp("force", optarg) == 0) { 617 color = init_color("01;31"); 618 } else if (strcasecmp("never", optarg) != 0 && 619 strcasecmp("none", optarg) != 0 && 620 strcasecmp("no", optarg) != 0) 621 errx(2, getstr(3), "--color"); 622 cflags &= ~REG_NOSUB; 623 break; 624 case LABEL_OPT: 625 label = optarg; 626 break; 627 case LINEBUF_OPT: 628 lbflag = true; 629 break; 630 case NULL_OPT: 631 nullflag = true; 632 break; 633 case R_INCLUDE_OPT: 634 finclude = true; 635 add_fpattern(optarg, INCL_PAT); 636 break; 637 case R_EXCLUDE_OPT: 638 fexclude = true; 639 add_fpattern(optarg, EXCL_PAT); 640 break; 641 case R_DINCLUDE_OPT: 642 dinclude = true; 643 add_dpattern(optarg, INCL_PAT); 644 break; 645 case R_DEXCLUDE_OPT: 646 dexclude = true; 647 add_dpattern(optarg, EXCL_PAT); 648 break; 649 case HELP_OPT: 650 default: 651 usage(); 652 } 653 lastc = c; 654 newarg = optind != prevoptind; 655 prevoptind = optind; 656 } 657 aargc -= optind; 658 aargv += optind; 659 660 /* Empty pattern file matches nothing */ 661 if (!needpattern && (patterns == 0)) 662 exit(1); 663 664 /* Fail if we don't have any pattern */ 665 if (aargc == 0 && needpattern) 666 usage(); 667 668 /* Process patterns from command line */ 669 if (aargc != 0 && needpattern) { 670 add_pattern(*aargv, strlen(*aargv)); 671 --aargc; 672 ++aargv; 673 } 674 675 switch (grepbehave) { 676 case GREP_BASIC: 677 break; 678 case GREP_FIXED: 679 /* XXX: header mess, REG_LITERAL not defined in gnu/regex.h */ 680 cflags |= 0020; 681 break; 682 case GREP_EXTENDED: 683 cflags |= REG_EXTENDED; 684 break; 685 default: 686 /* NOTREACHED */ 687 usage(); 688 } 689 690 fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern)); 691 r_pattern = grep_calloc(patterns, sizeof(*r_pattern)); 692 693 /* Check if cheating is allowed (always is for fgrep). */ 694 for (i = 0; i < patterns; ++i) { 695 if (fastncomp(&fg_pattern[i], pattern[i].pat, 696 pattern[i].len, cflags) != 0) { 697 /* Fall back to full regex library */ 698 c = regcomp(&r_pattern[i], pattern[i].pat, cflags); 699 if (c != 0) { 700 regerror(c, &r_pattern[i], re_error, 701 RE_ERROR_BUF); 702 errx(2, "%s", re_error); 703 } 704 } 705 } 706 707 if (lbflag) 708 setlinebuf(stdout); 709 710 if ((aargc == 0 || aargc == 1) && !Hflag) 711 hflag = true; 712 713 if (aargc == 0) 714 exit(!procfile("-")); 715 716 if (dirbehave == DIR_RECURSE) 717 c = grep_tree(aargv); 718 else 719 for (c = 0; aargc--; ++aargv) { 720 if ((finclude || fexclude) && !file_matching(*aargv)) 721 continue; 722 c+= procfile(*aargv); 723 } 724 725#ifndef WITHOUT_NLS 726 catclose(catalog); 727#endif 728 729 /* Find out the correct return value according to the 730 results and the command line option. */
|