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