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