/***********************************************************************
* *
* This software is part of the ast package *
* Copyright (c) 1995-2012 AT&T Intellectual Property *
* and is licensed under the *
* Eclipse Public License, Version 1.0 *
* by AT&T Intellectual Property *
* *
* A copy of the License is available at *
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
* *
* Information and Software Systems Research *
* AT&T Research *
* Florham Park NJ *
* *
* Glenn Fowler <gsf@research.att.com> *
* *
***********************************************************************/
#pragma prototyped
static const char usage[] =
"[-?\n@(#)$Id: grep (AT&T Research) 2012-05-07 $\n]"
"[+NAME?grep - search lines in files for matching patterns]"
"[+DESCRIPTION?The \bgrep\b commands search the named input files"
" for lines containing a match for the given \apatterns\a."
" Matching lines are printed by default. The standard input is searched"
" if no files are given or when the file \b-\b is specified.]"
"[+?There are six variants of \bgrep\b, each one using a different form of"
" \apattern\a, controlled either by option or the command path"
" base name. Details of each variant may be found in \bregex\b(3).]"
" {"
" [+grep?The default basic regular expressions (no alternations.)]"
" [+egrep?Extended regular expressions (alternations, one or more.)]"
" [+pgrep?\bperl\b(1) regular expressions (lenient extended.)]"
" [+xgrep?Augmented regular expressions (conjunction, negation.)]"
" [+fgrep?Fixed string expressions.]"
" [+agrep?Approximate regular expressions (not implemented.)]"
" }"
"[G:basic-regexp?\bgrep\b mode (default): basic regular expression \apatterns\a.]"
"[E:extended-regexp?\begrep\b mode: extended regular expression \apatterns\a.]"
"[X:augmented-regexp?\bxgrep\b mode: augmented regular expression \apatterns\a.]"
"[P:perl-regexp?\bpgrep\b mode: \bperl\b(1) regular expression \apatterns\a.]"
"[F:fixed-string?\bfgrep\b mode: fixed string \apatterns\a.]"
"[A:approximate-regexp?\bagrep\b mode: approximate regular expression \apatterns\a (not implemented.)]"
"[C:context?Set the matched line context \abefore\a and \aafter\a count."
" If ,\aafter\a is omitted then it is set to \abefore\a."
" By default only matched lines are printed.]:?"
" [before[,after]]:=2,2]"
"[c:count?Only print a matching line count for each file.]"
"[e:expression|pattern|regexp?Specify a matching \apattern\a. More than one"
" \apattern\a implies alternation. If this option is specified"
" then the command line \apattern\a must be omitted.]:"
" [pattern]"
"[f:file?Each line in \apattern-file\a is a \apattern\a, placed into a single"
" alternating expression.]:"
" [pattern-file]"
"[H:filename|with-filename?Prefix each matched line with the containing file name.]"
"[h:no-filename?Suppress containing file name prefix for each matched line.]"
"[i:ignore-case?Ignore case when matching.]"
"[l:files-with-matches?Only print file names with at least one match.]"
"[L:files-without-matches?Only print file names with no matches.]"
"[b:highlight?Highlight matches using the ansi terminal bold sequence.]"
"[v:invert-match|revert-match?Invert the \apattern\a match sense.]"
"[m:label?All patterns must be of the form \alabel\a:\apattern\a. Match and"
" count output will be prefixed by the corresponding \alabel\a:.]"
"[O:lenient?Enable lenient \apattern\a interpretation. This is the default.]"
"[x:line-match|line-regexp?Force \apatterns\a to match complete lines.]"
"[n:number|line-number?Prefix each matched line with its line number.]"
"[N:name?Set the standard input file name prefix to"
" \aname\a.]:[name:=empty]"
"[q:quiet|silent?Do not print matching lines.]"
"[r|R:recursive?Recursively process all files in each named directory. "
"Use \btw -e\b \aexpression\a \bgrep ...\b to control the directory "
"traversal.]"
"[S:strict?Enable strict \apattern\a interpretation with diagnostics.]"
"[s:suppress|no-messages?Suppress error and warning messages.]"
"[t:total?Only print a single matching line count for all files.]"
"[T:test?Enable implementation specific tests.]:"
" [test]"
"[w:word-match|word-regexp?Force \apatterns\a to match complete words.]"
"[a?Ignored for GNU compatibility.]"
"[Y:color|colour?Ignored for GNU compatibility.]:[when]"
"\n"
"\n[ pattern ] [ file ... ]\n"
"\n"
"[+DIAGNOSTICS?Exit status 0 if matches were found, 1 if no matches were found,"
" where \b-v\b invertes the exit status. Exit status 2 for other"
" errors that are accompanied by a message on the standard error.]"
"[+SEE ALSO?\bed\b(1), \bsed\b(1), \bperl\b(1), \btw\b(1), \bregex\b(3)]"
"[+CAVEATS?Some expressions of necessity require exponential space"
"[+BUGS?Some expressions may use sub-optimal algorithms. For example,"
" don't use this implementation to compute primes.]"
;
#include <ast.h>
#include <ctype.h>
#include <ccode.h>
#include <error.h>
#include <fts.h>
#include <regex.h>
#ifndef EISDIR
#endif
/*
* snarfed from Doug McElroy's C++ version
*
* this grep is based on the Posix re package.
* unfortunately it has to have a nonstandard interface.
* 1. fgrep does not have usual operators. REG_LITERAL
* caters for this.
* 2. grep allows null expressions, hence REG_NULL.
* 3. it may be possible to combine the multiple
* patterns of grep into single patterns. important
* special cases are handled by regcomb().
* 4. anchoring by -x has to be done separately from
* compilation (remember that fgrep has no ^ or $ operator),
* hence REG_LEFT|REG_RIGHT. (An honest, but slow alternative:
* run regexec with REG_NOSUB off and nmatch=1 and check
* whether the match is full length)
*/
{
} Item_t;
{
} List_t;
{
struct
{
} buffer;
} state;
static void
{
int c;
char* b;
Item_t* x;
Sfio_t* t;
b = s;
{
if (!(s = strchr(s, ':')))
c = s - b;
s++;
}
else
c = 0;
if (c)
{
if (!(t = sfstropen()))
sfputc(t, '\\');
sfputc(t, '<');
sfputr(t, s, -1);
sfputc(t, '\\');
sfputc(t, '>');
if (!(s = sfstruse(t)))
}
else
t = 0;
if (t)
sfstrclose(t);
if (!p->head)
{
}
{
}
else
free(x);
}
static void
{
Item_t* x;
if (p->head)
else
p->head = x;
p->tail = x;
}
static void
compile(void)
{
int line;
size_t n;
char* s;
char* t;
char* file;
Item_t* x;
Sfio_t* f;
{
s = x->string;
else
{
error_info.file = s;
error_info.line = 0;
{
if (!(n = sfvalue(f)))
break;
if (s[n - 1] != '\n')
{
for (t = s + n; t > s && *--t != '\n'; t--);
if (t == s)
{
sfread(f, s, 0);
break;
}
n = t - s + 1;
}
s[n - 1] = 0;
s[n - 1] = '\n';
sfread(f, s, n);
}
{
error_info.line++;
}
sfclose(f);
}
}
}
static void
{
}
static int
{
return -1;
{
else
}
return 0;
}
static void
{
register char* s;
char* file;
Item_t* x;
int result;
int line;
if (!name)
error_info.line = 0;
{
for (;;)
{
error_info.line++;
{
s[len] = '\n';
#endif
}
else
{
break;
}
do
{
{
break;
x->hits++;
goto done;
{
else
}
}
else if (result != REG_NOMATCH)
} while (x = x->next);
{
hits++;
break;
{
else
}
}
}
}
else
{
register char* e;
register char* t;
char* r;
static char* span = 0;
s = e = 0;
for (;;)
{
if (s < e)
{
t = span;
for (;;)
{
{
}
len = e - s;
t += len;
{
break;
}
e = s + len;
else
{
r = s + len;
{
}
len = e - s;
t += len;
s += len + 1;
e = r;
break;
}
}
*t = '\n';
len++;
do
{
if ((result = regrexec(&x->re, span, len, state.posnum, state.pos, state.options, '\n', (void*)x, record)) < 0)
goto done;
} while (x = x->next);
if (!s)
break;
}
else
{
{
break;
}
break;
e = s + len;
}
t = e;
while (t > s)
if (*--t == '\n')
{
len = t - s;
len++;
do
{
if ((result = regrexec(&x->re, s, len, state.posnum, state.pos, state.options, '\n', (void*)x, record)) < 0)
goto done;
} while (x = x->next);
s = t + 1;
break;
}
}
}
done:
{
{
{
{
else
{
}
}
}
{
}
}
}
else
{
do
{
{
break;
}
{
{
{
{
}
else
{
}
}
}
{
else
}
}
x->hits = 0;
} while (x = x->next);
}
}
int
{
int c;
char* s;
char* h;
Sfio_t* f;
int flags;
int old = 0;
h = 0;
if (!conformance(0, 0))
s++;
else
s = argv[0];
switch (*s)
{
case 'e':
case 'E':
s = "egrep";
break;
case 'f':
case 'F':
s = "fgrep";
break;
case 'p':
case 'P':
s = "pgrep";
break;
case 'x':
case 'X':
s = "xgrep";
break;
default:
s = "grep";
break;
}
error_info.id = s;
switch (c)
{
case 'C':
{
if (*s)
}
else
break;
case 'E':
break;
case 'F':
break;
case 'G':
break;
case 'H':
break;
case 'L':
break;
case 'N':
break;
case 'O':
break;
case 'P':
break;
case 'S':
break;
case 'T':
switch (*s)
{
case 'b':
case 'm':
c = *s++;
if (*s)
break;
case 'f':
break;
case 'l':
break;
case 'n':
break;
case 'r':
break;
default:
break;
}
break;
case 'X':
break;
case 'a':
break;
case 'b':
break;
case 'c':
break;
case 'e':
break;
case 'f':
break;
case 'h':
break;
case 'i':
break;
case 'l':
break;
case 'm':
break;
case 'n':
break;
case 'q':
break;
case 'r':
else
old = 1;
break;
case 's':
break;
case 't':
break;
case 'v':
else
break;
case 'w':
break;
case 'x':
break;
case 'Y':
/* ignored for GNU compatibility */
break;
case '?':
break;
case ':':
break;
default:
break;
}
{
if (!argv[0])
}
{
else
{
}
}
compile();
if (!argv[0])
{
}
else if (old) /* just in case the fts logic is incompatible */
{
else if (argv[1])
while (s = *argv++)
{
{
execute(f, s);
sfclose(f);
break;
}
else
{
}
}
}
else
{
{
case FTS_F:
{
sfclose(f);
goto quit;
break;
}
/*FALLTHROUGH*/
case FTS_NS:
case FTS_SLNONE:
break;
case FTS_DC:
break;
case FTS_DNR:
break;
case FTS_DNX:
break;
}
quit:
}
{
{
Item_t* x;
do
{
} while (x = x->next);
}
else
}
}