Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/cmd/kshlib/cmdtst/grep.c
1810 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
* Doug McIlroy <[email protected]> *
19
* *
20
***********************************************************************/
21
#pragma prototyped
22
23
static const char usage[] =
24
"[-?\n@(#)$Id: grep (AT&T Research) 2012-05-11 $\n]"
25
USAGE_LICENSE
26
"[--plugin?ksh]"
27
"[+NAME?grep - search lines in files for matching patterns]"
28
"[+DESCRIPTION?The \bgrep\b commands search the named input files for "
29
"lines containing a match for the given \apatterns\a. Matching lines are "
30
"printed by default. The standard input is searched if no files are "
31
"given or when the file \b-\b is specified.]"
32
"[+?There are six variants of \bgrep\b, each one using a different form "
33
"of \apattern\a, controlled either by option or the command path base "
34
"name. Details of each variant may be found in \bregex\b(3).]"
35
"{"
36
"[+grep?The default basic regular expressions (no "
37
"alternations.)]"
38
"[+egrep?Extended regular expressions (alternations, one or "
39
"more.)]"
40
"[+pgrep?\bperl\b(1) regular expressions (lenient extended.)]"
41
"[+xgrep?Augmented regular expressions (conjunction, negation.)]"
42
"[+fgrep?Fixed string expressions.]"
43
"[+agrep?Approximate regular expressions (not implemented.)]"
44
"}"
45
"[G:basic-regexp?\bgrep\b mode (default): basic regular expression "
46
"\apatterns\a.]"
47
"[E:extended-regexp?\begrep\b mode: extended regular expression "
48
"\apatterns\a.]"
49
"[X:augmented-regexp?\bxgrep\b mode: augmented regular expression "
50
"\apatterns\a.]"
51
"[P:perl-regexp?\bpgrep\b mode: \bperl\b(1) regular expression "
52
"\apatterns\a.]"
53
"[F:fixed-string?\bfgrep\b mode: fixed string \apatterns\a.]"
54
"[A:approximate-regexp?\bagrep\b mode: approximate regular expression "
55
"\apatterns\a (not implemented.)]"
56
"[C:context?Set the matched line context \abefore\a and \aafter\a count. "
57
"If ,\aafter\a is omitted then it is set to \abefore\a. By default only "
58
"matched lines are printed.]:?[before[,after]]:=2,2]"
59
"[c:count?Only print a matching line count for each file.]"
60
"[e:expression|pattern|regexp?Specify a matching \apattern\a. More than "
61
"one \apattern\a implies alternation. If this option is specified then "
62
"the command line \apattern\a must be omitted.]: [pattern]"
63
"[f:file?Each line in \apattern-file\a is a \apattern\a, placed into a "
64
"single alternating expression.]: [pattern-file]"
65
"[H:filename|with-filename?Prefix each matched line with the containing "
66
"file name.]"
67
"[h:no-filename?Suppress containing file name prefix for each matched "
68
"line.]"
69
"[i:ignore-case?Ignore case when matching.]"
70
"[l:files-with-matches?Only print file names with at least one match.]"
71
"[L:files-without-matches?Only print file names with no matches.]"
72
"[b:highlight?Highlight matches using the ansi terminal bold sequence.]"
73
"[v:invert-match|revert-match?Invert the \apattern\a match sense.]"
74
"[m:label?All patterns must be of the form \alabel\a:\apattern\a. Match "
75
"and count output will be prefixed by the corresponding \alabel\a:. At "
76
"most one label is output for each line; if more than one label matches "
77
"a line then it is undefined what label is output.]"
78
"[O:lenient?Enable lenient \apattern\a interpretation. This is the "
79
"default.]"
80
"[x:line-match|line-regexp?Force \apatterns\a to match complete lines.]"
81
"[n:number|line-number?Prefix each matched line with its line number.]"
82
"[N:name?Set the standard input file name prefix to "
83
"\aname\a.]:[name:=empty]"
84
"[q:quiet|silent?Do not print matching lines.]"
85
"[r|R:recursive?Recursively process all files in each named directory. "
86
"Use \btw -e\b \aexpression\a \bgrep ...\b to control the directory "
87
"traversal.]"
88
"[S:strict?Enable strict \apattern\a interpretation with diagnostics.]"
89
"[s:suppress|no-messages?Suppress error and warning messages.]"
90
"[t:total?Only print a single matching line count for all files.]"
91
"[T:test?Enable implementation specific tests.]: [test]"
92
"[w:word-match|word-regexp?Force \apatterns\a to match complete words.]"
93
"[a?Ignored for GNU compatibility.]"
94
"[Y:color|colour?Ignored for GNU compatibility.]:[when]"
95
"\n"
96
"\n[ pattern ] [ file ... ]\n"
97
"\n"
98
"[+DIAGNOSTICS?Exit status 0 if matches were found, 1 if no matches were "
99
"found, where \b-v\b invertes the exit status. Exit status 2 for other "
100
"errors that are accompanied by a message on the standard error.]"
101
"[+SEE ALSO?\bed\b(1), \bsed\b(1), \bperl\b(1), \btw\b(1), \bregex\b(3)]"
102
"[+CAVEATS?Some expressions of necessity require exponential space "
103
"and/or time.]"
104
"[+BUGS?Some expressions may use sub-optimal algorithms. For example, "
105
"don't use this implementation to compute primes.]"
106
;
107
108
#include <cmd.h>
109
#include <ctype.h>
110
#include <ccode.h>
111
#include <error.h>
112
#include <fts.h>
113
#include <regex.h>
114
#include <vmalloc.h>
115
116
#ifndef EISDIR
117
#define EISDIR (-1)
118
#endif
119
120
/*
121
* snarfed from Doug McElroy's C++ version
122
*
123
* this grep is based on the Posix re package.
124
* unfortunately it has to have a nonstandard interface.
125
* 1. fgrep does not have usual operators. REG_LITERAL
126
* caters for this.
127
* 2. grep allows null expressions, hence REG_NULL.
128
* 3. it may be possible to combine the multiple
129
* patterns of grep into single patterns. important
130
* special cases are handled by regcomb().
131
* 4. anchoring by -x has to be done separately from
132
* compilation (remember that fgrep has no ^ or $ operator),
133
* hence REG_LEFT|REG_RIGHT. (An honest, but slow alternative:
134
* run regexec with REG_NOSUB off and nmatch=1 and check
135
* whether the match is full length)
136
*/
137
138
struct State_s;
139
typedef struct State_s State_t;
140
141
typedef struct Item_s /* list item */
142
{
143
struct Item_s* next; /* next in list */
144
uintmax_t hits; /* labeled pattern matches */
145
uintmax_t total; /* total hits */
146
char string[1]; /* string value */
147
} Item_t;
148
149
typedef struct List_s /* generic list */
150
{
151
Item_t* head; /* list head */
152
Item_t* tail; /* list tail */
153
} List_t;
154
155
struct State_s /* program state */
156
{
157
regdisc_t redisc; /* regex discipline */
158
regex_t re; /* main compiled re */
159
160
Vmalloc_t* vm; /* allocation region */
161
162
struct
163
{
164
char* base; /* sfsetbuf buffer */
165
size_t size; /* sfsetbuf size */
166
int noshare; /* turn off SF_SHARE */
167
} buffer;
168
169
Item_t* hit; /* label for most recent match */
170
171
Sfio_t* tmp; /* tmp re compile string */
172
173
List_t files; /* pattern file list */
174
List_t patterns; /* pattern list */
175
List_t labels; /* labelled re list */
176
177
regmatch_t posvec[1]; /* match position vector */
178
regmatch_t* pos; /* match position pointer */
179
int posnum; /* number of match positions */
180
181
char* span; /* line span buffer */
182
size_t spansize; /* span buffer size */
183
184
int any; /* if any pattern hit */
185
int after; /* # lines to list after match */
186
int before; /* # lines to list before match */
187
int list; /* list files with hits */
188
int notfound; /* some input file not found */
189
int options; /* regex options */
190
191
uintmax_t hits; /* total matched pattern count */
192
193
unsigned char byline; /* multiple pattern line by line*/
194
unsigned char count; /* count number of hits */
195
unsigned char label; /* all patterns labelled */
196
unsigned char match; /* match sense */
197
unsigned char query; /* return status but no output */
198
unsigned char number; /* line numbers */
199
unsigned char prefix; /* print file prefix */
200
unsigned char suppress; /* no unopenable file messages */
201
unsigned char words; /* word matches only */
202
};
203
204
static void*
205
labelcomp(const regex_t* re, const char* s, size_t len, regdisc_t* disc)
206
{
207
const char* e = s + len;
208
uintmax_t n;
209
210
n = 0;
211
while (s < e)
212
n = (n << 3) + (*s++ - '0');
213
return (void*)((char*)0 + n);
214
}
215
216
static int
217
labelexec(const regex_t* re, void* data, const char* xstr, size_t xlen, const char* sstr, size_t slen, char** snxt, regdisc_t* disc)
218
{
219
((State_t*)disc)->hit = (Item_t*)data;
220
return 0;
221
}
222
223
static int
224
addre(State_t* state, char* s)
225
{
226
int c;
227
int r;
228
char* b;
229
Item_t* x;
230
231
x = 0;
232
r = -1;
233
b = s;
234
if (state->label)
235
{
236
if (!(s = strchr(s, ':')))
237
{
238
error(2, "%s: label:pattern expected", b);
239
goto done;
240
}
241
c = s - b;
242
s++;
243
if (!(x = vmnewof(state->vm, 0, Item_t, 1, c)))
244
{
245
error(ERROR_SYSTEM|2, "out of space (pattern `%s')", b);
246
goto done;
247
}
248
if (c)
249
memcpy(x->string, b, c);
250
x->string[c] = 0;
251
}
252
if (sfstrtell(state->tmp))
253
sfputc(state->tmp, '\n');
254
if (state->words)
255
{
256
if (!(state->options & REG_AUGMENTED))
257
sfputc(state->tmp, '\\');
258
sfputc(state->tmp, '<');
259
}
260
sfputr(state->tmp, s, -1);
261
if (state->words)
262
{
263
if (!(state->options & REG_AUGMENTED))
264
sfputc(state->tmp, '\\');
265
sfputc(state->tmp, '>');
266
}
267
if (x)
268
{
269
b = (state->options & (REG_AUGMENTED|REG_EXTENDED)) ? "" : "\\";
270
sfprintf(state->tmp, "%s(?{%I*o})", b, sizeof(ptrdiff_t), (char*)x - (char*)0);
271
if (state->labels.tail)
272
state->labels.tail = state->labels.tail->next = x;
273
else
274
state->labels.head = state->labels.tail = x;
275
}
276
state->any = 1;
277
r = 0;
278
done:
279
if (r && x)
280
vmfree(state->vm, x);
281
return r;
282
}
283
284
static int
285
addstring(State_t* state, List_t* p, char* s)
286
{
287
Item_t* x;
288
289
if (!(x = vmnewof(state->vm, 0, Item_t, 1, strlen(s))))
290
{
291
error(ERROR_SYSTEM|2, "out of space (string `%s')", s);
292
return -1;
293
}
294
strcpy(x->string, s);
295
if (p->head)
296
p->tail->next = x;
297
else
298
p->head = x;
299
p->tail = x;
300
return 0;
301
}
302
303
static int
304
compile(State_t* state)
305
{
306
int line;
307
int c;
308
int r;
309
size_t n;
310
char* s;
311
char* t;
312
char* file;
313
Item_t* x;
314
Sfio_t* f;
315
316
r = 1;
317
if (!(state->tmp = sfstropen()))
318
{
319
error(ERROR_SYSTEM|2, "out of space");
320
goto done;
321
}
322
if (state->number || state->before || state->after)
323
state->byline = 1;
324
for (x = state->patterns.head; x; x = x->next)
325
if (addre(state, x->string))
326
return r;
327
file = error_info.file;
328
line = error_info.line;
329
f = 0;
330
for (x = state->files.head; x; x = x->next)
331
{
332
s = x->string;
333
if (!(f = sfopen(NiL, s, "r")))
334
{
335
error(ERROR_SYSTEM|2, "%s: cannot open", s);
336
r = 2;
337
goto done;
338
}
339
error_info.file = s;
340
error_info.line = 0;
341
while (s = (char*)sfreserve(f, SF_UNBOUND, SF_LOCKR))
342
{
343
if (!(n = sfvalue(f)))
344
break;
345
if (s[n - 1] != '\n')
346
{
347
for (t = s + n; t > s && *--t != '\n'; t--);
348
if (t == s)
349
{
350
sfread(f, s, 0);
351
break;
352
}
353
n = t - s + 1;
354
}
355
s[n - 1] = 0;
356
if (addre(state, s))
357
goto done;
358
s[n - 1] = '\n';
359
sfread(f, s, n);
360
}
361
while ((s = sfgetr(f, '\n', 1)) || (s = sfgetr(f, '\n', -1)))
362
{
363
error_info.line++;
364
if (addre(state, s))
365
goto done;
366
}
367
error_info.file = file;
368
error_info.line = line;
369
sfclose(f);
370
f = 0;
371
}
372
if (!state->any)
373
{
374
error(2, "no pattern");
375
goto done;
376
}
377
state->any = 0;
378
if (!(s = sfstruse(state->tmp)))
379
{
380
error(ERROR_SYSTEM|2, "out of space");
381
goto done;
382
}
383
error(-1, "RE ``%s''", s);
384
state->re.re_disc = &state->redisc;
385
if (state->label)
386
{
387
state->redisc.re_compf = labelcomp;
388
state->redisc.re_execf = labelexec;
389
}
390
if (c = regcomp(&state->re, s, state->options))
391
{
392
regfatal(&state->re, 2, c);
393
goto done;
394
}
395
if (!regrecord(&state->re))
396
state->byline = 1;
397
if (!state->label)
398
{
399
if (!(state->hit = vmnewof(state->vm, 0, Item_t, 1, 0)))
400
{
401
error(ERROR_SYSTEM|2, "out of space");
402
goto done;
403
}
404
state->labels.head = state->labels.tail = state->hit;
405
}
406
r = 0;
407
done:
408
error_info.file = file;
409
error_info.line = line;
410
if (f)
411
sfclose(f);
412
if (state->tmp)
413
sfstrclose(state->tmp);
414
return r;
415
}
416
417
static void
418
highlight(Sfio_t* sp, const char* s, int n, int so, int eo)
419
{
420
static const char bold[] = {CC_esc,'[','1','m'};
421
static const char normal[] = {CC_esc,'[','0','m'};
422
423
sfwrite(sp, s, so);
424
sfwrite(sp, bold, sizeof(bold));
425
sfwrite(sp, s + so, eo - so);
426
sfwrite(sp, normal, sizeof(normal));
427
sfwrite(sp, s + eo, n - eo);
428
}
429
430
static int
431
hit(State_t* state, const char* prefix, int line, const char* s, size_t len)
432
{
433
state->hit->hits++;
434
if (state->query || state->list)
435
return -1;
436
if (!state->count)
437
{
438
if (state->prefix)
439
sfprintf(sfstdout, "%s:", prefix);
440
if (state->number && line)
441
sfprintf(sfstdout, "%d:", line);
442
if (state->label)
443
sfprintf(sfstdout, "%s:", state->hit->string);
444
if (state->pos)
445
highlight(sfstdout, s, len + 1, state->pos[0].rm_so, state->pos[0].rm_eo);
446
else
447
sfwrite(sfstdout, s, len + 1);
448
}
449
return 0;
450
}
451
452
static int
453
record(void* handle, const char* s, size_t len)
454
{
455
return hit((State_t*)handle, error_info.file, 0, s, len);
456
}
457
458
static int
459
execute(State_t* state, Sfio_t* input, char* name, Shbltin_t* context)
460
{
461
register char* s;
462
char* file;
463
Item_t* x;
464
size_t len;
465
int result;
466
int line;
467
468
int r = 1;
469
470
if (state->buffer.noshare)
471
sfset(input, SF_SHARE, 0);
472
if (state->buffer.size)
473
sfsetbuf(input, state->buffer.base, state->buffer.size);
474
if (!name)
475
name = "(standard input)"; /* posix! (ast prefers /dev/stdin) */
476
file = error_info.file;
477
error_info.file = name;
478
line = error_info.line;
479
error_info.line = 0;
480
if (state->byline)
481
{
482
for (;;)
483
{
484
if (sh_checksig(context))
485
goto bad;
486
error_info.line++;
487
if (s = sfgetr(input, '\n', 0))
488
len = sfvalue(input) - 1;
489
else if (s = sfgetr(input, '\n', -1))
490
{
491
len = sfvalue(input);
492
s[len] = '\n';
493
#if _you_like_the_noise
494
error(1, "newline appended");
495
#endif
496
}
497
else if (sferror(input) && errno != EISDIR)
498
{
499
error(ERROR_SYSTEM|2, "read error");
500
goto bad;
501
}
502
else
503
break;
504
if ((result = regnexec(&state->re, s, len, state->posnum, state->pos, 0)) && result != REG_NOMATCH)
505
{
506
regfatal(&state->re, 2, result);
507
goto bad;
508
}
509
if ((result == 0) == state->match && hit(state, name, error_info.line, s, len) < 0)
510
break;
511
}
512
}
513
else
514
{
515
register char* e;
516
register char* t;
517
char* r;
518
519
s = e = 0;
520
for (;;)
521
{
522
if (sh_checksig(context))
523
goto bad;
524
if (s < e)
525
{
526
t = state->span;
527
for (;;)
528
{
529
len = 2 * (e - s) + t - state->span + 1;
530
len = roundof(len, SF_BUFSIZE);
531
if (state->spansize < len)
532
{
533
state->spansize = len;
534
len = t - state->span;
535
if (!(state->span = vmnewof(state->vm, state->span, char, state->spansize, 0)))
536
{
537
error(ERROR_SYSTEM|2, "%s: line longer than %lu characters", name, len + e - s);
538
goto bad;
539
}
540
t = state->span + len;
541
}
542
len = e - s;
543
memcpy(t, s, len);
544
t += len;
545
if (!(s = sfreserve(input, SF_UNBOUND, 0)) || (len = sfvalue(input)) <= 0)
546
{
547
if ((sfvalue(input) || sferror(input)) && errno != EISDIR)
548
error(ERROR_SYSTEM|2, "%s: read error", name);
549
break;
550
}
551
else if (!(e = memchr(s, '\n', len)))
552
e = s + len;
553
else
554
{
555
r = s + len;
556
len = (e - s) + t - state->span;
557
len = roundof(len, SF_BUFSIZE);
558
if (state->spansize < len)
559
{
560
state->spansize = len;
561
len = t - state->span;
562
if (!(state->span = vmnewof(state->vm, state->span, char, state->spansize, 0)))
563
{
564
error(ERROR_SYSTEM|2, "%s: line longer than %lu characters", name, len + e - s);
565
goto bad;
566
}
567
t = state->span + len;
568
}
569
len = e - s;
570
memcpy(t, s, len);
571
t += len;
572
s += len + 1;
573
e = r;
574
break;
575
}
576
}
577
*t = '\n';
578
if (!(len = t - state->span))
579
len++;
580
if (result = regrexec(&state->re, state->span, len, state->posnum, state->pos, state->options, '\n', (void*)state, record))
581
{
582
if (result < 0)
583
goto done;
584
if (result != REG_NOMATCH)
585
{
586
regfatal(&state->re, 2, result);
587
goto bad;
588
}
589
}
590
if (!s)
591
break;
592
}
593
else
594
{
595
if (!(s = sfreserve(input, SF_UNBOUND, 0)))
596
{
597
if ((sfvalue(input) || sferror(input)) && errno != EISDIR)
598
error(ERROR_SYSTEM|2, "%s: read error", name);
599
break;
600
}
601
if ((len = sfvalue(input)) <= 0)
602
break;
603
e = s + len;
604
}
605
t = e;
606
while (t > s)
607
if (*--t == '\n')
608
{
609
len = t - s;
610
if (!len || t > s && *(t - 1) == '\n')
611
len++;
612
if (result = regrexec(&state->re, s, len, state->posnum, state->pos, state->options, '\n', (void*)state, record))
613
{
614
if (result < 0)
615
goto done;
616
if (result != REG_NOMATCH)
617
{
618
regfatal(&state->re, 2, result);
619
goto bad;
620
}
621
}
622
s = t + 1;
623
break;
624
}
625
}
626
}
627
done:
628
error_info.file = file;
629
error_info.line = line;
630
x = state->labels.head;
631
do
632
{
633
if (x->hits && state->list >= 0)
634
{
635
state->any = 1;
636
if (state->query)
637
break;
638
}
639
if (!state->query)
640
{
641
if (!state->list)
642
{
643
if (state->count)
644
{
645
if (state->count & 2)
646
x->total += x->hits;
647
else
648
{
649
if (state->prefix)
650
sfprintf(sfstdout, "%s:", name);
651
if (*x->string)
652
sfprintf(sfstdout, "%s:", x->string);
653
sfprintf(sfstdout, "%I*u\n", sizeof(x->hits), x->hits);
654
}
655
}
656
}
657
else if ((x->hits != 0) == (state->list > 0))
658
{
659
if (state->list < 0)
660
state->any = 1;
661
if (*x->string)
662
sfprintf(sfstdout, "%s:%s\n", name, x->string);
663
else
664
sfprintf(sfstdout, "%s\n", name);
665
}
666
}
667
x->hits = 0;
668
} while (x = x->next);
669
r = 0;
670
bad:
671
error_info.file = file;
672
error_info.line = line;
673
return r;
674
}
675
676
static int
677
grep(char* id, int options, int argc, char** argv, Shbltin_t* context)
678
{
679
int c;
680
char* s;
681
char* h;
682
Sfio_t* f;
683
int flags;
684
int r = 1;
685
FTS* fts;
686
FTSENT* ent;
687
State_t state;
688
689
cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY);
690
flags = fts_flags() | FTS_META | FTS_TOP | FTS_NOPOSTORDER | FTS_NOSEEDOTDIR;
691
memset(&state, 0, sizeof(state));
692
if (!(state.vm = vmopen(Vmdcheap, Vmbest, 0)))
693
{
694
error(ERROR_SYSTEM|2, "out of space");
695
return 2;
696
}
697
state.redisc.re_version = REG_VERSION;
698
state.redisc.re_flags = REG_NOFREE;
699
state.redisc.re_resizef = (regresize_t)vmgetmem;
700
state.redisc.re_resizehandle = (void*)state.vm;
701
state.match = 1;
702
state.options = REG_FIRST|REG_NOSUB|REG_NULL|REG_DISCIPLINE|REG_MULTIPLE|options;
703
if (strcmp(astconf("CONFORMANCE", NiL, NiL), "standard"))
704
state.options |= REG_LENIENT;
705
error_info.id = id;
706
h = 0;
707
fts = 0;
708
while (c = optget(argv, usage))
709
switch (c)
710
{
711
case 'C':
712
if (opt_info.arg)
713
{
714
state.before = (int)strtol(opt_info.arg, &s, 0);
715
state.after = (*s == ',') ? (int)strtol(s + 1, &s, 0) : state.before;
716
if (*s)
717
error(3, "%s: invalid context line count", s);
718
}
719
else
720
state.before = state.after = 2;
721
break;
722
case 'E':
723
state.options |= REG_EXTENDED;
724
break;
725
case 'F':
726
state.options |= REG_LITERAL;
727
break;
728
case 'G':
729
state.options &= ~(REG_AUGMENTED|REG_EXTENDED);
730
break;
731
case 'H':
732
state.prefix = opt_info.num;
733
break;
734
case 'L':
735
state.list = -opt_info.num;
736
break;
737
case 'N':
738
h = opt_info.arg;
739
break;
740
case 'O':
741
state.options |= REG_LENIENT;
742
break;
743
case 'P':
744
state.options |= REG_EXTENDED|REG_LENIENT;
745
break;
746
case 'S':
747
state.options &= ~REG_LENIENT;
748
break;
749
case 'T':
750
s = opt_info.arg;
751
switch (*s)
752
{
753
case 'b':
754
case 'm':
755
c = *s++;
756
state.buffer.size = strton(s, &s, NiL, 1);
757
if (c == 'b' && !(state.buffer.base = newof(0, char, state.buffer.size, 0)))
758
{
759
error(ERROR_SYSTEM|2, "out of space [test buffer]");
760
goto done;
761
}
762
if (*s)
763
{
764
error(2, "%s: invalid characters after test", s);
765
goto done;
766
}
767
break;
768
case 'f':
769
state.options |= REG_FIRST;
770
break;
771
case 'l':
772
state.options |= REG_LEFT;
773
break;
774
case 'n':
775
state.buffer.noshare = 1;
776
break;
777
case 'r':
778
state.options |= REG_RIGHT;
779
break;
780
case 'L':
781
state.byline = 1;
782
break;
783
default:
784
error(3, "%s: unknown test", s);
785
break;
786
}
787
break;
788
case 'X':
789
state.options |= REG_AUGMENTED;
790
break;
791
case 'a':
792
break;
793
case 'b':
794
state.options &= ~(REG_FIRST|REG_NOSUB);
795
break;
796
case 'c':
797
state.count |= 1;
798
break;
799
case 'e':
800
if (addstring(&state, &state.patterns, opt_info.arg))
801
goto done;
802
break;
803
case 'f':
804
if (addstring(&state, &state.files, opt_info.arg))
805
goto done;
806
break;
807
case 'h':
808
state.prefix = 2;
809
break;
810
case 'i':
811
state.options |= REG_ICASE;
812
break;
813
case 'l':
814
state.list = opt_info.num;
815
break;
816
case 'm':
817
state.label = 1;
818
break;
819
case 'n':
820
state.number = 1;
821
break;
822
case 'q':
823
state.query = 1;
824
break;
825
case 'r':
826
if (opt_info.num)
827
flags &= ~FTS_TOP;
828
break;
829
case 's':
830
state.suppress = opt_info.num;
831
break;
832
case 't':
833
state.count |= 2;
834
break;
835
case 'v':
836
if (state.match = !opt_info.num)
837
state.options &= ~REG_INVERT;
838
else
839
state.options |= REG_INVERT;
840
break;
841
case 'w':
842
state.words = 1;
843
break;
844
case 'x':
845
state.options |= REG_LEFT|REG_RIGHT;
846
break;
847
case 'Y':
848
/* ignored for GNU compatibility */
849
break;
850
case '?':
851
vmclose(state.vm);
852
error(ERROR_USAGE|4, "%s", opt_info.arg);
853
break;
854
case ':':
855
error(2, "%s", opt_info.arg);
856
goto done;
857
default:
858
error(2, "%s: not implemented", opt_info.name);
859
goto done;
860
}
861
argv += opt_info.index;
862
if ((state.options & REG_LITERAL) && (state.options & (REG_AUGMENTED|REG_EXTENDED)))
863
{
864
error(2, "-F and -A or -P or -X are incompatible");
865
goto done;
866
}
867
if ((state.options & REG_LITERAL) && state.words)
868
{
869
error(ERROR_SYSTEM|2, "-F and -w are incompatible");
870
goto done;
871
}
872
if (!state.files.head && !state.patterns.head)
873
{
874
if (!argv[0])
875
{
876
error(2, "no pattern");
877
goto done;
878
}
879
if (addstring(&state, &state.patterns, *argv++))
880
goto done;
881
}
882
if (!(state.options & (REG_FIRST|REG_NOSUB)))
883
{
884
if (state.count || state.list || state.query || (state.options & REG_INVERT))
885
state.options |= REG_FIRST|REG_NOSUB;
886
else
887
{
888
state.pos = state.posvec;
889
state.posnum = elementsof(state.posvec);
890
}
891
}
892
if (r = compile(&state))
893
goto done;
894
sfset(sfstdout, SF_LINE, 1);
895
if (!argv[0])
896
{
897
state.prefix = h ? 1 : 0;
898
if (r = execute(&state, sfstdin, h, context))
899
goto done;
900
}
901
if (state.prefix > 1)
902
state.prefix = 0;
903
else if (!(flags & FTS_TOP) || argv[1])
904
state.prefix = 1;
905
if (!(fts = fts_open(argv, flags, NiL)))
906
{
907
error(ERROR_SYSTEM|2, "%s: not found", argv[0]);
908
r = 1;
909
goto done;
910
}
911
while (!sh_checksig(context) && (ent = fts_read(fts)))
912
switch (ent->fts_info)
913
{
914
case FTS_F:
915
if (f = sfopen(NiL, ent->fts_accpath, "r"))
916
{
917
r = execute(&state, f, ent->fts_path, context);
918
sfclose(f);
919
if (r)
920
goto done;
921
if (state.query && state.any)
922
goto quit;
923
break;
924
}
925
/*FALLTHROUGH*/
926
case FTS_NS:
927
case FTS_SLNONE:
928
state.notfound = 1;
929
if (!state.suppress)
930
error(ERROR_SYSTEM|2, "%s: cannot open", ent->fts_path);
931
break;
932
case FTS_DC:
933
error(ERROR_WARNING|1, "%s: directory causes cycle", ent->fts_path);
934
break;
935
case FTS_DNR:
936
error(ERROR_SYSTEM|2, "%s: cannot read directory", ent->fts_path);
937
break;
938
case FTS_DNX:
939
error(ERROR_SYSTEM|2, "%s: cannot search directory", ent->fts_path);
940
break;
941
}
942
quit:
943
if ((state.count & 2) && !state.query && !state.list)
944
{
945
Item_t* x;
946
947
x = state.labels.head;
948
do
949
{
950
if (*x->string)
951
sfprintf(sfstdout, "%s:", x->string);
952
sfprintf(sfstdout, "%I*u\n", sizeof(x->total), x->total);
953
} while (x = x->next);
954
}
955
r = (state.notfound && !state.query) ? 2 : !state.any;
956
done:
957
if (fts)
958
fts_close(fts);
959
vmclose(state.vm);
960
sfset(sfstdout, SF_LINE, 0);
961
if (sfsync(sfstdout))
962
error(ERROR_SYSTEM|2, "write error");
963
if (sh_checksig(context))
964
{
965
errno = EINTR;
966
r = 2;
967
}
968
return r;
969
}
970
971
int
972
b_grep(int argc, char** argv, Shbltin_t* context)
973
{
974
char* s;
975
int options;
976
977
NoP(argc);
978
options = 0;
979
if (s = strrchr(argv[0], '/'))
980
s++;
981
else
982
s = argv[0];
983
switch (*s)
984
{
985
case 'e':
986
case 'E':
987
s = "egrep";
988
options = REG_EXTENDED;
989
break;
990
case 'f':
991
case 'F':
992
s = "fgrep";
993
options = REG_LITERAL;
994
break;
995
case 'p':
996
case 'P':
997
s = "pgrep";
998
options = REG_EXTENDED|REG_LENIENT;
999
break;
1000
case 'x':
1001
case 'X':
1002
s = "xgrep";
1003
options = REG_AUGMENTED;
1004
break;
1005
default:
1006
s = "grep";
1007
break;
1008
}
1009
return grep(s, options, argc, argv, context);
1010
}
1011
1012
int
1013
b_egrep(int argc, char** argv, Shbltin_t* context)
1014
{
1015
NoP(argc);
1016
return grep("egrep", REG_EXTENDED, argc, argv, context);
1017
}
1018
1019
int
1020
b_fgrep(int argc, char** argv, Shbltin_t* context)
1021
{
1022
NoP(argc);
1023
return grep("fgrep", REG_LITERAL, argc, argv, context);
1024
}
1025
1026
int
1027
b_pgrep(int argc, char** argv, Shbltin_t* context)
1028
{
1029
NoP(argc);
1030
return grep("pgrep", REG_EXTENDED|REG_LENIENT, argc, argv, context);
1031
}
1032
1033
int
1034
b_xgrep(int argc, char** argv, Shbltin_t* context)
1035
{
1036
NoP(argc);
1037
return grep("xgrep", REG_AUGMENTED, argc, argv, context);
1038
}
1039
1040
#if STANDALONE
1041
1042
int
1043
main(int argc, char** argv)
1044
{
1045
return b_grep(argc, argv, 0);
1046
}
1047
1048
#endif
1049
1050