Annotation of 43BSDReno/usr.bin/grep/egrep/egrep.c, revision 1.1.1.1

1.1       root        1: #ifndef lint
                      2: static char sccsid[] = "@(#)egrep.c    5.12 (Berkeley) 5/11/89";
                      3: #endif not lint
                      4: 
                      5: /*
                      6:      Hybrid Boyer/Moore/Gosper-assisted 'grep/egrep/fgrep' search, with delta0
                      7:      table as in original paper (CACM, October, 1977).  No delta1 or delta2.
                      8:      According to experiment (Horspool, Soft. Prac. Exp., 1982), delta2 is of
                      9:      minimal practical value.  However, to improve for worst case input,
                     10:      integrating the improved Galil strategies (Apostolico/Giancarlo, SIAM. J.
                     11:      Comput., Feb. 1986) deserves consideration.
                     12: 
                     13:      Method:   extract longest metacharacter-free string from expression.
                     14:                this is done using a side-effect from henry spencer's regcomp().
                     15:                use boyer-moore to match such, then pass submatching lines
                     16:                to either regexp() or standard 'egrep', depending on certain
                     17:                criteria within execstrategy() below.  [this tradeoff is due
                     18:                to the general slowness of the regexp() nondeterministic
                     19:                machine on complex expressions, as well as the startup time
                     20:                of standard 'egrep' on short files.]  alternatively, one may
                     21:                change the vendor-supplied 'egrep' automaton to include
                     22:                boyer-moore directly.  see accompanying writeup for discussion
                     23:                of kanji expression treatment.
                     24: 
                     25:                late addition:  apply trickbag for fast match of simple
                     26:                alternations (sublinear, in common low-cardinality cases).
                     27:                trap fgrep into this lair.
                     28: 
                     29:                gnu additions:  -f, newline as |, \< and \> [in regexec()], more
                     30:                                comments.  inspire better dfa exec() strategy.
                     31:                                serious testing and help with special cases.
                     32: 
                     33:      Algorithm amalgam summary:
                     34: 
                     35:                dfa e?grep              (aho/thompson)
                     36:                ndfa regexp()           (spencer/aho)
                     37:                bmg                     (boyer/moore/gosper)
                     38:                "superimposed" bmg      (jaw)
                     39:                fgrep                   (aho/corrasick)
                     40: 
                     41:                sorry, but the knuth/morris/pratt machine, horspool's
                     42:                "frequentist" code, and the rabin/karp matcher, however cute,
                     43:                just don't cut it for this production.
                     44: 
                     45:      James A. Woods                            Copyright (c) 1986
                     46:      NASA Ames Research Center
                     47: */
                     48: #include <sys/types.h>
                     49: #include <sys/stat.h>
                     50: #include <sys/file.h>
                     51: #include <regexp.h>            /* must be henry spencer's version */
                     52: #include <stdio.h>
                     53: #include <ctype.h>
                     54: #include "pathnames.h"
                     55: 
                     56: #define        MIN(A, B)       ((A) > (B) ? (B) : (A))
                     57: 
                     58: #ifdef SLOWSYS
                     59: #define read   xread
                     60: #endif
                     61: 
                     62: #define BUFSIZE        8192            /* make higher for cray */
                     63: #define PATSIZE 6000
                     64: #define LARGE  BUFSIZE + PATSIZE
                     65: 
                     66: #define NALT   7               /* tied to scanf() size in alternate() */
                     67: #define NMUSH  6               /* loosely relates to expected alt length */
                     68: 
                     69: #define        FIRSTFEW        33      /* Always do FIRSTFEW matches with regexec() */
                     70: #define        PUNTPERCENT     10      /* After FIRSTFEW, if PUNTPERCENT of the input
                     71:                                 * was processed by regexp(), exec std egrep. */
                     72: #define NL     '\n'
                     73: #define        EOS     '\0'
                     74: #define        NONASCII        0200    /* Bit mask for Kanji non-ascii chars */
                     75: #define META   "\n^$.[]()?+*|\\"       /* egrep meta-characters */
                     76: #define SS2    '\216'          /* EUC Katakana (or Chinese2) prefix */
                     77: #define SS3    '\217'          /* EUC Kanji2 (or Chinese3) prefix */
                     78: 
                     79: extern char *optarg;
                     80: extern int optind;
                     81: char *progname;
                     82: 
                     83: int cflag, iflag, eflag, fflag, lflag, nflag;  /* SVID flags */
                     84: int sflag, hflag;              /* v7, v8, bsd */
                     85: 
                     86: int firstflag;                 /* Stop at first match */
                     87: int grepflag;                  /* Called as "grep" */
                     88: int fgrepflag;                 /* Called as "fgrep" */
                     89: int altflag;                   /* Simple alternation in pattern */
                     90: int boyonly;                   /* No regexp needed -- all simple */
                     91: int flushflag;
                     92: int grepold, egrepold, fgrepold;
                     93: 
                     94: int nalt;                      /* Number of alternatives */
                     95: int nsuccess;                  /* 1 for match, 2 for error */
                     96: int altmin;                    /* Minimum length of all the alternate
                     97:                                 * strings */
                     98: int firstfile;                 /* argv index of first file argument */
                     99: int patind;                    /* argv index of pattern */
                    100: long nmatch;                   /* Number of matches in this file */
                    101: long incount, counted;         /* Amount of input consumed */
                    102: long rxcount;                  /* Bytes of input processed by regexec() */
                    103: int boyfound;                  /* accumulated partial matches (tripped by
                    104:                                 * FIRSTFEW) */
                    105: int prevmatch;                 /* next three lines aid fast -n */
                    106: long nline, prevnline;
                    107: char *prevloc;
                    108: 
                    109: regexp *rspencer;
                    110: char *pattern;
                    111: char *patboy;                  /* Pattern for simple Boyer-Moore */
                    112: char *patfile;                 /* Filename containing pattern(s) */
                    113: 
                    114: int delta0[256];               /* Boyer-Moore algorithm core */
                    115: char cmap[256];                        /* Usually 0-255, but if -i, maps upper to
                    116:                                 * lower case */
                    117: char str[BUFSIZE + 2];
                    118: int nleftover;
                    119: char linetemp[BUFSIZE];
                    120: char *altpat[NALT];            /* alternation component storage */
                    121: int altlen[NALT];
                    122: short altset[NMUSH + 1][256];
                    123: char preamble[200];            /* match prefix (filename, line no.) */
                    124: 
                    125: int fd;
                    126: char *
                    127: strchr(), *strrchr(), *strcpy(), *strncpy(), *strpbrk(), *malloc();
                    128: char *
                    129: grepxlat(), *fold(), *pfile(), *alternate(), *isolate();
                    130: char *gotamatch(), *kanji(), *linesave(), *submatch();
                    131: char **args;
                    132: 
                    133: main(argc, argv)
                    134:        int argc;
                    135:        char *argv[];
                    136: {
                    137:        int c, oflag;
                    138:        int errflag = 0;
                    139: 
                    140:        args = argv;
                    141: 
                    142:        if ((progname = strrchr(argv[0], '/')) != 0)
                    143:                progname++;
                    144:        else
                    145:                progname = argv[0];
                    146:        if (strcmp(progname, "grep") == 0)
                    147:                grepflag++;
                    148:        else if (strcmp(progname, "fgrep") == 0)
                    149:                fgrepflag++;
                    150: 
                    151:        oflag = 0;
                    152:        while ((c = getopt(argc, argv, "bchie:f:lnosvwxy1")) != EOF) {
                    153:                switch (c) {
                    154: 
                    155:                case 'f':
                    156:                        fflag++;
                    157:                        patfile = optarg;
                    158:                        continue;
                    159:                case 'b':
                    160:                case 'v':
                    161:                        egrepold++;     /* boyer-moore of little help here */
                    162:                        continue;
                    163:                case 'c':
                    164:                        cflag++;
                    165:                        continue;
                    166:                case 'e':
                    167:                        eflag++;
                    168:                        pattern = optarg;
                    169:                        continue;
                    170:                case 'h':
                    171:                        hflag++;
                    172:                        continue;
                    173:                case 'o':
                    174:                        oflag++;
                    175:                        continue;
                    176:                case '1':       /* Stop at very first match */
                    177:                        firstflag++;    /* spead freaks only */
                    178:                        continue;
                    179:                case 'i':
                    180:                        iflag++;
                    181:                        continue;
                    182:                case 'l':
                    183:                        lflag++;
                    184:                        continue;
                    185:                case 'n':
                    186:                        nflag++;
                    187:                        continue;
                    188:                case 's':
                    189:                        sflag++;
                    190:                        continue;
                    191:                case 'w':
                    192:                case 'y':
                    193:                        if (!grepflag)
                    194:                                errflag++;
                    195:                        grepold++;
                    196:                        continue;
                    197:                case 'x':       /* needs more work, like -b above */
                    198:                        if (!fgrepflag)
                    199:                                errflag++;
                    200:                        fgrepold++;
                    201:                        continue;
                    202:                case '?':
                    203:                        errflag++;
                    204:                }
                    205:        }
                    206:        if (errflag || ((argc <= optind) && !fflag && !eflag)) {
                    207:                if (grepflag)
                    208: oops("usage: grep [-bchilnosvwy] [-e] pattern [file ...]");
                    209:                else if (fgrepflag)
                    210: oops("usage: fgrep [-bchilnosvx] {-f patfile | [-e] strings} [file ...]");
                    211:                else            /* encourage SVID options, though we provide
                    212:                                 * others */
                    213: oops("usage: egrep [-bchilnosv] {-f patfile | [-e] pattern} [file ...]");
                    214:        }
                    215:        if (fflag)
                    216:                pattern = pfile(patfile);
                    217:        else if (!eflag) {
                    218:                patind = optind;
                    219:                pattern = argv[optind++];
                    220:        }
                    221: 
                    222:        if (!oflag && (argc - optind) <= 1)     /* Filename invisible given < 2 files */
                    223:                hflag++;
                    224:        if (pattern[0] == EOS)
                    225:                kernighan(argv);        /* same as it ever was */
                    226:        /*
                    227:         * 'grep/egrep' merger -- "old" grep is called to handle: tagged
                    228:         * exprs \( \), word matches \< and \>, -w and -y options, char
                    229:         * classes with '-' at end (egrep bug?), and patterns beginning with
                    230:         * an asterisk (don't ask why). otherwise, characters meaningful to
                    231:         * 'egrep' but not to 'grep' are escaped; the entire expr is then
                    232:         * passed to 'egrep'. 
                    233:         */
                    234:        if (grepflag && !grepold) {
                    235:                if (strindex(pattern, "\\(") >= 0 ||
                    236:                    strindex(pattern, "\\<") >= 0 ||
                    237:                    strindex(pattern, "\\>") >= 0 ||
                    238:                    strindex(pattern, "-]") >= 0 ||
                    239:                    pattern[0] == '*')  /* grep bug */
                    240:                        grepold++;
                    241:                else
                    242:                        pattern = grepxlat(pattern);
                    243:        }
                    244:        if (grepold || egrepold || fgrepold)
                    245:                kernighan(argv);
                    246: 
                    247:        if (iflag)
                    248:                strcpy(pattern, fold(pattern));
                    249:        /*
                    250:         * If the pattern is a plain string, just run boyer-moore. If it
                    251:         * consists of meta-free alternatives, run "superimposed" bmg.
                    252:         * Otherwise, find best string, and compile pattern for regexec(). 
                    253:         */
                    254:        if (strpbrk(pattern, META) == NULL) {   /* do boyer-moore only */
                    255:                boyonly++;
                    256:                patboy = pattern;
                    257:        } else {
                    258:                if ((patboy = alternate(pattern)) != NULL)
                    259:                        boyonly++;
                    260:                else {
                    261:                        if ((patboy = isolate(pattern)) == NULL)
                    262:                                kernighan(argv);        /* expr too involved */
                    263: #ifndef NOKANJI
                    264:                        for (c = 0; pattern[c] != EOS; c++)
                    265:                                if (pattern[c] & NONASCII)      /* kanji + meta */
                    266:                                        kernighan(argv);
                    267: #endif
                    268:                        if ((rspencer = regcomp(pattern)) == NULL)
                    269:                                oops("regcomp failure");
                    270:                }
                    271:        }
                    272:        gosper(patboy);         /* "pre-conditioning is wonderful"
                    273:                                 * -- v. strassen */
                    274: 
                    275:        if ((firstfile = optind) >= argc) {
                    276:                /* Grep standard input */
                    277:                if (lflag)      /* We don't know its name! */
                    278:                        exit(1);
                    279:                egsecute((char *) NULL);
                    280:        } else {
                    281:                while (optind < argc) {
                    282:                        egsecute(argv[optind]);
                    283:                        optind++;
                    284:                        if (firstflag && (nsuccess == 1))
                    285:                                break;
                    286:                }
                    287:        }
                    288:        exit((nsuccess == 2) ? 2 : (nsuccess == 0));
                    289: }
                    290: 
                    291: char *
                    292: pfile(pfname)                  /* absorb expression from file */
                    293:        char *pfname;
                    294: {
                    295:        int fd;
                    296:        struct stat patstat;
                    297:        static char *pat;
                    298: 
                    299:        if ((fd = open(pfname, O_RDONLY, 0)) < 0)
                    300:                oops("can't read pattern file");
                    301:        if (fstat(fd, &patstat) != 0)
                    302:                oops("can't stat pattern file");
                    303:        if (patstat.st_size > PATSIZE) {
                    304:                if (fgrepflag) {        /* defer to unix version */
                    305:                        fgrepold++;
                    306:                        return "dummy";
                    307:                } else
                    308:                        oops("pattern file too big");
                    309:        }
                    310:        if ((pat = malloc((unsigned) patstat.st_size + 1)) == NULL)
                    311:                oops("out of memory to read pattern file");
                    312:        if (patstat.st_size != read(fd, pat, (int)patstat.st_size))
                    313:                oops("error reading pattern file");
                    314:        (void) close(fd);
                    315: 
                    316:        pat[patstat.st_size] = EOS;
                    317:        if (pat[patstat.st_size - 1] == NL)     /* NOP for egrep; helps grep */
                    318:                pat[patstat.st_size - 1] = EOS;
                    319: 
                    320:        if (nlcount(pat, &pat[patstat.st_size]) > NALT) {
                    321:                if (fgrepflag)
                    322:                        fgrepold++;     /* "what's it all about, alfie?" */
                    323:                else
                    324:                        egrepold++;
                    325:        }
                    326:        return (pat);
                    327: }
                    328: 
                    329: egsecute(file)
                    330:        char *file;
                    331: {
                    332:        if (file == NULL)
                    333:                fd = 0;
                    334:        else if ((fd = open(file, O_RDONLY, 0)) <= 0) {
                    335:                fprintf(stderr, "%s: can't open %s\n", progname, file);
                    336:                nsuccess = 2;
                    337:                return;
                    338:        }
                    339:        chimaera(file, patboy);
                    340: 
                    341:        if (!boyonly && !flushflag && file != NULL)
                    342:                flushmatches();
                    343:        if (file != NULL)
                    344:                close(fd);
                    345: }
                    346: 
                    347: chimaera(file, pat)            /* "reach out and boyer-moore search someone" */
                    348:        char *file, *pat;       /* -- soon-to-be-popular bumper sticker */
                    349: {
                    350:        register char *k, *strend, *s;
                    351:        register int j, count;
                    352:        register int *deltazero = delta0;
                    353:        int patlen = altmin;
                    354:        char *t;
                    355: 
                    356:        nleftover = boyfound = flushflag = 0;
                    357:        nline = 1L;
                    358:        prevmatch = 0;
                    359:        nmatch = counted = rxcount = 0L;
                    360: 
                    361:        while ((count = read(fd, str + nleftover, BUFSIZE - nleftover)) > 0) {
                    362: 
                    363:                counted += count;
                    364:                strend = linesave(str, count);
                    365: 
                    366:                for (k = str + patlen - 1; k < strend;) {
                    367:                        /*
                    368:                         * for a large class of patterns, upwards of 80% of
                    369:                         * match time is spent on the next line.  we beat
                    370:                         * existing microcode (vax 'matchc') this way. 
                    371:                         */
                    372:                        while ((k += deltazero[*(unsigned char *) k]) < strend);
                    373:                        if (k < (str + LARGE))
                    374:                                break;
                    375:                        k -= LARGE;
                    376: 
                    377:                        if (altflag) {
                    378:                                /*
                    379:                                 * Parallel Boyer-Moore.  Check whether each
                    380:                                 * of the previous <altmin> chars COULD be
                    381:                                 * from one of the alternative strings. 
                    382:                                 */
                    383:                                s = k - 1;
                    384:                                j = altmin;
                    385:                                while (altset[--j][(unsigned char)
                    386:                                             cmap[*(unsigned char *) s--]]);
                    387:                                /*
                    388:                                 * quick test fails. in this life, compare
                    389:                                 * 'em all.  but, a "reverse trie" would
                    390:                                 * attenuate worst case (linear w/delta2?). 
                    391:                                 */
                    392:                                if (--j < 0) {
                    393:                                        count = nalt - 1;
                    394:                                        do {
                    395:                                                s = k;
                    396:                                                j = altlen[count];
                    397:                                                t = altpat[count];
                    398: 
                    399:                                                while
                    400:                                                        (cmap[*(unsigned char *) s--]
                    401:                                                         == t[--j]);
                    402:                                                if (j < 0)
                    403:                                                        break;
                    404:                                        }
                    405:                                        while (count--);
                    406:                                }
                    407:                        } else {
                    408:                                /* One string -- check it */
                    409:                                j = patlen - 1;
                    410:                                s = k - 1;
                    411:                                while (cmap[*(unsigned char *) s--] == pat[--j]);
                    412:                        }
                    413:                        /*
                    414:                         * delta-less shortcut for literati. short shrift for
                    415:                         * genetic engineers? 
                    416:                         */
                    417:                        if (j >= 0) {
                    418:                                k++;    /* no match; restart next char */
                    419:                                continue;
                    420:                        }
                    421:                        k = submatch(file, pat, str, strend, k, count);
                    422:                        if (k == NULL)
                    423:                                return;
                    424:                }
                    425:                if (nflag) {
                    426:                        if (prevmatch)
                    427:                                nline = prevnline + nlcount(prevloc, k);
                    428:                        else
                    429:                                nline = nline + nlcount(str, k);
                    430:                        prevmatch = 0;
                    431:                }
                    432:                strncpy(str, linetemp, nleftover);
                    433:        }
                    434:        if (cflag) {
                    435:                /* Bug from old grep: -c overrides -h.  We fix the bug. */
                    436:                if (!hflag)
                    437:                        printf("%s:", file);
                    438:                printf("%ld\n", nmatch);
                    439:        }
                    440: }
                    441: 
                    442: char *
                    443: linesave(str, count)           /* accumulate partial line at end of buffer */
                    444:        char str[];
                    445:        register int count;
                    446: {
                    447:        register int j;
                    448: 
                    449:        count += nleftover;
                    450:        if (count != BUFSIZE && fd != 0)
                    451:                str[count++] = NL;      /* insurance for broken last line */
                    452:        str[count] = EOS;
                    453:        for (j = count - 1; str[j] != NL && j >= 0;)
                    454:                j--;
                    455:        /*
                    456:         * break up these lines: long line (> BUFSIZE), last line of file, or
                    457:         * short return from read(), as from tee(1) input 
                    458:         */
                    459:        if (j < 0 && (count == (BUFSIZE - nleftover))) {
                    460:                str[count++] = NL;
                    461:                str[count] = EOS;
                    462:                linetemp[0] = EOS;
                    463:                nleftover = 0;
                    464:                return (str + count);
                    465:        } else {
                    466:                nleftover = count - j - 1;
                    467:                strncpy(linetemp, str + j + 1, nleftover);
                    468:                return (str + j);
                    469:        }
                    470: }
                    471: 
                    472: /*
                    473:  * Process partial match. First check for mis-aligned Kanji, then match line
                    474:  * against full compiled r.e. if statistics do not warrant handing off to
                    475:  * standard egrep. 
                    476:  */
                    477: char *
                    478: submatch(file, pat, str, strend, k, altindex)
                    479:        char file[], pat[], str[];
                    480:        register char *strend, *k;
                    481:        int altindex;
                    482: {
                    483:        register char *s;
                    484:        char *t, c;
                    485: 
                    486:        t = k;
                    487:        s = ((altflag) ? k - altlen[altindex] + 1 : k - altmin + 1);
                    488: #ifndef NOKANJI
                    489:        c = ((altflag) ? altpat[altindex][0] : pat[0]);
                    490:        if (c & NONASCII)
                    491:                if ((s = kanji(str, s, k)) == NULL)
                    492:                        return (++k);   /* reject false kanji */
                    493: #endif
                    494:        do;
                    495:        while (*s != NL && --s >= str);
                    496:        k = s + 1;              /* now at line start */
                    497: 
                    498:        if (boyonly)
                    499:                return (gotamatch(file, k));
                    500: 
                    501:        incount = counted - (strend - k);
                    502:        if (boyfound++ == FIRSTFEW)
                    503:                execstrategy(file);
                    504: 
                    505:        s = t;
                    506:        do
                    507:                rxcount++;
                    508:        while (*s++ != NL);
                    509:        *--s = EOS;
                    510:        /*
                    511:         * "quick henry -- the flit" (after theodor geisel) 
                    512:         */
                    513:        if (regexec(rspencer, ((iflag) ? fold(k) : k)) == 1) {
                    514:                *s = NL;
                    515:                if (gotamatch(file, k) == NULL)
                    516:                        return (NULL);
                    517:        }
                    518:        *s = NL;
                    519:        return (s + 1);
                    520: }
                    521: 
                    522: #ifndef NOKANJI
                    523: /*
                    524:  * EUC code disambiguation -- scan backwards to first 7-bit code, while
                    525:  * counting intervening 8-bit codes.  If odd, reject unaligned Kanji pattern. 
                    526:  * SS2/3 checks are for intermixed Japanase Katakana or Kanji2. 
                    527:  */
                    528: char *
                    529: kanji(str, s, k)
                    530:        register char *str, *s, *k;
                    531: {
                    532:        register int j = 0;
                    533: 
                    534:        for (s--; s >= str; s--) {
                    535:                if (*s == SS2 || *s == SS3 || (*s & NONASCII) == 0)
                    536:                        break;
                    537:                j++;
                    538:        }
                    539: #ifndef CHINESE
                    540:        if (*s == SS2)
                    541:                j -= 1;
                    542: #endif  CHINESE
                    543:        return ((j & 01) ? NULL : k);
                    544: }
                    545: #endif
                    546: 
                    547: /*
                    548:  * Compute "Boyer-Moore" delta table -- put skip distance in delta0[c] 
                    549:  */
                    550: gosper(pattern)
                    551:        char *pattern;          /* ... HAKMEM lives ... */
                    552: {
                    553:        register int i, j;
                    554:        unsigned char c;
                    555: 
                    556:        /* Make one-string case look like simple alternatives case */
                    557:        if (!altflag) {
                    558:                nalt = 1;
                    559:                altmin = altlen[0] = strlen(pattern);
                    560:                altpat[0] = pattern;
                    561:        }
                    562:        /* For chars that aren't in any string, skip by string length. */
                    563:        for (j = 0; j < 256; j++) {
                    564:                delta0[j] = altmin;
                    565:                cmap[j] = j;    /* Sneak in initialization of cmap */
                    566:        }
                    567: 
                    568:        /* For chars in a string, skip distance from char to end of string. */
                    569:        /* (If char appears more than once, skip minimum distance.) */
                    570:        for (i = 0; i < nalt; i++)
                    571:                for (j = 0; j < altlen[i] - 1; j++) {
                    572:                        c = altpat[i][j];
                    573:                        delta0[c] = MIN(delta0[c], altlen[i] - j - 1);
                    574:                        if (iflag && islower((int) c))
                    575:                                delta0[toupper((int) c)] = delta0[c];
                    576:                }
                    577: 
                    578:        /* For last char of each string, fall out of search loop. */
                    579:        for (i = 0; i < nalt; i++) {
                    580:                c = altpat[i][altlen[i] - 1];
                    581:                delta0[c] = LARGE;
                    582:                if (iflag && islower((int) c))
                    583:                        delta0[toupper((int) c)] = LARGE;
                    584:        }
                    585:        if (iflag)
                    586:                for (j = 'A'; j <= 'Z'; j++)
                    587:                        cmap[j] = tolower((int) j);
                    588: }
                    589: 
                    590: /*
                    591:  * Print, count, or stop on full match. Result is either the location for
                    592:  * continued search, or NULL to stop. 
                    593:  */
                    594: char *
                    595: gotamatch(file, s)
                    596:        register char *file, *s;
                    597: {
                    598:        char *savematch();
                    599:        int squirrel = 0;       /* nonzero to squirrel away FIRSTFEW matches */
                    600: 
                    601:        nmatch++;
                    602:        nsuccess = 1;
                    603:        if (!boyonly && boyfound <= FIRSTFEW && file != NULL)
                    604:                squirrel = 1;
                    605: 
                    606:        if (sflag)
                    607:                return (NULL);  /* -s usurps all flags (unlike some versions) */
                    608:        if (cflag) {            /* -c overrides -l, we guess */
                    609:                do;
                    610:                while (*s++ != NL);
                    611:        } else if (lflag) {
                    612:                puts(file);
                    613:                return (NULL);
                    614:        } else {
                    615:                if (!hflag)
                    616:                        if (!squirrel)
                    617:                                printf("%s:", file);
                    618:                        else
                    619:                                (void)sprintf(preamble, "%s:", file);
                    620:                if (nflag) {
                    621:                        if (prevmatch)
                    622:                                prevnline = prevnline + nlcount(prevloc, s);
                    623:                        else
                    624:                                prevnline = nline + nlcount(str, s);
                    625:                        prevmatch = 1;
                    626: 
                    627:                        if (!squirrel)
                    628:                                printf("%ld:", prevnline);
                    629:                        else
                    630:                                (void)sprintf(preamble + strlen(preamble),
                    631:                                        "%ld:", prevnline);
                    632:                }
                    633:                if (!squirrel) {
                    634:                        do
                    635:                                putchar(*s);
                    636:                        while (*s++ != NL);
                    637:                } else
                    638:                        s = savematch(s);
                    639: 
                    640:                if (nflag)
                    641:                        prevloc = s - 1;
                    642:        }
                    643:        return ((firstflag && !cflag) ? NULL : s);
                    644: }
                    645: 
                    646: char *
                    647: fold(line)
                    648:        char *line;
                    649: {
                    650:        static char fline[BUFSIZE];
                    651:        register char *s, *t = fline;
                    652: 
                    653:        for (s = line; *s != EOS; s++)
                    654:                *t++ = (isupper((int) *s) ? (char) tolower((int) *s) : *s);
                    655:        *t = EOS;
                    656:        return (fline);
                    657: }
                    658: 
                    659: strindex(s, t)                 /* the easy way, as in K&P, p. 192 */
                    660:        char *s, *t;
                    661: {
                    662:        int i, n;
                    663: 
                    664:        n = strlen(t);
                    665:        for (i = 0; s[i] != '\0'; i++)
                    666:                if (strncmp(s + i, t, n) == 0)
                    667:                        return (i);
                    668:        return (-1);
                    669: }
                    670: 
                    671: char *
                    672: grepxlat(pattern)              /* grep pattern meta conversion */
                    673:        char *pattern;
                    674: {
                    675:        register char *p, *s;
                    676:        static char newpat[BUFSIZE];
                    677: 
                    678:        for (s = newpat, p = pattern; *p != EOS;) {
                    679:                if (*p == '\\') {       /* skip escapes ... */
                    680:                        *s++ = *p++;
                    681:                        if (*p)
                    682:                                *s++ = *p++;
                    683:                } else if (*p == '[') { /* ... and char classes */
                    684:                        while (*p != EOS && *p != ']')
                    685:                                *s++ = *p++;
                    686:                } else if (strchr("+?|()", *p) != NULL) {
                    687:                        *s++ = '\\';    /* insert protection */
                    688:                        *s++ = *p++;
                    689:                } else
                    690:                        *s++ = *p++;
                    691:        }
                    692:        *s = EOS;
                    693:        grepflag = ((patind) ? 0 : 1);
                    694:        return (newpat);
                    695: }
                    696: 
                    697: /*
                    698:  * Test for simple alternation.  Result is NULL if it's not so simple, or is
                    699:  * a pointer to the first string if it is. Warning:  sscanf size is a
                    700:  * fixpoint, beyond which the speedup linearity starts to break down.  In the
                    701:  * wake of the elegant aho/corrasick "trie"-based fgrep, generalizing
                    702:  * altpat[] to arbitrary size is not useful. 
                    703:  */
                    704: char *
                    705: alternate(regexpr)
                    706:        char *regexpr;
                    707: {
                    708:        register int i, j;
                    709:        register char *start, *stop;
                    710:        unsigned char c;
                    711: 
                    712:        if (fgrepflag && strchr(regexpr, '|'))
                    713:                        return (NULL);
                    714: 
                    715:        /*
                    716:         * break pattern up into altpat array; delimit on newline, bar,
                    717:         * or EOS.  We know we won't overflow, we've already checked the
                    718:         * number of patterns we're going to find against NALT.
                    719:         * Also, set length of pattern and find minimum pattern length.
                    720:         */
                    721:        nalt = 0;
                    722:        altmin = NMUSH;
                    723:        for (start = stop = regexpr;; ++stop)
                    724:                if (!*stop || *stop == '|' || *stop == NL) {
                    725:                        altlen[nalt] = j = stop - start;
                    726:                        if (j < altmin)
                    727:                                altmin = j;
                    728:                        if (!(altpat[nalt] = malloc((u_int)(j + 1))))
                    729:                                oops("out of memory");
                    730:                        bcopy(start, altpat[nalt], j);
                    731:                        altpat[nalt][j] = EOS;
                    732:                        ++nalt;
                    733:                        if (!*stop)
                    734:                                break;
                    735:                        if (nalt == NALT)
                    736:                                return(NULL);
                    737:                        if (*stop == NL)
                    738:                                *stop = '|';
                    739:                        start = stop + 1;
                    740:                }
                    741:        if (!fgrepflag) {
                    742:                if (strchr(regexpr, '|') == NULL || regexpr[0] == '|')
                    743:                        return (NULL);
                    744:                if (strpbrk(regexpr, "^$.[]()?+*\\") != NULL
                    745:                    || strindex(regexpr, "||") >= 0)
                    746:                        return (NULL);
                    747:        }
                    748: 
                    749:        if (nalt > 1) {         /* build superimposed "pre-match" sets per
                    750:                                 * char */
                    751:                altflag++;
                    752:                for (j = 0; j < nalt; j++)
                    753:                        for (i = 0; i < altmin; i++) {
                    754:                                c = altpat[j][altlen[j] - altmin + i];
                    755:                                altset[i + 1][c] = 1;   /* offset for sentinel */
                    756:                        }
                    757:        }
                    758:        return (altpat[0]);
                    759: }
                    760: 
                    761: /*
                    762:  * Grapple with the dfa (std egrep) vs. ndfa (regexp) tradeoff. Criteria to
                    763:  * determine whether to use dfa-based egrep:  We do FIRSTFEW matches with
                    764:  * regexec().  If Boyer-Moore up to now matched more than PUNTPERCENT
                    765:  * of the input, the r.e. is likely to be underspecified, so do old *grep,
                    766:  * which is faster on complex patterns than regexp().  At FIRSTFEW,
                    767:  * dump the saved matches collected by savematch(). They are saved
                    768:  * so that a "PUNT" can "rewind" to ignore them.  Stdin is problematic,
                    769:  * since it's hard to rewind. 
                    770:  */
                    771: 
                    772: execstrategy(file)
                    773:        char *file;
                    774: {
                    775:        int pctmatch;
                    776: 
                    777:        pctmatch = (100 * rxcount) / incount;
                    778:        if (pctmatch > PUNTPERCENT && file != NULL)
                    779:                kernighan(args);
                    780:        if (file != NULL)
                    781:                flushmatches();
                    782: }
                    783: 
                    784: nlcount(bstart, bstop)         /* flail interval to totalize newlines. */
                    785:        char *bstart, *bstop;
                    786: {
                    787:        register char *s = bstart;
                    788:        register char *t = bstop;
                    789:        register int count = 0;
                    790: 
                    791:        do {                    /* loop unroll for older architectures */
                    792:                if (*t == NL)   /* ... ask ames!jaw for sample code */
                    793:                        count++;
                    794:        } while (t-- > s);
                    795: 
                    796:        return (count);
                    797: }
                    798: 
                    799: char *
                    800: isolate(regexpr)               /* isolate longest metacharacter-free string */
                    801:        char *regexpr;
                    802: {
                    803:        char *dummyexpr;
                    804: 
                    805:        /*
                    806:         * We add (.)* because Henry's regcomp only figures regmust if it
                    807:         * sees a leading * pattern.  Foo! 
                    808:         */
                    809:        dummyexpr = malloc((unsigned) strlen(regexpr) + 5);
                    810:        (void)sprintf(dummyexpr, "(.)*%s", regexpr);
                    811:        if ((rspencer = regcomp(dummyexpr)) == NULL)
                    812:                kernighan(args);
                    813:        return (rspencer->regmust);
                    814: }
                    815: 
                    816: char *matches[FIRSTFEW];
                    817: static int mcount = 0;
                    818: 
                    819: char *
                    820: savematch(s)                   /* horde matches during statistics gathering */
                    821:        register char *s;
                    822: {
                    823:        char *p;
                    824:        char *start = s;
                    825:        int msize = 0;
                    826:        int psize = strlen(preamble);
                    827: 
                    828:        while (*s++ != NL)
                    829:                msize++;
                    830:        *--s = EOS;
                    831: 
                    832:        p = malloc((unsigned) msize + 1 + psize);
                    833:        strcpy(p, preamble);
                    834:        strcpy(p + psize, start);
                    835:        matches[mcount++] = p;
                    836: 
                    837:        preamble[0] = 0;
                    838:        *s = NL;
                    839:        return (s);
                    840: }
                    841: 
                    842: flushmatches()
                    843: {
                    844:        int n;
                    845: 
                    846:        flushflag = 1;
                    847:        for (n = 0; n < mcount; n++)
                    848:                printf("%s\n", matches[n]);
                    849:        mcount = 0;
                    850: }
                    851: 
                    852: oops(message)
                    853:        char *message;
                    854: {
                    855:        fprintf(stderr, "%s: %s\n", progname, message);
                    856:        exit(2);
                    857: }
                    858: 
                    859: kernighan(args)                        /* "let others do the hard part ..." */
                    860:        char *args[];
                    861: {
                    862:        /*
                    863:         * We may have already run grep on some of the files; remove them
                    864:         * from the arg list we pass on.  Note that we can't delete them
                    865:         * totally because the number of file names affects the output
                    866:         * (automatic -h). 
                    867:         */
                    868:        /* better would be fork/exec per punted file -- jaw */
                    869: 
                    870:        while (firstfile && optind > firstfile)
                    871:                args[firstfile++] = _PATH_DEVNULL;
                    872:        if (patind)
                    873:                args[patind] = pattern;
                    874:        (void) fflush(stdout);
                    875: 
                    876:        if (grepflag)
                    877:                execvp(_PATH_GREPSTD, args), oops("can't exec old 'grep'");
                    878:        else if (fgrepflag)
                    879:                execvp(_PATH_FGREPSTD, args), oops("can't exec old 'fgrep'");
                    880:        else
                    881:                execvp(_PATH_EGREPSTD, args), oops("can't exec old 'egrep'");
                    882: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.