Commit | Line | Data |
---|---|---|
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 | |
8 | char 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 | 14 | static 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 | ||
41 | typedef 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 | 50 | NODE *head; /* head of the sorted binary tree */ |
16bc359f | 51 | |
e99c8356 KB |
52 | /* boolean "func" (see init()) */ |
53 | bool _wht[0177],_etk[0177],_itk[0177],_btk[0177],_gd[0177]; | |
16bc359f | 54 | |
e99c8356 KB |
55 | FILE *inf, /* ioptr for current input file */ |
56 | *outf; /* ioptr for tags file */ | |
16bc359f | 57 | |
e99c8356 | 58 | long lineftell; /* ftell after getc( inf ) == '\n' */ |
16bc359f | 59 | |
e99c8356 | 60 | int 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 |
69 | char *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 |
74 | main(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) { | |
125 | usage: 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 | 176 | init() |
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 |
207 | find_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 */ | |
235 | done: (void)fclose(inf); | |
236 | } | |
237 | ||
238 | /* | |
239 | * pfnote -- | |
240 | * enter a new node in the tree | |
241 | */ | |
242 | pfnote(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 | */ | |
292 | c_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 | */ | |
336 | endtok: 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: | |
426 | storec: 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 | */ | |
469 | func_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 | */ | |
488 | hash_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 | } | |
528 | skip: 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 | */ | |
540 | str_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 | */ | |
583 | skip_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 | */ | |
600 | skip_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 | */ | |
614 | skip_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 | */ | |
651 | Y_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 | ||
735 | char * | |
736 | toss_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 | */ | |
759 | getline() | |
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 | ||
772 | free_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 | ||
782 | add_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 | */ | |
815 | put_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 | ||
841 | char *dbp = lbuf; | |
842 | int pfcnt; | |
843 | ||
844 | PF_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 | ||
912 | tail(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 | ||
926 | takeprec() | |
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 | ||
944 | getit() | |
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 | ||
967 | char * | |
968 | savestr(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 | */ | |
985 | L_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 | ||
1013 | L_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 | */ | |
1051 | striccmp(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 | */ | |
1074 | first_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 | */ | |
1093 | toss_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 | } |