pass DESTDIR when installing filters
[unix-history] / usr / src / usr.bin / ctags / ctags.c
CommitLineData
9927b527 1static char *sccsid = "@(#)ctags.c 4.3 (Berkeley) 11/24/80";
f5c61c07
KB
2#include <stdio.h>
3#include <ctype.h>
16bc359f
KB
4
5/*
9927b527 6 * ctags: create a tags file
16bc359f
KB
7 */
8
9#define reg register
10#define logical char
11
12#define TRUE (1)
13#define FALSE (0)
14
15#define iswhite(arg) (_wht[arg]) /* T if char is white */
16#define begtoken(arg) (_btk[arg]) /* T if char can start token */
17#define intoken(arg) (_itk[arg]) /* T if char can be in token */
18#define endtoken(arg) (_etk[arg]) /* T if char ends tokens */
19#define isgood(arg) (_gd[arg]) /* T if char can be after ')' */
20
21#define max(I1,I2) (I1 > I2 ? I1 : I2)
22
23struct nd_st { /* sorting structure */
24 char *func; /* function name */
25 char *file; /* file name */
f5c61c07 26 int lno; /* for -x option */
16bc359f
KB
27 char *pat; /* search pattern */
28 logical been_warned; /* set if noticed dup */
29 struct nd_st *left,*right; /* left and right sons */
30};
31
32long ftell();
16bc359f
KB
33typedef struct nd_st NODE;
34
35logical number, /* T if on line starting with # */
36 term = FALSE, /* T if print on terminal */
37 makefile= TRUE, /* T if to creat "tags" file */
38 gotone, /* found a func already on line */
39 /* boolean "func" (see init) */
40 _wht[0177],_etk[0177],_itk[0177],_btk[0177],_gd[0177];
41
42char searchar = '?'; /* use ?...? searches */
16bc359f
KB
43
44int lineno; /* line number of current line */
f5c61c07 45char line[4*BUFSIZ], /* current input line */
16bc359f
KB
46 *curfile, /* current input file name */
47 *outfile= "tags", /* output file */
48 *white = " \f\t\n", /* white chars */
49 *endtk = " \t\n\"'#()[]{}=-+%*/&|^~!<>;,.:?",
50 /* token ending chars */
51 *begtk = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz",
52 /* token starting chars */
53 *intk = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz0123456789", /* valid in-token chars */
54 *notgd = ",;"; /* non-valid after-function chars */
55
56int file_num; /* current file number */
57int aflag; /* -a: append to tags */
58int uflag; /* -u: update tags */
59int wflag; /* -w: suppress warnings */
9927b527 60int vflag; /* -v: create vgrind style index output */
f5c61c07
KB
61int xflag; /* -x: create cxref style output */
62
63char lbuf[BUFSIZ];
16bc359f
KB
64
65FILE *inf, /* ioptr for current input file */
66 *outf; /* ioptr for tags file */
67
68long lineftell; /* ftell after getc( inf ) == '\n' */
69
70NODE *head; /* the head of the sorted binary tree */
71
f5c61c07
KB
72char *savestr();
73char *rindex();
16bc359f
KB
74main(ac,av)
75int ac;
76char *av[];
77{
78 char cmd[100];
79 int i;
80
81 while (ac > 1 && av[1][0] == '-') {
82 for (i=1; av[1][i]; i++) {
83 switch(av[1][i]) {
84 case 'a':
85 aflag++;
86 break;
87 case 'u':
88 uflag++;
89 break;
90 case 'w':
91 wflag++;
92 break;
9927b527
KB
93 case 'v':
94 vflag++;
95 xflag++;
96 break;
f5c61c07
KB
97 case 'x':
98 xflag++;
99 break;
16bc359f
KB
100 default:
101 goto usage;
102 }
103 }
104 ac--; av++;
105 }
106
107 if (ac <= 1) {
108 usage: printf("Usage: ctags [-au] file ...\n");
109 exit(1);
110 }
111
112 init(); /* set up boolean "functions" */
113 /*
114 * loop through files finding functions
115 */
116 for (file_num = 1; file_num < ac; file_num++)
117 find_funcs(av[file_num]);
118
f5c61c07
KB
119 if (xflag) {
120 put_funcs(head);
121 exit(0);
122 }
16bc359f
KB
123 if (uflag) {
124 for (i=1; i<ac; i++) {
f5c61c07
KB
125 sprintf(cmd,
126 "mv %s OTAGS;fgrep -v '\t%s\t' OTAGS >%s;rm OTAGS",
127 outfile, av[i], outfile);
16bc359f
KB
128 system(cmd);
129 }
130 aflag++;
131 }
f5c61c07
KB
132 outf = fopen(outfile, aflag ? "a" : "w");
133 if (outf == NULL) {
16bc359f
KB
134 perror(outfile);
135 exit(1);
136 }
f5c61c07
KB
137 put_funcs(head);
138 fclose(outf);
139 if (uflag) {
140 sprintf(cmd, "sort %s -o %s", outfile, outfile);
141 system(cmd);
142 }
16bc359f
KB
143 exit(0);
144}
145
146/*
f5c61c07 147 * This routine sets up the boolean psuedo-functions which work
16bc359f 148 * by seting boolean flags dependent upon the corresponding character
16bc359f
KB
149 * Every char which is NOT in that string is not a white char. Therefore,
150 * all of the array "_wht" is set to FALSE, and then the elements
151 * subscripted by the chars in "white" are set to TRUE. Thus "_wht"
152 * of a char is TRUE if it is the string "white", else FALSE.
16bc359f
KB
153 */
154init()
155{
156
157 reg char *sp;
158 reg int i;
159
160 for (i = 0; i < 0177; i++) {
161 _wht[i] = _etk[i] = _itk[i] = _btk[i] = FALSE;
162 _gd[i] = TRUE;
163 }
164 for (sp = white; *sp; sp++)
165 _wht[*sp] = TRUE;
166 for (sp = endtk; *sp; sp++)
167 _etk[*sp] = TRUE;
168 for (sp = intk; *sp; sp++)
169 _itk[*sp] = TRUE;
170 for (sp = begtk; *sp; sp++)
171 _btk[*sp] = TRUE;
172 for (sp = notgd; *sp; sp++)
173 _gd[*sp] = FALSE;
174}
175
176/*
f5c61c07
KB
177 * This routine opens the specified file and calls the function
178 * which finds the function definitions.
16bc359f
KB
179 */
180find_funcs(file)
181char *file;
182{
f5c61c07 183 char *cp;
16bc359f
KB
184
185 if ((inf=fopen(file,"r")) == NULL) {
186 perror(file);
187 return;
188 }
f5c61c07
KB
189 curfile = savestr(file);
190 cp = rindex(file, '.');
191 if (cp && (cp[1] != 'c' || cp[1] != 'h') && cp[2] == 0) {
192 if (PF_funcs(inf) == 0) {
193 rewind(inf);
194 C_funcs();
195 }
196 } else
197 C_funcs();
16bc359f
KB
198 fclose(inf);
199}
200
f5c61c07
KB
201pfnote(name, ln)
202 char *name;
203{
204 register char *fp;
205 register NODE *np;
206 char nbuf[BUFSIZ];
207
208 if ((np = (NODE *) malloc(sizeof (NODE))) == NULL) {
209 fprintf(stderr, "ctags: too many functions to sort\n");
210 put_funcs(head);
211 free_tree(head);
212 head = np = (NODE *) malloc(sizeof (NODE));
213 }
214 if (xflag == 0 && !strcmp(name, "main")) {
215 fp = rindex(curfile, '/');
216 if (fp == 0)
217 fp = curfile;
218 else
219 fp++;
220 sprintf(nbuf, "M%s", fp);
221 fp = rindex(nbuf, '.');
222 if (fp && fp[2] == 0)
223 *fp = 0;
224 name = nbuf;
225 }
226 np->func = savestr(name);
227 np->file = curfile;
228 np->lno = ln;
229 np->left = np->right = 0;
230 if (xflag == 0) {
231 lbuf[50] = 0;
232 strcat(lbuf, "$");
233 lbuf[50] = 0;
234 }
235 np->pat = savestr(lbuf);
236 if (head == NULL)
237 head = np;
238 else
239 add_node(np, head);
240}
241
16bc359f 242/*
f5c61c07 243 * This routine finds functions in C syntax and adds them
16bc359f
KB
244 * to the list.
245 */
246C_funcs()
247{
f5c61c07
KB
248 register int c;
249 register char *token, *tp;
250 int incomm, inquote, inchar, midtoken, level;
251 char *sp;
252 char tok[BUFSIZ];
16bc359f 253
f5c61c07
KB
254 lineno = 1;
255 number = gotone = midtoken = inquote = inchar = incomm = FALSE;
256 level = 0;
16bc359f 257 sp = tp = token = line;
f5c61c07
KB
258 for (;;) {
259 *sp=c=getc(inf);
260 if (feof(inf))
261 break;
262 if (c == '\n')
263 lineno++;
16bc359f
KB
264 if (c == '\\') {
265 c = *++sp = getc(inf);
16bc359f
KB
266 if (c = '\n')
267 c = ' ';
f5c61c07 268 } else if (incomm) {
16bc359f 269 if (c == '*') {
f5c61c07 270 while ((*++sp=c=getc(inf)) == '*')
16bc359f 271 continue;
f5c61c07
KB
272 if (c == '\n')
273 lineno++;
16bc359f 274 if (c == '/')
f5c61c07 275 incomm = FALSE;
16bc359f 276 }
f5c61c07 277 } else if (inquote) {
16bc359f
KB
278 /*
279 * Too dumb to know about \" not being magic, but
280 * they usually occur in pairs anyway.
281 */
f5c61c07 282 if (c == '"')
16bc359f
KB
283 inquote = FALSE;
284 continue;
f5c61c07
KB
285 } else if (inchar) {
286 if (c == '\'')
16bc359f
KB
287 inchar = FALSE;
288 continue;
f5c61c07
KB
289 } else switch (c) {
290 case '"':
16bc359f 291 inquote = TRUE;
f5c61c07
KB
292 continue;
293 case '\'':
16bc359f 294 inchar = TRUE;
f5c61c07
KB
295 continue;
296 case '/':
16bc359f 297 if ((*++sp=c=getc(inf)) == '*')
f5c61c07 298 incomm = TRUE;
16bc359f 299 else
f5c61c07
KB
300 ungetc(*sp, inf);
301 continue;
302 case '#':
16bc359f 303 if (sp == line)
f5c61c07
KB
304 number = TRUE;
305 continue;
306 case '{':
307 level++;
308 continue;
309 case '}':
310 if (sp == line)
311 level = 0; /* reset */
16bc359f 312 else
f5c61c07
KB
313 level--;
314 continue;
315 }
316 if (!level && !inquote && !incomm && gotone == 0) {
16bc359f
KB
317 if (midtoken) {
318 if (endtoken(c)) {
f5c61c07 319 int pfline = lineno;
16bc359f
KB
320 if (start_func(&sp,token,tp)) {
321 strncpy(tok,token,tp-token+1);
322 tok[tp-token+1] = 0;
f5c61c07
KB
323 getline();
324 pfnote(tok, pfline);
16bc359f
KB
325 gotone = TRUE;
326 }
327 midtoken = FALSE;
328 token = sp;
f5c61c07 329 } else if (intoken(c))
16bc359f 330 tp++;
f5c61c07 331 } else if (begtoken(c)) {
16bc359f
KB
332 token = tp = sp;
333 midtoken = TRUE;
334 }
335 }
16bc359f 336 sp++;
f5c61c07 337 if (c == '\n' || sp > &line[sizeof (line) - BUFSIZ]) {
16bc359f 338 tp = token = sp = line;
f5c61c07 339 lineftell = ftell(inf);
16bc359f 340 number = gotone = midtoken = inquote = inchar = FALSE;
16bc359f
KB
341 }
342 }
343}
344
345/*
346 * This routine checks to see if the current token is
347 * at the start of a function. It updates the input line
348 * so that the '(' will be in it when it returns.
349 */
350start_func(lp,token,tp)
351char **lp,*token,*tp;
352{
353
354 reg char c,*sp,*tsp;
355 static logical found;
356 logical firsttok; /* T if have seen first token in ()'s */
357 int bad;
358
359 sp = *lp;
360 c = *sp;
361 bad = FALSE;
f5c61c07 362 if (!number) { /* space is not allowed in macro defs */
16bc359f
KB
363 while (iswhite(c)) {
364 *++sp = c = getc(inf);
f5c61c07
KB
365 if (c == '\n') {
366 lineno++;
367 if (sp > &line[sizeof (line) - BUFSIZ])
368 goto ret;
369 }
16bc359f
KB
370 }
371 /* the following tries to make it so that a #define a b(c) */
372 /* doesn't count as a define of b. */
f5c61c07 373 } else {
16bc359f
KB
374 logical define;
375
376 define = TRUE;
377 for (tsp = "define"; *tsp && token < tp; tsp++)
378 if (*tsp != *token++) {
379 define = FALSE;
380 break;
381 }
382 if (define)
383 found = 0;
384 else
385 found++;
386 if (found >= 2) {
387 gotone = TRUE;
388badone: bad = TRUE;
389 goto ret;
390 }
391 }
392 if (c != '(')
393 goto badone;
394 firsttok = FALSE;
395 while ((*++sp=c=getc(inf)) != ')') {
f5c61c07
KB
396 if (c == '\n') {
397 lineno++;
398 if (sp > &line[sizeof (line) - BUFSIZ])
399 goto ret;
400 }
16bc359f
KB
401 /*
402 * This line used to confuse ctags:
403 * int (*oldhup)();
404 * This fixes it. A nonwhite char before the first
405 * token, other than a / (in case of a comment in there)
406 * makes this not a declaration.
407 */
408 if (begtoken(c) || c=='/') firsttok++;
409 else if (!iswhite(c) && !firsttok) goto badone;
16bc359f 410 }
16bc359f 411 while (iswhite(*++sp=c=getc(inf)))
f5c61c07
KB
412 if (c == '\n') {
413 lineno++;
414 if (sp > &line[sizeof (line) - BUFSIZ])
415 break;
416 }
16bc359f
KB
417ret:
418 *lp = --sp;
f5c61c07
KB
419 if (c == '\n')
420 lineno--;
16bc359f
KB
421 ungetc(c,inf);
422 return !bad && isgood(c);
423}
424
f5c61c07 425getline()
16bc359f 426{
f5c61c07
KB
427 long saveftell = ftell( inf );
428 register char *cp;
429
430 fseek( inf , lineftell , 0 );
431 fgets(lbuf, sizeof lbuf, inf);
432 cp = rindex(lbuf, '\n');
433 if (cp)
434 *cp = 0;
435 fseek(inf, saveftell, 0);
16bc359f
KB
436}
437
16bc359f
KB
438free_tree(node)
439NODE *node;
440{
441
442 while (node) {
443 free_tree(node->right);
444 cfree(node);
445 node = node->left;
446 }
447}
448
f5c61c07
KB
449add_node(node, cur_node)
450 NODE *node,*cur_node;
16bc359f 451{
f5c61c07 452 register int dif;
16bc359f
KB
453
454 dif = strcmp(node->func,cur_node->func);
16bc359f
KB
455 if (dif == 0) {
456 if (node->file == cur_node->file) {
457 if (!wflag) {
f5c61c07
KB
458fprintf(stderr,"Duplicate function in file %s, line %d: %s\n",
459 node->file,lineno,node->func);
460fprintf(stderr,"Second entry ignored\n");
16bc359f
KB
461 }
462 return;
463 }
f5c61c07
KB
464 if (!cur_node->been_warned)
465 if (!wflag)
466fprintf(stderr,"Duplicate function in files %s and %s: %s (Warning only)\n",
467 node->file, cur_node->file, node->func);
468 cur_node->been_warned = TRUE;
469 return;
470 }
471 if (dif < 0) {
16bc359f
KB
472 if (cur_node->left != NULL)
473 add_node(node,cur_node->left);
f5c61c07 474 else
16bc359f 475 cur_node->left = node;
f5c61c07
KB
476 return;
477 }
478 if (cur_node->right != NULL)
479 add_node(node,cur_node->right);
16bc359f 480 else
f5c61c07 481 cur_node->right = node;
16bc359f
KB
482}
483
16bc359f 484put_funcs(node)
f5c61c07 485reg NODE *node;
16bc359f 486{
f5c61c07 487 reg char *sp;
16bc359f
KB
488
489 if (node == NULL)
490 return;
491 put_funcs(node->left);
f5c61c07
KB
492 if (xflag == 0) {
493 fprintf(outf, "%s\t%s\t%c^", node->func, node->file ,searchar);
494 for (sp = node->pat; *sp; sp++)
495 if (*sp == '\\')
496 fprintf(outf, "\\\\");
497 else
498 putc(*sp, outf);
499 fprintf(outf, "%c\n", searchar);
500 }
9927b527
KB
501 else if (vflag)
502 fprintf(stdout, "%s %s %d\n", node->func, node->file, (node->lno+63)/64);
f5c61c07
KB
503 else
504 fprintf(stdout, "%-16s%4d %-16s %s\n",
505 node->func, node->lno, node->file, node->pat);
16bc359f
KB
506 put_funcs(node->right);
507}
508
f5c61c07
KB
509char *dbp = lbuf;
510int pfcnt;
511
512PF_funcs(fi)
513 FILE *fi;
16bc359f 514{
f5c61c07
KB
515
516 lineno = 0;
517 pfcnt = 0;
518 while (fgets(lbuf, sizeof(lbuf), fi)) {
519 lineno++;
520 dbp = lbuf;
521 if ( *dbp == '%' ) dbp++ ; /* Ratfor escape to fortran */
522 while (isspace(*dbp))
523 dbp++;
524 if (*dbp == 0)
525 continue;
526 switch (*dbp |' ') {
527
528 case 'i':
529 if (tail("integer"))
530 takeprec();
531 break;
532 case 'r':
533 if (tail("real"))
534 takeprec();
16bc359f 535 break;
f5c61c07
KB
536 case 'l':
537 if (tail("logical"))
538 takeprec();
16bc359f 539 break;
f5c61c07
KB
540 case 'c':
541 if (tail("complex") || tail("character"))
542 takeprec();
543 break;
544 case 'd':
545 if (tail("double")) {
546 while (isspace(*dbp))
547 dbp++;
548 if (*dbp == 0)
549 continue;
550 if (tail("precision"))
551 break;
552 continue;
553 }
16bc359f 554 break;
16bc359f 555 }
f5c61c07
KB
556 while (isspace(*dbp))
557 dbp++;
558 if (*dbp == 0)
559 continue;
560 switch (*dbp|' ') {
561
562 case 'f':
563 if (tail("function"))
564 getit();
565 continue;
566 case 's':
567 if (tail("subroutine"))
568 getit();
569 continue;
570 case 'p':
571 if (tail("program")) {
572 getit();
573 continue;
574 }
575 if (tail("procedure"))
576 getit();
577 continue;
578 }
579 }
580 return (pfcnt);
581}
582
583tail(cp)
584 char *cp;
585{
586 register int len = 0;
587
588 while (*cp && (*cp&~' ') == ((*(dbp+len))&~' '))
589 cp++, len++;
590 if (*cp == 0) {
591 dbp += len;
592 return (1);
593 }
594 return (0);
595}
596
597takeprec()
598{
599
600 while (isspace(*dbp))
601 dbp++;
602 if (*dbp != '*')
603 return;
604 dbp++;
605 while (isspace(*dbp))
606 dbp++;
607 if (!isdigit(*dbp)) {
608 --dbp; /* force failure */
609 return;
16bc359f 610 }
f5c61c07
KB
611 do
612 dbp++;
613 while (isdigit(*dbp));
614}
615
616getit()
617{
618 register char *cp;
619 char c;
620 char nambuf[BUFSIZ];
621
622 for (cp = lbuf; *cp; cp++)
623 ;
624 *--cp = 0; /* zap newline */
625 while (isspace(*dbp))
626 dbp++;
627 if (*dbp == 0 || !isalpha(*dbp))
628 return;
629 for (cp = dbp+1; *cp && (isalpha(*cp) || isdigit(*cp)); cp++)
630 continue;
631 c = cp[0];
632 cp[0] = 0;
633 strcpy(nambuf, dbp);
634 cp[0] = c;
635 pfnote(nambuf, lineno);
636 pfcnt++;
637}
638
639char *
640savestr(cp)
641 char *cp;
642{
643 register int len;
644 register char *dp;
645
646 len = strlen(cp);
647 dp = (char *)malloc(len+1);
648 strcpy(dp, cp);
649 return (dp);
650}
651
652/*
653 * Return the ptr in sp at which the character c last
654 * appears; NULL if not found
655 *
656 * Identical to v7 rindex, included for portability.
657 */
658
659char *
660rindex(sp, c)
661register char *sp, c;
662{
663 register char *r;
664
665 r = NULL;
666 do {
667 if (*sp == c)
668 r = sp;
669 } while (*sp++);
670 return(r);
16bc359f 671}