1N/A/***********************************************************************
1N/A* *
1N/A* This software is part of the ast package *
1N/A* Copyright (c) 1995-2010 AT&T Intellectual Property *
1N/A* and is licensed under the *
1N/A* Common Public License, Version 1.0 *
1N/A* by AT&T Intellectual Property *
1N/A* *
1N/A* A copy of the License is available at *
1N/A* http://www.opensource.org/licenses/cpl1.0.txt *
1N/A* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
1N/A* *
1N/A* Information and Software Systems Research *
1N/A* AT&T Research *
1N/A* Florham Park NJ *
1N/A* *
1N/A* Glenn Fowler <gsf@research.att.com> *
1N/A* *
1N/A***********************************************************************/
1N/A#pragma prototyped
1N/A
1N/Astatic const char usage[] =
1N/A"[-?\n@(#)$Id: grep (AT&T Research) 2010-12-10 $\n]"
1N/AUSAGE_LICENSE
1N/A"[+NAME?grep - search lines in files for matching patterns]"
1N/A"[+DESCRIPTION?The \bgrep\b commands search the named input files"
1N/A" for lines containing a match for the given \apatterns\a."
1N/A" Matching lines are printed by default. The standard input is searched"
1N/A" if no files are given or when the file \b-\b is specified.]"
1N/A"[+?There are six variants of \bgrep\b, each one using a different form of"
1N/A" \apattern\a, controlled either by option or the command path"
1N/A" base name. Details of each variant may be found in \bregex\b(3).]"
1N/A" {"
1N/A" [+grep?The default basic regular expressions (no alternations.)]"
1N/A" [+egrep?Extended regular expressions (alternations, one or more.)]"
1N/A" [+pgrep?\bperl\b(1) regular expressions (lenient extended.)]"
1N/A" [+xgrep?Augmented regular expressions (conjunction, negation.)]"
1N/A" [+fgrep?Fixed string expressions.]"
1N/A" [+agrep?Approximate regular expressions (not implemented.)]"
1N/A" }"
1N/A"[G:basic-regexp?\bgrep\b mode (default): basic regular expression \apatterns\a.]"
1N/A"[E:extended-regexp?\begrep\b mode: extended regular expression \apatterns\a.]"
1N/A"[X:augmented-regexp?\bxgrep\b mode: augmented regular expression \apatterns\a.]"
1N/A"[P:perl-regexp?\bpgrep\b mode: \bperl\b(1) regular expression \apatterns\a.]"
1N/A"[F:fixed-string?\bfgrep\b mode: fixed string \apatterns\a.]"
1N/A"[A:approximate-regexp?\bagrep\b mode: approximate regular expression \apatterns\a (not implemented.)]"
1N/A
1N/A"[C:context?Set the matched line context \abefore\a and \aafter\a count."
1N/A" If ,\aafter\a is omitted then it is set to \abefore\a."
1N/A" By default only matched lines are printed.]:?"
1N/A" [before[,after]]:=2,2]"
1N/A"[c:count?Only print a matching line count for each file.]"
1N/A"[e:expression|pattern|regexp?Specify a matching \apattern\a. More than one"
1N/A" \apattern\a implies alternation. If this option is specified"
1N/A" then the command line \apattern\a must be omitted.]:"
1N/A" [pattern]"
1N/A"[f:file?Each line in \apattern-file\a is a \apattern\a, placed into a single"
1N/A" alternating expression.]:"
1N/A" [pattern-file]"
1N/A"[H:filename|with-filename?Prefix each matched line with the containing file name.]"
1N/A"[h:no-filename?Suppress containing file name prefix for each matched line.]"
1N/A"[i:ignore-case?Ignore case when matching.]"
1N/A"[l:files-with-matches?Only print file names with at least one match.]"
1N/A"[L:files-without-matches?Only print file names with no matches.]"
1N/A"[b:highlight?Highlight matches using the ansi terminal bold sequence.]"
1N/A"[v:invert-match|revert-match?Invert the \apattern\a match sense.]"
1N/A"[m:label?All patterns must be of the form \alabel\a:\apattern\a. Match and"
1N/A" count output will be prefixed by the corresponding \alabel\a:.]"
1N/A"[O:lenient?Enable lenient \apattern\a interpretation. This is the default.]"
1N/A"[x:line-match|line-regexp?Force \apatterns\a to match complete lines.]"
1N/A"[n:number|line-number?Prefix each matched line with its line number.]"
1N/A"[N:name?Set the standard input file name prefix to"
1N/A" \aname\a.]:[name:=empty]"
1N/A"[q:quiet|silent?Do not print matching lines.]"
1N/A"[r|R:recursive?Recursively process all files in each named directory. "
1N/A "Use \btw -e\b \aexpression\a \bgrep ...\b to control the directory "
1N/A "traversal.]"
1N/A"[S:strict?Enable strict \apattern\a interpretation with diagnostics.]"
1N/A"[s:suppress|no-messages?Suppress error and warning messages.]"
1N/A"[t:total?Only print a single matching line count for all files.]"
1N/A"[T:test?Enable implementation specific tests.]:"
1N/A" [test]"
1N/A"[w:word-match|word-regexp?Force \apatterns\a to match complete words.]"
1N/A"[a?Ignored for GNU compatibility.]"
1N/A"\n"
1N/A"\n[ pattern ] [ file ... ]\n"
1N/A"\n"
1N/A"[+DIAGNOSTICS?Exit status 0 if matches were found, 1 if no matches were found,"
1N/A" where \b-v\b invertes the exit status. Exit status 2 for other"
1N/A" errors that are accompanied by a message on the standard error.]"
1N/A"[+SEE ALSO?\bed\b(1), \bsed\b(1), \bperl\b(1), \btw\b(1), \bregex\b(3)]"
1N/A"[+CAVEATS?Some expressions of necessity require exponential space"
1N/A" and/or time.]"
1N/A"[+BUGS?Some expressions may use sub-optimal algorithms. For example,"
1N/A" don't use this implementation to compute primes.]"
1N/A;
1N/A
1N/A#include <ast.h>
1N/A#include <ctype.h>
1N/A#include <ccode.h>
1N/A#include <error.h>
1N/A#include <fts.h>
1N/A#include <regex.h>
1N/A
1N/A#ifndef EISDIR
1N/A#define EISDIR (-1)
1N/A#endif
1N/A
1N/A/*
1N/A * snarfed from Doug McElroy's C++ version
1N/A *
1N/A * this grep is based on the Posix re package.
1N/A * unfortunately it has to have a nonstandard interface.
1N/A * 1. fgrep does not have usual operators. REG_LITERAL
1N/A * caters for this.
1N/A * 2. grep allows null expressions, hence REG_NULL.
1N/A * 3. it may be possible to combine the multiple
1N/A * patterns of grep into single patterns. important
1N/A * special cases are handled by regcomb().
1N/A * 4. anchoring by -x has to be done separately from
1N/A * compilation (remember that fgrep has no ^ or $ operator),
1N/A * hence REG_LEFT|REG_RIGHT. (An honest, but slow alternative:
1N/A * run regexec with REG_NOSUB off and nmatch=1 and check
1N/A * whether the match is full length)
1N/A */
1N/A
1N/Atypedef struct Item_s /* list item - sue me for waste */
1N/A{
1N/A struct Item_s* next; /* next in list */
1N/A regex_t re; /* compiled re */
1N/A Sfulong_t hits; /* labeled pattern matches */
1N/A Sfulong_t total; /* total hits */
1N/A char string[1]; /* string value */
1N/A} Item_t;
1N/A
1N/Atypedef struct List_s /* generic list */
1N/A{
1N/A Item_t* head; /* list head */
1N/A Item_t* tail; /* list tail */
1N/A} List_t;
1N/A
1N/Atypedef struct State_s /* program state */
1N/A{
1N/A struct
1N/A {
1N/A char* base; /* sfsetbuf buffer */
1N/A size_t size; /* sfsetbuf size */
1N/A int noshare; /* turn off SF_SHARE */
1N/A } buffer;
1N/A
1N/A List_t file; /* pattern file list */
1N/A List_t pattern; /* pattern list */
1N/A List_t re; /* re list */
1N/A
1N/A regmatch_t posvec[1]; /* match position vector */
1N/A regmatch_t* pos; /* match position pointer */
1N/A int posnum; /* number of match positions */
1N/A
1N/A int any; /* if any pattern hit */
1N/A int after; /* # lines to list after match */
1N/A int before; /* # lines to list before match */
1N/A int list; /* list files with hits */
1N/A int notfound; /* some input file not found */
1N/A int options; /* regex options */
1N/A
1N/A Sfulong_t hits; /* total matched pattern count */
1N/A
1N/A unsigned char byline; /* multiple pattern line by line*/
1N/A unsigned char count; /* count number of hits */
1N/A unsigned char label; /* all patterns labeled */
1N/A unsigned char match; /* match sense */
1N/A unsigned char query; /* return status but no output */
1N/A unsigned char number; /* line numbers */
1N/A unsigned char prefix; /* print file prefix */
1N/A unsigned char suppress; /* no unopenable file messages */
1N/A unsigned char words; /* word matches only */
1N/A} State_s;
1N/A
1N/Astatic void
1N/Aaddre(State_s *state, List_t* p, char* s)
1N/A{
1N/A int c;
1N/A char* b;
1N/A Item_t* x;
1N/A Sfio_t* t;
1N/A
1N/A b = s;
1N/A if (state->label)
1N/A {
1N/A if (!(s = strchr(s, ':')))
1N/A error(3, "%s: label:pattern expected", b);
1N/A c = s - b;
1N/A s++;
1N/A }
1N/A else
1N/A c = 0;
1N/A if (!(x = newof(0, Item_t, 1, c)))
1N/A error(ERROR_SYSTEM|3, "out of space (pattern `%s')", b);
1N/A if (c)
1N/A memcpy(x->string, b, c);
1N/A if (state->words)
1N/A {
1N/A if (!(t = sfstropen()))
1N/A error(ERROR_SYSTEM|3, "out of space (word pattern `%s')", s);
1N/A if (!(state->options & REG_AUGMENTED))
1N/A sfputc(t, '\\');
1N/A sfputc(t, '<');
1N/A sfputr(t, s, -1);
1N/A if (!(state->options & REG_AUGMENTED))
1N/A sfputc(t, '\\');
1N/A sfputc(t, '>');
1N/A if (!(s = sfstruse(t)))
1N/A error(ERROR_SYSTEM|3, "out of space");
1N/A }
1N/A else
1N/A t = 0;
1N/A if (c = regcomp(&x->re, s, state->options|REG_MULTIPLE))
1N/A regfatal(&x->re, 4, c);
1N/A if (t)
1N/A sfstrclose(t);
1N/A if (!p->head)
1N/A {
1N/A p->head = p->tail = x;
1N/A if (state->number || state->before || state->after || !regrecord(&x->re))
1N/A state->byline = 1;
1N/A }
1N/A else if (state->label || regcomb(&p->tail->re, &x->re))
1N/A {
1N/A p->tail = p->tail->next = x;
1N/A if (!state->byline && (state->number || !state->label || !regrecord(&x->re)))
1N/A state->byline = 1;
1N/A }
1N/A else
1N/A free(x);
1N/A}
1N/A
1N/Astatic void
1N/Aaddstring(State_s *state, List_t* p, char* s)
1N/A{
1N/A Item_t* x;
1N/A
1N/A if (!(x = newof(0, Item_t, 1, strlen(s))))
1N/A error(ERROR_SYSTEM|3, "out of space (string `%s')", s);
1N/A strcpy(x->string, s);
1N/A if (p->head)
1N/A p->tail->next = x;
1N/A else
1N/A p->head = x;
1N/A p->tail = x;
1N/A}
1N/A
1N/Astatic void
1N/Acompile(State_s *state)
1N/A{
1N/A int line;
1N/A size_t n;
1N/A char* s;
1N/A char* t;
1N/A char* file;
1N/A Item_t* x;
1N/A Sfio_t* f;
1N/A
1N/A for (x = state->pattern.head; x; x = x->next)
1N/A addre(state, &state->re, x->string);
1N/A for (x = state->file.head; x; x = x->next)
1N/A {
1N/A s = x->string;
1N/A if (!(f = sfopen(NiL, s, "r")))
1N/A error(ERROR_SYSTEM|4, "%s: cannot open", s);
1N/A else
1N/A {
1N/A file = error_info.file;
1N/A error_info.file = s;
1N/A line = error_info.line;
1N/A error_info.line = 0;
1N/A while (s = (char*)sfreserve(f, SF_UNBOUND, SF_LOCKR))
1N/A {
1N/A if (!(n = sfvalue(f)))
1N/A break;
1N/A if (s[n - 1] != '\n')
1N/A {
1N/A for (t = s + n; t > s && *--t != '\n'; t--);
1N/A if (t == s)
1N/A {
1N/A sfread(f, s, 0);
1N/A break;
1N/A }
1N/A n = t - s + 1;
1N/A }
1N/A s[n - 1] = 0;
1N/A addre(state, &state->re, s);
1N/A s[n - 1] = '\n';
1N/A sfread(f, s, n);
1N/A }
1N/A while ((s = sfgetr(f, '\n', 1)) || (s = sfgetr(f, '\n', -1)))
1N/A {
1N/A error_info.line++;
1N/A addre(state, &state->re, s);
1N/A }
1N/A error_info.file = file;
1N/A error_info.line = line;
1N/A sfclose(f);
1N/A }
1N/A }
1N/A if (!state->re.head)
1N/A error(3, "no pattern");
1N/A}
1N/A
1N/Astatic void
1N/Ahighlight(Sfio_t* sp, const char* s, int n, int so, int eo)
1N/A{
1N/A static const char bold[] = {CC_esc,'[','1','m'};
1N/A static const char normal[] = {CC_esc,'[','0','m'};
1N/A
1N/A sfwrite(sp, s, so);
1N/A sfwrite(sp, bold, sizeof(bold));
1N/A sfwrite(sp, s + so, eo - so);
1N/A sfwrite(sp, normal, sizeof(normal));
1N/A sfwrite(sp, s + eo, n - eo);
1N/A}
1N/A
1N/Atypedef struct
1N/A{
1N/A State_s *state;
1N/A Item_t *item;
1N/A} record_handle;
1N/A
1N/Astatic int
1N/Arecord(void* handle, const char* s, size_t len)
1N/A{
1N/A record_handle *r_x = (record_handle *)handle;
1N/A State_s *state = r_x->state;
1N/A Item_t *item = r_x->item;
1N/A
1N/A item->hits++;
1N/A if (state->query || state->list)
1N/A return -1;
1N/A if (!state->count)
1N/A {
1N/A if (state->prefix)
1N/A sfprintf(sfstdout, "%s:", error_info.file);
1N/A if (state->label)
1N/A sfprintf(sfstdout, "%s:", item->string);
1N/A if (state->pos)
1N/A highlight(sfstdout, s, len + 1, state->pos[0].rm_so, state->pos[0].rm_eo);
1N/A else
1N/A sfwrite(sfstdout, s, len + 1);
1N/A }
1N/A return 0;
1N/A}
1N/A
1N/Astatic void
1N/Aexecute(State_s *state, Sfio_t* input, char* name)
1N/A{
1N/A register char* s;
1N/A char* file;
1N/A Item_t* x;
1N/A size_t len;
1N/A int result;
1N/A int line;
1N/A
1N/A Sfulong_t hits = 0;
1N/A
1N/A if (state->buffer.noshare)
1N/A sfset(input, SF_SHARE, 0);
1N/A if (state->buffer.size)
1N/A sfsetbuf(input, state->buffer.base, state->buffer.size);
1N/A if (!name)
1N/A name = "/dev/stdin";
1N/A file = error_info.file;
1N/A error_info.file = name;
1N/A line = error_info.line;
1N/A error_info.line = 0;
1N/A if (state->byline)
1N/A {
1N/A for (;;)
1N/A {
1N/A error_info.line++;
1N/A if (s = sfgetr(input, '\n', 0))
1N/A len = sfvalue(input) - 1;
1N/A else if (s = sfgetr(input, '\n', -1))
1N/A {
1N/A len = sfvalue(input);
1N/A s[len] = '\n';
1N/A#if _you_like_the_noise
1N/A error(1, "newline appended");
1N/A#endif
1N/A }
1N/A else
1N/A {
1N/A if (sferror(input) && errno != EISDIR)
1N/A error(ERROR_SYSTEM|2, "read error");
1N/A break;
1N/A }
1N/A x = state->re.head;
1N/A do
1N/A {
1N/A if (!(result = regnexec(&x->re, s, len, state->posnum, state->pos, 0)))
1N/A {
1N/A if (!state->label)
1N/A break;
1N/A x->hits++;
1N/A if (state->query || state->list)
1N/A goto done;
1N/A if (!state->count)
1N/A {
1N/A if (state->prefix)
1N/A sfprintf(sfstdout, "%s:", name);
1N/A if (state->number)
1N/A sfprintf(sfstdout, "%d:", error_info.line);
1N/A sfprintf(sfstdout, "%s:", x->string);
1N/A if (state->pos)
1N/A highlight(sfstdout, s, len + 1, state->pos[0].rm_so, state->pos[0].rm_eo);
1N/A else
1N/A sfwrite(sfstdout, s, len + 1);
1N/A }
1N/A }
1N/A else if (result != REG_NOMATCH)
1N/A regfatal(&x->re, 4, result);
1N/A } while (x = x->next);
1N/A if (!state->label && (x != 0) == state->match)
1N/A {
1N/A hits++;
1N/A if (state->query || state->list)
1N/A break;
1N/A if (!state->count)
1N/A {
1N/A if (state->prefix)
1N/A sfprintf(sfstdout, "%s:", name);
1N/A if (state->number)
1N/A sfprintf(sfstdout, "%d:", error_info.line);
1N/A if (state->pos)
1N/A highlight(sfstdout, s, len + 1, state->pos[0].rm_so, state->pos[0].rm_eo);
1N/A else
1N/A sfwrite(sfstdout, s, len + 1);
1N/A }
1N/A }
1N/A }
1N/A }
1N/A else
1N/A {
1N/A register char* e;
1N/A register char* t;
1N/A char* r;
1N/A
1N/A static char* span = 0;
1N/A static size_t spansize = 0;
1N/A
1N/A s = e = 0;
1N/A for (;;)
1N/A {
1N/A if (s < e)
1N/A {
1N/A t = span;
1N/A for (;;)
1N/A {
1N/A len = 2 * (e - s) + t - span + 1;
1N/A len = roundof(len, SF_BUFSIZE);
1N/A if (spansize < len)
1N/A {
1N/A spansize = len;
1N/A len = t - span;
1N/A if (!(span = newof(span, char, spansize, 0)))
1N/A error(ERROR_SYSTEM|3, "%s: line longer than %lu characters", name, len + e - s);
1N/A t = span + len;
1N/A }
1N/A len = e - s;
1N/A memcpy(t, s, len);
1N/A t += len;
1N/A if (!(s = sfreserve(input, SF_UNBOUND, 0)) || (len = sfvalue(input)) <= 0)
1N/A {
1N/A if ((sfvalue(input) || sferror(input)) && errno != EISDIR)
1N/A error(ERROR_SYSTEM|2, "%s: read error", name);
1N/A break;
1N/A }
1N/A else if (!(e = memchr(s, '\n', len)))
1N/A e = s + len;
1N/A else
1N/A {
1N/A r = s + len;
1N/A len = (e - s) + t - span;
1N/A len = roundof(len, SF_BUFSIZE);
1N/A if (spansize < len)
1N/A {
1N/A spansize = len;
1N/A len = t - span;
1N/A if (!(span = newof(span, char, spansize, 0)))
1N/A error(ERROR_SYSTEM|3, "%s: line longer than %lu characters", name, len + e - s);
1N/A t = span + len;
1N/A }
1N/A len = e - s;
1N/A memcpy(t, s, len);
1N/A t += len;
1N/A s += len + 1;
1N/A e = r;
1N/A break;
1N/A }
1N/A }
1N/A *t = '\n';
1N/A if (!(len = t - span))
1N/A len++;
1N/A x = state->re.head;
1N/A do
1N/A {
1N/A record_handle r_x = { state, x };
1N/A if ((result = regrexec(&x->re, span, len, state->posnum, state->pos, state->options, '\n', (void*)&r_x, record)) < 0)
1N/A goto done;
1N/A if (result && result != REG_NOMATCH)
1N/A regfatal(&x->re, 4, result);
1N/A } while (x = x->next);
1N/A if (!s)
1N/A break;
1N/A }
1N/A else
1N/A {
1N/A if (!(s = sfreserve(input, SF_UNBOUND, 0)))
1N/A {
1N/A if ((sfvalue(input) || sferror(input)) && errno != EISDIR)
1N/A error(ERROR_SYSTEM|2, "%s: read error", name);
1N/A break;
1N/A }
1N/A if ((len = sfvalue(input)) <= 0)
1N/A break;
1N/A e = s + len;
1N/A }
1N/A t = e;
1N/A while (t > s)
1N/A if (*--t == '\n')
1N/A {
1N/A len = t - s;
1N/A if (!len || t > s && *(t - 1) == '\n')
1N/A len++;
1N/A x = state->re.head;
1N/A do
1N/A {
1N/A record_handle r_x = { state, x };
1N/A error(-1, "AHA#%d [%d] %-.*s", __LINE__, len, len, s);
1N/A if ((result = regrexec(&x->re, s, len, state->posnum, state->pos, state->options, '\n', (void*)&r_x, record)) < 0)
1N/A goto done;
1N/A if (result && result != REG_NOMATCH)
1N/A regfatal(&x->re, 4, result);
1N/A } while (x = x->next);
1N/A s = t + 1;
1N/A break;
1N/A }
1N/A }
1N/A }
1N/A done:
1N/A error_info.file = file;
1N/A error_info.line = line;
1N/A if (state->byline && !state->label)
1N/A {
1N/A if (hits && state->list >= 0)
1N/A state->any = 1;
1N/A if (!state->query)
1N/A {
1N/A if (!state->list)
1N/A {
1N/A if (state->count)
1N/A {
1N/A if (state->count & 2)
1N/A state->hits += hits;
1N/A else
1N/A {
1N/A if (state->prefix)
1N/A sfprintf(sfstdout, "%s:", name);
1N/A sfprintf(sfstdout, "%I*u\n", sizeof(hits), hits);
1N/A }
1N/A }
1N/A }
1N/A else if ((hits != 0) == (state->list > 0))
1N/A {
1N/A if (state->list < 0)
1N/A state->any = 1;
1N/A sfprintf(sfstdout, "%s\n", name);
1N/A }
1N/A }
1N/A }
1N/A else
1N/A {
1N/A x = state->re.head;
1N/A do
1N/A {
1N/A if (x->hits && state->list >= 0)
1N/A {
1N/A state->any = 1;
1N/A if (state->query)
1N/A break;
1N/A }
1N/A if (!state->query)
1N/A {
1N/A if (!state->list)
1N/A {
1N/A if (state->count)
1N/A {
1N/A if (state->count & 2)
1N/A {
1N/A x->total += x->hits;
1N/A state->hits += x->hits;
1N/A }
1N/A else
1N/A {
1N/A if (state->prefix)
1N/A sfprintf(sfstdout, "%s:", name);
1N/A if (state->label)
1N/A sfprintf(sfstdout, "%s:", x->string);
1N/A sfprintf(sfstdout, "%I*u\n", sizeof(x->hits), x->hits);
1N/A }
1N/A }
1N/A }
1N/A else if ((x->hits != 0) == (state->list > 0))
1N/A {
1N/A if (state->list < 0)
1N/A state->any = 1;
1N/A if (state->label)
1N/A sfprintf(sfstdout, "%s:%s\n", name, x->string);
1N/A else
1N/A sfprintf(sfstdout, "%s\n", name);
1N/A }
1N/A }
1N/A x->hits = 0;
1N/A } while (x = x->next);
1N/A }
1N/A}
1N/A
1N/A
1N/Astatic
1N/Aint grep_main(int argc, char** argv, void *context)
1N/A{
1N/A int c;
1N/A char* s;
1N/A char* h;
1N/A Sfio_t* f;
1N/A int flags;
1N/A int old = 0;
1N/A FTS* fts;
1N/A FTSENT* ent;
1N/A State_s state;
1N/A memset(&state, 0, sizeof(state));
1N/A
1N/A NoP(argc);
1N/A flags = fts_flags() | FTS_TOP | FTS_NOPOSTORDER | FTS_NOSEEDOTDIR;
1N/A state.match = 1;
1N/A state.options = REG_FIRST|REG_NOSUB|REG_NULL;
1N/A h = 0;
1N/A if (!conformance(0, 0))
1N/A state.options |= REG_LENIENT;
1N/A if (s = strrchr(argv[0], '/'))
1N/A s++;
1N/A else
1N/A s = argv[0];
1N/A switch (*s)
1N/A {
1N/A case 'e':
1N/A case 'E':
1N/A s = "egrep";
1N/A state.options |= REG_EXTENDED;
1N/A break;
1N/A case 'f':
1N/A case 'F':
1N/A s = "fgrep";
1N/A state.options |= REG_LITERAL;
1N/A break;
1N/A case 'p':
1N/A case 'P':
1N/A s = "pgrep";
1N/A state.options |= REG_EXTENDED|REG_LENIENT;
1N/A break;
1N/A case 'x':
1N/A case 'X':
1N/A s = "xgrep";
1N/A state.options |= REG_AUGMENTED;
1N/A break;
1N/A default:
1N/A s = "grep";
1N/A break;
1N/A }
1N/A error_info.id = s;
1N/A while (c = optget(argv, usage))
1N/A switch (c)
1N/A {
1N/A case 'C':
1N/A if (opt_info.arg)
1N/A {
1N/A state.before = (int)strtol(opt_info.arg, &s, 0);
1N/A state.after = (*s == ',') ? (int)strtol(s + 1, &s, 0) : state.before;
1N/A if (*s)
1N/A error(3, "%s: invalid context line count", s);
1N/A }
1N/A else
1N/A state.before = state.after = 2;
1N/A break;
1N/A case 'E':
1N/A state.options |= REG_EXTENDED;
1N/A break;
1N/A case 'F':
1N/A state.options |= REG_LITERAL;
1N/A break;
1N/A case 'G':
1N/A state.options &= ~(REG_AUGMENTED|REG_EXTENDED);
1N/A break;
1N/A case 'H':
1N/A state.prefix = opt_info.num;
1N/A break;
1N/A case 'L':
1N/A state.list = -opt_info.num;
1N/A break;
1N/A case 'N':
1N/A h = opt_info.arg;
1N/A break;
1N/A case 'O':
1N/A state.options |= REG_LENIENT;
1N/A break;
1N/A case 'P':
1N/A state.options |= REG_EXTENDED|REG_LENIENT;
1N/A break;
1N/A case 'S':
1N/A state.options &= ~REG_LENIENT;
1N/A break;
1N/A case 'T':
1N/A s = opt_info.arg;
1N/A switch (*s)
1N/A {
1N/A case 'b':
1N/A case 'm':
1N/A c = *s++;
1N/A state.buffer.size = strton(s, &s, NiL, 1);
1N/A if (c == 'b' && !(state.buffer.base = newof(0, char, state.buffer.size, 0)))
1N/A error(ERROR_SYSTEM|3, "out of space [test buffer]");
1N/A if (*s)
1N/A error(3, "%s: invalid characters after test", s);
1N/A break;
1N/A case 'f':
1N/A state.options |= REG_FIRST;
1N/A break;
1N/A case 'l':
1N/A state.options |= REG_LEFT;
1N/A break;
1N/A case 'n':
1N/A state.buffer.noshare = 1;
1N/A break;
1N/A case 'r':
1N/A state.options |= REG_RIGHT;
1N/A break;
1N/A default:
1N/A error(3, "%s: unknown test", s);
1N/A break;
1N/A }
1N/A break;
1N/A case 'X':
1N/A state.options |= REG_AUGMENTED;
1N/A break;
1N/A case 'a':
1N/A break;
1N/A case 'b':
1N/A state.options &= ~(REG_FIRST|REG_NOSUB);
1N/A break;
1N/A case 'c':
1N/A state.count |= 1;
1N/A break;
1N/A case 'e':
1N/A addstring(&state, &state.pattern, opt_info.arg);
1N/A break;
1N/A case 'f':
1N/A addstring(&state, &state.file, opt_info.arg);
1N/A break;
1N/A case 'h':
1N/A state.prefix = 2;
1N/A break;
1N/A case 'i':
1N/A state.options |= REG_ICASE;
1N/A break;
1N/A case 'l':
1N/A state.list = opt_info.num;
1N/A break;
1N/A case 'm':
1N/A state.label = 1;
1N/A break;
1N/A case 'n':
1N/A state.number = 1;
1N/A break;
1N/A case 'q':
1N/A state.query = 1;
1N/A break;
1N/A case 'r':
1N/A if (opt_info.num)
1N/A flags &= ~FTS_TOP;
1N/A else
1N/A old = 1;
1N/A break;
1N/A case 's':
1N/A state.suppress = opt_info.num;
1N/A break;
1N/A case 't':
1N/A state.count |= 2;
1N/A break;
1N/A case 'v':
1N/A if (state.match = !opt_info.num)
1N/A state.options &= ~REG_INVERT;
1N/A else
1N/A state.options |= REG_INVERT;
1N/A break;
1N/A case 'w':
1N/A state.words = 1;
1N/A break;
1N/A case 'x':
1N/A state.options |= REG_LEFT|REG_RIGHT;
1N/A break;
1N/A case '?':
1N/A error(ERROR_USAGE|4, "%s", opt_info.arg);
1N/A break;
1N/A case ':':
1N/A error(2, "%s", opt_info.arg);
1N/A break;
1N/A default:
1N/A error(3, "%s: not implemented", opt_info.name);
1N/A break;
1N/A }
1N/A argv += opt_info.index;
1N/A if ((state.options & REG_LITERAL) && (state.options & (REG_AUGMENTED|REG_EXTENDED)))
1N/A error(3, "-F and -A or -P or -X are incompatible");
1N/A if ((state.options & REG_LITERAL) && state.words)
1N/A error(ERROR_SYSTEM|3, "-F and -w are incompatible");
1N/A if (!state.file.head && !state.pattern.head)
1N/A {
1N/A if (!argv[0])
1N/A error(3, "no pattern");
1N/A addstring(&state, &state.pattern, *argv++);
1N/A }
1N/A if (!(state.options & (REG_FIRST|REG_NOSUB)))
1N/A {
1N/A if (state.count || state.list || state.query || (state.options & REG_INVERT))
1N/A state.options |= REG_FIRST|REG_NOSUB;
1N/A else
1N/A {
1N/A state.pos = state.posvec;
1N/A state.posnum = elementsof(state.posvec);
1N/A }
1N/A }
1N/A compile(&state);
1N/A if (!argv[0])
1N/A {
1N/A state.prefix = h ? 1 : 0;
1N/A execute(&state, sfstdin, h);
1N/A }
1N/A else if (old) /* just in case the fts logic is incompatible */
1N/A {
1N/A if (state.prefix > 1)
1N/A state.prefix = 0;
1N/A else if (argv[1])
1N/A state.prefix = 1;
1N/A while (s = *argv++)
1N/A {
1N/A if (f = sfopen(NiL, s, "r"))
1N/A {
1N/A execute(&state, f, s);
1N/A sfclose(f);
1N/A if (state.query && state.any)
1N/A break;
1N/A }
1N/A else
1N/A {
1N/A state.notfound = 1;
1N/A if (!state.suppress)
1N/A error(ERROR_SYSTEM|2, "%s: cannot open", s);
1N/A }
1N/A }
1N/A }
1N/A else
1N/A {
1N/A if (state.prefix > 1)
1N/A state.prefix = 0;
1N/A else if (!(flags & FTS_TOP) || argv[1])
1N/A state.prefix = 1;
1N/A if (!(fts = fts_open(argv, flags, NiL)))
1N/A error(ERROR_SYSTEM|3, "%s: not found", argv[0]);
1N/A while (ent = fts_read(fts))
1N/A switch (ent->fts_info)
1N/A {
1N/A case FTS_F:
1N/A if (f = sfopen(NiL, ent->fts_accpath, "r"))
1N/A {
1N/A execute(&state, f, ent->fts_path);
1N/A sfclose(f);
1N/A if (state.query && state.any)
1N/A goto quit;
1N/A break;
1N/A }
1N/A /*FALLTHROUGH*/
1N/A case FTS_NS:
1N/A state.notfound = 1;
1N/A if (!state.suppress)
1N/A error(ERROR_SYSTEM|2, "%s: cannot open", ent->fts_path);
1N/A break;
1N/A case FTS_DC:
1N/A error(ERROR_WARNING|1, "%s: directory causes cycle", ent->fts_path);
1N/A break;
1N/A case FTS_DNR:
1N/A error(ERROR_SYSTEM|2, "%s: cannot read directory", ent->fts_path);
1N/A break;
1N/A case FTS_DNX:
1N/A error(ERROR_SYSTEM|2, "%s: cannot search directory", ent->fts_path);
1N/A break;
1N/A }
1N/A quit:
1N/A fts_close(fts);
1N/A }
1N/A if ((state.count & 2) && !state.query && !state.list)
1N/A {
1N/A if (state.label)
1N/A {
1N/A Item_t* x;
1N/A
1N/A x = state.re.head;
1N/A do
1N/A {
1N/A sfprintf(sfstdout, "%s:%I*u\n", x->string, sizeof(x->total), x->total);
1N/A } while (x = x->next);
1N/A }
1N/A else
1N/A sfprintf(sfstdout, "%I*u\n", sizeof(state.hits), state.hits);
1N/A }
1N/A return (state.notfound && !state.query) ? 2 : !state.any;
1N/A}
1N/A
1N/A
1N/Aint b_egrep(int argc, char** argv, void *context)
1N/A{
1N/A return grep_main(argc, argv, context);
1N/A}
1N/A
1N/Aint b_grep(int argc, char** argv, void *context)
1N/A{
1N/A return grep_main(argc, argv, context);
1N/A}
1N/A
1N/Aint b_fgrep(int argc, char** argv, void *context)
1N/A{
1N/A return grep_main(argc, argv, context);
1N/A}
1N/A
1N/Aint b_pgrep(int argc, char** argv, void *context)
1N/A{
1N/A return grep_main(argc, argv, context);
1N/A}
1N/A
1N/Aint b_xgrep(int argc, char** argv, void *context)
1N/A{
1N/A return grep_main(argc, argv, context);
1N/A}