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