diff options
author | Roland Mainz <roland.mainz@nrubsig.org> | 2009-10-28 10:36:39 -0700 |
---|---|---|
committer | Roland Mainz <roland.mainz@nrubsig.org> | 2009-10-28 10:36:39 -0700 |
commit | 34f9b3eef6fdadbda0a846aa4d68691ac40eace5 (patch) | |
tree | 0b0fdfb35f8eb9324728de5a99e50e939aca650f /usr/src/lib/libcmd/common/grep.c | |
parent | 14969419acb89bb74e6c95fa472119b710224440 (diff) | |
download | illumos-joyent-34f9b3eef6fdadbda0a846aa4d68691ac40eace5.tar.gz |
Portions contributed by Olga Kryzhanovska <olga.kryzhanovska@gmail.com>
PSARC/2009/063 ksh93 update 2
PSARC/2009/248 ksh93 update to 2009-03-10
PSARC/2009/249 more ksh93 command conversions
6888396 libast sources should not include localedef.h
6605478 ksh93 profile shell option does not work
6631006 ksh93 hangs in situations that ksh handles okay
6661487 logname reports nothing after running the script command
6705126 first call to read doesn't honor new setting of HISTFILE
6764665 *libpp* Array overrun in libpp
6765756 *libast* Array overruns in libast
6769332 Recursive function+command substitutions terminate shell after 257 iterations
6777491 *ksh93* lacks arithmetric function iszero()
6778077 *ksh93* does not understand "THAW" as a signal for use with trap
6789247 [ku1] libast/ksh93 1-digit hexfloat base conversion rounds incorrectly
6791838 *ksh93* unset of a variable which is not set should return 0
6793714 RFE: Update /usr/bin/comm to AT&T AST "comm"
6793719 RFE: Update /usr/bin/cut to AT&T AST "cut"
6793721 RFE: Update /usr/bin/paste to AT&T AST "paste"
6793722 RFE: Update /usr/bin/cmp to AT&T AST "cmp"
6793726 RFE: Update /usr/bin/uniq to AT&T AST "uniq"
6793735 RFE: Update /usr/bin/wc to AT&T AST "wc"
6793744 RFE: Add /usr/share/doc/ksh/ for ksh93 documentation
6793747 RFE: Provide "print" builtin as /usr/bin/print for external applications
6793763 RFE: Update /usr/bin/ksh93 to ast-ksh.2009-05-05
6794952 RFE: Enable "globstar" mode in /etc/ksh.kshrc (= interactive ksh93 shells)
6805792 [ku1] Moving local compound var into array does not work
6805794 [ku1] printf returns "invalid character constant" for $ printf "%d\n" "'<euro>"
6805795 [ku1] ksh93 does not differ between -0 and +0
6805797 [ku1]Can't append to nodes of an array of compound vars if addressing them via nameref
6805799 Indexed compound variable arrays do not work...
6805800 [ku1] Declaring associative compound array does not work
6805813 RFE: Update /usr/bin/join to AT&T AST "join".
6805819 RFE: Update /usr/bin/tee to AT&T AST "tee".
6809663 shlint missing ending newline on errors
6811916 ksh93 repeatedly segfaults when "tee" builtin is interrupted via <ctrl-c> in interactive mode
6821113 SUNWosdem package issues
6828644 RFE: Update /usr/bin/logname to AT&T AST "logname".
6828692 RFE: Update /usr/bin/cksum to AT&T AST "cksum".
6834184 ksh93 gets SIGSEGV if HISTFILE is changed in place.
6834207 ksh93 gets SIGSEGV on interactive function definition with HISTSIZE unset
6835835 ksh93 "cat" builtin does not handle "-n" correctly
6841442 Need exception list for OS/Net trees managed via Subversion
6848486 "echo ${test}" with test undefined crashes the shell
6850672 ksh93 (VISUAL=vi) crashes with memory fault while scrolling through history
6855875 typeset -X x ; print $x # does not print sufficient digits to restore value
6857344 /usr/bin/hash core dump with invalid arguments
6866676 Need test suite module to test the kernel support for compiled shell scripts
6881017 Subshell doesn't exit, holds pipe open preventing callers from exiting
6884409 fts functions in libast library can result in segv with deep dir trees (similar to CERT VU#590371)
Diffstat (limited to 'usr/src/lib/libcmd/common/grep.c')
-rw-r--r-- | usr/src/lib/libcmd/common/grep.c | 876 |
1 files changed, 876 insertions, 0 deletions
diff --git a/usr/src/lib/libcmd/common/grep.c b/usr/src/lib/libcmd/common/grep.c new file mode 100644 index 0000000000..1e857f087c --- /dev/null +++ b/usr/src/lib/libcmd/common/grep.c @@ -0,0 +1,876 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1995-2009 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* 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) 2006-06-14 $\n]" +USAGE_LICENSE +"[+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." +" 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.]" +"[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.]" +"\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), \bregex\b(3)]" +"[+CAVEATS?Some expressions of necessity require exponential space" +" and/or time.]" +"[+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 <regex.h> + +#ifndef EISDIR +#define EISDIR (-1) +#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) + */ + +typedef struct Item_s /* list item - sue me for waste */ +{ + struct Item_s* next; /* next in list */ + regex_t re; /* compiled re */ + Sfulong_t hits; /* labeled pattern matches */ + Sfulong_t total; /* total hits */ + char string[1]; /* string value */ +} Item_t; + +typedef struct List_s /* generic list */ +{ + Item_t* head; /* list head */ + Item_t* tail; /* list tail */ +} List_t; + +typedef struct State_s /* program state */ +{ + struct + { + char* base; /* sfsetbuf buffer */ + size_t size; /* sfsetbuf size */ + int noshare; /* turn off SF_SHARE */ + } buffer; + + List_t file; /* pattern file list */ + List_t pattern; /* pattern list */ + List_t re; /* re list */ + + regmatch_t posvec[1]; /* match position vector */ + regmatch_t* pos; /* match position pointer */ + int posnum; /* number of match positions */ + + int any; /* if any pattern hit */ + int list; /* list files with hits */ + int notfound; /* some input file not found */ + int options; /* regex options */ + + Sfulong_t hits; /* total matched pattern count */ + + unsigned char byline; /* multiple pattern line by line*/ + unsigned char count; /* count number of hits */ + unsigned char label; /* all patterns labeled */ + unsigned char match; /* match sense */ + unsigned char query; /* return status but no output */ + unsigned char number; /* line numbers */ + unsigned char prefix; /* print file prefix */ + unsigned char suppress; /* no unopenable file messages */ + unsigned char words; /* word matches only */ +} State_s; + +static void +addre(State_s *state, List_t* p, char* s) +{ + int c; + char* b; + Item_t* x; + Sfio_t* t; + + b = s; + if (state->label) + { + if (!(s = strchr(s, ':'))) + error(3, "%s: label:pattern expected", b); + c = s - b; + s++; + } + else + c = 0; + if (!(x = newof(0, Item_t, 1, c))) + error(ERROR_SYSTEM|3, "out of space (pattern `%s')", b); + if (c) + memcpy(x->string, b, c); + if (state->words) + { + if (!(t = sfstropen())) + error(ERROR_SYSTEM|3, "out of space (word pattern `%s')", s); + if (!(state->options & REG_AUGMENTED)) + sfputc(t, '\\'); + sfputc(t, '<'); + sfputr(t, s, -1); + if (!(state->options & REG_AUGMENTED)) + sfputc(t, '\\'); + sfputc(t, '>'); + if (!(s = sfstruse(t))) + error(ERROR_SYSTEM|3, "out of space"); + } + else + t = 0; + if (c = regcomp(&x->re, s, state->options|REG_MULTIPLE)) + regfatal(&x->re, 3, c); + if (t) + sfstrclose(t); + if (!p->head) + { + p->head = p->tail = x; + if (state->number || !regrecord(&x->re)) + state->byline = 1; + } + else if (state->label || regcomb(&p->tail->re, &x->re)) + { + p->tail = p->tail->next = x; + if (!state->byline && (state->number || !state->label || !regrecord(&x->re))) + state->byline = 1; + } + else + free(x); +} + +static void +addstring(State_s *state, List_t* p, char* s) +{ + Item_t* x; + + if (!(x = newof(0, Item_t, 1, strlen(s)))) + error(ERROR_SYSTEM|3, "out of space (string `%s')", s); + strcpy(x->string, s); + if (p->head) + p->tail->next = x; + else + p->head = x; + p->tail = x; +} + +static void +compile(State_s *state) +{ + int line; + size_t n; + char* s; + char* t; + char* file; + Item_t* x; + Sfio_t* f; + + for (x = state->pattern.head; x; x = x->next) + addre(state, &state->re, x->string); + for (x = state->file.head; x; x = x->next) + { + s = x->string; + if (!(f = sfopen(NiL, s, "r"))) + error(ERROR_SYSTEM|4, "%s: cannot open", s); + else + { + file = error_info.file; + error_info.file = s; + line = error_info.line; + error_info.line = 0; + while (s = (char*)sfreserve(f, SF_UNBOUND, SF_LOCKR)) + { + 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; + addre(state, &state->re, s); + s[n - 1] = '\n'; + sfread(f, s, n); + } + while ((s = sfgetr(f, '\n', 1)) || (s = sfgetr(f, '\n', -1))) + { + error_info.line++; + addre(state, &state->re, s); + } + error_info.file = file; + error_info.line = line; + sfclose(f); + } + } + if (!state->re.head) + error(3, "no pattern"); +} + +static void +highlight(Sfio_t* sp, const char* s, int n, int so, int eo) +{ + static const char bold[] = {CC_esc,'[','1','m'}; + static const char normal[] = {CC_esc,'[','0','m'}; + + sfwrite(sp, s, so); + sfwrite(sp, bold, sizeof(bold)); + sfwrite(sp, s + so, eo - so); + sfwrite(sp, normal, sizeof(normal)); + sfwrite(sp, s + eo, n - eo); +} + +typedef struct +{ + State_s *state; + Item_t *item; +} record_handle; + +static int +record(void* handle, const char* s, size_t len) +{ + record_handle *r_x = (record_handle *)handle; + State_s *state = r_x->state; + Item_t *item = r_x->item; + + item->hits++; + if (state->query || state->list) + return -1; + if (!state->count) + { + if (state->prefix) + sfprintf(sfstdout, "%s:", error_info.file); + if (state->label) + sfprintf(sfstdout, "%s:", item->string); + if (state->pos) + highlight(sfstdout, s, len + 1, state->pos[0].rm_so, state->pos[0].rm_eo); + else + sfwrite(sfstdout, s, len + 1); + } + return 0; +} + +static void +execute(State_s *state, Sfio_t* input, char* name) +{ + register char* s; + char* file; + Item_t* x; + size_t len; + int result; + int line; + + Sfulong_t hits = 0; + + if (state->buffer.noshare) + sfset(input, SF_SHARE, 0); + if (state->buffer.size) + sfsetbuf(input, state->buffer.base, state->buffer.size); + if (!name) + name = "/dev/stdin"; + file = error_info.file; + error_info.file = name; + line = error_info.line; + error_info.line = 0; + if (state->byline) + { + for (;;) + { + error_info.line++; + if (s = sfgetr(input, '\n', 0)) + len = sfvalue(input) - 1; + else if (s = sfgetr(input, '\n', -1)) + { + len = sfvalue(input); + s[len] = '\n'; +#if _you_like_the_noise + error(1, "newline appended"); +#endif + } + else + { + if (sferror(input) && errno != EISDIR) + error(ERROR_SYSTEM|2, "read error"); + break; + } + x = state->re.head; + do + { + if (!(result = regnexec(&x->re, s, len, state->posnum, state->pos, 0))) + { + if (!state->label) + break; + x->hits++; + if (state->query || state->list) + goto done; + if (!state->count) + { + if (state->prefix) + sfprintf(sfstdout, "%s:", name); + if (state->number) + sfprintf(sfstdout, "%d:", error_info.line); + sfprintf(sfstdout, "%s:", x->string); + if (state->pos) + highlight(sfstdout, s, len + 1, state->pos[0].rm_so, state->pos[0].rm_eo); + else + sfwrite(sfstdout, s, len + 1); + } + } + else if (result != REG_NOMATCH) + regfatal(&x->re, 3, result); + } while (x = x->next); + if (!state->label && (x != 0) == state->match) + { + hits++; + if (state->query || state->list) + break; + if (!state->count) + { + if (state->prefix) + sfprintf(sfstdout, "%s:", name); + if (state->number) + sfprintf(sfstdout, "%d:", error_info.line); + if (state->pos) + highlight(sfstdout, s, len + 1, state->pos[0].rm_so, state->pos[0].rm_eo); + else + sfwrite(sfstdout, s, len + 1); + } + } + } + } + else + { + register char* e; + register char* t; + char* r; + + static char* span = 0; + static size_t spansize = 0; + + s = e = 0; + for (;;) + { + if (s < e) + { + t = span; + for (;;) + { + len = 2 * (e - s) + t - span + 1; + len = roundof(len, SF_BUFSIZE); + if (spansize < len) + { + spansize = len; + len = t - span; + if (!(span = newof(span, char, spansize, 0))) + error(ERROR_SYSTEM|3, "%s: line longer than %lu characters", name, len + e - s); + t = span + len; + } + len = e - s; + memcpy(t, s, len); + t += len; + if (!(s = sfreserve(input, SF_UNBOUND, 0)) || (len = sfvalue(input)) <= 0) + { + if ((sfvalue(input) || sferror(input)) && errno != EISDIR) + error(ERROR_SYSTEM|2, "%s: read error", name); + break; + } + else if (!(e = memchr(s, '\n', len))) + e = s + len; + else + { + r = s + len; + len = (e - s) + t - span; + len = roundof(len, SF_BUFSIZE); + if (spansize < len) + { + spansize = len; + len = t - span; + if (!(span = newof(span, char, spansize, 0))) + error(ERROR_SYSTEM|3, "%s: line longer than %lu characters", name, len + e - s); + t = span + len; + } + len = e - s; + memcpy(t, s, len); + t += len; + s += len + 1; + e = r; + break; + } + } + *t = '\n'; + x = state->re.head; + do + { + record_handle r_x = { state, x }; + if ((result = regrexec(&x->re, span, t - span, state->posnum, state->pos, state->options, '\n', (void*)&r_x, record)) < 0) + goto done; + if (result && result != REG_NOMATCH) + regfatal(&x->re, 3, result); + } while (x = x->next); + if (!s) + break; + } + else + { + if (!(s = sfreserve(input, SF_UNBOUND, 0))) + { + if ((sfvalue(input) || sferror(input)) && errno != EISDIR) + error(ERROR_SYSTEM|2, "%s: read error", name); + break; + } + if ((len = sfvalue(input)) <= 0) + break; + e = s + len; + } + t = e; + while (t > s) + if (*--t == '\n') + { + x = state->re.head; + do + { + record_handle r_x = { state, x }; + if ((result = regrexec(&x->re, s, t - s, state->posnum, state->pos, state->options, '\n', (void*)&r_x, record)) < 0) + goto done; + if (result && result != REG_NOMATCH) + regfatal(&x->re, 3, result); + } while (x = x->next); + s = t + 1; + break; + } + } + } + done: + error_info.file = file; + error_info.line = line; + if (state->byline && !state->label) + { + if (hits && state->list >= 0) + state->any = 1; + if (!state->query) + { + if (!state->list) + { + if (state->count) + { + if (state->count & 2) + state->hits += hits; + else + { + if (state->prefix) + sfprintf(sfstdout, "%s:", name); + sfprintf(sfstdout, "%I*u\n", sizeof(hits), hits); + } + } + } + else if ((hits != 0) == (state->list > 0)) + { + if (state->list < 0) + state->any = 1; + sfprintf(sfstdout, "%s\n", name); + } + } + } + else + { + x = state->re.head; + do + { + if (x->hits && state->list >= 0) + { + state->any = 1; + if (state->query) + break; + } + if (!state->query) + { + if (!state->list) + { + if (state->count) + { + if (state->count & 2) + { + x->total += x->hits; + state->hits += x->hits; + } + else + { + if (state->prefix) + sfprintf(sfstdout, "%s:", name); + if (state->label) + sfprintf(sfstdout, "%s:", x->string); + sfprintf(sfstdout, "%I*u\n", sizeof(x->hits), x->hits); + } + } + } + else if ((x->hits != 0) == (state->list > 0)) + { + if (state->list < 0) + state->any = 1; + if (state->label) + sfprintf(sfstdout, "%s:%s\n", name, x->string); + else + sfprintf(sfstdout, "%s\n", name); + } + } + x->hits = 0; + } while (x = x->next); + } +} + + +static +int grep_main(int argc, char** argv, void *context) +{ + int c; + char* s; + char* h; + Sfio_t* f; + State_s state; + memset(&state, 0, sizeof(state)); + + NoP(argc); + state.match = 1; + state.options = REG_FIRST|REG_NOSUB|REG_NULL; + h = 0; + if (strcmp(astconf("CONFORMANCE", NiL, NiL), "standard")) + state.options |= REG_LENIENT; + if (s = strrchr(argv[0], '/')) + s++; + else + s = argv[0]; + switch (*s) + { + case 'e': + case 'E': + s = "egrep"; + state.options |= REG_EXTENDED; + break; + case 'f': + case 'F': + s = "fgrep"; + state.options |= REG_LITERAL; + break; + case 'p': + case 'P': + s = "pgrep"; + state.options |= REG_EXTENDED|REG_LENIENT; + break; + case 'x': + case 'X': + s = "xgrep"; + state.options |= REG_AUGMENTED; + break; + default: + s = "grep"; + break; + } + error_info.id = s; + while (c = optget(argv, usage)) + switch (c) + { + case 'E': + state.options |= REG_EXTENDED; + break; + case 'F': + state.options |= REG_LITERAL; + break; + case 'G': + state.options &= ~(REG_AUGMENTED|REG_EXTENDED); + break; + case 'H': + state.prefix = opt_info.num; + break; + case 'L': + state.list = -opt_info.num; + break; + case 'N': + h = opt_info.arg; + break; + case 'O': + state.options |= REG_LENIENT; + break; + case 'P': + state.options |= REG_EXTENDED|REG_LENIENT; + break; + case 'S': + state.options &= ~REG_LENIENT; + break; + case 'T': + s = opt_info.arg; + switch (*s) + { + case 'b': + case 'm': + c = *s++; + state.buffer.size = strton(s, &s, NiL, 1); + if (c == 'b' && !(state.buffer.base = newof(0, char, state.buffer.size, 0))) + error(ERROR_SYSTEM|3, "out of space [test buffer]"); + if (*s) + error(3, "%s: invalid characters after test", s); + break; + case 'f': + state.options |= REG_FIRST; + break; + case 'l': + state.options |= REG_LEFT; + break; + case 'n': + state.buffer.noshare = 1; + break; + case 'r': + state.options |= REG_RIGHT; + break; + default: + error(3, "%s: unknown test", s); + break; + } + break; + case 'X': + state.options |= REG_AUGMENTED; + break; + case 'a': + break; + case 'b': + state.options &= ~(REG_FIRST|REG_NOSUB); + break; + case 'c': + state.count |= 1; + break; + case 'e': + addstring(&state, &state.pattern, opt_info.arg); + break; + case 'f': + addstring(&state, &state.file, opt_info.arg); + break; + case 'h': + state.prefix = 2; + break; + case 'i': + state.options |= REG_ICASE; + break; + case 'l': + state.list = opt_info.num; + break; + case 'm': + state.label = 1; + break; + case 'n': + state.number = 1; + break; + case 'q': + state.query = 1; + break; + case 's': + state.suppress = opt_info.num; + break; + case 't': + state.count |= 2; + break; + case 'v': + if (state.match = !opt_info.num) + state.options &= ~REG_INVERT; + else + state.options |= REG_INVERT; + break; + case 'w': + state.words = 1; + break; + case 'x': + state.options |= REG_LEFT|REG_RIGHT; + break; + case '?': + error(ERROR_USAGE|4, "%s", opt_info.arg); + break; + case ':': + error(2, "%s", opt_info.arg); + break; + default: + error(3, "%s: not implemented", opt_info.name); + break; + } + argv += opt_info.index; + if ((state.options & REG_LITERAL) && (state.options & (REG_AUGMENTED|REG_EXTENDED))) + error(3, "-F and -A or -P or -X are incompatible"); + if ((state.options & REG_LITERAL) && state.words) + error(ERROR_SYSTEM|3, "-F and -w are incompatible"); + if (!state.file.head && !state.pattern.head) + { + if (!argv[0]) + error(3, "no pattern"); + addstring(&state, &state.pattern, *argv++); + } + if (!(state.options & (REG_FIRST|REG_NOSUB))) + { + if (state.count || state.list || state.query || (state.options & REG_INVERT)) + state.options |= REG_FIRST|REG_NOSUB; + else + { + state.pos = state.posvec; + state.posnum = elementsof(state.posvec); + } + } + compile(&state); + if (!argv[0]) + { + state.prefix = h ? 1 : 0; + execute(&state, sfstdin, h); + } + else + { + if (state.prefix > 1) + state.prefix = 0; + else if (argv[1]) + state.prefix = 1; + while (s = *argv++) + { + if (f = sfopen(NiL, s, "r")) + { + execute(&state, f, s); + sfclose(f); + if (state.query && state.any) + break; + } + else + { + state.notfound = 1; + if (!state.suppress) + error(ERROR_SYSTEM|2, "%s: cannot open", s); + } + } + } + if ((state.count & 2) && !state.query && !state.list) + { + if (state.label) + { + Item_t* x; + + x = state.re.head; + do + { + sfprintf(sfstdout, "%s:%I*u\n", x->string, sizeof(x->total), x->total); + } while (x = x->next); + } + else + sfprintf(sfstdout, "%I*u\n", sizeof(state.hits), state.hits); + } + return (state.notfound && !state.query) ? 2 : !state.any; +} + + +int b_egrep(int argc, char** argv, void *context) +{ + return grep_main(argc, argv, context); +} + +int b_grep(int argc, char** argv, void *context) +{ + return grep_main(argc, argv, context); +} + +int b_fgrep(int argc, char** argv, void *context) +{ + return grep_main(argc, argv, context); +} + +int b_pgrep(int argc, char** argv, void *context) +{ + return grep_main(argc, argv, context); +} + +int b_xgrep(int argc, char** argv, void *context) +{ + return grep_main(argc, argv, context); +} |