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
addre(State_s * state,List_t * p,char * s)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
addstring(State_s * state,List_t * p,char * s)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
compile(State_s * state)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
highlight(Sfio_t * sp,const char * s,int n,int so,int eo)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
record(void * handle,const char * s,size_t len)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
execute(State_s * state,Sfio_t * input,char * name)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
grep_main(int argc,char ** argv,void * context)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
b_egrep(int argc,char ** argv,void * context)853 int b_egrep(int argc, char** argv, void *context)
854 {
855 return grep_main(argc, argv, context);
856 }
857
b_grep(int argc,char ** argv,void * context)858 int b_grep(int argc, char** argv, void *context)
859 {
860 return grep_main(argc, argv, context);
861 }
862
b_fgrep(int argc,char ** argv,void * context)863 int b_fgrep(int argc, char** argv, void *context)
864 {
865 return grep_main(argc, argv, context);
866 }
867
b_pgrep(int argc,char ** argv,void * context)868 int b_pgrep(int argc, char** argv, void *context)
869 {
870 return grep_main(argc, argv, context);
871 }
872
b_xgrep(int argc,char ** argv,void * context)873 int b_xgrep(int argc, char** argv, void *context)
874 {
875 return grep_main(argc, argv, context);
876 }
877