*** empty log message ***
[bpt/emacs.git] / lib-src / etags.c
CommitLineData
c6d46f5f 1/* Tags file maker to go with GNU Emacs
13fde0cd 2 Copyright (C) 1984, 1987, 1988, 1989, 1993 Free Software Foundation, Inc. and Ken Arnold
c6d46f5f 3
ea6cd314 4This file is not considered part of GNU Emacs.
c6d46f5f 5
ea6cd314 6This program is free software; you can redistribute it and/or modify
c6d46f5f 7it under the terms of the GNU General Public License as published by
ea6cd314
RS
8the Free Software Foundation; either version 2 of the License, or
9(at your option) any later version.
c6d46f5f 10
ea6cd314 11This program is distributed in the hope that it will be useful,
c6d46f5f
JB
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
ea6cd314
RS
17along with this program; if not, write to the Free Software
18Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
c6d46f5f
JB
19
20/*
21 * Authors:
22 * Ctags originally by Ken Arnold.
23 * FORTRAN added by Jim Kleckner.
24 * Ed Pelegri-Llopart added C typedefs.
25 * Gnu Emacs TAGS format and modifications by RMS?
26 * Sam Kendall added C++.
31d4b314 27 *
2b878b4c 28 * Francesco Potorti` (pot@cnuce.cnr.it) is the current maintainer. 9.4
c6d46f5f
JB
29 */
30
72a339d7 31#ifdef HAVE_CONFIG_H
18160b98 32#include <../src/config.h>
1e134a5f
RM
33#endif
34
918f9ad1
JB
35#include <stdio.h>
36#include <ctype.h>
37#include <sys/types.h>
38#include <sys/stat.h>
39
2b878b4c
FP
40#if !defined (S_ISREG) && defined (S_IFREG)
41# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
42#endif
43
918f9ad1
JB
44#include "getopt.h"
45
c6d46f5f
JB
46extern char *malloc (), *realloc ();
47extern char *getenv ();
c6d46f5f
JB
48extern char *strcpy (), *strncpy ();
49extern int strcmp ();
50
8a6c8bcf
RS
51char *etags_index (), *etags_rindex ();
52char *savenstr ();
c6d46f5f
JB
53
54/* Define the symbol ETAGS to make the program "etags",
55 which makes emacs-style tag tables by default.
56 Define CTAGS to make the program "ctags" compatible with the usual one.
57 Define neither one to get behavior that depends
58 on the name with which the program is invoked
59 (but we don't normally compile it that way). */
60
61#if !defined(ETAGS) && !defined(CTAGS)
62/* If neither is defined, program can be run as either. */
63#define ETAGS
64#define CTAGS
65#endif
66
67/* On VMS, CTAGS is not useful, so always do ETAGS. */
68#ifdef VMS
69#ifndef ETAGS
70#define ETAGS
71#endif
72#endif
73
74/* Exit codes for success and failure. */
75#ifdef VMS
76#define GOOD (1)
77#define BAD (0)
78#else
79#define GOOD (0)
80#define BAD (1)
81#endif
82
83/*
84 * The FILEPOS abstract type, which represents a position in a file,
85 * plus the following accessor functions:
86 *
87 * long GET_CHARNO (pos)
88 * returns absolute char number.
c6d46f5f
JB
89 * void SET_FILEPOS (pos, fp, charno)
90 * FILE *fp; long charno;
91 * sets `pos' from the current file
92 * position of `fp' and from `charno',
93 * which must be the absolute character
94 * number corresponding to the current
95 * position of `fp'.
96 *
97 * The `pos' parameter is an lvalue expression of type FILEPOS.
98 * Parameters to the accessor functions are evaluated 0 or more times,
99 * and so must have no side effects.
100 *
101 * FILEPOS objects can also be assigned and passed to and from
102 * functions in the normal C manner.
103 *
104 * Implementation notes: the `+ 0' is to enforce rvalue-ness.
105 */
c6d46f5f 106
c6d46f5f 107#ifndef DEBUG
13fde0cd 108 /* real implementation */
c6d46f5f
JB
109typedef long FILEPOS;
110#define GET_CHARNO(pos) ((pos) + 0)
c6d46f5f
JB
111#define SET_FILEPOS(pos, fp, cno) ((void) ((pos) = (cno)))
112#else
13fde0cd 113 /* debugging implementation */
c6d46f5f
JB
114typedef struct
115{
116 long charno;
117} FILEPOS;
118
119#define GET_CHARNO(pos) ((pos).charno + 0)
c6d46f5f
JB
120#define SET_FILEPOS(pos, fp, cno) \
121 ((void) ((pos).charno = (cno), \
122 (cno) != ftell (fp) ? (error ("SET_FILEPOS inconsistency"), 0) \
123 : 0))
124#endif
c6d46f5f
JB
125
126#define streq(s, t) (strcmp (s, t) == 0)
127#define strneq(s, t, n) (strncmp (s, t, n) == 0)
13fde0cd 128#define logical int
c6d46f5f
JB
129
130#define TRUE 1
131#define FALSE 0
132
133#define iswhite(arg) (_wht[arg]) /* T if char is white */
134#define begtoken(arg) (_btk[arg]) /* T if char can start token */
135#define intoken(arg) (_itk[arg]) /* T if char can be in token */
136#define endtoken(arg) (_etk[arg]) /* T if char ends tokens */
c6d46f5f
JB
137
138#define max(I1,I2) ((I1) > (I2) ? (I1) : (I2))
139
140struct nd_st
141{ /* sorting structure */
142 char *name; /* function or type name */
143 char *file; /* file name */
144 logical is_func; /* use pattern or line no */
fe0b3356 145 logical named; /* list name separately */
c6d46f5f
JB
146 logical been_warned; /* set if noticed dup */
147 int lno; /* line number tag is on */
148 long cno; /* character number line starts on */
149 char *pat; /* search pattern */
150 struct nd_st *left, *right; /* left and right sons */
151};
152
153long ftell ();
154typedef struct nd_st NODE;
155
13fde0cd
RS
156logical header_file; /* TRUE if .h file, FALSE o.w. */
157/* boolean "functions" (see init) */
158logical _wht[0177], _etk[0177], _itk[0177], _btk[0177];
c6d46f5f
JB
159
160
161char *concat ();
162char *savenstr ();
163char *savestr ();
164char *xmalloc ();
165char *xrealloc ();
31d4b314 166int L_isdef (), L_isquote ();
c6d46f5f
JB
167int PF_funcs ();
168int total_size_of_entries ();
169logical consider_token ();
170logical tail ();
171long readline ();
172void Asm_funcs ();
173void C_entries ();
174void L_funcs ();
175void L_getit ();
176void PAS_funcs ();
177void Scheme_funcs ();
178void TEX_funcs ();
179void add_node ();
180void error ();
181void fatal ();
182void find_entries ();
183void free_tree ();
184void getit ();
185void getline ();
186void init ();
187void initbuffer ();
188void initbuffer ();
189void pfnote ();
190void process_file ();
191void put_entries ();
192void takeprec ();
193
194/*
195 * MACRO
196 * xnew -- allocate storage
197 *
198 * SYNOPSIS
199 * Type *xnew (int n, Type);
200 */
201#define xnew(n, Type) ((Type *) xmalloc ((n) * sizeof (Type)))
202\f
203
204
205/*
206 * Symbol table stuff.
207 *
208 * Should probably be implemented with hash table; linked list for now.
209 */
210
211enum sym_type
212{
213 st_none, st_C_struct, st_C_enum, st_C_define, st_C_typedef, st_C_typespec
214};
215
216struct stab_entry
217{
218 char *sym;
219 int symlen;
220 enum sym_type type;
221 struct stab_entry *next;
222};
223
224typedef struct stab_entry Stab_entry;
225typedef Stab_entry *Stab;
226
227/*
228 * NAME
229 * Stab, Stab_entry, stab_create, stab_search, stab_find -- symbol table
230 *
231 * SYNOPSIS
232 * Types: Stab, Stab_entry, enum sym_type
233 *
234 * Stab * stab_create ()
235 *
236 * Stab_entry * stab_find (stab, sym)
237 * Stab *stab;
238 * char *sym;
239 *
240 * Stab_entry * stab_search (stab, sym)
241 * Stab *stab;
242 * char *sym;
243 *
244 * DESCRIPTION
245 * stab_create creates a Stab, a symbol table object, and returns a
246 * pointer to it. stab_find finds a symbol in a Stab; it returns a
247 * pointer to the Stab_entry if found, otherwise NULL. stab_search
248 * is like stab_find, except that it creates a new Stab_entry,
249 * initialized with type = st_none, if one did not exist already
250 * (it never returns NULL).
251 *
252 * A Stab_entry is a structure that contains at least the following
253 * members:
254 *
255 * char *name; // must not be modified
256 * enum sym_type type; // should be set
257 *
258 * The type field is initially set to st_none; it should be set to
259 * something else by the caller of stab_search. Other possible values
260 * of an enum sym_type can be added.
261 */
262
263Stab *
264stab_create ()
265{
266 Stab *sp;
267 sp = xnew (1, Stab);
268 *sp = NULL; /* a Stab starts out as a null Stab_entry* */
269 return sp;
270}
271
272Stab_entry *
273stab_find (stab, sym, symlen)
274 Stab *stab;
275 register char *sym;
276 register int symlen;
277{
278 register Stab_entry *se;
279 for (se = *stab; se != NULL; se = se->next)
280 {
281 if (se->symlen == symlen && strneq (se->sym, sym, symlen))
282 return se;
283 }
284
285 return NULL;
286}
287
288Stab_entry *
289stab_search (stab, sym, symlen)
290 register Stab *stab;
291 char *sym;
292 int symlen;
293{
294 register Stab_entry *se;
295 se = stab_find (stab, sym, symlen);
296
297 if (se == NULL)
298 {
299 /* make a new one */
300 se = xnew (1, Stab_entry);
301 se->sym = savenstr (sym, symlen);
302 se->symlen = symlen;
303 se->type = st_none;
304 se->next = *stab;
305 *stab = se;
306 }
307
308 return se;
309}
310
311/*
312 * NAME
313 * stab_type -- type of a symbol table entry
314 *
315 * SYNOPSIS
316 * enum sym_type stab_type (Stab_entry *se);
317 *
318 * WARNING
319 * May evaluate its argument more than once.
320 */
321
322#define stab_type(se) ((se)==NULL ? st_none : (se)->type)
323\f
324
325
326typedef int LINENO;
327
328typedef struct
329{
330 char *p;
331 int len;
c6d46f5f 332 LINENO lineno;
fe0b3356 333 logical named;
c6d46f5f 334} TOKEN;
c6d46f5f 335
13fde0cd 336/* C extensions.
c6d46f5f 337 */
13fde0cd
RS
338#define C_PLPL 0x00001 /* C++ */
339#define C_STAR 0x00003 /* C* */
340#define YACC 0x10000 /* yacc file */
c6d46f5f
JB
341
342char searchar = '/'; /* use /.../ searches */
343
344LINENO lineno; /* line number of current line */
345long charno; /* current character number */
c6d46f5f
JB
346
347long linecharno; /* charno of start of line; not used by C, but
348 * by every other language.
349 */
350
351char *curfile, /* current input file name */
352 *outfile, /* output file */
353 *white = " \f\t\n", /* white chars */
13fde0cd
RS
354 *endtk = " \t\n\"'#()[]{}=-+%*/&|^~!<>;,.:?", /* token ending chars */
355 /* token starting chars */
356 *begtk = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz$",
357 /* valid in-token chars */
358 *intk = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz$0123456789";
c6d46f5f 359
c6d46f5f
JB
360int append_to_tagfile; /* -a: append to tags */
361int emacs_tags_format; /* emacs style output (no -e option any more) */
362/* The following three default to 1 for etags, but to 0 for ctags. */
363int typedefs; /* -t: create tags for typedefs */
364int typedefs_and_cplusplus; /* -T: create tags for typedefs, level */
365 /* 0 struct/enum/union decls, and C++ */
366 /* member functions */
367int constantypedefs; /* -d: create tags for C #define and enum */
368 /* constants. Default under etags. Enum */
369 /* constants not implemented. */
370 /* -D: opposite of -d. Default under ctags. */
371int update; /* -u: update tags */
372int vgrind_style; /* -v: create vgrind style index output */
373int no_warnings; /* -w: suppress warnings */
374int cxref_style; /* -x: create cxref style output */
375int cplusplus; /* .[hc] means C++, not C */
376int noindentypedefs; /* -S: ignore indentation in C */
c6d46f5f
JB
377
378/* Name this program was invoked with. */
379char *progname;
380
4746118a
JB
381struct option longopts[] = {
382 { "append", no_argument, NULL, 'a' },
383 { "backward-search", no_argument, NULL, 'B' },
384 { "c++", no_argument, NULL, 'C' },
385 { "cxref", no_argument, NULL, 'x' },
386 { "defines", no_argument, NULL, 'd' },
387 { "forward-search", no_argument, NULL, 'F' },
388 { "help", no_argument, NULL, 'H' },
389 { "ignore-indentation", no_argument, NULL, 'S' },
390 { "include", required_argument, NULL, 'i' },
391 { "no-defines", no_argument, NULL, 'D' },
392 { "no-warn", no_argument, NULL, 'w' },
393 { "output", required_argument, NULL, 'o' },
394 { "typedefs", no_argument, NULL, 't' },
395 { "typedefs-and-c++", no_argument, NULL, 'T' },
396 { "update", no_argument, NULL, 'u' },
397 { "version", no_argument, NULL, 'V' },
398 { "vgrind", no_argument, NULL, 'v' },
399 { 0 }
400};
401
c6d46f5f
JB
402FILE *inf, /* ioptr for current input file */
403 *outf; /* ioptr for tags file */
404
405NODE *head; /* the head of the binary tree of tags */
406
407int permit_duplicates = 1; /* Nonzero means allow duplicate tags. */
408
409/* A `struct linebuffer' is a structure which holds a line of text.
410 `readline' reads a line from a stream into a linebuffer
411 and works regardless of the length of the line. */
412
413struct linebuffer
414{
415 long size;
416 char *buffer;
417};
418
419struct linebuffer lb; /* the current line */
c6d46f5f 420struct linebuffer filename_lb; /* used to read in filenames */
13fde0cd
RS
421struct
422{
423 FILEPOS linepos;
424 struct linebuffer lb; /* used by C_entries instead of lb */
425} lbs[2];
c6d46f5f 426\f
4746118a
JB
427void
428print_version ()
429{
430#ifdef CTAGS
431 printf ("CTAGS ");
432#ifdef ETAGS
433 printf ("and ");
434#endif
435#endif
436#ifdef ETAGS
437 printf ("ETAGS ");
438#endif
439 printf ("for Emacs version 19.0.\n");
440
441 exit (0);
442}
443
444void
445print_help ()
446{
447 printf ("These are the options accepted by %s. You may use unambiguous\n\
448abbreviations for the long option names.\n\n", progname);
449
52cc7c59
JB
450 puts ("-a, --append\n\
451 Append tag entries to existing tags file.");
452 puts ("-C, --c++\n\
4746118a
JB
453 Treat files with `.c' and `.h' extensions as C++ code, not C\n\
454 code. Files with `.C', `.H', `.cxx', `.hxx', or `.cc'\n\
52cc7c59
JB
455 extensions are always assumed to be C++ code.");
456 fputs ("-d, --defines\n\
4746118a
JB
457 Create tag entries for #defines, too.", stdout);
458
459#ifdef ETAGS
460 fputs (" This is the default\n\
461 behavior.", stdout);
462#endif
463
464 fputs ("\n\
465-D, --no-defines\n\
466 Don't create tag entries for #defines.", stdout);
467
468#ifdef CTAGS
469 fputs (" This is the default\n\
470 behavior.", stdout);
471#endif
472
473 puts ("\n\
474-o FILE, --output=FILE\n\
475 Write the tags to FILE.\n\
476-S, --ignore-indentation\n\
477 Don't rely on indentation quite as much as normal. Currently,\n\
478 this means not to assume that a closing brace in the first\n\
479 column is the final brace of a function or structure\n\
52cc7c59
JB
480 definition.");
481 puts ("-t, --typedefs\n\
4746118a 482 Generate tag entries for typedefs. This is the default\n\
52cc7c59
JB
483 behavior.");
484 puts ("-T, --typedefs-and-c++\n\
4746118a
JB
485 Generate tag entries for typedefs, struct/enum/union tags, and\n\
486 C++ member functions.");
487
488#ifdef ETAGS
489 puts ("-i FILE, --include=FILE\n\
490 Include a note in tag file indicating that, when searching for\n\
491 a tag, one should also consult the tags file FILE after\n\
492 checking the current file.");
493#endif
494
495#ifdef CTAGS
496 puts ("-B, --backward-search\n\
497 Write the search commands for the tag entries using '?', the\n\
52cc7c59
JB
498 backward-search command.");
499 puts ("-F, --forward-search\n\
4746118a 500 Write the search commands for the tag entries using '/', the\n\
52cc7c59
JB
501 forward-search command.");
502 puts ("-u, --update\n\
4746118a
JB
503 Update the tag entries for the given files, leaving tag\n\
504 entries for other files in place. Currently, this is\n\
505 implemented by deleting the existing entries for the given\n\
506 files and then rewriting the new entries at the end of the\n\
507 tags file. It is often faster to simply rebuild the entire\n\
52cc7c59
JB
508 tag file than to use this.");
509 puts ("-v, --vgrind\n\
4746118a
JB
510 Generates an index of items intended for human consumption,\n\
511 similar to the output of vgrind. The index is sorted, and\n\
52cc7c59
JB
512 gives the page number of each item.");
513 puts ("-x, --cxref\n\
4746118a
JB
514 Like --vgrind, but in the style of cxref, rather than vgrind.\n\
515 The output uses line numbers instead of page numbers, but\n\
516 beyond that the differences are cosmetic; try both to see\n\
52cc7c59
JB
517 which you like.");
518 puts ("-w, --no-warn\n\
4746118a
JB
519 Suppress warning messages about entries defined in multiple\n\
520 files.");
521#endif
522
523 puts ("-V, --version\n\
524 Print the version of the program.\n\
525-H, --help\n\
526 Print this help message.");
527
528 exit (0);
529}
530
531\f
c6d46f5f
JB
532void
533main (argc, argv)
534 int argc;
535 char *argv[];
536{
537 char cmd[100];
538 int i;
1e134a5f 539 unsigned int nincluded_files = 0;
72a339d7 540 char **included_files = xnew (argc, char *);
c6d46f5f
JB
541 char *this_file;
542#ifdef VMS
543 char got_err;
544
545 extern char *gfnames ();
546 extern char *massage_name ();
547#endif
548
549 progname = argv[0];
550
551#ifndef CTAGS
552 emacs_tags_format = 1;
553#else
554 emacs_tags_format = 0;
555#endif
556
557 /*
558 * If etags, always find typedefs and structure tags. Why not?
559 * Also default is to find macro constants.
560 */
561 if (emacs_tags_format)
562 typedefs = typedefs_and_cplusplus = constantypedefs = 1;
563
4746118a 564 for (;;)
c6d46f5f 565 {
4746118a 566 int opt;
3ed79319 567 opt = getopt_long (argc, argv, "aCdDo:f:StTi:BFuvxwVH", longopts, 0);
4746118a
JB
568
569 if (opt == EOF)
570 break;
571
572 switch (opt)
c6d46f5f 573 {
4746118a
JB
574 case '\0':
575 /* If getopt returns '\0', then it has already processed a
576 long-named option. We should do nothing. */
577 break;
578
579 /* Common options. */
580 case 'a':
581 append_to_tagfile++;
582 break;
583 case 'C':
584 cplusplus = 1;
585 break;
586 case 'd':
587 constantypedefs = 1;
588 break;
589 case 'D':
590 constantypedefs = 0;
591 break;
3ed79319 592 case 'f':
4746118a
JB
593 case 'o':
594 if (outfile)
c6d46f5f 595 {
4746118a 596 fprintf (stderr,
3ed79319 597 "%s: -%c flag may only be given once\n", progname, opt);
c6d46f5f
JB
598 goto usage;
599 }
4746118a
JB
600 outfile = optarg;
601 break;
602 case 'S':
603 noindentypedefs++;
604 break;
605 case 't':
606 typedefs++;
607 break;
608 case 'T':
609 typedefs++;
610 typedefs_and_cplusplus++;
611 break;
612 case 'V':
613 print_version ();
614 break;
615 case 'H':
616 print_help ();
617 break;
618
619 /* Etags options */
620 case 'i':
621 if (!emacs_tags_format)
622 goto usage;
623 included_files[nincluded_files++] = optarg;
624 break;
625
626 /* Ctags options. */
627 case 'B':
628 searchar = '?';
629 if (emacs_tags_format)
630 goto usage;
631 break;
632 case 'F':
633 searchar = '/';
634 if (emacs_tags_format)
635 goto usage;
636 break;
637 case 'u':
638 update++;
639 if (emacs_tags_format)
640 goto usage;
641 break;
642 case 'v':
643 vgrind_style++;
644 /*FALLTHRU*/
645 case 'x':
646 cxref_style++;
647 if (emacs_tags_format)
648 goto usage;
649 break;
650 case 'w':
651 no_warnings++;
652 if (emacs_tags_format)
653 goto usage;
654 break;
655
656 default:
657 goto usage;
c6d46f5f 658 }
c6d46f5f
JB
659 }
660
0e5ad25f 661 if (optind == argc && nincluded_files == 0)
c6d46f5f 662 {
4746118a
JB
663 fprintf (stderr, "%s: No input files specified.\n", progname);
664
c6d46f5f 665 usage:
0e5ad25f 666 fprintf (stderr, "%s: Try `%s --help' for a complete list of options.\n",
4746118a 667 progname, progname);
c6d46f5f
JB
668 exit (BAD);
669 }
670
671 if (outfile == 0)
672 {
673 outfile = emacs_tags_format ? "TAGS" : "tags";
674 }
675
676 init (); /* set up boolean "functions" */
677
678 initbuffer (&lb);
13fde0cd
RS
679 initbuffer (&lbs[0].lb);
680 initbuffer (&lbs[1].lb);
c6d46f5f
JB
681 initbuffer (&filename_lb);
682 /*
683 * loop through files finding functions
684 */
685 if (emacs_tags_format)
686 {
687 if (streq (outfile, "-"))
688 outf = stdout;
689 else
690 outf = fopen (outfile, append_to_tagfile ? "a" : "w");
691 if (!outf)
692 {
693 perror (outfile);
694 exit (1);
695 }
696 }
697
c6d46f5f 698#ifdef VMS
4746118a
JB
699 argc -= optind;
700 argv += optind;
701 while (gfnames (&argc, &argv, &got_err) != NULL)
c6d46f5f
JB
702 {
703 if (got_err)
704 {
705 error ("Can't find file %s\n", this_file);
706 argc--, argv++;
707 }
708 else
709 {
710 this_file = massage_name (this_file);
711#if 0
712 }
13fde0cd 713 } /* solely to balance out the ifdef'd parens above */
c6d46f5f
JB
714#endif
715#else
4746118a 716 for (; optind < argc; optind++)
c6d46f5f 717 {
4746118a 718 this_file = argv[optind];
c6d46f5f
JB
719 if (1)
720 {
721#endif
722 /* Input file named "-" means read file names from stdin
723 and use them. */
724 if (streq (this_file, "-"))
725 {
726 while (!feof (stdin))
727 {
728 (void) readline (&filename_lb, stdin);
729 if (strlen (filename_lb.buffer) > 0)
730 process_file (filename_lb.buffer);
731 }
732 }
733 else
734 process_file (this_file);
735 }
736 }
737
738 if (emacs_tags_format)
739 {
1e134a5f
RM
740 while (nincluded_files-- > 0)
741 fprintf (outf, "\f\n%s,include\n", *included_files++);
742
c6d46f5f
JB
743 (void) fclose (outf);
744 exit (0);
745 }
746
747 if (cxref_style)
748 {
749 put_entries (head);
750 exit (GOOD);
751 }
4746118a 752 if (update)
c6d46f5f 753 {
4746118a
JB
754 /* update cannot be set under VMS, so we may assume that argc
755 and argv have not been munged. */
756 for (i = optind; i < argc; i++)
c6d46f5f
JB
757 {
758 sprintf (cmd,
759 "mv %s OTAGS;fgrep -v '\t%s\t' OTAGS >%s;rm OTAGS",
760 outfile, argv[i], outfile);
761 (void) system (cmd);
762 }
763 append_to_tagfile++;
764 }
765 outf = fopen (outfile, append_to_tagfile ? "a" : "w");
766 if (outf == NULL)
767 {
768 perror (outfile);
769 exit (GOOD);
770 }
771 put_entries (head);
772 (void) fclose (outf);
773 if (update)
774 {
775 sprintf (cmd, "sort %s -o %s", outfile, outfile);
776 (void) system (cmd);
777 }
778 exit (GOOD);
779}
780
781
782/*
783 * This routine is called on each file argument.
784 */
785void
786process_file (file)
787 char *file;
788{
789 struct stat stat_buf;
790
2b878b4c 791 if (stat (file, &stat_buf) || !S_ISREG (stat_buf.st_mode))
c6d46f5f
JB
792 {
793 fprintf (stderr, "Skipping %s: it is not a regular file.\n", file);
794 return;
795 }
796
797 if (streq (file, outfile) && !streq (outfile, "-"))
798 {
799 fprintf (stderr, "Skipping inclusion of %s in self.\n", file);
800 return;
801 }
c6d46f5f
JB
802 find_entries (file);
803 if (emacs_tags_format)
804 {
2b878b4c 805 fprintf (outf, "\f\n%s,%d\n", file, total_size_of_entries (head));
c6d46f5f
JB
806 put_entries (head);
807 free_tree (head);
808 head = NULL;
809 }
810}
811
812/*
eb8c3be9 813 * This routine sets up the boolean pseudo-functions which work
99e0a2e0 814 * by setting boolean flags dependent upon the corresponding character
c6d46f5f
JB
815 * Every char which is NOT in that string is not a white char. Therefore,
816 * all of the array "_wht" is set to FALSE, and then the elements
817 * subscripted by the chars in "white" are set to TRUE. Thus "_wht"
818 * of a char is TRUE if it is the string "white", else FALSE.
819 */
820void
821init ()
822{
13fde0cd
RS
823 register char *sp;
824 register int i;
c6d46f5f
JB
825
826 for (i = 0; i < 0177; i++)
13fde0cd 827 _wht[i] = _etk[i] = _itk[i] = _btk[i] = FALSE;
c6d46f5f
JB
828 for (sp = white; *sp; sp++)
829 _wht[*sp] = TRUE;
830 for (sp = endtk; *sp; sp++)
831 _etk[*sp] = TRUE;
832 for (sp = intk; *sp; sp++)
833 _itk[*sp] = TRUE;
834 for (sp = begtk; *sp; sp++)
835 _btk[*sp] = TRUE;
c6d46f5f
JB
836 _wht[0] = _wht['\n'];
837 _etk[0] = _etk['\n'];
838 _btk[0] = _btk['\n'];
839 _itk[0] = _itk['\n'];
c6d46f5f
JB
840}
841
842/*
843 * This routine opens the specified file and calls the function
844 * which finds the function and type definitions.
845 */
846void
847find_entries (file)
848 char *file;
849{
850 char *cp;
851 void prolog_funcs ();
852
853 inf = fopen (file, "r");
854 if (inf == NULL)
855 {
856 perror (file);
857 return;
858 }
859 curfile = savestr (file);
8a6c8bcf 860 cp = etags_rindex (file, '.');
c6d46f5f
JB
861
862 header_file = (cp && (streq (cp + 1, "h")));
863
864 /* .tex, .aux or .bbl implies LaTeX source code */
865 if (cp && (streq (cp + 1, "tex") || streq (cp + 1, "aux")
866 || streq (cp + 1, "bbl")))
867 {
868 TEX_funcs (inf);
869 goto close_and_return;
870 }
871 /* .l or .el or .lisp (or .cl or .clisp or ...) implies lisp source code */
872 if (cp && (streq (cp + 1, "l")
873 || streq (cp + 1, "el")
874 || streq (cp + 1, "lsp")
875 || streq (cp + 1, "lisp")
876 || streq (cp + 1, "cl")
877 || streq (cp + 1, "clisp")))
878 {
879 L_funcs (inf);
880 goto close_and_return;
881 }
882 /* .scm or .sm or .scheme or ... implies scheme source code */
883 if (cp && (streq (cp + 1, "sm")
884 || streq (cp + 1, "scm")
885 || streq (cp + 1, "scheme")
886 || streq (cp + 1, "t")
887 || streq (cp + 1, "sch")
888 || streq (cp + 1, "SM")
889 || streq (cp + 1, "SCM")
890 /* The `SCM' or `scm' prefix with a version number */
891 || (cp[-1] == 'm' && cp[-2] == 'c' && cp[-3] == 's'
892 && string_numeric_p (cp + 1))
893 || (cp[-1] == 'M' && cp[-2] == 'C' && cp[-3] == 'S'
894 && string_numeric_p (cp + 1))))
895 {
896 Scheme_funcs (inf);
897 fclose (inf);
898 return;
899 }
13fde0cd
RS
900 /* Assume that ".s" or ".a" is assembly code. -wolfgang.
901 Or even ".sa". */
902 if (cp && (streq (cp + 1, "s")
903 || streq (cp + 1, "a")
904 || streq (cp + 1, "sa")))
c6d46f5f
JB
905 {
906 Asm_funcs (inf);
907 fclose (inf);
908 return;
909 }
910 /* .C or .H or .cxx or .hxx or .cc: a C++ file */
911 if (cp && (streq (cp + 1, "C")
912 || streq (cp + 1, "H")
913 || streq (cp + 1, "cxx")
914 || streq (cp + 1, "hxx")
915 || streq (cp + 1, "cc")))
916 {
917 C_entries (C_PLPL); /* C++ */
918 goto close_and_return;
919 }
920 /* .cs or .hs: a C* file */
13fde0cd
RS
921 if (cp && (streq (cp + 1, "cs")
922 || streq (cp + 1, "hs")))
c6d46f5f
JB
923 {
924 C_entries (C_STAR);
925 goto close_and_return;
926 }
13fde0cd
RS
927 /* .y: a yacc file */
928 if (cp && (streq (cp + 1, "y")))
929 {
930 C_entries (YACC);
931 goto close_and_return;
932 }
c6d46f5f 933 /* .pl implies prolog source code */
13fde0cd 934 if (cp && streq (cp + 1, "pl"))
c6d46f5f
JB
935 {
936 prolog_funcs (inf);
937 goto close_and_return;
938 }
939 /* .p or .pas: a Pascal file */
940 if (cp && (streq (cp + 1, "p")
941 || streq (cp + 1, "pas")))
942 {
943 PAS_funcs (inf);
944 goto close_and_return;
945 }
1e8e1162 946 /* If .f or .for, assume it is fortran or nothing. */
13fde0cd
RS
947 if (cp && (streq (cp + 1, "f")
948 || streq (cp + 1, "for")))
1e8e1162
RS
949 {
950 PF_funcs (inf);
951 goto close_and_return;
952 }
c6d46f5f 953 /* if not a .c or .h or .y file, try fortran */
1e8e1162
RS
954 if (cp && ((cp[1] != 'c'
955 && cp[1] != 'h'
956 && cp[1] != 'y')
957 || (cp[1] != 0 && cp[2] != 0)))
c6d46f5f
JB
958 {
959 if (PF_funcs (inf) != 0)
960 goto close_and_return;
961 rewind (inf); /* no fortran tags found, try C */
962 }
963 C_entries (cplusplus ? C_PLPL : 0);
964
965close_and_return:
966 (void) fclose (inf);
967}
968
969/* Nonzero if string STR is composed of digits. */
970
971int
972string_numeric_p (str)
973 char *str;
974{
975 while (*str)
976 {
977 if (*str < '0' || *str > '9')
978 return 0;
979 }
980 return 1;
981}
982\f
983/* Record a tag. */
984/* Should take a TOKEN* instead!! */
c6d46f5f 985void
fe0b3356 986pfnote (name, is_func, named, linestart, linelen, lno, cno)
c6d46f5f
JB
987 char *name; /* tag name */
988 logical is_func; /* function or type name? */
fe0b3356 989 logical named; /* tag different from text of definition? */
c6d46f5f
JB
990 char *linestart;
991 int linelen;
992 int lno;
993 long cno;
994{
995 register char *fp;
996 register NODE *np;
997 char tem[51];
998 char c;
999
1000 np = (NODE *) malloc (sizeof (NODE));
1001 if (np == NULL)
1002 {
1003 if (!emacs_tags_format)
1004 {
1005 /* It's okay to output early in etags -- it only disrupts the
1006 * character count of the tag entries, which is no longer used
1007 * by tags.el anyway.
1008 */
1009 error ("too many entries to sort");
1010 }
1011 put_entries (head);
1012 free_tree (head);
1013 head = NULL;
1014 np = xnew (1, NODE);
1015 }
1016 /* If ctags mode, change name "main" to M<thisfilename>. */
1017 if (!emacs_tags_format && !cxref_style && streq (name, "main"))
1018 {
8a6c8bcf 1019 fp = etags_rindex (curfile, '/');
c6d46f5f 1020 name = concat ("M", fp == 0 ? curfile : fp + 1, "");
8a6c8bcf 1021 fp = etags_rindex (name, '.');
c6d46f5f
JB
1022 if (fp && fp[1] != '\0' && fp[2] == '\0')
1023 *fp = 0;
fe0b3356 1024 named = TRUE;
c6d46f5f
JB
1025 }
1026 np->name = savestr (name);
1027 np->file = curfile;
1028 np->is_func = is_func;
fe0b3356 1029 np->named = named;
c6d46f5f
JB
1030 np->lno = lno;
1031 /* UNCOMMENT THE +1 HERE: */
1032 np->cno = cno /* + 1 */ ; /* our char numbers are 0-base; emacs's are 1-base */
1033 np->left = np->right = 0;
1034 if (emacs_tags_format)
1035 {
1036 c = linestart[linelen];
1037 linestart[linelen] = 0;
1038 }
1039 else if (cxref_style == 0)
1040 {
1041 sprintf (tem, strlen (linestart) < 50 ? "%s$" : "%.50s", linestart);
1042 linestart = tem;
1043 }
1044 np->pat = savestr (linestart);
1045 if (emacs_tags_format)
1046 {
1047 linestart[linelen] = c;
1048 }
1049
1050 add_node (np, &head);
1051}
1052
1053/*
1054 * free_tree ()
1055 * recurse on left children, iterate on right children.
1056 */
1057void
1058free_tree (node)
1059 register NODE *node;
1060{
1061 while (node)
1062 {
1063 register NODE *node_right = node->right;
1064 free_tree (node->left);
1065 free (node->name);
1066 free (node->pat);
1067 free ((char *) node);
1068 node = node_right;
1069 }
1070}
1071
1072/*
1073 * add_node ()
1074 * Adds a node to the tree of nodes. In etags mode, we don't keep
1075 * it sorted; we just keep a linear list. In ctags mode, maintain
1076 * an ordered tree, with no attempt at balancing.
1077 *
1078 * add_node is the only function allowed to add nodes, so it can
1079 * maintain state.
1080 */
8f53e1ee
RS
1081/* Must avoid static vars within functions since some systems
1082 #define static as nothing. */
1083static NODE *last_node = NULL;
1084
c6d46f5f
JB
1085void
1086add_node (node, cur_node_p)
1087 NODE *node, **cur_node_p;
1088{
1089 register int dif;
1090 register NODE *cur_node = *cur_node_p;
c6d46f5f
JB
1091
1092 if (cur_node == NULL)
1093 {
1094 *cur_node_p = node;
1095 last_node = node;
1096 return;
1097 }
1098
1099 if (emacs_tags_format)
1100 {
1101 /* Etags Mode */
1102 if (!last_node)
1103 fatal ("internal error in add_node");
1104 last_node->right = node;
1105 last_node = node;
1106 }
1107 else
1108 {
1109 /* Ctags Mode */
1110 dif = strcmp (node->name, cur_node->name);
1111
1112 /*
1113 * If this tag name matches an existing one, then
1114 * do not add the node, but maybe print a warning.
1115 */
1116 if (!dif)
1117 {
1118 if (node->file == cur_node->file)
1119 {
1120 if (!no_warnings)
1121 {
1122 fprintf (stderr, "Duplicate entry in file %s, line %d: %s\n",
1123 node->file, lineno, node->name);
1124 fprintf (stderr, "Second entry ignored\n");
1125 }
1126 return;
1127 }
1128 if (!cur_node->been_warned && !no_warnings)
1129 {
1130 fprintf (stderr,
1131 "Duplicate entry in files %s and %s: %s (Warning only)\n",
1132 node->file, cur_node->file, node->name);
1133 }
1134 cur_node->been_warned = TRUE;
1135 return;
1136 }
1137
1138 /* Maybe refuse to add duplicate nodes. */
1139 if (!permit_duplicates)
1140 {
1141 if (!strcmp (node->name, cur_node->name)
1142 && !strcmp (node->file, cur_node->file))
1143 return;
1144 }
1145
1146 /* Actually add the node */
1147 add_node (node, dif < 0 ? &cur_node->left : &cur_node->right);
1148 }
1149}
1150\f
1151void
1152put_entries (node)
13fde0cd 1153 register NODE *node;
c6d46f5f 1154{
13fde0cd 1155 register char *sp;
c6d46f5f
JB
1156
1157 if (node == NULL)
1158 return;
1159
1160 /* Output subentries that precede this one */
1161 put_entries (node->left);
1162
1163 /* Output this entry */
1164
1165 if (emacs_tags_format)
1166 {
fe0b3356 1167 if (node->named)
c6d46f5f
JB
1168 {
1169 fprintf (outf, "%s\177%s\001%d,%d\n",
cc6d6e58
RM
1170 node->pat, node->name,
1171 node->lno, node->cno);
c6d46f5f
JB
1172 }
1173 else
1174 {
1175 fprintf (outf, "%s\177%d,%d\n",
cc6d6e58
RM
1176 node->pat,
1177 node->lno, node->cno);
c6d46f5f
JB
1178 }
1179 }
1180 else if (!cxref_style)
1181 {
1182 fprintf (outf, "%s\t%s\t",
1183 node->name, node->file);
1184
1185 if (node->is_func)
1186 { /* a function */
1187 putc (searchar, outf);
1188 putc ('^', outf);
1189
1190 for (sp = node->pat; *sp; sp++)
1191 {
1192 if (*sp == '\\' || *sp == searchar)
1193 putc ('\\', outf);
1194 putc (*sp, outf);
1195 }
1196 putc (searchar, outf);
1197 }
1198 else
1199 { /* a typedef; text pattern inadequate */
1200 fprintf (outf, "%d", node->lno);
1201 }
1202 putc ('\n', outf);
1203 }
1204 else if (vgrind_style)
1205 fprintf (stdout, "%s %s %d\n",
1206 node->name, node->file, (node->lno + 63) / 64);
1207 else
daa37602 1208 fprintf (stdout, "%-16s %3d %-16s %s\n",
c6d46f5f
JB
1209 node->name, node->lno, node->file, node->pat);
1210
1211 /* Output subentries that follow this one */
1212 put_entries (node->right);
1213}
1214
1215/* Length of a number's decimal representation. */
1216int
1217number_len (num)
1218 long num;
1219{
1220 int len = 0;
1221 if (!num)
1222 return 1;
1223 for (; num; num /= 10)
1224 ++len;
1225 return len;
1226}
1227
1228/*
1229 * Return total number of characters that put_entries will output for
1230 * the nodes in the subtree of the specified node. Works only if emacs_tags_format
1231 * is set, but called only in that case. This count is irrelevant with
1232 * the new tags.el, but is still supplied for backward compatibility.
1233 */
1234int
1235total_size_of_entries (node)
13fde0cd 1236 register NODE *node;
c6d46f5f 1237{
13fde0cd 1238 register int total;
c6d46f5f
JB
1239
1240 if (node == NULL)
1241 return 0;
1242
1243 total = 0;
1244 for (; node; node = node->right)
1245 {
1246 /* Count left subentries. */
1247 total += total_size_of_entries (node->left);
1248
1249 /* Count this entry */
1250 total += strlen (node->pat) + 1;
1251 total += number_len ((long) node->lno) + 1 + number_len (node->cno) + 1;
fe0b3356 1252 if (node->named)
c6d46f5f
JB
1253 total += 1 + strlen (node->name); /* \001name */
1254 }
1255
1256 return total;
1257}
1258\f
1259/*
1260 * The C symbol tables.
1261 */
1262
1263Stab *C_stab, *C_PLPL_stab, *C_STAR_stab;
1264
1265/*
1266 * SYNOPSIS
1267 * Stab *get_C_stab (int c_ext);
1268 */
13fde0cd
RS
1269#define get_C_stab(c_ext) ((c_ext & C_STAR) ? C_STAR_stab : \
1270 (c_ext & C_PLPL) ? C_PLPL_stab : \
c6d46f5f
JB
1271 C_stab)
1272
1273void
1274add_keyword (stab, sym, type)
1275 Stab *stab;
1276 char *sym;
1277 enum sym_type type;
1278{
1279 stab_search (stab, sym, strlen (sym))->type = type;
1280}
1281
1282Stab *
1283C_create_stab (c_ext)
1284 int c_ext;
1285{
1286 Stab *stab;
1287
1288 stab = stab_create ();
1289
1290 /* C, C++ and C* */
1291 if (c_ext & C_PLPL)
1292 add_keyword (stab, "class", st_C_struct);
1293 if (c_ext & C_STAR)
1294 add_keyword (stab, "domain", st_C_struct);
1295 add_keyword (stab, "union", st_C_struct);
1296 add_keyword (stab, "struct", st_C_struct);
1297 add_keyword (stab, "enum", st_C_enum);
1298 add_keyword (stab, "typedef", st_C_typedef);
1299 add_keyword (stab, "define", st_C_define);
1300 add_keyword (stab, "long", st_C_typespec);
1301 add_keyword (stab, "short", st_C_typespec);
1302 add_keyword (stab, "int", st_C_typespec);
1303 add_keyword (stab, "char", st_C_typespec);
1304 add_keyword (stab, "float", st_C_typespec);
1305 add_keyword (stab, "double", st_C_typespec);
1306 add_keyword (stab, "signed", st_C_typespec);
1307 add_keyword (stab, "unsigned", st_C_typespec);
13fde0cd
RS
1308 add_keyword (stab, "auto", st_C_typespec);
1309 add_keyword (stab, "void", st_C_typespec);
1310 add_keyword (stab, "extern", st_C_typespec);
1311 add_keyword (stab, "static", st_C_typespec);
c6d46f5f
JB
1312 add_keyword (stab, "const", st_C_typespec);
1313 add_keyword (stab, "volatile", st_C_typespec);
1314
1315 return stab;
1316}
1317
1318void
1319C_create_stabs ()
1320{
1321 C_stab = C_create_stab (0);
1322 C_PLPL_stab = C_create_stab (C_PLPL);
1323 C_STAR_stab = C_create_stab (C_STAR | C_PLPL);
1324}
1325\f
13fde0cd 1326 /*
13fde0cd
RS
1327 * C functions are recognized using a simple finite automaton.
1328 * funcdef is its state variable.
1329 */
1330typedef enum
1331{
31d4b314
FP
1332 fnone, /* nothing seen */
1333 ftagseen, /* function-like tag seen */
1334 finlist, /* in parameter list */
1335 flistseen, /* after parameter list */
1336 fignore, /* before open brace */
13fde0cd
RS
1337} FUNCST;
1338FUNCST funcdef;
1339
1340
1341 /* typedefs are recognized using a simple finite automaton.
1342 * typeddef is its state variable.
1343 */
1344typedef enum
1345{
31d4b314
FP
1346 tnone, /* nothing seen */
1347 ttypedseen, /* typedef keyword seen */
1348 tinbody, /* inside typedef body */
1349 tend, /* just before typedef tag */
13fde0cd
RS
1350} TYPEDST;
1351TYPEDST typdef;
1352
1353
1354 /* struct tags for C++ are recognized using another simple
1355 * finite automaton. `structdef' is its state variable.
1356 * This machinery is only invoked for C++; otherwise structdef
1357 * should remain snone. However, this machinery can easily be
1358 * adapted to find structure tags in normal C code.
1359 */
1360typedef enum
1361{
1362 snone, /* nothing seen yet */
1363 skeyseen, /* struct-like keyword seen */
1364 stagseen, /* struct-like tag seen */
1365 scolonseen, /* colon seen after struct-like tag */
31d4b314 1366 sinbody, /* in struct body: recognize member func defs*/
13fde0cd
RS
1367} STRUCTST;
1368STRUCTST structdef;
1369/*
1370 * When structdef is stagseen, scolonseen, or sinbody, structtag is the
1371 * struct tag, and structkey is the preceding struct-like keyword.
1372 */
1373char structtag[BUFSIZ];
1374Stab_entry *structkey;
1375
1376/*
1377 * Yet another little state machine to deal with preprocessor lines.
1378 */
1379typedef enum
1380{
1381 dnone, /* nothing seen */
1382 dsharpseen, /* '#' seen as first char on line */
1383 ddefineseen, /* '#' and 'define' seen */
31d4b314 1384 dignorerest, /* ignore rest of line */
13fde0cd
RS
1385} DEFINEST;
1386DEFINEST definedef;
1387
1388/*
1389 * Set this to TRUE, and the next token considered is called a function.
1390 * Used only for GNUmacs's function-defining macros.
1391 */
1392logical next_token_is_func;
1393
1394/*
1395 * TRUE in the rules part of a yacc file, FALSE outside (parse as C).
1396 */
1397logical yacc_rules;
1398
c6d46f5f
JB
1399/*
1400 * C_entries ()
13fde0cd
RS
1401 * This routine finds functions, typedefs, #define's and
1402 * struct/union/enum definitions in C syntax and adds them
c6d46f5f
JB
1403 * to the list.
1404 */
1405
13fde0cd
RS
1406#define curlb (lbs[curndx].lb)
1407#define othlb (lbs[1-curndx].lb)
1408#define newlb (lbs[newndx].lb)
1409#define curlinepos (lbs[curndx].linepos)
1410#define othlinepos (lbs[1-curndx].linepos)
1411#define newlinepos (lbs[newndx].linepos)
1412
c6d46f5f 1413#define CNL_SAVE_DEFINEDEF \
13fde0cd
RS
1414do { \
1415 SET_FILEPOS (curlinepos, inf, charno); \
c6d46f5f 1416 lineno++; \
13fde0cd
RS
1417 charno += readline (&curlb, inf); \
1418 lp = curlb.buffer; \
1419 quotednl = FALSE; \
1420 newndx = curndx; \
1421} while (FALSE)
c6d46f5f
JB
1422
1423#define CNL \
13fde0cd 1424do { \
c6d46f5f
JB
1425 CNL_SAVE_DEFINEDEF; \
1426 definedef = dnone; \
13fde0cd
RS
1427} while (FALSE)
1428
fe0b3356 1429#define MAKE_TAG_FROM_NEW_LB(isfun) pfnote (nameb, isfun, tok.named, \
13fde0cd 1430 newlb.buffer, tokoff + toklen + 1, tok.lineno, GET_CHARNO (newlinepos))
fe0b3356 1431#define MAKE_TAG_FROM_OTH_LB(isfun) pfnote (nameb, isfun, tok.named, \
13fde0cd 1432 othlb.buffer, tokoff + toklen + 1, tok.lineno, GET_CHARNO (othlinepos))
c6d46f5f
JB
1433
1434void
1435C_entries (c_ext)
1436 int c_ext; /* extension of C? */
1437{
13fde0cd 1438 register char c; /* latest char read; '\0' for end of line */
c6d46f5f 1439 register char *lp; /* pointer one beyond the character `c' */
13fde0cd
RS
1440 int curndx, newndx; /* indices for current and new lb */
1441 TOKEN tok; /* latest token read for funcdef & structdef */
fe0b3356 1442 char nameb[BUFSIZ]; /* latest token name for funcdef & structdef */
13fde0cd
RS
1443 register int tokoff; /* offset in line of start of latest token */
1444 register int toklen; /* length of latest token */
591fa824 1445 int cblev; /* current curly brace level */
13fde0cd
RS
1446 logical incomm, inquote, inchar, quotednl, midtoken;
1447 logical cplpl;
c6d46f5f 1448
13fde0cd 1449 curndx = newndx = 0;
c6d46f5f
JB
1450 lineno = 0;
1451 charno = 0;
13fde0cd 1452 lp = curlb.buffer;
c6d46f5f
JB
1453 *lp = 0;
1454
13fde0cd
RS
1455 definedef = dnone; funcdef = fnone; typdef= tnone; structdef= snone;
1456 next_token_is_func = yacc_rules = FALSE;
1457 midtoken = inquote = inchar = incomm = quotednl = FALSE;
591fa824 1458 cblev = 0;
13fde0cd 1459 cplpl = c_ext & C_PLPL;
c6d46f5f
JB
1460
1461 C_create_stabs ();
1462
1463 while (!feof (inf))
1464 {
1465 c = *lp++;
c6d46f5f
JB
1466 if (c == '\\')
1467 {
4746118a
JB
1468 /* If we're at the end of the line, the next character is a
1469 '\0'; don't skip it, because it's the thing that tells us
1470 to read the next line. */
13fde0cd 1471 if (*lp == '\0')
99e0a2e0 1472 {
13fde0cd 1473 quotednl = TRUE;
99e0a2e0
RS
1474 continue;
1475 }
1e134a5f 1476 lp++;
c6d46f5f
JB
1477 c = ' ';
1478 }
1479 else if (incomm)
1480 {
13fde0cd 1481 switch (c)
c6d46f5f 1482 {
13fde0cd
RS
1483 case '*':
1484 if (*lp == '/')
1485 {
1486 c = *lp++;
1487 incomm = FALSE;
1488 }
1489 break;
1490 case '\0':
1491 /* Newlines inside comments do not end macro definitions in
1492 traditional cpp. */
1493 CNL_SAVE_DEFINEDEF;
1494 break;
c6d46f5f 1495 }
13fde0cd 1496 continue;
c6d46f5f
JB
1497 }
1498 else if (inquote)
1499 {
13fde0cd
RS
1500 switch (c)
1501 {
1502 case '"':
1503 inquote = FALSE;
1504 break;
1505 case '\0':
1506 /* Newlines inside strings, do not end macro definitions
1507 in traditional cpp, even though compilers don't
1508 usually accept them. */
1509 CNL_SAVE_DEFINEDEF;
1510 break;
1511 }
1512 continue;
c6d46f5f
JB
1513 }
1514 else if (inchar)
1515 {
1516 if (c == '\'')
1517 inchar = FALSE;
1518 continue;
1519 }
13fde0cd 1520 else
c6d46f5f
JB
1521 switch (c)
1522 {
1523 case '"':
1524 inquote = TRUE;
1525 continue;
1526 case '\'':
1527 inchar = TRUE;
1528 continue;
1529 case '/':
1530 if (*lp == '*')
1531 {
1532 lp++;
1533 incomm = TRUE;
13fde0cd 1534 continue;
c6d46f5f 1535 }
13fde0cd 1536 else if (cplpl && *lp == '/')
c6d46f5f 1537 {
daa37602
JB
1538 c = 0;
1539 break;
c6d46f5f
JB
1540 }
1541 continue;
13fde0cd
RS
1542 case '%':
1543 if ((c_ext & YACC) && *lp == '%')
1544 {
1545 /* entering or exiting rules section in yacc file */
1546 lp++;
1547 definedef = dnone; funcdef = fnone;
1548 typdef= tnone; structdef= snone;
1549 next_token_is_func = FALSE;
1550 midtoken = inquote = inchar = incomm = quotednl = FALSE;
591fa824 1551 cblev = 0;
13fde0cd
RS
1552 yacc_rules = !yacc_rules;
1553 continue;
591fa824 1554 }
c6d46f5f 1555 case '#':
13fde0cd 1556 if (lp == newlb.buffer + 1 && definedef == dnone)
c6d46f5f
JB
1557 definedef = dsharpseen;
1558 continue;
13fde0cd 1559 } /* switch (c) */
c6d46f5f 1560
c6d46f5f 1561
591fa824
RS
1562 /* Consider token only if some complicated conditions are satisfied. */
1563 if (((cblev == 0 && structdef != scolonseen)
1564 || (cblev == 1 && cplpl && structdef == sinbody))
13fde0cd 1565 && definedef != dignorerest
13fde0cd 1566 && funcdef != finlist)
c6d46f5f
JB
1567 {
1568 if (midtoken)
1569 {
1570 if (endtoken (c))
1571 {
13fde0cd 1572 if (cplpl && c == ':' && *lp == ':' && intoken (*(lp + 1)))
c6d46f5f
JB
1573 {
1574 /*
1575 * This handles :: in the middle, but not at beginning
1576 * of an identifier.
1577 */
1578 lp += 2;
1579 toklen += 3;
1580 }
1581 else
1582 {
fe0b3356 1583 logical is_func = FALSE;
c6d46f5f 1584
13fde0cd
RS
1585 tok.lineno = lineno;
1586 tok.p = newlb.buffer + tokoff;
c6d46f5f 1587 tok.len = toklen;
fe0b3356 1588 tok.named = FALSE;
13fde0cd
RS
1589 if (yacc_rules
1590 || consider_token (c, lp, &tok,
591fa824 1591 c_ext, cblev, &is_func))
c6d46f5f 1592 {
99e0a2e0 1593 if (structdef == sinbody
fe0b3356
FP
1594 && definedef == dnone
1595 && is_func)
1596 /* function defined in C++ class body */
1597 {
1598 tok.named = TRUE;
1599 sprintf (nameb, "%s::%.*s",
13fde0cd
RS
1600 ((structtag[0] == '\0')
1601 ? "_anonymous_" : structtag),
c6d46f5f 1602 tok.len, tok.p);
c6d46f5f
JB
1603 }
1604 else
1605 {
fe0b3356 1606 sprintf (nameb, "%.*s", tok.len, tok.p);
c6d46f5f 1607 }
13fde0cd 1608
fe0b3356
FP
1609 if (structdef == stagseen
1610 || typdef == tend)
1611 tok.named = TRUE;
1612
591fa824
RS
1613 if (funcdef == ftagseen
1614 || structdef == stagseen
1615 || typdef == tend)
13fde0cd
RS
1616 {
1617 if (newndx == curndx)
1618 curndx = 1 - curndx; /* switch line buffers */
1619 }
1620 else
1621 MAKE_TAG_FROM_NEW_LB (is_func);
c6d46f5f
JB
1622 }
1623 midtoken = FALSE;
1624 }
13fde0cd 1625 } /* if (endtoken (c)) */
c6d46f5f 1626 else if (intoken (c))
13fde0cd
RS
1627 {
1628 toklen++;
1629 continue;
1630 }
1631 } /* if (midtoken) */
c6d46f5f
JB
1632 else if (begtoken (c))
1633 {
13fde0cd
RS
1634 switch (funcdef)
1635 {
1636 case flistseen:
1637 MAKE_TAG_FROM_OTH_LB (TRUE);
31d4b314
FP
1638 funcdef = fignore;
1639 break;
13fde0cd
RS
1640 case ftagseen:
1641 funcdef = fnone;
1642 break;
1643 }
1644 if (structdef == stagseen)
1645 structdef = snone;
1646 if (!yacc_rules || lp == newlb.buffer + 1)
1647 {
1648 tokoff = lp - 1 - newlb.buffer;
1649 toklen = 1;
1650 midtoken = TRUE;
1651 }
1652 continue;
c6d46f5f 1653 }
13fde0cd
RS
1654 } /* if must look at token */
1655
1656
1657 /* Detect end of line, colon, comma, semicolon and various braces
1658 after having handled the last token on the line.*/
1659 switch (c)
1e134a5f 1660 {
13fde0cd
RS
1661 case ':':
1662 if (structdef == stagseen)
1663 structdef = scolonseen;
1664 else if (yacc_rules && funcdef == ftagseen)
57e83cfe 1665 {
13fde0cd 1666 MAKE_TAG_FROM_OTH_LB (FALSE);
31d4b314 1667 funcdef = fignore;
57e83cfe 1668 }
13fde0cd
RS
1669 break;
1670 case ';':
591fa824
RS
1671 if (cblev == 0 && typdef == tend)
1672 {
1673 typdef = tnone;
1674 MAKE_TAG_FROM_OTH_LB (FALSE);
1675 }
31d4b314
FP
1676 if (funcdef != fignore)
1677 funcdef = fnone;
13fde0cd
RS
1678 /* FALLTHRU */
1679 case ',':
13fde0cd
RS
1680 /* FALLTHRU */
1681 case '[':
31d4b314 1682 if (funcdef != finlist && funcdef != fignore)
13fde0cd
RS
1683 funcdef = fnone;
1684 if (structdef == stagseen)
1685 structdef = snone;
1686 break;
1687 case '(':
1688 switch (funcdef)
57e83cfe 1689 {
13fde0cd
RS
1690 case ftagseen:
1691 funcdef = finlist;
1692 break;
1693 case finlist:
1694 case flistseen:
1695 funcdef = fnone;
1696 break;
57e83cfe 1697 }
13fde0cd
RS
1698 break;
1699 case ')':
1700 if (funcdef == finlist)
1701 funcdef = flistseen;
1702 break;
1703 case '{':
1704 if (typdef == ttypedseen)
1705 typdef = tinbody;
1706 switch (structdef)
1707 {
1708 case skeyseen: /* unnamed struct */
1709 structtag[0] = '\0';
1710 structdef = sinbody;
1711 break;
1712 case stagseen:
1713 case scolonseen: /* named struct */
1714 structdef = sinbody;
1715 MAKE_TAG_FROM_OTH_LB (FALSE);
1716 break;
1717 }
31d4b314
FP
1718 switch (funcdef)
1719 {
1720 case flistseen:
1721 MAKE_TAG_FROM_OTH_LB (TRUE);
1722 /* FALLTHRU */
1723 case fignore:
1724 funcdef = fnone;
1725 }
591fa824 1726 cblev++;
31d4b314 1727 break;
13fde0cd
RS
1728 case '*':
1729 if (funcdef == flistseen)
1730 {
1731 MAKE_TAG_FROM_OTH_LB (TRUE);
31d4b314 1732 funcdef = fignore;
13fde0cd
RS
1733 }
1734 break;
1735 case '}':
1736 if (!noindentypedefs && lp == newlb.buffer + 1)
591fa824
RS
1737 cblev = 0; /* reset curly brace level if first column */
1738 else if (cblev > 0)
1739 cblev--;
1740 if (cblev == 0)
13fde0cd
RS
1741 {
1742 if (typdef == tinbody)
1743 typdef = tend;
1744 structdef = snone;
1745 (void) strcpy (structtag, "<error 2>");
1746 }
1747 break;
1748 case '\0':
1749 /* If a macro spans multiple lines don't reset its state. */
1750 if (quotednl)
1751 CNL_SAVE_DEFINEDEF;
1752 else
1753 CNL;
1754 break;
1755 } /* switch (c) */
1756
1757 } /* while not eof */
c6d46f5f
JB
1758}
1759
1760/*
1761 * consider_token ()
1762 * checks to see if the current token is at the start of a
13fde0cd
RS
1763 * function, or corresponds to a typedef, or is a struct/union/enum
1764 * tag.
c6d46f5f 1765 *
13fde0cd 1766 * *IS_FUNC gets TRUE iff the token is a function or macro with args.
c6d46f5f
JB
1767 * C_EXT is which language we are looking at.
1768 *
1769 * In the future we will need some way to adjust where the end of
1770 * the token is; for instance, implementing the C++ keyword
1771 * `operator' properly will adjust the end of the token to be after
1772 * whatever follows `operator'.
1773 *
1774 * Globals
13fde0cd
RS
1775 * funcdef IN OUT
1776 * structdef IN OUT
1777 * definedef IN OUT
1778 * typdef IN OUT
1779 * next_token_is_func IN OUT
c6d46f5f
JB
1780 */
1781
1782logical
591fa824 1783consider_token (c, lp, tokp, c_ext, cblev, is_func)
13fde0cd
RS
1784 register char c; /* IN: first char after the token */
1785 register char *lp; /* IN: lp points to 2nd char after the token */
591fa824
RS
1786 register TOKEN *tokp; /* IN: token pointer */
1787 int c_ext; /* IN: C extensions mask */
1788 int cblev; /* IN: curly brace level */
13fde0cd 1789 logical *is_func; /* OUT */
c6d46f5f 1790{
c6d46f5f
JB
1791 logical firsttok; /* TRUE if have seen first token in ()'s */
1792 Stab_entry *tokse = stab_find (get_C_stab (c_ext), tokp->p, tokp->len);
1793 enum sym_type toktype = stab_type (tokse);
1794
c6d46f5f 1795 /*
13fde0cd 1796 * Advance the definedef state machine.
c6d46f5f
JB
1797 */
1798 switch (definedef)
1799 {
1800 case dnone:
1801 /* We're not on a preprocessor line. */
1802 break;
1803 case dsharpseen:
1804 if (toktype == st_C_define)
1805 {
1806 definedef = ddefineseen;
c6d46f5f
JB
1807 }
1808 else
1809 {
1810 definedef = dignorerest;
c6d46f5f 1811 }
13fde0cd 1812 return (FALSE);
c6d46f5f
JB
1813 case ddefineseen:
1814 /*
1815 * Make a tag for any macro.
c6d46f5f 1816 */
c6d46f5f 1817 definedef = dignorerest;
13fde0cd 1818 *is_func = (c == '(');
c6d46f5f 1819 if (!*is_func && !constantypedefs)
13fde0cd
RS
1820 return (FALSE);
1821 else
1822 return (TRUE);
c6d46f5f 1823 case dignorerest:
13fde0cd 1824 return (FALSE);
c6d46f5f
JB
1825 default:
1826 error ("internal error: definedef value");
1827 }
1828
1829 /*
13fde0cd 1830 * Now typedefs
c6d46f5f 1831 */
13fde0cd 1832 switch (typdef)
c6d46f5f 1833 {
13fde0cd
RS
1834 case tnone:
1835 if (toktype == st_C_typedef)
c6d46f5f 1836 {
13fde0cd
RS
1837 if (typedefs)
1838 typdef = ttypedseen;
1839 return (FALSE);
c6d46f5f 1840 }
13fde0cd
RS
1841 break;
1842 case ttypedseen:
1843 switch (toktype)
c6d46f5f 1844 {
13fde0cd
RS
1845 case st_none:
1846 case st_C_typespec:
1847 typdef = tend;
1848 break;
1849 case st_C_struct:
1850 case st_C_enum:
1e134a5f 1851 break;
c6d46f5f 1852 }
13fde0cd 1853 /* Do not return here, so the structdef stuff has a chance. */
c6d46f5f 1854 break;
13fde0cd
RS
1855 case tend:
1856 switch (toktype)
c6d46f5f 1857 {
13fde0cd
RS
1858 case st_C_typespec:
1859 case st_C_struct:
1860 case st_C_enum:
1861 return (FALSE);
c6d46f5f 1862 }
13fde0cd 1863 return (TRUE);
c6d46f5f
JB
1864 }
1865
1866 /*
591fa824
RS
1867 * This structdef business is currently only invoked when cblev==0.
1868 * It should be recursively invoked whatever the curly brace level,
1869 * and a stack of states kept, to allow for definitions of structs
1870 * within structs.
c6d46f5f
JB
1871 *
1872 * This structdef business is NOT invoked when we are ctags and the
1873 * file is plain C. This is because a struct tag may have the same
1874 * name as another tag, and this loses with ctags.
1875 *
13fde0cd
RS
1876 * This if statement deals with the typdef state machine as
1877 * follows: if typdef==ttypedseen and token is struct/union/class/enum,
1878 * return (FALSE). All the other code here is for the structdef
1879 * state machine.
c6d46f5f
JB
1880 */
1881 switch (toktype)
1882 {
1883 case st_C_struct:
1884 case st_C_enum:
13fde0cd 1885 if (typdef == ttypedseen
591fa824 1886 || (typedefs_and_cplusplus && cblev == 0 && structdef == snone))
c6d46f5f
JB
1887 {
1888 structdef = skeyseen;
1889 structkey = tokse;
1890 }
13fde0cd 1891 return (FALSE);
c6d46f5f 1892 }
c6d46f5f
JB
1893 if (structdef == skeyseen)
1894 {
13fde0cd 1895 if (stab_type (structkey) == st_C_struct)
c6d46f5f 1896 {
13fde0cd
RS
1897 (void) strncpy (structtag, tokp->p, tokp->len);
1898 structtag[tokp->len] = '\0'; /* for struct/union/class */
c6d46f5f
JB
1899 }
1900 else
1901 {
13fde0cd 1902 structtag[0] = '\0'; /* for enum (why is it treated differently?) */
c6d46f5f 1903 }
13fde0cd
RS
1904 structdef = stagseen;
1905 return (TRUE);
c6d46f5f 1906 }
13fde0cd
RS
1907
1908 /* Avoid entering funcdef stuff if typdef is going on. */
1909 if (typdef != tnone)
c6d46f5f 1910 {
13fde0cd
RS
1911 definedef = dnone;
1912 return (FALSE);
c6d46f5f 1913 }
13fde0cd 1914
c6d46f5f 1915 /* Detect GNUmacs's function-defining macros. */
4746118a 1916 if (definedef == dnone)
c6d46f5f 1917 {
daa37602
JB
1918 if (strneq (tokp->p, "DEF", 3)
1919 || strneq (tokp->p, "ENTRY", 5)
1920 || strneq (tokp->p, "SYSCALL", 7)
1921 || strneq (tokp->p, "PSEUDO", 6))
4746118a
JB
1922 {
1923 next_token_is_func = TRUE;
13fde0cd 1924 return (FALSE);
4746118a 1925 }
13fde0cd 1926 if (strneq (tokp->p, "EXFUN", 5))
4746118a
JB
1927 {
1928 next_token_is_func = FALSE;
13fde0cd 1929 return (FALSE);
4746118a 1930 }
c6d46f5f
JB
1931 }
1932 if (next_token_is_func)
1933 {
1934 next_token_is_func = FALSE;
31d4b314
FP
1935 funcdef = fnone;
1936 *is_func = TRUE; /* to force search string in ctags */
13fde0cd 1937 return (TRUE);
c6d46f5f 1938 }
13fde0cd
RS
1939
1940 /* A function? */
1941 switch (toktype)
c6d46f5f 1942 {
13fde0cd 1943 case st_C_typespec:
591fa824 1944 funcdef = fnone; /* should be useless */
13fde0cd
RS
1945 return (FALSE);
1946 default:
31d4b314
FP
1947 if (funcdef == fnone)
1948 {
1949 funcdef = ftagseen;
1950 *is_func = TRUE;
1951 return (TRUE);
1952 }
c6d46f5f 1953 }
31d4b314
FP
1954
1955 return (FALSE);
c6d46f5f
JB
1956}
1957\f
1958/* Fortran parsing */
1959
1960char *dbp;
1961int pfcnt;
1962
1963int
1964PF_funcs (fi)
1965 FILE *fi;
1966{
1967 lineno = 0;
1968 charno = 0;
1969 pfcnt = 0;
1970
1971 while (!feof (fi))
1972 {
1973 lineno++;
1974 linecharno = charno;
1975 charno += readline (&lb, fi);
1976 dbp = lb.buffer;
1977 if (*dbp == '%')
1978 dbp++; /* Ratfor escape to fortran */
1979 while (isspace (*dbp))
1980 dbp++;
1981 if (*dbp == 0)
1982 continue;
1983 switch (*dbp | ' ')
1984 {
1985 case 'i':
1986 if (tail ("integer"))
1987 takeprec ();
1988 break;
1989 case 'r':
1990 if (tail ("real"))
1991 takeprec ();
1992 break;
1993 case 'l':
1994 if (tail ("logical"))
1995 takeprec ();
1996 break;
1997 case 'c':
1998 if (tail ("complex") || tail ("character"))
1999 takeprec ();
2000 break;
2001 case 'd':
2002 if (tail ("double"))
2003 {
2004 while (isspace (*dbp))
2005 dbp++;
2006 if (*dbp == 0)
2007 continue;
2008 if (tail ("precision"))
2009 break;
2010 continue;
2011 }
2012 break;
2013 }
2014 while (isspace (*dbp))
2015 dbp++;
2016 if (*dbp == 0)
2017 continue;
2018 switch (*dbp | ' ')
2019 {
2020 case 'f':
2021 if (tail ("function"))
2022 getit ();
2023 continue;
2024 case 's':
2025 if (tail ("subroutine"))
2026 getit ();
2027 continue;
8a6c8bcf
RS
2028 case 'e':
2029 if (tail ("entry"))
2030 getit ();
2031 continue;
c6d46f5f
JB
2032 case 'p':
2033 if (tail ("program"))
2034 {
2035 getit ();
2036 continue;
2037 }
2038 if (tail ("procedure"))
2039 getit ();
2040 continue;
2041 }
2042 }
2043 return (pfcnt);
2044}
2045
2046logical
2047tail (cp)
2048 char *cp;
2049{
2050 register int len = 0;
2051
2052 while (*cp && (*cp & ~' ') == ((*(dbp + len)) & ~' '))
2053 cp++, len++;
2054 if (*cp == 0)
2055 {
2056 dbp += len;
2057 return (1);
2058 }
2059 return (0);
2060}
2061
2062void
2063takeprec ()
2064{
2065 while (isspace (*dbp))
2066 dbp++;
2067 if (*dbp != '*')
2068 return;
2069 dbp++;
2070 while (isspace (*dbp))
2071 dbp++;
2072 if (!isdigit (*dbp))
2073 {
2074 --dbp; /* force failure */
2075 return;
2076 }
2077 do
2078 dbp++;
2079 while (isdigit (*dbp));
2080}
2081
2082void
2083getit ()
2084{
2085 register char *cp;
2086 char c;
2087 char nambuf[BUFSIZ];
2088
2089 while (isspace (*dbp))
2090 dbp++;
daa37602
JB
2091 if (*dbp == 0
2092 || (!isalpha (*dbp)
2093 && *dbp != '_'
2094 && *dbp != '$'))
c6d46f5f
JB
2095 return;
2096 for (cp = dbp + 1; *cp && (isalpha (*cp) || isdigit (*cp)
2097 || (*cp == '_') || (*cp == '$')); cp++)
2098 continue;
2099 c = cp[0];
2100 cp[0] = 0;
2101 (void) strcpy (nambuf, dbp);
2102 cp[0] = c;
2103 pfnote (nambuf, TRUE, FALSE, lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
2104 pfcnt++;
2105}
2106
2107/* Handle a file of assembler code. */
2108
2109void
2110Asm_funcs (fi)
2111 FILE *fi;
2112{
2113 int i;
2114 register char c;
2115
2116 lineno = 0;
2117 charno = 0;
2118 pfcnt = 0;
2119
2120 while (!feof (fi))
2121 {
2122 lineno++;
2123 linecharno = charno;
2124 charno += readline (&lb, fi);
2125 dbp = lb.buffer;
2126
2127 for (i = 0; ((c = dbp[i]) && !isspace (c)) && (c != ':'); i++)
2128 ;
2129
2130 if ((i > 0) && (c == ':'))
2131 getit ();
2132 }
2133}
2134\f
2135/* Added by Mosur Mohan, 4/22/88 */
2136/* Pascal parsing */
2137
2138#define GET_NEW_LINE \
2139{ \
2140 linecharno = charno; lineno++; \
2141 charno += 1 + readline (&lb, inf); \
2142 dbp = lb.buffer; \
2143}
2144
2145/* Locates tags for procedures & functions.
2146 * Doesn't do any type- or var-definitions.
2147 * It does look for the keyword "extern" or "forward"
2148 * immediately following the procedure statement;
2149 * if found, the tag is skipped.
2150 */
2151
2152void
2153PAS_funcs (fi)
2154 FILE *fi;
2155{
2156 struct linebuffer tline; /* mostly copied from C_entries */
2157 long save_lcno;
2158 int save_lineno;
2159 char c, *cp;
2160 char nambuf[BUFSIZ];
2161
2162 logical /* each of these flags is TRUE iff: */
2163 incomm1, /* point is inside {..} comment */
2164 incomm2, /* point is inside (*..*) comment */
2165 inquote, /* point is inside '..' string */
2166 get_tagname, /* point is after PROCEDURE/FUNCTION */
2167 /* keyword, so next item = potential tag */
2168 found_tag, /* point is after a potential tag */
2169 inparms, /* point is within parameter-list */
2170 verify_tag; /* point has passed the parm-list, so the */
2171 /* next token will determine whether */
2172 /* this is a FORWARD/EXTERN to be */
2173 /* ignored, or whether it is a real tag */
2174
2175 lineno = 0;
2176 charno = 0;
2177 dbp = lb.buffer;
2178 *dbp = 0;
2179 initbuffer (&tline);
2180
2181 incomm1 = incomm2 = inquote = FALSE;
2182 found_tag = FALSE; /* have a proc name; check if extern */
2183 get_tagname = FALSE; /* have found "procedure" keyword */
2184 inparms = FALSE; /* found '(' after "proc" */
2185 verify_tag = FALSE; /* check if "extern" is ahead */
2186
2187 /* long main loop to get next char */
2188 while (!feof (fi))
2189 {
2190 c = *dbp++;
2191 if (c == 0) /* if end of line */
2192 {
2193 GET_NEW_LINE;
2194 if (*dbp == 0)
2195 continue;
2196 if (!((found_tag && verify_tag) ||
2197 get_tagname))
2198 c = *dbp++; /* only if don't need *dbp pointing */
2199 /* to the beginning of the name of */
2200 /* the procedure or function */
2201 }
2202 if (incomm1) /* within { - } comments */
2203 {
2204 if (c == '}')
2205 incomm1 = FALSE;
2206 continue;
2207 }
2208 else if (incomm2) /* within (* - *) comments */
2209 {
2210 if (c == '*')
2211 {
2212 while ((c = *dbp++) == '*')
2213 continue;
2214 if (c == 0)
2215 GET_NEW_LINE;
2216 if (c == ')')
2217 incomm2 = FALSE;
2218 }
2219 continue;
2220 }
2221 else if (inquote)
2222 {
2223 if (c == '\'')
2224 inquote = FALSE;
2225 continue;
2226 }
2227 else
2228 switch (c)
2229 {
2230 case '\'':
2231 inquote = TRUE; /* found first quote */
2232 continue;
2233 case '{': /* found open-{-comment */
2234 incomm1 = TRUE;
2235 continue;
2236 case '(':
2237 if (*dbp == '*') /* found open-(*-comment */
2238 {
2239 incomm2 = TRUE;
2240 dbp++;
2241 }
2242 else if (found_tag) /* found '(' after tag, i.e., parm-list */
2243 inparms = TRUE;
2244 continue;
2245 case ')': /* end of parms list */
2246 if (inparms)
2247 inparms = FALSE;
2248 continue;
2249 case ';':
2250 if ((found_tag) && (!inparms)) /* end of proc or fn stmt */
2251 {
2252 verify_tag = TRUE;
2253 break;
2254 }
2255 continue;
2256 }
2257 if ((found_tag) && (verify_tag) && (*dbp != ' '))
2258 {
2259 /* check if this is an "extern" declaration */
2260 if (*dbp == 0)
2261 continue;
2262 if ((*dbp == 'e') || (*dbp == 'E'))
2263 {
2264 if (tail ("extern")) /* superfluous, really! */
2265 {
2266 found_tag = FALSE;
2267 verify_tag = FALSE;
2268 }
2269 }
2270 else if ((*dbp == 'f') || (*dbp == 'F'))
2271 {
2272 if (tail ("forward")) /* check for forward reference */
2273 {
2274 found_tag = FALSE;
2275 verify_tag = FALSE;
2276 }
2277 }
2278 if ((found_tag) && (verify_tag)) /* not external proc, so make tag */
2279 {
2280 found_tag = FALSE;
2281 verify_tag = FALSE;
2282 pfnote (nambuf, TRUE, FALSE,
2283 tline.buffer, cp - tline.buffer + 1,
2284 save_lineno, save_lcno);
2285 continue;
2286 }
2287 }
2288 if (get_tagname) /* grab name of proc or fn */
2289 {
2290 if (*dbp == 0)
2291 continue;
2292
2293 /* save all values for later tagging */
2294 tline.size = lb.size;
2295 strcpy (tline.buffer, lb.buffer);
2296 save_lineno = lineno;
2297 save_lcno = linecharno;
2298
2299 /* grab block name */
2300 for (cp = dbp + 1; *cp && (!endtoken (*cp)); cp++)
2301 continue;
2302 c = cp[0];
2303 cp[0] = 0;
2304 strcpy (nambuf, dbp);
2305 cp[0] = c;
2306 dbp = cp; /* restore dbp to e-o-token */
2307 get_tagname = FALSE;
2308 found_tag = TRUE;
2309 continue;
2310
2311 /* and proceed to check for "extern" */
2312 }
2313 if ((!incomm1) && (!incomm2) && (!inquote) &&
2314 (!found_tag) && (!get_tagname))
2315 {
2316 /* check for proc/fn keywords */
2317 switch (c | ' ')
2318 {
2319 case 'p':
2320 if (tail ("rocedure")) /* c = 'p', dbp has advanced */
2321 get_tagname = TRUE;
2322 continue;
2323 case 'f':
2324 if (tail ("unction"))
2325 get_tagname = TRUE;
2326 continue;
2327 }
2328 }
2329 } /* while not e-o-f */
2330}
2331\f
2332/*
2333 * lisp tag functions
2334 * just look for (def or (DEF
2335 */
2336
2337void
2338L_funcs (fi)
2339 FILE *fi;
2340{
2341 lineno = 0;
2342 charno = 0;
2343 pfcnt = 0;
2344
2345 while (!feof (fi))
2346 {
2347 lineno++;
2348 linecharno = charno;
2349 charno += readline (&lb, fi);
2350 dbp = lb.buffer;
2351 if (dbp[0] == '(')
2352 {
2353 if (L_isdef (dbp))
2354 {
2355 while (!isspace (*dbp))
2356 dbp++;
2357 while (isspace (*dbp))
2358 dbp++;
2359 L_getit ();
2360 }
2361 else
2362 {
2363 /* Check for (foo::defmumble name-defined ... */
31d4b314 2364 do
c6d46f5f 2365 dbp++;
31d4b314
FP
2366 while (*dbp && !isspace (*dbp)
2367 && *dbp != ':' && *dbp != '(' && *dbp != ')');
c6d46f5f
JB
2368 if (*dbp == ':')
2369 {
31d4b314 2370 do
c6d46f5f 2371 dbp++;
31d4b314 2372 while (*dbp == ':');
c6d46f5f 2373
31d4b314 2374 if (L_isdef (dbp - 1))
c6d46f5f
JB
2375 {
2376 while (!isspace (*dbp))
2377 dbp++;
2378 while (isspace (*dbp))
2379 dbp++;
2380 L_getit ();
2381 }
2382 }
2383 }
2384 }
2385 }
2386}
2387
2388int
2389L_isdef (dbp)
31d4b314 2390 register char *dbp;
c6d46f5f 2391{
31d4b314
FP
2392 return ((dbp[1] == 'd' || dbp[1] == 'D')
2393 && (dbp[2] == 'e' || dbp[2] == 'E')
2394 && (dbp[3] == 'f' || dbp[3] == 'F'));
2395}
2396
2397int
2398L_isquote (dbp)
2399 register char *dbp;
2400{
2401 return ((*(++dbp) == 'q' || *dbp == 'Q')
2402 && (*(++dbp) == 'u' || *dbp == 'U')
2403 && (*(++dbp) == 'o' || *dbp == 'O')
2404 && (*(++dbp) == 't' || *dbp == 'T')
2405 && (*(++dbp) == 'e' || *dbp == 'E')
2406 && isspace(*(++dbp)));
c6d46f5f
JB
2407}
2408
2409void
2410L_getit ()
2411{
2412 register char *cp;
2413 char c;
2414 char nambuf[BUFSIZ];
2415
31d4b314
FP
2416 if (*dbp == '\'') /* Skip prefix quote */
2417 dbp++;
2418 else if (*dbp == '(' && L_isquote (dbp)) /* Skip "(quote " */
2419 {
2420 dbp += 7;
2421 while (isspace(*dbp))
2422 dbp++;
2423 }
2424 for (cp = dbp /*+1*/; *cp && *cp != '(' && *cp != ' ' && *cp != ')'; cp++)
c6d46f5f 2425 continue;
31d4b314
FP
2426 if (cp == dbp)
2427 return;
2428
c6d46f5f
JB
2429 c = cp[0];
2430 cp[0] = 0;
2431 (void) strcpy (nambuf, dbp);
2432 cp[0] = c;
591fa824
RS
2433 pfnote (nambuf, TRUE, FALSE, lb.buffer,
2434 cp - lb.buffer + 1, lineno, linecharno);
c6d46f5f
JB
2435 pfcnt++;
2436}
2437\f
2438/*
2439 * Scheme tag functions
2440 * look for (def... xyzzy
2441 * look for (def... (xyzzy
2442 * look for (def ... ((...(xyzzy ....
2443 * look for (set! xyzzy
2444 */
2445
2446static void get_scheme ();
2447
2448void
2449Scheme_funcs (fi)
2450 FILE *fi;
2451{
2452 lineno = 0;
2453 charno = 0;
2454 pfcnt = 0;
2455
2456 while (!feof (fi))
2457 {
2458 lineno++;
2459 linecharno = charno;
2460 charno += readline (&lb, fi);
2461 dbp = lb.buffer;
2462 if (dbp[0] == '(' &&
2463 (dbp[1] == 'D' || dbp[1] == 'd') &&
2464 (dbp[2] == 'E' || dbp[2] == 'e') &&
2465 (dbp[3] == 'F' || dbp[3] == 'f'))
2466 {
2467 while (!isspace (*dbp))
2468 dbp++;
2469 /* Skip over open parens and white space */
2470 while (*dbp && (isspace (*dbp) || *dbp == '('))
2471 dbp++;
2472 get_scheme ();
2473 }
2474 if (dbp[0] == '(' &&
2475 (dbp[1] == 'S' || dbp[1] == 's') &&
2476 (dbp[2] == 'E' || dbp[2] == 'e') &&
2477 (dbp[3] == 'T' || dbp[3] == 't') &&
2478 (dbp[4] == '!' || dbp[4] == '!') &&
2479 (isspace (dbp[5])))
2480 {
2481 while (!isspace (*dbp))
2482 dbp++;
2483 /* Skip over white space */
2484 while (isspace (*dbp))
2485 dbp++;
2486 get_scheme ();
2487 }
2488 }
2489}
2490
2491static void
2492get_scheme ()
2493{
2494 register char *cp;
2495 char c;
2496 char nambuf[BUFSIZ];
2497
2498 if (*dbp == 0)
2499 return;
2500 /* Go till you get to white space or a syntactic break */
2501 for (cp = dbp + 1; *cp && *cp != '(' && *cp != ')' && !isspace (*cp); cp++)
2502 continue;
2503 /* Null terminate the string there. */
2504 c = cp[0];
2505 cp[0] = 0;
2506 /* Copy the string */
2507 strcpy (nambuf, dbp);
2508 /* Unterminate the string */
2509 cp[0] = c;
2510 /* Announce the change */
2511 pfnote (nambuf, TRUE, FALSE, lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
2512 pfcnt++;
2513}
2514\f
2515/* Find tags in TeX and LaTeX input files. */
2516
2517/* TEX_toktab is a table of TeX control sequences that define tags.
2518 Each TEX_tabent records one such control sequence.
2519 CONVERT THIS TO USE THE Stab TYPE!! */
2520
2521struct TEX_tabent
2522{
2523 char *name;
2524 int len;
2525};
2526
2527struct TEX_tabent *TEX_toktab = NULL; /* Table with tag tokens */
2528
2529/* Default set of control sequences to put into TEX_toktab.
2530 The value of environment var TEXTAGS is prepended to this. */
2531
2532static char *TEX_defenv =
2533":chapter:section:subsection:subsubsection:eqno:label:ref:cite:bibitem:typeout";
2534
2535void TEX_mode ();
2536struct TEX_tabent *TEX_decode_env ();
2537void TEX_getit ();
2538int TEX_Token ();
2539
2540static char TEX_esc = '\\';
2541static char TEX_opgrp = '{';
2542static char TEX_clgrp = '}';
2543
2544/*
2545 * TeX/LaTeX scanning loop.
2546 */
2547
2548void
2549TEX_funcs (fi)
2550 FILE *fi;
2551{
2552 char *lasthit;
2553
2554 lineno = 0;
2555 charno = 0;
2556 pfcnt = 0;
2557
2558 /* Select either \ or ! as escape character. */
2559 TEX_mode (fi);
2560
2561 /* Initialize token table once from environment. */
2562 if (!TEX_toktab)
2563 TEX_toktab = TEX_decode_env ("TEXTAGS", TEX_defenv);
2564
2565 while (!feof (fi))
d2729198 2566 { /* Scan each line in file */
c6d46f5f
JB
2567 lineno++;
2568 linecharno = charno;
2569 charno += readline (&lb, fi);
2570 dbp = lb.buffer;
2571 lasthit = dbp;
d2729198 2572 while (dbp = etags_index (dbp, TEX_esc)) /* Look at each escape in line */
8a6c8bcf
RS
2573 {
2574 register int i;
c6d46f5f 2575
8a6c8bcf
RS
2576 if (!*(++dbp))
2577 break;
2578 linecharno += dbp - lasthit;
c6d46f5f 2579 lasthit = dbp;
8a6c8bcf
RS
2580 i = TEX_Token (lasthit);
2581 if (0 <= i)
c6d46f5f 2582 {
8a6c8bcf 2583 TEX_getit (lasthit, TEX_toktab[i].len);
d2729198 2584 break; /* We only save a line once */
c6d46f5f
JB
2585 }
2586 }
2587 }
2588}
2589
2590#define TEX_LESC '\\'
2591#define TEX_SESC '!'
2592#define TEX_cmt '%'
2593
2594/* Figure out whether TeX's escapechar is '\\' or '!' and set grouping */
2595/* chars accordingly. */
2596
2597void
2598TEX_mode (f)
2599 FILE *f;
2600{
2601 int c;
2602
2603 while ((c = getc (f)) != EOF)
2604 {
2605 /* Skip to next line if we hit the TeX comment char. */
2606 if (c == TEX_cmt)
2607 while (c != '\n')
2608 c = getc (f);
2609 else if (c == TEX_LESC || c == TEX_SESC )
2610 break;
2611 }
2612
2613 if (c == TEX_LESC)
2614 {
2615 TEX_esc = TEX_LESC;
2616 TEX_opgrp = '{';
2617 TEX_clgrp = '}';
2618 }
2619 else
2620 {
2621 TEX_esc = TEX_SESC;
2622 TEX_opgrp = '<';
2623 TEX_clgrp = '>';
2624 }
2625 rewind (f);
2626}
2627
2628/* Read environment and prepend it to the default string. */
2629/* Build token table. */
2630
2631struct TEX_tabent *
2632TEX_decode_env (evarname, defenv)
2633 char *evarname;
2634 char *defenv;
2635{
2636 register char *env, *p;
c6d46f5f
JB
2637
2638 struct TEX_tabent *tab;
2639 int size, i;
2640
2641 /* Append default string to environment. */
2642 env = getenv (evarname);
2643 if (!env)
2644 env = defenv;
2645 else
2646 env = concat (env, defenv, "");
2647
2648 /* Allocate a token table */
2649 for (size = 1, p = env; p;)
8a6c8bcf 2650 if ((p = etags_index (p, ':')) && *(++p))
c6d46f5f 2651 size++;
8a6c8bcf
RS
2652 /* Add 1 to leave room for null terminator. */
2653 tab = xnew (size + 1, struct TEX_tabent);
c6d46f5f
JB
2654
2655 /* Unpack environment string into token table. Be careful about */
2656 /* zero-length strings (leading ':', "::" and trailing ':') */
2657 for (i = 0; *env;)
2658 {
8a6c8bcf 2659 p = etags_index (env, ':');
c6d46f5f
JB
2660 if (!p) /* End of environment string. */
2661 p = env + strlen (env);
2662 if (p - env > 0)
2663 { /* Only non-zero strings. */
2664 tab[i].name = savenstr (env, p - env);
2665 tab[i].len = strlen (tab[i].name);
2666 i++;
2667 }
2668 if (*p)
2669 env = p + 1;
2670 else
2671 {
2672 tab[i].name = NULL; /* Mark end of table. */
2673 tab[i].len = 0;
2674 break;
2675 }
2676 }
2677 return tab;
2678}
2679
2680/* Record a tag defined by a TeX command of length LEN and starting at NAME.
2681 The name being defined actually starts at (NAME + LEN + 1).
2682 But we seem to include the TeX command in the tag name. */
2683
2684void
2685TEX_getit (name, len)
2686 char *name;
2687 int len;
2688{
2689 char *p = name + len;
2690 char nambuf[BUFSIZ];
2691
2692 if (*name == 0)
2693 return;
2694
2695 /* Let tag name extend to next group close (or end of line) */
2696 while (*p && *p != TEX_clgrp)
2697 p++;
2698 (void) strncpy (nambuf, name, p - name);
2699 nambuf[p - name] = 0;
2700
2701 pfnote (nambuf, TRUE, FALSE, lb.buffer, strlen (lb.buffer), lineno, linecharno);
2702 pfcnt++;
2703}
2704
2705/* If the text at CP matches one of the tag-defining TeX command names,
8a6c8bcf 2706 return the etags_index of that command in TEX_toktab.
c6d46f5f
JB
2707 Otherwise return -1. */
2708
2709/* Keep the capital `T' in `Token' for dumb truncating compilers
2710 (this distinguishes it from `TEX_toktab' */
2711int
2712TEX_Token (cp)
2713 char *cp;
2714{
2715 int i;
2716
2717 for (i = 0; TEX_toktab[i].len > 0; i++)
2718 if (strncmp (TEX_toktab[i].name, cp, TEX_toktab[i].len) == 0)
2719 return i;
2720 return -1;
2721}
2722\f
2723/* Support for Prolog. */
2724
2725/* whole head (not only functor, but also arguments)
2726 is gotten in compound term. */
2727
2728void
2729prolog_getit (s, lineno, linecharno)
2730 char *s;
2731 int lineno;
2732 long linecharno;
2733{
2734 char nambuf[BUFSIZ], *save_s, tmpc;
2735 int insquote, npar;
2736
2737 save_s = s;
2738 insquote = FALSE;
2739 npar = 0;
2740 while (1)
2741 {
2742 if (*s == '\0') /* syntax error. */
2743 return;
2744 else if (insquote && *s == '\'' && *(s + 1) == '\'')
2745 s += 2;
2746 else if (*s == '\'')
2747 {
2748 insquote = !insquote;
2749 s++;
2750 }
2751 else if (!insquote && *s == '(')
2752 {
2753 npar++;
2754 s++;
2755 }
2756 else if (!insquote && *s == ')')
2757 {
2758 npar--;
2759 s++;
2760 if (npar == 0)
2761 break;
2762 else if (npar < 0) /* syntax error. */
2763 return;
2764 }
2765 else if (!insquote && *s == '.' && (isspace (*(s + 1)) || *(s + 1) == '\0'))
2766 { /* fullstop. */
2767 if (npar != 0) /* syntax error. */
2768 return;
2769 s++;
2770 break;
2771 }
2772 else
2773 s++;
2774 }
2775 tmpc = *s;
2776 *s = '\0';
2777 strcpy (nambuf, save_s);
2778 *s = tmpc;
2779 pfnote (nambuf, TRUE, save_s, strlen (nambuf), lineno, linecharno);
2780}
2781
2782/* It is assumed that prolog predicate starts from column 0. */
2783
2784void
2785prolog_funcs (fi)
2786 FILE *fi;
2787{
2788 void skip_comment (), prolog_getit ();
2789
2790 lineno = linecharno = charno = 0;
2791 while (!feof (fi))
2792 {
2793 lineno++;
2794 linecharno += charno;
2795 charno = readline (&lb, fi) + 1; /* 1 for newline. */
2796 dbp = lb.buffer;
2797 if (isspace (dbp[0])) /* not predicate header. */
2798 continue;
2799 else if (dbp[0] == '%') /* comment. */
2800 continue;
2801 else if (dbp[0] == '/' && dbp[1] == '*') /* comment. */
2802 skip_comment (&lb, fi, &lineno, &linecharno);
2803 else /* found. */
2804 prolog_getit (dbp, lineno, linecharno);
2805 }
2806}
2807
2808void
2809skip_comment (plb, fi, plineno, plinecharno)
2810 struct linebuffer *plb;
2811 FILE *fi;
2812 int *plineno; /* result */
2813 long *plinecharno; /* result */
2814{
2815 while (!substr ("*/", plb->buffer))
2816 {
2817 (*plineno)++;
2818 *plinecharno += readline (plb, fi) + 1;
2819 } /* 1 for newline. */
2820}
2821
2822/* Return TRUE if 'sub' exists somewhere in 's'. */
2823
2824int
2825substr (sub, s)
2826 char *sub;
2827 char *s;
2828{
8a6c8bcf 2829 while (*s && (s = etags_index (s, *sub)))
c6d46f5f
JB
2830 if (prestr (sub, s))
2831 return (TRUE);
2832 else
2833 s++;
2834 return (FALSE);
2835}
2836
2837/* Return TRUE if 'pre' is prefix of string 's'. */
2838
2839int
2840prestr (pre, s)
2841 char *pre;
2842 char *s;
2843{
2844 if (*pre == '\0')
2845 return (TRUE);
2846 else if (*pre == *s)
2847 return (prestr (pre + 1, s + 1));
2848 else
2849 return (FALSE);
2850}
2851\f
2852/* Initialize a linebuffer for use */
2853
2854void
2855initbuffer (linebuffer)
2856 struct linebuffer *linebuffer;
2857{
2858 linebuffer->size = 200;
2859 linebuffer->buffer = xnew (200, char);
2860}
2861
2862/*
2863 * Read a line of text from `stream' into `linebuffer'.
2864 * Return the number of characters read from `stream',
2865 * which is the length of the line including the newline, if any.
2866 */
2867long
2868readline (linebuffer, stream)
2869 struct linebuffer *linebuffer;
2870 register FILE *stream;
2871{
2872 char *buffer = linebuffer->buffer;
2873 register char *p = linebuffer->buffer;
2874 register char *pend;
2875 int newline; /* 1 if ended with newline, 0 if ended with EOF */
2876
eb8c3be9 2877 pend = p + linebuffer->size; /* Separate to avoid 386/IX compiler bug. */
c6d46f5f
JB
2878
2879 while (1)
2880 {
2881 register int c = getc (stream);
2882 if (p == pend)
2883 {
2884 linebuffer->size *= 2;
2885 buffer = (char *) xrealloc (buffer, linebuffer->size);
2886 p += buffer - linebuffer->buffer;
2887 pend = buffer + linebuffer->size;
2888 linebuffer->buffer = buffer;
2889 }
2890 if (c < 0 || c == '\n')
2891 {
2892 *p = 0;
2893 newline = (c == '\n' ? 1 : 0);
2894 break;
2895 }
2896 *p++ = c;
2897 }
2898
2899 return p - buffer + newline;
2900}
2901\f
2902char *
2903savestr (cp)
2904 char *cp;
2905{
2906 return savenstr (cp, strlen (cp));
2907}
2908
2909char *
2910savenstr (cp, len)
2911 char *cp;
2912 int len;
2913{
2914 register char *dp;
2915
2916 dp = xnew (len + 1, char);
2917 (void) strncpy (dp, cp, len);
2918 dp[len] = '\0';
2919 return dp;
2920}
2921
c6d46f5f
JB
2922/*
2923 * Return the ptr in sp at which the character c last
2924 * appears; NULL if not found
2925 *
2926 * Identical to v7 rindex, included for portability.
2927 */
2928
2929char *
8a6c8bcf 2930etags_rindex (sp, c)
c6d46f5f
JB
2931 register char *sp, c;
2932{
2933 register char *r;
2934
2935 r = NULL;
2936 do
2937 {
2938 if (*sp == c)
2939 r = sp;
2940 } while (*sp++);
2941 return (r);
2942}
2943
9d7ad1b3 2944
c6d46f5f
JB
2945/*
2946 * Return the ptr in sp at which the character c first
2947 * appears; NULL if not found
2948 *
2949 * Identical to v7 index, included for portability.
2950 */
2951
2952char *
8a6c8bcf 2953etags_index (sp, c)
c6d46f5f
JB
2954 register char *sp, c;
2955{
2956 do
2957 {
2958 if (*sp == c)
2959 return (sp);
2960 } while (*sp++);
2961 return (NULL);
2962}
2963
c6d46f5f
JB
2964/* Print error message and exit. */
2965
2966/* VARARGS1 */
2967void
2968fatal (s1, s2)
2969 char *s1, *s2;
2970{
2971 error (s1, s2);
2972 exit (1);
2973}
2974
2975/* Print error message. `s1' is printf control string, `s2' is arg for it. */
2976
2977/* VARARGS1 */
2978void
2979error (s1, s2)
2980 char *s1, *s2;
2981{
2982 fprintf (stderr, "%s: ", progname);
2983 fprintf (stderr, s1, s2);
2984 fprintf (stderr, "\n");
2985}
2986
2987/* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */
2988
2989char *
2990concat (s1, s2, s3)
2991 char *s1, *s2, *s3;
2992{
2993 int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
2994 char *result = xnew (len1 + len2 + len3 + 1, char);
2995
2996 (void) strcpy (result, s1);
2997 (void) strcpy (result + len1, s2);
2998 (void) strcpy (result + len1 + len2, s3);
2999 *(result + len1 + len2 + len3) = 0;
3000
3001 return result;
3002}
3003
3004/* Like malloc but get fatal error if memory is exhausted. */
3005
3006char *
3007xmalloc (size)
3008 int size;
3009{
3010 char *result = malloc (size);
3011 if (!result)
3012 fatal ("virtual memory exhausted", 0);
3013 return result;
3014}
3015
3016char *
3017xrealloc (ptr, size)
3018 char *ptr;
3019 int size;
3020{
3021 char *result = realloc (ptr, size);
3022 if (!result)
3023 fatal ("virtual memory exhausted");
3024 return result;
3025}