Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/cmd/re/grep.c
1808 views
1
/***********************************************************************
2
* *
3
* This software is part of the ast package *
4
* Copyright (c) 1995-2012 AT&T Intellectual Property *
5
* and is licensed under the *
6
* Eclipse Public License, Version 1.0 *
7
* by AT&T Intellectual Property *
8
* *
9
* A copy of the License is available at *
10
* http://www.eclipse.org/org/documents/epl-v10.html *
11
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12
* *
13
* Information and Software Systems Research *
14
* AT&T Research *
15
* Florham Park NJ *
16
* *
17
* Glenn Fowler <[email protected]> *
18
* *
19
***********************************************************************/
20
#pragma prototyped
21
22
static const char usage[] =
23
"[-?\n@(#)$Id: grep (AT&T Research) 2012-05-07 $\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
" If ,\aafter\a is omitted then it is set to \abefore\a."
50
" By default only matched lines are printed.]:?"
51
" [before[,after]]:=2,2]"
52
"[c:count?Only print a matching line count for each file.]"
53
"[e:expression|pattern|regexp?Specify a matching \apattern\a. More than one"
54
" \apattern\a implies alternation. If this option is specified"
55
" then the command line \apattern\a must be omitted.]:"
56
" [pattern]"
57
"[f:file?Each line in \apattern-file\a is a \apattern\a, placed into a single"
58
" alternating expression.]:"
59
" [pattern-file]"
60
"[H:filename|with-filename?Prefix each matched line with the containing file name.]"
61
"[h:no-filename?Suppress containing file name prefix for each matched line.]"
62
"[i:ignore-case?Ignore case when matching.]"
63
"[l:files-with-matches?Only print file names with at least one match.]"
64
"[L:files-without-matches?Only print file names with no matches.]"
65
"[b:highlight?Highlight matches using the ansi terminal bold sequence.]"
66
"[v:invert-match|revert-match?Invert the \apattern\a match sense.]"
67
"[m:label?All patterns must be of the form \alabel\a:\apattern\a. Match and"
68
" count output will be prefixed by the corresponding \alabel\a:.]"
69
"[O:lenient?Enable lenient \apattern\a interpretation. This is the default.]"
70
"[x:line-match|line-regexp?Force \apatterns\a to match complete lines.]"
71
"[n:number|line-number?Prefix each matched line with its line number.]"
72
"[N:name?Set the standard input file name prefix to"
73
" \aname\a.]:[name:=empty]"
74
"[q:quiet|silent?Do not print matching lines.]"
75
"[r|R:recursive?Recursively process all files in each named directory. "
76
"Use \btw -e\b \aexpression\a \bgrep ...\b to control the directory "
77
"traversal.]"
78
"[S:strict?Enable strict \apattern\a interpretation with diagnostics.]"
79
"[s:suppress|no-messages?Suppress error and warning messages.]"
80
"[t:total?Only print a single matching line count for all files.]"
81
"[T:test?Enable implementation specific tests.]:"
82
" [test]"
83
"[w:word-match|word-regexp?Force \apatterns\a to match complete words.]"
84
"[a?Ignored for GNU compatibility.]"
85
"[Y:color|colour?Ignored for GNU compatibility.]:[when]"
86
"\n"
87
"\n[ pattern ] [ file ... ]\n"
88
"\n"
89
"[+DIAGNOSTICS?Exit status 0 if matches were found, 1 if no matches were found,"
90
" where \b-v\b invertes the exit status. Exit status 2 for other"
91
" errors that are accompanied by a message on the standard error.]"
92
"[+SEE ALSO?\bed\b(1), \bsed\b(1), \bperl\b(1), \btw\b(1), \bregex\b(3)]"
93
"[+CAVEATS?Some expressions of necessity require exponential space"
94
" and/or time.]"
95
"[+BUGS?Some expressions may use sub-optimal algorithms. For example,"
96
" don't use this implementation to compute primes.]"
97
;
98
99
#include <ast.h>
100
#include <ctype.h>
101
#include <ccode.h>
102
#include <error.h>
103
#include <fts.h>
104
#include <regex.h>
105
106
#ifndef EISDIR
107
#define EISDIR (-1)
108
#endif
109
110
/*
111
* snarfed from Doug McElroy's C++ version
112
*
113
* this grep is based on the Posix re package.
114
* unfortunately it has to have a nonstandard interface.
115
* 1. fgrep does not have usual operators. REG_LITERAL
116
* caters for this.
117
* 2. grep allows null expressions, hence REG_NULL.
118
* 3. it may be possible to combine the multiple
119
* patterns of grep into single patterns. important
120
* special cases are handled by regcomb().
121
* 4. anchoring by -x has to be done separately from
122
* compilation (remember that fgrep has no ^ or $ operator),
123
* hence REG_LEFT|REG_RIGHT. (An honest, but slow alternative:
124
* run regexec with REG_NOSUB off and nmatch=1 and check
125
* whether the match is full length)
126
*/
127
128
typedef struct Item_s /* list item - sue me for waste */
129
{
130
struct Item_s* next; /* next in list */
131
regex_t re; /* compiled re */
132
Sfulong_t hits; /* labeled pattern matches */
133
Sfulong_t total; /* total hits */
134
char string[1]; /* string value */
135
} Item_t;
136
137
typedef struct List_s /* generic list */
138
{
139
Item_t* head; /* list head */
140
Item_t* tail; /* list tail */
141
} List_t;
142
143
static struct State_s /* program state */
144
{
145
struct
146
{
147
char* base; /* sfsetbuf buffer */
148
size_t size; /* sfsetbuf size */
149
int noshare; /* turn off SF_SHARE */
150
} buffer;
151
152
List_t file; /* pattern file list */
153
List_t pattern; /* pattern list */
154
List_t re; /* re list */
155
156
regmatch_t posvec[1]; /* match position vector */
157
regmatch_t* pos; /* match position pointer */
158
int posnum; /* number of match positions */
159
160
int any; /* if any pattern hit */
161
int after; /* # lines to list after match */
162
int before; /* # lines to list before match */
163
int list; /* list files with hits */
164
int notfound; /* some input file not found */
165
int options; /* regex options */
166
167
Sfulong_t hits; /* total matched pattern count */
168
169
unsigned char byline; /* multiple pattern line by line*/
170
unsigned char count; /* count number of hits */
171
unsigned char label; /* all patterns labeled */
172
unsigned char match; /* match sense */
173
unsigned char query; /* return status but no output */
174
unsigned char number; /* line numbers */
175
unsigned char prefix; /* print file prefix */
176
unsigned char suppress; /* no unopenable file messages */
177
unsigned char words; /* word matches only */
178
} state;
179
180
static void
181
addre(List_t* p, char* s)
182
{
183
int c;
184
char* b;
185
Item_t* x;
186
Sfio_t* t;
187
188
b = s;
189
if (state.label)
190
{
191
if (!(s = strchr(s, ':')))
192
error(3, "%s: label:pattern expected", b);
193
c = s - b;
194
s++;
195
}
196
else
197
c = 0;
198
if (!(x = newof(0, Item_t, 1, c)))
199
error(ERROR_SYSTEM|3, "out of space (pattern `%s')", b);
200
if (c)
201
memcpy(x->string, b, c);
202
if (state.words)
203
{
204
if (!(t = sfstropen()))
205
error(ERROR_SYSTEM|3, "out of space (word pattern `%s')", s);
206
if (!(state.options & REG_AUGMENTED))
207
sfputc(t, '\\');
208
sfputc(t, '<');
209
sfputr(t, s, -1);
210
if (!(state.options & REG_AUGMENTED))
211
sfputc(t, '\\');
212
sfputc(t, '>');
213
if (!(s = sfstruse(t)))
214
error(ERROR_SYSTEM|3, "out of space");
215
}
216
else
217
t = 0;
218
if (c = regcomp(&x->re, s, state.options|REG_MULTIPLE))
219
regfatal(&x->re, 4, c);
220
if (t)
221
sfstrclose(t);
222
if (!p->head)
223
{
224
p->head = p->tail = x;
225
if (state.number || state.before || state.after || !regrecord(&x->re))
226
state.byline = 1;
227
}
228
else if (state.label || regcomb(&p->tail->re, &x->re))
229
{
230
p->tail = p->tail->next = x;
231
if (!state.byline && (state.number || !state.label || !regrecord(&x->re)))
232
state.byline = 1;
233
}
234
else
235
free(x);
236
}
237
238
static void
239
addstring(List_t* p, char* s)
240
{
241
Item_t* x;
242
243
if (!(x = newof(0, Item_t, 1, strlen(s))))
244
error(ERROR_SYSTEM|3, "out of space (string `%s')", s);
245
strcpy(x->string, s);
246
if (p->head)
247
p->tail->next = x;
248
else
249
p->head = x;
250
p->tail = x;
251
}
252
253
static void
254
compile(void)
255
{
256
int line;
257
size_t n;
258
char* s;
259
char* t;
260
char* file;
261
Item_t* x;
262
Sfio_t* f;
263
264
for (x = state.pattern.head; x; x = x->next)
265
addre(&state.re, x->string);
266
for (x = state.file.head; x; x = x->next)
267
{
268
s = x->string;
269
if (!(f = sfopen(NiL, s, "r")))
270
error(ERROR_SYSTEM|4, "%s: cannot open", s);
271
else
272
{
273
file = error_info.file;
274
error_info.file = s;
275
line = error_info.line;
276
error_info.line = 0;
277
while (s = (char*)sfreserve(f, SF_UNBOUND, SF_LOCKR))
278
{
279
if (!(n = sfvalue(f)))
280
break;
281
if (s[n - 1] != '\n')
282
{
283
for (t = s + n; t > s && *--t != '\n'; t--);
284
if (t == s)
285
{
286
sfread(f, s, 0);
287
break;
288
}
289
n = t - s + 1;
290
}
291
s[n - 1] = 0;
292
addre(&state.re, s);
293
s[n - 1] = '\n';
294
sfread(f, s, n);
295
}
296
while ((s = sfgetr(f, '\n', 1)) || (s = sfgetr(f, '\n', -1)))
297
{
298
error_info.line++;
299
addre(&state.re, s);
300
}
301
error_info.file = file;
302
error_info.line = line;
303
sfclose(f);
304
}
305
}
306
if (!state.re.head)
307
error(3, "no pattern");
308
}
309
310
static void
311
highlight(Sfio_t* sp, const char* s, int n, int so, int eo)
312
{
313
static const char bold[] = {CC_esc,'[','1','m'};
314
static const char normal[] = {CC_esc,'[','0','m'};
315
316
sfwrite(sp, s, so);
317
sfwrite(sp, bold, sizeof(bold));
318
sfwrite(sp, s + so, eo - so);
319
sfwrite(sp, normal, sizeof(normal));
320
sfwrite(sp, s + eo, n - eo);
321
}
322
323
static int
324
record(void* handle, const char* s, size_t len)
325
{
326
Item_t* item = (Item_t*)handle;
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(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 = "(standard input)"; /* posix! (ast prefers /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, 4, 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
if (!(len = t - span))
494
len++;
495
x = state.re.head;
496
do
497
{
498
if ((result = regrexec(&x->re, span, len, state.posnum, state.pos, state.options, '\n', (void*)x, record)) < 0)
499
goto done;
500
if (result && result != REG_NOMATCH)
501
regfatal(&x->re, 4, result);
502
} while (x = x->next);
503
if (!s)
504
break;
505
}
506
else
507
{
508
if (!(s = sfreserve(input, SF_UNBOUND, 0)))
509
{
510
if ((sfvalue(input) || sferror(input)) && errno != EISDIR)
511
error(ERROR_SYSTEM|2, "%s: read error", name);
512
break;
513
}
514
if ((len = sfvalue(input)) <= 0)
515
break;
516
e = s + len;
517
}
518
t = e;
519
while (t > s)
520
if (*--t == '\n')
521
{
522
len = t - s;
523
if (!len || t > s && *(t - 1) == '\n')
524
len++;
525
x = state.re.head;
526
do
527
{
528
if ((result = regrexec(&x->re, s, len, state.posnum, state.pos, state.options, '\n', (void*)x, record)) < 0)
529
goto done;
530
if (result && result != REG_NOMATCH)
531
regfatal(&x->re, 4, result);
532
} while (x = x->next);
533
s = t + 1;
534
break;
535
}
536
}
537
}
538
done:
539
error_info.file = file;
540
error_info.line = line;
541
if (state.byline && !state.label)
542
{
543
if (hits && state.list >= 0)
544
state.any = 1;
545
if (!state.query)
546
{
547
if (!state.list)
548
{
549
if (state.count)
550
{
551
if (state.count & 2)
552
state.hits += hits;
553
else
554
{
555
if (state.prefix)
556
sfprintf(sfstdout, "%s:", name);
557
sfprintf(sfstdout, "%I*u\n", sizeof(hits), hits);
558
}
559
}
560
}
561
else if ((hits != 0) == (state.list > 0))
562
{
563
if (state.list < 0)
564
state.any = 1;
565
sfprintf(sfstdout, "%s\n", name);
566
}
567
}
568
}
569
else
570
{
571
x = state.re.head;
572
do
573
{
574
if (x->hits && state.list >= 0)
575
{
576
state.any = 1;
577
if (state.query)
578
break;
579
}
580
if (!state.query)
581
{
582
if (!state.list)
583
{
584
if (state.count)
585
{
586
if (state.count & 2)
587
{
588
x->total += x->hits;
589
state.hits += x->hits;
590
}
591
else
592
{
593
if (state.prefix)
594
sfprintf(sfstdout, "%s:", name);
595
if (state.label)
596
sfprintf(sfstdout, "%s:", x->string);
597
sfprintf(sfstdout, "%I*u\n", sizeof(x->hits), x->hits);
598
}
599
}
600
}
601
else if ((x->hits != 0) == (state.list > 0))
602
{
603
if (state.list < 0)
604
state.any = 1;
605
if (state.label)
606
sfprintf(sfstdout, "%s:%s\n", name, x->string);
607
else
608
sfprintf(sfstdout, "%s\n", name);
609
}
610
}
611
x->hits = 0;
612
} while (x = x->next);
613
}
614
}
615
616
int
617
main(int argc, char** argv)
618
{
619
int c;
620
char* s;
621
char* h;
622
Sfio_t* f;
623
int flags;
624
int old = 0;
625
FTS* fts;
626
FTSENT* ent;
627
628
NoP(argc);
629
flags = fts_flags() | FTS_META | FTS_TOP | FTS_NOPOSTORDER | FTS_NOSEEDOTDIR;
630
state.match = 1;
631
state.options = REG_FIRST|REG_NOSUB|REG_NULL;
632
h = 0;
633
if (!conformance(0, 0))
634
state.options |= REG_LENIENT;
635
if (s = strrchr(argv[0], '/'))
636
s++;
637
else
638
s = argv[0];
639
switch (*s)
640
{
641
case 'e':
642
case 'E':
643
s = "egrep";
644
state.options |= REG_EXTENDED;
645
break;
646
case 'f':
647
case 'F':
648
s = "fgrep";
649
state.options |= REG_LITERAL;
650
break;
651
case 'p':
652
case 'P':
653
s = "pgrep";
654
state.options |= REG_EXTENDED|REG_LENIENT;
655
break;
656
case 'x':
657
case 'X':
658
s = "xgrep";
659
state.options |= REG_AUGMENTED;
660
break;
661
default:
662
s = "grep";
663
break;
664
}
665
error_info.id = s;
666
while (c = optget(argv, usage))
667
switch (c)
668
{
669
case 'C':
670
if (opt_info.arg)
671
{
672
state.before = (int)strtol(opt_info.arg, &s, 0);
673
state.after = (*s == ',') ? (int)strtol(s + 1, &s, 0) : state.before;
674
if (*s)
675
error(3, "%s: invalid context line count", s);
676
}
677
else
678
state.before = state.after = 2;
679
break;
680
case 'E':
681
state.options |= REG_EXTENDED;
682
break;
683
case 'F':
684
state.options |= REG_LITERAL;
685
break;
686
case 'G':
687
state.options &= ~(REG_AUGMENTED|REG_EXTENDED);
688
break;
689
case 'H':
690
state.prefix = opt_info.num;
691
break;
692
case 'L':
693
state.list = -opt_info.num;
694
break;
695
case 'N':
696
h = opt_info.arg;
697
break;
698
case 'O':
699
state.options |= REG_LENIENT;
700
break;
701
case 'P':
702
state.options |= REG_EXTENDED|REG_LENIENT;
703
break;
704
case 'S':
705
state.options &= ~REG_LENIENT;
706
break;
707
case 'T':
708
s = opt_info.arg;
709
switch (*s)
710
{
711
case 'b':
712
case 'm':
713
c = *s++;
714
state.buffer.size = strton(s, &s, NiL, 1);
715
if (c == 'b' && !(state.buffer.base = newof(0, char, state.buffer.size, 0)))
716
error(ERROR_SYSTEM|3, "out of space [test buffer]");
717
if (*s)
718
error(3, "%s: invalid characters after test", s);
719
break;
720
case 'f':
721
state.options |= REG_FIRST;
722
break;
723
case 'l':
724
state.options |= REG_LEFT;
725
break;
726
case 'n':
727
state.buffer.noshare = 1;
728
break;
729
case 'r':
730
state.options |= REG_RIGHT;
731
break;
732
default:
733
error(3, "%s: unknown test", s);
734
break;
735
}
736
break;
737
case 'X':
738
state.options |= REG_AUGMENTED;
739
break;
740
case 'a':
741
break;
742
case 'b':
743
state.options &= ~(REG_FIRST|REG_NOSUB);
744
break;
745
case 'c':
746
state.count |= 1;
747
break;
748
case 'e':
749
addstring(&state.pattern, opt_info.arg);
750
break;
751
case 'f':
752
addstring(&state.file, opt_info.arg);
753
break;
754
case 'h':
755
state.prefix = 2;
756
break;
757
case 'i':
758
state.options |= REG_ICASE;
759
break;
760
case 'l':
761
state.list = opt_info.num;
762
break;
763
case 'm':
764
state.label = 1;
765
break;
766
case 'n':
767
state.number = 1;
768
break;
769
case 'q':
770
state.query = 1;
771
break;
772
case 'r':
773
if (opt_info.num)
774
flags &= ~FTS_TOP;
775
else
776
old = 1;
777
break;
778
case 's':
779
state.suppress = opt_info.num;
780
break;
781
case 't':
782
state.count |= 2;
783
break;
784
case 'v':
785
if (state.match = !opt_info.num)
786
state.options &= ~REG_INVERT;
787
else
788
state.options |= REG_INVERT;
789
break;
790
case 'w':
791
state.words = 1;
792
break;
793
case 'x':
794
state.options |= REG_LEFT|REG_RIGHT;
795
break;
796
case 'Y':
797
/* ignored for GNU compatibility */
798
break;
799
case '?':
800
error(ERROR_USAGE|4, "%s", opt_info.arg);
801
break;
802
case ':':
803
error(2, "%s", opt_info.arg);
804
break;
805
default:
806
error(3, "%s: not implemented", opt_info.name);
807
break;
808
}
809
argv += opt_info.index;
810
if ((state.options & REG_LITERAL) && (state.options & (REG_AUGMENTED|REG_EXTENDED)))
811
error(3, "-F and -A or -P or -X are incompatible");
812
if ((state.options & REG_LITERAL) && state.words)
813
error(ERROR_SYSTEM|3, "-F and -w are incompatible");
814
if (!state.file.head && !state.pattern.head)
815
{
816
if (!argv[0])
817
error(3, "no pattern");
818
addstring(&state.pattern, *argv++);
819
}
820
if (!(state.options & (REG_FIRST|REG_NOSUB)))
821
{
822
if (state.count || state.list || state.query || (state.options & REG_INVERT))
823
state.options |= REG_FIRST|REG_NOSUB;
824
else
825
{
826
state.pos = state.posvec;
827
state.posnum = elementsof(state.posvec);
828
}
829
}
830
compile();
831
if (!argv[0])
832
{
833
state.prefix = h ? 1 : 0;
834
execute(sfstdin, h);
835
}
836
else if (old) /* just in case the fts logic is incompatible */
837
{
838
if (state.prefix > 1)
839
state.prefix = 0;
840
else if (argv[1])
841
state.prefix = 1;
842
while (s = *argv++)
843
{
844
if (f = sfopen(NiL, s, "r"))
845
{
846
execute(f, s);
847
sfclose(f);
848
if (state.query && state.any)
849
break;
850
}
851
else
852
{
853
state.notfound = 1;
854
if (!state.suppress)
855
error(ERROR_SYSTEM|2, "%s: cannot open", s);
856
}
857
}
858
}
859
else
860
{
861
if (state.prefix > 1)
862
state.prefix = 0;
863
else if (!(flags & FTS_TOP) || argv[1])
864
state.prefix = 1;
865
if (!(fts = fts_open(argv, flags, NiL)))
866
error(ERROR_SYSTEM|3, "%s: not found", argv[0]);
867
while (ent = fts_read(fts))
868
switch (ent->fts_info)
869
{
870
case FTS_F:
871
if (f = sfopen(NiL, ent->fts_accpath, "r"))
872
{
873
execute(f, ent->fts_path);
874
sfclose(f);
875
if (state.query && state.any)
876
goto quit;
877
break;
878
}
879
/*FALLTHROUGH*/
880
case FTS_NS:
881
case FTS_SLNONE:
882
state.notfound = 1;
883
if (!state.suppress)
884
error(ERROR_SYSTEM|2, "%s: cannot open", ent->fts_path);
885
break;
886
case FTS_DC:
887
error(ERROR_WARNING|1, "%s: directory causes cycle", ent->fts_path);
888
break;
889
case FTS_DNR:
890
error(ERROR_SYSTEM|2, "%s: cannot read directory", ent->fts_path);
891
break;
892
case FTS_DNX:
893
error(ERROR_SYSTEM|2, "%s: cannot search directory", ent->fts_path);
894
break;
895
}
896
quit:
897
fts_close(fts);
898
}
899
if ((state.count & 2) && !state.query && !state.list)
900
{
901
if (state.label)
902
{
903
Item_t* x;
904
905
x = state.re.head;
906
do
907
{
908
sfprintf(sfstdout, "%s:%I*u\n", x->string, sizeof(x->total), x->total);
909
} while (x = x->next);
910
}
911
else
912
sfprintf(sfstdout, "%I*u\n", sizeof(state.hits), state.hits);
913
}
914
return (state.notfound && !state.query) ? 2 : !state.any;
915
}
916
917