1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1995-2009 AT&T Knowledge Ventures * 5 * and is licensed under the * 6 * Common Public License, Version 1.0 * 7 * by AT&T Knowledge Ventures * 8 * * 9 * A copy of the License is available at * 10 * http://www.opensource.org/licenses/cpl1.0.txt * 11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 12 * * 13 * Information and Software Systems Research * 14 * AT&T Research * 15 * Florham Park NJ * 16 * * 17 * Glenn Fowler <gsf@research.att.com> * 18 * * 19 ***********************************************************************/ 20 #pragma prototyped 21 22 static const char usage[] = 23 "[-?\n@(#)$Id: grep (AT&T Research) 2006-06-14 $\n]" 24 USAGE_LICENSE 25 "[+NAME?grep - search lines in files for matching patterns]" 26 "[+DESCRIPTION?The \bgrep\b commands search the named input files" 27 " for lines containing a match for the given \apatterns\a." 28 " Matching lines are printed by default. The standard input is searched" 29 " if no files are given or when the file \b-\b is specified.]" 30 "[+?There are six variants of \bgrep\b, each one using a different form of" 31 " \apattern\a, controlled either by option or the command path" 32 " base name. Details of each variant may be found in \bregex\b(3).]" 33 " {" 34 " [+grep?The default basic regular expressions (no alternations.)]" 35 " [+egrep?Extended regular expressions (alternations, one or more.)]" 36 " [+pgrep?\bperl\b(1) regular expressions (lenient extended.)]" 37 " [+xgrep?Augmented regular expressions (conjunction, negation.)]" 38 " [+fgrep?Fixed string expressions.]" 39 " [+agrep?Approximate regular expressions (not implemented.)]" 40 " }" 41 "[G:basic-regexp?\bgrep\b mode (default): basic regular expression \apatterns\a.]" 42 "[E:extended-regexp?\begrep\b mode: extended regular expression \apatterns\a.]" 43 "[X:augmented-regexp?\bxgrep\b mode: augmented regular expression \apatterns\a.]" 44 "[P:perl-regexp?\bpgrep\b mode: \bperl\b(1) regular expression \apatterns\a.]" 45 "[F:fixed-string?\bfgrep\b mode: fixed string \apatterns\a.]" 46 "[A:approximate-regexp?\bagrep\b mode: approximate regular expression \apatterns\a (not implemented.)]" 47 48 "[C:context?Set the matched line context \abefore\a and \aafter\a count." 49 " By default only matched lines are printed.]:?" 50 " [before[,after]]:=2,2]" 51 "[c:count?Only print a matching line count for each file.]" 52 "[e:expression|pattern|regexp?Specify a matching \apattern\a. More than one" 53 " \apattern\a implies alternation. If this option is specified" 54 " then the command line \apattern\a must be omitted.]:" 55 " [pattern]" 56 "[f:file?Each line in \apattern-file\a is a \apattern\a, placed into a single" 57 " alternating expression.]:" 58 " [pattern-file]" 59 "[H:filename|with-filename?Prefix each matched line with the containing file name.]" 60 "[h:no-filename?Suppress containing file name prefix for each matched line.]" 61 "[i:ignore-case?Ignore case when matching.]" 62 "[l:files-with-matches?Only print file names with at least one match.]" 63 "[L:files-without-matches?Only print file names with no matches.]" 64 "[b:highlight?Highlight matches using the ansi terminal bold sequence.]" 65 "[v:invert-match|revert-match?Invert the \apattern\a match sense.]" 66 "[m:label?All patterns must be of the form \alabel\a:\apattern\a. Match and" 67 " count output will be prefixed by the corresponding \alabel\a:.]" 68 "[O:lenient?Enable lenient \apattern\a interpretation. This is the default.]" 69 "[x:line-match|line-regexp?Force \apatterns\a to match complete lines.]" 70 "[n:number|line-number?Prefix each matched line with its line number.]" 71 "[N:name?Set the standard input file name prefix to" 72 " \aname\a.]:[name:=empty]" 73 "[q:quiet|silent?Do not print matching lines.]" 74 "[S:strict?Enable strict \apattern\a interpretation with diagnostics.]" 75 "[s:suppress|no-messages?Suppress error and warning messages.]" 76 "[t:total?Only print a single matching line count for all files.]" 77 "[T:test?Enable implementation specific tests.]:" 78 " [test]" 79 "[w:word-match|word-regexp?Force \apatterns\a to match complete words.]" 80 "[a?Ignored for GNU compatibility.]" 81 "\n" 82 "\n[ pattern ] [ file ... ]\n" 83 "\n" 84 "[+DIAGNOSTICS?Exit status 0 if matches were found, 1 if no matches were found," 85 " where \b-v\b invertes the exit status. Exit status 2 for other" 86 " errors that are accompanied by a message on the standard error.]" 87 "[+SEE ALSO?\bed\b(1), \bsed\b(1), \bperl\b(1), \bregex\b(3)]" 88 "[+CAVEATS?Some expressions of necessity require exponential space" 89 " and/or time.]" 90 "[+BUGS?Some expressions may use sub-optimal algorithms. For example," 91 " don't use this implementation to compute primes.]" 92 ; 93 94 #include <ast.h> 95 #include <ctype.h> 96 #include <ccode.h> 97 #include <error.h> 98 #include <regex.h> 99 100 #ifndef EISDIR 101 #define EISDIR (-1) 102 #endif 103 104 /* 105 * snarfed from Doug McElroy's C++ version 106 * 107 * this grep is based on the Posix re package. 108 * unfortunately it has to have a nonstandard interface. 109 * 1. fgrep does not have usual operators. REG_LITERAL 110 * caters for this. 111 * 2. grep allows null expressions, hence REG_NULL. 112 * 3. it may be possible to combine the multiple 113 * patterns of grep into single patterns. important 114 * special cases are handled by regcomb(). 115 * 4. anchoring by -x has to be done separately from 116 * compilation (remember that fgrep has no ^ or $ operator), 117 * hence REG_LEFT|REG_RIGHT. (An honest, but slow alternative: 118 * run regexec with REG_NOSUB off and nmatch=1 and check 119 * whether the match is full length) 120 */ 121 122 typedef struct Item_s /* list item - sue me for waste */ 123 { 124 struct Item_s* next; /* next in list */ 125 regex_t re; /* compiled re */ 126 Sfulong_t hits; /* labeled pattern matches */ 127 Sfulong_t total; /* total hits */ 128 char string[1]; /* string value */ 129 } Item_t; 130 131 typedef struct List_s /* generic list */ 132 { 133 Item_t* head; /* list head */ 134 Item_t* tail; /* list tail */ 135 } List_t; 136 137 typedef struct State_s /* program state */ 138 { 139 struct 140 { 141 char* base; /* sfsetbuf buffer */ 142 size_t size; /* sfsetbuf size */ 143 int noshare; /* turn off SF_SHARE */ 144 } buffer; 145 146 List_t file; /* pattern file list */ 147 List_t pattern; /* pattern list */ 148 List_t re; /* re list */ 149 150 regmatch_t posvec[1]; /* match position vector */ 151 regmatch_t* pos; /* match position pointer */ 152 int posnum; /* number of match positions */ 153 154 int any; /* if any pattern hit */ 155 int list; /* list files with hits */ 156 int notfound; /* some input file not found */ 157 int options; /* regex options */ 158 159 Sfulong_t hits; /* total matched pattern count */ 160 161 unsigned char byline; /* multiple pattern line by line*/ 162 unsigned char count; /* count number of hits */ 163 unsigned char label; /* all patterns labeled */ 164 unsigned char match; /* match sense */ 165 unsigned char query; /* return status but no output */ 166 unsigned char number; /* line numbers */ 167 unsigned char prefix; /* print file prefix */ 168 unsigned char suppress; /* no unopenable file messages */ 169 unsigned char words; /* word matches only */ 170 } State_s; 171 172 static void 173 addre(State_s *state, List_t* p, char* s) 174 { 175 int c; 176 char* b; 177 Item_t* x; 178 Sfio_t* t; 179 180 b = s; 181 if (state->label) 182 { 183 if (!(s = strchr(s, ':'))) 184 error(3, "%s: label:pattern expected", b); 185 c = s - b; 186 s++; 187 } 188 else 189 c = 0; 190 if (!(x = newof(0, Item_t, 1, c))) 191 error(ERROR_SYSTEM|3, "out of space (pattern `%s')", b); 192 if (c) 193 memcpy(x->string, b, c); 194 if (state->words) 195 { 196 if (!(t = sfstropen())) 197 error(ERROR_SYSTEM|3, "out of space (word pattern `%s')", s); 198 if (!(state->options & REG_AUGMENTED)) 199 sfputc(t, '\\'); 200 sfputc(t, '<'); 201 sfputr(t, s, -1); 202 if (!(state->options & REG_AUGMENTED)) 203 sfputc(t, '\\'); 204 sfputc(t, '>'); 205 if (!(s = sfstruse(t))) 206 error(ERROR_SYSTEM|3, "out of space"); 207 } 208 else 209 t = 0; 210 if (c = regcomp(&x->re, s, state->options|REG_MULTIPLE)) 211 regfatal(&x->re, 3, c); 212 if (t) 213 sfstrclose(t); 214 if (!p->head) 215 { 216 p->head = p->tail = x; 217 if (state->number || !regrecord(&x->re)) 218 state->byline = 1; 219 } 220 else if (state->label || regcomb(&p->tail->re, &x->re)) 221 { 222 p->tail = p->tail->next = x; 223 if (!state->byline && (state->number || !state->label || !regrecord(&x->re))) 224 state->byline = 1; 225 } 226 else 227 free(x); 228 } 229 230 static void 231 addstring(State_s *state, List_t* p, char* s) 232 { 233 Item_t* x; 234 235 if (!(x = newof(0, Item_t, 1, strlen(s)))) 236 error(ERROR_SYSTEM|3, "out of space (string `%s')", s); 237 strcpy(x->string, s); 238 if (p->head) 239 p->tail->next = x; 240 else 241 p->head = x; 242 p->tail = x; 243 } 244 245 static void 246 compile(State_s *state) 247 { 248 int line; 249 size_t n; 250 char* s; 251 char* t; 252 char* file; 253 Item_t* x; 254 Sfio_t* f; 255 256 for (x = state->pattern.head; x; x = x->next) 257 addre(state, &state->re, x->string); 258 for (x = state->file.head; x; x = x->next) 259 { 260 s = x->string; 261 if (!(f = sfopen(NiL, s, "r"))) 262 error(ERROR_SYSTEM|4, "%s: cannot open", s); 263 else 264 { 265 file = error_info.file; 266 error_info.file = s; 267 line = error_info.line; 268 error_info.line = 0; 269 while (s = (char*)sfreserve(f, SF_UNBOUND, SF_LOCKR)) 270 { 271 if (!(n = sfvalue(f))) 272 break; 273 if (s[n - 1] != '\n') 274 { 275 for (t = s + n; t > s && *--t != '\n'; t--); 276 if (t == s) 277 { 278 sfread(f, s, 0); 279 break; 280 } 281 n = t - s + 1; 282 } 283 s[n - 1] = 0; 284 addre(state, &state->re, s); 285 s[n - 1] = '\n'; 286 sfread(f, s, n); 287 } 288 while ((s = sfgetr(f, '\n', 1)) || (s = sfgetr(f, '\n', -1))) 289 { 290 error_info.line++; 291 addre(state, &state->re, s); 292 } 293 error_info.file = file; 294 error_info.line = line; 295 sfclose(f); 296 } 297 } 298 if (!state->re.head) 299 error(3, "no pattern"); 300 } 301 302 static void 303 highlight(Sfio_t* sp, const char* s, int n, int so, int eo) 304 { 305 static const char bold[] = {CC_esc,'[','1','m'}; 306 static const char normal[] = {CC_esc,'[','0','m'}; 307 308 sfwrite(sp, s, so); 309 sfwrite(sp, bold, sizeof(bold)); 310 sfwrite(sp, s + so, eo - so); 311 sfwrite(sp, normal, sizeof(normal)); 312 sfwrite(sp, s + eo, n - eo); 313 } 314 315 typedef struct 316 { 317 State_s *state; 318 Item_t *item; 319 } record_handle; 320 321 static int 322 record(void* handle, const char* s, size_t len) 323 { 324 record_handle *r_x = (record_handle *)handle; 325 State_s *state = r_x->state; 326 Item_t *item = r_x->item; 327 328 item->hits++; 329 if (state->query || state->list) 330 return -1; 331 if (!state->count) 332 { 333 if (state->prefix) 334 sfprintf(sfstdout, "%s:", error_info.file); 335 if (state->label) 336 sfprintf(sfstdout, "%s:", item->string); 337 if (state->pos) 338 highlight(sfstdout, s, len + 1, state->pos[0].rm_so, state->pos[0].rm_eo); 339 else 340 sfwrite(sfstdout, s, len + 1); 341 } 342 return 0; 343 } 344 345 static void 346 execute(State_s *state, Sfio_t* input, char* name) 347 { 348 register char* s; 349 char* file; 350 Item_t* x; 351 size_t len; 352 int result; 353 int line; 354 355 Sfulong_t hits = 0; 356 357 if (state->buffer.noshare) 358 sfset(input, SF_SHARE, 0); 359 if (state->buffer.size) 360 sfsetbuf(input, state->buffer.base, state->buffer.size); 361 if (!name) 362 name = "/dev/stdin"; 363 file = error_info.file; 364 error_info.file = name; 365 line = error_info.line; 366 error_info.line = 0; 367 if (state->byline) 368 { 369 for (;;) 370 { 371 error_info.line++; 372 if (s = sfgetr(input, '\n', 0)) 373 len = sfvalue(input) - 1; 374 else if (s = sfgetr(input, '\n', -1)) 375 { 376 len = sfvalue(input); 377 s[len] = '\n'; 378 #if _you_like_the_noise 379 error(1, "newline appended"); 380 #endif 381 } 382 else 383 { 384 if (sferror(input) && errno != EISDIR) 385 error(ERROR_SYSTEM|2, "read error"); 386 break; 387 } 388 x = state->re.head; 389 do 390 { 391 if (!(result = regnexec(&x->re, s, len, state->posnum, state->pos, 0))) 392 { 393 if (!state->label) 394 break; 395 x->hits++; 396 if (state->query || state->list) 397 goto done; 398 if (!state->count) 399 { 400 if (state->prefix) 401 sfprintf(sfstdout, "%s:", name); 402 if (state->number) 403 sfprintf(sfstdout, "%d:", error_info.line); 404 sfprintf(sfstdout, "%s:", x->string); 405 if (state->pos) 406 highlight(sfstdout, s, len + 1, state->pos[0].rm_so, state->pos[0].rm_eo); 407 else 408 sfwrite(sfstdout, s, len + 1); 409 } 410 } 411 else if (result != REG_NOMATCH) 412 regfatal(&x->re, 3, result); 413 } while (x = x->next); 414 if (!state->label && (x != 0) == state->match) 415 { 416 hits++; 417 if (state->query || state->list) 418 break; 419 if (!state->count) 420 { 421 if (state->prefix) 422 sfprintf(sfstdout, "%s:", name); 423 if (state->number) 424 sfprintf(sfstdout, "%d:", error_info.line); 425 if (state->pos) 426 highlight(sfstdout, s, len + 1, state->pos[0].rm_so, state->pos[0].rm_eo); 427 else 428 sfwrite(sfstdout, s, len + 1); 429 } 430 } 431 } 432 } 433 else 434 { 435 register char* e; 436 register char* t; 437 char* r; 438 439 static char* span = 0; 440 static size_t spansize = 0; 441 442 s = e = 0; 443 for (;;) 444 { 445 if (s < e) 446 { 447 t = span; 448 for (;;) 449 { 450 len = 2 * (e - s) + t - span + 1; 451 len = roundof(len, SF_BUFSIZE); 452 if (spansize < len) 453 { 454 spansize = len; 455 len = t - span; 456 if (!(span = newof(span, char, spansize, 0))) 457 error(ERROR_SYSTEM|3, "%s: line longer than %lu characters", name, len + e - s); 458 t = span + len; 459 } 460 len = e - s; 461 memcpy(t, s, len); 462 t += len; 463 if (!(s = sfreserve(input, SF_UNBOUND, 0)) || (len = sfvalue(input)) <= 0) 464 { 465 if ((sfvalue(input) || sferror(input)) && errno != EISDIR) 466 error(ERROR_SYSTEM|2, "%s: read error", name); 467 break; 468 } 469 else if (!(e = memchr(s, '\n', len))) 470 e = s + len; 471 else 472 { 473 r = s + len; 474 len = (e - s) + t - span; 475 len = roundof(len, SF_BUFSIZE); 476 if (spansize < len) 477 { 478 spansize = len; 479 len = t - span; 480 if (!(span = newof(span, char, spansize, 0))) 481 error(ERROR_SYSTEM|3, "%s: line longer than %lu characters", name, len + e - s); 482 t = span + len; 483 } 484 len = e - s; 485 memcpy(t, s, len); 486 t += len; 487 s += len + 1; 488 e = r; 489 break; 490 } 491 } 492 *t = '\n'; 493 x = state->re.head; 494 do 495 { 496 record_handle r_x = { state, x }; 497 if ((result = regrexec(&x->re, span, t - span, state->posnum, state->pos, state->options, '\n', (void*)&r_x, record)) < 0) 498 goto done; 499 if (result && result != REG_NOMATCH) 500 regfatal(&x->re, 3, result); 501 } while (x = x->next); 502 if (!s) 503 break; 504 } 505 else 506 { 507 if (!(s = sfreserve(input, SF_UNBOUND, 0))) 508 { 509 if ((sfvalue(input) || sferror(input)) && errno != EISDIR) 510 error(ERROR_SYSTEM|2, "%s: read error", name); 511 break; 512 } 513 if ((len = sfvalue(input)) <= 0) 514 break; 515 e = s + len; 516 } 517 t = e; 518 while (t > s) 519 if (*--t == '\n') 520 { 521 x = state->re.head; 522 do 523 { 524 record_handle r_x = { state, x }; 525 if ((result = regrexec(&x->re, s, t - s, state->posnum, state->pos, state->options, '\n', (void*)&r_x, record)) < 0) 526 goto done; 527 if (result && result != REG_NOMATCH) 528 regfatal(&x->re, 3, result); 529 } while (x = x->next); 530 s = t + 1; 531 break; 532 } 533 } 534 } 535 done: 536 error_info.file = file; 537 error_info.line = line; 538 if (state->byline && !state->label) 539 { 540 if (hits && state->list >= 0) 541 state->any = 1; 542 if (!state->query) 543 { 544 if (!state->list) 545 { 546 if (state->count) 547 { 548 if (state->count & 2) 549 state->hits += hits; 550 else 551 { 552 if (state->prefix) 553 sfprintf(sfstdout, "%s:", name); 554 sfprintf(sfstdout, "%I*u\n", sizeof(hits), hits); 555 } 556 } 557 } 558 else if ((hits != 0) == (state->list > 0)) 559 { 560 if (state->list < 0) 561 state->any = 1; 562 sfprintf(sfstdout, "%s\n", name); 563 } 564 } 565 } 566 else 567 { 568 x = state->re.head; 569 do 570 { 571 if (x->hits && state->list >= 0) 572 { 573 state->any = 1; 574 if (state->query) 575 break; 576 } 577 if (!state->query) 578 { 579 if (!state->list) 580 { 581 if (state->count) 582 { 583 if (state->count & 2) 584 { 585 x->total += x->hits; 586 state->hits += x->hits; 587 } 588 else 589 { 590 if (state->prefix) 591 sfprintf(sfstdout, "%s:", name); 592 if (state->label) 593 sfprintf(sfstdout, "%s:", x->string); 594 sfprintf(sfstdout, "%I*u\n", sizeof(x->hits), x->hits); 595 } 596 } 597 } 598 else if ((x->hits != 0) == (state->list > 0)) 599 { 600 if (state->list < 0) 601 state->any = 1; 602 if (state->label) 603 sfprintf(sfstdout, "%s:%s\n", name, x->string); 604 else 605 sfprintf(sfstdout, "%s\n", name); 606 } 607 } 608 x->hits = 0; 609 } while (x = x->next); 610 } 611 } 612 613 614 static 615 int grep_main(int argc, char** argv, void *context) 616 { 617 int c; 618 char* s; 619 char* h; 620 Sfio_t* f; 621 State_s state; 622 memset(&state, 0, sizeof(state)); 623 624 NoP(argc); 625 state.match = 1; 626 state.options = REG_FIRST|REG_NOSUB|REG_NULL; 627 h = 0; 628 if (strcmp(astconf("CONFORMANCE", NiL, NiL), "standard")) 629 state.options |= REG_LENIENT; 630 if (s = strrchr(argv[0], '/')) 631 s++; 632 else 633 s = argv[0]; 634 switch (*s) 635 { 636 case 'e': 637 case 'E': 638 s = "egrep"; 639 state.options |= REG_EXTENDED; 640 break; 641 case 'f': 642 case 'F': 643 s = "fgrep"; 644 state.options |= REG_LITERAL; 645 break; 646 case 'p': 647 case 'P': 648 s = "pgrep"; 649 state.options |= REG_EXTENDED|REG_LENIENT; 650 break; 651 case 'x': 652 case 'X': 653 s = "xgrep"; 654 state.options |= REG_AUGMENTED; 655 break; 656 default: 657 s = "grep"; 658 break; 659 } 660 error_info.id = s; 661 while (c = optget(argv, usage)) 662 switch (c) 663 { 664 case 'E': 665 state.options |= REG_EXTENDED; 666 break; 667 case 'F': 668 state.options |= REG_LITERAL; 669 break; 670 case 'G': 671 state.options &= ~(REG_AUGMENTED|REG_EXTENDED); 672 break; 673 case 'H': 674 state.prefix = opt_info.num; 675 break; 676 case 'L': 677 state.list = -opt_info.num; 678 break; 679 case 'N': 680 h = opt_info.arg; 681 break; 682 case 'O': 683 state.options |= REG_LENIENT; 684 break; 685 case 'P': 686 state.options |= REG_EXTENDED|REG_LENIENT; 687 break; 688 case 'S': 689 state.options &= ~REG_LENIENT; 690 break; 691 case 'T': 692 s = opt_info.arg; 693 switch (*s) 694 { 695 case 'b': 696 case 'm': 697 c = *s++; 698 state.buffer.size = strton(s, &s, NiL, 1); 699 if (c == 'b' && !(state.buffer.base = newof(0, char, state.buffer.size, 0))) 700 error(ERROR_SYSTEM|3, "out of space [test buffer]"); 701 if (*s) 702 error(3, "%s: invalid characters after test", s); 703 break; 704 case 'f': 705 state.options |= REG_FIRST; 706 break; 707 case 'l': 708 state.options |= REG_LEFT; 709 break; 710 case 'n': 711 state.buffer.noshare = 1; 712 break; 713 case 'r': 714 state.options |= REG_RIGHT; 715 break; 716 default: 717 error(3, "%s: unknown test", s); 718 break; 719 } 720 break; 721 case 'X': 722 state.options |= REG_AUGMENTED; 723 break; 724 case 'a': 725 break; 726 case 'b': 727 state.options &= ~(REG_FIRST|REG_NOSUB); 728 break; 729 case 'c': 730 state.count |= 1; 731 break; 732 case 'e': 733 addstring(&state, &state.pattern, opt_info.arg); 734 break; 735 case 'f': 736 addstring(&state, &state.file, opt_info.arg); 737 break; 738 case 'h': 739 state.prefix = 2; 740 break; 741 case 'i': 742 state.options |= REG_ICASE; 743 break; 744 case 'l': 745 state.list = opt_info.num; 746 break; 747 case 'm': 748 state.label = 1; 749 break; 750 case 'n': 751 state.number = 1; 752 break; 753 case 'q': 754 state.query = 1; 755 break; 756 case 's': 757 state.suppress = opt_info.num; 758 break; 759 case 't': 760 state.count |= 2; 761 break; 762 case 'v': 763 if (state.match = !opt_info.num) 764 state.options &= ~REG_INVERT; 765 else 766 state.options |= REG_INVERT; 767 break; 768 case 'w': 769 state.words = 1; 770 break; 771 case 'x': 772 state.options |= REG_LEFT|REG_RIGHT; 773 break; 774 case '?': 775 error(ERROR_USAGE|4, "%s", opt_info.arg); 776 break; 777 case ':': 778 error(2, "%s", opt_info.arg); 779 break; 780 default: 781 error(3, "%s: not implemented", opt_info.name); 782 break; 783 } 784 argv += opt_info.index; 785 if ((state.options & REG_LITERAL) && (state.options & (REG_AUGMENTED|REG_EXTENDED))) 786 error(3, "-F and -A or -P or -X are incompatible"); 787 if ((state.options & REG_LITERAL) && state.words) 788 error(ERROR_SYSTEM|3, "-F and -w are incompatible"); 789 if (!state.file.head && !state.pattern.head) 790 { 791 if (!argv[0]) 792 error(3, "no pattern"); 793 addstring(&state, &state.pattern, *argv++); 794 } 795 if (!(state.options & (REG_FIRST|REG_NOSUB))) 796 { 797 if (state.count || state.list || state.query || (state.options & REG_INVERT)) 798 state.options |= REG_FIRST|REG_NOSUB; 799 else 800 { 801 state.pos = state.posvec; 802 state.posnum = elementsof(state.posvec); 803 } 804 } 805 compile(&state); 806 if (!argv[0]) 807 { 808 state.prefix = h ? 1 : 0; 809 execute(&state, sfstdin, h); 810 } 811 else 812 { 813 if (state.prefix > 1) 814 state.prefix = 0; 815 else if (argv[1]) 816 state.prefix = 1; 817 while (s = *argv++) 818 { 819 if (f = sfopen(NiL, s, "r")) 820 { 821 execute(&state, f, s); 822 sfclose(f); 823 if (state.query && state.any) 824 break; 825 } 826 else 827 { 828 state.notfound = 1; 829 if (!state.suppress) 830 error(ERROR_SYSTEM|2, "%s: cannot open", s); 831 } 832 } 833 } 834 if ((state.count & 2) && !state.query && !state.list) 835 { 836 if (state.label) 837 { 838 Item_t* x; 839 840 x = state.re.head; 841 do 842 { 843 sfprintf(sfstdout, "%s:%I*u\n", x->string, sizeof(x->total), x->total); 844 } while (x = x->next); 845 } 846 else 847 sfprintf(sfstdout, "%I*u\n", sizeof(state.hits), state.hits); 848 } 849 return (state.notfound && !state.query) ? 2 : !state.any; 850 } 851 852 853 int b_egrep(int argc, char** argv, void *context) 854 { 855 return grep_main(argc, argv, context); 856 } 857 858 int b_grep(int argc, char** argv, void *context) 859 { 860 return grep_main(argc, argv, context); 861 } 862 863 int b_fgrep(int argc, char** argv, void *context) 864 { 865 return grep_main(argc, argv, context); 866 } 867 868 int b_pgrep(int argc, char** argv, void *context) 869 { 870 return grep_main(argc, argv, context); 871 } 872 873 int b_xgrep(int argc, char** argv, void *context) 874 { 875 return grep_main(argc, argv, context); 876 } 877