rework
[unix-history] / usr / src / usr.bin / ctags / ctags.c
CommitLineData
c30ca4f4 1/*
0404816e 2 * Copyright (c) 1980 Regents of the University of California.
c30ca4f4
KB
3 * All rights reserved. The Berkeley software License Agreement
4 * specifies the terms and conditions for redistribution.
5 */
6
7#ifndef lint
8char copyright[] =
0404816e 9"@(#) Copyright (c) 1980 Regents of the University of California.\n\
c30ca4f4
KB
10 All rights reserved.\n";
11#endif not lint
12
13#ifndef lint
0404816e 14static char sccsid[] = "@(#)ctags.x 5.1 (Berkeley) 10/22/88";
c30ca4f4 15#endif not lint
2e314d68 16
0404816e
KB
17#include <sys/types.h>
18#include <sys/file.h>
19#include <stdio.h>
20#include <ctype.h>
e99c8356 21#include <strings.h>
16bc359f
KB
22
23/*
9927b527 24 * ctags: create a tags file
16bc359f
KB
25 */
26
0404816e
KB
27#define bool char
28
29#define YES 1
30#define NO 0
31#define EOS '\0'
32#define MAXTOKEN 250 /* max size of single token */
33#define SETLINE {++lineno;lineftell = ftell(inf);}
34
35#define iswhite(arg) (_wht[arg]) /* T if char is white */
36#define begtoken(arg) (_btk[arg]) /* T if char can start token */
37#define intoken(arg) (_itk[arg]) /* T if char can be in token */
38#define endtoken(arg) (_etk[arg]) /* T if char ends tokens */
39#define isgood(arg) (_gd[arg]) /* T if char can be after ')' */
40
41typedef struct nd_st { /* sorting structure */
42 struct nd_st *left,
43 *right; /* left and right sons */
44 char *entry, /* function or type name */
45 *file, /* file name */
46 *pat; /* search pattern */
47 int lno; /* for -x option */
48 bool been_warned; /* set if noticed dup */
49} NODE;
e99c8356 50NODE *head; /* head of the sorted binary tree */
16bc359f 51
e99c8356
KB
52 /* boolean "func" (see init()) */
53bool _wht[0177],_etk[0177],_itk[0177],_btk[0177],_gd[0177];
16bc359f 54
e99c8356
KB
55FILE *inf, /* ioptr for current input file */
56 *outf; /* ioptr for tags file */
16bc359f 57
e99c8356 58long lineftell; /* ftell after getc( inf ) == '\n' */
16bc359f 59
e99c8356 60int lineno, /* line number of current line */
0404816e 61 aflag, /* -a: append to tags */
e99c8356
KB
62 dflag, /* -d: non-macro defines */
63 tflag, /* -t: create tags for typedefs */
0404816e 64 uflag, /* -u: update tags */
e99c8356
KB
65 wflag, /* -w: suppress warnings */
66 vflag, /* -v: vgrind style index output */
67 xflag; /* -x: cxref style output */
16bc359f 68
e99c8356
KB
69char *curfile, /* current input file name */
70 searchar = '/', /* use /.../ searches by default */
0404816e 71 line[4*BUFSIZ], /* current input line */
e99c8356 72 lbuf[BUFSIZ];
16bc359f 73
e99c8356
KB
74main(argc,argv)
75 int argc;
76 char **argv;
16bc359f 77{
e99c8356
KB
78 extern char *optarg; /* getopt arguments */
79 extern int optind;
80 static char *outfile = "tags"; /* output file */
0404816e 81 int exit_val, /* exit value */
e99c8356
KB
82 step, /* step through args */
83 ch; /* getopts char */
0404816e
KB
84 char cmd[100], /* too ugly to explain */
85 *savestr();
16bc359f 86
e99c8356
KB
87 while ((ch = getopt(argc,argv,"BFadf:tuwvx")) != EOF)
88 switch((char)ch) {
89 case 'B':
90 searchar = '?';
c30ca4f4 91 break;
e99c8356
KB
92 case 'F':
93 searchar = '/';
c30ca4f4 94 break;
e99c8356 95 case 'a':
c30ca4f4
KB
96 aflag++;
97 break;
e99c8356
KB
98 case 'd':
99 dflag++;
100 break;
101 case 'f':
102 outfile = optarg;
103 break;
104 case 't':
c30ca4f4
KB
105 tflag++;
106 break;
e99c8356 107 case 'u':
c30ca4f4
KB
108 uflag++;
109 break;
e99c8356 110 case 'w':
c30ca4f4
KB
111 wflag++;
112 break;
e99c8356 113 case 'v':
c30ca4f4 114 vflag++;
e99c8356 115 case 'x':
c30ca4f4
KB
116 xflag++;
117 break;
e99c8356
KB
118 case '?':
119 default:
c30ca4f4 120 goto usage;
16bc359f 121 }
e99c8356
KB
122 argv += optind;
123 argc -= optind;
124 if (!argc) {
125usage: puts("Usage: ctags [-BFadtuwvx] [-f tagsfile] file ...");
16bc359f
KB
126 exit(1);
127 }
128
e99c8356 129 init();
16bc359f 130
0404816e
KB
131 for (exit_val = step = 0;step < argc;++step) {
132 curfile = savestr(argv[step]);
e99c8356
KB
133 if (!(inf = fopen(argv[step],"r"))) {
134 perror(argv[step]);
135 exit_val = 1;
16bc359f 136 }
0404816e 137 else
e99c8356 138 find_entries(argv[step]);
0404816e 139 }
16bc359f 140
0404816e
KB
141 if (xflag) {
142 put_entries(head);
143 exit(exit_val);
144 }
145 if (uflag) {
146 for (step = 0;step < argc;step++) {
147 (void)sprintf(cmd,"mv %s OTAGS;fgrep -v '\t%s\t' OTAGS >%s;rm OTAGS",outfile,argv[step],outfile);
148 system(cmd);
16bc359f 149 }
0404816e
KB
150 ++aflag;
151 }
152 outf = fopen(outfile, aflag ? "a" : "w");
153 if (!outf) {
154 perror(outfile);
155 exit(exit_val);
156 }
157 put_entries(head);
158 (void)fclose(outf);
159 if (uflag) {
160 (void)sprintf(cmd,"sort %s -o %s",outfile,outfile);
161 system(cmd);
162 }
e99c8356 163 exit(exit_val);
16bc359f
KB
164}
165
166/*
e99c8356
KB
167 * init --
168 * this routine sets up the boolean psuedo-functions which work by
169 * setting boolean flags dependent upon the corresponding character.
170 * Every char which is NOT in that string is false with respect to
171 * the pseudo-function. Therefore, all of the array "_wht" is NO
172 * by default and then the elements subscripted by the chars in
173 * CWHITE are set to YES. Thus, "_wht" of a char is YES if it is in
174 * the string CWHITE, else NO.
16bc359f 175 */
e99c8356 176init()
16bc359f 177{
e99c8356
KB
178 register int i;
179 register char *sp;
16bc359f 180
e99c8356
KB
181 for (i = 0; i < 0177; i++) {
182 _wht[i] = _etk[i] = _itk[i] = _btk[i] = NO;
183 _gd[i] = YES;
184 }
185#define CWHITE " \f\t\n"
186 for (sp = CWHITE; *sp; sp++) /* white space chars */
187 _wht[*sp] = YES;
188#define CTOKEN " \t\n\"'#()[]{}=-+%*/&|^~!<>;,.:?"
189 for (sp = CTOKEN; *sp; sp++) /* token ending chars */
190 _etk[*sp] = YES;
191#define CINTOK "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz0123456789"
192 for (sp = CINTOK; *sp; sp++) /* valid in-token chars */
193 _itk[*sp] = YES;
194#define CBEGIN "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"
195 for (sp = CBEGIN; *sp; sp++) /* token starting chars */
196 _btk[*sp] = YES;
197#define CNOTGD ",;"
198 for (sp = CNOTGD; *sp; sp++) /* invalid after-function chars */
199 _gd[*sp] = NO;
16bc359f
KB
200}
201
c30ca4f4 202/*
e99c8356
KB
203 * find_entries --
204 * this routine opens the specified file and calls the function
205 * which searches the file.
c30ca4f4 206 */
e99c8356
KB
207find_entries(file)
208 char *file;
c30ca4f4 209{
e99c8356 210 register char *cp;
c30ca4f4 211
e99c8356
KB
212 lineno = 0; /* should be 1 ?? KB */
213 if (cp = rindex(file, '.')) {
214 if (cp[1] == 'l' && !cp[2]) {
0404816e
KB
215/* lisp */ if (index(";([",(char)first_char())) {
216 L_funcs();
217 goto done;
16bc359f 218 }
e99c8356 219/* lex */ else {
96d022f7 220 toss_yysec();
0404816e 221 getline();
96d022f7 222 pfnote("yylex",lineno);
0404816e 223 toss_yysec();
f5c61c07 224 }
f5c61c07 225 }
0404816e
KB
226/* yacc */ else if (cp[1] == 'y' && !cp[2])
227 Y_entries();
228/* fortran */ else if ((cp[1] != 'c' && cp[1] != 'h') && !cp[2]) {
229 if (PF_funcs())
230 goto done;
231 rewind(inf);
232 }
233 }
234 c_entries(); /* default: try C */
235done: (void)fclose(inf);
236}
237
238/*
239 * pfnote --
240 * enter a new node in the tree
241 */
242pfnote(name,ln)
243 char *name;
244 int ln;
245{
246 register NODE *np;
247 register char *fp;
248 char nbuf[BUFSIZ],
249 *malloc(), *savestr();
250
251 if (!(np = (NODE *)malloc(sizeof(NODE)))) {
252 fputs("ctags: too many entries to sort\n",stderr);
253 put_entries(head);
254 free_tree(head);
255 if (!(np = (NODE *)malloc(sizeof(NODE)))) {
256 fputs("ctags: out of space.\n",stderr);
257 exit(1);
258 }
259 head = np;
260 }
261 if (!xflag && !bcmp(name,"main",4)) {
262 if (!(fp = rindex(curfile,'/')))
263 fp = curfile;
264 else
265 ++fp;
266 (void)sprintf(nbuf,"M%s",fp);
267 fp = rindex(nbuf,'.');
268 if (fp && !fp[2])
269 *fp = 0;
270 name = nbuf;
271 }
272 np->entry = savestr(name);
273 np->file = curfile;
274 np->lno = ln;
275 np->left = np->right = 0;
276 if (!xflag) {
277 lbuf[50] = 0;
278 (void)strcat(lbuf,"$");
279 lbuf[50] = 0;
280 }
281 np->pat = savestr(lbuf);
282 if (!head)
283 head = np;
284 else
285 add_node(np, head);
286}
287
288/*
289 * c_entries --
290 * read .c and .h files and call appropriate routines
291 */
292c_entries()
293{
294 register int c, /* current character */
295 level; /* brace level */
296 register char *sp; /* buffer pointer */
297 int token, /* if reading a token */
298 t_def, /* if reading a typedef */
299 t_level; /* typedef's brace level */
300 char tok[MAXTOKEN]; /* token buffer */
301
302 lineftell = ftell(inf);
303 sp = tok; token = t_def = NO; t_level = -1; level = 0; lineno = 1;
304 while ((c = getc(inf)) != EOF) {
305
306 switch ((char)c) {
307 /*
308 * Here's where it DOESN'T handle:
309 * foo(a)
310 * {
311 * #ifdef notdef
312 * }
313 * #endif
314 * if (a)
315 * puts("hello, world");
316 * }
317 */
318 case '{':
319 ++level;
320 goto endtok;
321 case '}':
e99c8356 322 /*
0404816e
KB
323 * if level goes below zero, try and fix
324 * it, even though we've already messed up
e99c8356 325 */
0404816e
KB
326 if (--level < 0)
327 level = 0;
328 goto endtok;
329
330 case '\n':
331 SETLINE;
332 /*
333 * the above 3 cases are similar in that they
334 * are special characters that also end tokens.
335 */
336endtok: if (sp > tok) {
337 *sp = EOS;
338 token = YES;
339 sp = tok;
340 }
341 else
342 token = NO;
343 continue;
344
345 /* we ignore quoted strings and comments in their entirety */
346 case '"':
347 case '\'':
348 skip_key(c);
349 break;
350 case '/':
351 if ((c = getc(inf)) == '*') {
352 skip_comment();
353 break;
354 }
355 (void)ungetc(c,inf);
356 c = '/';
357 goto storec;
358
359 /* hash marks are interesting if they start #define's. */
360 case '#':
361 if (sp == tok) {
362 hash_entry();
363 break;
364 }
365 goto storec;
366
367 /*
368 * if we have a current token, parenthesis on
369 * level zero indicates a function.
370 */
371 case '(':
372 if (!level && token) {
373 int curline;
374
375 if (sp != tok)
376 *sp = EOS;
377 /*
378 * grab the line immediately, we may
379 * already be wrong, for example,
380 * foo
381 * (arg1,
382 */
383 getline();
384 curline = lineno;
385 if (func_entry()) {
386 ++level;
387 pfnote(tok,curline);
388 }
389 break;
390 }
391 goto storec;
392
393 /*
394 * semi-colons are interesting in that they indicate the end
395 * of a typedef; if we find a typedef we search for the next
396 * semi-colon of the same level as the typedef. They are
397 * fairly tough, can be:
398 *
399 * "typedef long time_t;"
400 * "typedef unsigned int u_int;"
401 * "typedef unsigned int u_int [10];"
402 *
403 * If looking at a typedef, we save a copy of the last token
404 * found. Then, when we find the ';' we take the current
405 * token if it starts with a valid token name, else we take
406 * the one we saved. There's probably some reasonable
407 * alternative to this...
408 */
409 case ';':
410 if (t_def && level == t_level) {
411 t_def = NO;
412 getline();
413 if (sp != tok)
414 *sp = EOS;
415 pfnote(tok,lineno);
416 break;
417 }
418 goto storec;
419
420 /*
421 * store characters until one that can't be part of a token
422 * comes along; check the current token against certain
423 * reserved words.
424 */
425 default:
426storec: if (!intoken(c)) {
427 if (sp == tok)
428 break;
429 *sp = EOS;
430 if (tflag) {
431 /* no typedefs inside typedefs */
432 if (!t_def && !bcmp(tok,"typedef",8)) {
433 t_def = YES;
434 t_level = level;
435 break;
436 }
437 /* catch "typedef struct" */
438 if ((!t_def || t_level < level)
439 && (!bcmp(tok,"struct",7)
440 || !bcmp(tok,"union",6)
441 || !bcmp(tok,"enum",5))) {
442 /*
443 * get line immediately;
444 * may change before '{'
445 */
446 getline();
447 if (str_entry(c))
448 ++level;
449 break;
450 }
451 }
452 sp = tok;
453 }
454 else if (sp != tok || begtoken(c)) {
455 *sp++ = c;
456 token = YES;
457 }
458 continue;
c30ca4f4 459 }
0404816e
KB
460 sp = tok;
461 token = NO;
462 }
463}
464
465/*
466 * func_entry --
467 * handle a function reference
468 */
469func_entry()
470{
471 register int c; /* current character */
472
473 skip_key((int)')');
474 while ((c = getc(inf)) != EOF && iswhite(c))
475 if (c == (int)'\n')
476 SETLINE;
477 if (!intoken(c) && c != (int)'{')
478 return(NO);
479 if (c != (int)'{')
480 skip_key((int)'{');
481 return(YES);
482}
483
484/*
485 * hash_entry --
486 * handle a line starting with a '#'
487 */
488hash_entry()
489{
490 register int c, /* character read */
491 curline; /* line started on */
492 register char *sp; /* buffer pointer */
493 char tok[MAXTOKEN]; /* storage buffer */
494
495 curline = lineno;
496 for (sp = tok;;) { /* get next token */
497 if ((c = getc(inf)) == EOF)
498 return;
499 if (iswhite(c))
500 break;
501 *sp++ = c;
502 }
503 *sp = EOS;
504 if (bcmp(tok,"define",6)) /* only interested in #define's */
505 goto skip;
506 for (;;) { /* this doesn't handle "#define \n" */
507 if ((c = getc(inf)) == EOF)
508 return;
509 if (!iswhite(c))
510 break;
511 }
512 for (sp = tok;;) { /* get next token */
513 *sp++ = c;
514 if ((c = getc(inf)) == EOF)
515 return;
516 /*
517 * this is where it DOESN'T handle
518 * "#define \n"
519 */
520 if (!intoken(c))
521 break;
522 }
523 *sp = EOS;
524 if (dflag || c == (int)'(') { /* only want macros */
525 getline();
526 pfnote(tok,curline);
527 }
528skip: if (c == (int)'\n') { /* get rid of rest of define */
529 SETLINE
530 if (*(sp - 1) != '\\')
531 return;
532 }
533 skip_line();
534}
535
536/*
537 * str_entry --
538 * handle a struct, union or enum entry
539 */
540str_entry(c)
541 register int c; /* current character */
542{
543 register char *sp; /* buffer pointer */
544 int curline; /* line started on */
545 char tok[BUFSIZ]; /* storage buffer */
546
547 curline = lineno;
548 while (iswhite(c))
549 if ((c = getc(inf)) == EOF)
550 return(NO);
551 if (c == (int)'{') /* it was "struct {" */
552 return(YES);
553 for (sp = tok;;) { /* get next token */
554 *sp++ = c;
555 if ((c = getc(inf)) == EOF)
556 return(NO);
557 if (!intoken(c))
558 break;
559 }
560 switch ((char)c) {
561 case '{': /* it was "struct foo{" */
562 --sp;
563 break;
564 case '\n': /* it was "struct foo\n" */
565 SETLINE;
566 /*FALLTHROUGH*/
567 default: /* probably "struct foo " */
568 while ((c = getc(inf)) != EOF)
569 if (!iswhite(c))
570 break;
571 if (c != (int)'{')
572 return(NO);
573 }
574 *sp = EOS;
575 pfnote(tok,curline);
576 return(YES);
577}
578
579/*
580 * skip_line --
581 * skip to next line
582 */
583skip_line()
584{
585 register int c,
586 savec;
587
588 for (savec = '\0';(c = getc(inf)) != EOF;savec = c)
589 if (c == (int)'\n') {
590 SETLINE;
591 if (savec != (int)'\\')
e99c8356 592 return;
c30ca4f4 593 }
0404816e
KB
594}
595
596/*
597 * skip_key --
598 * skip to next char "key"
599 */
600skip_key(key)
601 register int key;
602{
603 register int c;
604
605 while((c = getc(inf)) != EOF && c != key)
606 if (c == (int)'\n')
607 SETLINE;
608}
609
610/*
611 * skip_comment --
612 * skip over comment
613 */
614skip_comment()
615{
616 register int c,
617 level,
618 star,
619 slash;
620
621 for (level = 1,slash = star = 0;(c = getc(inf)) != EOF;)
622 switch((char)c) {
623 case '*':
624 if (slash) {
625 ++level;
626 slash = 0;
627 }
628 else
629 ++star;
630 break;
631 case '/':
632 if (star) {
633 if (!--level)
634 return;
635 star = 0;
636 }
637 else
638 ++slash;
639 break;
640 case '\n':
641 SETLINE;
642 default:
643 slash = star = 0;
644 }
645}
646
647/*
648 * Y_entries:
649 * Find the yacc tags and put them in.
650 */
651Y_entries()
652{
653 register char *sp,
654 *orig_sp;
655 register int brace;
656 register bool in_rule,
657 toklen;
658 char tok[BUFSIZ],
659 *toss_comment();
660
661 toss_yysec();
662 brace = 0;
663 getline();
664 pfnote("yyparse",lineno);
665 while (fgets(line,sizeof(line),inf))
666 for (sp = line;*sp;++sp)
667 switch (*sp) {
668 case '\n':
669 lineno++;
670 /* FALLTHROUGH */
671 case ' ':
672 case '\t':
673 case '\f':
674 case '\r':
675 break;
676 case '"':
677 do {
678 while (*++sp != '"')
679 continue;
680 } while (sp[-1] == '\\');
681 break;
682 case '\'':
683 do {
684 while (*++sp != '\'')
685 continue;
686 } while (sp[-1] == '\\');
687 break;
688 case '/':
689 if (*++sp == '*')
690 sp = toss_comment(sp);
691 else
692 --sp;
693 break;
694 case '{':
695 brace++;
696 break;
697 case '}':
698 brace--;
699 break;
700 case '%':
701 if (sp[1] == '%' && sp == line)
702 return;
703 break;
704 case '|':
705 case ';':
706 in_rule = NO;
707 break;
708 default:
709 if (!brace && !in_rule && (isalpha(*sp) ||
710 *sp == '.' || *sp == '_')) {
711 orig_sp = sp;
712 ++sp;
713 while (isalnum(*sp) || *sp == '_' ||
714 *sp == '.')
715 sp++;
716 toklen = sp - orig_sp;
717 while (isspace(*sp))
718 sp++;
719 if (*sp == ':' || (*sp == '\0' &&
720 first_char() == ':')) {
721 (void)strncpy(tok, orig_sp, toklen);
722 tok[toklen] = '\0';
723 (void)strcpy(lbuf, line);
724 lbuf[strlen(lbuf) - 1] = '\0';
725 pfnote(tok,lineno);
726 in_rule = YES;
727 }
728 else
729 sp--;
730 }
731 break;
732 }
733}
734
735char *
736toss_comment(start)
737 char *start;
738{
739 register char *sp;
740
741 /*
742 * first, see if the end-of-comment is on the same line
743 */
744 do {
745 while ((sp = index(start,'*')))
746 if (sp[1] == '/')
747 return(++sp);
748 else
749 start = ++sp;
750 start = line;
751 lineno++;
752 } while(fgets(line,sizeof(line),inf));
753}
754
755/*
756 * getline --
757 * get the line the token of interest occurred on
758 */
759getline()
760{
761 register char *cp;
762 long saveftell;
763
764 saveftell = ftell(inf);
765 fseek(inf,lineftell,L_SET);
766 (void)fgets(lbuf,sizeof(lbuf),inf);
767 if (cp = index(lbuf,'\n'))
768 *cp = '\0';
769 fseek(inf,saveftell,L_SET);
770}
771
772free_tree(node)
773 register NODE *node;
774{
775 while (node) {
776 free_tree(node->right);
777 cfree(node);
778 node = node->left;
779 }
780}
781
782add_node(node,cur_node)
783 register NODE *node,
784 *cur_node;
785{
786 register int dif;
787
788 dif = strcmp(node->entry,cur_node->entry);
789 if (!dif) {
790 if (node->file == cur_node->file) {
791 if (!wflag)
792 fprintf(stderr,"Duplicate entry in file %s, line %d: %s\nSecond entry ignored\n",node->file,lineno,node->entry);
793 return;
794 }
795 if (!cur_node->been_warned)
796 if (!wflag)
797 fprintf(stderr,"Duplicate entry in files %s and %s: %s (Warning only)\n",node->file,cur_node->file,node->entry);
798 cur_node->been_warned = YES;
799 }
800 else if (dif < 0)
801 if (cur_node->left)
802 add_node(node,cur_node->left);
803 else
804 cur_node->left = node;
805 else if (cur_node->right)
806 add_node(node,cur_node->right);
807 else
808 cur_node->right = node;
809}
810
811/*
812 * put_entries --
813 * write out the tags
814 */
815put_entries(node)
816 register NODE *node;
817{
818 register char *sp;
819
820 if (!node)
821 return;
822 put_entries(node->left);
823 if (!xflag) {
824 fprintf(outf,"%s\t%s\t%c^",node->entry,node->file,searchar);
825 for (sp = node->pat; *sp; sp++)
826 if (*sp == '\\')
827 fputs("\\\\",outf);
828 else if (*sp == searchar)
829 fprintf(outf,"\\%c",searchar);
830 else
831 putc(*sp,outf);
832 fprintf(outf,"%c\n",searchar);
833 }
834 else if (vflag)
835 printf("%s %s %d\n",node->entry,node->file,(node->lno+63)/64);
836 else
837 printf("%-16s%4d %-16s %s\n",node->entry,node->lno,node->file,node->pat);
838 put_entries(node->right);
839}
840
841char *dbp = lbuf;
842int pfcnt;
843
844PF_funcs()
845{
846 pfcnt = 0;
847 while (fgets(lbuf, sizeof(lbuf), inf)) {
848 lineno++;
849 dbp = lbuf;
850 if ( *dbp == '%' ) dbp++ ; /* Ratfor escape to fortran */
851 while (isspace(*dbp))
852 dbp++;
853 if (*dbp == 0)
854 continue;
855 switch (*dbp |' ') {
856
857 case 'i':
858 if (tail("integer"))
859 takeprec();
860 break;
861 case 'r':
862 if (tail("real"))
863 takeprec();
864 break;
865 case 'l':
866 if (tail("logical"))
867 takeprec();
868 break;
869 case 'c':
870 if (tail("complex") || tail("character"))
871 takeprec();
872 break;
873 case 'd':
874 if (tail("double")) {
875 while (isspace(*dbp))
876 dbp++;
877 if (*dbp == 0)
878 continue;
879 if (tail("precision"))
880 break;
881 continue;
882 }
883 break;
884 }
885 while (isspace(*dbp))
886 dbp++;
887 if (*dbp == 0)
888 continue;
889 switch (*dbp|' ') {
890
891 case 'f':
892 if (tail("function"))
893 getit();
894 continue;
895 case 's':
896 if (tail("subroutine"))
897 getit();
898 continue;
899 case 'p':
900 if (tail("program")) {
901 getit();
902 continue;
903 }
904 if (tail("procedure"))
905 getit();
906 continue;
907 }
908 }
909 return (pfcnt);
910}
911
912tail(cp)
913 char *cp;
914{
915 register int len = 0;
916
917 while (*cp && (*cp&~' ') == ((*(dbp+len))&~' '))
918 cp++, len++;
919 if (*cp == 0) {
920 dbp += len;
921 return (1);
922 }
923 return (0);
924}
925
926takeprec()
927{
928 while (isspace(*dbp))
929 dbp++;
930 if (*dbp != '*')
931 return;
932 dbp++;
933 while (isspace(*dbp))
934 dbp++;
935 if (!isdigit(*dbp)) {
936 --dbp; /* force failure */
937 return;
938 }
939 do
940 dbp++;
941 while (isdigit(*dbp));
942}
943
944getit()
945{
946 register char *cp;
947 char c;
948 char nambuf[BUFSIZ];
949
950 for (cp = lbuf; *cp; cp++)
951 ;
952 *--cp = 0; /* zap newline */
953 while (isspace(*dbp))
954 dbp++;
955 if (*dbp == 0 || !isalpha(*dbp))
956 return;
957 for (cp = dbp+1; *cp && (isalpha(*cp) || isdigit(*cp)); cp++)
958 continue;
959 c = cp[0];
960 cp[0] = 0;
961 (void)strcpy(nambuf, dbp);
962 cp[0] = c;
963 pfnote(nambuf, lineno);
964 pfcnt++;
965}
966
967char *
968savestr(str)
969 char *str;
970{
971 register char *space;
972 char *malloc();
973
974 if (!(space = malloc((u_int)(strlen(str) + 1)))) {
975 fputs("ctags: no more space.\n",stderr);
976 exit(1);
977 }
978 return(strcpy(space,str));
979}
980
981/*
982 * lisp tag functions
983 * just look for (def or (DEF
984 */
985L_funcs()
986{
987 register int special;
988
989 pfcnt = 0;
990 while (fgets(lbuf, sizeof(lbuf), inf)) {
991 lineno++;
992 dbp = lbuf;
993 if (dbp[0] == '(' &&
994 (dbp[1] == 'D' || dbp[1] == 'd') &&
995 (dbp[2] == 'E' || dbp[2] == 'e') &&
996 (dbp[3] == 'F' || dbp[3] == 'f')) {
997 dbp += 4;
998 if (striccmp(dbp, "method") == 0 ||
999 striccmp(dbp, "wrapper") == 0 ||
1000 striccmp(dbp, "whopper") == 0)
1001 special = YES;
1002 else
1003 special = NO;
1004 while (!isspace(*dbp))
1005 ++dbp;
1006 while (isspace(*dbp))
1007 ++dbp;
1008 L_getit(special);
1009 }
1010 }
1011}
1012
1013L_getit(special)
1014 int special;
1015{
1016 register char *cp,
1017 c;
1018 char nambuf[BUFSIZ];
1019
1020 for (cp = lbuf;*cp;cp++);
1021 *--cp = 0; /* zap newline */
1022 if (!*dbp)
1023 return;
1024 if (special) {
1025 if (!(cp = index(dbp,')')))
1026 return;
1027 for (;cp >= dbp && *cp != ':';--cp);
1028 if (cp < dbp)
1029 return;
1030 dbp = cp;
1031 for (;*cp && *cp != ')' && *cp != ' ';++cp);
1032 }
1033 else
1034 for (cp = dbp + 1; *cp && *cp != '(' && *cp != ' '; cp++);
1035 c = cp[0];
1036 cp[0] = 0;
1037 (void)strcpy(nambuf,dbp);
1038 cp[0] = c;
1039 pfnote(nambuf,lineno);
1040 ++pfcnt;
1041}
1042
1043/*
1044 * striccmp:
1045 * Compare two strings over the length of the second, ignoring
1046 * case distinctions. If they are the same, return 0. If they
1047 * are different, return the difference of the first two different
1048 * characters. It is assumed that the pattern (second string) is
1049 * completely lower case.
1050 */
1051striccmp(str, pat)
1052 register char *str, *pat;
1053{
1054 register int c1;
1055
1056 while (*pat) {
1057 if (isupper(*str))
1058 c1 = tolower(*str);
1059 else
1060 c1 = *str;
1061 if (c1 != *pat)
1062 return c1 - *pat;
1063 pat++;
1064 str++;
1065 }
1066 return 0;
1067}
1068
1069/*
1070 * first_char --
1071 * return the next non-blank character in the file. After finding
1072 * it, rewind the input file to the starting position.
1073 */
1074first_char()
1075{
1076 register int c;
1077 register long off;
1078
1079 off = ftell(inf);
1080 while ((c = getc(inf)) != EOF)
1081 if (!iswhite(c)) {
1082 (void)fseek(inf,off,L_SET);
1083 return(c);
1084 }
1085 (void)fseek(inf,off,L_SET);
1086 return(EOF);
1087}
1088
1089/*
1090 * toss_yysec --
1091 * toss away code until the next "%%" line.
1092 */
1093toss_yysec()
1094{
1095 char buf[BUFSIZ];
1096
1097 for (;;) {
1098 lineftell = ftell(inf);
1099 if (!fgets(buf,sizeof(buf),inf))
1100 return;
1101 ++lineno;
1102 if (!strncmp(buf,"%%",2))
1103 return;
c30ca4f4
KB
1104 }
1105}