|
|
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.