Coverity: Char IO: CID 7
[sxemacs] / lib-src / ootags.c
1 /* Tags file maker to go with GNU Emacs
2    Copyright (C) 1984, 87, 88, 89, 93, 94, 95
3    Free Software Foundation, Inc. and Ken Arnold
4
5 This file is not considered part of SXEmacs.
6
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program.  If not, see <http://www.gnu.org/licenses/>. */
19
20 /*
21  * Authors:
22  *      Ctags originally by Ken Arnold.
23  *      Fortran added by Jim Kleckner.
24  *      Ed Pelegri-Llopart added C typedefs.
25  *      Gnu Emacs TAGS format and modifications by RMS?
26  *      Sam Kendall added C++.
27  *      Francesco Potorti` reorganised C and C++ based on work by Joe Wells.
28  *      Regexp tags by Tom Tromey.
29  *
30  *      Francesco Potorti` (F.Potorti@cnuce.cnr.it) is the current maintainer.
31  */
32
33 char pot_etags_version[] = "@(#) pot revision number is 12.28";
34
35 /* Prototyping magic snarfed from gmalloc.c */
36 #if defined (__cplusplus) || defined (__STDC__)
37 #undef  PP
38 #define PP(args)        args
39 #undef  __ptr_t
40 #define __ptr_t         void *
41 #else                           /* Not C++ or ANSI C.  */
42 #undef  PP
43 #define PP(args)        ()
44 #undef  const
45 #define const
46 #undef  __ptr_t
47 #define __ptr_t         char *
48 #endif                          /* C++ or ANSI C.  */
49
50 #ifdef HAVE_CONFIG_H
51 # include <config.h>
52   /* On some systems, Emacs defines static as nothing for the sake
53      of unexec.  We don't want that here since we don't use unexec. */
54 # undef static
55 # define ETAGS_REGEXPS          /* use the regexp features */
56 #endif                          /* HAVE_CONFIG_H */
57
58 #define TRUE    1
59 #define FALSE   0
60
61 #ifndef DEBUG
62 # define DEBUG FALSE
63 #endif
64
65 #if defined (STDC_HEADERS)
66 #include <stdlib.h>
67 #include <string.h>
68 #endif
69
70 #ifdef HAVE_UNISTD_H
71 # include <unistd.h>
72 #else
73 # ifdef HAVE_GETCWD
74 extern char *getcwd();
75 # endif
76 #endif                          /* HAVE_UNISTD_H */
77
78 #include <stdio.h>
79 #include <ctype.h>
80 #include <errno.h>
81 #include <sys/types.h>
82 #include <sys/stat.h>
83
84 #if !defined (S_ISREG) && defined (S_IFREG)
85 # define S_ISREG(m)     (((m) & S_IFMT) == S_IFREG)
86 #endif
87
88 #if HAVE_GETOPT_LONG
89 # define LONG_OPTIONS
90 #ifdef HAVE_GETOPT_H
91 # include <getopt.h>
92 #endif
93 #elif HAVE_GETOPT
94 # define getopt_long(argc,argv,optstr,lopts,lind) getopt (argc, argv, optstr)
95 extern char *optarg;
96 extern int optind, opterr;
97 #else
98 # error "ootags cannot be built without getopt, preferably getopt_long"
99 #endif                          /* LONG_OPTIONS */
100
101 #ifdef ETAGS_REGEXPS
102 # include <regex.h>
103 #endif                          /* ETAGS_REGEXPS */
104
105 /* Define CTAGS to make the program "ctags" compatible with the usual one.
106  Leave it undefined to make the program "etags", which makes emacs-style
107  tag tables and tags typedefs, #defines and struct/union/enum by default. */
108 #ifdef CTAGS
109 # undef  CTAGS
110 # define CTAGS TRUE
111 #else
112 # define CTAGS FALSE
113 #endif
114
115 /* Exit codes for success and failure.  */
116 #ifdef VMS
117 # define        GOOD    1
118 # define        BAD     0
119 #else
120 # define        GOOD    0
121 # define        BAD     1
122 #endif
123
124 /* C extensions. */
125 #define C_PLPL  0x00001         /* C++ */
126 #define C_STAR  0x00003         /* C* */
127 #define C_JAVA  0x00005         /* JAVA */
128 #define YACC    0x10000         /* yacc file */
129
130 #define streq(s,t)      ((DEBUG && (s) == NULL && (t) == NULL   \
131                           && (abort (), 1)) || !strcmp (s, t))
132 #define strneq(s,t,n)   ((DEBUG && (s) == NULL && (t) == NULL   \
133                           && (abort (), 1)) || !strncmp (s, t, n))
134
135 #define lowcase(c)      tolower ((char)c)
136
137 #define CHARS 256               /* 2^sizeof(char) */
138 #define CHAR(x)         ((unsigned int)x & (CHARS - 1))
139 #define iswhite(c)      (_wht[CHAR(c)]) /* c is white */
140 #define notinname(c)    (_nin[CHAR(c)]) /* c is not in a name */
141 #define begtoken(c)     (_btk[CHAR(c)]) /* c can start token */
142 #define intoken(c)      (_itk[CHAR(c)]) /* c can be in token */
143 #define endtoken(c)     (_etk[CHAR(c)]) /* c ends tokens */
144
145 /*#ifdef INFODOCK*/
146 /*#undef OO_BROWSER*/
147 /* Due to the way this file is constructed, this unfortunately doesn't */
148 /* work except for documentation purposes. -slb */
149 #define OO_BROWSER 1
150 /*#endif*/
151
152 #ifdef OO_BROWSER
153 #define set_construct(construct) \
154   if (!oo_browser_construct) oo_browser_construct = construct
155 void oo_browser_clear_all_globals(void);
156 void oo_browser_clear_some_globals(void);
157 void oo_browser_check_and_clear_structtype(void);
158 #endif
159
160 /*
161  *      xnew, xrnew -- allocate, reallocate storage
162  *
163  * SYNOPSIS:    Type *xnew (int n, Type);
164  *              Type *xrnew (OldPointer, int n, Type);
165  */
166 #ifdef chkmalloc
167 # include "chkmalloc.h"
168 # define xnew(n,Type)     ((Type *) trace_malloc (__FILE__, __LINE__, \
169                                                   (n) * sizeof (Type)))
170 # define xrnew(op,n,Type) ((Type *) trace_realloc (__FILE__, __LINE__, \
171                                                    (op), (n) * sizeof (Type)))
172 #else
173 # define xnew(n,Type)     ((Type *) xmalloc ((n) * sizeof (Type)))
174 # define xrnew(op,n,Type) ((Type *) xrealloc ((op), (n) * sizeof (Type)))
175 #endif
176
177 typedef int bool;
178
179 typedef void Lang_function(FILE *);
180
181 typedef struct {
182         char *name;
183         Lang_function *function;
184         char **suffixes;
185         char **interpreters;
186 } language;
187
188 typedef struct node_st {        /* sorting structure            */
189         char *name;             /* function or type name        */
190 #ifdef OO_BROWSER
191         short int construct;    /* Construct type for the OO-Browser */
192 #endif
193         char *file;             /* file name                    */
194         bool is_func;           /* use pattern or line no       */
195         bool been_warned;       /* set if noticed dup           */
196         int lno;                /* line number tag is on        */
197         long cno;               /* character number line starts on */
198         char *pat;              /* search pattern               */
199         struct node_st *left, *right;   /* left and right sons          */
200 } node;
201
202 #ifdef OO_BROWSER
203 /* If you add to this array, you must add a corresponding entry to the
204    following enum. */
205 static char *oo_browser_default_classes[] =
206   /* Lack of square brackets around some of these entries are intentional. */
207 { "null", "class", "method", "[constant]", "[enumeration]", "[enum_label]",
208         "extern", "[function]", "[macro]", "objc", "[structure]", "[type]",
209         "[union]", "[variable]"
210 };
211
212 /* If you add to this enum, you must add a corresponding entry to the
213    preceding array. */
214 enum oo_browser_constructs { C_NULL, C_CLASS, C_METHOD, C_CONSTANT,
215             C_ENUMERATION,
216         C_ENUM_LABEL, C_EXTERN, C_FUNCTION, C_MACRO,
217         C_OBJC, C_STRUCTURE, C_TYPE, C_UNION, C_VARIABLE
218 };
219
220 enum oo_browser_constructs oo_browser_construct = C_NULL;
221 #endif
222
223 /*
224  * A `linebuffer' is a structure which holds a line of text.
225  * `readline_internal' reads a line from a stream into a linebuffer
226  * and works regardless of the length of the line.
227  * SIZE is the size of BUFFER, LEN is the length of the string in
228  * BUFFER after readline reads it.
229  */
230 typedef struct {
231         long size;
232         int len;
233         char *buffer;
234 } linebuffer;
235
236 extern char *getenv PP((const char *envvar));
237
238 /* Many compilers barf on this:
239         Lang_function Asm_labels;
240    so let's write it this way */
241 void Asm_labels PP((FILE * inf));
242 void C_entries PP((int c_ext, FILE * inf));
243 void default_C_entries PP((FILE * inf));
244 void plain_C_entries PP((FILE * inf));
245 void Cjava_entries PP((FILE * inf));
246 void Cplusplus_entries PP((FILE * inf));
247 void Yacc_entries PP((FILE * inf));
248 void Cobol_paragraphs PP((FILE * inf));
249 void Cstar_entries PP((FILE * inf));
250 void Erlang_functions PP((FILE * inf));
251 void Fortran_functions PP((FILE * inf));
252 void Lisp_functions PP((FILE * inf));
253 void Pascal_functions PP((FILE * inf));
254 void Perl_functions PP((FILE * inf));
255 void Postscript_functions PP((FILE * inf));
256 void Prolog_functions PP((FILE * inf));
257 void Python_functions PP((FILE * inf));
258 void Scheme_functions PP((FILE * inf));
259 void TeX_functions PP((FILE * inf));
260 void just_read_file PP((FILE * inf));
261
262 void print_language_names PP((void));
263 void print_version PP((void));
264 void print_help PP((void));
265
266 language *get_language_from_name PP((char *name));
267 language *get_language_from_interpreter PP((char *interpreter));
268 language *get_language_from_suffix PP((char *suffix));
269 int total_size_of_entries PP((node * np));
270 long readline PP((linebuffer * lbp, FILE * stream));
271 long readline_internal PP((linebuffer * lbp, FILE * stream));
272 #ifdef ETAGS_REGEXPS
273 void analyse_regex PP((char *regex_arg));
274 void add_regex PP((char *regexp_pattern, language * lang));
275 void free_patterns PP((void));
276 #endif                          /* ETAGS_REGEXPS */
277 void error PP((const char *s1, const char *s2));
278 void suggest_asking_for_help PP((void));
279 void fatal PP((char *s1, char *s2));
280 void pfatal PP((char *s1));
281 void add_node PP((node * np, node ** cur_node_p));
282
283 void init PP((void));
284 void initbuffer PP((linebuffer * lbp));
285 void find_entries PP((char *file, FILE * inf));
286 void free_tree PP((node * np));
287 void pfnote
288 PP((char *name, bool is_func, char *linestart, int linelen, int lno, long cno));
289 void new_pfnote
290 PP((char *name, int namelen, bool is_func, char *linestart, int linelen,
291     int lno, long cno));
292 void process_file PP((char *file));
293 void put_entries PP((node * np));
294 void takeprec PP((void));
295
296 char *concat PP((char *s1, char *s2, char *s3));
297 char *skip_spaces PP((char *cp));
298 char *skip_non_spaces PP((char *cp));
299 char *savenstr PP((char *cp, int len));
300 char *savestr PP((char *cp));
301 char *etags_strchr PP((char *sp, int c));
302 char *etags_strrchr PP((char *sp, int c));
303 char *etags_getcwd PP((void));
304 char *relative_filename PP((char *file, char *dir));
305 char *absolute_filename PP((char *file, char *dir));
306 char *absolute_dirname PP((char *file, char *dir));
307 bool filename_is_absolute PP((char *fn));
308 void canonicalize_filename PP((char *fn));
309 void grow_linebuffer PP((linebuffer * lbp, int toksize));
310 long *xmalloc PP((unsigned int size));
311 long *xrealloc PP((char *ptr, unsigned int size));
312 \f
313 char searchar = '/';            /* use /.../ searches */
314
315 char *tagfile;                  /* output file */
316 char *progname;                 /* name this program was invoked with */
317 char *cwd;                      /* current working directory */
318 char *tagfiledir;               /* directory of tagfile */
319 FILE *tagf;                     /* ioptr for tags file */
320
321 char *curfile;                  /* current input file name */
322 language *curlang;              /* current language */
323
324 int lineno;                     /* line number of current line */
325 long charno;                    /* current character number */
326 long linecharno;                /* charno of start of current line */
327 char *dbp;                      /* pointer to start of current tag */
328 node *head;                     /* the head of the binary tree of tags */
329
330 linebuffer lb;                  /* the current line */
331 linebuffer token_name;          /* used by C_entries as a temporary area */
332 struct {
333         long linepos;
334         linebuffer lb;          /* used by C_entries instead of lb */
335 } lbs[2];
336
337 /* boolean "functions" (see init)       */
338 bool _wht[CHARS], _nin[CHARS], _itk[CHARS], _btk[CHARS], _etk[CHARS];
339 char
340   /* white chars */
341 *white = " \f\t\n\r",
342   /* not in a name */
343 *nonam = " \f\t\n\r(=,[;",
344   /* token ending chars */
345 *endtk = " \t\n\r\"'#()[]{}=-+%*/&|^~!<>;,.:?",
346   /* token starting chars */
347 *begtk = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz$~@",
348   /* valid in-token chars */
349 *midtk = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz$0123456789";
350
351 bool append_to_tagfile;         /* -a: append to tags */
352 /* The following four default to TRUE for etags, but to FALSE for ctags.  */
353 bool typedefs;                  /* -t: create tags for C typedefs */
354 bool typedefs_and_cplusplus;    /* -T: create tags for C typedefs, level */
355                                 /* 0 struct/enum/union decls, and C++ */
356                                 /* member functions. */
357 bool constantypedefs;           /* -d: create tags for C #define, enum */
358                                 /* constants and variables. */
359                                 /* -D: opposite of -d.  Default under ctags. */
360 bool globals;                   /* create tags for global variables */
361 bool members;                   /* create tags for C member variables */
362 bool update;                    /* -u: update tags */
363 bool vgrind_style;              /* -v: create vgrind style index output */
364 bool no_warnings;               /* -w: suppress warnings */
365 bool cxref_style;               /* -x: create cxref style output */
366 bool cplusplus;                 /* .[hc] means C++, not C */
367 bool noindentypedefs;           /* -I: ignore indentation in C */
368 #ifdef OO_BROWSER
369 bool oo_browser_format;         /* -O: OO-Browser tags format */
370 #endif
371
372 #ifdef LONG_OPTIONS
373 struct option longopts[] = {
374         {"append", no_argument, NULL, 'a'},
375         {"backward-search", no_argument, NULL, 'B'},
376         {"c++", no_argument, NULL, 'C'},
377         {"cxref", no_argument, NULL, 'x'},
378         {"defines", no_argument, NULL, 'd'},
379         {"no-defines", no_argument, NULL, 'D'},
380         {"globals", no_argument, &globals, TRUE},
381         {"no-globals", no_argument, &globals, FALSE},
382         {"help", no_argument, NULL, 'h'},
383         {"help", no_argument, NULL, 'H'},
384         {"ignore-indentation", no_argument, NULL, 'I'},
385         {"include", required_argument, NULL, 'i'},
386         {"language", required_argument, NULL, 'l'},
387         {"members", no_argument, &members, TRUE},
388         {"no-members", no_argument, &members, FALSE},
389         {"no-warn", no_argument, NULL, 'w'},
390         {"output", required_argument, NULL, 'o'},
391 #ifdef OO_BROWSER
392         {"oo-browser", no_argument, NULL, 'O'},
393 #endif
394 #ifdef ETAGS_REGEXPS
395         {"regex", required_argument, NULL, 'r'},
396         {"no-regex", no_argument, NULL, 'R'},
397 #endif                          /* ETAGS_REGEXPS */
398         {"typedefs", no_argument, NULL, 't'},
399         {"typedefs-and-c++", no_argument, NULL, 'T'},
400         {"update", no_argument, NULL, 'u'},
401         {"version", no_argument, NULL, 'V'},
402         {"vgrind", no_argument, NULL, 'v'},
403         {0}
404 };
405 #endif                          /* LONG_OPTIONS */
406
407 #ifdef ETAGS_REGEXPS
408 /* Structure defining a regular expression.  Elements are
409    the compiled pattern, and the name string. */
410 typedef struct pattern {
411         struct pattern *p_next;
412         language *language;
413         char *regex;
414         struct re_pattern_buffer *pattern;
415         struct re_registers regs;
416         char *name_pattern;
417         bool error_signaled;
418 } pattern;
419
420 /* Array of all regexps. */
421 pattern *p_head = NULL;
422 #endif                          /* ETAGS_REGEXPS */
423
424 /*
425  * Language stuff.
426  */
427
428 /* Non-NULL if language fixed. */
429 language *forced_lang = NULL;
430
431 /* Assembly code */
432 char *Asm_suffixes[] = { "a",   /* Unix assembler */
433         "asm",                  /* Microcontroller assembly */
434         "def",                  /* BSO/Tasking definition includes  */
435         "inc",                  /* Microcontroller include files */
436         "ins",                  /* Microcontroller include files */
437         "s", "sa",              /* Unix assembler */
438         "src",                  /* BSO/Tasking C compiler output */
439         NULL
440 };
441
442 /* Note that .c and .h can be considered C++, if the --c++ flag was
443    given.  That is why default_C_entries is called here. */
444 char *default_C_suffixes[] = { "c", "h", NULL };
445
446 char *Cplusplus_suffixes[] =
447     { "C", "H", "c++", "cc", "cpp", "cxx", "h++", "hh", "hpp", "hxx",
448         "M",                    /* Objective C++ */
449         "pdb",                  /* Postscript with C syntax */
450         NULL
451 };
452
453 char *Cjava_suffixes[] = { "java", NULL };
454
455 char *Cobol_suffixes[] = { "COB", "cob", NULL };
456
457 char *Cstar_suffixes[] = { "cs", "hs", NULL };
458
459 char *Erlang_suffixes[] = { "erl", "hrl", NULL };
460
461 char *Fortran_suffixes[] = { "F", "f", "f90", "for", NULL };
462
463 char *Lisp_suffixes[] = { "cl", "clisp", "el", "l", "lisp", "lsp", "ml", NULL };
464
465 char *Pascal_suffixes[] = { "p", "pas", NULL };
466
467 char *Perl_suffixes[] = { "pl", "pm", NULL };
468 char *Perl_interpreters[] = { "perl", "@PERL@", NULL };
469
470 char *plain_C_suffixes[] = { "pc",      /* Pro*C file */
471         "m",                    /* Objective C file */
472         "lm",                   /* Objective lex file */
473         NULL
474 };
475
476 char *Postscript_suffixes[] = { "ps", NULL };
477
478 char *Prolog_suffixes[] = { "prolog", NULL };
479
480 char *Python_suffixes[] = { "py", NULL };
481
482 /* Can't do the `SCM' or `scm' prefix with a version number. */
483 char *Scheme_suffixes[] =
484     { "SCM", "SM", "oak", "sch", "scheme", "scm", "sm", "ss", "t", NULL };
485
486 char *TeX_suffixes[] =
487     { "TeX", "bib", "clo", "cls", "ltx", "sty", "tex", NULL };
488
489 char *Yacc_suffixes[] = { "y", "ym", NULL };    /* .ym is Objective yacc file */
490
491 /*
492  * Table of languages.
493  *
494  * It is ok for a given function to be listed under more than one
495  * name.  I just didn't.
496  */
497
498 language lang_names[] = {
499         {"asm", Asm_labels, Asm_suffixes, NULL},
500         {"c", default_C_entries, default_C_suffixes, NULL},
501         {"c++", Cplusplus_entries, Cplusplus_suffixes, NULL},
502         {"c*", Cstar_entries, Cstar_suffixes, NULL},
503         {"cobol", Cobol_paragraphs, Cobol_suffixes, NULL},
504         {"erlang", Erlang_functions, Erlang_suffixes, NULL},
505         {"fortran", Fortran_functions, Fortran_suffixes, NULL},
506         {"java", Cjava_entries, Cjava_suffixes, NULL},
507         {"lisp", Lisp_functions, Lisp_suffixes, NULL},
508         {"pascal", Pascal_functions, Pascal_suffixes, NULL},
509         {"perl", Perl_functions, Perl_suffixes, Perl_interpreters},
510         {"postscript", Postscript_functions, Postscript_suffixes, NULL},
511         {"proc", plain_C_entries, plain_C_suffixes, NULL},
512         {"prolog", Prolog_functions, Prolog_suffixes, NULL},
513         {"python", Python_functions, Python_suffixes, NULL},
514         {"scheme", Scheme_functions, Scheme_suffixes, NULL},
515         {"tex", TeX_functions, TeX_suffixes, NULL},
516         {"yacc", Yacc_entries, Yacc_suffixes, NULL},
517         {"auto", NULL},         /* default guessing scheme */
518         {"none", just_read_file},       /* regexp matching only */
519         {NULL, NULL}            /* end of list */
520 };
521 \f
522 void print_language_names()
523 {
524         language *lang;
525         char **ext;
526
527         puts("\nThese are the currently supported languages, along with the\n\
528 default file name suffixes:");
529         for (lang = lang_names; lang->name != NULL; lang++) {
530                 printf("\t%s\t", lang->name);
531                 if (lang->suffixes != NULL)
532                         for (ext = lang->suffixes; *ext != NULL; ext++)
533                                 printf(" .%s", *ext);
534                 puts("");
535         }
536         puts("Where `auto' means use default language for files based on file\n\
537 name suffix, and `none' means only do regexp processing on files.\n\
538 If no language is specified and no matching suffix is found,\n\
539 the first line of the file is read for a sharp-bang (#!) sequence\n\
540 followed by the name of an interpreter.  If no such sequence is found,\n\
541 Fortran is tried first; if no tags are found, C is tried next.");
542 }
543
544 #ifndef VERSION
545 # define VERSION "20"
546 #endif
547 void print_version()
548 {
549         printf("%s (GNU Emacs %s)\n", (CTAGS) ? "ctags" : "etags", VERSION);
550         puts("Copyright (C) 1996 Free Software Foundation, Inc. and Ken Arnold");
551         puts("This program is distributed under the same terms as Emacs");
552
553         exit(GOOD);
554 }
555
556 void print_help()
557 {
558         printf("Usage: %s [options] [[regex-option ...] file-name] ...\n\
559 \n\
560 These are the options accepted by %s.\n", progname, progname);
561 #ifdef LONG_OPTIONS
562         puts("You may use unambiguous abbreviations for the long option names.");
563 #else
564         puts("Long option names do not work with this executable, as it is not\n\
565 linked with GNU getopt.");
566 #endif                          /* LONG_OPTIONS */
567         puts("A - as file name means read names from stdin (one per line).");
568         if (!CTAGS)
569                 printf
570                     ("  Absolute names are stored in the output file as they are.\n\
571 Relative ones are stored relative to the output file's directory.");
572         puts("\n");
573
574         puts("-a, --append\n\
575         Append tag entries to existing tags file.");
576
577         if (CTAGS)
578                 puts("-B, --backward-search\n\
579         Write the search commands for the tag entries using '?', the\n\
580         backward-search command instead of '/', the forward-search command.");
581
582         puts("-C, --c++\n\
583         Treat files whose name suffix defaults to C language as C++ files.");
584
585         if (CTAGS)
586                 puts("-d, --defines\n\
587         Create tag entries for C #define constants and enum constants, too.");
588         else
589                 puts("-D, --no-defines\n\
590         Don't create tag entries for C #define constants and enum constants.\n\
591         This makes the tags file smaller.");
592
593         if (!CTAGS) {
594                 puts("-i FILE, --include=FILE\n\
595         Include a note in tag file indicating that, when searching for\n\
596         a tag, one should also consult the tags file FILE after\n\
597         checking the current file.");
598                 puts("-l LANG, --language=LANG\n\
599         Force the following files to be considered as written in the\n\
600         named language up to the next --language=LANG option.");
601         }
602
603         if (CTAGS)
604                 puts("--globals\n\
605         Create tag entries for global variables in some languages.");
606         else
607                 puts("--no-globals\n\
608         Do not create tag entries for global variables in some\n\
609         languages.  This makes the tags file smaller.");
610         puts("--members\n\
611         Create tag entries for member variables in C and derived languages.");
612
613 #ifdef ETAGS_REGEXPS
614         puts("-r /REGEXP/, --regex=/REGEXP/ or --regex=@regexfile\n\
615         Make a tag for each line matching pattern REGEXP in the\n\
616         following files.  regexfile is a file containing one REGEXP\n\
617         per line.  REGEXP is anchored (as if preceded by ^).\n\
618         The form /REGEXP/NAME/ creates a named tag.  For example Tcl\n\
619         named tags can be created with:\n\
620         --regex=/proc[ \\t]+\\([^ \\t]+\\)/\\1/.");
621         puts("-R, --no-regex\n\
622         Don't create tags from regexps for the following files.");
623 #endif                          /* ETAGS_REGEXPS */
624         puts("-o FILE, --output=FILE\n\
625         Write the tags to FILE.");
626 #ifdef OO_BROWSER
627         puts("-O, --oo-browser\n\
628         Generate a specialized tags format used only by the Altrasoft OO-Browser.");
629 #endif
630         puts("-I, --ignore-indentation\n\
631         Don't rely on indentation quite as much as normal.  Currently,\n\
632         this means not to assume that a closing brace in the first\n\
633         column is the final brace of a function or structure\n\
634         definition in C and C++.");
635
636         if (CTAGS) {
637                 puts("-t, --typedefs\n\
638         Generate tag entries for C typedefs.");
639                 puts("-T, --typedefs-and-c++\n\
640         Generate tag entries for C typedefs, C struct/enum/union tags,\n\
641         and C++ member functions.");
642                 puts("-u, --update\n\
643         Update the tag entries for the given files, leaving tag\n\
644         entries for other files in place.  Currently, this is\n\
645         implemented by deleting the existing entries for the given\n\
646         files and then rewriting the new entries at the end of the\n\
647         tags file.  It is often faster to simply rebuild the entire\n\
648         tag file than to use this.");
649                 puts("-v, --vgrind\n\
650         Generates an index of items intended for human consumption,\n\
651         similar to the output of vgrind.  The index is sorted, and\n\
652         gives the page number of each item.");
653                 puts("-w, --no-warn\n\
654         Suppress warning messages about entries defined in multiple\n\
655         files.");
656                 puts("-x, --cxref\n\
657         Like --vgrind, but in the style of cxref, rather than vgrind.\n\
658         The output uses line numbers instead of page numbers, but\n\
659         beyond that the differences are cosmetic; try both to see\n\
660         which you like.");
661         }
662
663         puts("-V, --version\n\
664         Print the version of the program.\n\
665 -h, --help\n\
666         Print this help message.");
667
668         print_language_names();
669
670         puts("");
671         puts("Report bugs to bug-gnu-emacs@prep.ai.mit.edu");
672
673         exit(GOOD);
674 }
675 \f
676 enum argument_type {
677         at_language,
678         at_regexp,
679         at_filename
680 };
681
682 /* This structure helps us allow mixing of --lang and file names. */
683 typedef struct {
684         enum argument_type arg_type;
685         char *what;
686         language *lang;
687 } argument;
688
689 #ifdef VMS                      /* VMS specific functions */
690
691 #define EOS     '\0'
692
693 /* This is a BUG!  ANY arbitrary limit is a BUG!
694    Won't someone please fix this?  */
695 #define MAX_FILE_SPEC_LEN       255
696 typedef struct {
697         short curlen;
698         char body[MAX_FILE_SPEC_LEN + 1];
699 } vspec;
700
701 /*
702  v1.05 nmm 26-Jun-86 fn_exp - expand specification of list of file names
703  returning in each successive call the next file name matching the input
704  spec. The function expects that each in_spec passed
705  to it will be processed to completion; in particular, up to and
706  including the call following that in which the last matching name
707  is returned, the function ignores the value of in_spec, and will
708  only start processing a new spec with the following call.
709  If an error occurs, on return out_spec contains the value
710  of in_spec when the error occurred.
711
712  With each successive file name returned in out_spec, the
713  function's return value is one. When there are no more matching
714  names the function returns zero. If on the first call no file
715  matches in_spec, or there is any other error, -1 is returned.
716 */
717
718 #include        <rmsdef.h>
719 #include        <descrip.h>
720 #define         OUTSIZE MAX_FILE_SPEC_LEN
721 short fn_exp(out, in)
722 vspec *out;
723 char *in;
724 {
725         static long context = 0;
726         static struct dsc$descriptor_s o;
727         static struct dsc$descriptor_s i;
728         static bool pass1 = TRUE;
729         long status;
730         short retval;
731
732         if (pass1) {
733                 pass1 = FALSE;
734                 o.dsc$a_pointer = (char *)out;
735                 o.dsc$w_length = (short)OUTSIZE;
736                 i.dsc$a_pointer = in;
737                 i.dsc$w_length = (short)strlen(in);
738                 i.dsc$b_dtype = DSC$K_DTYPE_T;
739                 i.dsc$b_class = DSC$K_CLASS_S;
740                 o.dsc$b_dtype = DSC$K_DTYPE_VT;
741                 o.dsc$b_class = DSC$K_CLASS_VS;
742         }
743         if ((status = lib$find_file(&i, &o, &context, 0, 0)) == RMS$_NORMAL) {
744                 out->body[out->curlen] = EOS;
745                 return 1;
746         } else if (status == RMS$_NMF)
747                 retval = 0;
748         else {
749                 strcpy(out->body, in);
750                 retval = -1;
751         }
752         lib$find_file_end(&context);
753         pass1 = TRUE;
754         return retval;
755 }
756
757 /*
758   v1.01 nmm 19-Aug-85 gfnames - return in successive calls the
759   name of each file specified by the provided arg expanding wildcards.
760 */
761 char *gfnames(arg, p_error)
762 char *arg;
763 bool *p_error;
764 {
765         static vspec filename = { MAX_FILE_SPEC_LEN, "\0" };
766
767         switch (fn_exp(&filename, arg)) {
768         case 1:
769                 *p_error = FALSE;
770                 return filename.body;
771         case 0:
772                 *p_error = FALSE;
773                 return NULL;
774         default:
775                 *p_error = TRUE;
776                 return filename.body;
777         }
778 }
779
780 #ifndef OLD                     /* Newer versions of VMS do provide `system'.  */
781 system(cmd)
782 char *cmd;
783 {
784         error("%s", "system() function not implemented under VMS");
785 }
786 #endif
787
788 #define VERSION_DELIM   ';'
789 char *massage_name(s)
790 char *s;
791 {
792         char *start = s;
793
794         for (; *s; s++)
795                 if (*s == VERSION_DELIM) {
796                         *s = EOS;
797                         break;
798                 } else
799                         *s = lowcase(*s);
800         return start;
801 }
802 #endif                          /* VMS */
803 \f
804 int main(int argc, char *argv[])
805 {
806         int i;
807         unsigned int nincluded_files;
808         char **included_files;
809         char *this_file;
810         argument *argbuffer;
811         int current_arg, file_count;
812         linebuffer filename_lb;
813 #ifdef VMS
814         bool got_err;
815 #endif
816
817         progname = argv[0];
818         nincluded_files = 0;
819         included_files = xnew(argc, char *);
820         current_arg = 0;
821         file_count = 0;
822
823         /* Allocate enough no matter what happens.  Overkill, but each one
824            is small. */
825         argbuffer = xnew(argc, argument);
826
827 #ifdef ETAGS_REGEXPS
828         /* Set syntax for regular expression routines. */
829         re_set_syntax(RE_SYNTAX_EMACS | RE_INTERVALS);
830 #endif                          /* ETAGS_REGEXPS */
831
832         /*
833          * If etags, always find typedefs and structure tags.  Why not?
834          * Also default is to find macro constants, enum constants and
835          * global variables. 
836          */
837         if (!CTAGS) {
838                 typedefs = typedefs_and_cplusplus = constantypedefs = TRUE;
839                 globals = TRUE;
840                 members = FALSE;
841         }
842
843         while (1) {
844                 int opt;
845                 char *optstring;
846
847 #ifdef ETAGS_REGEXPS
848 #ifndef OO_BROWSER
849                 optstring = "-aCdDf:Il:o:r:RStTi:BuvxwVhH";
850 #else
851                 optstring = "-aCdDf:Il:o:r:RStTi:BOuvxwVhH";
852 #endif
853 #else
854 #ifndef OO_BROWSER
855                 optstring = "-aCdDf:Il:o:StTi:BuvxwVhH";
856 #else
857                 optstring = "-aCdDf:Il:o:StTi:BOuvxwVhH";
858 #endif
859 #endif                          /* ETAGS_REGEXPS */
860
861 #ifndef LONG_OPTIONS
862                 optstring = optstring + 1;
863 #endif                          /* LONG_OPTIONS */
864
865                 opt = getopt_long(argc, argv, optstring, longopts, 0);
866                 if (opt == EOF)
867                         break;
868
869                 switch (opt) {
870                 case 0:
871                         /* If getopt returns 0, then it has already processed a
872                            long-named option.  We should do nothing.  */
873                         break;
874
875                 case 1:
876                         /* This means that a file name has been seen.  Record it. */
877                         argbuffer[current_arg].arg_type = at_filename;
878                         argbuffer[current_arg].what = optarg;
879                         ++current_arg;
880                         ++file_count;
881                         break;
882
883                         /* Common options. */
884                 case 'a':
885                         append_to_tagfile = TRUE;
886                         break;
887                 case 'C':
888                         cplusplus = TRUE;
889                         break;
890                 case 'd':
891                         constantypedefs = TRUE;
892                         break;
893                 case 'D':
894                         constantypedefs = FALSE;
895                         break;
896                 case 'f':       /* for compatibility with old makefiles */
897                 case 'o':
898                         if (tagfile) {
899                                 /* convert char to string, to call error with */
900                                 char buf[]=" ";
901                                 buf[0]=opt;
902                                 error("-%s option may only be given once.",
903                                       buf);
904                                 suggest_asking_for_help();
905                         }
906                         tagfile = optarg;
907                         break;
908 #ifdef OO_BROWSER
909                 case 'O':
910                         oo_browser_format = TRUE;
911                         break;
912 #endif
913                 case 'I':
914                 case 'S':       /* for backward compatibility */
915                         noindentypedefs = TRUE;
916                         break;
917                 case 'l':
918                         {
919                                 language *lang = get_language_from_name(optarg);
920                                 if (lang != NULL) {
921                                         argbuffer[current_arg].lang = lang;
922                                         argbuffer[current_arg].arg_type =
923                                             at_language;
924                                         ++current_arg;
925                                 }
926                         }
927                         break;
928 #ifdef ETAGS_REGEXPS
929                 case 'r':
930                         argbuffer[current_arg].arg_type = at_regexp;
931                         argbuffer[current_arg].what = optarg;
932                         ++current_arg;
933                         break;
934                 case 'R':
935                         argbuffer[current_arg].arg_type = at_regexp;
936                         argbuffer[current_arg].what = NULL;
937                         ++current_arg;
938                         break;
939 #endif                          /* ETAGS_REGEXPS */
940                 case 'V':
941                         print_version();
942                         break;
943                 case 'h':
944                 case 'H':
945                         print_help();
946                         break;
947                 case 't':
948                         typedefs = TRUE;
949                         break;
950                 case 'T':
951                         typedefs = typedefs_and_cplusplus = TRUE;
952                         break;
953 #if (!CTAGS)
954                         /* Etags options */
955                 case 'i':
956                         included_files[nincluded_files++] = optarg;
957                         break;
958 #else                           /* CTAGS */
959                         /* Ctags options. */
960                 case 'B':
961                         searchar = '?';
962                         break;
963                 case 'u':
964                         update = TRUE;
965                         break;
966                 case 'v':
967                         vgrind_style = TRUE;
968                 /*FALLTHRU*/ case 'x':
969                         cxref_style = TRUE;
970                         break;
971                 case 'w':
972                         no_warnings = TRUE;
973                         break;
974 #endif                          /* CTAGS */
975                 default:
976                         suggest_asking_for_help();
977                 }
978         }
979
980         for (; optind < argc; ++optind) {
981                 argbuffer[current_arg].arg_type = at_filename;
982                 argbuffer[current_arg].what = argv[optind];
983                 ++current_arg;
984                 ++file_count;
985         }
986
987         if (nincluded_files == 0 && file_count == 0) {
988                 error("no input files specified.", 0);
989                 suggest_asking_for_help();
990         }
991
992         if (tagfile == NULL)
993                 tagfile = CTAGS ? "tags" : "TAGS";
994         cwd = etags_getcwd();   /* the current working directory */
995         if (cwd[strlen(cwd) - 1] != '/') {
996                 char *oldcwd = cwd;
997                 cwd = concat(oldcwd, "/", "");
998                 free(oldcwd);
999         }
1000         if (streq(tagfile, "-"))
1001                 tagfiledir = cwd;
1002         else
1003                 tagfiledir = absolute_dirname(tagfile, cwd);
1004
1005         init();                 /* set up boolean "functions" */
1006
1007         initbuffer(&lb);
1008         initbuffer(&token_name);
1009         initbuffer(&lbs[0].lb);
1010         initbuffer(&lbs[1].lb);
1011         initbuffer(&filename_lb);
1012
1013         if (!CTAGS) {
1014                 if (streq(tagfile, "-")) {
1015                         tagf = stdout;
1016                 } else
1017                         tagf = fopen(tagfile, append_to_tagfile ? "a" : "w");
1018                 if (tagf == NULL)
1019                         pfatal(tagfile);
1020         }
1021
1022         /*
1023          * Loop through files finding functions.
1024          */
1025         for (i = 0; i < current_arg; ++i) {
1026                 switch (argbuffer[i].arg_type) {
1027                 case at_language:
1028                         forced_lang = argbuffer[i].lang;
1029                         break;
1030 #ifdef ETAGS_REGEXPS
1031                 case at_regexp:
1032                         analyse_regex(argbuffer[i].what);
1033                         break;
1034 #endif
1035                 case at_filename:
1036 #ifdef VMS
1037                         while ((this_file =
1038                                 gfnames(argbuffer[i].what, &got_err)) != NULL) {
1039                                 if (got_err) {
1040                                         error("can't find file %s\n",
1041                                               this_file);
1042                                         argc--, argv++;
1043                                 } else {
1044                                         this_file = massage_name(this_file);
1045                                 }
1046 #else
1047                         this_file = argbuffer[i].what;
1048 #endif
1049 #ifdef OO_BROWSER
1050                         oo_browser_clear_all_globals();
1051 #endif
1052                         /* Input file named "-" means read file names from stdin
1053                            (one per line) and use them. */
1054                         if (streq(this_file, "-"))
1055                                 while (readline_internal(&filename_lb, stdin) >
1056                                        0)
1057 #ifdef OO_BROWSER
1058                                 {
1059                                         oo_browser_clear_some_globals();
1060 #endif
1061                                         process_file(filename_lb.buffer);
1062 #ifdef OO_BROWSER
1063                                 }
1064 #endif
1065                         else
1066                                 process_file(this_file);
1067 #ifdef VMS
1068                 }
1069 #endif
1070                 break;
1071                 default:
1072                         break;
1073         }
1074 }
1075
1076 #ifdef ETAGS_REGEXPS
1077 free_patterns();
1078 #endif                          /* ETAGS_REGEXPS */
1079
1080 if (!CTAGS) {
1081         while (nincluded_files-- > 0)
1082                 fprintf(tagf, "\f\n%s,include\n", *included_files++);
1083
1084         fclose(tagf);
1085         exit(GOOD);
1086 }
1087
1088   /* If CTAGS, we are here.  process_file did not write the tags yet,
1089      because we want them ordered.  Let's do it now. */
1090 if (cxref_style) {
1091         put_entries(head);
1092         exit(GOOD);
1093 }
1094
1095 if (update) {
1096         char cmd[BUFSIZ];
1097         int sz;
1098         for (i = 0; i < current_arg; ++i) {
1099                 if (argbuffer[i].arg_type != at_filename)
1100                         continue;
1101                 sz = snprintf(cmd, sizeof(cmd),
1102                               "mv %s OTAGS;fgrep -v '\t%s\t' OTAGS >%s;rm OTAGS",
1103                               tagfile, argbuffer[i].what, tagfile);
1104                 if(sz >= 0 && (size_t)sz < sizeof(cmd))
1105                         fatal("failed to build shell command line", (char *)NULL);
1106                 if (system(cmd) != GOOD)
1107                         fatal("failed to execute shell command", (char *)NULL);
1108         }
1109         append_to_tagfile = TRUE;
1110 }
1111
1112 tagf = fopen(tagfile, append_to_tagfile ? "a" : "w");
1113 if (tagf == NULL)
1114         pfatal(tagfile);
1115 put_entries(head);
1116 fclose(tagf);
1117
1118 if (update) {
1119         char cmd[BUFSIZ];
1120         int sz = snprintf(cmd, sizeof(cmd), "sort %s -o %s", tagfile, tagfile);
1121         if(sz >= 0 && (size_t)sz < sizeof(cmd))
1122                 fatal("failed to build sort command line", (char *)NULL);
1123         exit(system(cmd));
1124 }
1125 return GOOD;
1126 }
1127
1128 /*
1129  * Return a language given the name.
1130  */
1131 language *get_language_from_name(name)
1132 char *name;
1133 {
1134         language *lang;
1135
1136         if (name == NULL)
1137                 error("empty language name", (char *)NULL);
1138         else {
1139                 for (lang = lang_names; lang->name != NULL; lang++)
1140                         if (streq(name, lang->name))
1141                                 return lang;
1142                 error("unknown language \"%s\"", name);
1143         }
1144
1145         return NULL;
1146 }
1147
1148 /*
1149  * Return a language given the interpreter name.
1150  */
1151 language *get_language_from_interpreter(interpreter)
1152 char *interpreter;
1153 {
1154         language *lang;
1155         char **iname;
1156
1157         if (interpreter == NULL)
1158                 return NULL;
1159         for (lang = lang_names; lang->name != NULL; lang++)
1160                 if (lang->interpreters != NULL)
1161                         for (iname = lang->interpreters; *iname != NULL;
1162                              iname++)
1163                                 if (streq(*iname, interpreter))
1164                                         return lang;
1165
1166         return NULL;
1167 }
1168
1169 /*
1170  * Return a language given the file suffix.
1171  */
1172 language *get_language_from_suffix(suffix)
1173 char *suffix;
1174 {
1175         language *lang;
1176         char **ext;
1177
1178         if (suffix == NULL)
1179                 return NULL;
1180         for (lang = lang_names; lang->name != NULL; lang++)
1181                 if (lang->suffixes != NULL)
1182                         for (ext = lang->suffixes; *ext != NULL; ext++)
1183                                 if (streq(*ext, suffix))
1184                                         return lang;
1185
1186         return NULL;
1187 }
1188
1189 /*
1190  * This routine is called on each file argument.
1191  */
1192 void process_file(file)
1193 char *file;
1194 {
1195         struct stat stat_buf;
1196         FILE *inf;
1197
1198         canonicalize_filename(file);
1199         if (stat(file, &stat_buf) == 0 && !S_ISREG(stat_buf.st_mode)) {
1200                 error("skipping %s: it is not a regular file.", file);
1201                 return;
1202         }
1203         if (streq(file, tagfile) && !streq(tagfile, "-")) {
1204                 error("skipping inclusion of %s in self.", file);
1205                 return;
1206         }
1207         inf = fopen(file, "r");
1208         if (inf == NULL) {
1209                 perror(file);
1210                 return;
1211         }
1212
1213         find_entries(file, inf);
1214
1215         if (!CTAGS) {
1216                 char *filename;
1217
1218                 if (filename_is_absolute(file)) {
1219                         /* file is an absolute file name.  Canonicalise it. */
1220                         filename = absolute_filename(file, cwd);
1221                 } else {
1222                         /* file is a file name relative to cwd.  Make it relative
1223                            to the directory of the tags file. */
1224                         filename = relative_filename(file, tagfiledir);
1225                 }
1226 #ifdef OO_BROWSER
1227                 if (oo_browser_format)
1228                         fprintf(tagf, "\f\n%s\n", filename);
1229                 else
1230 #endif
1231                         fprintf(tagf, "\f\n%s,%d\n", filename,
1232                                 total_size_of_entries(head));
1233                 free(filename);
1234                 put_entries(head);
1235                 free_tree(head);
1236                 head = NULL;
1237         }
1238 }
1239
1240 /*
1241  * This routine sets up the boolean pseudo-functions which work
1242  * by setting boolean flags dependent upon the corresponding character.
1243  * Every char which is NOT in that string is not a white char.  Therefore,
1244  * all of the array "_wht" is set to FALSE, and then the elements
1245  * subscripted by the chars in "white" are set to TRUE.  Thus "_wht"
1246  * of a char is TRUE if it is the string "white", else FALSE.
1247  */
1248 void init()
1249 {
1250         register char *sp;
1251         register int i;
1252
1253         for (i = 0; i < CHARS; i++)
1254                 iswhite(i) = notinname(i) = begtoken(i) = intoken(i) =
1255                     endtoken(i) = FALSE;
1256         for (sp = white; *sp != '\0'; sp++)
1257                 iswhite(*sp) = TRUE;
1258         for (sp = nonam; *sp != '\0'; sp++)
1259                 notinname(*sp) = TRUE;
1260         for (sp = begtk; *sp != '\0'; sp++)
1261                 begtoken(*sp) = TRUE;
1262         for (sp = midtk; *sp != '\0'; sp++)
1263                 intoken(*sp) = TRUE;
1264         for (sp = endtk; *sp != '\0'; sp++)
1265                 endtoken(*sp) = TRUE;
1266         iswhite('\0') = iswhite('\n');
1267         notinname('\0') = notinname('\n');
1268         begtoken('\0') = begtoken('\n');
1269         intoken('\0') = intoken('\n');
1270         endtoken('\0') = endtoken('\n');
1271 }
1272
1273 /*
1274  * This routine opens the specified file and calls the function
1275  * which finds the function and type definitions.
1276  */
1277 node *last_node = NULL;
1278
1279 void find_entries(file, inf)
1280 char *file;
1281 FILE *inf;
1282 {
1283         char *cp;
1284         language *lang;
1285         node *old_last_node;
1286
1287         curfile = savestr(file);
1288
1289         /* If user specified a language, use it. */
1290         lang = forced_lang;
1291         if (lang != NULL && lang->function != NULL) {
1292                 curlang = lang;
1293                 lang->function(inf);
1294                 free(curfile);
1295                 fclose(inf);
1296                 return;
1297         }
1298
1299         cp = etags_strrchr(file, '.');
1300         if (cp != NULL) {
1301                 cp += 1;
1302                 lang = get_language_from_suffix(cp);
1303                 if (lang != NULL && lang->function != NULL) {
1304                         curlang = lang;
1305                         lang->function(inf);
1306                         free(curfile);
1307                         fclose(inf);
1308                         return;
1309                 }
1310         }
1311
1312         /* Look for sharp-bang as the first two characters. */
1313         if (readline_internal(&lb, inf) > 0
1314             && lb.len >= 2 && lb.buffer[0] == '#' && lb.buffer[1] == '!') {
1315                 char *lp;
1316
1317                 /* Set lp to point at the first char after the last slash in the
1318                    line or, if no slashes, at the first nonblank.  Then set cp to
1319                    the first successive blank and terminate the string. */
1320                 lp = etags_strrchr(lb.buffer + 2, '/');
1321                 if (lp != NULL)
1322                         lp += 1;
1323                 else
1324                         lp = skip_spaces(lb.buffer + 2);
1325                 cp = skip_non_spaces(lp);
1326                 *cp = '\0';
1327
1328                 if (strlen(lp) > 0) {
1329                         lang = get_language_from_interpreter(lp);
1330                         if (lang != NULL && lang->function != NULL) {
1331                                 curlang = lang;
1332                                 lang->function(inf);
1333                                 fclose(inf);
1334                                 free(curfile);
1335                                 return;
1336                         }
1337                 }
1338         }
1339         rewind(inf);
1340
1341         /* Try Fortran. */
1342         old_last_node = last_node;
1343         curlang = get_language_from_name("fortran");
1344         Fortran_functions(inf);
1345
1346         /* No Fortran entries found.  Try C. */
1347         if (old_last_node == last_node) {
1348                 rewind(inf);
1349                 curlang = get_language_from_name(cplusplus ? "c++" : "c");
1350                 default_C_entries(inf);
1351         }
1352         free(curfile);
1353         fclose(inf);
1354         return;
1355 }
1356 \f
1357 /* Record a tag. */
1358 void pfnote(name, is_func, linestart, linelen, lno, cno)
1359 char *name;                     /* tag name, or NULL if unnamed */
1360 bool is_func;                   /* tag is a function */
1361 char *linestart;                /* start of the line where tag is */
1362 int linelen;                    /* length of the line where tag is */
1363 int lno;                        /* line number */
1364 long cno;                       /* character number */
1365 {
1366         register node *np;
1367
1368         if (CTAGS && name == NULL)
1369                 return;
1370
1371         np = xnew(1, node);
1372
1373         /* If ctags mode, change name "main" to M<thisfilename>. */
1374         if (CTAGS && !cxref_style && streq(name, "main")) {
1375                 register char *fp = etags_strrchr(curfile, '/');
1376                 np->name = concat("M", fp == 0 ? curfile : fp + 1, "");
1377                 fp = etags_strrchr(np->name, '.');
1378                 if (fp && fp[1] != '\0' && fp[2] == '\0')
1379                         fp[0] = 0;
1380         } else
1381                 np->name = name;
1382         np->been_warned = FALSE;
1383         np->file = curfile;
1384         np->is_func = is_func;
1385         np->lno = lno;
1386         /* Our char numbers are 0-base, because of C language tradition?
1387            ctags compatibility?  old versions compatibility?   I don't know.
1388            Anyway, since emacs's are 1-base we expect etags.el to take care
1389            of the difference.  If we wanted to have 1-based numbers, we would
1390            uncomment the +1 below. */
1391         np->cno = cno /* + 1 */ ;
1392         np->left = np->right = NULL;
1393         if (CTAGS && !cxref_style) {
1394                 if (strlen(linestart) < 50)
1395                         np->pat = concat(linestart, "$", "");
1396                 else
1397                         np->pat = savenstr(linestart, 50);
1398         } else
1399                 np->pat = savenstr(linestart, linelen);
1400
1401 #ifdef OO_BROWSER
1402         if (oo_browser_format)
1403                 np->construct = oo_browser_construct;
1404         oo_browser_construct = C_NULL;
1405         oo_browser_check_and_clear_structtype();
1406 #endif
1407
1408         add_node(np, &head);
1409 }
1410
1411 /* Date: Wed, 22 Jan 1997 02:56:31 -0500 [last amended 18 Sep 1997]
1412  * From: Sam Kendall <kendall@mv.mv.com>
1413  * Subject: Proposal for firming up the TAGS format specification
1414  * To: F.Potorti@cnuce.cnr.it
1415  *
1416  * pfnote should emit the optimized form [unnamed tag] only if:
1417  *  1. name does not contain any of the characters " \t\r\n(),;";
1418  *  2. linestart contains name as either a rightmost, or rightmost but
1419  *     one character, substring;
1420  *  3. the character, if any, immediately before name in linestart must
1421  *     be one of the characters " \t(),;";
1422  *  4. the character, if any, immediately after name in linestart must
1423  *     also be one of the characters " \t(),;".
1424  *
1425  * The real implementation uses the notinname() macro, which recognises
1426  * characters slightly different form " \t\r\n(),;".  See the variable
1427  * `nonam'.
1428  */
1429 #define traditional_tag_style TRUE
1430 void new_pfnote(name, namelen, is_func, linestart, linelen, lno, cno)
1431 char *name;                     /* tag name, or NULL if unnamed */
1432 int namelen;                    /* tag length */
1433 bool is_func;                   /* tag is a function */
1434 char *linestart;                /* start of the line where tag is */
1435 int linelen;                    /* length of the line where tag is */
1436 int lno;                        /* line number */
1437 long cno;                       /* character number */
1438 {
1439         register char *cp;
1440         bool named;
1441
1442         named = TRUE;
1443         if (!CTAGS) {
1444                 for (cp = name; !notinname(*cp); cp++)
1445                         continue;
1446                 if (*cp == '\0') {      /* rule #1 */
1447                         cp = linestart + linelen - namelen;
1448                         if (notinname(linestart[linelen - 1]))
1449                                 cp -= 1;        /* rule #4 */
1450 #ifdef OO_BROWSER
1451                         if (!oo_browser_format && cp >= linestart       /* rule #2 */
1452 #else
1453                         if (cp >= linestart     /* rule #2 */
1454 #endif
1455                             && (cp == linestart || notinname(cp[-1]))   /* rule #3 */
1456                             &&strneq(name, cp, namelen))        /* rule #2 */
1457                                 named = FALSE;  /* use unnamed tag */
1458                 }
1459         }
1460
1461         if (named)
1462                 name = savenstr(name, namelen);
1463         else
1464                 name = NULL;
1465         pfnote(name, is_func, linestart, linelen, lno, cno);
1466 }
1467
1468 /*
1469  * free_tree ()
1470  *      recurse on left children, iterate on right children.
1471  */
1472 void free_tree(np)
1473 register node *np;
1474 {
1475         while (np) {
1476                 register node *node_right = np->right;
1477                 free_tree(np->left);
1478                 if (np->name != NULL)
1479                         free(np->name);
1480                 free(np->pat);
1481                 free(np);
1482                 np = node_right;
1483         }
1484 }
1485
1486 /*
1487  * add_node ()
1488  *      Adds a node to the tree of nodes.  In etags mode, we don't keep
1489  *      it sorted; we just keep a linear list.  In ctags mode, maintain
1490  *      an ordered tree, with no attempt at balancing.
1491  *
1492  *      add_node is the only function allowed to add nodes, so it can
1493  *      maintain state.
1494  */
1495 void add_node(np, cur_node_p)
1496 node *np, **cur_node_p;
1497 {
1498         register int dif;
1499         register node *cur_node = *cur_node_p;
1500
1501         if (cur_node == NULL) {
1502                 *cur_node_p = np;
1503                 last_node = np;
1504                 return;
1505         }
1506
1507         if (!CTAGS) {
1508                 /* Etags Mode */
1509                 if (last_node == NULL)
1510                         fatal("internal error in add_node", (char *)NULL);
1511                 last_node->right = np;
1512                 last_node = np;
1513         } else {
1514                 /* Ctags Mode */
1515                 dif = strcmp(np->name, cur_node->name);
1516
1517                 /*
1518                  * If this tag name matches an existing one, then
1519                  * do not add the node, but maybe print a warning.
1520                  */
1521                 if (!dif) {
1522                         if (streq(np->file, cur_node->file)) {
1523                                 if (!no_warnings) {
1524                                         fprintf(stderr,
1525                                                 "Duplicate entry in file %s, line %d: %s\n",
1526                                                 np->file, lineno, np->name);
1527                                         fprintf(stderr,
1528                                                 "Second entry ignored\n");
1529                                 }
1530                         } else if (!cur_node->been_warned && !no_warnings) {
1531                                 fprintf
1532                                     (stderr,
1533                                      "Duplicate entry in files %s and %s: %s (Warning only)\n",
1534                                      np->file, cur_node->file, np->name);
1535                                 cur_node->been_warned = TRUE;
1536                         }
1537                         return;
1538                 }
1539
1540                 /* Actually add the node */
1541                 add_node(np, dif < 0 ? &cur_node->left : &cur_node->right);
1542         }
1543 }
1544 \f
1545 #ifdef OO_BROWSER
1546 /* Default class name for the current OO-Browser tag. */
1547 static char *oo_browser_class;
1548 /* Prefix character to use in OO-Browser listings for the current tag. */
1549 static char oo_browser_prefix;
1550 #endif
1551
1552 void put_entries(node * np)
1553 {
1554         register char *sp;
1555
1556         if (np == NULL)
1557                 return;
1558
1559         /* Output subentries that precede this one */
1560         put_entries(np->left);
1561
1562         /* Output this entry */
1563
1564         if (!CTAGS) {
1565 #ifdef OO_BROWSER
1566                 if (oo_browser_format) {
1567                         /* Omit C++ `class' and `method' entries as well as Objective-C
1568                            entries from this OO-Browser tags file since the browser handles
1569                            them independently of this file.  Omit `extern' variable declarations
1570                            as they are unused by the OO-Browser. */
1571                         if (np->construct != C_CLASS
1572                             && np->construct != C_METHOD
1573                             && np->construct != C_EXTERN
1574                             && np->construct != C_OBJC) {
1575                                 oo_browser_class =
1576                                     oo_browser_default_classes[np->construct];
1577                                 switch (np->construct) {
1578                                 case C_CONSTANT:
1579                                 case C_ENUMERATION:
1580                                 case C_ENUM_LABEL:
1581                                 case C_STRUCTURE:
1582                                 case C_TYPE:
1583                                 case C_UNION:
1584                                 case C_VARIABLE:
1585                                         oo_browser_prefix = '=';
1586                                         break;
1587                                 case C_FUNCTION:
1588                                 case C_MACRO:
1589                                         oo_browser_prefix = '-';
1590                                         break;
1591                                 default:
1592                                         break;
1593                                 }
1594                                 if (np->name != NULL)
1595                                         fprintf(tagf, "%s@%c %s@%s\n",
1596                                                 oo_browser_class,
1597                                                 oo_browser_prefix, np->name,
1598                                                 np->pat);
1599                                 else
1600                                         fprintf(tagf, "%s@%c ???@%s\n",
1601                                                 oo_browser_class,
1602                                                 oo_browser_prefix, np->pat);
1603                         }
1604                 } else {
1605 #endif
1606                         if (np->name != NULL)
1607                                 fprintf(tagf, "%s\177%s\001%d,%ld\n",
1608                                         np->pat, np->name, np->lno, np->cno);
1609                         else
1610                                 fprintf(tagf, "%s\177%d,%ld\n",
1611                                         np->pat, np->lno, np->cno);
1612 #ifdef OO_BROWSER
1613                 }
1614 #endif
1615         } else {
1616                 if (np->name == NULL)
1617                         error("internal error: NULL name in ctags mode.",
1618                               (char *)NULL);
1619
1620                 if (cxref_style) {
1621                         if (vgrind_style)
1622                                 fprintf(stdout, "%s %s %d\n",
1623                                         np->name, np->file,
1624                                         (np->lno + 63) / 64);
1625                         else
1626                                 fprintf(stdout, "%-16s %3d %-16s %s\n",
1627                                         np->name, np->lno, np->file, np->pat);
1628                 } else {
1629                         fprintf(tagf, "%s\t%s\t", np->name, np->file);
1630
1631                         if (np->is_func) {      /* a function */
1632                                 putc(searchar, tagf);
1633                                 putc('^', tagf);
1634
1635                                 for (sp = np->pat; *sp; sp++) {
1636                                         if (*sp == '\\' || *sp == searchar)
1637                                                 putc('\\', tagf);
1638                                         putc(*sp, tagf);
1639                                 }
1640                                 putc(searchar, tagf);
1641                         } else {        /* a typedef; text pattern inadequate */
1642                                 fprintf(tagf, "%d", np->lno);
1643                         }
1644                         putc('\n', tagf);
1645                 }
1646         }
1647
1648         /* Output subentries that follow this one */
1649         put_entries(np->right);
1650 }
1651
1652 /* Length of a number's decimal representation. */
1653 int number_len PP((long num));
1654 int number_len(num)
1655 long num;
1656 {
1657         int len = 1;
1658         while ((num /= 10) > 0)
1659                 len += 1;
1660         return len;
1661 }
1662
1663 /*
1664  * Return total number of characters that put_entries will output for
1665  * the nodes in the subtree of the specified node.  Works only if
1666  * we are not ctags, but called only in that case.  This count
1667  * is irrelevant with the new tags.el, but is still supplied for
1668  * backward compatibility.
1669  */
1670 int total_size_of_entries(np)
1671 register node *np;
1672 {
1673         register int total;
1674
1675         if (np == NULL)
1676                 return 0;
1677
1678         for (total = 0; np != NULL; np = np->right) {
1679                 /* Count left subentries. */
1680                 total += total_size_of_entries(np->left);
1681
1682                 /* Count this entry */
1683                 total += strlen(np->pat) + 1;
1684                 total +=
1685                     number_len((long)np->lno) + 1 + number_len(np->cno) + 1;
1686                 if (np->name != NULL)
1687                         total += 1 + strlen(np->name);  /* \001name */
1688         }
1689
1690         return total;
1691 }
1692 \f
1693 /*
1694  * The C symbol tables.
1695  */
1696 enum sym_type {
1697         st_none,
1698         st_C_objprot, st_C_objimpl, st_C_objend,
1699         st_C_gnumacro,
1700         st_C_ignore,
1701         st_C_javastruct,
1702         st_C_struct, st_C_enum, st_C_define, st_C_typedef, st_C_typespec,
1703         st_C_const
1704 #ifdef OO_BROWSER
1705             , st_C_union, st_C_class, st_C_extern, st_C_inline
1706 #endif
1707 };
1708
1709 /* Feed stuff between (but not including) %[ and %] lines to:
1710       gperf -c -k 1,3 -o -p -r -t
1711 %[
1712 struct C_stab_entry { char *name; int c_ext; enum sym_type type; }
1713 %%
1714 @interface,     0,      st_C_objprot
1715 @protocol,      0,      st_C_objprot
1716 @implementation,0,      st_C_objimpl
1717 @end,           0,      st_C_objend
1718 import,         C_JAVA, st_C_ignore
1719 package,        C_JAVA, st_C_ignore
1720 friend,         C_PLPL, st_C_ignore
1721 extends,        C_JAVA, st_C_javastruct
1722 implements,     C_JAVA, st_C_javastruct
1723 interface,      C_JAVA, st_C_struct
1724 class,          C_PLPL, st_C_class
1725 namespace,      C_PLPL, st_C_struct
1726 domain,         C_STAR, st_C_struct
1727 union,          0,      st_C_union
1728 struct,         0,      st_C_struct
1729 enum,           0,      st_C_enum
1730 typedef,        0,      st_C_typedef
1731 define,         0,      st_C_define
1732 inline,         0,      st_C_inline
1733 bool,           C_PLPL, st_C_typespec
1734 long,           0,      st_C_typespec
1735 short,          0,      st_C_typespec
1736 int,            0,      st_C_typespec
1737 char,           0,      st_C_typespec
1738 float,          0,      st_C_typespec
1739 double,         0,      st_C_typespec
1740 signed,         0,      st_C_typespec
1741 unsigned,       0,      st_C_typespec
1742 auto,           0,      st_C_typespec
1743 void,           0,      st_C_typespec
1744 extern,         0,      st_C_extern
1745 static,         0,      st_C_typespec
1746 const,          0,      st_C_const
1747 volatile,       0,      st_C_typespec
1748 explicit,       C_PLPL, st_C_typespec
1749 mutable,        C_PLPL, st_C_typespec
1750 typename,       C_PLPL, st_C_typespec
1751 # DEFUN used in emacs, the next three used in glibc (SYSCALL only for mach).
1752 DEFUN,          0,      st_C_gnumacro
1753 SYSCALL,        0,      st_C_gnumacro
1754 ENTRY,          0,      st_C_gnumacro
1755 PSEUDO,         0,      st_C_gnumacro
1756 # These are defined inside C functions, so currently they are not met.
1757 # EXFUN used in glibc, DEFVAR_* in emacs.
1758 #EXFUN,         0,      st_C_gnumacro
1759 #DEFVAR_,       0,      st_C_gnumacro
1760 %]
1761 and replace lines between %< and %> with its output. */
1762 /*%<*/
1763 /* C code produced by gperf version 2.5 (GNU C++ version) */
1764 /* Command-line: gperf -c -k 1,3 -o -p -r -t  */
1765 struct C_stab_entry {
1766         char *name;
1767         int c_ext;
1768         enum sym_type type;
1769 };
1770
1771 #define TOTAL_KEYWORDS 41
1772 #define MIN_WORD_LENGTH 3
1773 #define MAX_WORD_LENGTH 15
1774 #define MIN_HASH_VALUE 13
1775 #define MAX_HASH_VALUE 129
1776 /* maximum key range = 117, duplicates = 0 */
1777
1778 static unsigned int hash(char *str, unsigned int len)
1779 {
1780         static unsigned char asso_values[] = {
1781                 130, 130, 130, 130, 130, 130, 130, 130, 130, 130,
1782                 130, 130, 130, 130, 130, 130, 130, 130, 130, 130,
1783                 130, 130, 130, 130, 130, 130, 130, 130, 130, 130,
1784                 130, 130, 130, 130, 130, 130, 130, 130, 130, 130,
1785                 130, 130, 130, 130, 130, 130, 130, 130, 130, 130,
1786                 130, 130, 130, 130, 130, 130, 130, 130, 130, 130,
1787                 130, 130, 130, 130, 13, 130, 130, 130, 33, 32,
1788                 47, 130, 130, 130, 130, 130, 130, 130, 130, 130,
1789                 5, 130, 130, 20, 32, 130, 130, 130, 130, 130,
1790                 130, 130, 130, 130, 130, 130, 130, 47, 55, 8,
1791                 15, 33, 61, 38, 130, 60, 130, 130, 2, 9,
1792                 10, 62, 59, 130, 28, 27, 50, 19, 3, 130,
1793                 130, 130, 130, 130, 130, 130, 130, 130,
1794         };
1795         return len +
1796             asso_values[(unsigned char)str[2]] +
1797             asso_values[(unsigned char)str[0]];
1798 }
1799
1800 static struct C_stab_entry *in_word_set(char *str, unsigned int len)
1801 {
1802         static struct C_stab_entry wordlist[] = {
1803                 {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",},
1804                 {"",}, {"",}, {"",}, {"",},
1805                 {"volatile", 0, st_C_typespec},
1806                 {"",}, {"",},
1807                 {"long", 0, st_C_typespec},
1808                 {"",}, {"",}, {"",}, {"",}, {"",}, {"",},
1809                 {"const", 0, st_C_const},
1810                 {"",}, {"",}, {"",},
1811                 {"@end", 0, st_C_objend},
1812                 {"namespace", C_PLPL, st_C_struct},
1813                 {"",},
1814                 {"domain", C_STAR, st_C_struct},
1815                 {"",}, {"",},
1816                 {"@interface", 0, st_C_objprot},
1817                 {"",}, {"",}, {"",},
1818                 {"@implementation", 0, st_C_objimpl},
1819                 {"",}, {"",},
1820                 {"double", 0, st_C_typespec},
1821                 {"",}, {"",},
1822                 {"PSEUDO", 0, st_C_gnumacro},
1823                 {"",}, {"",}, {"",},
1824                 {"SYSCALL", 0, st_C_gnumacro},
1825                 {"",}, {"",},
1826                 {"@protocol", 0, st_C_objprot},
1827                 {"",}, {"",}, {"",},
1828                 {"unsigned", 0, st_C_typespec},
1829                 {"",},
1830                 {"enum", 0, st_C_enum},
1831                 {"",}, {"",},
1832                 {"char", 0, st_C_typespec},
1833                 {"class", C_PLPL, st_C_class},
1834                 {"struct", 0, st_C_struct},
1835                 {"",}, {"",}, {"",}, {"",},
1836                 {"mutable", C_PLPL, st_C_typespec},
1837                 {"void", 0, st_C_typespec},
1838                 {"inline", 0, st_C_inline},
1839                 {"ENTRY", 0, st_C_gnumacro},
1840                 {"",},
1841                 {"signed", 0, st_C_typespec},
1842                 {"",}, {"",},
1843                 {"package", C_JAVA, st_C_ignore},
1844                 {"",}, {"",}, {"",}, {"",}, {"",},
1845                 {"static", 0, st_C_typespec},
1846                 {"",},
1847                 {"define", 0, st_C_define},
1848                 {"",},
1849                 {"union", 0, st_C_union},
1850                 {"DEFUN", 0, st_C_gnumacro},
1851                 {"",}, {"",}, {"",},
1852                 {"extern", 0, st_C_extern},
1853                 {"extends", C_JAVA, st_C_javastruct},
1854                 {"",}, {"",}, {"",},
1855                 {"short", 0, st_C_typespec},
1856                 {"",}, {"",}, {"",}, {"",}, {"",},
1857                 {"explicit", C_PLPL, st_C_typespec},
1858                 {"auto", 0, st_C_typespec},
1859                 {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",},
1860                 {"",}, {"",},
1861                 {"int", 0, st_C_typespec},
1862                 {"",}, {"",},
1863                 {"typedef", 0, st_C_typedef},
1864                 {"typename", C_PLPL, st_C_typespec},
1865                 {"",},
1866                 {"interface", C_JAVA, st_C_struct},
1867                 {"",},
1868                 {"bool", C_PLPL, st_C_typespec},
1869                 {"",}, {"",}, {"",},
1870                 {"import", C_JAVA, st_C_ignore},
1871                 {"",},
1872                 {"friend", C_PLPL, st_C_ignore},
1873                 {"float", 0, st_C_typespec},
1874                 {"implements", C_JAVA, st_C_javastruct},
1875         };
1876
1877         if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) {
1878                 register int key = hash(str, len);
1879
1880                 if (key <= MAX_HASH_VALUE && key >= 0) {
1881                         register char *s = wordlist[key].name;
1882
1883                         if (*s == *str && !strncmp(str + 1, s + 1, len - 1))
1884                                 return &wordlist[key];
1885                 }
1886         }
1887         return 0;
1888 }
1889
1890 /*%>*/
1891
1892 enum sym_type C_symtype PP((char *str, int len, int c_ext));
1893 enum sym_type C_symtype(str, len, c_ext)
1894 char *str;
1895 int len;
1896 int c_ext;
1897 {
1898         register struct C_stab_entry *se = in_word_set(str, len);
1899
1900         if (se == NULL || (se->c_ext && !(c_ext & se->c_ext)))
1901                 return st_none;
1902         return se->type;
1903 }
1904 \f
1905  /*
1906   * C functions and variables are recognized using a simple
1907   * finite automaton.  fvdef is its state variable.
1908   */
1909 enum {
1910         fvnone,                 /* nothing seen */
1911         fvnameseen,             /* function or variable name seen */
1912         fstartlist,             /* func: just after open parenthesis */
1913         finlist,                /* func: in parameter list */
1914         flistseen,              /* func: after parameter list */
1915         fignore,                /* func: before open brace */
1916         vignore                 /* var-like: ignore until ';' */
1917 } fvdef;
1918
1919  /*
1920   * typedefs are recognized using a simple finite automaton.
1921   * typdef is its state variable.
1922   */
1923 enum {
1924         tnone,                  /* nothing seen */
1925         ttypedseen,             /* typedef keyword seen */
1926         tinbody,                /* inside typedef body */
1927         tend,                   /* just before typedef tag */
1928         tignore                 /* junk after typedef tag */
1929 } typdef;
1930
1931  /*
1932   * struct-like structures (enum, struct and union) are recognized
1933   * using another simple finite automaton.  `structdef' is its state
1934   * variable.
1935   */
1936 enum {
1937         snone,                  /* nothing seen yet */
1938         skeyseen,               /* struct-like keyword seen */
1939         stagseen,               /* struct-like tag seen */
1940         scolonseen,             /* colon seen after struct-like tag */
1941         sinbody                 /* in struct body: recognize member func defs */
1942 } structdef;
1943
1944 /*
1945  * When structdef is stagseen, scolonseen, or sinbody, structtag is the
1946  * struct tag, and structtype is the type of the preceding struct-like
1947  * keyword.
1948  */
1949 char *structtag = "<uninited>";
1950 enum sym_type structtype;
1951
1952 #ifdef OO_BROWSER
1953 void oo_browser_check_and_clear_structtype(void)
1954 {
1955         /* Allow for multiple enum_label tags. */
1956         if (structtype != st_C_enum)
1957                 structtype = st_none;
1958 }
1959 #endif
1960
1961 /*
1962  * When objdef is different from onone, objtag is the name of the class.
1963  */
1964 char *objtag = "<uninited>";
1965
1966 /*
1967  * Yet another little state machine to deal with preprocessor lines.
1968  */
1969 enum {
1970         dnone,                  /* nothing seen */
1971         dsharpseen,             /* '#' seen as first char on line */
1972         ddefineseen,            /* '#' and 'define' seen */
1973         dignorerest             /* ignore rest of line */
1974 } definedef;
1975
1976 /*
1977  * State machine for Objective C protocols and implementations.
1978  * Tom R.Hageman <tom@basil.icce.rug.nl>
1979  */
1980 enum {
1981         onone,                  /* nothing seen */
1982         oprotocol,              /* @interface or @protocol seen */
1983         oimplementation,        /* @implementations seen */
1984         otagseen,               /* class name seen */
1985         oparenseen,             /* parenthesis before category seen */
1986         ocatseen,               /* category name seen */
1987         oinbody,                /* in @implementation body */
1988         omethodsign,            /* in @implementation body, after +/- */
1989         omethodtag,             /* after method name */
1990         omethodcolon,           /* after method colon */
1991         omethodparm,            /* after method parameter */
1992         oignore                 /* wait for @end */
1993 } objdef;
1994
1995 /*
1996  * Use this structure to keep info about the token read, and how it
1997  * should be tagged.  Used by the make_C_tag function to build a tag.
1998  */
1999 typedef struct {
2000         bool valid;
2001         char *str;
2002         bool named;
2003         int linelen;
2004         int lineno;
2005         long linepos;
2006         char *buffer;
2007 } token;
2008
2009 token tok;                      /* latest token read */
2010
2011 /*
2012  * Set this to TRUE, and the next token considered is called a function.
2013  * Used only for GNU emacs's function-defining macros.
2014  */
2015 bool next_token_is_func;
2016
2017 /*
2018  * TRUE in the rules part of a yacc file, FALSE outside (parse as C).
2019  */
2020 bool yacc_rules;
2021
2022 /*
2023  * methodlen is the length of the method name stored in token_name.
2024  */
2025 int methodlen;
2026
2027 #ifdef OO_BROWSER
2028 void oo_browser_clear_all_globals(void)
2029 {
2030         /* Initialize globals so there is no carry over between files. */
2031         oo_browser_construct = C_NULL;
2032         fvdef = fvnone;
2033         typdef = tnone;
2034         structdef = snone;
2035         definedef = dnone;
2036         objdef = onone;
2037         structtype = st_none;
2038         next_token_is_func = yacc_rules = FALSE;
2039 }
2040
2041 void oo_browser_clear_some_globals(void)
2042 {
2043         oo_browser_construct = C_NULL;
2044         structtype = st_none;
2045 }
2046 #endif
2047
2048 /*
2049  * consider_token ()
2050  *      checks to see if the current token is at the start of a
2051  *      function or variable, or corresponds to a typedef, or
2052  *      is a struct/union/enum tag, or #define, or an enum constant.
2053  *
2054  *      *IS_FUNC gets TRUE iff the token is a function or #define macro
2055  *      with args.  C_EXT is which language we are looking at.
2056  *
2057  *      In the future we will need some way to adjust where the end of
2058  *      the token is; for instance, implementing the C++ keyword
2059  *      `operator' properly will adjust the end of the token to be after
2060  *      whatever follows `operator'.
2061  *
2062  * Globals
2063  *      fvdef                   IN OUT
2064  *      structdef               IN OUT
2065  *      definedef               IN OUT
2066  *      typdef                  IN OUT
2067  *      objdef                  IN OUT
2068  *      next_token_is_func      IN OUT
2069  */
2070 bool consider_token PP((char *str, int len, int c, int c_ext,
2071                         int cblev, int parlev, bool * is_func_or_var));
2072 bool consider_token(str, len, c, c_ext, cblev, parlev, is_func_or_var)
2073 register char *str;             /* IN: token pointer */
2074 register int len;               /* IN: token length */
2075 register int c;                 /* IN: first char after the token */
2076 int c_ext;                      /* IN: C extensions mask */
2077 int cblev;                      /* IN: curly brace level */
2078 int parlev;                     /* IN: parenthesis level */
2079 bool *is_func_or_var;           /* OUT: function or variable found */
2080 {
2081         enum sym_type toktype = C_symtype(str, len, c_ext);
2082
2083 #ifdef OO_BROWSER
2084         switch ((unsigned int)toktype) {
2085         case st_C_struct:
2086                 set_construct(C_STRUCTURE);
2087                 break;
2088         case st_C_union:
2089                 set_construct(C_UNION);
2090                 break;
2091         case st_C_class:
2092                 set_construct(C_CLASS);
2093                 break;
2094         case st_C_enum:
2095                 set_construct(C_ENUMERATION);
2096                 break;
2097         case st_C_typedef:
2098                 set_construct(C_TYPE);
2099                 break;
2100         case st_C_extern:
2101                 set_construct(C_EXTERN);
2102                 break;
2103         case st_C_inline:
2104                 set_construct(C_FUNCTION);
2105                 break;
2106
2107                 /* all the rest */
2108         default:
2109                 break;
2110         }
2111 #endif
2112
2113         /*
2114          * Advance the definedef state machine.
2115          */
2116         switch (definedef) {
2117         case dnone:
2118                 /* We're not on a preprocessor line. */
2119                 break;
2120         case dsharpseen:
2121                 if (toktype == st_C_define) {
2122                         definedef = ddefineseen;
2123                 } else {
2124                         definedef = dignorerest;
2125                 }
2126                 return FALSE;
2127         case ddefineseen:
2128                 /*
2129                  * Make a tag for any macro, unless it is a constant
2130                  * and constantypedefs is FALSE.
2131                  */
2132                 definedef = dignorerest;
2133 #ifndef OO_BROWSER
2134                 *is_func_or_var = (c == '(');
2135 #else
2136                 {
2137                         char *p = str + len * sizeof(char);
2138
2139                         if (*p == '(')
2140                                 /* This must be a macro since there is no
2141                                    whitespace between the opening parenthesis
2142                                    and the definition name. */
2143                                 *is_func_or_var = TRUE;
2144                         else {
2145                                 *is_func_or_var = FALSE;
2146
2147                                 /* Handle possible whitespace between macro tag and opening
2148                                    parenthesis and ensure this is an actual macro.
2149                                    -- Bob Weiner, Altrasoft, 11/19/1997 */
2150                                 while (*p && isspace(*p))
2151                                         p++;
2152                                 if (*p)
2153                                         c = *p;
2154
2155                                 /* Skip over nested parentheses. */
2156                                 if (c == '(') {
2157                                         short depth = 1;
2158
2159                                         while (*++p && depth > 0 && *p != '\n') {
2160                                                 switch (*p) {
2161                                                 case '(':
2162                                                         depth++;
2163                                                         break;
2164                                                 case ')':
2165                                                         depth--;
2166                                                         break;
2167                                                 default:
2168                                                         break;
2169                                                 }
2170                                         }
2171
2172                                         /* If this is a macro, we have just passed
2173                                            the arguments and there will be more on
2174                                            the line before the NULL character that marks
2175                                            the end of the line token. */
2176                                         while (*p == ' ' || *p == '\t')
2177                                                 p++;
2178                                         if (*p)
2179                                                 *is_func_or_var = TRUE;
2180                                 }
2181                         }
2182                 }
2183
2184                 set_construct((*is_func_or_var) ? C_MACRO : C_CONSTANT);
2185 #endif
2186                 if (!*is_func_or_var && !constantypedefs)
2187                         return FALSE;
2188                 else
2189                         return TRUE;
2190         case dignorerest:
2191                 return FALSE;
2192         default:
2193                 error("internal error: definedef value.", (char *)NULL);
2194         }
2195
2196         /*
2197          * Now typedefs
2198          */
2199         switch (typdef) {
2200         case tnone:
2201                 if (toktype == st_C_typedef) {
2202                         if (typedefs)
2203                                 typdef = ttypedseen;
2204                         fvdef = fvnone;
2205                         return FALSE;
2206                 }
2207                 break;
2208         case ttypedseen:
2209                 switch ((unsigned int)toktype) {
2210                 case st_C_const:
2211                         set_construct(C_CONSTANT);
2212                         /* fall through */
2213                 case st_none:
2214                 case st_C_typespec:
2215 #ifdef OO_BROWSER
2216                 case st_C_extern:
2217 #endif
2218                         typdef = tend;
2219                         break;
2220                 case st_C_struct:
2221                 case st_C_enum:
2222 #ifdef OO_BROWSER
2223                 case st_C_union:
2224                 case st_C_class:
2225 #endif
2226                         break;
2227
2228                         /* all the rest */
2229                 default:
2230                         break;
2231                 }
2232                 /* Do not return here, so the structdef stuff has a chance. */
2233                 break;
2234         case tend:
2235                 switch ((unsigned int)toktype) {
2236                 case st_C_const:
2237                         set_construct(C_CONSTANT);
2238                         /* fall through */
2239                 case st_C_typespec:
2240                 case st_C_struct:
2241                 case st_C_enum:
2242 #ifdef OO_BROWSER
2243                 case st_C_extern:
2244                 case st_C_union:
2245                 case st_C_class:
2246 #endif
2247                         return FALSE;
2248
2249                         /* all the rest */
2250                 default:
2251                         break;
2252                 }
2253                 return TRUE;
2254
2255                 /* all the rest */
2256         case tinbody:
2257         case tignore:
2258         default:
2259                 break;
2260         }
2261
2262         /*
2263          * This structdef business is currently only invoked when cblev==0.
2264          * It should be recursively invoked whatever the curly brace level,
2265          * and a stack of states kept, to allow for definitions of structs
2266          * within structs.
2267          *
2268          * This structdef business is NOT invoked when we are ctags and the
2269          * file is plain C.  This is because a struct tag may have the same
2270          * name as another tag, and this loses with ctags.
2271          */
2272         switch ((unsigned int)toktype) {
2273         case st_C_javastruct:
2274                 if (structdef == stagseen)
2275                         structdef = scolonseen;
2276                 return FALSE;
2277         case st_C_struct:
2278         case st_C_enum:
2279 #ifdef OO_BROWSER
2280         case st_C_union:
2281         case st_C_class:
2282         case st_C_extern:
2283 #endif
2284                 if (typdef == ttypedseen
2285                     || (typedefs_and_cplusplus && cblev == 0
2286                         && structdef == snone)) {
2287                         structdef = skeyseen;
2288                         structtype = toktype;
2289                 }
2290                 return FALSE;
2291
2292                 /* all the rest */
2293         default:
2294                 break;
2295         }
2296
2297         if (structdef == skeyseen) {
2298                 /* Save the tag for struct/union/class, for functions and variables
2299                    that may be defined inside. */
2300 #ifndef OO_BROWSER
2301                 if (structtype == st_C_struct)
2302 #else
2303                 if (structtype == st_C_struct
2304                     || structtype == st_C_union || structtype == st_C_class)
2305 #endif
2306                         structtag = savenstr(str, len);
2307                 else
2308                         structtag = "<enum>";
2309                 structdef = stagseen;
2310                 return TRUE;
2311         }
2312
2313         /* Avoid entering fvdef stuff if typdef is going on. */
2314         if (typdef != tnone) {
2315                 definedef = dnone;
2316                 return FALSE;
2317         }
2318
2319         /* Detect GNU macros.
2320
2321            DEFUN note for writers of emacs C code:
2322            The DEFUN macro, used in emacs C source code, has a first arg
2323            that is a string (the lisp function name), and a second arg that
2324            is a C function name.  Since etags skips strings, the second arg
2325            is tagged.  This is unfortunate, as it would be better to tag the
2326            first arg.  The simplest way to deal with this problem would be
2327            to name the tag with a name built from the function name, by
2328            removing the initial 'F' character and substituting '-' for '_'.
2329            Anyway, this assumes that the conventions of naming lisp
2330            functions will never change.  Currently, this method is not
2331            implemented, so writers of emacs code are recommended to put the
2332            first two args of a DEFUN on the same line. */
2333         if (definedef == dnone && toktype == st_C_gnumacro) {
2334                 next_token_is_func = TRUE;
2335                 return FALSE;
2336         }
2337         if (next_token_is_func) {
2338                 next_token_is_func = FALSE;
2339                 fvdef = fignore;
2340                 *is_func_or_var = TRUE;
2341                 return TRUE;
2342         }
2343
2344         /* Detect Objective C constructs. */
2345         switch (objdef) {
2346         case onone:
2347                 switch ((unsigned int)toktype) {
2348                 case st_C_objprot:
2349 #ifdef OO_BROWSER
2350                         set_construct(C_OBJC);
2351 #endif
2352                         objdef = oprotocol;
2353                         return FALSE;
2354                 case st_C_objimpl:
2355 #ifdef OO_BROWSER
2356                         set_construct(C_OBJC);
2357 #endif
2358                         objdef = oimplementation;
2359                         return FALSE;
2360
2361                         /* all the rest */
2362                 default:
2363                         break;
2364                 }
2365                 break;
2366         case oimplementation:
2367                 /* Save the class tag for functions or variables defined inside. */
2368                 objtag = savenstr(str, len);
2369                 objdef = oinbody;
2370                 return FALSE;
2371         case oprotocol:
2372                 /* Save the class tag for categories. */
2373                 objtag = savenstr(str, len);
2374                 objdef = otagseen;
2375                 *is_func_or_var = TRUE;
2376                 return TRUE;
2377         case oparenseen:
2378                 objdef = ocatseen;
2379                 *is_func_or_var = TRUE;
2380                 return TRUE;
2381         case oinbody:
2382                 break;
2383         case omethodsign:
2384                 if (parlev == 0) {
2385                         objdef = omethodtag;
2386                         methodlen = len;
2387                         grow_linebuffer(&token_name, methodlen + 1);
2388                         strncpy(token_name.buffer, str, len);
2389                         token_name.buffer[methodlen] = '\0';
2390                         token_name.len = methodlen;
2391                         return TRUE;
2392                 }
2393                 return FALSE;
2394         case omethodcolon:
2395                 if (parlev == 0)
2396                         objdef = omethodparm;
2397                 return FALSE;
2398         case omethodparm:
2399                 if (parlev == 0) {
2400                         objdef = omethodtag;
2401                         methodlen += len;
2402                         grow_linebuffer(&token_name, methodlen + 1);
2403                         strncat(token_name.buffer, str, len);
2404                         token_name.len = methodlen;
2405                         return TRUE;
2406                 }
2407                 return FALSE;
2408         case oignore:
2409                 if (toktype == st_C_objend) {
2410                         /* Memory leakage here: the string pointed by objtag is
2411                            never released, because many tests would be needed to
2412                            avoid breaking on incorrect input code.  The amount of
2413                            memory leaked here is the sum of the lengths of the
2414                            class tags.
2415                            free (objtag); */
2416                         objdef = onone;
2417                 }
2418                 return FALSE;
2419
2420                 /* all the rest */
2421         case otagseen:
2422         case ocatseen:
2423         case omethodtag:
2424         default:
2425                 break;
2426         }
2427
2428         /* A function, variable or enum constant? */
2429         switch ((unsigned int)toktype) {
2430         case st_C_const:
2431                 set_construct(C_CONSTANT);
2432                 /* fall through */
2433         case st_C_typespec:
2434 #ifdef OO_BROWSER
2435         case st_C_extern:
2436 #endif
2437                 if (fvdef != finlist && fvdef != fignore && fvdef != vignore)
2438                         fvdef = fvnone; /* should be useless */
2439                 return FALSE;
2440         case st_C_ignore:
2441                 fvdef = vignore;
2442                 return FALSE;
2443         case st_none:
2444                 if (constantypedefs && structdef == sinbody
2445                     && structtype == st_C_enum)
2446 #ifdef OO_BROWSER
2447                 {
2448                         oo_browser_construct = C_ENUM_LABEL;
2449 #endif
2450                         return TRUE;
2451 #ifdef OO_BROWSER
2452                 }
2453 #endif
2454                 if (fvdef == fvnone) {
2455                         fvdef = fvnameseen;     /* function or variable */
2456                         *is_func_or_var = TRUE;
2457                         return TRUE;
2458                 }
2459
2460                 /* all the rest */
2461         default:
2462                 break;
2463         }
2464
2465         return FALSE;
2466 }
2467
2468 /*
2469  * C_entries ()
2470  *      This routine finds functions, variables, typedefs,
2471  *      #define's, enum constants and struct/union/enum definitions in
2472  *      #C syntax and adds them to the list.
2473  */
2474 #define current_lb_is_new (newndx == curndx)
2475 #define switch_line_buffers() (curndx = 1 - curndx)
2476
2477 #define curlb (lbs[curndx].lb)
2478 #define othlb (lbs[1-curndx].lb)
2479 #define newlb (lbs[newndx].lb)
2480 #define curlinepos (lbs[curndx].linepos)
2481 #define othlinepos (lbs[1-curndx].linepos)
2482 #define newlinepos (lbs[newndx].linepos)
2483
2484 #define CNL_SAVE_DEFINEDEF()                                            \
2485 do {                                                                    \
2486   curlinepos = charno;                                                  \
2487   lineno++;                                                             \
2488   linecharno = charno;                                                  \
2489   charno += readline (&curlb, inf);                                     \
2490   lp = curlb.buffer;                                                    \
2491   quotednl = FALSE;                                                     \
2492   newndx = curndx;                                                      \
2493 } while (0)
2494
2495 #define CNL()                                                           \
2496 do {                                                                    \
2497   CNL_SAVE_DEFINEDEF();                                                 \
2498   if (savetok.valid)                                                    \
2499     {                                                                   \
2500       tok = savetok;                                                    \
2501       savetok.valid = FALSE;                                            \
2502     }                                                                   \
2503   definedef = dnone;                                                    \
2504 } while (0)
2505
2506 void make_C_tag PP((bool isfun));
2507 void make_C_tag(isfun)
2508 bool isfun;
2509 {
2510         /* This function should never be called when tok.valid is FALSE, but
2511            we must protect against invalid input or internal errors. */
2512         if (tok.valid) {
2513                 if (traditional_tag_style) {
2514                         /* This was the original code.  Now we call new_pfnote instead,
2515                            which uses the new method for naming tags (see new_pfnote). */
2516                         char *name = NULL;
2517
2518                         if (CTAGS || tok.named)
2519                                 name = savestr(token_name.buffer);
2520                         pfnote(name, isfun,
2521                                tok.buffer, tok.linelen, tok.lineno,
2522                                tok.linepos);
2523                 } else
2524                         new_pfnote(token_name.buffer, token_name.len, isfun,
2525                                    tok.buffer, tok.linelen, tok.lineno,
2526                                    tok.linepos);
2527                 tok.valid = FALSE;
2528         } else if (DEBUG)
2529                 abort();
2530 }
2531
2532 void C_entries(c_ext, inf)
2533 int c_ext;                      /* extension of C */
2534 FILE *inf;                      /* input file */
2535 {
2536         register char c;        /* latest char read; '\0' for end of line */
2537         register char *lp;      /* pointer one beyond the character `c' */
2538         int curndx, newndx;     /* indices for current and new lb */
2539         register int tokoff;    /* offset in line of start of current token */
2540         register int toklen;    /* length of current token */
2541         char *qualifier;        /* string used to qualify names */
2542         int qlen;               /* length of qualifier */
2543         int cblev;              /* current curly brace level */
2544         int parlev;             /* current parenthesis level */
2545         bool incomm, inquote, inchar, quotednl, midtoken;
2546         bool cplpl, cjava;
2547         token savetok;          /* token saved during preprocessor handling */
2548
2549         /* initialise savetok */
2550         memset(&savetok, 0, sizeof(token));
2551
2552         tokoff = toklen = 0;    /* keep compiler quiet */
2553         curndx = newndx = 0;
2554         lineno = 0;
2555         charno = 0;
2556         lp = curlb.buffer;
2557         *lp = 0;
2558
2559         fvdef = fvnone;
2560         typdef = tnone;
2561         structdef = snone;
2562         definedef = dnone;
2563         objdef = onone;
2564         next_token_is_func = yacc_rules = FALSE;
2565         midtoken = inquote = inchar = incomm = quotednl = FALSE;
2566         tok.valid = savetok.valid = FALSE;
2567         cblev = 0;
2568         parlev = 0;
2569         cplpl = (c_ext & C_PLPL) == C_PLPL;
2570         cjava = (c_ext & C_JAVA) == C_JAVA;
2571         if (cjava) {
2572                 qualifier = ".";
2573                 qlen = 1;
2574         } else {
2575                 qualifier = "::";
2576                 qlen = 2;
2577         }
2578
2579         while (!feof(inf)) {
2580                 c = *lp++;
2581                 if (c == '\\') {
2582                         /* If we're at the end of the line, the next character is a
2583                            '\0'; don't skip it, because it's the thing that tells us
2584                            to read the next line.  */
2585                         if (*lp == '\0') {
2586                                 quotednl = TRUE;
2587                                 continue;
2588                         }
2589                         lp++;
2590                         c = ' ';
2591                 } else if (incomm) {
2592                         switch (c) {
2593                         case '*':
2594                                 if (*lp == '/') {
2595                                         c = *lp++;
2596                                         incomm = FALSE;
2597                                 }
2598                                 break;
2599                         case '\0':
2600                                 /* Newlines inside comments do not end macro definitions in
2601                                    traditional cpp. */
2602                                 CNL_SAVE_DEFINEDEF();
2603                                 break;
2604                         default:
2605                                 break;
2606                         }
2607                         continue;
2608                 } else if (inquote) {
2609                         switch (c) {
2610                         case '"':
2611                                 inquote = FALSE;
2612                                 break;
2613                         case '\0':
2614                                 /* Newlines inside strings do not end macro definitions
2615                                    in traditional cpp, even though compilers don't
2616                                    usually accept them. */
2617                                 CNL_SAVE_DEFINEDEF();
2618                                 break;
2619                         default:
2620                                 break;
2621                         }
2622                         continue;
2623                 } else if (inchar) {
2624                         switch (c) {
2625                         case '\0':
2626                                 /* Hmmm, something went wrong. */
2627                                 CNL();
2628                                 /* FALLTHRU */
2629                         case '\'':
2630                                 inchar = FALSE;
2631                                 break;
2632                         default:
2633                                 break;
2634                         }
2635                         continue;
2636                 } else
2637                         switch (c) {
2638                         case '"':
2639                                 inquote = TRUE;
2640                                 if (fvdef != finlist && fvdef != fignore
2641                                     && fvdef != vignore)
2642                                         fvdef = fvnone;
2643                                 continue;
2644                         case '\'':
2645                                 inchar = TRUE;
2646                                 if (fvdef != finlist && fvdef != fignore
2647                                     && fvdef != vignore)
2648                                         fvdef = fvnone;
2649                                 continue;
2650                         case '/':
2651                                 if (*lp == '*') {
2652                                         lp++;
2653                                         incomm = TRUE;
2654                                         continue;
2655                                 } else if ( /* cplpl && */ *lp == '/') {
2656                                         c = '\0';
2657                                         break;
2658                                 } else
2659                                         break;
2660                         case '%':
2661                                 if ((c_ext & YACC) && *lp == '%') {
2662                                         /* entering or exiting rules section in yacc file */
2663                                         lp++;
2664                                         definedef = dnone;
2665                                         fvdef = fvnone;
2666                                         typdef = tnone;
2667                                         structdef = snone;
2668                                         next_token_is_func = FALSE;
2669                                         midtoken = inquote = inchar = incomm =
2670                                             quotednl = FALSE;
2671                                         cblev = 0;
2672                                         yacc_rules = !yacc_rules;
2673                                         continue;
2674                                 } else
2675                                         break;
2676                         case '#':
2677                                 if (definedef == dnone) {
2678                                         char *cp;
2679                                         bool cpptoken = TRUE;
2680
2681                                         /* Look back on this line.  If all blanks, or nonblanks
2682                                            followed by an end of comment, this is a preprocessor
2683                                            token. */
2684                                         for (cp = newlb.buffer; cp < lp - 1;
2685                                              cp++)
2686                                                 if (!iswhite(*cp)) {
2687                                                         if (*cp == '*'
2688                                                             && *(cp + 1) ==
2689                                                             '/') {
2690                                                                 cp++;
2691                                                                 cpptoken = TRUE;
2692                                                         } else
2693                                                                 cpptoken =
2694                                                                     FALSE;
2695                                                 }
2696                                         if (cpptoken)
2697                                                 definedef = dsharpseen;
2698                                 }
2699                                 /* if (definedef == dnone) */
2700                                 continue;
2701                         default:
2702                                 break;
2703                         }       /* switch (c) */
2704
2705                 /* Consider token only if some complicated conditions are satisfied. */
2706                 if ((definedef != dnone
2707                      || (cblev == 0 && structdef != scolonseen)
2708                      || (cblev == 1 && cplpl && structdef == sinbody)
2709                      || (structdef == sinbody && structtype == st_C_enum))
2710                     && typdef != tignore
2711                     && definedef != dignorerest && fvdef != finlist) {
2712                         if (midtoken) {
2713                                 if (endtoken(c)) {
2714                                         if (c == ':' && cplpl && *lp == ':'
2715                                             && begtoken(*(lp + 1))) {
2716                                                 /*
2717                                                  * This handles :: in the middle, but not at the
2718                                                  * beginning of an identifier.
2719                                                  */
2720                                                 lp += 2;
2721                                                 toklen += 3;
2722 #ifdef OO_BROWSER
2723                                                 set_construct(C_METHOD);
2724 #endif
2725                                         } else {
2726                                                 bool funorvar = FALSE;
2727
2728                                                 if (yacc_rules
2729                                                     || consider_token(newlb.
2730                                                                       buffer +
2731                                                                       tokoff,
2732                                                                       toklen, c,
2733                                                                       c_ext,
2734                                                                       cblev,
2735                                                                       parlev,
2736                                                                       &funorvar))
2737                                                 {
2738                                                         tok.named = FALSE;
2739                                                         if (structdef == sinbody
2740                                                             && definedef ==
2741                                                             dnone && funorvar)
2742                                                                 /* function or var defined in C++ class body */
2743                                                         {
2744                                                                 int len =
2745                                                                     strlen
2746                                                                     (structtag)
2747                                                                     + qlen +
2748                                                                     toklen;
2749                                                                 grow_linebuffer
2750                                                                     (&token_name,
2751                                                                      len + 1);
2752                                                                 strcpy
2753                                                                     (token_name.
2754                                                                      buffer,
2755                                                                      structtag);
2756                                                                 strcat
2757                                                                     (token_name.
2758                                                                      buffer,
2759                                                                      qualifier);
2760                                                                 strncat
2761                                                                     (token_name.
2762                                                                      buffer,
2763                                                                      newlb.
2764                                                                      buffer +
2765                                                                      tokoff,
2766                                                                      toklen);
2767                                                                 token_name.len =
2768                                                                     len;
2769                                                                 tok.named =
2770                                                                     TRUE;
2771 #ifdef OO_BROWSER
2772                                                                 oo_browser_construct
2773                                                                     = C_METHOD;
2774 #endif
2775                                                         } else if (objdef ==
2776                                                                    ocatseen)
2777                                                                 /* Objective C category */
2778                                                         {
2779                                                                 int len =
2780                                                                     strlen
2781                                                                     (objtag) +
2782                                                                     2 + toklen;
2783                                                                 grow_linebuffer
2784                                                                     (&token_name,
2785                                                                      len + 1);
2786                                                                 strcpy
2787                                                                     (token_name.
2788                                                                      buffer,
2789                                                                      objtag);
2790                                                                 strcat
2791                                                                     (token_name.
2792                                                                      buffer,
2793                                                                      "(");
2794                                                                 strncat
2795                                                                     (token_name.
2796                                                                      buffer,
2797                                                                      newlb.
2798                                                                      buffer +
2799                                                                      tokoff,
2800                                                                      toklen);
2801                                                                 strcat
2802                                                                     (token_name.
2803                                                                      buffer,
2804                                                                      ")");
2805                                                                 token_name.len =
2806                                                                     len;
2807                                                                 tok.named =
2808                                                                     TRUE;
2809 #ifdef OO_BROWSER
2810                                                                 oo_browser_construct
2811                                                                     = C_OBJC;
2812 #endif
2813                                                         } else if (objdef ==
2814                                                                    omethodtag
2815                                                                    || objdef ==
2816                                                                    omethodparm)
2817                                                                 /* Objective C method */
2818                                                         {
2819                                                                 tok.named =
2820                                                                     TRUE;
2821 #ifdef OO_BROWSER
2822                                                                 oo_browser_construct
2823                                                                     = C_OBJC;
2824 #endif
2825                                                         } else {
2826                                                                 grow_linebuffer
2827                                                                     (&token_name,
2828                                                                      toklen +
2829                                                                      1);
2830                                                                 strncpy
2831                                                                     (token_name.
2832                                                                      buffer,
2833                                                                      newlb.
2834                                                                      buffer +
2835                                                                      tokoff,
2836                                                                      toklen);
2837                                                                 token_name.
2838                                                                     buffer
2839                                                                     [toklen] =
2840                                                                     '\0';
2841                                                                 token_name.len =
2842                                                                     toklen;
2843                                                                 /* Name macros. */
2844                                                                 tok.named
2845                                                                     =
2846                                                                     (structdef
2847                                                                      == stagseen
2848                                                                      || typdef
2849                                                                      == tend
2850 #ifdef OO_BROWSER
2851                                                                      /* Also name #define constants,
2852                                                                         enumerations and enum_labels.
2853                                                                         Conditionalize `funorvar' reference
2854                                                                         here or #defines will appear without
2855                                                                         their #names.
2856                                                                         -- Bob Weiner, Altrasoft, 4/25/1998 */
2857                                                                      ||
2858                                                                      ((oo_browser_format || funorvar)
2859                                                                       &&
2860                                                                       definedef
2861                                                                       ==
2862                                                                       dignorerest)
2863                                                                      ||
2864                                                                      (oo_browser_format
2865                                                                       &&
2866                                                                       (oo_browser_construct
2867                                                                        ==
2868                                                                        C_ENUMERATION
2869                                                                        ||
2870                                                                        oo_browser_construct
2871                                                                        ==
2872                                                                        C_ENUM_LABEL))
2873 #else
2874                                                                      ||
2875                                                                      (funorvar
2876                                                                       &&
2877                                                                       definedef
2878                                                                       ==
2879                                                                       dignorerest)
2880 #endif
2881                                                                     );
2882                                                         }
2883                                                         tok.lineno = lineno;
2884                                                         tok.linelen =
2885                                                             tokoff + toklen + 1;
2886                                                         tok.buffer =
2887                                                             newlb.buffer;
2888                                                         tok.linepos =
2889                                                             newlinepos;
2890                                                         tok.valid = TRUE;
2891
2892                                                         if (definedef == dnone
2893                                                             && (fvdef ==
2894                                                                 fvnameseen
2895                                                                 || structdef ==
2896                                                                 stagseen
2897                                                                 || typdef ==
2898                                                                 tend
2899                                                                 || objdef !=
2900                                                                 onone)) {
2901                                                                 if (current_lb_is_new)
2902                                                                         switch_line_buffers
2903                                                                             ();
2904                                                         } else
2905                                                                 make_C_tag
2906                                                                     (funorvar);
2907                                                 }
2908                                                 midtoken = FALSE;
2909                                         }
2910                                 } /* if (endtoken (c)) */
2911                                 else if (intoken(c)) {
2912                                         toklen++;
2913                                         continue;
2914                                 }
2915                         } /* if (midtoken) */
2916                         else if (begtoken(c)) {
2917                                 switch (definedef) {
2918                                 case dnone:
2919                                         switch ((unsigned int)fvdef) {
2920                                         case fstartlist:
2921                                                 fvdef = finlist;
2922                                                 continue;
2923                                         case flistseen:
2924 #ifdef OO_BROWSER
2925                                                 set_construct(C_MACRO);
2926 #endif
2927                                                 make_C_tag(TRUE);       /* a function */
2928                                                 fvdef = fignore;
2929                                                 break;
2930                                         case fvnameseen:
2931                                                 fvdef = fvnone;
2932                                                 break;
2933
2934                                                 /* all the rest */
2935                                         default:
2936                                                 break;
2937                                         }
2938                                         if (structdef == stagseen && !cjava)
2939                                                 structdef = snone;
2940                                         break;
2941                                 case dsharpseen:
2942                                         savetok = tok;
2943
2944                                         /* all the rest */
2945                                 case ddefineseen:
2946                                 case dignorerest:
2947                                 default:
2948                                         break;
2949                                 }
2950                                 if (!yacc_rules || lp == newlb.buffer + 1) {
2951                                         tokoff = lp - 1 - newlb.buffer;
2952                                         toklen = 1;
2953                                         midtoken = TRUE;
2954                                 }
2955                                 continue;
2956                         }       /* if (begtoken) */
2957                 }
2958
2959                 /* if must look at token */
2960                 /* Detect end of line, colon, comma, semicolon and various braces
2961                    after having handled a token. */
2962                 switch (c) {
2963                 case ':':
2964                         if (definedef != dnone)
2965                                 break;
2966                         switch ((unsigned int)objdef) {
2967                         case otagseen:
2968                                 objdef = oignore;
2969                                 make_C_tag(TRUE);       /* an Objective C class */
2970                                 break;
2971                         case omethodtag:
2972                         case omethodparm:
2973                                 objdef = omethodcolon;
2974                                 methodlen += 1;
2975                                 grow_linebuffer(&token_name, methodlen + 1);
2976                                 strcat(token_name.buffer, ":");
2977                                 token_name.len = methodlen;
2978                                 break;
2979
2980                                 /* all the rest */
2981                         default:
2982                                 break;
2983                         }
2984                         if (structdef == stagseen)
2985                                 structdef = scolonseen;
2986                         else
2987                                 switch ((unsigned int)fvdef) {
2988                                 case fvnameseen:
2989                                         if (yacc_rules) {
2990                                                 make_C_tag(FALSE);      /* a yacc function */
2991                                                 fvdef = fignore;
2992                                         }
2993                                         break;
2994                                 case fstartlist:
2995                                         fvdef = fvnone;
2996                                         break;
2997
2998                                         /* all the rest */
2999                                 default:
3000                                         break;
3001                                 }
3002                         break;
3003                 case ';':
3004                         if (definedef != dnone)
3005                                 break;
3006                         if (cblev == 0)
3007                                 switch ((unsigned int)typdef) {
3008                                 case tend:
3009 #ifdef OO_BROWSER
3010                                         set_construct(C_TYPE);
3011 #endif
3012                                         make_C_tag(FALSE);      /* a typedef */
3013                                         /* FALLTHRU */
3014                                 default:
3015                                         typdef = tnone;
3016                                 }
3017                         switch ((unsigned int)fvdef) {
3018                         case fignore:
3019                                 break;
3020                         case fvnameseen:
3021                                 if ((globals && cblev == 0)
3022                                     || (members && cblev == 1))
3023 #ifndef OO_BROWSER
3024                                         make_C_tag(FALSE);      /* a variable */
3025 #else
3026 /*            if (constantypedefs && structdef == snone)*/
3027                                 {
3028                                         tok.named = TRUE;
3029                                         switch ((unsigned int)structtype) {
3030                                         case st_C_enum:
3031                                                 set_construct(C_ENUMERATION);
3032                                                 break;
3033                                         case st_C_class:
3034                                                 set_construct(C_CLASS);
3035                                                 break;
3036                                         default:
3037                                                 set_construct(C_VARIABLE);
3038                                                 break;
3039                                         }
3040                                         make_C_tag(FALSE);
3041                                         /* Force reset of st_C_enum structtype value. */
3042                                         structtype = st_none;
3043                                 }
3044 #endif
3045                                 /* FALLTHRU */
3046                         default:
3047                                 fvdef = fvnone;
3048                                 /* The following instruction invalidates the token.
3049                                    Probably the token should be invalidated in all
3050                                    other cases  where some state machine is reset. */
3051                                 tok.valid = FALSE;
3052                         }
3053                         if (structdef == stagseen)
3054                                 structdef = snone;
3055                         break;
3056                 case ',':
3057                         if (definedef != dnone)
3058                                 break;
3059                         switch ((unsigned int)objdef) {
3060                         case omethodtag:
3061                         case omethodparm:
3062                                 make_C_tag(TRUE);       /* an Objective C method */
3063                                 objdef = oinbody;
3064                                 break;
3065
3066                                 /* all the rest */
3067                         default:
3068                                 break;
3069                         }
3070                         switch ((unsigned int)fvdef) {
3071                         case finlist:
3072                         case fignore:
3073                         case vignore:
3074                                 break;
3075                         case fvnameseen:
3076                                 if ((globals && cblev == 0)
3077                                     || (members && cblev == 1))
3078                                         make_C_tag(FALSE);      /* a variable */
3079                                 break;
3080                         default:
3081                                 fvdef = fvnone;
3082                         }
3083                         if (structdef == stagseen)
3084                                 structdef = snone;
3085                         break;
3086                 case '[':
3087                         if (definedef != dnone)
3088                                 break;
3089                         if (cblev == 0 && typdef == tend) {
3090 #ifdef OO_BROWSER
3091                                 set_construct(C_TYPE);
3092 #endif
3093                                 typdef = tignore;
3094                                 make_C_tag(FALSE);      /* a typedef */
3095                                 break;
3096                         }
3097                         switch ((unsigned int)fvdef) {
3098                         case finlist:
3099                         case fignore:
3100                         case vignore:
3101                                 break;
3102                         case fvnameseen:
3103 #ifndef OO_BROWSER
3104                                 if ((globals && cblev == 0)
3105                                     || (members && cblev == 1))
3106                                         make_C_tag(FALSE);      /* a variable */
3107 #else
3108                                 if (constantypedefs && structdef == snone) {
3109                                         tok.named = TRUE;
3110                                         switch ((unsigned int)structtype) {
3111                                         case st_C_enum:
3112                                                 set_construct(C_ENUMERATION);
3113                                                 break;
3114                                         case st_C_class:
3115                                                 set_construct(C_CLASS);
3116                                                 break;
3117                                         default:
3118                                                 set_construct(C_VARIABLE);
3119                                                 break;
3120                                         }
3121                                         make_C_tag(FALSE);
3122                                         /* Force reset of st_C_enum structtype value. */
3123                                         structtype = st_none;
3124                                 }
3125 #endif
3126                                 /* FALLTHRU */
3127                         case fvnone:
3128                         case fstartlist:
3129                         case flistseen:
3130                         default:
3131                                 fvdef = fvnone;
3132                         }
3133                         if (structdef == stagseen)
3134                                 structdef = snone;
3135                         break;
3136                 case '(':
3137                         if (definedef != dnone)
3138                                 break;
3139                         if (objdef == otagseen && parlev == 0)
3140                                 objdef = oparenseen;
3141                         switch ((unsigned int)fvdef) {
3142                         case fvnone:
3143                                 switch (typdef) {
3144                                 case ttypedseen:
3145                                 case tend:
3146                                         if (tok.valid && *lp != '*') {
3147                                                 /* This handles constructs like:
3148                                                    typedef void OperatorFun (int fun); */
3149                                                 typdef = tignore;
3150 #ifdef OO_BROWSER
3151                                                 set_construct(C_TYPE);
3152 #endif
3153                                                 make_C_tag(FALSE);
3154                                         }
3155                                         break;
3156
3157                                         /* all the rest */
3158                                 case tnone:
3159                                 case tinbody:
3160                                 case tignore:
3161                                 default:
3162                                         break;
3163                                 }       /* switch (typdef) */
3164                                 break;
3165                         case fvnameseen:
3166                                 fvdef = fstartlist;
3167                                 break;
3168                         case flistseen:
3169                                 fvdef = finlist;
3170                                 break;
3171
3172                                 /* all the rest */
3173                         default:
3174                                 break;
3175                         }
3176                         parlev++;
3177                         break;
3178                 case ')':
3179                         if (definedef != dnone)
3180                                 break;
3181                         if (objdef == ocatseen && parlev == 1) {
3182                                 make_C_tag(TRUE);       /* an Objective C category */
3183                                 objdef = oignore;
3184                         }
3185                         if (--parlev == 0) {
3186                                 switch ((unsigned int)fvdef) {
3187                                 case fstartlist:
3188                                 case finlist:
3189                                         fvdef = flistseen;
3190                                         break;
3191
3192                                         /* all the rest */
3193                                 default:
3194                                         break;
3195                                 }
3196                                 if (cblev == 0 && typdef == tend) {
3197 #ifdef OO_BROWSER
3198                                         set_construct(C_TYPE);
3199 #endif
3200                                         typdef = tignore;
3201                                         make_C_tag(FALSE);      /* a typedef */
3202                                 }
3203                         } else if (parlev < 0)  /* can happen due to ill-conceived #if's. */
3204                                 parlev = 0;
3205                         break;
3206                 case '{':
3207                         if (definedef != dnone)
3208                                 break;
3209                         if (typdef == ttypedseen)
3210                                 typdef = tinbody;
3211                         switch (structdef) {
3212                         case skeyseen:  /* unnamed struct */
3213                                 structdef = sinbody;
3214                                 structtag = "_anonymous_";
3215                                 break;
3216                         case stagseen:
3217                         case scolonseen:        /* named struct */
3218                                 structdef = sinbody;
3219                                 make_C_tag(FALSE);      /* a struct */
3220                                 break;
3221
3222                                 /* all the rest */
3223                         case snone:
3224                         case sinbody:
3225                         default:
3226                                 break;
3227                         }
3228                         switch ((unsigned int)fvdef) {
3229                         case flistseen:
3230 #ifdef OO_BROWSER
3231                                 set_construct(C_FUNCTION);
3232                                 /* Ensure function name is recorded.
3233                                    -- Bob Weiner, Altrasoft */
3234                                 tok.named = TRUE;
3235 #endif
3236                                 make_C_tag(TRUE);       /* a function */
3237                                 /* FALLTHRU */
3238                         case fignore:
3239                                 fvdef = fvnone;
3240                                 break;
3241                         case fvnone:
3242                                 switch ((unsigned int)objdef) {
3243                                 case otagseen:
3244                                         make_C_tag(TRUE);       /* an Objective C class */
3245                                         objdef = oignore;
3246                                         break;
3247                                 case omethodtag:
3248                                 case omethodparm:
3249                                         make_C_tag(TRUE);       /* an Objective C method */
3250                                         objdef = oinbody;
3251                                         break;
3252                                 default:
3253                                         /* Neutralize `extern "C" {' grot. */
3254                                         if (cblev == 0 && structdef == snone
3255                                             && typdef == tnone)
3256                                                 cblev = -1;
3257                                 }
3258
3259                                 /* all the rest */
3260                         default:
3261                                 break;
3262                         }
3263                         cblev++;
3264                         break;
3265                 case '*':
3266                         if (definedef != dnone)
3267                                 break;
3268                         if (fvdef == fstartlist)
3269                                 fvdef = fvnone; /* avoid tagging `foo' in `foo (*bar()) ()' */
3270                         break;
3271                 case '}':
3272                         if (definedef != dnone)
3273                                 break;
3274                         if (!noindentypedefs && lp == newlb.buffer + 1) {
3275                                 cblev = 0;      /* reset curly brace level if first column */
3276                                 parlev = 0;     /* also reset paren level, just in case... */
3277                         } else if (cblev > 0)
3278                                 cblev--;
3279                         if (cblev == 0) {
3280                                 if (typdef == tinbody)
3281                                         typdef = tend;
3282                                 /* Memory leakage here: the string pointed by structtag is
3283                                    never released, because I fear to miss something and
3284                                    break things while freeing the area.  The amount of
3285                                    memory leaked here is the sum of the lengths of the
3286                                    struct tags.
3287                                    if (structdef == sinbody)
3288                                    free (structtag); */
3289
3290                                 structdef = snone;
3291                                 structtag = "<error>";
3292 #ifdef OO_BROWSER
3293                                 /* Next line added to avoid any state carryover between
3294                                    functions. -- Bob Weiner, Altrasoft, 11/19/1997 */
3295                                 fvdef = fvnone;
3296                                 oo_browser_construct = C_NULL;
3297 #endif
3298                         }
3299                         break;
3300                 case '=':
3301                         if (definedef != dnone)
3302                                 break;
3303 #ifdef OO_BROWSER
3304                         {
3305                                 int is_method = 0;
3306 #endif
3307                                 switch (fvdef) {
3308                                 case finlist:
3309                                 case fignore:
3310                                 case vignore:
3311                                         break;
3312                                 case fvnameseen:
3313                                         if ((globals && cblev == 0)
3314                                             || (members && cblev == 1))
3315 #ifndef OO_BROWSER
3316                                                 make_C_tag(FALSE);      /* a variable */
3317 #else
3318                                         {
3319                                                 tok.named = TRUE;
3320                                                 switch ((unsigned int)structtype) {
3321                                                 case st_C_enum:
3322                                                         set_construct
3323                                                             (C_ENUMERATION);
3324                                                         break;
3325                                                 case st_C_class:
3326                                                         set_construct(C_CLASS);
3327                                                         break;
3328                                                 default:
3329                                                         /* a global variable */
3330                                                         set_construct
3331                                                             (C_VARIABLE);
3332                                                         break;
3333                                                 }
3334
3335                                                 /* ootags categorizes each tag found whereas etags doesn't.
3336                                                    Set the is_method flag if this tag has been marked as
3337                                                    such by an earlier section of code.
3338                                                    -- Steve Baur, Altrasoft, 5/7/1998 */
3339                                                 is_method =
3340                                                     (oo_browser_construct ==
3341                                                      C_METHOD);
3342
3343                                                 make_C_tag(FALSE);
3344                                                 /* Force reset of st_C_enum structtype value. */
3345                                                 structtype = st_none;
3346                                         }
3347 #endif
3348                                         /* FALLTHRU */
3349                                 case fvnone:
3350                                 case fstartlist:
3351                                 case flistseen:
3352                                 default:
3353 #ifdef OO_BROWSER
3354                                         fvdef = is_method ? fignore : vignore;
3355 #else
3356                                         fvdef = vignore;
3357 #endif
3358                                 }
3359 #ifdef OO_BROWSER
3360                         }
3361 #endif
3362                         break;
3363                 case '+':
3364                 case '-':
3365                         if (objdef == oinbody && cblev == 0) {
3366                                 objdef = omethodsign;
3367                                 break;
3368                         }
3369                         /* FALLTHRU */
3370                 case '#':
3371                 case '~':
3372                 case '&':
3373                 case '%':
3374                 case '/':
3375                 case '|':
3376                 case '^':
3377                 case '!':
3378                 case '<':
3379                 case '>':
3380                 case '.':
3381                 case '?':
3382                 case ']':
3383                         if (definedef != dnone)
3384                                 break;
3385 #ifdef OO_BROWSER
3386                         if (!cplpl) {
3387 #endif
3388                                 /* The above characters cannot follow a function tag in C, so
3389                                    unmark this as a function entry.  For C++, these characters
3390                                    may follow an `operator' function construct, so skip the
3391                                    unmarking conditional below.
3392                                    -- Steve Baur, Altrasoft, 5/7/1998 */
3393                                 if (fvdef != finlist && fvdef != fignore
3394                                     && fvdef != vignore)
3395                                         fvdef = fvnone;
3396 #ifdef OO_BROWSER
3397                         }
3398 #endif
3399                         break;
3400                 case '\0':
3401                         if (objdef == otagseen) {
3402                                 make_C_tag(TRUE);       /* an Objective C class */
3403                                 objdef = oignore;
3404                         }
3405                         /* If a macro spans multiple lines don't reset its state. */
3406                         if (quotednl)
3407                                 CNL_SAVE_DEFINEDEF();
3408                         else
3409                                 CNL();
3410                         break;
3411
3412                         /* all the rest */
3413                 default:
3414                         break;
3415                 }               /* switch (c) */
3416
3417         }                       /* while not eof */
3418 }
3419
3420 /*
3421  * Process either a C++ file or a C file depending on the setting
3422  * of a global flag.
3423  */
3424 void default_C_entries(inf)
3425 FILE *inf;
3426 {
3427         C_entries(cplusplus ? C_PLPL : 0, inf);
3428 }
3429
3430 /* Always do plain ANSI C. */
3431 void plain_C_entries(inf)
3432 FILE *inf;
3433 {
3434         C_entries(0, inf);
3435 }
3436
3437 /* Always do C++. */
3438 void Cplusplus_entries(inf)
3439 FILE *inf;
3440 {
3441         C_entries(C_PLPL, inf);
3442 }
3443
3444 /* Always do Java. */
3445 void Cjava_entries(inf)
3446 FILE *inf;
3447 {
3448         C_entries(C_JAVA, inf);
3449 }
3450
3451 /* Always do C*. */
3452 void Cstar_entries(inf)
3453 FILE *inf;
3454 {
3455         C_entries(C_STAR, inf);
3456 }
3457
3458 /* Always do Yacc. */
3459 void Yacc_entries(inf)
3460 FILE *inf;
3461 {
3462         C_entries(YACC, inf);
3463 }
3464 \f
3465 /* A useful macro. */
3466 #define LOOP_ON_INPUT_LINES(file_pointer, line_buffer, char_pointer)    \
3467   for (lineno = charno = 0;     /* loop initialization */               \
3468        !feof (file_pointer)     /* loop test */                         \
3469        && (lineno++,            /* instructions at start of loop */     \
3470            linecharno = charno,                                         \
3471            charno += readline (&line_buffer, file_pointer),             \
3472            char_pointer = lb.buffer,                                    \
3473            TRUE);                                                       \
3474       )
3475
3476 /*
3477  * Read a file, but do no processing.  This is used to do regexp
3478  * matching on files that have no language defined.
3479  */
3480 void just_read_file(inf)
3481 FILE *inf;
3482 {
3483         register char *dummy;
3484
3485         LOOP_ON_INPUT_LINES(inf, lb, dummy)
3486             continue;
3487 }
3488 \f
3489 /* Fortran parsing */
3490
3491 bool tail PP((char *cp));
3492 bool tail(cp)
3493 char *cp;
3494 {
3495         register int len = 0;
3496
3497         while (*cp && lowcase(*cp) == lowcase(dbp[len]))
3498                 cp++, len++;
3499         if (*cp == '\0' && !intoken(dbp[len])) {
3500                 dbp += len;
3501                 return TRUE;
3502         }
3503         return FALSE;
3504 }
3505
3506 void takeprec()
3507 {
3508         dbp = skip_spaces(dbp);
3509         if (*dbp != '*')
3510                 return;
3511         dbp++;
3512         dbp = skip_spaces(dbp);
3513         if (strneq(dbp, "(*)", 3)) {
3514                 dbp += 3;
3515                 return;
3516         }
3517         if (!isdigit(*dbp)) {
3518                 --dbp;          /* force failure */
3519                 return;
3520         }
3521         do
3522                 dbp++;
3523         while (isdigit(*dbp));
3524 }
3525
3526 void getit PP((FILE * inf));
3527 void getit(inf)
3528 FILE *inf;
3529 {
3530         register char *cp;
3531
3532         dbp = skip_spaces(dbp);
3533         if (*dbp == '\0') {
3534                 lineno++;
3535                 linecharno = charno;
3536                 charno += readline(&lb, inf);
3537                 dbp = lb.buffer;
3538                 if (dbp[5] != '&')
3539                         return;
3540                 dbp += 6;
3541                 dbp = skip_spaces(dbp);
3542         }
3543         if (!isalpha(*dbp)
3544             && *dbp != '_' && *dbp != '$')
3545                 return;
3546         for (cp = dbp + 1; *cp && intoken(*cp); cp++)
3547                 continue;
3548         pfnote((CTAGS) ? savenstr(dbp, cp - dbp) : NULL, TRUE,
3549                lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
3550 }
3551
3552 void Fortran_functions(inf)
3553 FILE *inf;
3554 {
3555         LOOP_ON_INPUT_LINES(inf, lb, dbp) {
3556                 if (*dbp == '%')
3557                         dbp++;  /* Ratfor escape to fortran */
3558                 dbp = skip_spaces(dbp);
3559                 if (*dbp == '\0')
3560                         continue;
3561                 switch (lowcase(*dbp)) {
3562                 case 'i':
3563                         if (tail("integer"))
3564                                 takeprec();
3565                         break;
3566                 case 'r':
3567                         if (tail("real"))
3568                                 takeprec();
3569                         break;
3570                 case 'l':
3571                         if (tail("logical"))
3572                                 takeprec();
3573                         break;
3574                 case 'c':
3575                         if (tail("complex") || tail("character"))
3576                                 takeprec();
3577                         break;
3578                 case 'd':
3579                         if (tail("double")) {
3580                                 dbp = skip_spaces(dbp);
3581                                 if (*dbp == '\0')
3582                                         continue;
3583                                 if (tail("precision"))
3584                                         break;
3585                                 continue;
3586                         }
3587                         break;
3588                 default:
3589                         break;
3590                 }
3591                 dbp = skip_spaces(dbp);
3592                 if (*dbp == '\0')
3593                         continue;
3594                 switch (lowcase(*dbp)) {
3595                 case 'f':
3596                         if (tail("function"))
3597                                 getit(inf);
3598                         continue;
3599                 case 's':
3600                         if (tail("subroutine"))
3601                                 getit(inf);
3602                         continue;
3603                 case 'e':
3604                         if (tail("entry"))
3605                                 getit(inf);
3606                         continue;
3607                 case 'p':
3608                         if (tail("program")) {
3609                                 getit(inf);
3610                                 continue;
3611                         }
3612                         if (tail("procedure"))
3613                                 getit(inf);
3614                         continue;
3615                 default:
3616                         break;
3617                 }
3618         }
3619 }
3620 \f
3621 /*
3622  * Bob Weiner, Motorola Inc., 4/3/94
3623  * Unix and microcontroller assembly tag handling
3624  * look for '^[a-zA-Z_.$][a-zA_Z0-9_.$]*[: ^I^J]'
3625  */
3626 void Asm_labels(FILE * inf)
3627 {
3628         register char *cp;
3629
3630         LOOP_ON_INPUT_LINES(inf, lb, cp) {
3631                 /* If first char is alphabetic or one of [_.$], test for colon
3632                    following identifier. */
3633                 if (isalpha(*cp) || *cp == '_' || *cp == '.' || *cp == '$') {
3634                         /* Read past label. */
3635                         cp++;
3636                         while (isalnum(*cp) || *cp == '_' || *cp == '.'
3637                                || *cp == '$')
3638                                 cp++;
3639                         if (*cp == ':' || isspace(*cp)) {
3640                                 /* Found end of label, so copy it and add it to the table. */
3641                                 pfnote((CTAGS) ?
3642                                        savenstr(lb.buffer,
3643                                                 cp - lb.buffer) : NULL, TRUE,
3644                                        lb.buffer, cp - lb.buffer + 1, lineno,
3645                                        linecharno);
3646                         }
3647                 }
3648         }
3649 }
3650 \f
3651 /*
3652  * Perl support by Bart Robinson <lomew@cs.utah.edu>
3653  *              enhanced by Michael Ernst <mernst@alum.mit.edu>
3654  * Perl sub names: look for /^sub[ \t\n]+[^ \t\n{]+/
3655  * Perl variable names: /^(my|local).../
3656  */
3657 void Perl_functions(inf)
3658 FILE *inf;
3659 {
3660         register char *cp;
3661
3662         LOOP_ON_INPUT_LINES(inf, lb, cp) {
3663                 if (*cp++ == 's'
3664                     && *cp++ == 'u' && *cp++ == 'b' && isspace(*cp++)) {
3665                         cp = skip_spaces(cp);
3666                         if (*cp != '\0') {
3667                                 while (*cp != '\0'
3668                                        && !isspace(*cp) && *cp != '{'
3669                                        && *cp != '(')
3670                                         cp++;
3671                                 pfnote((CTAGS) ?
3672                                        savenstr(lb.buffer,
3673                                                 cp - lb.buffer) : NULL, TRUE,
3674                                        lb.buffer, cp - lb.buffer + 1, lineno,
3675                                        linecharno);
3676                         }
3677                 } else if (globals      /* only if tagging global vars is enabled */
3678                            && ((cp = lb.buffer, *cp++ == 'm' && *cp++ == 'y')
3679                                || (cp = lb.buffer,
3680                                    *cp++ == 'l'
3681                                    && *cp++ == 'o'
3682                                    && *cp++ == 'c'
3683                                    && *cp++ == 'a' && *cp++ == 'l'))
3684                            && (*cp == '(' || isspace(*cp))) {
3685                         /* After "my" or "local", but before any following paren or space. */
3686                         char *varname = NULL;
3687
3688                         cp = skip_spaces(cp);
3689                         if (*cp == '$' || *cp == '@' || *cp == '%') {
3690                                 char *varstart = ++cp;
3691                                 while (isalnum(*cp) || *cp == '_')
3692                                         cp++;
3693                                 varname = savenstr(varstart, cp - varstart);
3694                         } else {
3695                                 /* Should be examining a variable list at this point;
3696                                    could insist on seeing an open parenthesis. */
3697                                 while (*cp != '\0' && *cp != ';' && *cp != '='
3698                                        && *cp != ')')
3699                                         cp++;
3700                         }
3701
3702                         /* Perhaps I should back cp up one character, so the TAGS table
3703                            doesn't mention (and so depend upon) the following char. */
3704                         pfnote((CTAGS) ? savenstr(lb.buffer, cp - lb.buffer) :
3705                                varname, FALSE, lb.buffer, cp - lb.buffer + 1,
3706                                lineno, linecharno);
3707                 }
3708         }
3709 }
3710 \f
3711 /*
3712  * Python support by Eric S. Raymond <esr@thyrsus.com>
3713  * Look for /^def[ \t\n]+[^ \t\n(:]+/ or /^class[ \t\n]+[^ \t\n(:]+/
3714  */
3715 void Python_functions(inf)
3716 FILE *inf;
3717 {
3718         register char *cp;
3719
3720         LOOP_ON_INPUT_LINES(inf, lb, cp) {
3721                 if (*cp++ == 'd'
3722                     && *cp++ == 'e' && *cp++ == 'f' && isspace(*cp++)) {
3723                         cp = skip_spaces(cp);
3724                         while (*cp != '\0' && !isspace(*cp) && *cp != '('
3725                                && *cp != ':')
3726                                 cp++;
3727                         pfnote((char *)NULL, TRUE,
3728                                lb.buffer, cp - lb.buffer + 1, lineno,
3729                                linecharno);
3730                 }
3731
3732                 cp = lb.buffer;
3733                 if (*cp++ == 'c'
3734                     && *cp++ == 'l'
3735                     && *cp++ == 'a'
3736                     && *cp++ == 's' && *cp++ == 's' && isspace(*cp++)) {
3737                         cp = skip_spaces(cp);
3738                         while (*cp != '\0' && !isspace(*cp) && *cp != '('
3739                                && *cp != ':')
3740                                 cp++;
3741                         pfnote((char *)NULL, TRUE,
3742                                lb.buffer, cp - lb.buffer + 1, lineno,
3743                                linecharno);
3744                 }
3745         }
3746 }
3747 \f
3748 /* Idea by Corny de Souza
3749  * Cobol tag functions
3750  * We could look for anything that could be a paragraph name.
3751  * i.e. anything that starts in column 8 is one word and ends in a full stop.
3752  */
3753 void Cobol_paragraphs(inf)
3754 FILE *inf;
3755 {
3756         register char *bp, *ep;
3757
3758         LOOP_ON_INPUT_LINES(inf, lb, bp) {
3759                 if (lb.len < 9)
3760                         continue;
3761                 bp += 8;
3762
3763                 /* If eoln, compiler option or comment ignore whole line. */
3764                 if (bp[-1] != ' ' || !isalnum(bp[0]))
3765                         continue;
3766
3767                 for (ep = bp; isalnum(*ep) || *ep == '-'; ep++)
3768                         continue;
3769                 if (*ep++ == '.')
3770                         pfnote((CTAGS) ? savenstr(bp, ep - bp) : NULL, TRUE,
3771                                lb.buffer, ep - lb.buffer + 1, lineno,
3772                                linecharno);
3773         }
3774 }
3775 \f
3776 /* Added by Mosur Mohan, 4/22/88 */
3777 /* Pascal parsing                */
3778
3779 /*
3780  *  Locates tags for procedures & functions.  Doesn't do any type- or
3781  *  var-definitions.  It does look for the keyword "extern" or
3782  *  "forward" immediately following the procedure statement; if found,
3783  *  the tag is skipped.
3784  */
3785 void Pascal_functions(inf)
3786 FILE *inf;
3787 {
3788         linebuffer tline;       /* mostly copied from C_entries */
3789         long save_lcno;
3790         int save_lineno, save_len;
3791         char c, *cp, *namebuf;
3792
3793         bool                    /* each of these flags is TRUE iff: */
3794             incomment,          /* point is inside a comment */
3795             inquote,            /* point is inside '..' string */
3796             get_tagname,        /* point is after PROCEDURE/FUNCTION
3797                                    keyword, so next item = potential tag */
3798             found_tag,          /* point is after a potential tag */
3799             inparms,            /* point is within parameter-list */
3800             verify_tag;         /* point has passed the parm-list, so the
3801                                    next token will determine whether this
3802                                    is a FORWARD/EXTERN to be ignored, or
3803                                    whether it is a real tag */
3804
3805         save_lcno = save_lineno = save_len = 0; /* keep compiler quiet */
3806         namebuf = NULL;         /* keep compiler quiet */
3807         lineno = 0;
3808         charno = 0;
3809         dbp = lb.buffer;
3810         *dbp = '\0';
3811         initbuffer(&tline);
3812
3813         incomment = inquote = FALSE;
3814         found_tag = FALSE;      /* have a proc name; check if extern */
3815         get_tagname = FALSE;    /* have found "procedure" keyword    */
3816         inparms = FALSE;        /* found '(' after "proc"            */
3817         verify_tag = FALSE;     /* check if "extern" is ahead        */
3818
3819         while (!feof(inf)) {    /* long main loop to get next char */
3820                 c = *dbp++;
3821                 if (c == '\0') {        /* if end of line */
3822                         lineno++;
3823                         linecharno = charno;
3824                         charno += readline(&lb, inf);
3825                         dbp = lb.buffer;
3826                         if (*dbp == '\0')
3827                                 continue;
3828                         if (!((found_tag && verify_tag)
3829                               || get_tagname))
3830                                 c = *dbp++;     /* only if don't need *dbp pointing
3831                                                    to the beginning of the name of
3832                                                    the procedure or function */
3833                 }
3834                 if (incomment) {
3835                         if (c == '}')   /* within { } comments */
3836                                 incomment = FALSE;
3837                         else if (c == '*' && *dbp == ')') {     /* within (* *) comments */
3838                                 dbp++;
3839                                 incomment = FALSE;
3840                         }
3841                         continue;
3842                 } else if (inquote) {
3843                         if (c == '\'')
3844                                 inquote = FALSE;
3845                         continue;
3846                 } else
3847                         switch (c) {
3848                         case '\'':
3849                                 inquote = TRUE; /* found first quote */
3850                                 continue;
3851                         case '{':       /* found open { comment */
3852                                 incomment = TRUE;
3853                                 continue;
3854                         case '(':
3855                                 if (*dbp == '*') {      /* found open (* comment */
3856                                         incomment = TRUE;
3857                                         dbp++;
3858                                 } else if (found_tag)   /* found '(' after tag, i.e., parm-list */
3859                                         inparms = TRUE;
3860                                 continue;
3861                         case ')':       /* end of parms list */
3862                                 if (inparms)
3863                                         inparms = FALSE;
3864                                 continue;
3865                         case ';':
3866                                 if (found_tag && !inparms) {    /* end of proc or fn stmt */
3867                                         verify_tag = TRUE;
3868                                         break;
3869                                 }
3870                                 continue;
3871                         default:
3872                                 break;
3873                         }
3874                 if (found_tag && verify_tag && (*dbp != ' ')) {
3875                         /* check if this is an "extern" declaration */
3876                         if (*dbp == '\0')
3877                                 continue;
3878                         if (lowcase(*dbp == 'e')) {
3879                                 if (tail("extern")) {   /* superfluous, really! */
3880                                         found_tag = FALSE;
3881                                         verify_tag = FALSE;
3882                                 }
3883                         } else if (lowcase(*dbp) == 'f') {
3884                                 if (tail("forward")) {  /*  check for forward reference */
3885                                         found_tag = FALSE;
3886                                         verify_tag = FALSE;
3887                                 }
3888                         }
3889                         if (found_tag && verify_tag) {  /* not external proc, so make tag */
3890                                 found_tag = FALSE;
3891                                 verify_tag = FALSE;
3892                                 pfnote(namebuf, TRUE,
3893                                        tline.buffer, save_len, save_lineno,
3894                                        save_lcno);
3895                                 continue;
3896                         }
3897                 }
3898                 if (get_tagname) {      /* grab name of proc or fn */
3899                         if (*dbp == '\0')
3900                                 continue;
3901
3902                         /* save all values for later tagging */
3903                         grow_linebuffer(&tline, lb.len + 1);
3904                         strncpy(tline.buffer, lb.buffer, lb.len);
3905                         save_lineno = lineno;
3906                         save_lcno = linecharno;
3907
3908                         /* grab block name */
3909                         for (cp = dbp + 1; *cp != '\0' && !endtoken(*cp); cp++)
3910                                 continue;
3911                         namebuf = (CTAGS) ? savenstr(dbp, cp - dbp) : NULL;
3912                         dbp = cp;       /* set dbp to e-o-token */
3913                         save_len = dbp - lb.buffer + 1;
3914                         get_tagname = FALSE;
3915                         found_tag = TRUE;
3916                         continue;
3917
3918                         /* and proceed to check for "extern" */
3919                 } else if (!incomment && !inquote && !found_tag) {
3920                         /* check for proc/fn keywords */
3921                         switch (lowcase(c)) {
3922                         case 'p':
3923                                 if (tail("rocedure"))   /* c = 'p', dbp has advanced */
3924                                         get_tagname = TRUE;
3925                                 continue;
3926                         case 'f':
3927                                 if (tail("unction"))
3928                                         get_tagname = TRUE;
3929                                 continue;
3930                         default:
3931                                 break;
3932                         }
3933                 }
3934         }                       /* while not eof */
3935
3936         free(tline.buffer);
3937 }
3938 \f
3939 /*
3940  * lisp tag functions
3941  *  look for (def or (DEF, quote or QUOTE
3942  */
3943 int L_isdef PP((char *strp));
3944 int L_isdef(strp)
3945 register char *strp;
3946 {
3947         return ((strp[1] == 'd' || strp[1] == 'D')
3948                 && (strp[2] == 'e' || strp[2] == 'E')
3949                 && (strp[3] == 'f' || strp[3] == 'F'));
3950 }
3951 int L_isquote PP((char *strp));
3952 int L_isquote(strp)
3953 register char *strp;
3954 {
3955         return ((*++strp == 'q' || *strp == 'Q')
3956                 && (*++strp == 'u' || *strp == 'U')
3957                 && (*++strp == 'o' || *strp == 'O')
3958                 && (*++strp == 't' || *strp == 'T')
3959                 && (*++strp == 'e' || *strp == 'E')
3960                 && isspace(*++strp));
3961 }
3962
3963 void L_getit PP((void));
3964 void L_getit()
3965 {
3966         register char *cp;
3967
3968         if (*dbp == '\'')       /* Skip prefix quote */
3969                 dbp++;
3970         else if (*dbp == '(') {
3971                 if (L_isquote(dbp))
3972                         dbp += 7;       /* Skip "(quote " */
3973                 else
3974                         dbp += 1;       /* Skip "(" before name in (defstruct (foo)) */
3975                 dbp = skip_spaces(dbp);
3976         }
3977
3978         for (cp = dbp /*+1 */ ;
3979              *cp != '\0' && *cp != '(' && *cp != ' ' && *cp != ')'; cp++)
3980                 continue;
3981         if (cp == dbp)
3982                 return;
3983
3984         pfnote((CTAGS) ? savenstr(dbp, cp - dbp) : NULL, TRUE,
3985                lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
3986 }
3987
3988 void Lisp_functions(inf)
3989 FILE *inf;
3990 {
3991         LOOP_ON_INPUT_LINES(inf, lb, dbp) {
3992                 if (dbp[0] == '(') {
3993                         if (L_isdef(dbp)) {
3994                                 dbp = skip_non_spaces(dbp);
3995                                 dbp = skip_spaces(dbp);
3996                                 L_getit();
3997                         } else {
3998                                 /* Check for (foo::defmumble name-defined ... */
3999                                 do
4000                                         dbp++;
4001                                 while (*dbp != '\0' && !isspace(*dbp)
4002                                        && *dbp != ':' && *dbp != '('
4003                                        && *dbp != ')');
4004                                 if (*dbp == ':') {
4005                                         do
4006                                                 dbp++;
4007                                         while (*dbp == ':');
4008
4009                                         if (L_isdef(dbp - 1)) {
4010                                                 dbp = skip_non_spaces(dbp);
4011                                                 dbp = skip_spaces(dbp);
4012                                                 L_getit();
4013                                         }
4014                                 }
4015                         }
4016                 }
4017         }
4018 }
4019 \f
4020 /*
4021  * Postscript tag functions
4022  * Just look for lines where the first character is '/'
4023  * Richard Mlynarik <mly@adoc.xerox.com>
4024  */
4025 void Postscript_functions(inf)
4026 FILE *inf;
4027 {
4028         register char *bp, *ep;
4029
4030         LOOP_ON_INPUT_LINES(inf, lb, bp) {
4031                 if (bp[0] == '/') {
4032                         for (ep = bp + 1;
4033                              *ep != '\0' && *ep != ' ' && *ep != '{'; ep++)
4034                                 continue;
4035                         pfnote((CTAGS) ? savenstr(bp, ep - bp) : NULL, TRUE,
4036                                lb.buffer, ep - lb.buffer + 1, lineno,
4037                                linecharno);
4038                 }
4039         }
4040 }
4041 \f
4042 /*
4043  * Scheme tag functions
4044  * look for (def... xyzzy
4045  * look for (def... (xyzzy
4046  * look for (def ... ((...(xyzzy ....
4047  * look for (set! xyzzy
4048  */
4049
4050 void get_scheme PP((void));
4051
4052 void Scheme_functions(inf)
4053 FILE *inf;
4054 {
4055         LOOP_ON_INPUT_LINES(inf, lb, dbp) {
4056                 if (dbp[0] == '(' && (dbp[1] == 'D' || dbp[1] == 'd')
4057                     && (dbp[2] == 'E' || dbp[2] == 'e')
4058                     && (dbp[3] == 'F' || dbp[3] == 'f')) {
4059                         dbp = skip_non_spaces(dbp);
4060                         /* Skip over open parens and white space */
4061                         while (isspace(*dbp) || *dbp == '(')
4062                                 dbp++;
4063                         get_scheme();
4064                 }
4065                 if (dbp[0] == '(' && (dbp[1] == 'S' || dbp[1] == 's')
4066                     && (dbp[2] == 'E' || dbp[2] == 'e')
4067                     && (dbp[3] == 'T' || dbp[3] == 't')
4068                     && (dbp[4] == '!' || dbp[4] == '!')
4069                     && (isspace(dbp[5]))) {
4070                         dbp = skip_non_spaces(dbp);
4071                         dbp = skip_spaces(dbp);
4072                         get_scheme();
4073                 }
4074         }
4075 }
4076
4077 void get_scheme()
4078 {
4079         register char *cp;
4080
4081         if (*dbp == '\0')
4082                 return;
4083         /* Go till you get to white space or a syntactic break */
4084         for (cp = dbp + 1;
4085              *cp != '\0' && *cp != '(' && *cp != ')' && !isspace(*cp); cp++)
4086                 continue;
4087         pfnote((CTAGS) ? savenstr(dbp, cp - dbp) : NULL, TRUE,
4088                lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
4089 }
4090 \f
4091 /* Find tags in TeX and LaTeX input files.  */
4092
4093 /* TEX_toktab is a table of TeX control sequences that define tags.
4094    Each TEX_tabent records one such control sequence.
4095    CONVERT THIS TO USE THE Stab TYPE!! */
4096 struct TEX_tabent {
4097         char *name;
4098         int len;
4099 };
4100
4101 struct TEX_tabent *TEX_toktab = NULL;   /* Table with tag tokens */
4102
4103 /* Default set of control sequences to put into TEX_toktab.
4104    The value of environment var TEXTAGS is prepended to this.  */
4105
4106 char *TEX_defenv = "\
4107 :chapter:section:subsection:subsubsection:eqno:label:ref:cite:bibitem\
4108 :part:appendix:entry:index";
4109
4110 void TEX_mode PP((FILE * inf));
4111 struct TEX_tabent *TEX_decode_env PP((char *evarname, char *defenv));
4112 int TEX_Token PP((char *cp));
4113
4114 char TEX_esc = '\\';
4115 char TEX_opgrp = '{';
4116 char TEX_clgrp = '}';
4117
4118 /*
4119  * TeX/LaTeX scanning loop.
4120  */
4121 void TeX_functions(inf)
4122 FILE *inf;
4123 {
4124         char *cp, *lasthit;
4125         register int i;
4126
4127         /* Select either \ or ! as escape character.  */
4128         TEX_mode(inf);
4129
4130         /* Initialize token table once from environment. */
4131         if (!TEX_toktab)
4132                 TEX_toktab = TEX_decode_env("TEXTAGS", TEX_defenv);
4133
4134         LOOP_ON_INPUT_LINES(inf, lb, cp) {
4135                 lasthit = cp;
4136                 /* Look at each esc in line. */
4137                 while ((cp = etags_strchr(cp, TEX_esc)) != NULL) {
4138                         if (*++cp == '\0')
4139                                 break;
4140                         linecharno += cp - lasthit;
4141                         lasthit = cp;
4142                         i = TEX_Token(lasthit);
4143                         if (i >= 0) {
4144                                 /* We seem to include the TeX command in the tag name.
4145                                    register char *p;
4146                                    for (p = lasthit + TEX_toktab[i].len;
4147                                    *p != '\0' && *p != TEX_clgrp;
4148                                    p++)
4149                                    continue; */
4150                                 pfnote( /*savenstr (lasthit, p-lasthit) */
4151                                     (char *)NULL, TRUE,
4152                                        lb.buffer, lb.len, lineno, linecharno);
4153                                 break;  /* We only tag a line once */
4154                         }
4155                 }
4156         }
4157 }
4158
4159 #define TEX_LESC '\\'
4160 #define TEX_SESC '!'
4161 #define TEX_cmt  '%'
4162
4163 /* Figure out whether TeX's escapechar is '\\' or '!' and set grouping
4164    chars accordingly. */
4165 void TEX_mode(inf)
4166 FILE *inf;
4167 {
4168         int c;
4169
4170         while ((c = getc(inf)) != EOF) {
4171                 /* Skip to next line if we hit the TeX comment char. */
4172                 if (c == TEX_cmt)
4173                         while (c != '\n')
4174                                 c = getc(inf);
4175                 else if (c == TEX_LESC || c == TEX_SESC)
4176                         break;
4177         }
4178
4179         if (c == TEX_LESC) {
4180                 TEX_esc = TEX_LESC;
4181                 TEX_opgrp = '{';
4182                 TEX_clgrp = '}';
4183         } else {
4184                 TEX_esc = TEX_SESC;
4185                 TEX_opgrp = '<';
4186                 TEX_clgrp = '>';
4187         }
4188         rewind(inf);
4189 }
4190
4191 /* Read environment and prepend it to the default string.
4192    Build token table. */
4193 struct TEX_tabent *TEX_decode_env(evarname, defenv)
4194 char *evarname;
4195 char *defenv;
4196 {
4197         register char *env, *p;
4198
4199         struct TEX_tabent *tab;
4200         int size, i;
4201
4202         /* Append default string to environment. */
4203         env = getenv(evarname);
4204         if (!env)
4205                 env = defenv;
4206         else {
4207                 char *oldenv = env;
4208                 env = concat(oldenv, defenv, "");
4209                 free(oldenv);
4210         }
4211
4212         /* Allocate a token table */
4213         for (size = 1, p = env; p;)
4214                 if ((p = etags_strchr(p, ':')) && *++p != '\0')
4215                         size++;
4216         /* Add 1 to leave room for null terminator.  */
4217         tab = xnew(size + 1, struct TEX_tabent);
4218
4219         /* Unpack environment string into token table. Be careful about */
4220         /* zero-length strings (leading ':', "::" and trailing ':') */
4221         for (i = 0; *env;) {
4222                 p = etags_strchr(env, ':');
4223                 if (!p)         /* End of environment string. */
4224                         p = env + strlen(env);
4225                 if (p - env > 0) {      /* Only non-zero strings. */
4226                         tab[i].name = savenstr(env, p - env);
4227                         tab[i].len = strlen(tab[i].name);
4228                         i++;
4229                 }
4230                 if (*p)
4231                         env = p + 1;
4232                 else {
4233                         tab[i].name = NULL;     /* Mark end of table. */
4234                         tab[i].len = 0;
4235                         break;
4236                 }
4237         }
4238         return tab;
4239 }
4240
4241 /* If the text at CP matches one of the tag-defining TeX command names,
4242    return the pointer to the first occurrence of that command in TEX_toktab.
4243    Otherwise return -1.
4244    Keep the capital `T' in `token' for dumb truncating compilers
4245    (this distinguishes it from `TEX_toktab' */
4246 int TEX_Token(cp)
4247 char *cp;
4248 {
4249         int i;
4250
4251         for (i = 0; TEX_toktab[i].len > 0; i++)
4252                 if (strneq(TEX_toktab[i].name, cp, TEX_toktab[i].len))
4253                         return i;
4254         return -1;
4255 }
4256 \f
4257 /*
4258  * Prolog support (rewritten) by Anders Lindgren, Mar. 96
4259  *
4260  * Assumes that the predicate starts at column 0.
4261  * Only the first clause of a predicate is added. 
4262  */
4263 int prolog_pred PP((char *s, char *last));
4264 void prolog_skip_comment PP((linebuffer * plb, FILE * inf));
4265 int prolog_atom PP((char *s, int pos));
4266
4267 void Prolog_functions(inf)
4268 FILE *inf;
4269 {
4270         char *cp, *last;
4271         int len;
4272         int allocated;
4273
4274         allocated = 0;
4275         len = 0;
4276         last = NULL;
4277
4278         LOOP_ON_INPUT_LINES(inf, lb, cp) {
4279                 if (cp[0] == '\0')      /* Empty line */
4280                         continue;
4281                 else if (isspace(cp[0]))        /* Not a predicate */
4282                         continue;
4283                 else if (cp[0] == '/' && cp[1] == '*')  /* comment. */
4284                         prolog_skip_comment(&lb, inf);
4285                 else if ((len = prolog_pred(cp, last)) > 0) {
4286                         /* Predicate.  Store the function name so that we only
4287                            generate a tag for the first clause.  */
4288                         if (last == NULL)
4289                                 last = xnew(len + 1, char);
4290                         else if (len + 1 > allocated)
4291                                 last = xrnew(last, len + 1, char);
4292                         allocated = len + 1;
4293                         strncpy(last, cp, len);
4294                         last[len] = '\0';
4295                 }
4296         }
4297         free(last);
4298 }
4299
4300 void prolog_skip_comment(plb, inf)
4301 linebuffer *plb;
4302 FILE *inf;
4303 {
4304         char *cp;
4305
4306         do {
4307                 for (cp = plb->buffer; *cp != '\0'; cp++)
4308                         if (cp[0] == '*' && cp[1] == '/')
4309                                 return;
4310                 lineno++;
4311                 linecharno += readline(plb, inf);
4312         }
4313         while (!feof(inf));
4314 }
4315
4316 /*
4317  * A predicate definition is added if it matches:
4318  *     <beginning of line><Prolog Atom><whitespace>(
4319  *
4320  * It is added to the tags database if it doesn't match the
4321  * name of the previous clause header.
4322  *
4323  * Return the size of the name of the predicate, or 0 if no header
4324  * was found.
4325  */
4326 int prolog_pred(s, last)
4327 char *s;
4328 char *last;                     /* Name of last clause. */
4329 {
4330         int pos;
4331         int len;
4332
4333         pos = prolog_atom(s, 0);
4334         if (pos < 1)
4335                 return 0;
4336
4337         len = pos;
4338         pos = skip_spaces(s + pos) - s;
4339
4340         if ((s[pos] == '(') || (s[pos] == '.')) {
4341                 if (s[pos] == '(')
4342                         pos++;
4343
4344                 /* Save only the first clause. */
4345                 if (last == NULL || len != (int)strlen(last)
4346                     || !strneq(s, last, len)) {
4347                         pfnote((CTAGS) ? savenstr(s, len) : NULL, TRUE,
4348                                s, pos, lineno, linecharno);
4349                         return len;
4350                 }
4351         }
4352         return 0;
4353 }
4354
4355 /*
4356  * Consume a Prolog atom.
4357  * Return the number of bytes consumed, or -1 if there was an error.
4358  *
4359  * A prolog atom, in this context, could be one of:
4360  * - An alphanumeric sequence, starting with a lower case letter.
4361  * - A quoted arbitrary string. Single quotes can escape themselves.
4362  *   Backslash quotes everything.
4363  */
4364 int prolog_atom(s, pos)
4365 char *s;
4366 int pos;
4367 {
4368         int origpos;
4369
4370         origpos = pos;
4371
4372         if (islower(s[pos]) || (s[pos] == '_')) {
4373                 /* The atom is unquoted. */
4374                 pos++;
4375                 while (isalnum(s[pos]) || (s[pos] == '_')) {
4376                         pos++;
4377                 }
4378                 return pos - origpos;
4379         } else if (s[pos] == '\'') {
4380                 pos++;
4381
4382                 while (1) {
4383                         if (s[pos] == '\'') {
4384                                 pos++;
4385                                 if (s[pos] != '\'')
4386                                         break;
4387                                 pos++;  /* A double quote */
4388                         } else if (s[pos] == '\0')
4389                                 /* Multiline quoted atoms are ignored. */
4390                                 return -1;
4391                         else if (s[pos] == '\\') {
4392                                 if (s[pos + 1] == '\0')
4393                                         return -1;
4394                                 pos += 2;
4395                         } else
4396                                 pos++;
4397                 }
4398                 return pos - origpos;
4399         } else
4400                 return -1;
4401 }
4402 \f
4403 /* 
4404  * Support for Erlang  --  Anders Lindgren, Feb 1996.
4405  *
4406  * Generates tags for functions, defines, and records.
4407  *
4408  * Assumes that Erlang functions start at column 0.
4409  */
4410 int erlang_func PP((char *s, char *last));
4411 void erlang_attribute PP((char *s));
4412 int erlang_atom PP((char *s, int pos));
4413
4414 void Erlang_functions(inf)
4415 FILE *inf;
4416 {
4417         char *cp, *last;
4418         int len;
4419         int allocated;
4420
4421         allocated = 0;
4422         len = 0;
4423         last = NULL;
4424
4425         LOOP_ON_INPUT_LINES(inf, lb, cp) {
4426                 if (cp[0] == '\0')      /* Empty line */
4427                         continue;
4428                 else if (isspace(cp[0]))        /* Not function nor attribute */
4429                         continue;
4430                 else if (cp[0] == '%')  /* comment */
4431                         continue;
4432                 else if (cp[0] == '"')  /* Sometimes, strings start in column one */
4433                         continue;
4434                 else if (cp[0] == '-') {        /* attribute, e.g. "-define" */
4435                         erlang_attribute(cp);
4436                         free(last);
4437                         last = NULL;
4438                 } else if ((len = erlang_func(cp, last)) > 0) {
4439                         /* 
4440                          * Function.  Store the function name so that we only
4441                          * generates a tag for the first clause.
4442                          */
4443                         if (last == NULL)
4444                                 last = xnew(len + 1, char);
4445                         else if (len + 1 > allocated)
4446                                 last = xrnew(last, len + 1, char);
4447                         allocated = len + 1;
4448                         strncpy(last, cp, len);
4449                         last[len] = '\0';
4450                 }
4451         }
4452         free(last);
4453 }
4454
4455 /*
4456  * A function definition is added if it matches:
4457  *     <beginning of line><Erlang Atom><whitespace>(
4458  *
4459  * It is added to the tags database if it doesn't match the
4460  * name of the previous clause header.
4461  *
4462  * Return the size of the name of the function, or 0 if no function
4463  * was found.
4464  */
4465 int erlang_func(s, last)
4466 char *s;
4467 char *last;                     /* Name of last clause. */
4468 {
4469         int pos;
4470         int len;
4471
4472         pos = erlang_atom(s, 0);
4473         if (pos < 1)
4474                 return 0;
4475
4476         len = pos;
4477         pos = skip_spaces(s + pos) - s;
4478
4479         /* Save only the first clause. */
4480         if (s[pos++] == '(' && (last == NULL || len != (int)strlen(last)
4481                                 || !strneq(s, last, len))) {
4482                 pfnote((CTAGS) ? savenstr(s, len) : NULL, TRUE,
4483                        s, pos, lineno, linecharno);
4484                 return len;
4485         }
4486
4487         return 0;
4488 }
4489
4490 /*
4491  * Handle attributes.  Currently, tags are generated for defines 
4492  * and records.
4493  *
4494  * They are on the form:
4495  * -define(foo, bar).
4496  * -define(Foo(M, N), M+N).
4497  * -record(graph, {vtab = notable, cyclic = true}).
4498  */
4499 void erlang_attribute(s)
4500 char *s;
4501 {
4502         int pos;
4503         int len;
4504
4505         if (strneq(s, "-define", 7) || strneq(s, "-record", 7)) {
4506                 pos = skip_spaces(s + 7) - s;
4507                 if (s[pos++] == '(') {
4508                         pos = skip_spaces(s + pos) - s;
4509                         len = erlang_atom(s, pos);
4510                         if (len != 0)
4511                                 pfnote((CTAGS) ? savenstr(&s[pos], len) : NULL,
4512                                        TRUE, s, pos + len, lineno, linecharno);
4513                 }
4514         }
4515         return;
4516 }
4517
4518 /*
4519  * Consume an Erlang atom (or variable).
4520  * Return the number of bytes consumed, or -1 if there was an error.
4521  */
4522 int erlang_atom(s, pos)
4523 char *s;
4524 int pos;
4525 {
4526         int origpos;
4527
4528         origpos = pos;
4529
4530         if (isalpha(s[pos]) || s[pos] == '_') {
4531                 /* The atom is unquoted. */
4532                 pos++;
4533                 while (isalnum(s[pos]) || s[pos] == '_')
4534                         pos++;
4535                 return pos - origpos;
4536         } else if (s[pos] == '\'') {
4537                 pos++;
4538
4539                 while (1) {
4540                         if (s[pos] == '\'') {
4541                                 pos++;
4542                                 break;
4543                         } else if (s[pos] == '\0')
4544                                 /* Multiline quoted atoms are ignored. */
4545                                 return -1;
4546                         else if (s[pos] == '\\') {
4547                                 if (s[pos + 1] == '\0')
4548                                         return -1;
4549                                 pos += 2;
4550                         } else
4551                                 pos++;
4552                 }
4553                 return pos - origpos;
4554         } else
4555                 return -1;
4556 }
4557 \f
4558 #ifdef ETAGS_REGEXPS
4559
4560 /* Take a string like "/blah/" and turn it into "blah", making sure
4561    that the first and last characters are the same, and handling
4562    quoted separator characters.  Actually, stops on the occurrence of
4563    an unquoted separator.  Also turns "\t" into a Tab character.
4564    Returns pointer to terminating separator.  Works in place.  Null
4565    terminates name string. */
4566 char *scan_separators PP((char *name));
4567 char *scan_separators(name)
4568 char *name;
4569 {
4570         char sep = name[0];
4571         char *copyto = name;
4572         bool quoted = FALSE;
4573
4574         for (++name; *name != '\0'; ++name) {
4575                 if (quoted) {
4576                         if (*name == 't')
4577                                 *copyto++ = '\t';
4578                         else if (*name == sep)
4579                                 *copyto++ = sep;
4580                         else {
4581                                 /* Something else is quoted, so preserve the quote. */
4582                                 *copyto++ = '\\';
4583                                 *copyto++ = *name;
4584                         }
4585                         quoted = FALSE;
4586                 } else if (*name == '\\')
4587                         quoted = TRUE;
4588                 else if (*name == sep)
4589                         break;
4590                 else
4591                         *copyto++ = *name;
4592         }
4593
4594         /* Terminate copied string. */
4595         *copyto = '\0';
4596         return name;
4597 }
4598
4599 /* Look at the argument of --regex or --no-regex and do the right
4600    thing.  Same for each line of a regexp file. */
4601 void analyse_regex(regex_arg)
4602 char *regex_arg;
4603 {
4604         if (regex_arg == NULL) {
4605                 free_patterns();        /* --no-regex: remove existing regexps */
4606                 return;
4607         }
4608         /* A real --regexp option or a line in a regexp file. */
4609         switch (regex_arg[0]) {
4610                 /* Comments in regexp file or null arg to --regex. */
4611         case '\0':
4612         case ' ':
4613         case '\t':
4614                 break;
4615
4616                 /* Read a regex file.  This is recursive and may result in a
4617                    loop, which will stop when the file descriptors are exhausted. */
4618         case '@':
4619                 {
4620                         FILE *regexfp;
4621                         linebuffer regexbuf;
4622                         char *regexfile = regex_arg + 1;
4623
4624                         /* regexfile is a file containing regexps, one per line. */
4625                         regexfp = fopen(regexfile, "r");
4626                         if (regexfp == NULL) {
4627                                 pfatal(regexfile);
4628                                 return;
4629                         }
4630                         initbuffer(&regexbuf);
4631                         while (readline_internal(&regexbuf, regexfp) > 0)
4632                                 analyse_regex(regexbuf.buffer);
4633                         free(regexbuf.buffer);
4634                         fclose(regexfp);
4635                 }
4636                 break;
4637
4638                 /* Regexp to be used for a specific language only. */
4639         case '{':
4640                 {
4641                         language *lang;
4642                         char *lang_name = regex_arg + 1;
4643                         char *cp;
4644
4645                         for (cp = lang_name; *cp != '}'; cp++)
4646                                 if (*cp == '\0') {
4647                                         error
4648                                             ("unterminated language name in regex: %s",
4649                                              regex_arg);
4650                                         return;
4651                                 }
4652                         *cp = '\0';
4653                         lang = get_language_from_name(lang_name);
4654                         if (lang == NULL)
4655                                 return;
4656                         add_regex(cp + 1, lang);
4657                 }
4658                 break;
4659
4660                 /* Regexp to be used for any language. */
4661         default:
4662                 add_regex(regex_arg, NULL);
4663                 break;
4664         }
4665 }
4666
4667 /* Turn a name, which is an ed-style (but Emacs syntax) regular
4668    expression, into a real regular expression by compiling it. */
4669 void add_regex(regexp_pattern, lang)
4670 char *regexp_pattern;
4671 language *lang;
4672 {
4673         char *name;
4674         const char *err;
4675         struct re_pattern_buffer *patbuf;
4676         pattern *pp;
4677
4678         if (regexp_pattern[strlen(regexp_pattern) - 1] != regexp_pattern[0]) {
4679                 error("%s: unterminated regexp", regexp_pattern);
4680                 return;
4681         }
4682         name = scan_separators(regexp_pattern);
4683         if (regexp_pattern[0] == '\0') {
4684                 error("null regexp", (char *)NULL);
4685                 return;
4686         }
4687         (void)scan_separators(name);
4688
4689         patbuf = xnew(1, struct re_pattern_buffer);
4690         patbuf->translate = NULL;
4691         patbuf->fastmap = NULL;
4692         patbuf->buffer = NULL;
4693         patbuf->allocated = 0;
4694
4695         err =
4696             re_compile_pattern(regexp_pattern, strlen(regexp_pattern), patbuf);
4697         if (err != NULL) {
4698                 error("%s while compiling pattern", err);
4699                 return;
4700         }
4701
4702         pp = p_head;
4703         p_head = xnew(1, pattern);
4704         p_head->regex = savestr(regexp_pattern);
4705         p_head->p_next = pp;
4706         p_head->language = lang;
4707         p_head->pattern = patbuf;
4708         p_head->name_pattern = savestr(name);
4709         p_head->error_signaled = FALSE;
4710 }
4711
4712 /*
4713  * Do the substitutions indicated by the regular expression and
4714  * arguments.
4715  */
4716 char *substitute PP((char *in, char *out, struct re_registers * regs));
4717 char *substitute(in, out, regs)
4718 char *in, *out;
4719 struct re_registers *regs;
4720 {
4721         char *result, *t;
4722         int size, dig, diglen;
4723
4724         result = NULL;
4725         size = strlen(out);
4726
4727         /* Pass 1: figure out how much to allocate by finding all \N strings. */
4728         if (out[size - 1] == '\\')
4729                 fatal("pattern error in \"%s\"", out);
4730         for (t = etags_strchr(out, '\\');
4731              t != NULL; t = etags_strchr(t + 2, '\\'))
4732                 if (isdigit(t[1])) {
4733                         dig = t[1] - '0';
4734                         diglen = regs->end[dig] - regs->start[dig];
4735                         size += diglen - 2;
4736                 } else
4737                         size -= 1;
4738
4739         /* Allocate space and do the substitutions. */
4740         result = xnew(size + 1, char);
4741
4742         for (t = result; *out != '\0'; out++)
4743                 if (*out == '\\' && isdigit(*++out)) {
4744                         /* Using "dig2" satisfies my debugger.  Bleah. */
4745                         dig = *out - '0';
4746                         diglen = regs->end[dig] - regs->start[dig];
4747                         strncpy(t, in + regs->start[dig], diglen);
4748                         t += diglen;
4749                 } else
4750                         *t++ = *out;
4751         *t = '\0';
4752
4753         if (DEBUG && (t > result + size || t - result != strlen(result)))
4754                 abort();
4755
4756         return result;
4757 }
4758
4759 /* Deallocate all patterns. */
4760 void free_patterns()
4761 {
4762         pattern *pp;
4763         while (p_head != NULL) {
4764                 pp = p_head->p_next;
4765                 free(p_head->regex);
4766                 free(p_head->name_pattern);
4767                 free(p_head);
4768                 p_head = pp;
4769         }
4770         return;
4771 }
4772 \f
4773 #endif                          /* ETAGS_REGEXPS */
4774 /* Initialize a linebuffer for use */
4775 void initbuffer(lbp)
4776 linebuffer *lbp;
4777 {
4778         lbp->size = 200;
4779         lbp->buffer = xnew(200, char);
4780 }
4781
4782 /*
4783  * Read a line of text from `stream' into `lbp', excluding the
4784  * newline or CR-NL, if any.  Return the number of characters read from
4785  * `stream', which is the length of the line including the newline.
4786  *
4787  * On DOS or Windows we do not count the CR character, if any, before the
4788  * NL, in the returned length; this mirrors the behavior of emacs on those
4789  * platforms (for text files, it translates CR-NL to NL as it reads in the
4790  * file).
4791  */
4792 long readline_internal(lbp, stream)
4793 linebuffer *lbp;
4794 register FILE *stream;
4795 {
4796         char *buffer = lbp->buffer;
4797         register char *p = lbp->buffer;
4798         register char *pend;
4799         int chars_deleted;
4800
4801         pend = p + lbp->size;   /* Separate to avoid 386/IX compiler bug.  */
4802
4803         while (1) {
4804                 register int c = getc(stream);
4805                 if (p == pend) {
4806                         /* We're at the end of linebuffer: expand it. */
4807                         lbp->size *= 2;
4808                         buffer = xrnew(buffer, lbp->size, char);
4809                         p += buffer - lbp->buffer;
4810                         pend = buffer + lbp->size;
4811                         lbp->buffer = buffer;
4812                 }
4813                 if (c == EOF) {
4814                         *p = '\0';
4815                         chars_deleted = 0;
4816                         break;
4817                 }
4818                 if (c == '\n') {
4819                         if (p > buffer && p[-1] == '\r') {
4820                                 p -= 1;
4821                                 chars_deleted = 2;
4822                         } else {
4823                                 chars_deleted = 1;
4824                         }
4825                         *p = '\0';
4826                         break;
4827                 }
4828                 *p++ = c;
4829         }
4830         lbp->len = p - buffer;
4831
4832         return lbp->len + chars_deleted;
4833 }
4834
4835 /*
4836  * Like readline_internal, above, but in addition try to match the
4837  * input line against relevant regular expressions.
4838  */
4839 long readline(lbp, stream)
4840 linebuffer *lbp;
4841 FILE *stream;
4842 {
4843         /* Read new line. */
4844         long result = readline_internal(lbp, stream);
4845 #ifdef ETAGS_REGEXPS
4846         int match;
4847         pattern *pp;
4848
4849         /* Match against relevant patterns. */
4850         if (lbp->len > 0)
4851                 for (pp = p_head; pp != NULL; pp = pp->p_next) {
4852                         /* Only use generic regexps or those for the current language. */
4853                         if (pp->language != NULL && pp->language != curlang)
4854                                 continue;
4855
4856                         match =
4857                             re_match(pp->pattern, lbp->buffer, lbp->len, 0,
4858                                      &pp->regs);
4859                         switch (match) {
4860                         case -2:
4861                                 /* Some error. */
4862                                 if (!pp->error_signaled) {
4863                                         error("error while matching \"%s\"",
4864                                               pp->regex);
4865                                         pp->error_signaled = TRUE;
4866                                 }
4867                                 break;
4868                         case -1:
4869                                 /* No match. */
4870                                 break;
4871                         default:
4872                                 /* Match occurred.  Construct a tag. */
4873                                 if (pp->name_pattern[0] != '\0') {
4874                                         /* Make a named tag. */
4875                                         char *name = substitute(lbp->buffer,
4876                                                                 pp->
4877                                                                 name_pattern,
4878                                                                 &pp->regs);
4879                                         if (name != NULL)
4880                                                 pfnote(name, TRUE, lbp->buffer,
4881                                                        match, lineno,
4882                                                        linecharno);
4883                                 } else {
4884                                         /* Make an unnamed tag. */
4885                                         pfnote((char *)NULL, TRUE,
4886                                                lbp->buffer, match, lineno,
4887                                                linecharno);
4888                                 }
4889                                 break;
4890                         }
4891                 }
4892 #endif                          /* ETAGS_REGEXPS */
4893
4894         return result;
4895 }
4896 \f
4897 /*
4898  * Return a pointer to a space of size strlen(cp)+1 allocated
4899  * with xnew where the string CP has been copied.
4900  */
4901 char *savestr(cp)
4902 char *cp;
4903 {
4904         return savenstr(cp, strlen(cp));
4905 }
4906
4907 /*
4908  * Return a pointer to a space of size LEN+1 allocated with xnew where
4909  * the string CP has been copied for at most the first LEN characters.
4910  */
4911 char *savenstr(cp, len)
4912 char *cp;
4913 int len;
4914 {
4915         register char *dp;
4916
4917         dp = xnew(len + 1, char);
4918         strncpy(dp, cp, len);
4919         dp[len] = '\0';
4920         return dp;
4921 }
4922
4923 /*
4924  * Return the ptr in sp at which the character c last
4925  * appears; NULL if not found
4926  *
4927  * Identical to System V strrchr, included for portability.
4928  */
4929 char *etags_strrchr(sp, c)
4930 register char *sp;
4931 register int c;
4932 {
4933         register char *r;
4934
4935         r = NULL;
4936         do {
4937                 if (*sp == c)
4938                         r = sp;
4939         } while (*sp++);
4940         return r;
4941 }
4942
4943 /*
4944  * Return the ptr in sp at which the character c first
4945  * appears; NULL if not found
4946  *
4947  * Identical to System V strchr, included for portability.
4948  */
4949 char *etags_strchr(sp, c)
4950 register char *sp;
4951 register int c;
4952 {
4953         do {
4954                 if (*sp == c)
4955                         return sp;
4956         } while (*sp++);
4957         return NULL;
4958 }
4959
4960 /* Skip spaces, return new pointer. */
4961 char *skip_spaces(cp)
4962 char *cp;
4963 {
4964         while (isspace(*cp))    /* isspace('\0')==FALSE */
4965                 cp++;
4966         return cp;
4967 }
4968
4969 /* Skip non spaces, return new pointer. */
4970 char *skip_non_spaces(cp)
4971 char *cp;
4972 {
4973         while (!iswhite(*cp))   /* iswhite('\0')==TRUE */
4974                 cp++;
4975         return cp;
4976 }
4977
4978 /* Print error message and exit.  */
4979 void fatal(s1, s2)
4980 char *s1, *s2;
4981 {
4982         error(s1, s2);
4983         exit(BAD);
4984 }
4985
4986 void pfatal(s1)
4987 char *s1;
4988 {
4989         perror(s1);
4990         exit(BAD);
4991 }
4992
4993 void suggest_asking_for_help()
4994 {
4995         fprintf(stderr, "\tTry `%s %s' for a complete list of options.\n",
4996                 progname,
4997 #ifdef LONG_OPTIONS
4998                 "--help"
4999 #else
5000                 "-h"
5001 #endif
5002             );
5003         exit(BAD);
5004 }
5005
5006 /* Print error message.  `s1' is printf control string, `s2' is arg for it. */
5007 void error(s1, s2)
5008 const char *s1, *s2;
5009 {
5010         fprintf(stderr, "%s: ", progname);
5011         fprintf(stderr, s1, s2);
5012         fprintf(stderr, "\n");
5013 }
5014
5015 /* Return a newly-allocated string whose contents
5016    concatenate those of s1, s2, s3.  */
5017 char *concat(s1, s2, s3)
5018 char *s1, *s2, *s3;
5019 {
5020         int len1 = strlen(s1), len2 = strlen(s2), len3 = strlen(s3);
5021         char *result = xnew(len1 + len2 + len3 + 1, char);
5022
5023         strcpy(result, s1);
5024         strcpy(result + len1, s2);
5025         strcpy(result + len1 + len2, s3);
5026         result[len1 + len2 + len3] = '\0';
5027
5028         return result;
5029 }
5030 \f
5031 /* Does the same work as the system V getcwd, but does not need to
5032    guess the buffer size in advance. */
5033 char *etags_getcwd()
5034 {
5035 #ifdef HAVE_GETCWD
5036         int bufsize = 200;
5037         char *path = xnew(bufsize, char);
5038
5039         while (getcwd(path, bufsize) == NULL) {
5040                 if (errno != ERANGE)
5041                         pfatal("getcwd");
5042                 bufsize *= 2;
5043                 free(path);
5044                 path = xnew(bufsize, char);
5045         }
5046
5047         canonicalize_filename(path);
5048         return path;
5049
5050 #else                           /* not HAVE_GETCWD */
5051         linebuffer path;
5052         FILE *pipe;
5053
5054         initbuffer(&path);
5055         pipe = (FILE *) popen("pwd 2>/dev/null", "r");
5056         if (pipe == NULL || readline_internal(&path, pipe) == 0)
5057                 pfatal("pwd");
5058         pclose(pipe);
5059
5060         return path.buffer;
5061 #endif                          /* not HAVE_GETCWD */
5062 }
5063
5064 /* Return a newly allocated string containing the file name of FILE
5065    relative to the absolute directory DIR (which should end with a slash). */
5066 char *relative_filename(file, dir)
5067 char *file, *dir;
5068 {
5069         char *fp, *dp, *afn, *res;
5070         int i;
5071         ssize_t res_left;
5072
5073         /* Find the common root of file and dir (with a trailing slash). */
5074         afn = absolute_filename(file, cwd);
5075         fp = afn;
5076         dp = dir;
5077         while (*fp++ == *dp++)
5078                 continue;
5079         fp--, dp--;             /* back to the first differing char */
5080         do                      /* look at the equal chars until '/' */
5081                 fp--, dp--;
5082         while (*fp != '/');
5083         fp ++; /* Advance past the '/' */
5084
5085         /* Build a sequence of "../" strings for the resulting relative file name. */
5086         i = 0;
5087         while ((dp = etags_strchr(dp + 1, '/')) != NULL)
5088                 i += 1;
5089         res_left = 3 * i + strlen(fp);
5090         res = xnew( res_left + 1, char);
5091         res[0] = '\0';
5092         for ( ; i-- > 0 ; res_left -= 4 )
5093                 strncat(res, "../", res_left );
5094
5095         /* Add the file name relative to the common root of file and dir. */
5096         strncat(res, fp, res_left);
5097         free(afn);
5098
5099         return res;
5100 }
5101
5102 /* Return a newly allocated string containing the absolute file name
5103    of FILE given DIR (which should end with a slash). */
5104 char *absolute_filename(file, dir)
5105 char *file, *dir;
5106 {
5107         char *slashp, *cp, *res;
5108
5109         if (filename_is_absolute(file))
5110                 res = savestr(file);
5111         else
5112                 res = concat(dir, file, "");
5113
5114         /* Delete the "/dirname/.." and "/." substrings. */
5115         slashp = etags_strchr(res, '/');
5116         while (slashp != NULL && slashp[0] != '\0') {
5117                 if (slashp[1] == '.') {
5118                         if (slashp[2] == '.'
5119                             && (slashp[3] == '/' || slashp[3] == '\0')) {
5120                                 cp = slashp;
5121                                 do
5122                                         cp--;
5123                                 while (cp >= res && !filename_is_absolute(cp));
5124                                 if (cp < res)
5125                                         cp = slashp;    /* the absolute name begins with "/.." */
5126                                 strcpy(cp, slashp + 3);
5127                                 slashp = cp;
5128                                 continue;
5129                         } else if (slashp[2] == '/' || slashp[2] == '\0') {
5130                                 strcpy(slashp, slashp + 2);
5131                                 continue;
5132                         }
5133                 }
5134
5135                 slashp = etags_strchr(slashp + 1, '/');
5136         }
5137
5138         if (res[0] == '\0')
5139                 return savestr("/");
5140         else
5141                 return res;
5142 }
5143
5144 /* Return a newly allocated string containing the absolute
5145    file name of dir where FILE resides given DIR (which should
5146    end with a slash). */
5147 char *absolute_dirname(file, dir)
5148 char *file, *dir;
5149 {
5150         char *slashp, *res;
5151         char save;
5152
5153         canonicalize_filename(file);
5154         slashp = etags_strrchr(file, '/');
5155         if (slashp == NULL)
5156                 return savestr(dir);
5157         save = slashp[1];
5158         slashp[1] = '\0';
5159         res = absolute_filename(file, dir);
5160         slashp[1] = save;
5161
5162         return res;
5163 }
5164
5165 /* Whether the argument string is an absolute file name.  The argument
5166    string must have been canonicalized with canonicalize_filename. */
5167 bool filename_is_absolute(fn)
5168 char *fn;
5169 {
5170         return (fn[0] == '/');
5171 }
5172
5173 /* Translate backslashes into slashes.  Works in place. */
5174 void canonicalize_filename(fn)
5175 register char *fn;
5176 {
5177         /* No action. */
5178 }
5179
5180 /* Increase the size of a linebuffer. */
5181 void grow_linebuffer(lbp, toksize)
5182 linebuffer *lbp;
5183 int toksize;
5184 {
5185         while (lbp->size < toksize)
5186                 lbp->size *= 2;
5187         lbp->buffer = xrnew(lbp->buffer, lbp->size, char);
5188 }
5189
5190 /* Like malloc but get fatal error if memory is exhausted.  */
5191 long *xmalloc(size)
5192 unsigned int size;
5193 {
5194         long *result = (long *)malloc(size);
5195         if (result == NULL)
5196                 fatal("virtual memory exhausted", (char *)NULL);
5197         return result;
5198 }
5199
5200 long *xrealloc(ptr, size)
5201 char *ptr;
5202 unsigned int size;
5203 {
5204         long *result = (long *)realloc(ptr, size);
5205         if (result == NULL)
5206                 fatal("virtual memory exhausted", (char *)NULL);
5207         return result;
5208 }