BSD 4_4 development
[unix-history] / usr / src / contrib / emacs-18.57 / etc / etags.c
CommitLineData
f7d1dbb0
C
1/* Tags file maker to go with GNUmacs
2 Copyright (C) 1984, 1987, 1988 Free Software Foundation, Inc. and Ken Arnold
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 1, or (at your option)
7 any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18In other words, you are welcome to use, share and improve this program.
19You are forbidden to forbid anyone else to use, share and improve
20what you give them. Help stamp out software-hoarding! */
21
22#include <stdio.h>
23#include <ctype.h>
24
25/* Define the symbol ETAGS to make the program "etags",
26 which makes emacs-style tag tables by default.
27 Define CTAGS to make the program "ctags" compatible with the usual one.
28 Define neither one to get behavior that depends
29 on the name with which the program is invoked
30 (but we don't normally compile it that way). */
31
32/* On VMS, CTAGS is not useful, so always do ETAGS. */
33#ifdef VMS
34#ifndef ETAGS
35#define ETAGS
36#endif
37#endif
38
39/* Exit codes for success and failure. */
40
41#ifdef VMS
42#define GOOD (1)
43#define BAD (0)
44#else
45#define GOOD (0)
46#define BAD (1)
47#endif
48
49#define reg register
50#define logical char
51
52#define TRUE (1)
53#define FALSE (0)
54
55#define iswhite(arg) (_wht[arg]) /* T if char is white */
56#define begtoken(arg) (_btk[arg]) /* T if char can start token */
57#define intoken(arg) (_itk[arg]) /* T if char can be in token */
58#define endtoken(arg) (_etk[arg]) /* T if char ends tokens */
59#define isgood(arg) (_gd[arg]) /* T if char can be after ')' */
60
61#define max(I1,I2) (I1 > I2 ? I1 : I2)
62
63/* cause token checking for typedef, struct, union, enum to distinguish
64 keywords from identifier-prefixes (e.g. struct vs struct_tag). */
65#define istoken(s, tok, len) (!strncmp(s,tok,len) && endtoken(*((s)+(len))))
66
67struct nd_st { /* sorting structure */
68 char *name; /* function or type name */
69 char *file; /* file name */
70 logical f; /* use pattern or line no */
71 int lno; /* line number tag is on */
72 long cno; /* character number line starts on */
73 char *pat; /* search pattern */
74 logical been_warned; /* set if noticed dup */
75 struct nd_st *left,*right; /* left and right sons */
76};
77
78long ftell();
79typedef struct nd_st NODE;
80
81int number; /* tokens found so far on line starting with # (including #) */
82logical gotone, /* found a func already on line */
83 /* boolean "func" (see init) */
84 _wht[0177],_etk[0177],_itk[0177],_btk[0177],_gd[0177];
85
86 /* typedefs are recognized using a simple finite automata,
87 * tydef is its state variable.
88 */
89typedef enum {none, begin, tag_ok, middle, end } TYST;
90
91TYST tydef = none;
92
93char searchar = '/'; /* use /.../ searches */
94
95int lineno; /* line number of current line */
96long charno; /* current character number */
97long linecharno; /* character number of start of line */
98
99char *curfile, /* current input file name */
100 *outfile= 0, /* output file */
101 *white = " \f\t\n", /* white chars */
102 *endtk = " \t\n\"'#()[]{}=-+%*/&|^~!<>;,.:?",
103 /* token ending chars */
104 *begtk = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz$",
105 /* token starting chars */
106 *intk = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz$0123456789",
107 /* valid in-token chars */
108 *notgd = ",;"; /* non-valid after-function chars */
109
110int file_num = 0; /* current file number */
111int aflag = 0; /* -a: append to tags */
112int tflag = 0; /* -t: create tags for typedefs */
113int uflag = 0; /* -u: update tags */
114int wflag = 0; /* -w: suppress warnings */
115int vflag = 0; /* -v: create vgrind style index output */
116int xflag = 0; /* -x: create cxref style output */
117int eflag = 0; /* -e: emacs style output */
118
119/* Name this program was invoked with. */
120char *progname;
121
122FILE *inf, /* ioptr for current input file */
123 *outf; /* ioptr for tags file */
124
125NODE *head; /* the head of the sorted binary tree */
126
127char *savestr();
128char *savenstr ();
129char *rindex();
130char *index();
131char *concat ();
132void initbuffer ();
133long readline ();
134
135/* A `struct linebuffer' is a structure which holds a line of text.
136 `readline' reads a line from a stream into a linebuffer
137 and works regardless of the length of the line. */
138
139struct linebuffer
140 {
141 long size;
142 char *buffer;
143 };
144
145struct linebuffer lb, lb1;
146\f
147#if 0 /* VMS now provides the `system' function. */
148#ifdef VMS
149
150#include <descrip.h>
151
152void
153system (buf)
154 char *buf;
155{
156 struct dsc$descriptor_s command =
157 {
158 strlen(buf), DSC$K_DTYPE_T, DSC$K_CLASS_S, buf
159 };
160
161 LIB$SPAWN(&command);
162}
163#endif /* VMS */
164#endif /* 0 */
165\f
166main(ac,av)
167 int ac;
168 char *av[];
169{
170 char cmd[100];
171 int i;
172 int fflag = 0;
173 char *this_file;
174#ifdef VMS
175 char got_err;
176
177 extern char *gfnames();
178 extern char *massage_name();
179#endif
180
181 progname = av[0];
182
183#ifdef ETAGS
184 eflag = 1;
185#else
186#ifdef CTAGS
187 eflag = 0;
188#else
189 {
190 char *subname = rindex (progname, '/');
191 if (subname++ == NULL)
192 subname = progname;
193 eflag = ! strcmp(subname, "ctags");
194 }
195#endif
196#endif
197
198 while (ac > 1 && av[1][0] == '-')
199 {
200 for (i=1; av[1][i]; i++)
201 {
202 switch(av[1][i])
203 {
204#ifndef VMS /* These options are useful only with ctags,
205 and VMS can't input them, so just omit them. */
206 case 'B':
207 searchar='?';
208 eflag = 0;
209 break;
210 case 'F':
211 searchar='/';
212 eflag = 0;
213 break;
214#endif
215 case 'a':
216 aflag++;
217 break;
218 case 'e':
219 eflag++;
220 break;
221 case 'f':
222 if (fflag > 0)
223 {
224 fprintf(stderr,
225 "%s: -f flag may only be given once\n", progname);
226 goto usage;
227 }
228 fflag++, ac--; av++;
229 if (ac <= 1 || av[1][0] == '\0')
230 {
231 fprintf(stderr,
232 "%s: -f flag must be followed by a filename\n",
233 progname);
234 goto usage;
235 }
236 outfile = av[1];
237 goto end_loop;
238 case 't':
239 tflag++;
240 break;
241#ifndef VMS
242 case 'u':
243 uflag++;
244 eflag = 0;
245 break;
246#endif
247 case 'w':
248 wflag++;
249 break;
250 case 'v':
251 vflag++;
252 xflag++;
253 eflag = 0;
254 break;
255 case 'x':
256 xflag++;
257 eflag = 0;
258 break;
259 default:
260 goto usage;
261 }
262 }
263 end_loop: ;
264 ac--; av++;
265 }
266
267 if (ac <= 1)
268 {
269 usage:
270#ifdef VMS
271 fprintf (stderr, "Usage: %s [-aetwvx] [-f outfile] file ...\n", progname);
272#else
273 fprintf (stderr, "Usage: %s [-BFaetuwvx] [-f outfile] file ...\n", progname);
274#endif
275 exit(BAD);
276 }
277
278 if (outfile == 0)
279 {
280 outfile = eflag ? "TAGS" : "tags";
281 }
282
283 init(); /* set up boolean "functions" */
284
285 initbuffer (&lb);
286 initbuffer (&lb1);
287 /*
288 * loop through files finding functions
289 */
290 if (eflag)
291 {
292 outf = fopen (outfile, aflag ? "a" : "w");
293 if (!outf)
294 {
295 fprintf (stderr, "%s: ", progname);
296 perror (outfile);
297 exit (BAD);
298 }
299 }
300
301 file_num = 1;
302#ifdef VMS
303 for (ac--, av++;
304 (this_file = gfnames (&ac, &av, &got_err)) != NULL; file_num++)
305 {
306 if (got_err)
307 {
308 error("Can't find file %s\n", this_file);
309 ac--, av++;
310 }
311 else
312 {
313 this_file = massage_name (this_file);
314#else
315 for (; file_num < ac; file_num++)
316 {
317 this_file = av[file_num];
318 if (1)
319 {
320#endif
321 find_entries (this_file);
322 if (eflag)
323 {
324 fprintf (outf, "\f\n%s,%d\n",
325 this_file, total_size_of_entries (head));
326 put_entries (head);
327 free_tree (head);
328 head = NULL;
329 }
330 }
331 }
332
333 if (eflag)
334 {
335 fclose (outf);
336 exit (GOOD);
337 }
338
339 if (xflag)
340 {
341 put_entries(head);
342 exit(GOOD);
343 }
344 if (uflag)
345 {
346 for (i=1; i<ac; i++)
347 {
348 sprintf(cmd,
349 "mv %s OTAGS;fgrep -v '\t%s\t' OTAGS >%s;rm OTAGS",
350 outfile, av[i], outfile);
351 system(cmd);
352 }
353 aflag++;
354 }
355 outf = fopen(outfile, aflag ? "a" : "w");
356 if (outf == NULL)
357 {
358 fprintf (stderr, "%s: ", outfile);
359 perror(outfile);
360 exit(BAD);
361 }
362 put_entries(head);
363 fclose(outf);
364#ifndef VMS
365 if (uflag)
366 {
367 sprintf(cmd, "sort %s -o %s", outfile, outfile);
368 system(cmd);
369 }
370#endif
371 exit(GOOD);
372}
373
374/*
375 * This routine sets up the boolean psuedo-functions which work
376 * by seting boolean flags dependent upon the corresponding character
377 * Every char which is NOT in that string is not a white char. Therefore,
378 * all of the array "_wht" is set to FALSE, and then the elements
379 * subscripted by the chars in "white" are set to TRUE. Thus "_wht"
380 * of a char is TRUE if it is the string "white", else FALSE.
381 */
382init()
383{
384
385 reg char *sp;
386 reg int i;
387
388 for (i = 0; i < 0177; i++)
389 {
390 _wht[i] = _etk[i] = _itk[i] = _btk[i] = FALSE;
391 _gd[i] = TRUE;
392 }
393 for (sp = white; *sp; sp++)
394 _wht[*sp] = TRUE;
395 for (sp = endtk; *sp; sp++)
396 _etk[*sp] = TRUE;
397 for (sp = intk; *sp; sp++)
398 _itk[*sp] = TRUE;
399 for (sp = begtk; *sp; sp++)
400 _btk[*sp] = TRUE;
401 for (sp = notgd; *sp; sp++)
402 _gd[*sp] = FALSE;
403 _wht[0] = _wht['\n'];
404 _etk[0] = _etk['\n'];
405 _btk[0] = _btk['\n'];
406 _itk[0] = _itk['\n'];
407 _gd[0] = _gd['\n'];
408}
409
410/*
411 * This routine opens the specified file and calls the function
412 * which finds the function and type definitions.
413 */
414find_entries (file)
415 char *file;
416{
417 char *cp;
418
419 if ((inf=fopen(file,"r")) == NULL)
420 {
421 fprintf (stderr, "%s: ", progname);
422 perror(file);
423 return;
424 }
425 curfile = savestr(file);
426 cp = rindex(file, '.');
427 /* .tex, .aux or .bbl implies LaTeX source code */
428 if (cp && (!strcmp (cp + 1, "tex") || !strcmp (cp + 1, "aux")
429 || !strcmp (cp + 1, "bbl")))
430 {
431 TEX_funcs(inf);
432 fclose(inf);
433 return;
434 }
435 /* .l or .el or .lisp (or .cl or .clisp or ...) implies lisp source code */
436 if (cp && (!strcmp (cp + 1, "l") ||
437 !strcmp (cp + 1, "el") ||
438 !strcmp (cp + 1, "lsp") ||
439 !strcmp (cp + 1, "lisp") ||
440 !strcmp (cp + 1, "cl") ||
441 !strcmp (cp + 1, "clisp")))
442 {
443 L_funcs(inf);
444 fclose(inf);
445 return;
446 }
447 /* .scm or .sm or .scheme implies scheme source code */
448 if (cp && (!strcmp (cp + 1, "sm")
449 || !strcmp (cp + 1, "scm")
450 || !strcmp (cp + 1, "scheme")
451 || !strcmp (cp + 1, "t")
452 || !strcmp (cp + 1, "sch")
453 || !strcmp (cp + 1, "SM")
454 || !strcmp (cp + 1, "SCM")
455 /* The `SCM' or `scm' prefix with a version number */
456 || (cp[-1] == 'm' && cp[-2] == 'c' && cp[-3] == 's'
457 && string_numeric_p (cp + 1))
458 || (cp[-1] == 'M' && cp[-2] == 'C' && cp[-3] == 'S'
459 && string_numeric_p (cp + 1))))
460 {
461 Scheme_funcs(inf);
462 fclose(inf);
463 return;
464 }
465 /* if not a .c or .h or .y file, try fortran */
466 if (cp && (cp[1] != 'c' && cp[1] != 'h' && cp[1] != 'y')
467 && cp[2] == '\0')
468 {
469 if (PF_funcs(inf) != 0)
470 {
471 fclose(inf);
472 return;
473 }
474 rewind(inf); /* no fortran tags found, try C */
475 }
476 C_entries();
477 fclose(inf);
478}
479
480/* Nonzero if string STR is composed of digits. */
481
482int
483string_numeric_p (str)
484 char *str;
485{
486 while (*str)
487 {
488 if (*str < '0' || *str > '9')
489 return 0;
490 }
491 return 1;
492}
493\f
494/* Record a tag on the current line.
495 name is the tag name,
496 f is nonzero to use a pattern, zero to use line number instead. */
497
498pfnote (name, f, linestart, linelen, lno, cno)
499 char *name;
500 logical f; /* f == TRUE when function */
501 char *linestart;
502 int linelen;
503 int lno;
504 long cno;
505{
506 register char *fp;
507 register NODE *np;
508 char *altname;
509 char tem[51];
510
511 if ((np = (NODE *) malloc (sizeof (NODE))) == NULL)
512 {
513 fprintf(stderr, "%s: too many entries to sort\n", progname);
514 put_entries(head);
515 free_tree(head);
516 head = NULL;
517 np = (NODE *) xmalloc(sizeof (NODE));
518 }
519 /* Change name "main" to M<thisfilename>. */
520 if (!eflag && !xflag && !strcmp(name, "main"))
521 {
522 fp = rindex(curfile, '/');
523 if (fp == 0)
524 fp = curfile;
525 else
526 fp++;
527 altname = concat ("M", fp, "");
528 fp = rindex(altname, '.');
529 if (fp && fp[2] == 0)
530 *fp = 0;
531 name = altname;
532 }
533 np->name = savestr(name);
534 np->file = curfile;
535 np->f = f;
536 np->lno = lno;
537 np->cno = cno;
538 np->left = np->right = 0;
539 if (eflag)
540 {
541 linestart[linelen] = 0;
542 }
543 else if (xflag == 0)
544 {
545 sprintf (tem, strlen (linestart) < 50 ? "%s$" : "%.50s", linestart);
546 linestart = tem;
547 }
548 np->pat = savestr (linestart);
549 if (head == NULL)
550 head = np;
551 else
552 add_node(np, head);
553}
554
555free_tree(node)
556 NODE *node;
557{
558 while (node)
559 {
560 free_tree(node->right);
561 free(node);
562 node = node->left;
563 }
564}
565
566add_node(node, cur_node)
567 NODE *node,*cur_node;
568{
569 register int dif;
570
571 dif = strcmp(node->name, cur_node->name);
572
573 /* If this tag name matches an existing one, then
574 unless -e was given, do not add the node, but maybe print a warning */
575 if (!eflag && !dif)
576 {
577 if (node->file == cur_node->file)
578 {
579 if (!wflag)
580 {
581 fprintf(stderr,"%s: Duplicate entry in file %s, line %d: %s\n",
582 progname, node->file,lineno,node->name);
583 fprintf(stderr,"Second entry ignored\n");
584 }
585 return;
586 }
587 if (!cur_node->been_warned)
588 if (!wflag)
589 fprintf(stderr,"%s: Duplicate entry in files %s and %s: %s (Warning only)\n",
590 progname, node->file, cur_node->file, node->name);
591 cur_node->been_warned = TRUE;
592 return;
593 }
594
595 /* Actually add the node */
596 if (dif < 0)
597 {
598 if (cur_node->left != NULL)
599 add_node(node,cur_node->left);
600 else
601 cur_node->left = node;
602 return;
603 }
604 if (cur_node->right != NULL)
605 add_node(node,cur_node->right);
606 else
607 cur_node->right = node;
608}
609\f
610put_entries(node)
611 reg NODE *node;
612{
613 reg char *sp;
614
615 if (node == NULL)
616 return;
617
618 /* Output subentries that precede this one */
619 put_entries (node->left);
620
621 /* Output this entry */
622
623 if (eflag)
624 {
625 fprintf (outf, "%s%c%d,%d\n",
626 node->pat, 0177, node->lno, node->cno);
627 }
628 else if (!xflag)
629 {
630 fprintf (outf, "%s\t%s\t",
631 node->name, node->file);
632
633 if (node->f)
634 { /* a function */
635 putc (searchar, outf);
636 putc ('^', outf);
637
638 for (sp = node->pat; *sp; sp++)
639 {
640 if (*sp == '\\' || *sp == searchar)
641 putc ('\\', outf);
642 putc (*sp, outf);
643 }
644 putc (searchar, outf);
645 }
646 else
647 { /* a typedef; text pattern inadequate */
648 fprintf (outf, "%d", node->lno);
649 }
650 putc ('\n', outf);
651 }
652 else if (vflag)
653 fprintf (stdout, "%s %s %d\n",
654 node->name, node->file, (node->lno+63)/64);
655 else
656 fprintf (stdout, "%-16s%4d %-16s %s\n",
657 node->name, node->lno, node->file, node->pat);
658
659 /* Output subentries that follow this one */
660 put_entries (node->right);
661}
662
663/* Return total number of characters that put_entries will output for
664 the nodes in the subtree of the specified node.
665 Works only if eflag is set, but called only in that case. */
666
667total_size_of_entries(node)
668 reg NODE *node;
669{
670 reg int total = 0;
671 reg long num;
672
673 if (node == NULL)
674 return 0;
675
676 /* Count subentries that precede this one */
677 total = total_size_of_entries (node->left);
678
679 /* Count subentries that follow this one */
680 total += total_size_of_entries (node->right);
681
682 /* Count this entry */
683
684 total += strlen (node->pat) + 3;
685
686 num = node->lno;
687 while (num)
688 {
689 total++;
690 num /= 10;
691 }
692
693 num = node->cno;
694 if (!num) total++;
695 while (num)
696 {
697 total++;
698 num /= 10;
699 }
700 return total;
701}
702\f
703/*
704 * This routine finds functions and typedefs in C syntax and adds them
705 * to the list.
706 */
707#ifdef VMS
708long vmslinecharno;
709#define VMS_SET_LINECHARNO (vmslinecharno = ftell(inf))
710#else
711#define VMS_SET_LINECHARNO
712#endif
713
714#define CNL_SAVE_NUMBER \
715{ \
716 VMS_SET_LINECHARNO; \
717 linecharno = charno; lineno++; \
718 charno += 1 + readline (&lb, inf); \
719 lp = lb.buffer; \
720}
721
722#define CNL \
723{ \
724 CNL_SAVE_NUMBER; \
725 number = 0; \
726}
727
728C_entries ()
729{
730 register int c;
731 register char *token, *tp, *lp;
732 logical incomm, inquote, inchar, midtoken;
733 int level;
734 char tok[BUFSIZ];
735
736 lineno = 0;
737 charno = 0;
738 lp = lb.buffer;
739 *lp = 0;
740
741 number = 0;
742 gotone = midtoken = inquote = inchar = incomm = FALSE;
743 level = 0;
744
745 while (!feof (inf))
746 {
747 c = *lp++;
748 if (c == 0)
749 {
750 CNL;
751 gotone = FALSE;
752 }
753 if (c == '\\')
754 {
755 c = *lp++;
756 if (c == 0)
757 CNL_SAVE_NUMBER;
758 c = ' ';
759 }
760 else if (incomm)
761 {
762 if (c == '*')
763 {
764 while ((c = *lp++) == '*')
765 continue;
766 if (c == 0)
767 CNL;
768 if (c == '/')
769 incomm = FALSE;
770 }
771 }
772 else if (inquote)
773 {
774 /*
775 * Too dumb to know about \" not being magic, but
776 * they usually occur in pairs anyway.
777 */
778 if (c == '"')
779 inquote = FALSE;
780 continue;
781 }
782 else if (inchar)
783 {
784 if (c == '\'')
785 inchar = FALSE;
786 continue;
787 }
788 else switch (c)
789 {
790 case '"':
791 inquote = TRUE;
792 continue;
793 case '\'':
794 inchar = TRUE;
795 continue;
796 case '/':
797 if (*lp == '*')
798 {
799 lp++;
800 incomm = TRUE;
801 }
802 continue;
803 case '#':
804 if (lp == lb.buffer + 1)
805 number = 1;
806 continue;
807 case '{':
808 if (tydef == tag_ok)
809 {
810 tydef=middle;
811 }
812 level++;
813 continue;
814 case '}':
815 if (lp == lb.buffer + 1)
816 level = 0; /* reset */
817 else
818 level--;
819 if (!level && tydef==middle)
820 {
821 tydef=end;
822 }
823 continue;
824 }
825 if (!level && !inquote && !incomm && gotone == FALSE)
826 {
827 if (midtoken)
828 {
829 if (endtoken(c))
830 {
831 int f;
832 char *buf = lb.buffer;
833 int endpos = lp - lb.buffer;
834 char *lp1 = lp;
835 int line = lineno;
836 long linestart = linecharno;
837#ifdef VMS
838 long vmslinestart = vmslinecharno;
839#endif
840 int tem = consider_token (&lp1, token, &f, level);
841 lp = lp1;
842 if (tem)
843 {
844 if (linestart != linecharno)
845 {
846#ifdef VMS
847 getline (vmslinestart);
848#else
849 getline (linestart);
850#endif
851 strncpy (tok, token + (lb1.buffer - buf),
852 tp-token+1);
853 tok[tp-token+1] = 0;
854 pfnote(tok, f, lb1.buffer, endpos, line, linestart);
855 }
856 else
857 {
858 strncpy (tok, token, tp-token+1);
859 tok[tp-token+1] = 0;
860 pfnote(tok, f, lb.buffer, endpos, line, linestart);
861 }
862 gotone = f; /* function */
863 }
864 midtoken = FALSE;
865 token = lp - 1;
866 }
867 else if (intoken(c))
868 tp++;
869 }
870 else if (begtoken(c))
871 {
872 token = tp = lp - 1;
873 midtoken = TRUE;
874 }
875 }
876 if (c == ';' && tydef==end) /* clean with typedefs */
877 tydef=none;
878 }
879}
880
881/*
882 * This routine checks to see if the current token is
883 * at the start of a function, or corresponds to a typedef
884 * It updates the input line * so that the '(' will be
885 * in it when it returns.
886 */
887consider_token (lpp, token, f, level)
888 char **lpp, *token;
889 int *f, level;
890{
891 reg char *lp = *lpp;
892 reg char c;
893 static logical next_token_is_func;
894 logical firsttok; /* T if have seen first token in ()'s */
895 int bad, win;
896
897 *f = 1; /* a function */
898 c = lp[-1];
899 bad = FALSE;
900 if (!number)
901 { /* space is not allowed in macro defs */
902 while (iswhite(c))
903 {
904 c = *lp++;
905 if (c == 0)
906 {
907 if (feof (inf))
908 break;
909 CNL;
910 }
911 }
912 /* the following tries to make it so that a #define a b(c) */
913 /* doesn't count as a define of b. */
914 }
915 else
916 {
917 number++;
918 if (number >= 4 || (number==2 && strncmp (token, "define", 6)))
919 {
920 gotone = TRUE;
921 badone:
922 bad = TRUE;
923 goto ret;
924 }
925 }
926 /* check for the typedef cases */
927 if (tflag && istoken(token, "typedef", 7))
928 {
929 tydef=begin;
930 goto badone;
931 }
932 if (tydef==begin && (istoken(token, "struct", 6) ||
933 istoken(token, "union", 5) || istoken(token, "enum", 4)))
934 {
935 tydef=tag_ok;
936 goto badone;
937 }
938 if (tydef==tag_ok)
939 {
940 tydef=middle;
941 goto badone;
942 }
943 if (tydef==begin) /* e.g. typedef ->int<- */
944 {
945 tydef=end;
946 goto badone;
947 }
948 if (tydef==middle && level == 0) /* e.g. typedef struct tag ->struct_t<- */
949 {
950 tydef=end;
951 }
952 if (tydef==end)
953 {
954 *f = 0;
955 win = 1;
956 goto ret;
957 }
958 /* Detect GNUmacs's function-defining macros. */
959 if (!number && !strncmp (token, "DEF", 3))
960
961 {
962 next_token_is_func = 1;
963 goto badone;
964 }
965 if (next_token_is_func)
966 {
967 next_token_is_func = 0;
968 win = 1;
969 goto ret;
970 }
971 if (c != '(')
972 goto badone;
973 firsttok = FALSE;
974 while ((c = *lp++) != ')')
975 {
976 if (c == 0)
977 {
978 if (feof (inf))
979 break;
980 CNL;
981 }
982 /*
983 * This line used to confuse ctags:
984 * int (*oldhup)();
985 * This fixes it. A nonwhite char before the first
986 * token, other than a / (in case of a comment in there)
987 * makes this not a declaration.
988 */
989 if (begtoken(c) || c=='/') firsttok++;
990 else if (!iswhite(c) && !firsttok) goto badone;
991 }
992 while (iswhite (c = *lp++))
993 {
994 if (c == 0)
995 {
996 if (feof (inf))
997 break;
998 CNL;
999 }
1000 }
1001 win = isgood (c);
1002ret:
1003 *lpp = lp - 1;
1004 return !bad && win;
1005}
1006
1007getline (atchar)
1008 long atchar;
1009{
1010 long saveftell = ftell (inf);
1011
1012 fseek (inf, atchar, 0);
1013 readline (&lb1, inf);
1014 fseek (inf, saveftell, 0);
1015}
1016\f
1017/* Fortran parsing */
1018
1019char *dbp;
1020int pfcnt;
1021
1022PF_funcs(fi)
1023 FILE *fi;
1024{
1025 lineno = 0;
1026 charno = 0;
1027 pfcnt = 0;
1028
1029 while (!feof (fi))
1030 {
1031 lineno++;
1032 linecharno = charno;
1033 charno += readline (&lb, fi) + 1;
1034 dbp = lb.buffer;
1035 if (*dbp == '%') dbp++ ; /* Ratfor escape to fortran */
1036 while (isspace(*dbp))
1037 dbp++;
1038 if (*dbp == 0)
1039 continue;
1040 switch (*dbp |' ')
1041 {
1042 case 'i':
1043 if (tail("integer"))
1044 takeprec();
1045 break;
1046 case 'r':
1047 if (tail("real"))
1048 takeprec();
1049 break;
1050 case 'l':
1051 if (tail("logical"))
1052 takeprec();
1053 break;
1054 case 'c':
1055 if (tail("complex") || tail("character"))
1056 takeprec();
1057 break;
1058 case 'd':
1059 if (tail("double"))
1060 {
1061 while (isspace(*dbp))
1062 dbp++;
1063 if (*dbp == 0)
1064 continue;
1065 if (tail("precision"))
1066 break;
1067 continue;
1068 }
1069 break;
1070 }
1071 while (isspace(*dbp))
1072 dbp++;
1073 if (*dbp == 0)
1074 continue;
1075 switch (*dbp|' ')
1076 {
1077 case 'f':
1078 if (tail("function"))
1079 getit();
1080 continue;
1081 case 's':
1082 if (tail("subroutine"))
1083 getit();
1084 continue;
1085 case 'p':
1086 if (tail("program"))
1087 {
1088 getit();
1089 continue;
1090 }
1091 if (tail("procedure"))
1092 getit();
1093 continue;
1094 }
1095 }
1096 return (pfcnt);
1097}
1098
1099tail(cp)
1100 char *cp;
1101{
1102 register int len = 0;
1103
1104 while (*cp && (*cp&~' ') == ((*(dbp+len))&~' '))
1105 cp++, len++;
1106 if (*cp == 0)
1107 {
1108 dbp += len;
1109 return (1);
1110 }
1111 return (0);
1112}
1113
1114takeprec()
1115{
1116 while (isspace(*dbp))
1117 dbp++;
1118 if (*dbp != '*')
1119 return;
1120 dbp++;
1121 while (isspace(*dbp))
1122 dbp++;
1123 if (!isdigit(*dbp))
1124 {
1125 --dbp; /* force failure */
1126 return;
1127 }
1128 do
1129 dbp++;
1130 while (isdigit(*dbp));
1131}
1132
1133getit()
1134{
1135 register char *cp;
1136 char c;
1137 char nambuf[BUFSIZ];
1138
1139 while (isspace(*dbp))
1140 dbp++;
1141 if (*dbp == 0 || !isalpha(*dbp))
1142 return;
1143 for (cp = dbp+1; *cp && (isalpha(*cp) || isdigit(*cp)); cp++)
1144 continue;
1145 c = cp[0];
1146 cp[0] = 0;
1147 strcpy(nambuf, dbp);
1148 cp[0] = c;
1149 pfnote(nambuf, TRUE, lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
1150 pfcnt++;
1151}
1152\f
1153/*
1154 * lisp tag functions
1155 * just look for (def or (DEF
1156 */
1157
1158L_funcs (fi)
1159 FILE *fi;
1160{
1161 lineno = 0;
1162 charno = 0;
1163 pfcnt = 0;
1164
1165 while (!feof (fi))
1166 {
1167 lineno++;
1168 linecharno = charno;
1169 charno += readline (&lb, fi) + 1;
1170 dbp = lb.buffer;
1171 if (dbp[0] == '(' &&
1172 (dbp[1] == 'D' || dbp[1] == 'd') &&
1173 (dbp[2] == 'E' || dbp[2] == 'e') &&
1174 (dbp[3] == 'F' || dbp[3] == 'f'))
1175 {
1176 while (!isspace(*dbp)) dbp++;
1177 while (isspace(*dbp)) dbp++;
1178 L_getit();
1179 }
1180 }
1181}
1182
1183L_getit()
1184{
1185 register char *cp;
1186 char c;
1187 char nambuf[BUFSIZ];
1188
1189 if (*dbp == 0) return;
1190 for (cp = dbp+1; *cp && *cp != '(' && *cp != ' '; cp++)
1191 continue;
1192 c = cp[0];
1193 cp[0] = 0;
1194 strcpy(nambuf, dbp);
1195 cp[0] = c;
1196 pfnote(nambuf, TRUE, lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
1197 pfcnt++;
1198}
1199\f
1200/*
1201 * Scheme tag functions
1202 * look for (def... xyzzy
1203 * look for (def... (xyzzy
1204 * look for (def ... ((...(xyzzy ....
1205 * look for (set! xyzzy
1206 */
1207
1208static get_scheme ();
1209Scheme_funcs (fi)
1210 FILE *fi;
1211{
1212 lineno = 0;
1213 charno = 0;
1214 pfcnt = 0;
1215
1216 while (!feof (fi))
1217 {
1218 lineno++;
1219 linecharno = charno;
1220 charno += readline (&lb, fi) + 1;
1221 dbp = lb.buffer;
1222 if (dbp[0] == '(' &&
1223 (dbp[1] == 'D' || dbp[1] == 'd') &&
1224 (dbp[2] == 'E' || dbp[2] == 'e') &&
1225 (dbp[3] == 'F' || dbp[3] == 'f'))
1226 {
1227 while (!isspace(*dbp)) dbp++;
1228 /* Skip over open parens and white space */
1229 while (*dbp && (isspace(*dbp) || *dbp == '(')) dbp++;
1230 get_scheme ();
1231 }
1232 if (dbp[0] == '(' &&
1233 (dbp[1] == 'S' || dbp[1] == 's') &&
1234 (dbp[2] == 'E' || dbp[2] == 'e') &&
1235 (dbp[3] == 'T' || dbp[3] == 't') &&
1236 (dbp[4] == '!' || dbp[4] == '!') &&
1237 (isspace(dbp[5])))
1238 {
1239 while (!isspace(*dbp)) dbp++;
1240 /* Skip over white space */
1241 while (isspace(*dbp)) dbp++;
1242 get_scheme ();
1243 }
1244 }
1245}
1246
1247static
1248get_scheme()
1249{
1250 register char *cp;
1251 char c;
1252 char nambuf[BUFSIZ];
1253
1254 if (*dbp == 0) return;
1255 /* Go till you get to white space or a syntactic break */
1256 for (cp = dbp+1; *cp && *cp != '(' && *cp != ')' && !isspace(*cp); cp++)
1257 continue;
1258 /* Null terminate the string there. */
1259 c = cp[0];
1260 cp[0] = 0;
1261 /* Copy the string */
1262 strcpy(nambuf, dbp);
1263 /* Unterminate the string */
1264 cp[0] = c;
1265 /* Announce the change */
1266 pfnote(nambuf, TRUE, lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
1267 pfcnt++;
1268}
1269\f
1270/* Find tags in TeX and LaTeX input files. */
1271
1272/* TEX_toktab is a table of TeX control sequences that define tags.
1273 Each TEX_tabent records one such control sequence. */
1274
1275struct TEX_tabent
1276{
1277 char *name;
1278 int len;
1279};
1280
1281struct TEX_tabent *TEX_toktab = NULL; /* Table with tag tokens */
1282
1283/* Default set of control sequences to put into TEX_toktab.
1284 The value of environment var TEXTAGS is prepended to this. */
1285
1286static char *TEX_defenv =
1287 ":chapter:section:subsection:subsubsection:eqno:label:ref:cite:bibitem:typeout";
1288
1289struct TEX_tabent *TEX_decode_env ();
1290
1291static char TEX_esc = '\\';
1292static char TEX_opgrp = '{';
1293static char TEX_clgrp = '}';
1294
1295/*
1296 * TeX/LaTeX scanning loop.
1297 */
1298
1299TEX_funcs (fi)
1300 FILE *fi;
1301{
1302 char *lasthit;
1303
1304 lineno = 0;
1305 charno = 0;
1306 pfcnt = 0;
1307
1308 /* Select either \ or ! as escape character. */
1309 TEX_mode (fi);
1310
1311 /* Initialize token table once from environment. */
1312 if (!TEX_toktab)
1313 TEX_toktab = TEX_decode_env ("TEXTAGS", TEX_defenv);
1314
1315 while (!feof (fi))
1316 {
1317 lineno++;
1318 linecharno = charno;
1319 charno += readline (&lb, fi) + 1;
1320 dbp = lb.buffer;
1321 lasthit = dbp;
1322
1323 while (!feof (fi))
1324 { /* Scan each line in file */
1325 lineno++;
1326 linecharno = charno;
1327 charno += readline (&lb, fi) + 1;
1328 dbp = lb.buffer;
1329 lasthit = dbp;
1330 while (dbp = index (dbp, TEX_esc)) /* Look at each escape in line */
1331 {
1332 register int i;
1333
1334 if (! *(++dbp))
1335 break;
1336 linecharno += dbp - lasthit;
1337 lasthit = dbp;
1338 i = TEX_Token (lasthit);
1339 if (0 <= i)
1340 {
1341 TEX_getit (lasthit, TEX_toktab[i].len);
1342 break; /* We only save a line once */
1343 }
1344 }
1345 }
1346 }
1347}
1348
1349#define TEX_LESC '\\'
1350#define TEX_SESC '!'
1351
1352/* Figure out whether TeX's escapechar is '\\' or '!' and set grouping */
1353/* chars accordingly. */
1354
1355TEX_mode (f)
1356 FILE *f;
1357{
1358 int c;
1359
1360 while ((c = getc (f)) != EOF)
1361 if (c == TEX_LESC || c == TEX_SESC)
1362 break;
1363
1364 if (c == TEX_LESC)
1365 {
1366 TEX_esc = TEX_LESC;
1367 TEX_opgrp = '{';
1368 TEX_clgrp = '}';
1369 }
1370 else
1371 {
1372 TEX_esc = TEX_SESC;
1373 TEX_opgrp = '<';
1374 TEX_clgrp = '>';
1375 }
1376 rewind (f);
1377}
1378
1379/* Read environment and prepend it to the default string. */
1380/* Build token table. */
1381
1382struct TEX_tabent *
1383TEX_decode_env (evarname, defenv)
1384 char *evarname;
1385 char *defenv;
1386{
1387 register char *env, *p;
1388 extern char *savenstr (), *index ();
1389
1390 struct TEX_tabent *tab;
1391 int size, i;
1392
1393 /* Append deafult string to environment. */
1394 env = (char *) getenv (evarname);
1395 if (!env)
1396 env = defenv;
1397 else
1398 env = concat (env, defenv, "");
1399
1400 /* Allocate a token table */
1401 for (size = 1, p=env; p;)
1402 if ((p = index (p, ':')) && *(++p))
1403 size++;
1404 tab = (struct TEX_tabent *) xmalloc (size * sizeof (struct TEX_tabent));
1405
1406 /* Unpack environment string into token table. Be careful about */
1407 /* zero-length strings (leading ':', "::" and trailing ':') */
1408 for (i = 0; *env;)
1409 {
1410 p = index (env, ':');
1411 if (!p) /* End of environment string. */
1412 p = env + strlen (env);
1413 if (p - env > 0)
1414 { /* Only non-zero strings. */
1415 tab[i].name = savenstr (env, p - env);
1416 tab[i].len = strlen (tab[i].name);
1417 i++;
1418 }
1419 if (*p)
1420 env = p + 1;
1421 else
1422 {
1423 tab[i].name = NULL; /* Mark end of table. */
1424 tab[i].len = 0;
1425 break;
1426 }
1427 }
1428 return tab;
1429}
1430
1431/* Record a tag defined by a TeX command of length LEN and starting at NAME.
1432 The name being defined actually starts at (NAME + LEN + 1).
1433 But we seem to include the TeX command in the tag name. */
1434
1435TEX_getit (name, len)
1436 char *name;
1437 int len;
1438{
1439 char *p = name + len;
1440 char nambuf[BUFSIZ];
1441
1442 if (*name == 0) return;
1443
1444 /* Let tag name extend to next group close (or end of line) */
1445 while (*p && *p != TEX_clgrp)
1446 p++;
1447 strncpy (nambuf, name, p - name);
1448 nambuf[p - name] = 0;
1449
1450 pfnote (nambuf, TRUE, lb.buffer, strlen (lb.buffer), lineno, linecharno);
1451 pfcnt++;
1452}
1453
1454/* If the text at CP matches one of the tag-defining TeX command names,
1455 return the index of that command in TEX_toktab.
1456 Otherwise return -1. */
1457
1458/* Keep the capital `T' in `Token' for dumb truncating compilers
1459 (this distinguishes it from `TEX_toktab' */
1460TEX_Token (cp)
1461 char *cp;
1462{
1463 int i;
1464
1465 for (i = 0; TEX_toktab[i].len > 0; i++)
1466 if (strncmp (TEX_toktab[i].name, cp, TEX_toktab[i].len) == 0)
1467 return i;
1468 return -1;
1469}
1470\f
1471/* Initialize a linebuffer for use */
1472
1473void
1474initbuffer (linebuffer)
1475 struct linebuffer *linebuffer;
1476{
1477 linebuffer->size = 200;
1478 linebuffer->buffer = (char *) xmalloc (200);
1479}
1480
1481/* Read a line of text from `stream' into `linebuffer'.
1482 Return the length of the line. */
1483
1484long
1485readline (linebuffer, stream)
1486 struct linebuffer *linebuffer;
1487 register FILE *stream;
1488{
1489 char *buffer = linebuffer->buffer;
1490 register char *p = linebuffer->buffer;
1491 register char *pend;
1492
1493 pend = p + linebuffer->size;
1494
1495 while (1)
1496 {
1497 int c = getc (stream);
1498 if (p == pend)
1499 {
1500 linebuffer->size *= 2;
1501 buffer = (char *) xrealloc (buffer, linebuffer->size);
1502 p += buffer - linebuffer->buffer;
1503 pend = buffer + linebuffer->size;
1504 linebuffer->buffer = buffer;
1505 }
1506 if (c < 0 || c == '\n')
1507 {
1508 *p = 0;
1509 break;
1510 }
1511 *p++ = c;
1512 }
1513
1514 return p - buffer;
1515}
1516\f
1517char *
1518savestr(cp)
1519 char *cp;
1520{
1521 return savenstr (cp, strlen (cp));
1522}
1523
1524char *
1525savenstr(cp, len)
1526 char *cp;
1527 int len;
1528{
1529 register char *dp;
1530
1531 dp = (char *) xmalloc (len + 1);
1532 strncpy (dp, cp, len);
1533 dp[len] = '\0';
1534 return dp;
1535}
1536
1537/*
1538 * Return the ptr in sp at which the character c last
1539 * appears; NULL if not found
1540 *
1541 * Identical to v7 rindex, included for portability.
1542 */
1543
1544char *
1545rindex(sp, c)
1546 register char *sp, c;
1547{
1548 register char *r;
1549
1550 r = NULL;
1551 do
1552 {
1553 if (*sp == c)
1554 r = sp;
1555 } while (*sp++);
1556 return(r);
1557}
1558
1559/*
1560 * Return the ptr in sp at which the character c first
1561 * appears; NULL if not found
1562 *
1563 * Identical to v7 index, included for portability.
1564 */
1565
1566char *
1567index(sp, c)
1568 register char *sp, c;
1569{
1570 do
1571 {
1572 if (*sp == c)
1573 return (sp);
1574 } while (*sp++);
1575 return (NULL);
1576}
1577
1578/* Print error message and exit. */
1579
1580fatal (s1, s2)
1581 char *s1, *s2;
1582{
1583 error (s1, s2);
1584 exit (BAD);
1585}
1586
1587/* Print error message. `s1' is printf control string, `s2' is arg for it. */
1588
1589error (s1, s2)
1590 char *s1, *s2;
1591{
1592 fprintf (stderr, "%s: ", progname);
1593 fprintf (stderr, s1, s2);
1594 fprintf (stderr, "\n");
1595}
1596
1597/* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */
1598
1599char *
1600concat (s1, s2, s3)
1601 char *s1, *s2, *s3;
1602{
1603 int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
1604 char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
1605
1606 strcpy (result, s1);
1607 strcpy (result + len1, s2);
1608 strcpy (result + len1 + len2, s3);
1609 *(result + len1 + len2 + len3) = 0;
1610
1611 return result;
1612}
1613
1614/* Like malloc but get fatal error if memory is exhausted. */
1615
1616int
1617xmalloc (size)
1618 int size;
1619{
1620 int result = malloc (size);
1621 if (!result)
1622 fatal ("virtual memory exhausted", 0);
1623 return result;
1624}
1625
1626int
1627xrealloc (ptr, size)
1628 char *ptr;
1629 int size;
1630{
1631 int result = realloc (ptr, size);
1632 if (!result)
1633 fatal ("virtual memory exhausted");
1634 return result;
1635}