1: /* Tags file maker to go with GNUmacs 2: Copyright (C) 1984 Richard M. Stallman and Ken Arnold 3: 4: This program is distributed in the hope that it will be useful, 5: but without any warranty. No author or distributor 6: accepts responsibility to anyone for the consequences of using it 7: or for whether it serves any particular purpose or works at all, 8: unless he says so in writing. 9: 10: Permission is granted to anyone to distribute verbatim copies 11: of this program's source code as received, in any medium, provided that 12: the copyright notice, the nonwarraty notice above 13: and this permission notice are preserved, 14: and that the distributor grants the recipient all rights 15: for further redistribution as permitted by this notice, 16: and informs him of these rights. 17: 18: Permission is granted to distribute modified versions of this 19: program's source code, or of portions of it, under the above 20: conditions, plus the conditions that all changed files carry 21: prominent notices stating who last changed them and that the 22: derived material, including anything packaged together with it and 23: conceptually functioning as a modification of it rather than an 24: application of it, is in its entirety subject to a permission 25: notice identical to this one. 26: 27: Permission is granted to distribute this program (verbatim or 28: as modified) in compiled or executable form, provided verbatim 29: redistribution is permitted as stated above for source code, and 30: A. it is accompanied by the corresponding machine-readable 31: source code, under the above conditions, or 32: B. it is accompanied by a written offer, with no time limit, 33: to distribute the corresponding machine-readable source code, 34: under the above conditions, to any one, in return for reimbursement 35: of the cost of distribution. Verbatim redistribution of the 36: written offer must be permitted. Or, 37: C. it is distributed by someone who received only the 38: compiled or executable form, and is accompanied by a copy of the 39: written offer of source code which he received along with it. 40: 41: Permission is granted to distribute this program (verbatim or as modified) 42: in executable form as part of a larger system provided that the source 43: code for this program, including any modifications used, 44: is also distributed or offered as stated in the preceding paragraph. 45: 46: In other words, you are welcome to use, share and improve this program. 47: You are forbidden to forbid anyone else to use, share and improve 48: what you give them. Help stamp out software-hoarding! */ 49: 50: #include <stdio.h> 51: #include <ctype.h> 52: 53: /* Define the symbol ETAGS to make the program "etags", 54: which makes emacs-style tag tables by default. 55: Define CTAGS to make the program "ctags" compatible with the usual one. 56: Default is ETAGS. */ 57: 58: #ifndef CTAGS 59: #define ETAGS 1 60: #endif 61: 62: #define reg register 63: #define logical char 64: 65: #define TRUE (1) 66: #define FALSE (0) 67: 68: #define iswhite(arg) (_wht[arg]) /* T if char is white */ 69: #define begtoken(arg) (_btk[arg]) /* T if char can start token */ 70: #define intoken(arg) (_itk[arg]) /* T if char can be in token */ 71: #define endtoken(arg) (_etk[arg]) /* T if char ends tokens */ 72: #define isgood(arg) (_gd[arg]) /* T if char can be after ')' */ 73: 74: #define max(I1,I2) (I1 > I2 ? I1 : I2) 75: 76: struct nd_st { /* sorting structure */ 77: char *entry; /* function or type name */ 78: char *file; /* file name */ 79: logical f; /* use pattern or line no */ 80: int lno; /* line number tag is on */ 81: long cno; /* character number line starts on */ 82: char *pat; /* search pattern */ 83: logical been_warned; /* set if noticed dup */ 84: struct nd_st *left,*right; /* left and right sons */ 85: }; 86: 87: long ftell(); 88: typedef struct nd_st NODE; 89: 90: int number; /* tokens found so far on line starting with # (including #) */ 91: logical gotone, /* found a func already on line */ 92: /* boolean "func" (see init) */ 93: _wht[0177],_etk[0177],_itk[0177],_btk[0177],_gd[0177]; 94: 95: /* typedefs are recognized using a simple finite automata, 96: * tydef is its state variable. 97: */ 98: typedef enum {none, begin, middle, end } TYST; 99: 100: TYST tydef = none; 101: 102: char searchar = '/'; /* use /.../ searches */ 103: 104: int lineno; /* line number of current line */ 105: long charno; /* current character number */ 106: long linecharno; /* character number of start of line */ 107: 108: char *curfile, /* current input file name */ 109: *outfile= "tags", /* output file */ 110: *white = " \f\t\n", /* white chars */ 111: *endtk = " \t\n\"'#()[]{}=-+%*/&|^~!<>;,.:?", 112: /* token ending chars */ 113: *begtk = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz", 114: /* token starting chars */ 115: *intk = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz0123456789", 116: /* valid in-token chars */ 117: *notgd = ",;"; /* non-valid after-function chars */ 118: 119: int file_num; /* current file number */ 120: int aflag; /* -a: append to tags */ 121: int tflag; /* -t: create tags for typedefs */ 122: int uflag; /* -u: update tags */ 123: int wflag; /* -w: suppress warnings */ 124: int vflag; /* -v: create vgrind style index output */ 125: int xflag; /* -x: create cxref style output */ 126: int eflag; /* -e: emacs style output */ 127: 128: FILE *inf, /* ioptr for current input file */ 129: *outf; /* ioptr for tags file */ 130: 131: NODE *head; /* the head of the sorted binary tree */ 132: 133: char *savestr(); 134: char *rindex(); 135: char *concat (); 136: void initbuffer (); 137: long readline (); 138: 139: /* A `struct linebuffer' is a structure which holds a line of text. 140: `readline' reads a line from a stream into a linebuffer 141: and works regardless of the length of the line. */ 142: 143: struct linebuffer 144: { 145: long size; 146: char *buffer; 147: }; 148: 149: struct linebuffer lb, lb1; 150: 151: main(ac,av) 152: int ac; 153: char *av[]; 154: { 155: char cmd[100]; 156: int i; 157: 158: #ifdef ETAGS 159: eflag = 1; 160: #endif 161: 162: while (ac > 1 && av[1][0] == '-') 163: { 164: eflag = 0; 165: for (i=1; av[1][i]; i++) 166: { 167: switch(av[1][i]) 168: { 169: case 'B': 170: searchar='?'; 171: break; 172: case 'F': 173: searchar='/'; 174: break; 175: case 'a': 176: aflag++; 177: break; 178: case 'e': 179: eflag++; 180: break; 181: case 't': 182: tflag++; 183: break; 184: case 'u': 185: uflag++; 186: break; 187: case 'w': 188: wflag++; 189: break; 190: case 'v': 191: vflag++; 192: xflag++; 193: break; 194: case 'x': 195: xflag++; 196: break; 197: default: 198: goto usage; 199: } 200: } 201: ac--; av++; 202: } 203: 204: if (ac <= 1) 205: { 206: usage: 207: printf("Usage: ctags [-BFaetuwvx] file ...\n"); 208: exit(1); 209: } 210: 211: if (eflag) 212: outfile = "TAGS"; 213: 214: init(); /* set up boolean "functions" */ 215: 216: initbuffer (&lb); 217: initbuffer (&lb1); 218: /* 219: * loop through files finding functions 220: */ 221: if (eflag) 222: { 223: outf = fopen (outfile, aflag ? "a" : "w"); 224: if (!outf) 225: { 226: perror (outfile); 227: exit (1); 228: } 229: } 230: 231: for (file_num = 1; file_num < ac; file_num++) 232: { 233: find_entries(av[file_num]); 234: if (eflag) 235: { 236: fprintf (outf, "\f\n%s,%d\n", 237: av[file_num], total_size_of_entries (head)); 238: put_entries (head); 239: free_tree (head); 240: head = NULL; 241: } 242: } 243: 244: if (eflag) 245: { 246: fclose (outf); 247: exit (0); 248: } 249: 250: if (xflag) 251: { 252: put_entries(head); 253: exit(0); 254: } 255: if (uflag) 256: { 257: for (i=1; i<ac; i++) 258: { 259: sprintf(cmd, 260: "mv %s OTAGS;fgrep -v '\t%s\t' OTAGS >%s;rm OTAGS", 261: outfile, av[i], outfile); 262: system(cmd); 263: } 264: aflag++; 265: } 266: outf = fopen(outfile, aflag ? "a" : "w"); 267: if (outf == NULL) 268: { 269: perror(outfile); 270: exit(1); 271: } 272: put_entries(head); 273: fclose(outf); 274: if (uflag) 275: { 276: sprintf(cmd, "sort %s -o %s", outfile, outfile); 277: system(cmd); 278: } 279: exit(0); 280: } 281: 282: /* 283: * This routine sets up the boolean psuedo-functions which work 284: * by seting boolean flags dependent upon the corresponding character 285: * Every char which is NOT in that string is not a white char. Therefore, 286: * all of the array "_wht" is set to FALSE, and then the elements 287: * subscripted by the chars in "white" are set to TRUE. Thus "_wht" 288: * of a char is TRUE if it is the string "white", else FALSE. 289: */ 290: init() 291: { 292: 293: reg char *sp; 294: reg int i; 295: 296: for (i = 0; i < 0177; i++) 297: { 298: _wht[i] = _etk[i] = _itk[i] = _btk[i] = FALSE; 299: _gd[i] = TRUE; 300: } 301: for (sp = white; *sp; sp++) 302: _wht[*sp] = TRUE; 303: for (sp = endtk; *sp; sp++) 304: _etk[*sp] = TRUE; 305: for (sp = intk; *sp; sp++) 306: _itk[*sp] = TRUE; 307: for (sp = begtk; *sp; sp++) 308: _btk[*sp] = TRUE; 309: for (sp = notgd; *sp; sp++) 310: _gd[*sp] = FALSE; 311: _wht[0] = _wht['\n']; 312: _etk[0] = _etk['\n']; 313: _btk[0] = _btk['\n']; 314: _itk[0] = _itk['\n']; 315: _gd[0] = _gd['\n']; 316: } 317: 318: /* 319: * This routine opens the specified file and calls the function 320: * which finds the function and type definitions. 321: */ 322: find_entries (file) 323: char *file; 324: { 325: char *cp; 326: 327: if ((inf=fopen(file,"r")) == NULL) 328: { 329: perror(file); 330: return; 331: } 332: curfile = savestr(file); 333: cp = rindex(file, '.'); 334: /* .l or .el or .lisp implies lisp source code */ 335: if (cp && (!strcmp (cp + 1, "l") || !strcmp (cp + 1, "el") 336: || !strcmp (cp + 1, "lisp"))) 337: { 338: L_funcs(inf); 339: fclose(inf); 340: return; 341: } 342: /* if not a .c or .h or .y file, try fortran */ 343: if (cp && (cp[1] != 'c' && cp[1] != 'h' && cp[1] != 'y') 344: && cp[2] == '\0') 345: { 346: if (PF_funcs(inf) != 0) 347: { 348: fclose(inf); 349: return; 350: } 351: rewind(inf); /* no fortran tags found, try C */ 352: } 353: C_entries(); 354: fclose(inf); 355: } 356: 357: /* Record a tag on the current line. 358: name is the tag name, 359: f is nonzero to use a pattern, zero to use line number instead. */ 360: 361: pfnote (name, f, linestart, linelen, lno, cno) 362: char *name; 363: logical f; /* f == TRUE when function */ 364: char *linestart; 365: int linelen; 366: int lno; 367: long cno; 368: { 369: register char *fp; 370: register NODE *np; 371: char *altname; 372: char tem[51]; 373: 374: if ((np = (NODE *) malloc(sizeof (NODE))) == NULL) 375: { 376: fprintf(stderr, "ctags: too many entries to sort\n"); 377: put_entries(head); 378: free_tree(head); 379: head = NULL; 380: np = (NODE *) xmalloc(sizeof (NODE)); 381: } 382: /* Change name "main" to M<thisfilename>. */ 383: if (!eflag && !xflag && !strcmp(name, "main")) 384: { 385: fp = rindex(curfile, '/'); 386: if (fp == 0) 387: fp = curfile; 388: else 389: fp++; 390: altname = concat ("M", fp, ""); 391: fp = rindex(altname, '.'); 392: if (fp && fp[2] == 0) 393: *fp = 0; 394: name = altname; 395: } 396: np->entry = savestr(name); 397: np->file = curfile; 398: np->f = f; 399: np->lno = lno; 400: np->cno = cno; 401: np->left = np->right = 0; 402: if (eflag) 403: { 404: linestart[linelen] = 0; 405: } 406: else if (xflag == 0) 407: { 408: sprintf (tem, strlen (linestart) < 50 ? "%s$" : "%.50s", linestart); 409: linestart = tem; 410: } 411: np->pat = savestr (linestart); 412: if (head == NULL) 413: head = np; 414: else 415: add_node(np, head); 416: } 417: 418: free_tree(node) 419: NODE *node; 420: { 421: while (node) 422: { 423: free_tree(node->right); 424: free(node); 425: node = node->left; 426: } 427: } 428: 429: add_node(node, cur_node) 430: NODE *node,*cur_node; 431: { 432: register int dif; 433: 434: dif = strcmp(node->entry, cur_node->entry); 435: 436: /* If this tag name matches an existing one, then 437: unless -e was given, do not add the node, but maybe print a warning */ 438: if (!eflag && !dif) 439: { 440: if (node->file == cur_node->file) 441: { 442: if (!wflag) 443: { 444: fprintf(stderr,"Duplicate entry in file %s, line %d: %s\n", 445: node->file,lineno,node->entry); 446: fprintf(stderr,"Second entry ignored\n"); 447: } 448: return; 449: } 450: if (!cur_node->been_warned) 451: if (!wflag) 452: fprintf(stderr,"Duplicate entry in files %s and %s: %s (Warning only)\n", 453: node->file, cur_node->file, node->entry); 454: cur_node->been_warned = TRUE; 455: return; 456: } 457: 458: /* Actually add the node */ 459: if (dif < 0) 460: { 461: if (cur_node->left != NULL) 462: add_node(node,cur_node->left); 463: else 464: cur_node->left = node; 465: return; 466: } 467: if (cur_node->right != NULL) 468: add_node(node,cur_node->right); 469: else 470: cur_node->right = node; 471: } 472: 473: put_entries(node) 474: reg NODE *node; 475: { 476: reg char *sp; 477: 478: if (node == NULL) 479: return; 480: 481: /* Output subentries that precede this one */ 482: put_entries (node->left); 483: 484: /* Output this entry */ 485: 486: if (eflag) 487: { 488: fprintf (outf, "%s%c%d,%d\n", 489: node->pat, 0177, node->lno, node->cno); 490: } 491: else if (!xflag) 492: { 493: fprintf (outf, "%s\t%s\t", 494: node->entry, node->file); 495: 496: if (node->f) 497: { /* a function */ 498: putc (searchar, outf); 499: putc ('^', outf); 500: 501: for (sp = node->pat; *sp; sp++) 502: { 503: if (*sp == '\\' || *sp == searchar) 504: putc ('\\', outf); 505: putc (*sp, outf); 506: } 507: putc (searchar, outf); 508: } 509: else 510: { /* a typedef; text pattern inadequate */ 511: fprintf (outf, "%d", node->lno); 512: } 513: putc ('\n', outf); 514: } 515: else if (vflag) 516: fprintf (stdout, "%s %s %d\n", 517: node->entry, node->file, (node->lno+63)/64); 518: else 519: fprintf (stdout, "%-16s%4d %-16s %s\n", 520: node->entry, node->lno, node->file, node->pat); 521: 522: /* Output subentries that follow this one */ 523: put_entries (node->right); 524: } 525: 526: /* Return total number of characters that put_entries will output for 527: the nodes in the subtree of the specified node. 528: Works only if eflag is set, but called only in that case. */ 529: 530: total_size_of_entries(node) 531: reg NODE *node; 532: { 533: reg int total = 0; 534: reg long num; 535: 536: if (node == NULL) 537: return 0; 538: 539: /* Count subentries that precede this one */ 540: total = total_size_of_entries (node->left); 541: 542: /* Count subentries that follow this one */ 543: total += total_size_of_entries (node->right); 544: 545: /* Count this entry */ 546: 547: total += strlen (node->pat) + 3; 548: 549: num = node->lno; 550: while (num) 551: { 552: total++; 553: num /= 10; 554: } 555: 556: num = node->cno; 557: if (!num) total++; 558: while (num) 559: { 560: total++; 561: num /= 10; 562: } 563: return total; 564: } 565: 566: /* 567: * This routine finds functions and typedefs in C syntax and adds them 568: * to the list. 569: */ 570: #define CNL_SAVE_NUMBER \ 571: { \ 572: linecharno = charno; lineno++; \ 573: charno += 1 + readline (&lb, inf); \ 574: lp = lb.buffer; \ 575: } 576: 577: #define CNL \ 578: { \ 579: CNL_SAVE_NUMBER; \ 580: number = 0; \ 581: } 582: 583: C_entries () 584: { 585: register int c; 586: register char *token, *tp, *lp; 587: logical incomm, inquote, inchar, midtoken; 588: int level; 589: char tok[BUFSIZ]; 590: 591: lineno = 0; 592: charno = 0; 593: lp = lb.buffer; 594: *lp = 0; 595: 596: number = 0; 597: gotone = midtoken = inquote = inchar = incomm = FALSE; 598: level = 0; 599: 600: while (!feof (inf)) 601: { 602: c = *lp++; 603: if (c == 0) 604: { 605: CNL; 606: gotone = FALSE; 607: } 608: if (c == '\\') 609: { 610: c = *lp++; 611: if (c == 0) 612: CNL_SAVE_NUMBER; 613: c = ' '; 614: } 615: else if (incomm) 616: { 617: if (c == '*') 618: { 619: while ((c = *lp++) == '*') 620: continue; 621: if (c == 0) 622: CNL; 623: if (c == '/') 624: incomm = FALSE; 625: } 626: } 627: else if (inquote) 628: { 629: /* 630: * Too dumb to know about \" not being magic, but 631: * they usually occur in pairs anyway. 632: */ 633: if (c == '"') 634: inquote = FALSE; 635: continue; 636: } 637: else if (inchar) 638: { 639: if (c == '\'') 640: inchar = FALSE; 641: continue; 642: } 643: else switch (c) 644: { 645: case '"': 646: inquote = TRUE; 647: continue; 648: case '\'': 649: inchar = TRUE; 650: continue; 651: case '/': 652: if (*lp == '*') 653: { 654: lp++; 655: incomm = TRUE; 656: } 657: continue; 658: case '#': 659: if (lp == lb.buffer + 1) 660: number = 1; 661: continue; 662: case '{': 663: if (tydef == begin) 664: { 665: tydef=middle; 666: } 667: level++; 668: continue; 669: case '}': 670: if (lp == lb.buffer + 1) 671: level = 0; /* reset */ 672: else 673: level--; 674: if (!level && tydef==middle) 675: { 676: tydef=end; 677: } 678: continue; 679: } 680: if (!level && !inquote && !incomm && gotone == FALSE) 681: { 682: if (midtoken) 683: { 684: if (endtoken(c)) 685: { 686: int f; 687: char *buf = lb.buffer; 688: int endpos = lp - lb.buffer; 689: char *lp1 = lp; 690: int line = lineno; 691: long linestart = linecharno; 692: int tem = consider_token (&lp1, token, &f); 693: lp = lp1; 694: if (tem) 695: { 696: if (linestart != linecharno) 697: { 698: getline (linestart); 699: strncpy (tok, token + (lb1.buffer - buf), 700: tp-token+1); 701: tok[tp-token+1] = 0; 702: pfnote(tok, f, lb1.buffer, endpos, line, linestart); 703: } 704: else 705: { 706: strncpy (tok, token, tp-token+1); 707: tok[tp-token+1] = 0; 708: pfnote(tok, f, lb.buffer, endpos, line, linestart); 709: } 710: gotone = f; /* function */ 711: } 712: midtoken = FALSE; 713: token = lp - 1; 714: } 715: else if (intoken(c)) 716: tp++; 717: } 718: else if (begtoken(c)) 719: { 720: token = tp = lp - 1; 721: midtoken = TRUE; 722: } 723: } 724: if (c == ';' && tydef==end) /* clean with typedefs */ 725: tydef=none; 726: } 727: } 728: 729: /* 730: * This routine checks to see if the current token is 731: * at the start of a function, or corresponds to a typedef 732: * It updates the input line * so that the '(' will be 733: * in it when it returns. 734: */ 735: consider_token (lpp, token, f) 736: char **lpp, *token; 737: int *f; 738: { 739: reg char *lp = *lpp; 740: reg char c; 741: static logical next_token_is_func; 742: logical firsttok; /* T if have seen first token in ()'s */ 743: int bad, win; 744: 745: *f = 1; /* a function */ 746: c = lp[-1]; 747: bad = FALSE; 748: if (!number) 749: { /* space is not allowed in macro defs */ 750: while (iswhite(c)) 751: { 752: c = *lp++; 753: if (c == 0) 754: { 755: if (feof (inf)) 756: break; 757: CNL; 758: } 759: } 760: /* the following tries to make it so that a #define a b(c) */ 761: /* doesn't count as a define of b. */ 762: } 763: else 764: { 765: number++; 766: if (number >= 4 || (number==2 && strncmp (token, "define", 6))) 767: { 768: gotone = TRUE; 769: badone: 770: bad = TRUE; 771: goto ret; 772: } 773: } 774: /* check for the typedef cases */ 775: if (tflag && !strncmp(token, "typedef", 7)) 776: { 777: tydef=begin; 778: goto badone; 779: } 780: if (tydef==begin && (!strncmp(token, "struct", 6) || 781: !strncmp(token, "union", 5) || !strncmp(token, "enum", 4))) 782: { 783: goto badone; 784: } 785: if (tydef==begin) 786: { 787: tydef=end; 788: goto badone; 789: } 790: if (tydef==end) 791: { 792: *f = 0; 793: win = 1; 794: goto ret; 795: } 796: /* Detect GNUmacs's function-defining macros. */ 797: if (!number && !strncmp (token, "DEF", 3)) 798: 799: { 800: next_token_is_func = 1; 801: goto badone; 802: } 803: if (next_token_is_func) 804: { 805: next_token_is_func = 0; 806: win = 1; 807: goto ret; 808: } 809: if (c != '(') 810: goto badone; 811: firsttok = FALSE; 812: while ((c = *lp++) != ')') 813: { 814: if (c == 0) 815: { 816: if (feof (inf)) 817: break; 818: CNL; 819: } 820: /* 821: * This line used to confuse ctags: 822: * int (*oldhup)(); 823: * This fixes it. A nonwhite char before the first 824: * token, other than a / (in case of a comment in there) 825: * makes this not a declaration. 826: */ 827: if (begtoken(c) || c=='/') firsttok++; 828: else if (!iswhite(c) && !firsttok) goto badone; 829: } 830: while (iswhite (c = *lp++)) 831: { 832: if (!c) 833: CNL; 834: } 835: win = isgood (c); 836: ret: 837: *lpp = lp - 1; 838: return !bad && win; 839: } 840: 841: getline (atchar) 842: long atchar; 843: { 844: long saveftell = ftell (inf); 845: 846: fseek (inf, atchar, 0); 847: readline (&lb1, inf); 848: fseek (inf, saveftell, 0); 849: } 850: 851: /* Fortran parsing */ 852: 853: char *dbp; 854: int pfcnt; 855: 856: PF_funcs(fi) 857: FILE *fi; 858: { 859: lineno = 0; 860: charno = 0; 861: pfcnt = 0; 862: 863: while (!feof (fi)) 864: { 865: lineno++; 866: linecharno = charno; 867: charno += readline (&lb, fi) + 1; 868: dbp = lb.buffer; 869: if (*dbp == '%') dbp++ ; /* Ratfor escape to fortran */ 870: while (isspace(*dbp)) 871: dbp++; 872: if (*dbp == 0) 873: continue; 874: switch (*dbp |' ') 875: { 876: case 'i': 877: if (tail("integer")) 878: takeprec(); 879: break; 880: case 'r': 881: if (tail("real")) 882: takeprec(); 883: break; 884: case 'l': 885: if (tail("logical")) 886: takeprec(); 887: break; 888: case 'c': 889: if (tail("complex") || tail("character")) 890: takeprec(); 891: break; 892: case 'd': 893: if (tail("double")) 894: { 895: while (isspace(*dbp)) 896: dbp++; 897: if (*dbp == 0) 898: continue; 899: if (tail("precision")) 900: break; 901: continue; 902: } 903: break; 904: } 905: while (isspace(*dbp)) 906: dbp++; 907: if (*dbp == 0) 908: continue; 909: switch (*dbp|' ') 910: { 911: case 'f': 912: if (tail("function")) 913: getit(); 914: continue; 915: case 's': 916: if (tail("subroutine")) 917: getit(); 918: continue; 919: case 'p': 920: if (tail("program")) 921: { 922: getit(); 923: continue; 924: } 925: if (tail("procedure")) 926: getit(); 927: continue; 928: } 929: } 930: return (pfcnt); 931: } 932: 933: tail(cp) 934: char *cp; 935: { 936: register int len = 0; 937: 938: while (*cp && (*cp&~' ') == ((*(dbp+len))&~' ')) 939: cp++, len++; 940: if (*cp == 0) 941: { 942: dbp += len; 943: return (1); 944: } 945: return (0); 946: } 947: 948: takeprec() 949: { 950: while (isspace(*dbp)) 951: dbp++; 952: if (*dbp != '*') 953: return; 954: dbp++; 955: while (isspace(*dbp)) 956: dbp++; 957: if (!isdigit(*dbp)) 958: { 959: --dbp; /* force failure */ 960: return; 961: } 962: do 963: dbp++; 964: while (isdigit(*dbp)); 965: } 966: 967: getit() 968: { 969: register char *cp; 970: char c; 971: char nambuf[BUFSIZ]; 972: 973: while (isspace(*dbp)) 974: dbp++; 975: if (*dbp == 0 || !isalpha(*dbp)) 976: return; 977: for (cp = dbp+1; *cp && (isalpha(*cp) || isdigit(*cp)); cp++) 978: continue; 979: c = cp[0]; 980: cp[0] = 0; 981: strcpy(nambuf, dbp); 982: cp[0] = c; 983: pfnote(nambuf, TRUE, lb.buffer, cp - lb.buffer + 1, lineno, linecharno); 984: pfcnt++; 985: } 986: 987: /* 988: * lisp tag functions 989: * just look for (def or (DEF 990: */ 991: 992: L_funcs (fi) 993: FILE *fi; 994: { 995: lineno = 0; 996: charno = 0; 997: pfcnt = 0; 998: 999: while (!feof (fi)) 1000: { 1001: lineno++; 1002: linecharno = charno; 1003: charno += readline (&lb, fi) + 1; 1004: dbp = lb.buffer; 1005: if (dbp[0] == '(' && 1006: (dbp[1] == 'D' || dbp[1] == 'd') && 1007: (dbp[2] == 'E' || dbp[2] == 'e') && 1008: (dbp[3] == 'F' || dbp[3] == 'f')) 1009: { 1010: while (!isspace(*dbp)) dbp++; 1011: while (isspace(*dbp)) dbp++; 1012: L_getit(); 1013: } 1014: } 1015: } 1016: 1017: L_getit() 1018: { 1019: register char *cp; 1020: char c; 1021: char nambuf[BUFSIZ]; 1022: 1023: if (*dbp == 0) return; 1024: for (cp = dbp+1; *cp && *cp != '(' && *cp != ' '; cp++) 1025: continue; 1026: c = cp[0]; 1027: cp[0] = 0; 1028: strcpy(nambuf, dbp); 1029: cp[0] = c; 1030: pfnote(nambuf, TRUE, lb.buffer, cp - lb.buffer + 1, lineno, linecharno); 1031: pfcnt++; 1032: } 1033: 1034: /* Initialize a linebuffer for use */ 1035: 1036: void 1037: initbuffer (linebuffer) 1038: struct linebuffer *linebuffer; 1039: { 1040: linebuffer->size = 200; 1041: linebuffer->buffer = (char *) xmalloc (200); 1042: } 1043: 1044: /* Read a line of text from `stream' into `linebuffer'. 1045: Return the length of the line. */ 1046: 1047: long 1048: readline (linebuffer, stream) 1049: struct linebuffer *linebuffer; 1050: register FILE *stream; 1051: { 1052: char *buffer = linebuffer->buffer; 1053: register char *p = linebuffer->buffer; 1054: register char *pend = p + linebuffer->size; 1055: 1056: while (1) 1057: { 1058: int c = getc (stream); 1059: if (p == pend) 1060: { 1061: buffer = (char *) xrealloc (buffer, linebuffer->size *= 2); 1062: p += buffer - linebuffer->buffer; 1063: pend += buffer - linebuffer->buffer; 1064: linebuffer->buffer = buffer; 1065: } 1066: if (c < 0 || c == '\n') 1067: { 1068: *p = 0; 1069: break; 1070: } 1071: *p++ = c; 1072: } 1073: 1074: return p - buffer; 1075: } 1076: 1077: char * 1078: savestr(cp) 1079: char *cp; 1080: { 1081: register int len; 1082: register char *dp; 1083: 1084: len = strlen(cp); 1085: dp = (char *)xmalloc(len+1); 1086: strcpy(dp, cp); 1087: return (dp); 1088: } 1089: 1090: /* 1091: * Return the ptr in sp at which the character c last 1092: * appears; NULL if not found 1093: * 1094: * Identical to v7 rindex, included for portability. 1095: */ 1096: 1097: char * 1098: rindex(sp, c) 1099: register char *sp, c; 1100: { 1101: register char *r; 1102: 1103: r = NULL; 1104: do 1105: { 1106: if (*sp == c) 1107: r = sp; 1108: } while (*sp++); 1109: return(r); 1110: } 1111: 1112: /* Print error message and exit. */ 1113: 1114: fatal (s1, s2) 1115: char *s1, *s2; 1116: { 1117: error (s1, s2); 1118: exit (1); 1119: } 1120: 1121: /* Print error message. `s1' is printf control string, `s2' is arg for it. */ 1122: 1123: error (s1, s2) 1124: char *s1, *s2; 1125: { 1126: #ifdef CTAGS 1127: printf ("ctags: "); 1128: #else 1129: printf ("etags: "); 1130: #endif 1131: printf (s1, s2); 1132: printf ("\n"); 1133: } 1134: 1135: /* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */ 1136: 1137: char * 1138: concat (s1, s2, s3) 1139: char *s1, *s2, *s3; 1140: { 1141: int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3); 1142: char *result = (char *) xmalloc (len1 + len2 + len3 + 1); 1143: 1144: strcpy (result, s1); 1145: strcpy (result + len1, s2); 1146: strcpy (result + len1 + len2, s3); 1147: *(result + len1 + len2 + len3) = 0; 1148: 1149: return result; 1150: } 1151: 1152: /* Like malloc but get fatal error if memory is exhausted. */ 1153: 1154: int 1155: xmalloc (size) 1156: int size; 1157: { 1158: int result = malloc (size); 1159: if (!result) 1160: fatal ("virtual memory exhausted", 0); 1161: return result; 1162: } 1163: 1164: int 1165: xrealloc (ptr, size) 1166: char *ptr; 1167: int size; 1168: { 1169: int result = realloc (ptr, size); 1170: if (!result) 1171: fatal ("virtual memory exhausted"); 1172: return result; 1173: }