(describe-current-display-table):
[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 *
bff8edcc 28 * Francesco Potorti` (pot@cnuce.cnr.it) is the current maintainer. 9.7
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
0aaf5c4a 439 printf ("for Emacs version 19.\n");
4746118a
JB
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 Stab_entry *tokse = stab_find (get_C_stab (c_ext), tokp->p, tokp->len);
1792 enum sym_type toktype = stab_type (tokse);
1793
c6d46f5f 1794 /*
13fde0cd 1795 * Advance the definedef state machine.
c6d46f5f
JB
1796 */
1797 switch (definedef)
1798 {
1799 case dnone:
1800 /* We're not on a preprocessor line. */
1801 break;
1802 case dsharpseen:
1803 if (toktype == st_C_define)
1804 {
1805 definedef = ddefineseen;
c6d46f5f
JB
1806 }
1807 else
1808 {
1809 definedef = dignorerest;
c6d46f5f 1810 }
13fde0cd 1811 return (FALSE);
c6d46f5f
JB
1812 case ddefineseen:
1813 /*
1814 * Make a tag for any macro.
c6d46f5f 1815 */
c6d46f5f 1816 definedef = dignorerest;
13fde0cd 1817 *is_func = (c == '(');
c6d46f5f 1818 if (!*is_func && !constantypedefs)
13fde0cd
RS
1819 return (FALSE);
1820 else
1821 return (TRUE);
c6d46f5f 1822 case dignorerest:
13fde0cd 1823 return (FALSE);
c6d46f5f
JB
1824 default:
1825 error ("internal error: definedef value");
1826 }
1827
1828 /*
13fde0cd 1829 * Now typedefs
c6d46f5f 1830 */
13fde0cd 1831 switch (typdef)
c6d46f5f 1832 {
13fde0cd
RS
1833 case tnone:
1834 if (toktype == st_C_typedef)
c6d46f5f 1835 {
13fde0cd
RS
1836 if (typedefs)
1837 typdef = ttypedseen;
1838 return (FALSE);
c6d46f5f 1839 }
13fde0cd
RS
1840 break;
1841 case ttypedseen:
1842 switch (toktype)
c6d46f5f 1843 {
13fde0cd
RS
1844 case st_none:
1845 case st_C_typespec:
1846 typdef = tend;
1847 break;
1848 case st_C_struct:
1849 case st_C_enum:
1e134a5f 1850 break;
c6d46f5f 1851 }
13fde0cd 1852 /* Do not return here, so the structdef stuff has a chance. */
c6d46f5f 1853 break;
13fde0cd
RS
1854 case tend:
1855 switch (toktype)
c6d46f5f 1856 {
13fde0cd
RS
1857 case st_C_typespec:
1858 case st_C_struct:
1859 case st_C_enum:
1860 return (FALSE);
c6d46f5f 1861 }
13fde0cd 1862 return (TRUE);
c6d46f5f
JB
1863 }
1864
1865 /*
591fa824
RS
1866 * This structdef business is currently only invoked when cblev==0.
1867 * It should be recursively invoked whatever the curly brace level,
1868 * and a stack of states kept, to allow for definitions of structs
1869 * within structs.
c6d46f5f
JB
1870 *
1871 * This structdef business is NOT invoked when we are ctags and the
1872 * file is plain C. This is because a struct tag may have the same
1873 * name as another tag, and this loses with ctags.
1874 *
13fde0cd
RS
1875 * This if statement deals with the typdef state machine as
1876 * follows: if typdef==ttypedseen and token is struct/union/class/enum,
1877 * return (FALSE). All the other code here is for the structdef
1878 * state machine.
c6d46f5f
JB
1879 */
1880 switch (toktype)
1881 {
1882 case st_C_struct:
1883 case st_C_enum:
13fde0cd 1884 if (typdef == ttypedseen
591fa824 1885 || (typedefs_and_cplusplus && cblev == 0 && structdef == snone))
c6d46f5f
JB
1886 {
1887 structdef = skeyseen;
1888 structkey = tokse;
1889 }
13fde0cd 1890 return (FALSE);
c6d46f5f 1891 }
c6d46f5f
JB
1892 if (structdef == skeyseen)
1893 {
13fde0cd 1894 if (stab_type (structkey) == st_C_struct)
c6d46f5f 1895 {
13fde0cd
RS
1896 (void) strncpy (structtag, tokp->p, tokp->len);
1897 structtag[tokp->len] = '\0'; /* for struct/union/class */
c6d46f5f
JB
1898 }
1899 else
1900 {
13fde0cd 1901 structtag[0] = '\0'; /* for enum (why is it treated differently?) */
c6d46f5f 1902 }
13fde0cd
RS
1903 structdef = stagseen;
1904 return (TRUE);
c6d46f5f 1905 }
13fde0cd
RS
1906
1907 /* Avoid entering funcdef stuff if typdef is going on. */
1908 if (typdef != tnone)
c6d46f5f 1909 {
13fde0cd
RS
1910 definedef = dnone;
1911 return (FALSE);
c6d46f5f 1912 }
13fde0cd 1913
c6d46f5f 1914 /* Detect GNUmacs's function-defining macros. */
4746118a 1915 if (definedef == dnone)
c6d46f5f 1916 {
daa37602
JB
1917 if (strneq (tokp->p, "DEF", 3)
1918 || strneq (tokp->p, "ENTRY", 5)
1919 || strneq (tokp->p, "SYSCALL", 7)
1920 || strneq (tokp->p, "PSEUDO", 6))
4746118a
JB
1921 {
1922 next_token_is_func = TRUE;
13fde0cd 1923 return (FALSE);
4746118a 1924 }
13fde0cd 1925 if (strneq (tokp->p, "EXFUN", 5))
4746118a
JB
1926 {
1927 next_token_is_func = FALSE;
13fde0cd 1928 return (FALSE);
4746118a 1929 }
c6d46f5f
JB
1930 }
1931 if (next_token_is_func)
1932 {
1933 next_token_is_func = FALSE;
31d4b314
FP
1934 funcdef = fnone;
1935 *is_func = TRUE; /* to force search string in ctags */
13fde0cd 1936 return (TRUE);
c6d46f5f 1937 }
13fde0cd
RS
1938
1939 /* A function? */
1940 switch (toktype)
c6d46f5f 1941 {
13fde0cd 1942 case st_C_typespec:
591fa824 1943 funcdef = fnone; /* should be useless */
13fde0cd
RS
1944 return (FALSE);
1945 default:
31d4b314
FP
1946 if (funcdef == fnone)
1947 {
1948 funcdef = ftagseen;
1949 *is_func = TRUE;
1950 return (TRUE);
1951 }
c6d46f5f 1952 }
31d4b314
FP
1953
1954 return (FALSE);
c6d46f5f
JB
1955}
1956\f
1957/* Fortran parsing */
1958
1959char *dbp;
1960int pfcnt;
1961
1962int
1963PF_funcs (fi)
1964 FILE *fi;
1965{
1966 lineno = 0;
1967 charno = 0;
1968 pfcnt = 0;
1969
1970 while (!feof (fi))
1971 {
1972 lineno++;
1973 linecharno = charno;
1974 charno += readline (&lb, fi);
1975 dbp = lb.buffer;
1976 if (*dbp == '%')
1977 dbp++; /* Ratfor escape to fortran */
1978 while (isspace (*dbp))
1979 dbp++;
1980 if (*dbp == 0)
1981 continue;
1982 switch (*dbp | ' ')
1983 {
1984 case 'i':
1985 if (tail ("integer"))
1986 takeprec ();
1987 break;
1988 case 'r':
1989 if (tail ("real"))
1990 takeprec ();
1991 break;
1992 case 'l':
1993 if (tail ("logical"))
1994 takeprec ();
1995 break;
1996 case 'c':
1997 if (tail ("complex") || tail ("character"))
1998 takeprec ();
1999 break;
2000 case 'd':
2001 if (tail ("double"))
2002 {
2003 while (isspace (*dbp))
2004 dbp++;
2005 if (*dbp == 0)
2006 continue;
2007 if (tail ("precision"))
2008 break;
2009 continue;
2010 }
2011 break;
2012 }
2013 while (isspace (*dbp))
2014 dbp++;
2015 if (*dbp == 0)
2016 continue;
2017 switch (*dbp | ' ')
2018 {
2019 case 'f':
2020 if (tail ("function"))
2021 getit ();
2022 continue;
2023 case 's':
2024 if (tail ("subroutine"))
2025 getit ();
2026 continue;
8a6c8bcf
RS
2027 case 'e':
2028 if (tail ("entry"))
2029 getit ();
2030 continue;
c6d46f5f
JB
2031 case 'p':
2032 if (tail ("program"))
2033 {
2034 getit ();
2035 continue;
2036 }
2037 if (tail ("procedure"))
2038 getit ();
2039 continue;
2040 }
2041 }
2042 return (pfcnt);
2043}
2044
2045logical
2046tail (cp)
2047 char *cp;
2048{
2049 register int len = 0;
2050
2051 while (*cp && (*cp & ~' ') == ((*(dbp + len)) & ~' '))
2052 cp++, len++;
2053 if (*cp == 0)
2054 {
2055 dbp += len;
2056 return (1);
2057 }
2058 return (0);
2059}
2060
2061void
2062takeprec ()
2063{
2064 while (isspace (*dbp))
2065 dbp++;
2066 if (*dbp != '*')
2067 return;
2068 dbp++;
2069 while (isspace (*dbp))
2070 dbp++;
2071 if (!isdigit (*dbp))
2072 {
2073 --dbp; /* force failure */
2074 return;
2075 }
2076 do
2077 dbp++;
2078 while (isdigit (*dbp));
2079}
2080
2081void
2082getit ()
2083{
2084 register char *cp;
2085 char c;
2086 char nambuf[BUFSIZ];
2087
2088 while (isspace (*dbp))
2089 dbp++;
daa37602
JB
2090 if (*dbp == 0
2091 || (!isalpha (*dbp)
2092 && *dbp != '_'
2093 && *dbp != '$'))
c6d46f5f
JB
2094 return;
2095 for (cp = dbp + 1; *cp && (isalpha (*cp) || isdigit (*cp)
2096 || (*cp == '_') || (*cp == '$')); cp++)
2097 continue;
2098 c = cp[0];
2099 cp[0] = 0;
2100 (void) strcpy (nambuf, dbp);
2101 cp[0] = c;
2102 pfnote (nambuf, TRUE, FALSE, lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
2103 pfcnt++;
2104}
2105
2106/* Handle a file of assembler code. */
2107
2108void
2109Asm_funcs (fi)
2110 FILE *fi;
2111{
2112 int i;
2113 register char c;
2114
2115 lineno = 0;
2116 charno = 0;
2117 pfcnt = 0;
2118
2119 while (!feof (fi))
2120 {
2121 lineno++;
2122 linecharno = charno;
2123 charno += readline (&lb, fi);
2124 dbp = lb.buffer;
2125
2126 for (i = 0; ((c = dbp[i]) && !isspace (c)) && (c != ':'); i++)
2127 ;
2128
2129 if ((i > 0) && (c == ':'))
2130 getit ();
2131 }
2132}
2133\f
2134/* Added by Mosur Mohan, 4/22/88 */
2135/* Pascal parsing */
2136
2137#define GET_NEW_LINE \
2138{ \
2139 linecharno = charno; lineno++; \
2140 charno += 1 + readline (&lb, inf); \
2141 dbp = lb.buffer; \
2142}
2143
2144/* Locates tags for procedures & functions.
2145 * Doesn't do any type- or var-definitions.
2146 * It does look for the keyword "extern" or "forward"
2147 * immediately following the procedure statement;
2148 * if found, the tag is skipped.
2149 */
2150
2151void
2152PAS_funcs (fi)
2153 FILE *fi;
2154{
2155 struct linebuffer tline; /* mostly copied from C_entries */
2156 long save_lcno;
2157 int save_lineno;
2158 char c, *cp;
2159 char nambuf[BUFSIZ];
2160
2161 logical /* each of these flags is TRUE iff: */
2162 incomm1, /* point is inside {..} comment */
2163 incomm2, /* point is inside (*..*) comment */
2164 inquote, /* point is inside '..' string */
2165 get_tagname, /* point is after PROCEDURE/FUNCTION */
2166 /* keyword, so next item = potential tag */
2167 found_tag, /* point is after a potential tag */
2168 inparms, /* point is within parameter-list */
2169 verify_tag; /* point has passed the parm-list, so the */
2170 /* next token will determine whether */
2171 /* this is a FORWARD/EXTERN to be */
2172 /* ignored, or whether it is a real tag */
2173
2174 lineno = 0;
2175 charno = 0;
2176 dbp = lb.buffer;
2177 *dbp = 0;
2178 initbuffer (&tline);
2179
2180 incomm1 = incomm2 = inquote = FALSE;
2181 found_tag = FALSE; /* have a proc name; check if extern */
2182 get_tagname = FALSE; /* have found "procedure" keyword */
2183 inparms = FALSE; /* found '(' after "proc" */
2184 verify_tag = FALSE; /* check if "extern" is ahead */
2185
2186 /* long main loop to get next char */
2187 while (!feof (fi))
2188 {
2189 c = *dbp++;
2190 if (c == 0) /* if end of line */
2191 {
2192 GET_NEW_LINE;
2193 if (*dbp == 0)
2194 continue;
2195 if (!((found_tag && verify_tag) ||
2196 get_tagname))
2197 c = *dbp++; /* only if don't need *dbp pointing */
2198 /* to the beginning of the name of */
2199 /* the procedure or function */
2200 }
2201 if (incomm1) /* within { - } comments */
2202 {
2203 if (c == '}')
2204 incomm1 = FALSE;
2205 continue;
2206 }
2207 else if (incomm2) /* within (* - *) comments */
2208 {
2209 if (c == '*')
2210 {
2211 while ((c = *dbp++) == '*')
2212 continue;
2213 if (c == 0)
2214 GET_NEW_LINE;
2215 if (c == ')')
2216 incomm2 = FALSE;
2217 }
2218 continue;
2219 }
2220 else if (inquote)
2221 {
2222 if (c == '\'')
2223 inquote = FALSE;
2224 continue;
2225 }
2226 else
2227 switch (c)
2228 {
2229 case '\'':
2230 inquote = TRUE; /* found first quote */
2231 continue;
2232 case '{': /* found open-{-comment */
2233 incomm1 = TRUE;
2234 continue;
2235 case '(':
2236 if (*dbp == '*') /* found open-(*-comment */
2237 {
2238 incomm2 = TRUE;
2239 dbp++;
2240 }
2241 else if (found_tag) /* found '(' after tag, i.e., parm-list */
2242 inparms = TRUE;
2243 continue;
2244 case ')': /* end of parms list */
2245 if (inparms)
2246 inparms = FALSE;
2247 continue;
2248 case ';':
2249 if ((found_tag) && (!inparms)) /* end of proc or fn stmt */
2250 {
2251 verify_tag = TRUE;
2252 break;
2253 }
2254 continue;
2255 }
2256 if ((found_tag) && (verify_tag) && (*dbp != ' '))
2257 {
2258 /* check if this is an "extern" declaration */
2259 if (*dbp == 0)
2260 continue;
2261 if ((*dbp == 'e') || (*dbp == 'E'))
2262 {
2263 if (tail ("extern")) /* superfluous, really! */
2264 {
2265 found_tag = FALSE;
2266 verify_tag = FALSE;
2267 }
2268 }
2269 else if ((*dbp == 'f') || (*dbp == 'F'))
2270 {
2271 if (tail ("forward")) /* check for forward reference */
2272 {
2273 found_tag = FALSE;
2274 verify_tag = FALSE;
2275 }
2276 }
2277 if ((found_tag) && (verify_tag)) /* not external proc, so make tag */
2278 {
2279 found_tag = FALSE;
2280 verify_tag = FALSE;
2281 pfnote (nambuf, TRUE, FALSE,
2282 tline.buffer, cp - tline.buffer + 1,
2283 save_lineno, save_lcno);
2284 continue;
2285 }
2286 }
2287 if (get_tagname) /* grab name of proc or fn */
2288 {
2289 if (*dbp == 0)
2290 continue;
2291
2292 /* save all values for later tagging */
2293 tline.size = lb.size;
2294 strcpy (tline.buffer, lb.buffer);
2295 save_lineno = lineno;
2296 save_lcno = linecharno;
2297
2298 /* grab block name */
2299 for (cp = dbp + 1; *cp && (!endtoken (*cp)); cp++)
2300 continue;
2301 c = cp[0];
2302 cp[0] = 0;
2303 strcpy (nambuf, dbp);
2304 cp[0] = c;
2305 dbp = cp; /* restore dbp to e-o-token */
2306 get_tagname = FALSE;
2307 found_tag = TRUE;
2308 continue;
2309
2310 /* and proceed to check for "extern" */
2311 }
2312 if ((!incomm1) && (!incomm2) && (!inquote) &&
2313 (!found_tag) && (!get_tagname))
2314 {
2315 /* check for proc/fn keywords */
2316 switch (c | ' ')
2317 {
2318 case 'p':
2319 if (tail ("rocedure")) /* c = 'p', dbp has advanced */
2320 get_tagname = TRUE;
2321 continue;
2322 case 'f':
2323 if (tail ("unction"))
2324 get_tagname = TRUE;
2325 continue;
2326 }
2327 }
2328 } /* while not e-o-f */
2329}
2330\f
2331/*
2332 * lisp tag functions
2333 * just look for (def or (DEF
2334 */
2335
2336void
2337L_funcs (fi)
2338 FILE *fi;
2339{
2340 lineno = 0;
2341 charno = 0;
2342 pfcnt = 0;
2343
2344 while (!feof (fi))
2345 {
2346 lineno++;
2347 linecharno = charno;
2348 charno += readline (&lb, fi);
2349 dbp = lb.buffer;
2350 if (dbp[0] == '(')
2351 {
2352 if (L_isdef (dbp))
2353 {
2354 while (!isspace (*dbp))
2355 dbp++;
2356 while (isspace (*dbp))
2357 dbp++;
2358 L_getit ();
2359 }
2360 else
2361 {
2362 /* Check for (foo::defmumble name-defined ... */
31d4b314 2363 do
c6d46f5f 2364 dbp++;
31d4b314
FP
2365 while (*dbp && !isspace (*dbp)
2366 && *dbp != ':' && *dbp != '(' && *dbp != ')');
c6d46f5f
JB
2367 if (*dbp == ':')
2368 {
31d4b314 2369 do
c6d46f5f 2370 dbp++;
31d4b314 2371 while (*dbp == ':');
c6d46f5f 2372
31d4b314 2373 if (L_isdef (dbp - 1))
c6d46f5f
JB
2374 {
2375 while (!isspace (*dbp))
2376 dbp++;
2377 while (isspace (*dbp))
2378 dbp++;
2379 L_getit ();
2380 }
2381 }
2382 }
2383 }
2384 }
2385}
2386
2387int
2388L_isdef (dbp)
31d4b314 2389 register char *dbp;
c6d46f5f 2390{
31d4b314
FP
2391 return ((dbp[1] == 'd' || dbp[1] == 'D')
2392 && (dbp[2] == 'e' || dbp[2] == 'E')
2393 && (dbp[3] == 'f' || dbp[3] == 'F'));
2394}
2395
2396int
2397L_isquote (dbp)
2398 register char *dbp;
2399{
2400 return ((*(++dbp) == 'q' || *dbp == 'Q')
2401 && (*(++dbp) == 'u' || *dbp == 'U')
2402 && (*(++dbp) == 'o' || *dbp == 'O')
2403 && (*(++dbp) == 't' || *dbp == 'T')
2404 && (*(++dbp) == 'e' || *dbp == 'E')
2405 && isspace(*(++dbp)));
c6d46f5f
JB
2406}
2407
2408void
2409L_getit ()
2410{
2411 register char *cp;
2412 char c;
2413 char nambuf[BUFSIZ];
2414
31d4b314
FP
2415 if (*dbp == '\'') /* Skip prefix quote */
2416 dbp++;
2417 else if (*dbp == '(' && L_isquote (dbp)) /* Skip "(quote " */
2418 {
2419 dbp += 7;
2420 while (isspace(*dbp))
2421 dbp++;
2422 }
2423 for (cp = dbp /*+1*/; *cp && *cp != '(' && *cp != ' ' && *cp != ')'; cp++)
c6d46f5f 2424 continue;
31d4b314
FP
2425 if (cp == dbp)
2426 return;
2427
c6d46f5f
JB
2428 c = cp[0];
2429 cp[0] = 0;
2430 (void) strcpy (nambuf, dbp);
2431 cp[0] = c;
591fa824
RS
2432 pfnote (nambuf, TRUE, FALSE, lb.buffer,
2433 cp - lb.buffer + 1, lineno, linecharno);
c6d46f5f
JB
2434 pfcnt++;
2435}
2436\f
2437/*
2438 * Scheme tag functions
2439 * look for (def... xyzzy
2440 * look for (def... (xyzzy
2441 * look for (def ... ((...(xyzzy ....
2442 * look for (set! xyzzy
2443 */
2444
2445static void get_scheme ();
2446
2447void
2448Scheme_funcs (fi)
2449 FILE *fi;
2450{
2451 lineno = 0;
2452 charno = 0;
2453 pfcnt = 0;
2454
2455 while (!feof (fi))
2456 {
2457 lineno++;
2458 linecharno = charno;
2459 charno += readline (&lb, fi);
2460 dbp = lb.buffer;
2461 if (dbp[0] == '(' &&
2462 (dbp[1] == 'D' || dbp[1] == 'd') &&
2463 (dbp[2] == 'E' || dbp[2] == 'e') &&
2464 (dbp[3] == 'F' || dbp[3] == 'f'))
2465 {
2466 while (!isspace (*dbp))
2467 dbp++;
2468 /* Skip over open parens and white space */
2469 while (*dbp && (isspace (*dbp) || *dbp == '('))
2470 dbp++;
2471 get_scheme ();
2472 }
2473 if (dbp[0] == '(' &&
2474 (dbp[1] == 'S' || dbp[1] == 's') &&
2475 (dbp[2] == 'E' || dbp[2] == 'e') &&
2476 (dbp[3] == 'T' || dbp[3] == 't') &&
2477 (dbp[4] == '!' || dbp[4] == '!') &&
2478 (isspace (dbp[5])))
2479 {
2480 while (!isspace (*dbp))
2481 dbp++;
2482 /* Skip over white space */
2483 while (isspace (*dbp))
2484 dbp++;
2485 get_scheme ();
2486 }
2487 }
2488}
2489
2490static void
2491get_scheme ()
2492{
2493 register char *cp;
2494 char c;
2495 char nambuf[BUFSIZ];
2496
2497 if (*dbp == 0)
2498 return;
2499 /* Go till you get to white space or a syntactic break */
2500 for (cp = dbp + 1; *cp && *cp != '(' && *cp != ')' && !isspace (*cp); cp++)
2501 continue;
2502 /* Null terminate the string there. */
2503 c = cp[0];
2504 cp[0] = 0;
2505 /* Copy the string */
2506 strcpy (nambuf, dbp);
2507 /* Unterminate the string */
2508 cp[0] = c;
2509 /* Announce the change */
2510 pfnote (nambuf, TRUE, FALSE, lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
2511 pfcnt++;
2512}
2513\f
2514/* Find tags in TeX and LaTeX input files. */
2515
2516/* TEX_toktab is a table of TeX control sequences that define tags.
2517 Each TEX_tabent records one such control sequence.
2518 CONVERT THIS TO USE THE Stab TYPE!! */
2519
2520struct TEX_tabent
2521{
2522 char *name;
2523 int len;
2524};
2525
2526struct TEX_tabent *TEX_toktab = NULL; /* Table with tag tokens */
2527
2528/* Default set of control sequences to put into TEX_toktab.
2529 The value of environment var TEXTAGS is prepended to this. */
2530
2531static char *TEX_defenv =
2532":chapter:section:subsection:subsubsection:eqno:label:ref:cite:bibitem:typeout";
2533
2534void TEX_mode ();
2535struct TEX_tabent *TEX_decode_env ();
2536void TEX_getit ();
2537int TEX_Token ();
2538
2539static char TEX_esc = '\\';
2540static char TEX_opgrp = '{';
2541static char TEX_clgrp = '}';
2542
2543/*
2544 * TeX/LaTeX scanning loop.
2545 */
2546
2547void
2548TEX_funcs (fi)
2549 FILE *fi;
2550{
2551 char *lasthit;
2552
2553 lineno = 0;
2554 charno = 0;
2555 pfcnt = 0;
2556
2557 /* Select either \ or ! as escape character. */
2558 TEX_mode (fi);
2559
2560 /* Initialize token table once from environment. */
2561 if (!TEX_toktab)
2562 TEX_toktab = TEX_decode_env ("TEXTAGS", TEX_defenv);
2563
2564 while (!feof (fi))
d2729198 2565 { /* Scan each line in file */
c6d46f5f
JB
2566 lineno++;
2567 linecharno = charno;
2568 charno += readline (&lb, fi);
2569 dbp = lb.buffer;
2570 lasthit = dbp;
d2729198 2571 while (dbp = etags_index (dbp, TEX_esc)) /* Look at each escape in line */
8a6c8bcf
RS
2572 {
2573 register int i;
c6d46f5f 2574
8a6c8bcf
RS
2575 if (!*(++dbp))
2576 break;
2577 linecharno += dbp - lasthit;
c6d46f5f 2578 lasthit = dbp;
8a6c8bcf
RS
2579 i = TEX_Token (lasthit);
2580 if (0 <= i)
c6d46f5f 2581 {
8a6c8bcf 2582 TEX_getit (lasthit, TEX_toktab[i].len);
d2729198 2583 break; /* We only save a line once */
c6d46f5f
JB
2584 }
2585 }
2586 }
2587}
2588
2589#define TEX_LESC '\\'
2590#define TEX_SESC '!'
2591#define TEX_cmt '%'
2592
2593/* Figure out whether TeX's escapechar is '\\' or '!' and set grouping */
2594/* chars accordingly. */
2595
2596void
2597TEX_mode (f)
2598 FILE *f;
2599{
2600 int c;
2601
2602 while ((c = getc (f)) != EOF)
2603 {
2604 /* Skip to next line if we hit the TeX comment char. */
2605 if (c == TEX_cmt)
2606 while (c != '\n')
2607 c = getc (f);
2608 else if (c == TEX_LESC || c == TEX_SESC )
2609 break;
2610 }
2611
2612 if (c == TEX_LESC)
2613 {
2614 TEX_esc = TEX_LESC;
2615 TEX_opgrp = '{';
2616 TEX_clgrp = '}';
2617 }
2618 else
2619 {
2620 TEX_esc = TEX_SESC;
2621 TEX_opgrp = '<';
2622 TEX_clgrp = '>';
2623 }
2624 rewind (f);
2625}
2626
2627/* Read environment and prepend it to the default string. */
2628/* Build token table. */
2629
2630struct TEX_tabent *
2631TEX_decode_env (evarname, defenv)
2632 char *evarname;
2633 char *defenv;
2634{
2635 register char *env, *p;
c6d46f5f
JB
2636
2637 struct TEX_tabent *tab;
2638 int size, i;
2639
2640 /* Append default string to environment. */
2641 env = getenv (evarname);
2642 if (!env)
2643 env = defenv;
2644 else
2645 env = concat (env, defenv, "");
2646
2647 /* Allocate a token table */
2648 for (size = 1, p = env; p;)
8a6c8bcf 2649 if ((p = etags_index (p, ':')) && *(++p))
c6d46f5f 2650 size++;
8a6c8bcf
RS
2651 /* Add 1 to leave room for null terminator. */
2652 tab = xnew (size + 1, struct TEX_tabent);
c6d46f5f
JB
2653
2654 /* Unpack environment string into token table. Be careful about */
2655 /* zero-length strings (leading ':', "::" and trailing ':') */
2656 for (i = 0; *env;)
2657 {
8a6c8bcf 2658 p = etags_index (env, ':');
c6d46f5f
JB
2659 if (!p) /* End of environment string. */
2660 p = env + strlen (env);
2661 if (p - env > 0)
2662 { /* Only non-zero strings. */
2663 tab[i].name = savenstr (env, p - env);
2664 tab[i].len = strlen (tab[i].name);
2665 i++;
2666 }
2667 if (*p)
2668 env = p + 1;
2669 else
2670 {
2671 tab[i].name = NULL; /* Mark end of table. */
2672 tab[i].len = 0;
2673 break;
2674 }
2675 }
2676 return tab;
2677}
2678
2679/* Record a tag defined by a TeX command of length LEN and starting at NAME.
2680 The name being defined actually starts at (NAME + LEN + 1).
2681 But we seem to include the TeX command in the tag name. */
2682
2683void
2684TEX_getit (name, len)
2685 char *name;
2686 int len;
2687{
2688 char *p = name + len;
2689 char nambuf[BUFSIZ];
2690
2691 if (*name == 0)
2692 return;
2693
2694 /* Let tag name extend to next group close (or end of line) */
2695 while (*p && *p != TEX_clgrp)
2696 p++;
2697 (void) strncpy (nambuf, name, p - name);
2698 nambuf[p - name] = 0;
2699
2700 pfnote (nambuf, TRUE, FALSE, lb.buffer, strlen (lb.buffer), lineno, linecharno);
2701 pfcnt++;
2702}
2703
2704/* If the text at CP matches one of the tag-defining TeX command names,
8a6c8bcf 2705 return the etags_index of that command in TEX_toktab.
c6d46f5f
JB
2706 Otherwise return -1. */
2707
2708/* Keep the capital `T' in `Token' for dumb truncating compilers
2709 (this distinguishes it from `TEX_toktab' */
2710int
2711TEX_Token (cp)
2712 char *cp;
2713{
2714 int i;
2715
2716 for (i = 0; TEX_toktab[i].len > 0; i++)
2717 if (strncmp (TEX_toktab[i].name, cp, TEX_toktab[i].len) == 0)
2718 return i;
2719 return -1;
2720}
2721\f
2722/* Support for Prolog. */
2723
2724/* whole head (not only functor, but also arguments)
2725 is gotten in compound term. */
2726
2727void
2728prolog_getit (s, lineno, linecharno)
2729 char *s;
2730 int lineno;
2731 long linecharno;
2732{
2733 char nambuf[BUFSIZ], *save_s, tmpc;
2734 int insquote, npar;
2735
2736 save_s = s;
2737 insquote = FALSE;
2738 npar = 0;
2739 while (1)
2740 {
2741 if (*s == '\0') /* syntax error. */
2742 return;
2743 else if (insquote && *s == '\'' && *(s + 1) == '\'')
2744 s += 2;
2745 else if (*s == '\'')
2746 {
2747 insquote = !insquote;
2748 s++;
2749 }
2750 else if (!insquote && *s == '(')
2751 {
2752 npar++;
2753 s++;
2754 }
2755 else if (!insquote && *s == ')')
2756 {
2757 npar--;
2758 s++;
2759 if (npar == 0)
2760 break;
2761 else if (npar < 0) /* syntax error. */
2762 return;
2763 }
2764 else if (!insquote && *s == '.' && (isspace (*(s + 1)) || *(s + 1) == '\0'))
2765 { /* fullstop. */
2766 if (npar != 0) /* syntax error. */
2767 return;
2768 s++;
2769 break;
2770 }
2771 else
2772 s++;
2773 }
2774 tmpc = *s;
2775 *s = '\0';
2776 strcpy (nambuf, save_s);
2777 *s = tmpc;
bff8edcc 2778 pfnote (nambuf, TRUE, FALSE, save_s, strlen (nambuf), lineno, linecharno);
c6d46f5f
JB
2779}
2780
2781/* It is assumed that prolog predicate starts from column 0. */
2782
2783void
2784prolog_funcs (fi)
2785 FILE *fi;
2786{
2787 void skip_comment (), prolog_getit ();
2788
2789 lineno = linecharno = charno = 0;
2790 while (!feof (fi))
2791 {
2792 lineno++;
2793 linecharno += charno;
2794 charno = readline (&lb, fi) + 1; /* 1 for newline. */
2795 dbp = lb.buffer;
2796 if (isspace (dbp[0])) /* not predicate header. */
2797 continue;
2798 else if (dbp[0] == '%') /* comment. */
2799 continue;
2800 else if (dbp[0] == '/' && dbp[1] == '*') /* comment. */
2801 skip_comment (&lb, fi, &lineno, &linecharno);
2802 else /* found. */
2803 prolog_getit (dbp, lineno, linecharno);
2804 }
2805}
2806
2807void
2808skip_comment (plb, fi, plineno, plinecharno)
2809 struct linebuffer *plb;
2810 FILE *fi;
2811 int *plineno; /* result */
2812 long *plinecharno; /* result */
2813{
2814 while (!substr ("*/", plb->buffer))
2815 {
2816 (*plineno)++;
2817 *plinecharno += readline (plb, fi) + 1;
2818 } /* 1 for newline. */
2819}
2820
2821/* Return TRUE if 'sub' exists somewhere in 's'. */
2822
2823int
2824substr (sub, s)
2825 char *sub;
2826 char *s;
2827{
8a6c8bcf 2828 while (*s && (s = etags_index (s, *sub)))
c6d46f5f
JB
2829 if (prestr (sub, s))
2830 return (TRUE);
2831 else
2832 s++;
2833 return (FALSE);
2834}
2835
2836/* Return TRUE if 'pre' is prefix of string 's'. */
2837
2838int
2839prestr (pre, s)
2840 char *pre;
2841 char *s;
2842{
2843 if (*pre == '\0')
2844 return (TRUE);
2845 else if (*pre == *s)
2846 return (prestr (pre + 1, s + 1));
2847 else
2848 return (FALSE);
2849}
2850\f
2851/* Initialize a linebuffer for use */
2852
2853void
2854initbuffer (linebuffer)
2855 struct linebuffer *linebuffer;
2856{
2857 linebuffer->size = 200;
2858 linebuffer->buffer = xnew (200, char);
2859}
2860
2861/*
2862 * Read a line of text from `stream' into `linebuffer'.
2863 * Return the number of characters read from `stream',
2864 * which is the length of the line including the newline, if any.
2865 */
2866long
2867readline (linebuffer, stream)
2868 struct linebuffer *linebuffer;
2869 register FILE *stream;
2870{
2871 char *buffer = linebuffer->buffer;
2872 register char *p = linebuffer->buffer;
2873 register char *pend;
2874 int newline; /* 1 if ended with newline, 0 if ended with EOF */
2875
eb8c3be9 2876 pend = p + linebuffer->size; /* Separate to avoid 386/IX compiler bug. */
c6d46f5f
JB
2877
2878 while (1)
2879 {
2880 register int c = getc (stream);
2881 if (p == pend)
2882 {
2883 linebuffer->size *= 2;
2884 buffer = (char *) xrealloc (buffer, linebuffer->size);
2885 p += buffer - linebuffer->buffer;
2886 pend = buffer + linebuffer->size;
2887 linebuffer->buffer = buffer;
2888 }
2889 if (c < 0 || c == '\n')
2890 {
2891 *p = 0;
2892 newline = (c == '\n' ? 1 : 0);
2893 break;
2894 }
2895 *p++ = c;
2896 }
2897
2898 return p - buffer + newline;
2899}
2900\f
2901char *
2902savestr (cp)
2903 char *cp;
2904{
2905 return savenstr (cp, strlen (cp));
2906}
2907
2908char *
2909savenstr (cp, len)
2910 char *cp;
2911 int len;
2912{
2913 register char *dp;
2914
2915 dp = xnew (len + 1, char);
2916 (void) strncpy (dp, cp, len);
2917 dp[len] = '\0';
2918 return dp;
2919}
2920
c6d46f5f
JB
2921/*
2922 * Return the ptr in sp at which the character c last
2923 * appears; NULL if not found
2924 *
2925 * Identical to v7 rindex, included for portability.
2926 */
2927
2928char *
8a6c8bcf 2929etags_rindex (sp, c)
c6d46f5f
JB
2930 register char *sp, c;
2931{
2932 register char *r;
2933
2934 r = NULL;
2935 do
2936 {
2937 if (*sp == c)
2938 r = sp;
2939 } while (*sp++);
2940 return (r);
2941}
2942
9d7ad1b3 2943
c6d46f5f
JB
2944/*
2945 * Return the ptr in sp at which the character c first
2946 * appears; NULL if not found
2947 *
2948 * Identical to v7 index, included for portability.
2949 */
2950
2951char *
8a6c8bcf 2952etags_index (sp, c)
c6d46f5f
JB
2953 register char *sp, c;
2954{
2955 do
2956 {
2957 if (*sp == c)
2958 return (sp);
2959 } while (*sp++);
2960 return (NULL);
2961}
2962
c6d46f5f
JB
2963/* Print error message and exit. */
2964
2965/* VARARGS1 */
2966void
2967fatal (s1, s2)
2968 char *s1, *s2;
2969{
2970 error (s1, s2);
2971 exit (1);
2972}
2973
2974/* Print error message. `s1' is printf control string, `s2' is arg for it. */
2975
2976/* VARARGS1 */
2977void
2978error (s1, s2)
2979 char *s1, *s2;
2980{
2981 fprintf (stderr, "%s: ", progname);
2982 fprintf (stderr, s1, s2);
2983 fprintf (stderr, "\n");
2984}
2985
2986/* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */
2987
2988char *
2989concat (s1, s2, s3)
2990 char *s1, *s2, *s3;
2991{
2992 int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
2993 char *result = xnew (len1 + len2 + len3 + 1, char);
2994
2995 (void) strcpy (result, s1);
2996 (void) strcpy (result + len1, s2);
2997 (void) strcpy (result + len1 + len2, s3);
2998 *(result + len1 + len2 + len3) = 0;
2999
3000 return result;
3001}
3002
3003/* Like malloc but get fatal error if memory is exhausted. */
3004
3005char *
3006xmalloc (size)
3007 int size;
3008{
3009 char *result = malloc (size);
3010 if (!result)
3011 fatal ("virtual memory exhausted", 0);
3012 return result;
3013}
3014
3015char *
3016xrealloc (ptr, size)
3017 char *ptr;
3018 int size;
3019{
3020 char *result = realloc (ptr, size);
3021 if (!result)
3022 fatal ("virtual memory exhausted");
3023 return result;
3024}