Annotation of coherent/d/bin/cgrep.c, revision 1.1.1.1

1.1       root        1: /*
                      2:  * To compile use
                      3:  * cc cgrep.c -lmisc
                      4:  * This uses libmisc.a and the misc.h header from libmisc.a.Z
                      5:  */
                      6: 
                      7: /*
                      8:  * cgrep is egrep for c source programs.
                      9:  *     cgrep [-r new] [-clnsA] [pattern] [file ...]
                     10:  *
                     11:  * cgrep checks all c identifiers (cgrep considers if, etc to be
                     12:  * identifiers) against an egrep type pattern for a full match.
                     13:  *
                     14:  *     cgrep tmp *.c
                     15:  * will find tmp as an identifier, but not tmpname, or tmp in a string or
                     16:  * comment.
                     17:  *     cgrep "x|abc|d" *.c
                     18:  * will find x, ab, or d. Note these are egrep style patterns with a
                     19:  * surrounding ^( )$ Thus "reg*" will not match register but "reg.*"
                     20:  * will.
                     21:  *
                     22:  * cgrep accumulates names with included . and -> for testing against the
                     23:  * egrep pattern. Thus you can look for ptr->val with
                     24:  *     cgrep "ptr->val" x.c
                     25:  * This will find ptr->val even if it contains spaces, comments or is
                     26:  * spread across lines. If it is spread across lines it will be reported
                     27:  * on the line containing the last token unless the -A option is used
                     28:  * in which case it will be reported on the line containing the first
                     29:  * token.
                     30:  *
                     31:  * For structure.member use
                     32:  *     cgrep "structure\.member" x.c
                     33:  * because . has the egrep meaning any character. Do not include spaces
                     34:  * in any pattern. Only identifiers and . or -> between identifiers are
                     35:  * included in the tokens checked for pattern match.
                     36:  *
                     37:  * Where no files are given cgrep will read stdin. Options -lA require
                     38:  * a file name. Where stdin is used with option -r a new file is written
                     39:  * out stdout.
                     40:  *
                     41:  * Options.
                     42:  *
                     43:  * -l lists the files with hits not the lines.
                     44:  *
                     45:  * -n puts a line number on found lines.
                     46:  *
                     47:  * -s List all strings. This form takes no pattern.
                     48:  *
                     49:  * -c List all comments. This form takes no pattern.
                     50:  *
                     51:  * -A builds a tmp file and calls 'me' to process the file with the tmp file
                     52:  *    as an "error" list like the -A option of cc. Each line of this list
                     53:  *    shows the found pattern and where multiple patterns are found on a
                     54:  *    line there are multiple lines, just like cc -A showing multiple
                     55:  *    errors on a line. This is to allow emacs scripts to make systematic
                     56:  *    changes. Where a patttern is split across multiple lines -A causes
                     57:  *    it to be reported on the line with the first token, also to
                     58:  *    simplify emacs scripts.
                     59:  *
                     60:  *    If cgrep -A is used to process a number of files it will search each
                     61:  *    file for patttern matches and call 'me' only if the file has a hit. To
                     62:  *    stop the process exit 'me' with <ctl-u><ctl-x><ctl-c>. See the new emacs
                     63:  *    initialization macro feature for a way to process these files
                     64:  *    automatically.
                     65:  *
                     66:  * -r Replaces all occurances of the pattern with "new". This form only matches
                     67:  *    simple tokens, not things like "ptr->val". -r is incompatible with all
                     68:  *    other options.
                     69:  */
                     70: #include <ctype.h>
                     71: #include <stdio.h>
                     72: #include <misc.h>
                     73: 
                     74: /*
                     75:  * Cgrep never runs out of room on lines or buffers until malloc fails
                     76:  * these are buffer expanders.
                     77:  */
                     78: #define TROOM(buf, has, needs) while ((needs) >= has) \
                     79:  if (NULL == (buf = realloc(buf, sizeof(*buf) * (has += 10)))) \
                     80:   fatal(outSpace)
                     81: 
                     82: #define ROOM(buf, has, needs)  while ((needs) >= has) \
                     83:  if (NULL == (buf = realloc(buf, has += 512))) \
                     84:   fatal(outSpace)
                     85: 
                     86: extern char *realloc();
                     87: static char outSpace[] = "cgrep: out of space";
                     88: 
                     89: struct token { /* collected token array */
                     90:        int start;      /* token index on buff */
                     91:        int atline;     /* line number where token spotted */
                     92: };
                     93: 
                     94: enum fstate {  /* lexical processing state */
                     95:        start,
                     96:        slash,          /* slash encountered in normal state */
                     97:        comment,        /* in comment */
                     98:        star,           /* * in comment */
                     99:        bsl,            /* back slash */
                    100:        dquote,         /* double quote */
                    101:        squote,         /* single quote */
                    102:        token,          /* c word */
                    103:        minus           /* - maybe -> */
                    104: };
                    105: 
                    106: enum wstate { /* word processing states */
                    107:        word,   /* some c identifier */
                    108:        dot,    /* . or -> */
                    109:        other   /* anything else */
                    110: };
                    111: 
                    112: /* cgrep has no fixed size arrays only growable buffers */
                    113: static char *buff = NULL;      /* accumulate stuff */
                    114: static int buffLen;            /* buffer length */
                    115: 
                    116: static struct token *tokens = NULL; /* token array */
                    117: static int tokenLen;           /* size of token array */
                    118: 
                    119: static char *line;             /* expandable input line */
                    120: static int  lineLen;           /* current length of input line */
                    121: 
                    122: static char lswitch;           /* list files found */
                    123: static char aswitch;           /* call emacs with line list */
                    124: static char nswitch;           /* print line number */
                    125: static char sswitch;           /* print all strings */
                    126: static char cswitch;           /* print all comments */
                    127: static char rswitch;           /* replace found pattern */
                    128: 
                    129: static regexp *pat;            /* a compiled regular expression */
                    130: 
                    131: static char *newstr;           /* The new string with rswitch */
                    132: 
                    133: static char *filen = NULL;     /* the file currently being processed */
                    134: static char *tname = NULL;     /* temp file name */
                    135: static FILE *tfp;              /* tmp file pointer */
                    136: 
                    137: static int lineno;             /* current line number */
                    138: static int marked;             /* 1 if pattern found on line. */
                    139: 
                    140: /*
                    141:  * Character types table
                    142:  * for the ASCII character set modified to see _ as alpha.
                    143:  * _ctype[0] is for EOF, the rest if indexed
                    144:  * by the ascii values of the characters.
                    145:  */
                    146: static unsigned char _ctype[] = {
                    147:        0,      /* EOF */
                    148:        _C, _C, _C, _C, _C, _C, _C, _C,
                    149:        _C, _S|_C, _S|_C, _S|_C, _S|_C, _S|_C, _C, _C,
                    150:        _C, _C, _C, _C, _C, _C, _C, _C,
                    151:        _C, _C, _C, _C, _C, _C, _C, _C,
                    152:        _S|_X, _P, _P, _P, _P, _P, _P, _P,
                    153:        _P, _P, _P, _P, _P, _P, _P, _P,
                    154:        _D, _D, _D, _D, _D, _D, _D, _D,
                    155:        _D, _D, _P, _P, _P, _P, _P, _P,
                    156:        _P, _U, _U, _U, _U, _U, _U, _U,
                    157:        _U, _U, _U, _U, _U, _U, _U, _U,
                    158:        _U, _U, _U, _U, _U, _U, _U, _U,
                    159:        _U, _U, _U, _P, _P, _P, _P, _L,
                    160:        _P, _L, _L, _L, _L, _L, _L, _L,
                    161:        _L, _L, _L, _L, _L, _L, _L, _L,
                    162:        _L, _L, _L, _L, _L, _L, _L, _L,
                    163:        _L, _L, _L, _P, _P, _P, _P, _C,
                    164:        _C, _C, _C, _C, _C, _C, _C, _C,
                    165:        _C, _C, _C, _C, _C, _C, _C, _C,
                    166:        _C, _C, _C, _C, _C, _C, _C, _C,
                    167:        _C, _C, _C, _C, _C, _C, _C, _C,
                    168:        _C, _C, _C, _C, _C, _C, _C, _C,
                    169:        _C, _C, _C, _C, _C, _C, _C, _C,
                    170:        _C, _C, _C, _C, _C, _C, _C, _C,
                    171:        _C, _C, _C, _C, _C, _C, _C, _C,
                    172:        _C, _C, _C, _C, _C, _C, _C, _C,
                    173:        _C, _C, _C, _C, _C, _C, _C, _C,
                    174:        _C, _C, _C, _C, _C, _C, _C, _C,
                    175:        _C, _C, _C, _C, _C, _C, _C, _C,
                    176:        _C, _C, _C, _C, _C, _C, _C, _C,
                    177:        _C, _C, _C, _C, _C, _C, _C, _C,
                    178:        _C, _C, _C, _C, _C, _C, _C, _C,
                    179:        _C, _C, _C, _C, _C, _C, _C, _C
                    180: };
                    181: 
                    182: /*
                    183:  * Report errors for public domain regexp package.
                    184:  */
                    185: void
                    186: regerror(s)
                    187: char *s;
                    188: {
                    189:        fatal("cgrep: pattern error %s\n", s);
                    190: }
                    191: 
                    192: /*
                    193:  * Pattern found with -A mode. It is assumed that users of
                    194:  * this mode will want to know about all hits on a line.
                    195:  */
                    196: static void
                    197: emacsLine(found, atline)
                    198: char *found;
                    199: {
                    200:        extern char *tempnam();
                    201: 
                    202:        /* if tmp file not opened open it. */
                    203:        if ((NULL == tname) &&
                    204:            ((NULL == (tname = tempnam(NULL, "cgr"))) ||
                    205:              (NULL == (tfp = fopen(tname, "w")))))
                    206:                fatal("cgrep: Cannot open tmp file");
                    207:        fprintf(tfp, "%d: %s: found '%s'\n", atline, filen, found);
                    208: }
                    209: 
                    210: /*
                    211:  * When we get a word, dot, arrow or other we come here.
                    212:  *
                    213:  * cgrep accumulates things like ptr->memb.x in buff.
                    214:  * as these are accumulated it is nessisary to match buff
                    215:  * against the regular expression one slice at a time
                    216:  * that is when the x in ptr->memb.x was found cgrep would check
                    217:  * ptr->memb.x then memb.x then x
                    218:  * against the pattern. Thus memb.x matches ptr->memb.x
                    219:  */
                    220: static void
                    221: gota(got, what)
                    222: register enum wstate got;      /* what we have */
                    223: char *what;            /* the string we have or NULL */
                    224: {
                    225:        static int tokenCt;         /* number of tokens */
                    226:        static enum wstate state;   /* current word processing state */
                    227:        static int wlen, blen;      /* strlen(what) and strlen(buff) + 1 */
                    228:        int i;
                    229: 
                    230:        if (sswitch || cswitch)
                    231:                return;
                    232: 
                    233:        if (rswitch) {  /* replace mode works on tokens only */
                    234:                marked = (word == got) && regexec(pat, what);
                    235:                return;
                    236:        }
                    237: 
                    238:        switch (got) {
                    239:        case word:
                    240:                wlen = strlen(what);
                    241:                switch (state) {
                    242:                case other:
                    243:                case word:
                    244:                        /* store start and line number of token */
                    245:                        tokenCt = 1;
                    246:                        TROOM(tokens, tokenLen, tokenCt);
                    247:                        tokens[0].start = 0;
                    248:                        tokens[0].atline = lineno;
                    249: 
                    250:                        /* store token */
                    251:                        blen = wlen;
                    252:                        ROOM(buff, buffLen, blen);
                    253:                        strcpy(buff, what);
                    254:                        break;
                    255:                case dot:
                    256:                        /* store start and line number of token */
                    257:                        TROOM(tokens, tokenLen, tokenCt);
                    258:                        tokens[tokenCt].start = blen;
                    259:                        tokens[tokenCt++].atline = lineno;
                    260: 
                    261:                        /* store token */
                    262:                        blen += wlen;
                    263:                        ROOM(buff, buffLen, blen);
                    264:                        strcat(buff, what);
                    265:                }
                    266: 
                    267:                /* Check the accumulated token for matches */
                    268:                for (i = 0; i < tokenCt; i++) {
                    269:                        char *p;
                    270: 
                    271:                        if (regexec(pat, p = buff + tokens[i].start)) {
                    272:                                if (aswitch)
                    273:                                        emacsLine(p, tokens[i].atline);
                    274:                                else {
                    275:                                        marked = 1;
                    276:                                        break;
                    277:                                }
                    278:                        }
                    279:                }
                    280: 
                    281:                state = got;
                    282:                break;
                    283: 
                    284:        case dot:
                    285:                if (word == state) {
                    286:                        blen += strlen(what);
                    287:                        ROOM(buff, buffLen, blen);
                    288:                        strcat(buff, what);
                    289:                        state = got;
                    290:                        break;
                    291:                }
                    292:        case other:
                    293:                state = other;
                    294:        }
                    295: }
                    296: 
                    297: /*
                    298:  * call emacs with file and tmpfile.
                    299:  */
                    300: static void
                    301: callEmacs()
                    302: {
                    303: #ifdef GEMDOS
                    304: #include <path.h>
                    305:        extern  char *path(), *getenv();
                    306:        extern char **environ;
                    307:        static char* cmda[5] = { NULL, "-e", NULL, NULL, NULL };
                    308: #endif
                    309:        int quit;
                    310: 
                    311:        fclose(tfp);
                    312: #ifdef MSDOS
                    313:        sprintf(line, "-e %s %s", tname, filen);
                    314:        if (0x7f == (quit = execall("me", line)))
                    315: #endif
                    316: #ifdef COHERENT
                    317:        sprintf(line, "me -e %s %s ", tname, filen);
                    318:        if (0x7f == (quit = system(line)))
                    319: #endif
                    320: #ifdef GEMDOS
                    321:        cmda[2] = tname;
                    322:        cmda[3] = filen;
                    323:        if ((NULL == cmda[0]) &&
                    324:           (NULL == (cmda[0] = path(getenv("PATH"), "me.tos", 1))))
                    325:                fatal("cgrep: Cannot locate me.tos\n");
                    326:        else if ((quit = execve(cmda[0], cmda, environ)) < 0)
                    327: #endif
                    328:                fatal("cgrep: cannot execute 'me'");
                    329:        unlink(tname);
                    330:        free(tname);
                    331:        tname = NULL;
                    332:        if (quit)
                    333:                exit(0);
                    334: }
                    335: 
                    336: /*
                    337:  * print a hit for options -s or -c.
                    338:  */
                    339: static void
                    340: printx(s)
                    341: char *s;
                    342: {
                    343:        if (aswitch)
                    344:                emacsLine(s, lineno);
                    345:        else {
                    346:                if (NULL != filen)
                    347:                        printf("%s: ", filen);
                    348:                if (nswitch)
                    349:                        printf("%4d: ", lineno);
                    350:                printf("%s\n", s);
                    351:        }
                    352: }
                    353: 
                    354: /*
                    355:  * Lexically process a file.
                    356:  */
                    357: static void
                    358: lex()
                    359: {
                    360:        int  c, i;
                    361:        enum fstate state, pstate;
                    362:        char *w, changed;
                    363:        FILE *ifp, *tfp;
                    364: 
                    365:        if (NULL == filen)
                    366:                ifp = stdin;
                    367:        else if (NULL == (ifp = fopen(filen, "r"))) {
                    368:                fprintf(stderr, "cgrep: warning cannot open %s\n", filen);
                    369:                return;
                    370:        }
                    371: 
                    372:        if (rswitch) {
                    373:                changed = 0;    /* no changes so far */
                    374: 
                    375:                if (NULL == filen)
                    376:                        tfp = stdout;
                    377:                else if ((NULL == (tname = tempnam(NULL, "cse"))) ||
                    378:                         (NULL == (tfp = fopen(tname, "w"))))
                    379:                        fatal("csed: Cannot open tmp file");
                    380:        }
                    381: 
                    382:        lineno = 1;
                    383:        i = marked = 0;
                    384:        gota(other, NULL);      /* initialize word machine */
                    385:        for (state = start; ; ) {
                    386:                line[i] = '\0';
                    387:                c = fgetc(ifp);
                    388: 
                    389:                switch (state) {
                    390:                case minus:
                    391:                        if ('>' == c) {
                    392:                                gota(dot, "->");
                    393:                                state = start;
                    394:                                break;
                    395:                        }
                    396:                        goto isstart;
                    397:                case token:
                    398:                        if (isalnum(c))
                    399:                                break;
                    400:                        gota(word, w);
                    401: 
                    402:                        /* we have a word to replace */
                    403:                        if (rswitch && marked) {
                    404:                                i += strlen(newstr) - strlen(w);
                    405:                                ROOM(line, lineLen, i);
                    406:                                strcpy(w, newstr);
                    407:                                changed = 1;
                    408:                        }
                    409: isstart:               state = start;
                    410:                case start:
                    411:                        switch (c) {
                    412:                        case '.':
                    413:                                gota(dot, ".");
                    414:                                break;
                    415:                        case '-':
                    416:                                state = minus;
                    417:                                break;
                    418:                        case '/':
                    419:                                state = slash;
                    420:                                break;
                    421:                        case '\\':
                    422:                                pstate = state;
                    423:                                state = bsl;
                    424:                                break;
                    425:                        case '"':
                    426:                                w = line + i;
                    427:                                state = dquote;
                    428:                                break;
                    429:                        case '\'':
                    430:                                state = squote;
                    431:                                break;
                    432:                        default:
                    433:                                if (isalpha(c)) {
                    434:                                        w = line + i;
                    435:                                        state = token;
                    436:                                }
                    437:                                else if (!isspace(c))
                    438:                                        gota(other, NULL);
                    439:                        }
                    440:                        break;
                    441:                case slash:
                    442:                        if ('*' != c)
                    443:                                goto isstart;
                    444:                        w = line + i + 1;
                    445:                        state = comment;
                    446:                        break;
                    447:                case star:
                    448:                        if ('/' == c) {
                    449:                                if (cswitch) { /* report comment */
                    450:                                        line[i - 1] = '\0';
                    451:                                        printx(w);
                    452:                                        line[i - 1] = '*';
                    453:                                }
                    454:                                state = start;
                    455:                                break;
                    456:                        }
                    457:                        state = comment;
                    458:                case comment:
                    459:                        if ('*' == c)
                    460:                                state = star;
                    461:                        break;
                    462:                case bsl:
                    463:                        state = pstate;
                    464:                        break;
                    465:                case dquote:
                    466:                        switch (c) {
                    467:                        case '"':
                    468:                        case '\n':
                    469:                                state = start;
                    470:                                if (sswitch)
                    471:                                        printx(w + 1);
                    472:                                else
                    473:                                        gota(other, NULL);
                    474:                                break;
                    475:                        case '\\':
                    476:                                pstate = state;
                    477:                                state  = bsl;
                    478:                                break;
                    479:                        }
                    480:                        break;
                    481:                case squote:
                    482:                        switch (c) {
                    483:                        case '\'':
                    484:                        case '\n':
                    485:                                gota(other, NULL);
                    486:                                state = start;
                    487:                                break;
                    488:                        case '\\':
                    489:                                pstate = state;
                    490:                                state  = bsl;
                    491:                                break;
                    492:                        }
                    493:                        break;
                    494:                }
                    495:                if (('\n' != c) && (EOF != c)) {
                    496:                        line[i++] = c;
                    497:                        ROOM(line, lineLen, i);
                    498:                }
                    499:                else {  /* end of line */
                    500:                        if (rswitch) {
                    501:                                if (EOF == c) {
                    502:                                        if (!i) /* null line */
                    503:                                                break;
                    504:                                }
                    505:                                else
                    506:                                        line[i++] = c;
                    507:                                ROOM(line, lineLen, i);
                    508:                                line[i] = 0;
                    509:                                fputs(line, tfp);
                    510:                                marked = 0;
                    511:                        }
                    512: 
                    513:                        if (cswitch && (comment == state)) {
                    514:                                printx(w);
                    515:                                w = line;
                    516:                        }
                    517: 
                    518:                        if (marked) {
                    519:                                marked = 0;                     
                    520:                                if (lswitch) {
                    521:                                        printf("%s\n", filen);
                    522:                                        break;
                    523:                                }
                    524:                                printx(line);
                    525:                        }
                    526: 
                    527:                        lineno++;
                    528:                        i = 0;
                    529:                        if (EOF == c)
                    530:                                break;
                    531:                }
                    532:        }
                    533: 
                    534:        fclose(ifp);
                    535: 
                    536:        if (rswitch) {
                    537:                fclose(tfp);
                    538: 
                    539:                if (NULL == filen)
                    540:                        ; /* do nothing file is already out */
                    541:                else if (changed) {
                    542:                        unlink(filen);
                    543:                        sprintf(line, "mv %s %s", tname, filen);
                    544:                        system(line);
                    545:                }
                    546:                else
                    547:                        unlink(tname);
                    548:        }
                    549: 
                    550:        if (aswitch && (NULL != tname)) /* tmp file opened for -A option */
                    551:                callEmacs();
                    552: }
                    553: 
                    554: main(argc, argv)
                    555: int argc;
                    556: register char **argv;
                    557: {
                    558:        register char c;
                    559:        int errsw = 0;
                    560:        static char msg[] = "cgrep [-r newStr] [-clnsA] [pattern] filename ...";
                    561:        char *p, *q;
                    562:        extern int optind;
                    563:        extern char *optarg;
                    564: 
                    565:        if (1 == argc)
                    566:                usage(msg);
                    567: 
                    568:        while (EOF != (c = getopt(argc, argv, "cslnA?r:V"))) {
                    569:                switch (c) {
                    570:                case 'V':
                    571:                        printf("cgrep version 1.1\n");
                    572:                        exit(0);
                    573:                case 'c':
                    574:                        cswitch = 1;    /* comments only */
                    575:                        break;
                    576:                case 's':
                    577:                        sswitch = 1;    /* strings only */
                    578:                        break;
                    579:                case 'l':
                    580:                        lswitch = 1;    /* print filenames only */
                    581:                        break;
                    582:                case 'n':
                    583:                        nswitch = 1;    /* print line numbers */
                    584:                        break;
                    585:                case 'A':
                    586:                        aswitch = 1;    /* interact with emacs */
                    587:                        break;
                    588:                case 'r':
                    589:                        rswitch = 1;    /* replace hits */
                    590:                        newstr = optarg;
                    591:                        break;
                    592:                default:
                    593:                        errsw = 1;
                    594:                }
                    595:        }
                    596: 
                    597:        /* check unknown switches and rswitch goes with no other switches */
                    598:        if (errsw || 
                    599:            (rswitch && (aswitch | nswitch | lswitch | cswitch | sswitch)))
                    600:                usage(msg);
                    601: 
                    602:        if (!sswitch && !cswitch) {     /* process pattern */
                    603:                if (optind == argc)             /* no pattern */
                    604:                        usage(msg);
                    605: 
                    606:                /* inclose pattern in ^(  )$ to force full match */
                    607:                p = alloc(5 + strlen(q = argv[optind++]));
                    608:                sprintf(p, "^(%s)$", q);
                    609:                if (NULL == (pat = regcomp(p)))
                    610:                        fatal("cgrep: Illegal pattern\n");
                    611:        }
                    612: 
                    613:        ROOM(line, lineLen, 1); /* get input line started */
                    614: 
                    615:        if (optind == argc) {
                    616:                if (aswitch | lswitch)
                    617:                        fatal("cgrep: -A and -l require a filename");
                    618:                lex();
                    619:        }
                    620:        else {
                    621:                while (optind < argc) {
                    622:                        filen = argv[optind++];
                    623:                        lex();
                    624:                }
                    625:        }
                    626:        exit(0);
                    627: }

unix.superglobalmegacorp.com

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