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