home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume3 / ctags < prev    next >
Encoding:
Internet Message Format  |  1986-11-30  |  23.8 KB

  1. From: decvax!ucbvax!ucsf-cgl.ARPA!arnold (Ken Arnold)
  2. Subject: ctags source
  3. Newsgroups: mod.sources
  4. Approved: jpn@panda.UUCP
  5.  
  6. Mod.sources:  Volume 3, Issue 53
  7. Submitted by: ucbvax!ucsf-cgl.ARPA!arnold (Ken Arnold)
  8.  
  9.  
  10. #!/bin/sh
  11. #
  12. # I have received enough requests for the current source to ctags to
  13. # post it.  Here is the latest version (what will go out with 4.3,
  14. # modulo any bugs fixed during the beta period).  It is the 4.2 ctags
  15. # with recognition of yacc and lex tags added.  Files ending in .y are
  16. # presumed to be yacc files, .l files are either lisp or lex files,
  17. # depending upon the first non-blank character (yeah, it's a hack, but
  18. # it works).  Man page is included.  Note that I wrote the original
  19. # ctags (recognized only C), and added the YACC and lex after others
  20. # wrote the recognizers for FORTRAN, PASCAL, and lisp, so this code is
  21. # rather a mix of styles, for which I take no responsibility.  So, here
  22. # come those famous words:
  23. #
  24. # This is a shell archive, meaning:
  25. # 1. Remove everything above the #!/bin/sh line.
  26. # 2. Save the resulting text in a file.
  27. # 3. Execute the file with /bin/sh (not csh) to create the files:
  28. #    ctags.1
  29. #    ctags.c
  30. # This archive created: Mon Nov 25 12:53:49 1985
  31. export PATH; PATH=/bin:$PATH
  32. if test -f 'ctags.1'
  33. then
  34.     echo shar: over-writing existing file "'ctags.1'"
  35. fi
  36. cat << \SHAR_EOF > 'ctags.1'
  37. .TH CTAGS 1 "25 August 1982"
  38. .UC 4
  39. .SH NAME
  40. ctags \- create a tags file
  41. .SH SYNOPSIS
  42. .B ctags
  43. .B \-BFatuwvx
  44. ]
  45. name ...
  46. .SH DESCRIPTION
  47. .I Ctags
  48. makes a tags file for
  49. .IR ex (1)
  50. from the specified C, Pascal, Fortran, YACC, lex, and lisp sources.
  51. A tags file gives the locations of specified objects (in this case
  52. functions and typedefs) in a group of files.  Each line of the tags
  53. file contains the object name, the file in which it is defined, and
  54. an address specification for the object definition. Functions are
  55. searched with a pattern, typedefs with a line number. Specifiers are
  56. given in separate fields on the line, separated by blanks or tabs.
  57. Using the
  58. .I tags
  59. file,
  60. .I ex
  61. can quickly find these objects definitions.
  62. .PP
  63. If the
  64. .B \-x
  65. flag is given, 
  66. .I ctags
  67. produces a list of object names, the line number and file
  68. name on which each is defined, as well as the text of that line
  69. and prints this on the standard output.  This is a simple index
  70. which can be printed out as an off-line readable function index.
  71. .PP
  72. If the
  73. .B \-v
  74. flag is given,
  75. an index of the form expected by
  76. .IR vgrind (1)
  77. is produced on the standard output.
  78. This listing contains the function name,
  79. file name, and page number
  80. (assuming 64 line pages).
  81. Since the output will be sorted into lexicographic order,
  82. it may be desired to run the output through
  83. .BR "sort \-f" .
  84. Sample use:
  85. .nf
  86.     ctags \-v files | sort \-f > index
  87.     vgrind \-x index
  88. .fi
  89. .PP
  90. Files whose names end in 
  91. .B \.c
  92. or
  93. .B \.h
  94. are assumed to be C source files and are searched for C routine and
  95. macro definitions.
  96. Files whose names end in
  97. .B \.y
  98. are assumed to be YACC source files.
  99. Files whose names end in
  100. .B \.l
  101. are assumed to be either lisp files
  102. if their first non-blank character is `;', `(', or `[',
  103. or lex files otherwise.
  104. Other files are first examined to see if they contain any Pascal or
  105. Fortran routine definitions; if not, they are processed again
  106. looking for C definitions.
  107. .PP
  108. Other options are:
  109. .TP 5
  110. .B \-F
  111. use forward searching patterns (/.../) (default).
  112. .TP 5
  113. .B \-B
  114. use backward searching patterns (?...?).
  115. .TP 5
  116. .B \-a
  117. append to tags file.
  118. .TP 5
  119. .B \-t
  120. create tags for typedefs.
  121. .TP 5
  122. .B \-w
  123. suppressing warning diagnostics.
  124. .TP 5
  125. .B \-u
  126. causing the specified files to be
  127. .I updated
  128. in tags, that is, all references to them are deleted,
  129. and the new values are appended to the file.
  130. (Beware: this option is implemented in a way which is rather slow;
  131. it is usually faster to simply rebuild the
  132. .I tags
  133. file.)
  134. .PP
  135. The tag
  136. .I main
  137. is treated specially in C programs.
  138. The tag formed is created by prepending
  139. .I M
  140. to the name of the file, with a trailing .c removed, if
  141. any, and leading pathname components also removed.
  142. This makes use of
  143. .I ctags
  144. practical in directories with more than one program.
  145. .SH FILES
  146. .DT
  147. tags        output tags file
  148. .SH SEE ALSO
  149. ex(1), vi(1)
  150. .SH AUTHOR
  151. Ken Arnold; FORTRAN added by Jim Kleckner; Bill Joy
  152. added Pascal and
  153. .B \-x,
  154. replacing
  155. .I cxref;
  156. C typedefs added by Ed Pelegri-Llopart.
  157. .SH BUGS
  158. Recognition of \fBfunctions\fR, \fBsubroutines\fR and \fBprocedures\fR
  159. for FORTRAN and Pascal is done is a very simpleminded way.
  160. No attempt is made to deal with block structure; if you have two
  161. Pascal procedures in different blocks with the same name you lose.
  162. .PP
  163. The method of deciding whether to look for C or Pascal and FORTRAN
  164. functions is a hack.
  165. .PP
  166. Does not know about #ifdefs.
  167. .PP
  168. Should know about Pascal types.
  169. Relies on the input being well formed to detect typedefs.
  170. Use of -tx shows only the last line of typedefs.
  171. SHAR_EOF
  172. if test -f 'ctags.c'
  173. then
  174.     echo shar: over-writing existing file "'ctags.c'"
  175. fi
  176. cat << \SHAR_EOF > 'ctags.c'
  177. /* $Header */
  178.  
  179. static char *sccsid = "@(#)ctags.c    4.7 (Berkeley) 8/18/83";
  180.  
  181. #include <stdio.h>
  182. #include <ctype.h>
  183.  
  184. /*
  185.  * ctags: create a tags file
  186.  */
  187.  
  188. #define    reg    register
  189. #define    bool    char
  190.  
  191. #define    TRUE    (1)
  192. #define    FALSE    (0)
  193.  
  194. #define    iswhite(arg)    (_wht[arg])    /* T if char is white        */
  195. #define    begtoken(arg)    (_btk[arg])    /* T if char can start token    */
  196. #define    intoken(arg)    (_itk[arg])    /* T if char can be in token    */
  197. #define    endtoken(arg)    (_etk[arg])    /* T if char ends tokens    */
  198. #define    isgood(arg)    (_gd[arg])    /* T if char can be after ')'    */
  199.  
  200. #define    max(I1,I2)    (I1 > I2 ? I1 : I2)
  201.  
  202. struct    nd_st {            /* sorting structure            */
  203.     char    *entry;            /* function or type name    */
  204.     char    *file;            /* file name            */
  205.     bool    f;            /* use pattern or line no    */
  206.     int    lno;            /* for -x option        */
  207.     char    *pat;            /* search pattern        */
  208.     bool    been_warned;        /* set if noticed dup        */
  209.     struct    nd_st    *left,*right;    /* left and right sons        */
  210. };
  211.  
  212. long    ftell();
  213. typedef    struct    nd_st    NODE;
  214.  
  215. bool    number,                /* T if on line starting with #    */
  216.     term    = FALSE,        /* T if print on terminal    */
  217.     makefile= TRUE,            /* T if to creat "tags" file    */
  218.     gotone,                /* found a func already on line    */
  219.                     /* boolean "func" (see init)    */
  220.     _wht[0177],_etk[0177],_itk[0177],_btk[0177],_gd[0177];
  221.  
  222.     /* typedefs are recognized using a simple finite automata,
  223.      * tydef is its state variable.
  224.      */
  225. typedef enum {none, begin, middle, end } TYST;
  226.  
  227. TYST tydef = none;
  228.  
  229. char    searchar = '/';            /* use /.../ searches         */
  230.  
  231. int    lineno;                /* line number of current line */
  232. char    line[4*BUFSIZ],        /* current input line            */
  233.     *curfile,        /* current input file name        */
  234.     *outfile= "tags",    /* output file                */
  235.     *white    = " \f\t\n",    /* white chars                */
  236.     *endtk    = " \t\n\"'#()[]{}=-+%*/&|^~!<>;,.:?",
  237.                 /* token ending chars            */
  238.     *begtk    = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz",
  239.                 /* token starting chars            */
  240.     *intk    = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz0123456789",
  241.                 /* valid in-token chars            */
  242.     *notgd    = ",;";        /* non-valid after-function chars    */
  243.  
  244. int    file_num;        /* current file number            */
  245. int    aflag;            /* -a: append to tags */
  246. int    tflag;            /* -t: create tags for typedefs */
  247. int    uflag;            /* -u: update tags */
  248. int    wflag;            /* -w: suppress warnings */
  249. int    vflag;            /* -v: create vgrind style index output */
  250. int    xflag;            /* -x: create cxref style output */
  251.  
  252. char    lbuf[BUFSIZ];
  253.  
  254. FILE    *inf,            /* ioptr for current input file        */
  255.     *outf;            /* ioptr for tags file            */
  256.  
  257. long    lineftell;        /* ftell after getc( inf ) == '\n'     */
  258.  
  259. NODE    *head;            /* the head of the sorted binary tree    */
  260.  
  261. char    *savestr();
  262. char    *rindex(), *index();
  263. char    *toss_comment();
  264.  
  265. main(ac,av)
  266. int    ac;
  267. char    *av[];
  268. {
  269.     char cmd[100];
  270.     int i;
  271.  
  272.     while (ac > 1 && av[1][0] == '-') {
  273.         for (i=1; av[1][i]; i++) {
  274.             switch(av[1][i]) {
  275.               case 'B':
  276.                 searchar='?';
  277.                 break;
  278.               case 'F':
  279.                 searchar='/';
  280.                 break;
  281.               case 'a':
  282.                 aflag++;
  283.                 break;
  284.               case 't':
  285.                 tflag++;
  286.                 break;
  287.               case 'u':
  288.                 uflag++;
  289.                 break;
  290.               case 'w':
  291.                 wflag++;
  292.                 break;
  293.               case 'v':
  294.                 vflag++;
  295.                 xflag++;
  296.                 break;
  297.               case 'x':
  298.                 xflag++;
  299.                 break;
  300.               default:
  301.                 goto usage;
  302.         }
  303.         }
  304.         ac--; av++;
  305.     }
  306.  
  307.     if (ac <= 1) {
  308. usage:
  309.         printf("Usage: ctags [-BFatuwvx] file ...\n");
  310.         exit(1);
  311.     }
  312.  
  313.     init();            /* set up boolean "functions"        */
  314.     /*
  315.      * loop through files finding functions
  316.      */
  317.     for (file_num = 1; file_num < ac; file_num++)
  318.         find_entries(av[file_num]);
  319.  
  320.     if (xflag) {
  321.         put_entries(head);
  322.         exit(0);
  323.     }
  324.     if (uflag) {
  325.         for (i=1; i<ac; i++) {
  326.             sprintf(cmd,
  327.                 "mv %s OTAGS;fgrep -v '\t%s\t' OTAGS >%s;rm OTAGS",
  328.                 outfile, av[i], outfile);
  329.             system(cmd);
  330.         }
  331.         aflag++;
  332.     }
  333.     outf = fopen(outfile, aflag ? "a" : "w");
  334.     if (outf == NULL) {
  335.         perror(outfile);
  336.         exit(1);
  337.     }
  338.     put_entries(head);
  339.     fclose(outf);
  340.     if (uflag) {
  341.         sprintf(cmd, "sort %s -o %s", outfile, outfile);
  342.         system(cmd);
  343.     }
  344.     exit(0);
  345. }
  346.  
  347. /*
  348.  * This routine sets up the boolean psuedo-functions which work
  349.  * by seting boolean flags dependent upon the corresponding character
  350.  * Every char which is NOT in that string is not a white char.  Therefore,
  351.  * all of the array "_wht" is set to FALSE, and then the elements
  352.  * subscripted by the chars in "white" are set to TRUE.  Thus "_wht"
  353.  * of a char is TRUE if it is the string "white", else FALSE.
  354.  */
  355. init()
  356. {
  357.  
  358.     reg    char    *sp;
  359.     reg    int    i;
  360.  
  361.     for (i = 0; i < 0177; i++) {
  362.         _wht[i] = _etk[i] = _itk[i] = _btk[i] = FALSE;
  363.         _gd[i] = TRUE;
  364.     }
  365.     for (sp = white; *sp; sp++)
  366.         _wht[*sp] = TRUE;
  367.     for (sp = endtk; *sp; sp++)
  368.         _etk[*sp] = TRUE;
  369.     for (sp = intk; *sp; sp++)
  370.         _itk[*sp] = TRUE;
  371.     for (sp = begtk; *sp; sp++)
  372.         _btk[*sp] = TRUE;
  373.     for (sp = notgd; *sp; sp++)
  374.         _gd[*sp] = FALSE;
  375. }
  376.  
  377. /*
  378.  * This routine opens the specified file and calls the function
  379.  * which finds the function and type definitions.
  380.  */
  381. find_entries(file)
  382. char    *file;
  383. {
  384.     char *cp;
  385.  
  386.     if ((inf = fopen(file,"r")) == NULL) {
  387.         perror(file);
  388.         return;
  389.     }
  390.     curfile = savestr(file);
  391.     lineno = 0;
  392.     cp = rindex(file, '.');
  393.     /* .l implies lisp or lex source code */
  394.     if (cp && cp[1] == 'l' && cp[2] == '\0') {
  395.         if (index(";([", first_char()) != NULL) {    /* lisp */
  396.             L_funcs(inf);
  397.             fclose(inf);
  398.             return;
  399.         }
  400.         else {                        /* lex */
  401.             /*
  402.              * throw away all the code before the second "%%"
  403.              */
  404.             toss_yysec();
  405.             getline();
  406.             pfnote("yylex", lineno, TRUE);
  407.             toss_yysec();
  408.             C_entries();
  409.             fclose(inf);
  410.             return;
  411.         }
  412.     }
  413.     /* .y implies a yacc file */
  414.     if (cp && cp[1] == 'y' && cp[2] == '\0') {
  415.         toss_yysec();
  416.         Y_entries();
  417.         C_entries();
  418.         fclose(inf);
  419.         return;
  420.     }
  421.     /* if not a .c or .h file, try fortran */
  422.     if (cp && (cp[1] != 'c' && cp[1] != 'h') && cp[2] == '\0') {
  423.         if (PF_funcs(inf) != 0) {
  424.             fclose(inf);
  425.             return;
  426.         }
  427.         rewind(inf);    /* no fortran tags found, try C */
  428.     }
  429.     C_entries();
  430.     fclose(inf);
  431. }
  432.  
  433. pfnote(name, ln, f)
  434. char    *name;
  435. int    ln;
  436. bool    f;        /* f == TRUE when function */
  437. {
  438.     register char *fp;
  439.     register NODE *np;
  440.     char nbuf[BUFSIZ];
  441.  
  442.     if ((np = (NODE *) malloc(sizeof (NODE))) == NULL) {
  443.         fprintf(stderr, "ctags: too many entries to sort\n");
  444.         put_entries(head);
  445.         free_tree(head);
  446.         head = np = (NODE *) malloc(sizeof (NODE));
  447.     }
  448.     if (xflag == 0 && !strcmp(name, "main")) {
  449.         fp = rindex(curfile, '/');
  450.         if (fp == 0)
  451.             fp = curfile;
  452.         else
  453.             fp++;
  454.         sprintf(nbuf, "M%s", fp);
  455.         fp = rindex(nbuf, '.');
  456.         if (fp && fp[2] == 0)
  457.             *fp = 0;
  458.         name = nbuf;
  459.     }
  460.     np->entry = savestr(name);
  461.     np->file = curfile;
  462.     np->f = f;
  463.     np->lno = ln;
  464.     np->left = np->right = 0;
  465.     if (xflag == 0) {
  466.         lbuf[50] = 0;
  467.         strcat(lbuf, "$");
  468.         lbuf[50] = 0;
  469.     }
  470.     np->pat = savestr(lbuf);
  471.     if (head == NULL)
  472.         head = np;
  473.     else
  474.         add_node(np, head);
  475. }
  476.  
  477. /*
  478.  * This routine finds functions and typedefs in C syntax and adds them
  479.  * to the list.
  480.  */
  481. C_entries()
  482. {
  483.     register int c;
  484.     register char *token, *tp;
  485.     bool incomm, inquote, inchar, midtoken;
  486.     int level;
  487.     char *sp;
  488.     char tok[BUFSIZ];
  489.  
  490.     number = gotone = midtoken = inquote = inchar = incomm = FALSE;
  491.     level = 0;
  492.     sp = tp = token = line;
  493.     lineno++;
  494.     lineftell = ftell(inf);
  495.     for (;;) {
  496.         *sp = c = getc(inf);
  497.         if (feof(inf))
  498.             break;
  499.         if (c == '\n')
  500.             lineno++;
  501.         else if (c == '\\') {
  502.             c = *++sp = getc(inf);
  503.             if (c == '\n')
  504.                 c = ' ';
  505.         }
  506.         else if (incomm) {
  507.             if (c == '*') {
  508.                 while ((*++sp=c=getc(inf)) == '*')
  509.                     continue;
  510.                 if (c == '\n')
  511.                     lineno++;
  512.                 if (c == '/')
  513.                     incomm = FALSE;
  514.             }
  515.         }
  516.         else if (inquote) {
  517.             /*
  518.              * Too dumb to know about \" not being magic, but
  519.              * they usually occur in pairs anyway.
  520.              */
  521.             if (c == '"')
  522.                 inquote = FALSE;
  523.             continue;
  524.         }
  525.         else if (inchar) {
  526.             if (c == '\'')
  527.                 inchar = FALSE;
  528.             continue;
  529.         }
  530.         else switch (c) {
  531.           case '"':
  532.             inquote = TRUE;
  533.             continue;
  534.           case '\'':
  535.             inchar = TRUE;
  536.             continue;
  537.           case '/':
  538.             if ((*++sp=c=getc(inf)) == '*')
  539.                 incomm = TRUE;
  540.             else
  541.                 ungetc(*sp, inf);
  542.             continue;
  543.           case '#':
  544.             if (sp == line)
  545.                 number = TRUE;
  546.             continue;
  547.           case '{':
  548.             if (tydef == begin) {
  549.                 tydef=middle;
  550.             }
  551.             level++;
  552.             continue;
  553.           case '}':
  554.             if (sp == line)
  555.                 level = 0;    /* reset */
  556.             else
  557.                 level--;
  558.             if (!level && tydef==middle) {
  559.                 tydef=end;
  560.             }
  561.             continue;
  562.         }
  563.         if (!level && !inquote && !incomm && gotone == FALSE) {
  564.             if (midtoken) {
  565.                 if (endtoken(c)) {
  566.                     int f;
  567.                     int pfline = lineno;
  568.                     if (start_entry(&sp,token,tp,&f)) {
  569.                         strncpy(tok,token,tp-token+1);
  570.                         tok[tp-token+1] = 0;
  571.                         getline();
  572.                         pfnote(tok, pfline, f);
  573.                         gotone = f;    /* function */
  574.                     }
  575.                     midtoken = FALSE;
  576.                     token = sp;
  577.                 }
  578.                 else if (intoken(c))
  579.                     tp++;
  580.             }
  581.             else if (begtoken(c)) {
  582.                 token = tp = sp;
  583.                 midtoken = TRUE;
  584.             }
  585.         }
  586.         if (c == ';'  &&  tydef==end)    /* clean with typedefs */
  587.             tydef=none;
  588.         sp++;
  589.         if (c == '\n' || sp > &line[sizeof (line) - BUFSIZ]) {
  590.             tp = token = sp = line;
  591.             lineftell = ftell(inf);
  592.             number = gotone = midtoken = inquote = inchar = FALSE;
  593.         }
  594.     }
  595. }
  596.  
  597. /*
  598.  * This routine  checks to see if the current token is
  599.  * at the start of a function, or corresponds to a typedef
  600.  * It updates the input line * so that the '(' will be
  601.  * in it when it returns.
  602.  */
  603. start_entry(lp,token,tp,f)
  604. char    **lp,*token,*tp;
  605. int    *f;
  606. {
  607.     reg    char    c,*sp,*tsp;
  608.     static    bool    found;
  609.     bool    firsttok;        /* T if have seen first token in ()'s */
  610.     int    bad;
  611.  
  612.     *f = 1;            /* a function */
  613.     sp = *lp;
  614.     c = *sp;
  615.     bad = FALSE;
  616.     if (!number) {        /* space is not allowed in macro defs    */
  617.         while (iswhite(c)) {
  618.             *++sp = c = getc(inf);
  619.             if (c == '\n') {
  620.                 lineno++;
  621.                 if (sp > &line[sizeof (line) - BUFSIZ])
  622.                     goto ret;
  623.             }
  624.         }
  625.     /* the following tries to make it so that a #define a b(c)    */
  626.     /* doesn't count as a define of b.                */
  627.     }
  628.     else {
  629.         if (!strncmp(token, "define", 6))
  630.             found = 0;
  631.         else
  632.             found++;
  633.         if (found >= 2) {
  634.             gotone = TRUE;
  635. badone:            bad = TRUE;
  636.             goto ret;
  637.         }
  638.     }
  639.     /* check for the typedef cases        */
  640.     if (tflag && !strncmp(token, "typedef", 7)) {
  641.         tydef=begin;
  642.         goto badone;
  643.     }
  644.     if (tydef==begin && (!strncmp(token, "struct", 6) ||
  645.         !strncmp(token, "union", 5) || !strncmp(token, "enum", 4))) {
  646.         goto badone;
  647.     }
  648.     if (tydef==begin) {
  649.         tydef=end;
  650.         goto badone;
  651.     }
  652.     if (tydef==end) {
  653.         *f = 0;
  654.         goto ret;
  655.     }
  656.     if (c != '(')
  657.         goto badone;
  658.     firsttok = FALSE;
  659.     while ((*++sp=c=getc(inf)) != ')') {
  660.         if (c == '\n') {
  661.             lineno++;
  662.             if (sp > &line[sizeof (line) - BUFSIZ])
  663.                 goto ret;
  664.         }
  665.         /*
  666.          * This line used to confuse ctags:
  667.          *    int    (*oldhup)();
  668.          * This fixes it. A nonwhite char before the first
  669.          * token, other than a / (in case of a comment in there)
  670.          * makes this not a declaration.
  671.          */
  672.         if (begtoken(c) || c=='/')
  673.             firsttok++;
  674.         else if (!iswhite(c) && !firsttok)
  675.             goto badone;
  676.     }
  677.     while (iswhite(*++sp=c=getc(inf)))
  678.         if (c == '\n') {
  679.             lineno++;
  680.             if (sp > &line[sizeof (line) - BUFSIZ])
  681.                 break;
  682.         }
  683. ret:
  684.     *lp = --sp;
  685.     if (c == '\n')
  686.         lineno--;
  687.     ungetc(c,inf);
  688.     return !bad && (!*f || isgood(c));
  689.                     /* hack for typedefs */
  690. }
  691.  
  692. /*
  693.  * Y_entries:
  694.  *    Find the yacc tags and put them in.
  695.  */
  696. Y_entries()
  697. {
  698.     register char    *sp, *orig_sp;
  699.     register int    brace;
  700.     register bool    in_rule, toklen;
  701.     char        tok[BUFSIZ];
  702.  
  703.     brace = 0;
  704.     getline();
  705.     pfnote("yyparse", lineno, TRUE);
  706.     while (fgets(line, sizeof line, inf) != NULL)
  707.         for (sp = line; *sp; sp++)
  708.             switch (*sp) {
  709.               case '\n':
  710.                 lineno++;
  711.                 /* FALLTHROUGH */
  712.               case ' ':
  713.               case '\t':
  714.               case '\f':
  715.               case '\r':
  716.                 break;
  717.               case '"':
  718.                 do {
  719.                     while (*++sp != '"')
  720.                         continue;
  721.                 } while (sp[-1] == '\\');
  722.                 break;
  723.               case '\'':
  724.                 do {
  725.                     while (*++sp != '\'')
  726.                         continue;
  727.                 } while (sp[-1] == '\\');
  728.                 break;
  729.               case '/':
  730.                 if (*++sp == '*')
  731.                     sp = toss_comment(sp);
  732.                 else
  733.                     --sp;
  734.                 break;
  735.               case '{':
  736.                 brace++;
  737.                 break;
  738.               case '}':
  739.                 brace--;
  740.                 break;
  741.               case '%':
  742.                 if (sp[1] == '%' && sp == line)
  743.                     return;
  744.                 break;
  745.               case '|':
  746.               case ';':
  747.                 in_rule = FALSE;
  748.                 break;
  749.               default:
  750.                 if (brace == 0  && !in_rule && (isalpha(*sp) ||
  751.                                 *sp == '.' ||
  752.                                 *sp == '_')) {
  753.                     orig_sp = sp;
  754.                     ++sp;
  755.                     while (isalnum(*sp) || *sp == '_' ||
  756.                            *sp == '.')
  757.                         sp++;
  758.                     toklen = sp - orig_sp;
  759.                     while (isspace(*sp))
  760.                         sp++;
  761.                     if (*sp == ':' || (*sp == '\0' &&
  762.                                first_char() == ':'))
  763.                     {
  764.                         strncpy(tok, orig_sp, toklen);
  765.                         tok[toklen] = '\0';
  766.                         strcpy(lbuf, line);
  767.                         lbuf[strlen(lbuf) - 1] = '\0';
  768.                         pfnote(tok, lineno, TRUE);
  769.                         in_rule = TRUE;
  770.                     }
  771.                     else
  772.                         sp--;
  773.                 }
  774.                 break;
  775.             }
  776. }
  777.  
  778. char *
  779. toss_comment(start)
  780. char    *start;
  781. {
  782.     register char    *sp;
  783.  
  784.     /*
  785.      * first, see if the end-of-comment is on the same line
  786.      */
  787.     do {
  788.         while ((sp = index(start, '*')) != NULL)
  789.             if (sp[1] == '/')
  790.                 return ++sp;
  791.             else
  792.                 start = ++sp;
  793.         start = line;
  794.         lineno++;
  795.     } while (fgets(line, sizeof line, inf) != NULL);
  796. }
  797.  
  798. getline()
  799. {
  800.     long saveftell = ftell( inf );
  801.     register char *cp;
  802.  
  803.     fseek( inf , lineftell , 0 );
  804.     fgets(lbuf, sizeof lbuf, inf);
  805.     cp = rindex(lbuf, '\n');
  806.     if (cp)
  807.         *cp = 0;
  808.     fseek(inf, saveftell, 0);
  809. }
  810.  
  811. free_tree(node)
  812. NODE    *node;
  813. {
  814.  
  815.     while (node) {
  816.         free_tree(node->right);
  817.         cfree(node);
  818.         node = node->left;
  819.     }
  820. }
  821.  
  822. add_node(node, cur_node)
  823.     NODE *node,*cur_node;
  824. {
  825.     register int dif;
  826.  
  827.     dif = strcmp(node->entry, cur_node->entry);
  828.     if (dif == 0) {
  829.         if (node->file == cur_node->file) {
  830.             if (!wflag) {
  831. fprintf(stderr,"Duplicate entry in file %s, line %d: %s\n",
  832.     node->file,lineno,node->entry);
  833. fprintf(stderr,"Second entry ignored\n");
  834.             }
  835.             return;
  836.         }
  837.         if (!cur_node->been_warned)
  838.             if (!wflag)
  839. fprintf(stderr,"Duplicate entry in files %s and %s: %s (Warning only)\n",
  840.     node->file, cur_node->file, node->entry);
  841.         cur_node->been_warned = TRUE;
  842.         return;
  843.     }
  844.  
  845.     if (dif < 0) {
  846.         if (cur_node->left != NULL)
  847.             add_node(node,cur_node->left);
  848.         else
  849.             cur_node->left = node;
  850.         return;
  851.     }
  852.     if (cur_node->right != NULL)
  853.         add_node(node,cur_node->right);
  854.     else
  855.         cur_node->right = node;
  856. }
  857.  
  858. put_entries(node)
  859. reg NODE    *node;
  860. {
  861.     reg char    *sp;
  862.  
  863.     if (node == NULL)
  864.         return;
  865.     put_entries(node->left);
  866.     if (xflag == 0)
  867.         if (node->f) {        /* a function */
  868.             fprintf(outf, "%s\t%s\t%c^",
  869.                 node->entry, node->file, searchar);
  870.             for (sp = node->pat; *sp; sp++)
  871.                 if (*sp == '\\')
  872.                     fprintf(outf, "\\\\");
  873.                 else if (*sp == searchar)
  874.                     fprintf(outf, "\\%c", searchar);
  875.                 else
  876.                     putc(*sp, outf);
  877.             fprintf(outf, "%c\n", searchar);
  878.         }
  879.         else {        /* a typedef; text pattern inadequate */
  880.             fprintf(outf, "%s\t%s\t%d\n",
  881.                 node->entry, node->file, node->lno);
  882.         }
  883.     else if (vflag)
  884.         fprintf(stdout, "%s %s %d\n",
  885.                 node->entry, node->file, (node->lno+63)/64);
  886.     else
  887.         fprintf(stdout, "%-16s%4d %-16s %s\n",
  888.             node->entry, node->lno, node->file, node->pat);
  889.     put_entries(node->right);
  890. }
  891. char    *dbp = lbuf;
  892. int    pfcnt;
  893.  
  894. PF_funcs(fi)
  895.     FILE *fi;
  896. {
  897.  
  898.     pfcnt = 0;
  899.     while (fgets(lbuf, sizeof(lbuf), fi)) {
  900.         lineno++;
  901.         dbp = lbuf;
  902.         if ( *dbp == '%' ) dbp++ ;    /* Ratfor escape to fortran */
  903.         while (isspace(*dbp))
  904.             dbp++;
  905.         if (*dbp == 0)
  906.             continue;
  907.         switch (*dbp |' ') {
  908.  
  909.           case 'i':
  910.             if (tail("integer"))
  911.                 takeprec();
  912.             break;
  913.           case 'r':
  914.             if (tail("real"))
  915.                 takeprec();
  916.             break;
  917.           case 'l':
  918.             if (tail("logical"))
  919.                 takeprec();
  920.             break;
  921.           case 'c':
  922.             if (tail("complex") || tail("character"))
  923.                 takeprec();
  924.             break;
  925.           case 'd':
  926.             if (tail("double")) {
  927.                 while (isspace(*dbp))
  928.                     dbp++;
  929.                 if (*dbp == 0)
  930.                     continue;
  931.                 if (tail("precision"))
  932.                     break;
  933.                 continue;
  934.             }
  935.             break;
  936.         }
  937.         while (isspace(*dbp))
  938.             dbp++;
  939.         if (*dbp == 0)
  940.             continue;
  941.         switch (*dbp|' ') {
  942.  
  943.           case 'f':
  944.             if (tail("function"))
  945.                 getit();
  946.             continue;
  947.           case 's':
  948.             if (tail("subroutine"))
  949.                 getit();
  950.             continue;
  951.           case 'p':
  952.             if (tail("program")) {
  953.                 getit();
  954.                 continue;
  955.             }
  956.             if (tail("procedure"))
  957.                 getit();
  958.             continue;
  959.         }
  960.     }
  961.     return (pfcnt);
  962. }
  963.  
  964. tail(cp)
  965.     char *cp;
  966. {
  967.     register int len = 0;
  968.  
  969.     while (*cp && (*cp&~' ') == ((*(dbp+len))&~' '))
  970.         cp++, len++;
  971.     if (*cp == 0) {
  972.         dbp += len;
  973.         return (1);
  974.     }
  975.     return (0);
  976. }
  977.  
  978. takeprec()
  979. {
  980.  
  981.     while (isspace(*dbp))
  982.         dbp++;
  983.     if (*dbp != '*')
  984.         return;
  985.     dbp++;
  986.     while (isspace(*dbp))
  987.         dbp++;
  988.     if (!isdigit(*dbp)) {
  989.         --dbp;        /* force failure */
  990.         return;
  991.     }
  992.     do
  993.         dbp++;
  994.     while (isdigit(*dbp));
  995. }
  996.  
  997. getit()
  998. {
  999.     register char *cp;
  1000.     char c;
  1001.     char nambuf[BUFSIZ];
  1002.  
  1003.     for (cp = lbuf; *cp; cp++)
  1004.         ;
  1005.     *--cp = 0;    /* zap newline */
  1006.     while (isspace(*dbp))
  1007.         dbp++;
  1008.     if (*dbp == 0 || !isalpha(*dbp))
  1009.         return;
  1010.     for (cp = dbp+1; *cp && (isalpha(*cp) || isdigit(*cp)); cp++)
  1011.         continue;
  1012.     c = cp[0];
  1013.     cp[0] = 0;
  1014.     strcpy(nambuf, dbp);
  1015.     cp[0] = c;
  1016.     pfnote(nambuf, lineno);
  1017.     pfcnt++;
  1018. }
  1019.  
  1020. char *
  1021. savestr(cp)
  1022.     char *cp;
  1023. {
  1024.     register int len;
  1025.     register char *dp;
  1026.  
  1027.     len = strlen(cp);
  1028.     dp = (char *)malloc(len+1);
  1029.     strcpy(dp, cp);
  1030.     return (dp);
  1031. }
  1032.  
  1033. /*
  1034.  * Return the ptr in sp at which the character c last
  1035.  * appears; NULL if not found
  1036.  *
  1037.  * Identical to v7 rindex, included for portability.
  1038.  */
  1039.  
  1040. char *
  1041. rindex(sp, c)
  1042. register char *sp, c;
  1043. {
  1044.     register char *r;
  1045.  
  1046.     r = NULL;
  1047.     do {
  1048.         if (*sp == c)
  1049.             r = sp;
  1050.     } while (*sp++);
  1051.     return(r);
  1052. }
  1053.  
  1054. /*
  1055.  * lisp tag functions
  1056.  * just look for (def or (DEF
  1057.  */
  1058.  
  1059. L_funcs (fi)
  1060. FILE *fi;
  1061. {
  1062.     register int    special;
  1063.  
  1064.     pfcnt = 0;
  1065.     while (fgets(lbuf, sizeof(lbuf), fi)) {
  1066.         lineno++;
  1067.         dbp = lbuf;
  1068.         if (dbp[0] == '(' &&
  1069.             (dbp[1] == 'D' || dbp[1] == 'd') &&
  1070.             (dbp[2] == 'E' || dbp[2] == 'e') &&
  1071.             (dbp[3] == 'F' || dbp[3] == 'f')) {
  1072.             dbp += 4;
  1073.             if (striccmp(dbp, "method") == 0 ||
  1074.                 striccmp(dbp, "wrapper") == 0 ||
  1075.                 striccmp(dbp, "whopper") == 0)
  1076.                 special = TRUE;
  1077.             else
  1078.                 special = FALSE;
  1079.             while (!isspace(*dbp))
  1080.                 dbp++;
  1081.             while (isspace(*dbp))
  1082.                 dbp++;
  1083.             L_getit(special);
  1084.         }
  1085.     }
  1086. }
  1087.  
  1088. L_getit(special)
  1089. int    special;
  1090. {
  1091.     register char    *cp;
  1092.     register char    c;
  1093.     char        nambuf[BUFSIZ];
  1094.  
  1095.     for (cp = lbuf; *cp; cp++)
  1096.         continue;
  1097.     *--cp = 0;        /* zap newline */
  1098.     if (*dbp == 0)
  1099.         return;
  1100.     if (special) {
  1101.         if ((cp = index(dbp, ')')) == NULL)
  1102.             return;
  1103.         while (cp >= dbp && *cp != ':')
  1104.             cp--;
  1105.         if (cp < dbp)
  1106.             return;
  1107.         dbp = cp;
  1108.         while (*cp && *cp != ')' && *cp != ' ')
  1109.             cp++;
  1110.     }
  1111.     else
  1112.         for (cp = dbp + 1; *cp && *cp != '(' && *cp != ' '; cp++)
  1113.             continue;
  1114.     c = cp[0];
  1115.     cp[0] = 0;
  1116.     strcpy(nambuf, dbp);
  1117.     cp[0] = c;
  1118.     pfnote(nambuf, lineno,TRUE);
  1119.     pfcnt++;
  1120. }
  1121.  
  1122. /*
  1123.  * striccmp:
  1124.  *    Compare two strings over the length of the second, ignoring
  1125.  *    case distinctions.  If they are the same, return 0.  If they
  1126.  *    are different, return the difference of the first two different
  1127.  *    characters.  It is assumed that the pattern (second string) is
  1128.  *    completely lower case.
  1129.  */
  1130. striccmp(str, pat)
  1131. register char    *str, *pat;
  1132. {
  1133.     register int    c1;
  1134.  
  1135.     while (*pat) {
  1136.         if (isupper(*str))
  1137.             c1 = tolower(*str);
  1138.         else
  1139.             c1 = *str;
  1140.         if (c1 != *pat)
  1141.             return c1 - *pat;
  1142.         pat++;
  1143.         str++;
  1144.     }
  1145.     return 0;
  1146. }
  1147.  
  1148. /*
  1149.  * first_char:
  1150.  *    Return the first non-blank character in the file.  After
  1151.  *    finding it, rewind the input file so we start at the beginning
  1152.  *    again.
  1153.  */
  1154. first_char()
  1155. {
  1156.     register int    c;
  1157.     register long    off;
  1158.  
  1159.     off = ftell(inf);
  1160.     while ((c = getc(inf)) != EOF)
  1161.         if (!isspace(c) && c != '\r') {
  1162.             fseek(inf, off, 0);
  1163.             return c;
  1164.         }
  1165.     fseek(inf, off, 0);
  1166.     return EOF;
  1167. }
  1168.  
  1169. /*
  1170.  * toss_yysec:
  1171.  *    Toss away code until the next "%%" line.
  1172.  */
  1173. toss_yysec()
  1174. {
  1175.     char        buf[BUFSIZ];
  1176.  
  1177.     for (;;) {
  1178.         lineftell = ftell(inf);
  1179.         if (fgets(buf, BUFSIZ, inf) == NULL)
  1180.             return;
  1181.         lineno++;
  1182.         if (strncmp(buf, "%%", 2) == 0)
  1183.             return;
  1184.     }
  1185. }
  1186. SHAR_EOF
  1187. #    End of shell archive
  1188. exit 0
  1189.  
  1190.