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