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