Coverity fixes, etc from Nelson
[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 (streq(file, tagfile) && !streq(tagfile, "-")) {
1200                 error("skipping inclusion of %s in self.", file);
1201                 return;
1202         }
1203         inf = fopen(file, "r");
1204         if (stat(file, &stat_buf) == 0 && !S_ISREG(stat_buf.st_mode)) {
1205                 error("skipping %s: it is not a regular file.", file);
1206                 if( inf != NULL)
1207                         fclose(inf);
1208                 return;
1209         }
1210         if (inf == NULL) {
1211                 perror(file);
1212                 return;
1213         }
1214
1215         find_entries(file, inf);
1216
1217         if (!CTAGS) {
1218                 char *filename;
1219
1220                 if (filename_is_absolute(file)) {
1221                         /* file is an absolute file name.  Canonicalise it. */
1222                         filename = absolute_filename(file, cwd);
1223                 } else {
1224                         /* file is a file name relative to cwd.  Make it relative
1225                            to the directory of the tags file. */
1226                         filename = relative_filename(file, tagfiledir);
1227                 }
1228 #ifdef OO_BROWSER
1229                 if (oo_browser_format)
1230                         fprintf(tagf, "\f\n%s\n", filename);
1231                 else
1232 #endif
1233                         fprintf(tagf, "\f\n%s,%d\n", filename,
1234                                 total_size_of_entries(head));
1235                 free(filename);
1236                 put_entries(head);
1237                 free_tree(head);
1238                 head = NULL;
1239         }
1240 }
1241
1242 /*
1243  * This routine sets up the boolean pseudo-functions which work
1244  * by setting boolean flags dependent upon the corresponding character.
1245  * Every char which is NOT in that string is not a white char.  Therefore,
1246  * all of the array "_wht" is set to FALSE, and then the elements
1247  * subscripted by the chars in "white" are set to TRUE.  Thus "_wht"
1248  * of a char is TRUE if it is the string "white", else FALSE.
1249  */
1250 void init()
1251 {
1252         register char *sp;
1253         register int i;
1254
1255         for (i = 0; i < CHARS; i++)
1256                 iswhite(i) = notinname(i) = begtoken(i) = intoken(i) =
1257                     endtoken(i) = FALSE;
1258         for (sp = white; *sp != '\0'; sp++)
1259                 iswhite(*sp) = TRUE;
1260         for (sp = nonam; *sp != '\0'; sp++)
1261                 notinname(*sp) = TRUE;
1262         for (sp = begtk; *sp != '\0'; sp++)
1263                 begtoken(*sp) = TRUE;
1264         for (sp = midtk; *sp != '\0'; sp++)
1265                 intoken(*sp) = TRUE;
1266         for (sp = endtk; *sp != '\0'; sp++)
1267                 endtoken(*sp) = TRUE;
1268         iswhite('\0') = iswhite('\n');
1269         notinname('\0') = notinname('\n');
1270         begtoken('\0') = begtoken('\n');
1271         intoken('\0') = intoken('\n');
1272         endtoken('\0') = endtoken('\n');
1273 }
1274
1275 /*
1276  * This routine opens the specified file and calls the function
1277  * which finds the function and type definitions.
1278  */
1279 node *last_node = NULL;
1280
1281 void find_entries(file, inf)
1282 char *file;
1283 FILE *inf;
1284 {
1285         char *cp;
1286         language *lang;
1287         node *old_last_node;
1288
1289         curfile = savestr(file);
1290
1291         /* If user specified a language, use it. */
1292         lang = forced_lang;
1293         if (lang != NULL && lang->function != NULL) {
1294                 curlang = lang;
1295                 lang->function(inf);
1296                 free(curfile);
1297                 fclose(inf);
1298                 return;
1299         }
1300
1301         cp = etags_strrchr(file, '.');
1302         if (cp != NULL) {
1303                 cp += 1;
1304                 lang = get_language_from_suffix(cp);
1305                 if (lang != NULL && lang->function != NULL) {
1306                         curlang = lang;
1307                         lang->function(inf);
1308                         free(curfile);
1309                         fclose(inf);
1310                         return;
1311                 }
1312         }
1313
1314         /* Look for sharp-bang as the first two characters. */
1315         if (readline_internal(&lb, inf) > 0
1316             && lb.len >= 2 && lb.buffer[0] == '#' && lb.buffer[1] == '!') {
1317                 char *lp;
1318
1319                 /* Set lp to point at the first char after the last slash in the
1320                    line or, if no slashes, at the first nonblank.  Then set cp to
1321                    the first successive blank and terminate the string. */
1322                 lp = etags_strrchr(lb.buffer + 2, '/');
1323                 if (lp != NULL)
1324                         lp += 1;
1325                 else
1326                         lp = skip_spaces(lb.buffer + 2);
1327                 cp = skip_non_spaces(lp);
1328                 *cp = '\0';
1329
1330                 if (strlen(lp) > 0) {
1331                         lang = get_language_from_interpreter(lp);
1332                         if (lang != NULL && lang->function != NULL) {
1333                                 curlang = lang;
1334                                 lang->function(inf);
1335                                 fclose(inf);
1336                                 free(curfile);
1337                                 return;
1338                         }
1339                 }
1340         }
1341         rewind(inf);
1342
1343         /* Try Fortran. */
1344         old_last_node = last_node;
1345         curlang = get_language_from_name("fortran");
1346         Fortran_functions(inf);
1347
1348         /* No Fortran entries found.  Try C. */
1349         if (old_last_node == last_node) {
1350                 rewind(inf);
1351                 curlang = get_language_from_name(cplusplus ? "c++" : "c");
1352                 default_C_entries(inf);
1353         }
1354         free(curfile);
1355         fclose(inf);
1356         return;
1357 }
1358 \f
1359 /* Record a tag. */
1360 void pfnote(name, is_func, linestart, linelen, lno, cno)
1361 char *name;                     /* tag name, or NULL if unnamed */
1362 bool is_func;                   /* tag is a function */
1363 char *linestart;                /* start of the line where tag is */
1364 int linelen;                    /* length of the line where tag is */
1365 int lno;                        /* line number */
1366 long cno;                       /* character number */
1367 {
1368         register node *np;
1369
1370         if (CTAGS && name == NULL)
1371                 return;
1372
1373         np = xnew(1, node);
1374
1375         /* If ctags mode, change name "main" to M<thisfilename>. */
1376         if (CTAGS && !cxref_style && streq(name, "main")) {
1377                 register char *fp = etags_strrchr(curfile, '/');
1378                 np->name = concat("M", fp == 0 ? curfile : fp + 1, "");
1379                 fp = etags_strrchr(np->name, '.');
1380                 if (fp && fp[1] != '\0' && fp[2] == '\0')
1381                         fp[0] = 0;
1382         } else
1383                 np->name = name;
1384         np->been_warned = FALSE;
1385         np->file = curfile;
1386         np->is_func = is_func;
1387         np->lno = lno;
1388         /* Our char numbers are 0-base, because of C language tradition?
1389            ctags compatibility?  old versions compatibility?   I don't know.
1390            Anyway, since emacs's are 1-base we expect etags.el to take care
1391            of the difference.  If we wanted to have 1-based numbers, we would
1392            uncomment the +1 below. */
1393         np->cno = cno /* + 1 */ ;
1394         np->left = np->right = NULL;
1395         if (CTAGS && !cxref_style) {
1396                 if (strlen(linestart) < 50)
1397                         np->pat = concat(linestart, "$", "");
1398                 else
1399                         np->pat = savenstr(linestart, 50);
1400         } else
1401                 np->pat = savenstr(linestart, linelen);
1402
1403 #ifdef OO_BROWSER
1404         if (oo_browser_format)
1405                 np->construct = oo_browser_construct;
1406         oo_browser_construct = C_NULL;
1407         oo_browser_check_and_clear_structtype();
1408 #endif
1409
1410         add_node(np, &head);
1411 }
1412
1413 /* Date: Wed, 22 Jan 1997 02:56:31 -0500 [last amended 18 Sep 1997]
1414  * From: Sam Kendall <kendall@mv.mv.com>
1415  * Subject: Proposal for firming up the TAGS format specification
1416  * To: F.Potorti@cnuce.cnr.it
1417  *
1418  * pfnote should emit the optimized form [unnamed tag] only if:
1419  *  1. name does not contain any of the characters " \t\r\n(),;";
1420  *  2. linestart contains name as either a rightmost, or rightmost but
1421  *     one character, substring;
1422  *  3. the character, if any, immediately before name in linestart must
1423  *     be one of the characters " \t(),;";
1424  *  4. the character, if any, immediately after name in linestart must
1425  *     also be one of the characters " \t(),;".
1426  *
1427  * The real implementation uses the notinname() macro, which recognises
1428  * characters slightly different form " \t\r\n(),;".  See the variable
1429  * `nonam'.
1430  */
1431 #define traditional_tag_style TRUE
1432 void new_pfnote(name, namelen, is_func, linestart, linelen, lno, cno)
1433 char *name;                     /* tag name, or NULL if unnamed */
1434 int namelen;                    /* tag length */
1435 bool is_func;                   /* tag is a function */
1436 char *linestart;                /* start of the line where tag is */
1437 int linelen;                    /* length of the line where tag is */
1438 int lno;                        /* line number */
1439 long cno;                       /* character number */
1440 {
1441         register char *cp;
1442         bool named;
1443
1444         named = TRUE;
1445         if (!CTAGS) {
1446                 for (cp = name; !notinname(*cp); cp++)
1447                         continue;
1448                 if (*cp == '\0') {      /* rule #1 */
1449                         cp = linestart + linelen - namelen;
1450                         if (notinname(linestart[linelen - 1]))
1451                                 cp -= 1;        /* rule #4 */
1452 #ifdef OO_BROWSER
1453                         if (!oo_browser_format && cp >= linestart       /* rule #2 */
1454 #else
1455                         if (cp >= linestart     /* rule #2 */
1456 #endif
1457                             && (cp == linestart || notinname(cp[-1]))   /* rule #3 */
1458                             &&strneq(name, cp, namelen))        /* rule #2 */
1459                                 named = FALSE;  /* use unnamed tag */
1460                 }
1461         }
1462
1463         if (named)
1464                 name = savenstr(name, namelen);
1465         else
1466                 name = NULL;
1467         pfnote(name, is_func, linestart, linelen, lno, cno);
1468 }
1469
1470 /*
1471  * free_tree ()
1472  *      recurse on left children, iterate on right children.
1473  */
1474 void free_tree(np)
1475 register node *np;
1476 {
1477         while (np) {
1478                 register node *node_right = np->right;
1479                 free_tree(np->left);
1480                 if (np->name != NULL)
1481                         free(np->name);
1482                 free(np->pat);
1483                 free(np);
1484                 np = node_right;
1485         }
1486 }
1487
1488 /*
1489  * add_node ()
1490  *      Adds a node to the tree of nodes.  In etags mode, we don't keep
1491  *      it sorted; we just keep a linear list.  In ctags mode, maintain
1492  *      an ordered tree, with no attempt at balancing.
1493  *
1494  *      add_node is the only function allowed to add nodes, so it can
1495  *      maintain state.
1496  */
1497 void add_node(np, cur_node_p)
1498 node *np, **cur_node_p;
1499 {
1500         register int dif;
1501         register node *cur_node = *cur_node_p;
1502
1503         if (cur_node == NULL) {
1504                 *cur_node_p = np;
1505                 last_node = np;
1506                 return;
1507         }
1508
1509         if (!CTAGS) {
1510                 /* Etags Mode */
1511                 if (last_node == NULL)
1512                         fatal("internal error in add_node", (char *)NULL);
1513                 last_node->right = np;
1514                 last_node = np;
1515         } else {
1516                 /* Ctags Mode */
1517                 dif = strcmp(np->name, cur_node->name);
1518
1519                 /*
1520                  * If this tag name matches an existing one, then
1521                  * do not add the node, but maybe print a warning.
1522                  */
1523                 if (!dif) {
1524                         if (streq(np->file, cur_node->file)) {
1525                                 if (!no_warnings) {
1526                                         fprintf(stderr,
1527                                                 "Duplicate entry in file %s, line %d: %s\n",
1528                                                 np->file, lineno, np->name);
1529                                         fprintf(stderr,
1530                                                 "Second entry ignored\n");
1531                                 }
1532                         } else if (!cur_node->been_warned && !no_warnings) {
1533                                 fprintf
1534                                     (stderr,
1535                                      "Duplicate entry in files %s and %s: %s (Warning only)\n",
1536                                      np->file, cur_node->file, np->name);
1537                                 cur_node->been_warned = TRUE;
1538                         }
1539                         return;
1540                 }
1541
1542                 /* Actually add the node */
1543                 add_node(np, dif < 0 ? &cur_node->left : &cur_node->right);
1544         }
1545 }
1546 \f
1547 #ifdef OO_BROWSER
1548 /* Default class name for the current OO-Browser tag. */
1549 static char *oo_browser_class;
1550 /* Prefix character to use in OO-Browser listings for the current tag. */
1551 static char oo_browser_prefix;
1552 #endif
1553
1554 void put_entries(node * np)
1555 {
1556         register char *sp;
1557
1558         if (np == NULL)
1559                 return;
1560
1561         /* Output subentries that precede this one */
1562         put_entries(np->left);
1563
1564         /* Output this entry */
1565
1566         if (!CTAGS) {
1567 #ifdef OO_BROWSER
1568                 if (oo_browser_format) {
1569                         /* Omit C++ `class' and `method' entries as well as Objective-C
1570                            entries from this OO-Browser tags file since the browser handles
1571                            them independently of this file.  Omit `extern' variable declarations
1572                            as they are unused by the OO-Browser. */
1573                         if (np->construct != C_CLASS
1574                             && np->construct != C_METHOD
1575                             && np->construct != C_EXTERN
1576                             && np->construct != C_OBJC) {
1577                                 oo_browser_class =
1578                                     oo_browser_default_classes[np->construct];
1579                                 switch (np->construct) {
1580                                 case C_CONSTANT:
1581                                 case C_ENUMERATION:
1582                                 case C_ENUM_LABEL:
1583                                 case C_STRUCTURE:
1584                                 case C_TYPE:
1585                                 case C_UNION:
1586                                 case C_VARIABLE:
1587                                         oo_browser_prefix = '=';
1588                                         break;
1589                                 case C_FUNCTION:
1590                                 case C_MACRO:
1591                                         oo_browser_prefix = '-';
1592                                         break;
1593                                 default:
1594                                         break;
1595                                 }
1596                                 if (np->name != NULL)
1597                                         fprintf(tagf, "%s@%c %s@%s\n",
1598                                                 oo_browser_class,
1599                                                 oo_browser_prefix, np->name,
1600                                                 np->pat);
1601                                 else
1602                                         fprintf(tagf, "%s@%c ???@%s\n",
1603                                                 oo_browser_class,
1604                                                 oo_browser_prefix, np->pat);
1605                         }
1606                 } else {
1607 #endif
1608                         if (np->name != NULL)
1609                                 fprintf(tagf, "%s\177%s\001%d,%ld\n",
1610                                         np->pat, np->name, np->lno, np->cno);
1611                         else
1612                                 fprintf(tagf, "%s\177%d,%ld\n",
1613                                         np->pat, np->lno, np->cno);
1614 #ifdef OO_BROWSER
1615                 }
1616 #endif
1617         } else {
1618                 if (np->name == NULL)
1619                         error("internal error: NULL name in ctags mode.",
1620                               (char *)NULL);
1621
1622                 if (cxref_style) {
1623                         if (vgrind_style)
1624                                 fprintf(stdout, "%s %s %d\n",
1625                                         np->name, np->file,
1626                                         (np->lno + 63) / 64);
1627                         else
1628                                 fprintf(stdout, "%-16s %3d %-16s %s\n",
1629                                         np->name, np->lno, np->file, np->pat);
1630                 } else {
1631                         fprintf(tagf, "%s\t%s\t", np->name, np->file);
1632
1633                         if (np->is_func) {      /* a function */
1634                                 putc(searchar, tagf);
1635                                 putc('^', tagf);
1636
1637                                 for (sp = np->pat; *sp; sp++) {
1638                                         if (*sp == '\\' || *sp == searchar)
1639                                                 putc('\\', tagf);
1640                                         putc(*sp, tagf);
1641                                 }
1642                                 putc(searchar, tagf);
1643                         } else {        /* a typedef; text pattern inadequate */
1644                                 fprintf(tagf, "%d", np->lno);
1645                         }
1646                         putc('\n', tagf);
1647                 }
1648         }
1649
1650         /* Output subentries that follow this one */
1651         put_entries(np->right);
1652 }
1653
1654 /* Length of a number's decimal representation. */
1655 int number_len PP((long num));
1656 int number_len(num)
1657 long num;
1658 {
1659         int len = 1;
1660         while ((num /= 10) > 0)
1661                 len += 1;
1662         return len;
1663 }
1664
1665 /*
1666  * Return total number of characters that put_entries will output for
1667  * the nodes in the subtree of the specified node.  Works only if
1668  * we are not ctags, but called only in that case.  This count
1669  * is irrelevant with the new tags.el, but is still supplied for
1670  * backward compatibility.
1671  */
1672 int total_size_of_entries(np)
1673 register node *np;
1674 {
1675         register int total;
1676
1677         if (np == NULL)
1678                 return 0;
1679
1680         for (total = 0; np != NULL; np = np->right) {
1681                 /* Count left subentries. */
1682                 total += total_size_of_entries(np->left);
1683
1684                 /* Count this entry */
1685                 total += strlen(np->pat) + 1;
1686                 total +=
1687                     number_len((long)np->lno) + 1 + number_len(np->cno) + 1;
1688                 if (np->name != NULL)
1689                         total += 1 + strlen(np->name);  /* \001name */
1690         }
1691
1692         return total;
1693 }
1694 \f
1695 /*
1696  * The C symbol tables.
1697  */
1698 enum sym_type {
1699         st_none,
1700         st_C_objprot, st_C_objimpl, st_C_objend,
1701         st_C_gnumacro,
1702         st_C_ignore,
1703         st_C_javastruct,
1704         st_C_struct, st_C_enum, st_C_define, st_C_typedef, st_C_typespec,
1705         st_C_const
1706 #ifdef OO_BROWSER
1707             , st_C_union, st_C_class, st_C_extern, st_C_inline
1708 #endif
1709 };
1710
1711 /* Feed stuff between (but not including) %[ and %] lines to:
1712       gperf -c -k 1,3 -o -p -r -t
1713 %[
1714 struct C_stab_entry { char *name; int c_ext; enum sym_type type; }
1715 %%
1716 @interface,     0,      st_C_objprot
1717 @protocol,      0,      st_C_objprot
1718 @implementation,0,      st_C_objimpl
1719 @end,           0,      st_C_objend
1720 import,         C_JAVA, st_C_ignore
1721 package,        C_JAVA, st_C_ignore
1722 friend,         C_PLPL, st_C_ignore
1723 extends,        C_JAVA, st_C_javastruct
1724 implements,     C_JAVA, st_C_javastruct
1725 interface,      C_JAVA, st_C_struct
1726 class,  C_PLPL, st_C_class
1727 namespace,      C_PLPL, st_C_struct
1728 domain, C_STAR, st_C_struct
1729 union,  0,      st_C_union
1730 struct, 0,      st_C_struct
1731 enum,   0,      st_C_enum
1732 typedef,        0,      st_C_typedef
1733 define, 0,      st_C_define
1734 inline,         0,      st_C_inline
1735 bool,           C_PLPL, st_C_typespec
1736 long,   0,      st_C_typespec
1737 short,  0,      st_C_typespec
1738 int,    0,      st_C_typespec
1739 char,   0,      st_C_typespec
1740 float,  0,      st_C_typespec
1741 double, 0,      st_C_typespec
1742 signed, 0,      st_C_typespec
1743 unsigned,       0,      st_C_typespec
1744 auto,   0,      st_C_typespec
1745 void,   0,      st_C_typespec
1746 extern, 0,      st_C_extern
1747 static, 0,      st_C_typespec
1748 const,  0,      st_C_const
1749 volatile,       0,      st_C_typespec
1750 explicit,       C_PLPL, st_C_typespec
1751 mutable,        C_PLPL, st_C_typespec
1752 typename,       C_PLPL, st_C_typespec
1753 # DEFUN used in emacs, the next three used in glibc (SYSCALL only for mach).
1754 DEFUN,          0,      st_C_gnumacro
1755 SYSCALL,        0,      st_C_gnumacro
1756 ENTRY,          0,      st_C_gnumacro
1757 PSEUDO,         0,      st_C_gnumacro
1758 # These are defined inside C functions, so currently they are not met.
1759 # EXFUN used in glibc, DEFVAR_* in emacs.
1760 #EXFUN,         0,      st_C_gnumacro
1761 #DEFVAR_,       0,      st_C_gnumacro
1762 %]
1763 and replace lines between %< and %> with its output. */
1764 /*%<*/
1765 /* C code produced by gperf version 2.5 (GNU C++ version) */
1766 /* Command-line: gperf -c -k 1,3 -o -p -r -t  */
1767 struct C_stab_entry {
1768         char *name;
1769         int c_ext;
1770         enum sym_type type;
1771 };
1772
1773 #define TOTAL_KEYWORDS 41
1774 #define MIN_WORD_LENGTH 3
1775 #define MAX_WORD_LENGTH 15
1776 #define MIN_HASH_VALUE 13
1777 #define MAX_HASH_VALUE 129
1778 /* maximum key range = 117, duplicates = 0 */
1779
1780 static unsigned int hash(char *str, unsigned int len)
1781 {
1782         static unsigned char asso_values[] = {
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, 130, 130, 130, 130, 130, 130,
1788                 130, 130, 130, 130, 130, 130, 130, 130, 130, 130,
1789                 130, 130, 130, 130, 13, 130, 130, 130, 33, 32,
1790                 47, 130, 130, 130, 130, 130, 130, 130, 130, 130,
1791                 5, 130, 130, 20, 32, 130, 130, 130, 130, 130,
1792                 130, 130, 130, 130, 130, 130, 130, 47, 55, 8,
1793                 15, 33, 61, 38, 130, 60, 130, 130, 2, 9,
1794                 10, 62, 59, 130, 28, 27, 50, 19, 3, 130,
1795                 130, 130, 130, 130, 130, 130, 130, 130,
1796         };
1797         return len +
1798             asso_values[(unsigned char)str[2]] +
1799             asso_values[(unsigned char)str[0]];
1800 }
1801
1802 static struct C_stab_entry *in_word_set(char *str, unsigned int len)
1803 {
1804         static struct C_stab_entry wordlist[] = {
1805                 {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",},
1806                 {"",}, {"",}, {"",}, {"",},
1807                 {"volatile", 0, st_C_typespec},
1808                 {"",}, {"",},
1809                 {"long", 0, st_C_typespec},
1810                 {"",}, {"",}, {"",}, {"",}, {"",}, {"",},
1811                 {"const", 0, st_C_const},
1812                 {"",}, {"",}, {"",},
1813                 {"@end", 0, st_C_objend},
1814                 {"namespace", C_PLPL, st_C_struct},
1815                 {"",},
1816                 {"domain", C_STAR, st_C_struct},
1817                 {"",}, {"",},
1818                 {"@interface", 0, st_C_objprot},
1819                 {"",}, {"",}, {"",},
1820                 {"@implementation", 0, st_C_objimpl},
1821                 {"",}, {"",},
1822                 {"double", 0, st_C_typespec},
1823                 {"",}, {"",},
1824                 {"PSEUDO", 0, st_C_gnumacro},
1825                 {"",}, {"",}, {"",},
1826                 {"SYSCALL", 0, st_C_gnumacro},
1827                 {"",}, {"",},
1828                 {"@protocol", 0, st_C_objprot},
1829                 {"",}, {"",}, {"",},
1830                 {"unsigned", 0, st_C_typespec},
1831                 {"",},
1832                 {"enum", 0, st_C_enum},
1833                 {"",}, {"",},
1834                 {"char", 0, st_C_typespec},
1835                 {"class", C_PLPL, st_C_class},
1836                 {"struct", 0, st_C_struct},
1837                 {"",}, {"",}, {"",}, {"",},
1838                 {"mutable", C_PLPL, st_C_typespec},
1839                 {"void", 0, st_C_typespec},
1840                 {"inline", 0, st_C_inline},
1841                 {"ENTRY", 0, st_C_gnumacro},
1842                 {"",},
1843                 {"signed", 0, st_C_typespec},
1844                 {"",}, {"",},
1845                 {"package", C_JAVA, st_C_ignore},
1846                 {"",}, {"",}, {"",}, {"",}, {"",},
1847                 {"static", 0, st_C_typespec},
1848                 {"",},
1849                 {"define", 0, st_C_define},
1850                 {"",},
1851                 {"union", 0, st_C_union},
1852                 {"DEFUN", 0, st_C_gnumacro},
1853                 {"",}, {"",}, {"",},
1854                 {"extern", 0, st_C_extern},
1855                 {"extends", C_JAVA, st_C_javastruct},
1856                 {"",}, {"",}, {"",},
1857                 {"short", 0, st_C_typespec},
1858                 {"",}, {"",}, {"",}, {"",}, {"",},
1859                 {"explicit", C_PLPL, st_C_typespec},
1860                 {"auto", 0, st_C_typespec},
1861                 {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",},
1862                 {"",}, {"",},
1863                 {"int", 0, st_C_typespec},
1864                 {"",}, {"",},
1865                 {"typedef", 0, st_C_typedef},
1866                 {"typename", C_PLPL, st_C_typespec},
1867                 {"",},
1868                 {"interface", C_JAVA, st_C_struct},
1869                 {"",},
1870                 {"bool", C_PLPL, st_C_typespec},
1871                 {"",}, {"",}, {"",},
1872                 {"import", C_JAVA, st_C_ignore},
1873                 {"",},
1874                 {"friend", C_PLPL, st_C_ignore},
1875                 {"float", 0, st_C_typespec},
1876                 {"implements", C_JAVA, st_C_javastruct},
1877         };
1878
1879         if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) {
1880                 register int key = hash(str, len);
1881
1882                 if (key <= MAX_HASH_VALUE && key >= 0) {
1883                         register char *s = wordlist[key].name;
1884
1885                         if (*s == *str && !strncmp(str + 1, s + 1, len - 1))
1886                                 return &wordlist[key];
1887                 }
1888         }
1889         return 0;
1890 }
1891
1892 /*%>*/
1893
1894 enum sym_type C_symtype PP((char *str, int len, int c_ext));
1895 enum sym_type C_symtype(str, len, c_ext)
1896 char *str;
1897 int len;
1898 int c_ext;
1899 {
1900         register struct C_stab_entry *se = in_word_set(str, len);
1901
1902         if (se == NULL || (se->c_ext && !(c_ext & se->c_ext)))
1903                 return st_none;
1904         return se->type;
1905 }
1906 \f
1907  /*
1908   * C functions and variables are recognized using a simple
1909   * finite automaton.  fvdef is its state variable.
1910   */
1911 enum {
1912         fvnone,                 /* nothing seen */
1913         fvnameseen,             /* function or variable name seen */
1914         fstartlist,             /* func: just after open parenthesis */
1915         finlist,                /* func: in parameter list */
1916         flistseen,              /* func: after parameter list */
1917         fignore,                /* func: before open brace */
1918         vignore                 /* var-like: ignore until ';' */
1919 } fvdef;
1920
1921  /*
1922   * typedefs are recognized using a simple finite automaton.
1923   * typdef is its state variable.
1924   */
1925 enum {
1926         tnone,                  /* nothing seen */
1927         ttypedseen,             /* typedef keyword seen */
1928         tinbody,                /* inside typedef body */
1929         tend,                   /* just before typedef tag */
1930         tignore                 /* junk after typedef tag */
1931 } typdef;
1932
1933  /*
1934   * struct-like structures (enum, struct and union) are recognized
1935   * using another simple finite automaton.  `structdef' is its state
1936   * variable.
1937   */
1938 enum {
1939         snone,                  /* nothing seen yet */
1940         skeyseen,               /* struct-like keyword seen */
1941         stagseen,               /* struct-like tag seen */
1942         scolonseen,             /* colon seen after struct-like tag */
1943         sinbody                 /* in struct body: recognize member func defs */
1944 } structdef;
1945
1946 /*
1947  * When structdef is stagseen, scolonseen, or sinbody, structtag is the
1948  * struct tag, and structtype is the type of the preceding struct-like
1949  * keyword.
1950  */
1951 char *structtag = "<uninited>";
1952 enum sym_type structtype;
1953
1954 #ifdef OO_BROWSER
1955 void oo_browser_check_and_clear_structtype(void)
1956 {
1957         /* Allow for multiple enum_label tags. */
1958         if (structtype != st_C_enum)
1959                 structtype = st_none;
1960 }
1961 #endif
1962
1963 /*
1964  * When objdef is different from onone, objtag is the name of the class.
1965  */
1966 char *objtag = "<uninited>";
1967
1968 /*
1969  * Yet another little state machine to deal with preprocessor lines.
1970  */
1971 enum {
1972         dnone,                  /* nothing seen */
1973         dsharpseen,             /* '#' seen as first char on line */
1974         ddefineseen,            /* '#' and 'define' seen */
1975         dignorerest             /* ignore rest of line */
1976 } definedef;
1977
1978 /*
1979  * State machine for Objective C protocols and implementations.
1980  * Tom R.Hageman <tom@basil.icce.rug.nl>
1981  */
1982 enum {
1983         onone,                  /* nothing seen */
1984         oprotocol,              /* @interface or @protocol seen */
1985         oimplementation,        /* @implementations seen */
1986         otagseen,               /* class name seen */
1987         oparenseen,             /* parenthesis before category seen */
1988         ocatseen,               /* category name seen */
1989         oinbody,                /* in @implementation body */
1990         omethodsign,            /* in @implementation body, after +/- */
1991         omethodtag,             /* after method name */
1992         omethodcolon,           /* after method colon */
1993         omethodparm,            /* after method parameter */
1994         oignore                 /* wait for @end */
1995 } objdef;
1996
1997 /*
1998  * Use this structure to keep info about the token read, and how it
1999  * should be tagged.  Used by the make_C_tag function to build a tag.
2000  */
2001 typedef struct {
2002         bool valid;
2003         char *str;
2004         bool named;
2005         int linelen;
2006         int lineno;
2007         long linepos;
2008         char *buffer;
2009 } token;
2010
2011 token tok;                      /* latest token read */
2012
2013 /*
2014  * Set this to TRUE, and the next token considered is called a function.
2015  * Used only for GNU emacs's function-defining macros.
2016  */
2017 bool next_token_is_func;
2018
2019 /*
2020  * TRUE in the rules part of a yacc file, FALSE outside (parse as C).
2021  */
2022 bool yacc_rules;
2023
2024 /*
2025  * methodlen is the length of the method name stored in token_name.
2026  */
2027 int methodlen;
2028
2029 #ifdef OO_BROWSER
2030 void oo_browser_clear_all_globals(void)
2031 {
2032         /* Initialize globals so there is no carry over between files. */
2033         oo_browser_construct = C_NULL;
2034         fvdef = fvnone;
2035         typdef = tnone;
2036         structdef = snone;
2037         definedef = dnone;
2038         objdef = onone;
2039         structtype = st_none;
2040         next_token_is_func = yacc_rules = FALSE;
2041 }
2042
2043 void oo_browser_clear_some_globals(void)
2044 {
2045         oo_browser_construct = C_NULL;
2046         structtype = st_none;
2047 }
2048 #endif
2049
2050 /*
2051  * consider_token ()
2052  *      checks to see if the current token is at the start of a
2053  *      function or variable, or corresponds to a typedef, or
2054  *      is a struct/union/enum tag, or #define, or an enum constant.
2055  *
2056  *      *IS_FUNC gets TRUE iff the token is a function or #define macro
2057  *      with args.  C_EXT is which language we are looking at.
2058  *
2059  *      In the future we will need some way to adjust where the end of
2060  *      the token is; for instance, implementing the C++ keyword
2061  *      `operator' properly will adjust the end of the token to be after
2062  *      whatever follows `operator'.
2063  *
2064  * Globals
2065  *      fvdef                   IN OUT
2066  *      structdef               IN OUT
2067  *      definedef               IN OUT
2068  *      typdef                  IN OUT
2069  *      objdef                  IN OUT
2070  *      next_token_is_func      IN OUT
2071  */
2072 bool consider_token PP((char *str, int len, int c, int c_ext,
2073                         int cblev, int parlev, bool * is_func_or_var));
2074 bool consider_token(str, len, c, c_ext, cblev, parlev, is_func_or_var)
2075 register char *str;             /* IN: token pointer */
2076 register int len;               /* IN: token length */
2077 register int c;                 /* IN: first char after the token */
2078 int c_ext;                      /* IN: C extensions mask */
2079 int cblev;                      /* IN: curly brace level */
2080 int parlev;                     /* IN: parenthesis level */
2081 bool *is_func_or_var;           /* OUT: function or variable found */
2082 {
2083         enum sym_type toktype = C_symtype(str, len, c_ext);
2084
2085 #ifdef OO_BROWSER
2086         switch ((unsigned int)toktype) {
2087         case st_C_struct:
2088                 set_construct(C_STRUCTURE);
2089                 break;
2090         case st_C_union:
2091                 set_construct(C_UNION);
2092                 break;
2093         case st_C_class:
2094                 set_construct(C_CLASS);
2095                 break;
2096         case st_C_enum:
2097                 set_construct(C_ENUMERATION);
2098                 break;
2099         case st_C_typedef:
2100                 set_construct(C_TYPE);
2101                 break;
2102         case st_C_extern:
2103                 set_construct(C_EXTERN);
2104                 break;
2105         case st_C_inline:
2106                 set_construct(C_FUNCTION);
2107                 break;
2108
2109                 /* all the rest */
2110         default:
2111                 break;
2112         }
2113 #endif
2114
2115         /*
2116          * Advance the definedef state machine.
2117          */
2118         switch (definedef) {
2119         case dnone:
2120                 /* We're not on a preprocessor line. */
2121                 break;
2122         case dsharpseen:
2123                 if (toktype == st_C_define) {
2124                         definedef = ddefineseen;
2125                 } else {
2126                         definedef = dignorerest;
2127                 }
2128                 return FALSE;
2129         case ddefineseen:
2130                 /*
2131                  * Make a tag for any macro, unless it is a constant
2132                  * and constantypedefs is FALSE.
2133                  */
2134                 definedef = dignorerest;
2135 #ifndef OO_BROWSER
2136                 *is_func_or_var = (c == '(');
2137 #else
2138                 {
2139                         char *p = str + len * sizeof(char);
2140
2141                         if (*p == '(')
2142                                 /* This must be a macro since there is no
2143                                    whitespace between the opening parenthesis
2144                                    and the definition name. */
2145                                 *is_func_or_var = TRUE;
2146                         else {
2147                                 *is_func_or_var = FALSE;
2148
2149                                 /* Handle possible whitespace between macro tag and opening
2150                                    parenthesis and ensure this is an actual macro.
2151                                    -- Bob Weiner, Altrasoft, 11/19/1997 */
2152                                 while (*p && isspace(*p))
2153                                         p++;
2154                                 if (*p)
2155                                         c = *p;
2156
2157                                 /* Skip over nested parentheses. */
2158                                 if (c == '(') {
2159                                         short depth = 1;
2160
2161                                         while (*++p && depth > 0 && *p != '\n') {
2162                                                 switch (*p) {
2163                                                 case '(':
2164                                                         depth++;
2165                                                         break;
2166                                                 case ')':
2167                                                         depth--;
2168                                                         break;
2169                                                 default:
2170                                                         break;
2171                                                 }
2172                                         }
2173
2174                                         /* If this is a macro, we have just passed
2175                                            the arguments and there will be more on
2176                                            the line before the NULL character that marks
2177                                            the end of the line token. */
2178                                         while (*p == ' ' || *p == '\t')
2179                                                 p++;
2180                                         if (*p)
2181                                                 *is_func_or_var = TRUE;
2182                                 }
2183                         }
2184                 }
2185
2186                 set_construct((*is_func_or_var) ? C_MACRO : C_CONSTANT);
2187 #endif
2188                 if (!*is_func_or_var && !constantypedefs)
2189                         return FALSE;
2190                 else
2191                         return TRUE;
2192         case dignorerest:
2193                 return FALSE;
2194         default:
2195                 error("internal error: definedef value.", (char *)NULL);
2196         }
2197
2198         /*
2199          * Now typedefs
2200          */
2201         switch (typdef) {
2202         case tnone:
2203                 if (toktype == st_C_typedef) {
2204                         if (typedefs)
2205                                 typdef = ttypedseen;
2206                         fvdef = fvnone;
2207                         return FALSE;
2208                 }
2209                 break;
2210         case ttypedseen:
2211                 switch ((unsigned int)toktype) {
2212                 case st_C_const:
2213                         set_construct(C_CONSTANT);
2214                         /* fall through */
2215                 case st_none:
2216                 case st_C_typespec:
2217 #ifdef OO_BROWSER
2218                 case st_C_extern:
2219 #endif
2220                         typdef = tend;
2221                         break;
2222                 case st_C_struct:
2223                 case st_C_enum:
2224 #ifdef OO_BROWSER
2225                 case st_C_union:
2226                 case st_C_class:
2227 #endif
2228                         break;
2229
2230                         /* all the rest */
2231                 default:
2232                         break;
2233                 }
2234                 /* Do not return here, so the structdef stuff has a chance. */
2235                 break;
2236         case tend:
2237                 switch ((unsigned int)toktype) {
2238                 case st_C_const:
2239                         set_construct(C_CONSTANT);
2240                         /* fall through */
2241                 case st_C_typespec:
2242                 case st_C_struct:
2243                 case st_C_enum:
2244 #ifdef OO_BROWSER
2245                 case st_C_extern:
2246                 case st_C_union:
2247                 case st_C_class:
2248 #endif
2249                         return FALSE;
2250
2251                         /* all the rest */
2252                 default:
2253                         break;
2254                 }
2255                 return TRUE;
2256
2257                 /* all the rest */
2258         case tinbody:
2259         case tignore:
2260         default:
2261                 break;
2262         }
2263
2264         /*
2265          * This structdef business is currently only invoked when cblev==0.
2266          * It should be recursively invoked whatever the curly brace level,
2267          * and a stack of states kept, to allow for definitions of structs
2268          * within structs.
2269          *
2270          * This structdef business is NOT invoked when we are ctags and the
2271          * file is plain C.  This is because a struct tag may have the same
2272          * name as another tag, and this loses with ctags.
2273          */
2274         switch ((unsigned int)toktype) {
2275         case st_C_javastruct:
2276                 if (structdef == stagseen)
2277                         structdef = scolonseen;
2278                 return FALSE;
2279         case st_C_struct:
2280         case st_C_enum:
2281 #ifdef OO_BROWSER
2282         case st_C_union:
2283         case st_C_class:
2284         case st_C_extern:
2285 #endif
2286                 if (typdef == ttypedseen
2287                     || (typedefs_and_cplusplus && cblev == 0
2288                         && structdef == snone)) {
2289                         structdef = skeyseen;
2290                         structtype = toktype;
2291                 }
2292                 return FALSE;
2293
2294                 /* all the rest */
2295         default:
2296                 break;
2297         }
2298
2299         if (structdef == skeyseen) {
2300                 /* Save the tag for struct/union/class, for functions and variables
2301                    that may be defined inside. */
2302 #ifndef OO_BROWSER
2303                 if (structtype == st_C_struct)
2304 #else
2305                 if (structtype == st_C_struct
2306                     || structtype == st_C_union || structtype == st_C_class)
2307 #endif
2308                         structtag = savenstr(str, len);
2309                 else
2310                         structtag = "<enum>";
2311                 structdef = stagseen;
2312                 return TRUE;
2313         }
2314
2315         /* Avoid entering fvdef stuff if typdef is going on. */
2316         if (typdef != tnone) {
2317                 definedef = dnone;
2318                 return FALSE;
2319         }
2320
2321         /* Detect GNU macros.
2322
2323            DEFUN note for writers of emacs C code:
2324            The DEFUN macro, used in emacs C source code, has a first arg
2325            that is a string (the lisp function name), and a second arg that
2326            is a C function name.  Since etags skips strings, the second arg
2327            is tagged.  This is unfortunate, as it would be better to tag the
2328            first arg.  The simplest way to deal with this problem would be
2329            to name the tag with a name built from the function name, by
2330            removing the initial 'F' character and substituting '-' for '_'.
2331            Anyway, this assumes that the conventions of naming lisp
2332            functions will never change.  Currently, this method is not
2333            implemented, so writers of emacs code are recommended to put the
2334            first two args of a DEFUN on the same line. */
2335         if (definedef == dnone && toktype == st_C_gnumacro) {
2336                 next_token_is_func = TRUE;
2337                 return FALSE;
2338         }
2339         if (next_token_is_func) {
2340                 next_token_is_func = FALSE;
2341                 fvdef = fignore;
2342                 *is_func_or_var = TRUE;
2343                 return TRUE;
2344         }
2345
2346         /* Detect Objective C constructs. */
2347         switch (objdef) {
2348         case onone:
2349                 switch ((unsigned int)toktype) {
2350                 case st_C_objprot:
2351 #ifdef OO_BROWSER
2352                         set_construct(C_OBJC);
2353 #endif
2354                         objdef = oprotocol;
2355                         return FALSE;
2356                 case st_C_objimpl:
2357 #ifdef OO_BROWSER
2358                         set_construct(C_OBJC);
2359 #endif
2360                         objdef = oimplementation;
2361                         return FALSE;
2362
2363                         /* all the rest */
2364                 default:
2365                         break;
2366                 }
2367                 break;
2368         case oimplementation:
2369                 /* Save the class tag for functions or variables defined inside. */
2370                 objtag = savenstr(str, len);
2371                 objdef = oinbody;
2372                 return FALSE;
2373         case oprotocol:
2374                 /* Save the class tag for categories. */
2375                 objtag = savenstr(str, len);
2376                 objdef = otagseen;
2377                 *is_func_or_var = TRUE;
2378                 return TRUE;
2379         case oparenseen:
2380                 objdef = ocatseen;
2381                 *is_func_or_var = TRUE;
2382                 return TRUE;
2383         case oinbody:
2384                 break;
2385         case omethodsign:
2386                 if (parlev == 0) {
2387                         objdef = omethodtag;
2388                         methodlen = len;
2389                         grow_linebuffer(&token_name, methodlen + 1);
2390                         strncpy(token_name.buffer, str, len);
2391                         token_name.buffer[methodlen] = '\0';
2392                         token_name.len = methodlen;
2393                         return TRUE;
2394                 }
2395                 return FALSE;
2396         case omethodcolon:
2397                 if (parlev == 0)
2398                         objdef = omethodparm;
2399                 return FALSE;
2400         case omethodparm:
2401                 if (parlev == 0) {
2402                         objdef = omethodtag;
2403                         methodlen += len;
2404                         grow_linebuffer(&token_name, methodlen + 1);
2405                         strncat(token_name.buffer, str, len);
2406                         token_name.len = methodlen;
2407                         return TRUE;
2408                 }
2409                 return FALSE;
2410         case oignore:
2411                 if (toktype == st_C_objend) {
2412                         /* Memory leakage here: the string pointed by objtag is
2413                            never released, because many tests would be needed to
2414                            avoid breaking on incorrect input code.  The amount of
2415                            memory leaked here is the sum of the lengths of the
2416                            class tags.
2417                            free (objtag); */
2418                         objdef = onone;
2419                 }
2420                 return FALSE;
2421
2422                 /* all the rest */
2423         case otagseen:
2424         case ocatseen:
2425         case omethodtag:
2426         default:
2427                 break;
2428         }
2429
2430         /* A function, variable or enum constant? */
2431         switch ((unsigned int)toktype) {
2432         case st_C_const:
2433                 set_construct(C_CONSTANT);
2434                 /* fall through */
2435         case st_C_typespec:
2436 #ifdef OO_BROWSER
2437         case st_C_extern:
2438 #endif
2439                 if (fvdef != finlist && fvdef != fignore && fvdef != vignore)
2440                         fvdef = fvnone; /* should be useless */
2441                 return FALSE;
2442         case st_C_ignore:
2443                 fvdef = vignore;
2444                 return FALSE;
2445         case st_none:
2446                 if (constantypedefs && structdef == sinbody
2447                     && structtype == st_C_enum)
2448 #ifdef OO_BROWSER
2449                 {
2450                         oo_browser_construct = C_ENUM_LABEL;
2451 #endif
2452                         return TRUE;
2453 #ifdef OO_BROWSER
2454                 }
2455 #endif
2456                 if (fvdef == fvnone) {
2457                         fvdef = fvnameseen;     /* function or variable */
2458                         *is_func_or_var = TRUE;
2459                         return TRUE;
2460                 }
2461
2462                 /* all the rest */
2463         default:
2464                 break;
2465         }
2466
2467         return FALSE;
2468 }
2469
2470 /*
2471  * C_entries ()
2472  *      This routine finds functions, variables, typedefs,
2473  *      #define's, enum constants and struct/union/enum definitions in
2474  *      #C syntax and adds them to the list.
2475  */
2476 #define current_lb_is_new (newndx == curndx)
2477 #define switch_line_buffers() (curndx = 1 - curndx)
2478
2479 #define curlb (lbs[curndx].lb)
2480 #define othlb (lbs[1-curndx].lb)
2481 #define newlb (lbs[newndx].lb)
2482 #define curlinepos (lbs[curndx].linepos)
2483 #define othlinepos (lbs[1-curndx].linepos)
2484 #define newlinepos (lbs[newndx].linepos)
2485
2486 #define CNL_SAVE_DEFINEDEF()                                            \
2487 do {                                                                    \
2488   curlinepos = charno;                                                  \
2489   lineno++;                                                             \
2490   linecharno = charno;                                                  \
2491   charno += readline (&curlb, inf);                                     \
2492   lp = curlb.buffer;                                                    \
2493   quotednl = FALSE;                                                     \
2494   newndx = curndx;                                                      \
2495 } while (0)
2496
2497 #define CNL()                                                           \
2498 do {                                                                    \
2499   CNL_SAVE_DEFINEDEF();                                                 \
2500   if (savetok.valid)                                                    \
2501     {                                                                   \
2502       tok = savetok;                                                    \
2503       savetok.valid = FALSE;                                            \
2504     }                                                                   \
2505   definedef = dnone;                                                    \
2506 } while (0)
2507
2508 void make_C_tag PP((bool isfun));
2509 void make_C_tag(isfun)
2510 bool isfun;
2511 {
2512         /* This function should never be called when tok.valid is FALSE, but
2513            we must protect against invalid input or internal errors. */
2514         if (tok.valid) {
2515                 if (traditional_tag_style) {
2516                         /* This was the original code.  Now we call new_pfnote instead,
2517                            which uses the new method for naming tags (see new_pfnote). */
2518                         char *name = NULL;
2519
2520                         if (CTAGS || tok.named)
2521                                 name = savestr(token_name.buffer);
2522                         pfnote(name, isfun,
2523                                tok.buffer, tok.linelen, tok.lineno,
2524                                tok.linepos);
2525                 } else
2526                         new_pfnote(token_name.buffer, token_name.len, isfun,
2527                                    tok.buffer, tok.linelen, tok.lineno,
2528                                    tok.linepos);
2529                 tok.valid = FALSE;
2530         } else if (DEBUG)
2531                 abort();
2532 }
2533
2534 void C_entries(c_ext, inf)
2535 int c_ext;                      /* extension of C */
2536 FILE *inf;                      /* input file */
2537 {
2538         register char c;        /* latest char read; '\0' for end of line */
2539         register char *lp;      /* pointer one beyond the character `c' */
2540         int curndx, newndx;     /* indices for current and new lb */
2541         register int tokoff;    /* offset in line of start of current token */
2542         register int toklen;    /* length of current token */
2543         char *qualifier;        /* string used to qualify names */
2544         int qlen;               /* length of qualifier */
2545         int cblev;              /* current curly brace level */
2546         int parlev;             /* current parenthesis level */
2547         bool incomm, inquote, inchar, quotednl, midtoken;
2548         bool cplpl, cjava;
2549         token savetok;          /* token saved during preprocessor handling */
2550
2551         /* initialise savetok */
2552         memset(&savetok, 0, sizeof(token));
2553
2554         tokoff = toklen = 0;    /* keep compiler quiet */
2555         curndx = newndx = 0;
2556         lineno = 0;
2557         charno = 0;
2558         lp = curlb.buffer;
2559         *lp = 0;
2560
2561         fvdef = fvnone;
2562         typdef = tnone;
2563         structdef = snone;
2564         definedef = dnone;
2565         objdef = onone;
2566         next_token_is_func = yacc_rules = FALSE;
2567         midtoken = inquote = inchar = incomm = quotednl = FALSE;
2568         tok.valid = savetok.valid = FALSE;
2569         cblev = 0;
2570         parlev = 0;
2571         cplpl = (c_ext & C_PLPL) == C_PLPL;
2572         cjava = (c_ext & C_JAVA) == C_JAVA;
2573         if (cjava) {
2574                 qualifier = ".";
2575                 qlen = 1;
2576         } else {
2577                 qualifier = "::";
2578                 qlen = 2;
2579         }
2580
2581         while (!feof(inf)) {
2582                 c = *lp++;
2583                 if (c == '\\') {
2584                         /* If we're at the end of the line, the next character is a
2585                            '\0'; don't skip it, because it's the thing that tells us
2586                            to read the next line.  */
2587                         if (*lp == '\0') {
2588                                 quotednl = TRUE;
2589                                 continue;
2590                         }
2591                         lp++;
2592                         c = ' ';
2593                 } else if (incomm) {
2594                         switch (c) {
2595                         case '*':
2596                                 if (*lp == '/') {
2597                                         c = *lp++;
2598                                         incomm = FALSE;
2599                                 }
2600                                 break;
2601                         case '\0':
2602                                 /* Newlines inside comments do not end macro definitions in
2603                                    traditional cpp. */
2604                                 CNL_SAVE_DEFINEDEF();
2605                                 break;
2606                         default:
2607                                 break;
2608                         }
2609                         continue;
2610                 } else if (inquote) {
2611                         switch (c) {
2612                         case '"':
2613                                 inquote = FALSE;
2614                                 break;
2615                         case '\0':
2616                                 /* Newlines inside strings do not end macro definitions
2617                                    in traditional cpp, even though compilers don't
2618                                    usually accept them. */
2619                                 CNL_SAVE_DEFINEDEF();
2620                                 break;
2621                         default:
2622                                 break;
2623                         }
2624                         continue;
2625                 } else if (inchar) {
2626                         switch (c) {
2627                         case '\0':
2628                                 /* Hmmm, something went wrong. */
2629                                 CNL();
2630                                 /* FALLTHRU */
2631                         case '\'':
2632                                 inchar = FALSE;
2633                                 break;
2634                         default:
2635                                 break;
2636                         }
2637                         continue;
2638                 } else
2639                         switch (c) {
2640                         case '"':
2641                                 inquote = TRUE;
2642                                 if (fvdef != finlist && fvdef != fignore
2643                                     && fvdef != vignore)
2644                                         fvdef = fvnone;
2645                                 continue;
2646                         case '\'':
2647                                 inchar = TRUE;
2648                                 if (fvdef != finlist && fvdef != fignore
2649                                     && fvdef != vignore)
2650                                         fvdef = fvnone;
2651                                 continue;
2652                         case '/':
2653                                 if (*lp == '*') {
2654                                         lp++;
2655                                         incomm = TRUE;
2656                                         continue;
2657                                 } else if ( /* cplpl && */ *lp == '/') {
2658                                         c = '\0';
2659                                         break;
2660                                 } else
2661                                         break;
2662                         case '%':
2663                                 if ((c_ext & YACC) && *lp == '%') {
2664                                         /* entering or exiting rules section in yacc file */
2665                                         lp++;
2666                                         definedef = dnone;
2667                                         fvdef = fvnone;
2668                                         typdef = tnone;
2669                                         structdef = snone;
2670                                         next_token_is_func = FALSE;
2671                                         midtoken = inquote = inchar = incomm =
2672                                             quotednl = FALSE;
2673                                         cblev = 0;
2674                                         yacc_rules = !yacc_rules;
2675                                         continue;
2676                                 } else
2677                                         break;
2678                         case '#':
2679                                 if (definedef == dnone) {
2680                                         char *cp;
2681                                         bool cpptoken = TRUE;
2682
2683                                         /* Look back on this line.  If all blanks, or nonblanks
2684                                            followed by an end of comment, this is a preprocessor
2685                                            token. */
2686                                         for (cp = newlb.buffer; cp < lp - 1;
2687                                              cp++)
2688                                                 if (!iswhite(*cp)) {
2689                                                         if (*cp == '*'
2690                                                             && *(cp + 1) ==
2691                                                             '/') {
2692                                                                 cp++;
2693                                                                 cpptoken = TRUE;
2694                                                         } else
2695                                                                 cpptoken =
2696                                                                     FALSE;
2697                                                 }
2698                                         if (cpptoken)
2699                                                 definedef = dsharpseen;
2700                                 }
2701                                 /* if (definedef == dnone) */
2702                                 continue;
2703                         default:
2704                                 break;
2705                         }       /* switch (c) */
2706
2707                 /* Consider token only if some complicated conditions are satisfied. */
2708                 if ((definedef != dnone
2709                      || (cblev == 0 && structdef != scolonseen)
2710                      || (cblev == 1 && cplpl && structdef == sinbody)
2711                      || (structdef == sinbody && structtype == st_C_enum))
2712                     && typdef != tignore
2713                     && definedef != dignorerest && fvdef != finlist) {
2714                         if (midtoken) {
2715                                 if (endtoken(c)) {
2716                                         if (c == ':' && cplpl && *lp == ':'
2717                                             && begtoken(*(lp + 1))) {
2718                                                 /*
2719                                                  * This handles :: in the middle, but not at the
2720                                                  * beginning of an identifier.
2721                                                  */
2722                                                 lp += 2;
2723                                                 toklen += 3;
2724 #ifdef OO_BROWSER
2725                                                 set_construct(C_METHOD);
2726 #endif
2727                                         } else {
2728                                                 bool funorvar = FALSE;
2729
2730                                                 if (yacc_rules
2731                                                     || consider_token(newlb.
2732                                                                       buffer +
2733                                                                       tokoff,
2734                                                                       toklen, c,
2735                                                                       c_ext,
2736                                                                       cblev,
2737                                                                       parlev,
2738                                                                       &funorvar))
2739                                                 {
2740                                                         tok.named = FALSE;
2741                                                         if (structdef == sinbody
2742                                                             && definedef ==
2743                                                             dnone && funorvar)
2744                                                                 /* function or var defined in C++ class body */
2745                                                         {
2746                                                                 int len =
2747                                                                     strlen
2748                                                                     (structtag)
2749                                                                     + qlen +
2750                                                                     toklen;
2751                                                                 grow_linebuffer
2752                                                                     (&token_name,
2753                                                                      len + 1);
2754                                                                 strcpy
2755                                                                     (token_name.
2756                                                                      buffer,
2757                                                                      structtag);
2758                                                                 strcat
2759                                                                     (token_name.
2760                                                                      buffer,
2761                                                                      qualifier);
2762                                                                 strncat
2763                                                                     (token_name.
2764                                                                      buffer,
2765                                                                      newlb.
2766                                                                      buffer +
2767                                                                      tokoff,
2768                                                                      toklen);
2769                                                                 token_name.len =
2770                                                                     len;
2771                                                                 tok.named =
2772                                                                     TRUE;
2773 #ifdef OO_BROWSER
2774                                                                 oo_browser_construct
2775                                                                     = C_METHOD;
2776 #endif
2777                                                         } else if (objdef ==
2778                                                                    ocatseen)
2779                                                                 /* Objective C category */
2780                                                         {
2781                                                                 int len =
2782                                                                     strlen
2783                                                                     (objtag) +
2784                                                                     2 + toklen;
2785                                                                 grow_linebuffer
2786                                                                     (&token_name,
2787                                                                      len + 1);
2788                                                                 strcpy
2789                                                                     (token_name.
2790                                                                      buffer,
2791                                                                      objtag);
2792                                                                 strcat
2793                                                                     (token_name.
2794                                                                      buffer,
2795                                                                      "(");
2796                                                                 strncat
2797                                                                     (token_name.
2798                                                                      buffer,
2799                                                                      newlb.
2800                                                                      buffer +
2801                                                                      tokoff,
2802                                                                      toklen);
2803                                                                 strcat
2804                                                                     (token_name.
2805                                                                      buffer,
2806                                                                      ")");
2807                                                                 token_name.len =
2808                                                                     len;
2809                                                                 tok.named =
2810                                                                     TRUE;
2811 #ifdef OO_BROWSER
2812                                                                 oo_browser_construct
2813                                                                     = C_OBJC;
2814 #endif
2815                                                         } else if (objdef ==
2816                                                                    omethodtag
2817                                                                    || objdef ==
2818                                                                    omethodparm)
2819                                                                 /* Objective C method */
2820                                                         {
2821                                                                 tok.named =
2822                                                                     TRUE;
2823 #ifdef OO_BROWSER
2824                                                                 oo_browser_construct
2825                                                                     = C_OBJC;
2826 #endif
2827                                                         } else {
2828                                                                 grow_linebuffer
2829                                                                     (&token_name,
2830                                                                      toklen +
2831                                                                      1);
2832                                                                 strncpy
2833                                                                     (token_name.
2834                                                                      buffer,
2835                                                                      newlb.
2836                                                                      buffer +
2837                                                                      tokoff,
2838                                                                      toklen);
2839                                                                 token_name.
2840                                                                     buffer
2841                                                                     [toklen] =
2842                                                                     '\0';
2843                                                                 token_name.len =
2844                                                                     toklen;
2845                                                                 /* Name macros. */
2846                                                                 tok.named
2847                                                                     =
2848                                                                     (structdef
2849                                                                      == stagseen
2850                                                                      || typdef
2851                                                                      == tend
2852 #ifdef OO_BROWSER
2853                                                                      /* Also name #define constants,
2854                                                                         enumerations and enum_labels.
2855                                                                         Conditionalize `funorvar' reference
2856                                                                         here or #defines will appear without
2857                                                                         their #names.
2858                                                                         -- Bob Weiner, Altrasoft, 4/25/1998 */
2859                                                                      ||
2860                                                                      ((oo_browser_format || funorvar)
2861                                                                       &&
2862                                                                       definedef
2863                                                                       ==
2864                                                                       dignorerest)
2865                                                                      ||
2866                                                                      (oo_browser_format
2867                                                                       &&
2868                                                                       (oo_browser_construct
2869                                                                        ==
2870                                                                        C_ENUMERATION
2871                                                                        ||
2872                                                                        oo_browser_construct
2873                                                                        ==
2874                                                                        C_ENUM_LABEL))
2875 #else
2876                                                                      ||
2877                                                                      (funorvar
2878                                                                       &&
2879                                                                       definedef
2880                                                                       ==
2881                                                                       dignorerest)
2882 #endif
2883                                                                     );
2884                                                         }
2885                                                         tok.lineno = lineno;
2886                                                         tok.linelen =
2887                                                             tokoff + toklen + 1;
2888                                                         tok.buffer =
2889                                                             newlb.buffer;
2890                                                         tok.linepos =
2891                                                             newlinepos;
2892                                                         tok.valid = TRUE;
2893
2894                                                         if (definedef == dnone
2895                                                             && (fvdef ==
2896                                                                 fvnameseen
2897                                                                 || structdef ==
2898                                                                 stagseen
2899                                                                 || typdef ==
2900                                                                 tend
2901                                                                 || objdef !=
2902                                                                 onone)) {
2903                                                                 if (current_lb_is_new)
2904                                                                         switch_line_buffers
2905                                                                             ();
2906                                                         } else
2907                                                                 make_C_tag
2908                                                                     (funorvar);
2909                                                 }
2910                                                 midtoken = FALSE;
2911                                         }
2912                                 } /* if (endtoken (c)) */
2913                                 else if (intoken(c)) {
2914                                         toklen++;
2915                                         continue;
2916                                 }
2917                         } /* if (midtoken) */
2918                         else if (begtoken(c)) {
2919                                 switch (definedef) {
2920                                 case dnone:
2921                                         switch ((unsigned int)fvdef) {
2922                                         case fstartlist:
2923                                                 fvdef = finlist;
2924                                                 continue;
2925                                         case flistseen:
2926 #ifdef OO_BROWSER
2927                                                 set_construct(C_MACRO);
2928 #endif
2929                                                 make_C_tag(TRUE);       /* a function */
2930                                                 fvdef = fignore;
2931                                                 break;
2932                                         case fvnameseen:
2933                                                 fvdef = fvnone;
2934                                                 break;
2935
2936                                                 /* all the rest */
2937                                         default:
2938                                                 break;
2939                                         }
2940                                         if (structdef == stagseen && !cjava)
2941                                                 structdef = snone;
2942                                         break;
2943                                 case dsharpseen:
2944                                         savetok = tok;
2945
2946                                         /* all the rest */
2947                                 case ddefineseen:
2948                                 case dignorerest:
2949                                 default:
2950                                         break;
2951                                 }
2952                                 if (!yacc_rules || lp == newlb.buffer + 1) {
2953                                         tokoff = lp - 1 - newlb.buffer;
2954                                         toklen = 1;
2955                                         midtoken = TRUE;
2956                                 }
2957                                 continue;
2958                         }       /* if (begtoken) */
2959                 }
2960
2961                 /* if must look at token */
2962                 /* Detect end of line, colon, comma, semicolon and various braces
2963                    after having handled a token. */
2964                 switch (c) {
2965                 case ':':
2966                         if (definedef != dnone)
2967                                 break;
2968                         switch ((unsigned int)objdef) {
2969                         case otagseen:
2970                                 objdef = oignore;
2971                                 make_C_tag(TRUE);       /* an Objective C class */
2972                                 break;
2973                         case omethodtag:
2974                         case omethodparm:
2975                                 objdef = omethodcolon;
2976                                 methodlen += 1;
2977                                 grow_linebuffer(&token_name, methodlen + 1);
2978                                 strcat(token_name.buffer, ":");
2979                                 token_name.len = methodlen;
2980                                 break;
2981
2982                                 /* all the rest */
2983                         default:
2984                                 break;
2985                         }
2986                         if (structdef == stagseen)
2987                                 structdef = scolonseen;
2988                         else
2989                                 switch ((unsigned int)fvdef) {
2990                                 case fvnameseen:
2991                                         if (yacc_rules) {
2992                                                 make_C_tag(FALSE);      /* a yacc function */
2993                                                 fvdef = fignore;
2994                                         }
2995                                         break;
2996                                 case fstartlist:
2997                                         fvdef = fvnone;
2998                                         break;
2999
3000                                         /* all the rest */
3001                                 default:
3002                                         break;
3003                                 }
3004                         break;
3005                 case ';':
3006                         if (definedef != dnone)
3007                                 break;
3008                         if (cblev == 0)
3009                                 switch ((unsigned int)typdef) {
3010                                 case tend:
3011 #ifdef OO_BROWSER
3012                                         set_construct(C_TYPE);
3013 #endif
3014                                         make_C_tag(FALSE);      /* a typedef */
3015                                         /* FALLTHRU */
3016                                 default:
3017                                         typdef = tnone;
3018                                 }
3019                         switch ((unsigned int)fvdef) {
3020                         case fignore:
3021                                 break;
3022                         case fvnameseen:
3023                                 if ((globals && cblev == 0)
3024                                     || (members && cblev == 1))
3025 #ifndef OO_BROWSER
3026                                         make_C_tag(FALSE);      /* a variable */
3027 #else
3028 /*            if (constantypedefs && structdef == snone)*/
3029                                 {
3030                                         tok.named = TRUE;
3031                                         switch ((unsigned int)structtype) {
3032                                         case st_C_enum:
3033                                                 set_construct(C_ENUMERATION);
3034                                                 break;
3035                                         case st_C_class:
3036                                                 set_construct(C_CLASS);
3037                                                 break;
3038                                         default:
3039                                                 set_construct(C_VARIABLE);
3040                                                 break;
3041                                         }
3042                                         make_C_tag(FALSE);
3043                                         /* Force reset of st_C_enum structtype value. */
3044                                         structtype = st_none;
3045                                 }
3046 #endif
3047                                 /* FALLTHRU */
3048                         default:
3049                                 fvdef = fvnone;
3050                                 /* The following instruction invalidates the token.
3051                                    Probably the token should be invalidated in all
3052                                    other cases  where some state machine is reset. */
3053                                 tok.valid = FALSE;
3054                         }
3055                         if (structdef == stagseen)
3056                                 structdef = snone;
3057                         break;
3058                 case ',':
3059                         if (definedef != dnone)
3060                                 break;
3061                         switch ((unsigned int)objdef) {
3062                         case omethodtag:
3063                         case omethodparm:
3064                                 make_C_tag(TRUE);       /* an Objective C method */
3065                                 objdef = oinbody;
3066                                 break;
3067
3068                                 /* all the rest */
3069                         default:
3070                                 break;
3071                         }
3072                         switch ((unsigned int)fvdef) {
3073                         case finlist:
3074                         case fignore:
3075                         case vignore:
3076                                 break;
3077                         case fvnameseen:
3078                                 if ((globals && cblev == 0)
3079                                     || (members && cblev == 1))
3080                                         make_C_tag(FALSE);      /* a variable */
3081                                 break;
3082                         default:
3083                                 fvdef = fvnone;
3084                         }
3085                         if (structdef == stagseen)
3086                                 structdef = snone;
3087                         break;
3088                 case '[':
3089                         if (definedef != dnone)
3090                                 break;
3091                         if (cblev == 0 && typdef == tend) {
3092 #ifdef OO_BROWSER
3093                                 set_construct(C_TYPE);
3094 #endif
3095                                 typdef = tignore;
3096                                 make_C_tag(FALSE);      /* a typedef */
3097                                 break;
3098                         }
3099                         switch ((unsigned int)fvdef) {
3100                         case finlist:
3101                         case fignore:
3102                         case vignore:
3103                                 break;
3104                         case fvnameseen:
3105 #ifndef OO_BROWSER
3106                                 if ((globals && cblev == 0)
3107                                     || (members && cblev == 1))
3108                                         make_C_tag(FALSE);      /* a variable */
3109 #else
3110                                 if (constantypedefs && structdef == snone) {
3111                                         tok.named = TRUE;
3112                                         switch ((unsigned int)structtype) {
3113                                         case st_C_enum:
3114                                                 set_construct(C_ENUMERATION);
3115                                                 break;
3116                                         case st_C_class:
3117                                                 set_construct(C_CLASS);
3118                                                 break;
3119                                         default:
3120                                                 set_construct(C_VARIABLE);
3121                                                 break;
3122                                         }
3123                                         make_C_tag(FALSE);
3124                                         /* Force reset of st_C_enum structtype value. */
3125                                         structtype = st_none;
3126                                 }
3127 #endif
3128                                 /* FALLTHRU */
3129                         case fvnone:
3130                         case fstartlist:
3131                         case flistseen:
3132                         default:
3133                                 fvdef = fvnone;
3134                         }
3135                         if (structdef == stagseen)
3136                                 structdef = snone;
3137                         break;
3138                 case '(':
3139                         if (definedef != dnone)
3140                                 break;
3141                         if (objdef == otagseen && parlev == 0)
3142                                 objdef = oparenseen;
3143                         switch ((unsigned int)fvdef) {
3144                         case fvnone:
3145                                 switch (typdef) {
3146                                 case ttypedseen:
3147                                 case tend:
3148                                         if (tok.valid && *lp != '*') {
3149                                                 /* This handles constructs like:
3150                                                    typedef void OperatorFun (int fun); */
3151                                                 typdef = tignore;
3152 #ifdef OO_BROWSER
3153                                                 set_construct(C_TYPE);
3154 #endif
3155                                                 make_C_tag(FALSE);
3156                                         }
3157                                         break;
3158
3159                                         /* all the rest */
3160                                 case tnone:
3161                                 case tinbody:
3162                                 case tignore:
3163                                 default:
3164                                         break;
3165                                 }       /* switch (typdef) */
3166                                 break;
3167                         case fvnameseen:
3168                                 fvdef = fstartlist;
3169                                 break;
3170                         case flistseen:
3171                                 fvdef = finlist;
3172                                 break;
3173
3174                                 /* all the rest */
3175                         default:
3176                                 break;
3177                         }
3178                         parlev++;
3179                         break;
3180                 case ')':
3181                         if (definedef != dnone)
3182                                 break;
3183                         if (objdef == ocatseen && parlev == 1) {
3184                                 make_C_tag(TRUE);       /* an Objective C category */
3185                                 objdef = oignore;
3186                         }
3187                         if (--parlev == 0) {
3188                                 switch ((unsigned int)fvdef) {
3189                                 case fstartlist:
3190                                 case finlist:
3191                                         fvdef = flistseen;
3192                                         break;
3193
3194                                         /* all the rest */
3195                                 default:
3196                                         break;
3197                                 }
3198                                 if (cblev == 0 && typdef == tend) {
3199 #ifdef OO_BROWSER
3200                                         set_construct(C_TYPE);
3201 #endif
3202                                         typdef = tignore;
3203                                         make_C_tag(FALSE);      /* a typedef */
3204                                 }
3205                         } else if (parlev < 0)  /* can happen due to ill-conceived #if's. */
3206                                 parlev = 0;
3207                         break;
3208                 case '{':
3209                         if (definedef != dnone)
3210                                 break;
3211                         if (typdef == ttypedseen)
3212                                 typdef = tinbody;
3213                         switch (structdef) {
3214                         case skeyseen:  /* unnamed struct */
3215                                 structdef = sinbody;
3216                                 structtag = "_anonymous_";
3217                                 break;
3218                         case stagseen:
3219                         case scolonseen:        /* named struct */
3220                                 structdef = sinbody;
3221                                 make_C_tag(FALSE);      /* a struct */
3222                                 break;
3223
3224                                 /* all the rest */
3225                         case snone:
3226                         case sinbody:
3227                         default:
3228                                 break;
3229                         }
3230                         switch ((unsigned int)fvdef) {
3231                         case flistseen:
3232 #ifdef OO_BROWSER
3233                                 set_construct(C_FUNCTION);
3234                                 /* Ensure function name is recorded.
3235                                    -- Bob Weiner, Altrasoft */
3236                                 tok.named = TRUE;
3237 #endif
3238                                 make_C_tag(TRUE);       /* a function */
3239                                 /* FALLTHRU */
3240                         case fignore:
3241                                 fvdef = fvnone;
3242                                 break;
3243                         case fvnone:
3244                                 switch ((unsigned int)objdef) {
3245                                 case otagseen:
3246                                         make_C_tag(TRUE);       /* an Objective C class */
3247                                         objdef = oignore;
3248                                         break;
3249                                 case omethodtag:
3250                                 case omethodparm:
3251                                         make_C_tag(TRUE);       /* an Objective C method */
3252                                         objdef = oinbody;
3253                                         break;
3254                                 default:
3255                                         /* Neutralize `extern "C" {' grot. */
3256                                         if (cblev == 0 && structdef == snone
3257                                             && typdef == tnone)
3258                                                 cblev = -1;
3259                                 }
3260
3261                                 /* all the rest */
3262                         default:
3263                                 break;
3264                         }
3265                         cblev++;
3266                         break;
3267                 case '*':
3268                         if (definedef != dnone)
3269                                 break;
3270                         if (fvdef == fstartlist)
3271                                 fvdef = fvnone; /* avoid tagging `foo' in `foo (*bar()) ()' */
3272                         break;
3273                 case '}':
3274                         if (definedef != dnone)
3275                                 break;
3276                         if (!noindentypedefs && lp == newlb.buffer + 1) {
3277                                 cblev = 0;      /* reset curly brace level if first column */
3278                                 parlev = 0;     /* also reset paren level, just in case... */
3279                         } else if (cblev > 0)
3280                                 cblev--;
3281                         if (cblev == 0) {
3282                                 if (typdef == tinbody)
3283                                         typdef = tend;
3284                                 /* Memory leakage here: the string pointed by structtag is
3285                                    never released, because I fear to miss something and
3286                                    break things while freeing the area.  The amount of
3287                                    memory leaked here is the sum of the lengths of the
3288                                    struct tags.
3289                                    if (structdef == sinbody)
3290                                    free (structtag); */
3291
3292                                 structdef = snone;
3293                                 structtag = "<error>";
3294 #ifdef OO_BROWSER
3295                                 /* Next line added to avoid any state carryover between
3296                                    functions. -- Bob Weiner, Altrasoft, 11/19/1997 */
3297                                 fvdef = fvnone;
3298                                 oo_browser_construct = C_NULL;
3299 #endif
3300                         }
3301                         break;
3302                 case '=':
3303                         if (definedef != dnone)
3304                                 break;
3305 #ifdef OO_BROWSER
3306                         {
3307                                 int is_method = 0;
3308 #endif
3309                                 switch (fvdef) {
3310                                 case finlist:
3311                                 case fignore:
3312                                 case vignore:
3313                                         break;
3314                                 case fvnameseen:
3315                                         if ((globals && cblev == 0)
3316                                             || (members && cblev == 1))
3317 #ifndef OO_BROWSER
3318                                                 make_C_tag(FALSE);      /* a variable */
3319 #else
3320                                         {
3321                                                 tok.named = TRUE;
3322                                                 switch ((unsigned int)structtype) {
3323                                                 case st_C_enum:
3324                                                         set_construct
3325                                                             (C_ENUMERATION);
3326                                                         break;
3327                                                 case st_C_class:
3328                                                         set_construct(C_CLASS);
3329                                                         break;
3330                                                 default:
3331                                                         /* a global variable */
3332                                                         set_construct
3333                                                             (C_VARIABLE);
3334                                                         break;
3335                                                 }
3336
3337                                                 /* ootags categorizes each tag found whereas etags doesn't.
3338                                                    Set the is_method flag if this tag has been marked as
3339                                                    such by an earlier section of code.
3340                                                    -- Steve Baur, Altrasoft, 5/7/1998 */
3341                                                 is_method =
3342                                                     (oo_browser_construct ==
3343                                                      C_METHOD);
3344
3345                                                 make_C_tag(FALSE);
3346                                                 /* Force reset of st_C_enum structtype value. */
3347                                                 structtype = st_none;
3348                                         }
3349 #endif
3350                                         /* FALLTHRU */
3351                                 case fvnone:
3352                                 case fstartlist:
3353                                 case flistseen:
3354                                 default:
3355 #ifdef OO_BROWSER
3356                                         fvdef = is_method ? fignore : vignore;
3357 #else
3358                                         fvdef = vignore;
3359 #endif
3360                                 }
3361 #ifdef OO_BROWSER
3362                         }
3363 #endif
3364                         break;
3365                 case '+':
3366                 case '-':
3367                         if (objdef == oinbody && cblev == 0) {
3368                                 objdef = omethodsign;
3369                                 break;
3370                         }
3371                         /* FALLTHRU */
3372                 case '#':
3373                 case '~':
3374                 case '&':
3375                 case '%':
3376                 case '/':
3377                 case '|':
3378                 case '^':
3379                 case '!':
3380                 case '<':
3381                 case '>':
3382                 case '.':
3383                 case '?':
3384                 case ']':
3385                         if (definedef != dnone)
3386                                 break;
3387 #ifdef OO_BROWSER
3388                         if (!cplpl) {
3389 #endif
3390                                 /* The above characters cannot follow a function tag in C, so
3391                                    unmark this as a function entry.  For C++, these characters
3392                                    may follow an `operator' function construct, so skip the
3393                                    unmarking conditional below.
3394                                    -- Steve Baur, Altrasoft, 5/7/1998 */
3395                                 if (fvdef != finlist && fvdef != fignore
3396                                     && fvdef != vignore)
3397                                         fvdef = fvnone;
3398 #ifdef OO_BROWSER
3399                         }
3400 #endif
3401                         break;
3402                 case '\0':
3403                         if (objdef == otagseen) {
3404                                 make_C_tag(TRUE);       /* an Objective C class */
3405                                 objdef = oignore;
3406                         }
3407                         /* If a macro spans multiple lines don't reset its state. */
3408                         if (quotednl)
3409                                 CNL_SAVE_DEFINEDEF();
3410                         else
3411                                 CNL();
3412                         break;
3413
3414                         /* all the rest */
3415                 default:
3416                         break;
3417                 }               /* switch (c) */
3418
3419         }                       /* while not eof */
3420 }
3421
3422 /*
3423  * Process either a C++ file or a C file depending on the setting
3424  * of a global flag.
3425  */
3426 void default_C_entries(inf)
3427 FILE *inf;
3428 {
3429         C_entries(cplusplus ? C_PLPL : 0, inf);
3430 }
3431
3432 /* Always do plain ANSI C. */
3433 void plain_C_entries(inf)
3434 FILE *inf;
3435 {
3436         C_entries(0, inf);
3437 }
3438
3439 /* Always do C++. */
3440 void Cplusplus_entries(inf)
3441 FILE *inf;
3442 {
3443         C_entries(C_PLPL, inf);
3444 }
3445
3446 /* Always do Java. */
3447 void Cjava_entries(inf)
3448 FILE *inf;
3449 {
3450         C_entries(C_JAVA, inf);
3451 }
3452
3453 /* Always do C*. */
3454 void Cstar_entries(inf)
3455 FILE *inf;
3456 {
3457         C_entries(C_STAR, inf);
3458 }
3459
3460 /* Always do Yacc. */
3461 void Yacc_entries(inf)
3462 FILE *inf;
3463 {
3464         C_entries(YACC, inf);
3465 }
3466 \f
3467 /* A useful macro. */
3468 #define LOOP_ON_INPUT_LINES(file_pointer, line_buffer, char_pointer)    \
3469   for (lineno = charno = 0;     /* loop initialization */               \
3470        !feof (file_pointer)     /* loop test */                         \
3471        && (lineno++,            /* instructions at start of loop */     \
3472            linecharno = charno,                                         \
3473            charno += readline (&line_buffer, file_pointer),             \
3474            char_pointer = lb.buffer,                                    \
3475            TRUE);                                                       \
3476       )
3477
3478 /*
3479  * Read a file, but do no processing.  This is used to do regexp
3480  * matching on files that have no language defined.
3481  */
3482 void just_read_file(inf)
3483 FILE *inf;
3484 {
3485         register char *dummy;
3486
3487         LOOP_ON_INPUT_LINES(inf, lb, dummy)
3488             continue;
3489 }
3490 \f
3491 /* Fortran parsing */
3492
3493 bool tail PP((char *cp));
3494 bool tail(cp)
3495 char *cp;
3496 {
3497         register int len = 0;
3498
3499         while (*cp && lowcase(*cp) == lowcase(dbp[len]))
3500                 cp++, len++;
3501         if (*cp == '\0' && !intoken(dbp[len])) {
3502                 dbp += len;
3503                 return TRUE;
3504         }
3505         return FALSE;
3506 }
3507
3508 void takeprec()
3509 {
3510         dbp = skip_spaces(dbp);
3511         if (*dbp != '*')
3512                 return;
3513         dbp++;
3514         dbp = skip_spaces(dbp);
3515         if (strneq(dbp, "(*)", 3)) {
3516                 dbp += 3;
3517                 return;
3518         }
3519         if (!isdigit(*dbp)) {
3520                 --dbp;          /* force failure */
3521                 return;
3522         }
3523         do
3524                 dbp++;
3525         while (isdigit(*dbp));
3526 }
3527
3528 void getit PP((FILE * inf));
3529 void getit(inf)
3530 FILE *inf;
3531 {
3532         register char *cp;
3533
3534         dbp = skip_spaces(dbp);
3535         if (*dbp == '\0') {
3536                 lineno++;
3537                 linecharno = charno;
3538                 charno += readline(&lb, inf);
3539                 dbp = lb.buffer;
3540                 if (dbp[5] != '&')
3541                         return;
3542                 dbp += 6;
3543                 dbp = skip_spaces(dbp);
3544         }
3545         if (!isalpha(*dbp)
3546             && *dbp != '_' && *dbp != '$')
3547                 return;
3548         for (cp = dbp + 1; *cp && intoken(*cp); cp++)
3549                 continue;
3550         pfnote((CTAGS) ? savenstr(dbp, cp - dbp) : NULL, TRUE,
3551                lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
3552 }
3553
3554 void Fortran_functions(inf)
3555 FILE *inf;
3556 {
3557         LOOP_ON_INPUT_LINES(inf, lb, dbp) {
3558                 if (*dbp == '%')
3559                         dbp++;  /* Ratfor escape to fortran */
3560                 dbp = skip_spaces(dbp);
3561                 if (*dbp == '\0')
3562                         continue;
3563                 switch (lowcase(*dbp)) {
3564                 case 'i':
3565                         if (tail("integer"))
3566                                 takeprec();
3567                         break;
3568                 case 'r':
3569                         if (tail("real"))
3570                                 takeprec();
3571                         break;
3572                 case 'l':
3573                         if (tail("logical"))
3574                                 takeprec();
3575                         break;
3576                 case 'c':
3577                         if (tail("complex") || tail("character"))
3578                                 takeprec();
3579                         break;
3580                 case 'd':
3581                         if (tail("double")) {
3582                                 dbp = skip_spaces(dbp);
3583                                 if (*dbp == '\0')
3584                                         continue;
3585                                 if (tail("precision"))
3586                                         break;
3587                                 continue;
3588                         }
3589                         break;
3590                 default:
3591                         break;
3592                 }
3593                 dbp = skip_spaces(dbp);
3594                 if (*dbp == '\0')
3595                         continue;
3596                 switch (lowcase(*dbp)) {
3597                 case 'f':
3598                         if (tail("function"))
3599                                 getit(inf);
3600                         continue;
3601                 case 's':
3602                         if (tail("subroutine"))
3603                                 getit(inf);
3604                         continue;
3605                 case 'e':
3606                         if (tail("entry"))
3607                                 getit(inf);
3608                         continue;
3609                 case 'p':
3610                         if (tail("program")) {
3611                                 getit(inf);
3612                                 continue;
3613                         }
3614                         if (tail("procedure"))
3615                                 getit(inf);
3616                         continue;
3617                 default:
3618                         break;
3619                 }
3620         }
3621 }
3622 \f
3623 /*
3624  * Bob Weiner, Motorola Inc., 4/3/94
3625  * Unix and microcontroller assembly tag handling
3626  * look for '^[a-zA-Z_.$][a-zA_Z0-9_.$]*[: ^I^J]'
3627  */
3628 void Asm_labels(FILE * inf)
3629 {
3630         register char *cp;
3631
3632         LOOP_ON_INPUT_LINES(inf, lb, cp) {
3633                 /* If first char is alphabetic or one of [_.$], test for colon
3634                    following identifier. */
3635                 if (isalpha(*cp) || *cp == '_' || *cp == '.' || *cp == '$') {
3636                         /* Read past label. */
3637                         cp++;
3638                         while (isalnum(*cp) || *cp == '_' || *cp == '.'
3639                                || *cp == '$')
3640                                 cp++;
3641                         if (*cp == ':' || isspace(*cp)) {
3642                                 /* Found end of label, so copy it and add it to the table. */
3643                                 pfnote((CTAGS) ?
3644                                        savenstr(lb.buffer,
3645                                                 cp - lb.buffer) : NULL, TRUE,
3646                                        lb.buffer, cp - lb.buffer + 1, lineno,
3647                                        linecharno);
3648                         }
3649                 }
3650         }
3651 }
3652 \f
3653 /*
3654  * Perl support by Bart Robinson <lomew@cs.utah.edu>
3655  *              enhanced by Michael Ernst <mernst@alum.mit.edu>
3656  * Perl sub names: look for /^sub[ \t\n]+[^ \t\n{]+/
3657  * Perl variable names: /^(my|local).../
3658  */
3659 void Perl_functions(inf)
3660 FILE *inf;
3661 {
3662         register char *cp;
3663
3664         LOOP_ON_INPUT_LINES(inf, lb, cp) {
3665                 if (*cp++ == 's'
3666                     && *cp++ == 'u' && *cp++ == 'b' && isspace(*cp++)) {
3667                         cp = skip_spaces(cp);
3668                         if (*cp != '\0') {
3669                                 while (*cp != '\0'
3670                                        && !isspace(*cp) && *cp != '{'
3671                                        && *cp != '(')
3672                                         cp++;
3673                                 pfnote((CTAGS) ?
3674                                        savenstr(lb.buffer,
3675                                                 cp - lb.buffer) : NULL, TRUE,
3676                                        lb.buffer, cp - lb.buffer + 1, lineno,
3677                                        linecharno);
3678                         }
3679                 } else if (globals      /* only if tagging global vars is enabled */
3680                            && ((cp = lb.buffer, *cp++ == 'm' && *cp++ == 'y')
3681                                || (cp = lb.buffer,
3682                                    *cp++ == 'l'
3683                                    && *cp++ == 'o'
3684                                    && *cp++ == 'c'
3685                                    && *cp++ == 'a' && *cp++ == 'l'))
3686                            && (*cp == '(' || isspace(*cp))) {
3687                         /* After "my" or "local", but before any following paren or space. */
3688                         char *varname = NULL;
3689
3690                         cp = skip_spaces(cp);
3691                         if (*cp == '$' || *cp == '@' || *cp == '%') {
3692                                 char *varstart = ++cp;
3693                                 while (isalnum(*cp) || *cp == '_')
3694                                         cp++;
3695                                 varname = savenstr(varstart, cp - varstart);
3696                         } else {
3697                                 /* Should be examining a variable list at this point;
3698                                    could insist on seeing an open parenthesis. */
3699                                 while (*cp != '\0' && *cp != ';' && *cp != '='
3700                                        && *cp != ')')
3701                                         cp++;
3702                         }
3703
3704                         /* Perhaps I should back cp up one character, so the TAGS table
3705                            doesn't mention (and so depend upon) the following char. */
3706                         pfnote((CTAGS) ? savenstr(lb.buffer, cp - lb.buffer) :
3707                                varname, FALSE, lb.buffer, cp - lb.buffer + 1,
3708                                lineno, linecharno);
3709                 }
3710         }
3711 }
3712 \f
3713 /*
3714  * Python support by Eric S. Raymond <esr@thyrsus.com>
3715  * Look for /^def[ \t\n]+[^ \t\n(:]+/ or /^class[ \t\n]+[^ \t\n(:]+/
3716  */
3717 void Python_functions(inf)
3718 FILE *inf;
3719 {
3720         register char *cp;
3721
3722         LOOP_ON_INPUT_LINES(inf, lb, cp) {
3723                 if (*cp++ == 'd'
3724                     && *cp++ == 'e' && *cp++ == 'f' && isspace(*cp++)) {
3725                         cp = skip_spaces(cp);
3726                         while (*cp != '\0' && !isspace(*cp) && *cp != '('
3727                                && *cp != ':')
3728                                 cp++;
3729                         pfnote((char *)NULL, TRUE,
3730                                lb.buffer, cp - lb.buffer + 1, lineno,
3731                                linecharno);
3732                 }
3733
3734                 cp = lb.buffer;
3735                 if (*cp++ == 'c'
3736                     && *cp++ == 'l'
3737                     && *cp++ == 'a'
3738                     && *cp++ == 's' && *cp++ == 's' && isspace(*cp++)) {
3739                         cp = skip_spaces(cp);
3740                         while (*cp != '\0' && !isspace(*cp) && *cp != '('
3741                                && *cp != ':')
3742                                 cp++;
3743                         pfnote((char *)NULL, TRUE,
3744                                lb.buffer, cp - lb.buffer + 1, lineno,
3745                                linecharno);
3746                 }
3747         }
3748 }
3749 \f
3750 /* Idea by Corny de Souza
3751  * Cobol tag functions
3752  * We could look for anything that could be a paragraph name.
3753  * i.e. anything that starts in column 8 is one word and ends in a full stop.
3754  */
3755 void Cobol_paragraphs(inf)
3756 FILE *inf;
3757 {
3758         register char *bp, *ep;
3759
3760         LOOP_ON_INPUT_LINES(inf, lb, bp) {
3761                 if (lb.len < 9)
3762                         continue;
3763                 bp += 8;
3764
3765                 /* If eoln, compiler option or comment ignore whole line. */
3766                 if (bp[-1] != ' ' || !isalnum(bp[0]))
3767                         continue;
3768
3769                 for (ep = bp; isalnum(*ep) || *ep == '-'; ep++)
3770                         continue;
3771                 if (*ep++ == '.')
3772                         pfnote((CTAGS) ? savenstr(bp, ep - bp) : NULL, TRUE,
3773                                lb.buffer, ep - lb.buffer + 1, lineno,
3774                                linecharno);
3775         }
3776 }
3777 \f
3778 /* Added by Mosur Mohan, 4/22/88 */
3779 /* Pascal parsing                */
3780
3781 /*
3782  *  Locates tags for procedures & functions.  Doesn't do any type- or
3783  *  var-definitions.  It does look for the keyword "extern" or
3784  *  "forward" immediately following the procedure statement; if found,
3785  *  the tag is skipped.
3786  */
3787 void Pascal_functions(inf)
3788 FILE *inf;
3789 {
3790         linebuffer tline;       /* mostly copied from C_entries */
3791         long save_lcno;
3792         int save_lineno, save_len;
3793         char c, *cp, *namebuf;
3794
3795         bool                    /* each of these flags is TRUE iff: */
3796             incomment,          /* point is inside a comment */
3797             inquote,            /* point is inside '..' string */
3798             get_tagname,        /* point is after PROCEDURE/FUNCTION
3799                                    keyword, so next item = potential tag */
3800             found_tag,          /* point is after a potential tag */
3801             inparms,            /* point is within parameter-list */
3802             verify_tag;         /* point has passed the parm-list, so the
3803                                    next token will determine whether this
3804                                    is a FORWARD/EXTERN to be ignored, or
3805                                    whether it is a real tag */
3806
3807         save_lcno = save_lineno = save_len = 0; /* keep compiler quiet */
3808         namebuf = NULL;         /* keep compiler quiet */
3809         lineno = 0;
3810         charno = 0;
3811         dbp = lb.buffer;
3812         *dbp = '\0';
3813         initbuffer(&tline);
3814
3815         incomment = inquote = FALSE;
3816         found_tag = FALSE;      /* have a proc name; check if extern */
3817         get_tagname = FALSE;    /* have found "procedure" keyword    */
3818         inparms = FALSE;        /* found '(' after "proc"            */
3819         verify_tag = FALSE;     /* check if "extern" is ahead        */
3820
3821         while (!feof(inf)) {    /* long main loop to get next char */
3822                 c = *dbp++;
3823                 if (c == '\0') {        /* if end of line */
3824                         lineno++;
3825                         linecharno = charno;
3826                         charno += readline(&lb, inf);
3827                         dbp = lb.buffer;
3828                         if (*dbp == '\0')
3829                                 continue;
3830                         if (!((found_tag && verify_tag)
3831                               || get_tagname))
3832                                 c = *dbp++;     /* only if don't need *dbp pointing
3833                                                    to the beginning of the name of
3834                                                    the procedure or function */
3835                 }
3836                 if (incomment) {
3837                         if (c == '}')   /* within { } comments */
3838                                 incomment = FALSE;
3839                         else if (c == '*' && *dbp == ')') {     /* within (* *) comments */
3840                                 dbp++;
3841                                 incomment = FALSE;
3842                         }
3843                         continue;
3844                 } else if (inquote) {
3845                         if (c == '\'')
3846                                 inquote = FALSE;
3847                         continue;
3848                 } else
3849                         switch (c) {
3850                         case '\'':
3851                                 inquote = TRUE; /* found first quote */
3852                                 continue;
3853                         case '{':       /* found open { comment */
3854                                 incomment = TRUE;
3855                                 continue;
3856                         case '(':
3857                                 if (*dbp == '*') {      /* found open (* comment */
3858                                         incomment = TRUE;
3859                                         dbp++;
3860                                 } else if (found_tag)   /* found '(' after tag, i.e., parm-list */
3861                                         inparms = TRUE;
3862                                 continue;
3863                         case ')':       /* end of parms list */
3864                                 if (inparms)
3865                                         inparms = FALSE;
3866                                 continue;
3867                         case ';':
3868                                 if (found_tag && !inparms) {    /* end of proc or fn stmt */
3869                                         verify_tag = TRUE;
3870                                         break;
3871                                 }
3872                                 continue;
3873                         default:
3874                                 break;
3875                         }
3876                 if (found_tag && verify_tag && (*dbp != ' ')) {
3877                         /* check if this is an "extern" declaration */
3878                         if (*dbp == '\0')
3879                                 continue;
3880                         if (lowcase(*dbp == 'e')) {
3881                                 if (tail("extern")) {   /* superfluous, really! */
3882                                         found_tag = FALSE;
3883                                         verify_tag = FALSE;
3884                                 }
3885                         } else if (lowcase(*dbp) == 'f') {
3886                                 if (tail("forward")) {  /*  check for forward reference */
3887                                         found_tag = FALSE;
3888                                         verify_tag = FALSE;
3889                                 }
3890                         }
3891                         if (found_tag && verify_tag) {  /* not external proc, so make tag */
3892                                 found_tag = FALSE;
3893                                 verify_tag = FALSE;
3894                                 pfnote(namebuf, TRUE,
3895                                        tline.buffer, save_len, save_lineno,
3896                                        save_lcno);
3897                                 continue;
3898                         }
3899                 }
3900                 if (get_tagname) {      /* grab name of proc or fn */
3901                         if (*dbp == '\0')
3902                                 continue;
3903
3904                         /* save all values for later tagging */
3905                         grow_linebuffer(&tline, lb.len + 1);
3906                         strncpy(tline.buffer, lb.buffer, lb.len);
3907                         save_lineno = lineno;
3908                         save_lcno = linecharno;
3909
3910                         /* grab block name */
3911                         for (cp = dbp + 1; *cp != '\0' && !endtoken(*cp); cp++)
3912                                 continue;
3913                         namebuf = (CTAGS) ? savenstr(dbp, cp - dbp) : NULL;
3914                         dbp = cp;       /* set dbp to e-o-token */
3915                         save_len = dbp - lb.buffer + 1;
3916                         get_tagname = FALSE;
3917                         found_tag = TRUE;
3918                         continue;
3919
3920                         /* and proceed to check for "extern" */
3921                 } else if (!incomment && !inquote && !found_tag) {
3922                         /* check for proc/fn keywords */
3923                         switch (lowcase(c)) {
3924                         case 'p':
3925                                 if (tail("rocedure"))   /* c = 'p', dbp has advanced */
3926                                         get_tagname = TRUE;
3927                                 continue;
3928                         case 'f':
3929                                 if (tail("unction"))
3930                                         get_tagname = TRUE;
3931                                 continue;
3932                         default:
3933                                 break;
3934                         }
3935                 }
3936         }                       /* while not eof */
3937
3938         free(tline.buffer);
3939 }
3940 \f
3941 /*
3942  * lisp tag functions
3943  *  look for (def or (DEF, quote or QUOTE
3944  */
3945 int L_isdef PP((char *strp));
3946 int L_isdef(strp)
3947 register char *strp;
3948 {
3949         return ((strp[1] == 'd' || strp[1] == 'D')
3950                 && (strp[2] == 'e' || strp[2] == 'E')
3951                 && (strp[3] == 'f' || strp[3] == 'F'));
3952 }
3953 int L_isquote PP((char *strp));
3954 int L_isquote(strp)
3955 register char *strp;
3956 {
3957         return ((*++strp == 'q' || *strp == 'Q')
3958                 && (*++strp == 'u' || *strp == 'U')
3959                 && (*++strp == 'o' || *strp == 'O')
3960                 && (*++strp == 't' || *strp == 'T')
3961                 && (*++strp == 'e' || *strp == 'E')
3962                 && isspace(*++strp));
3963 }
3964
3965 void L_getit PP((void));
3966 void L_getit()
3967 {
3968         register char *cp;
3969
3970         if (*dbp == '\'')       /* Skip prefix quote */
3971                 dbp++;
3972         else if (*dbp == '(') {
3973                 if (L_isquote(dbp))
3974                         dbp += 7;       /* Skip "(quote " */
3975                 else
3976                         dbp += 1;       /* Skip "(" before name in (defstruct (foo)) */
3977                 dbp = skip_spaces(dbp);
3978         }
3979
3980         for (cp = dbp /*+1 */ ;
3981              *cp != '\0' && *cp != '(' && *cp != ' ' && *cp != ')'; cp++)
3982                 continue;
3983         if (cp == dbp)
3984                 return;
3985
3986         pfnote((CTAGS) ? savenstr(dbp, cp - dbp) : NULL, TRUE,
3987                lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
3988 }
3989
3990 void Lisp_functions(inf)
3991 FILE *inf;
3992 {
3993         LOOP_ON_INPUT_LINES(inf, lb, dbp) {
3994                 if (dbp[0] == '(') {
3995                         if (L_isdef(dbp)) {
3996                                 dbp = skip_non_spaces(dbp);
3997                                 dbp = skip_spaces(dbp);
3998                                 L_getit();
3999                         } else {
4000                                 /* Check for (foo::defmumble name-defined ... */
4001                                 do
4002                                         dbp++;
4003                                 while (*dbp != '\0' && !isspace(*dbp)
4004                                        && *dbp != ':' && *dbp != '('
4005                                        && *dbp != ')');
4006                                 if (*dbp == ':') {
4007                                         do
4008                                                 dbp++;
4009                                         while (*dbp == ':');
4010
4011                                         if (L_isdef(dbp - 1)) {
4012                                                 dbp = skip_non_spaces(dbp);
4013                                                 dbp = skip_spaces(dbp);
4014                                                 L_getit();
4015                                         }
4016                                 }
4017                         }
4018                 }
4019         }
4020 }
4021 \f
4022 /*
4023  * Postscript tag functions
4024  * Just look for lines where the first character is '/'
4025  * Richard Mlynarik <mly@adoc.xerox.com>
4026  */
4027 void Postscript_functions(inf)
4028 FILE *inf;
4029 {
4030         register char *bp, *ep;
4031
4032         LOOP_ON_INPUT_LINES(inf, lb, bp) {
4033                 if (bp[0] == '/') {
4034                         for (ep = bp + 1;
4035                              *ep != '\0' && *ep != ' ' && *ep != '{'; ep++)
4036                                 continue;
4037                         pfnote((CTAGS) ? savenstr(bp, ep - bp) : NULL, TRUE,
4038                                lb.buffer, ep - lb.buffer + 1, lineno,
4039                                linecharno);
4040                 }
4041         }
4042 }
4043 \f
4044 /*
4045  * Scheme tag functions
4046  * look for (def... xyzzy
4047  * look for (def... (xyzzy
4048  * look for (def ... ((...(xyzzy ....
4049  * look for (set! xyzzy
4050  */
4051
4052 void get_scheme PP((void));
4053
4054 void Scheme_functions(inf)
4055 FILE *inf;
4056 {
4057         LOOP_ON_INPUT_LINES(inf, lb, dbp) {
4058                 if (dbp[0] == '(' && (dbp[1] == 'D' || dbp[1] == 'd')
4059                     && (dbp[2] == 'E' || dbp[2] == 'e')
4060                     && (dbp[3] == 'F' || dbp[3] == 'f')) {
4061                         dbp = skip_non_spaces(dbp);
4062                         /* Skip over open parens and white space */
4063                         while (isspace(*dbp) || *dbp == '(')
4064                                 dbp++;
4065                         get_scheme();
4066                 }
4067                 if (dbp[0] == '(' && (dbp[1] == 'S' || dbp[1] == 's')
4068                     && (dbp[2] == 'E' || dbp[2] == 'e')
4069                     && (dbp[3] == 'T' || dbp[3] == 't')
4070                     && (dbp[4] == '!' || dbp[4] == '!')
4071                     && (isspace(dbp[5]))) {
4072                         dbp = skip_non_spaces(dbp);
4073                         dbp = skip_spaces(dbp);
4074                         get_scheme();
4075                 }
4076         }
4077 }
4078
4079 void get_scheme()
4080 {
4081         register char *cp;
4082
4083         if (*dbp == '\0')
4084                 return;
4085         /* Go till you get to white space or a syntactic break */
4086         for (cp = dbp + 1;
4087              *cp != '\0' && *cp != '(' && *cp != ')' && !isspace(*cp); cp++)
4088                 continue;
4089         pfnote((CTAGS) ? savenstr(dbp, cp - dbp) : NULL, TRUE,
4090                lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
4091 }
4092 \f
4093 /* Find tags in TeX and LaTeX input files.  */
4094
4095 /* TEX_toktab is a table of TeX control sequences that define tags.
4096    Each TEX_tabent records one such control sequence.
4097    CONVERT THIS TO USE THE Stab TYPE!! */
4098 struct TEX_tabent {
4099         char *name;
4100         int len;
4101 };
4102
4103 struct TEX_tabent *TEX_toktab = NULL;   /* Table with tag tokens */
4104
4105 /* Default set of control sequences to put into TEX_toktab.
4106    The value of environment var TEXTAGS is prepended to this.  */
4107
4108 char *TEX_defenv = "\
4109 :chapter:section:subsection:subsubsection:eqno:label:ref:cite:bibitem\
4110 :part:appendix:entry:index";
4111
4112 void TEX_mode PP((FILE * inf));
4113 struct TEX_tabent *TEX_decode_env PP((char *evarname, char *defenv));
4114 int TEX_Token PP((char *cp));
4115
4116 char TEX_esc = '\\';
4117 char TEX_opgrp = '{';
4118 char TEX_clgrp = '}';
4119
4120 /*
4121  * TeX/LaTeX scanning loop.
4122  */
4123 void TeX_functions(inf)
4124 FILE *inf;
4125 {
4126         char *cp, *lasthit;
4127         register int i;
4128
4129         /* Select either \ or ! as escape character.  */
4130         TEX_mode(inf);
4131
4132         /* Initialize token table once from environment. */
4133         if (!TEX_toktab)
4134                 TEX_toktab = TEX_decode_env("TEXTAGS", TEX_defenv);
4135
4136         LOOP_ON_INPUT_LINES(inf, lb, cp) {
4137                 lasthit = cp;
4138                 /* Look at each esc in line. */
4139                 while ((cp = etags_strchr(cp, TEX_esc)) != NULL) {
4140                         if (*++cp == '\0')
4141                                 break;
4142                         linecharno += cp - lasthit;
4143                         lasthit = cp;
4144                         i = TEX_Token(lasthit);
4145                         if (i >= 0) {
4146                                 /* We seem to include the TeX command in the tag name.
4147                                    register char *p;
4148                                    for (p = lasthit + TEX_toktab[i].len;
4149                                    *p != '\0' && *p != TEX_clgrp;
4150                                    p++)
4151                                    continue; */
4152                                 pfnote( /*savenstr (lasthit, p-lasthit) */
4153                                     (char *)NULL, TRUE,
4154                                        lb.buffer, lb.len, lineno, linecharno);
4155                                 break;  /* We only tag a line once */
4156                         }
4157                 }
4158         }
4159 }
4160
4161 #define TEX_LESC '\\'
4162 #define TEX_SESC '!'
4163 #define TEX_cmt  '%'
4164
4165 /* Figure out whether TeX's escapechar is '\\' or '!' and set grouping
4166    chars accordingly. */
4167 void TEX_mode(inf)
4168 FILE *inf;
4169 {
4170         int c;
4171
4172         while ((c = getc(inf)) != EOF) {
4173                 /* Skip to next line if we hit the TeX comment char. */
4174                 if (c == TEX_cmt)
4175                         while (c != '\n')
4176                                 c = getc(inf);
4177                 else if (c == TEX_LESC || c == TEX_SESC)
4178                         break;
4179         }
4180
4181         if (c == TEX_LESC) {
4182                 TEX_esc = TEX_LESC;
4183                 TEX_opgrp = '{';
4184                 TEX_clgrp = '}';
4185         } else {
4186                 TEX_esc = TEX_SESC;
4187                 TEX_opgrp = '<';
4188                 TEX_clgrp = '>';
4189         }
4190         rewind(inf);
4191 }
4192
4193 /* Read environment and prepend it to the default string.
4194    Build token table. */
4195 struct TEX_tabent *TEX_decode_env(evarname, defenv)
4196 char *evarname;
4197 char *defenv;
4198 {
4199         register char *env, *p;
4200
4201         struct TEX_tabent *tab;
4202         int size, i;
4203
4204         /* Append default string to environment. */
4205         env = getenv(evarname);
4206         if (!env)
4207                 env = defenv;
4208         else {
4209                 char *oldenv = env;
4210                 env = concat(oldenv, defenv, "");
4211                 free(oldenv);
4212         }
4213
4214         /* Allocate a token table */
4215         for (size = 1, p = env; p;)
4216                 if ((p = etags_strchr(p, ':')) && *++p != '\0')
4217                         size++;
4218         /* Add 1 to leave room for null terminator.  */
4219         tab = xnew(size + 1, struct TEX_tabent);
4220
4221         /* Unpack environment string into token table. Be careful about */
4222         /* zero-length strings (leading ':', "::" and trailing ':') */
4223         for (i = 0; *env;) {
4224                 p = etags_strchr(env, ':');
4225                 if (!p)         /* End of environment string. */
4226                         p = env + strlen(env);
4227                 if (p - env > 0) {      /* Only non-zero strings. */
4228                         tab[i].name = savenstr(env, p - env);
4229                         tab[i].len = strlen(tab[i].name);
4230                         i++;
4231                 }
4232                 if (*p)
4233                         env = p + 1;
4234                 else {
4235                         tab[i].name = NULL;     /* Mark end of table. */
4236                         tab[i].len = 0;
4237                         break;
4238                 }
4239         }
4240         return tab;
4241 }
4242
4243 /* If the text at CP matches one of the tag-defining TeX command names,
4244    return the pointer to the first occurrence of that command in TEX_toktab.
4245    Otherwise return -1.
4246    Keep the capital `T' in `token' for dumb truncating compilers
4247    (this distinguishes it from `TEX_toktab' */
4248 int TEX_Token(cp)
4249 char *cp;
4250 {
4251         int i;
4252
4253         for (i = 0; TEX_toktab[i].len > 0; i++)
4254                 if (strneq(TEX_toktab[i].name, cp, TEX_toktab[i].len))
4255                         return i;
4256         return -1;
4257 }
4258 \f
4259 /*
4260  * Prolog support (rewritten) by Anders Lindgren, Mar. 96
4261  *
4262  * Assumes that the predicate starts at column 0.
4263  * Only the first clause of a predicate is added.
4264  */
4265 int prolog_pred PP((char *s, char *last));
4266 void prolog_skip_comment PP((linebuffer * plb, FILE * inf));
4267 int prolog_atom PP((char *s, int pos));
4268
4269 void Prolog_functions(inf)
4270 FILE *inf;
4271 {
4272         char *cp, *last;
4273         int len;
4274         int allocated;
4275
4276         allocated = 0;
4277         len = 0;
4278         last = NULL;
4279
4280         LOOP_ON_INPUT_LINES(inf, lb, cp) {
4281                 if (cp[0] == '\0')      /* Empty line */
4282                         continue;
4283                 else if (isspace(cp[0]))        /* Not a predicate */
4284                         continue;
4285                 else if (cp[0] == '/' && cp[1] == '*')  /* comment. */
4286                         prolog_skip_comment(&lb, inf);
4287                 else if ((len = prolog_pred(cp, last)) > 0) {
4288                         /* Predicate.  Store the function name so that we only
4289                            generate a tag for the first clause.  */
4290                         if (last == NULL)
4291                                 last = xnew(len + 1, char);
4292                         else if (len + 1 > allocated)
4293                                 last = xrnew(last, len + 1, char);
4294                         allocated = len + 1;
4295                         strncpy(last, cp, len);
4296                         last[len] = '\0';
4297                 }
4298         }
4299         free(last);
4300 }
4301
4302 void prolog_skip_comment(plb, inf)
4303 linebuffer *plb;
4304 FILE *inf;
4305 {
4306         char *cp;
4307
4308         do {
4309                 for (cp = plb->buffer; *cp != '\0'; cp++)
4310                         if (cp[0] == '*' && cp[1] == '/')
4311                                 return;
4312                 lineno++;
4313                 linecharno += readline(plb, inf);
4314         }
4315         while (!feof(inf));
4316 }
4317
4318 /*
4319  * A predicate definition is added if it matches:
4320  *     <beginning of line><Prolog Atom><whitespace>(
4321  *
4322  * It is added to the tags database if it doesn't match the
4323  * name of the previous clause header.
4324  *
4325  * Return the size of the name of the predicate, or 0 if no header
4326  * was found.
4327  */
4328 int prolog_pred(s, last)
4329 char *s;
4330 char *last;                     /* Name of last clause. */
4331 {
4332         int pos;
4333         int len;
4334
4335         pos = prolog_atom(s, 0);
4336         if (pos < 1)
4337                 return 0;
4338
4339         len = pos;
4340         pos = skip_spaces(s + pos) - s;
4341
4342         if ((s[pos] == '(') || (s[pos] == '.')) {
4343                 if (s[pos] == '(')
4344                         pos++;
4345
4346                 /* Save only the first clause. */
4347                 if (last == NULL || len != (int)strlen(last)
4348                     || !strneq(s, last, len)) {
4349                         pfnote((CTAGS) ? savenstr(s, len) : NULL, TRUE,
4350                                s, pos, lineno, linecharno);
4351                         return len;
4352                 }
4353         }
4354         return 0;
4355 }
4356
4357 /*
4358  * Consume a Prolog atom.
4359  * Return the number of bytes consumed, or -1 if there was an error.
4360  *
4361  * A prolog atom, in this context, could be one of:
4362  * - An alphanumeric sequence, starting with a lower case letter.
4363  * - A quoted arbitrary string. Single quotes can escape themselves.
4364  *   Backslash quotes everything.
4365  */
4366 int prolog_atom(s, pos)
4367 char *s;
4368 int pos;
4369 {
4370         int origpos;
4371
4372         origpos = pos;
4373
4374         if (islower(s[pos]) || (s[pos] == '_')) {
4375                 /* The atom is unquoted. */
4376                 pos++;
4377                 while (isalnum(s[pos]) || (s[pos] == '_')) {
4378                         pos++;
4379                 }
4380                 return pos - origpos;
4381         } else if (s[pos] == '\'') {
4382                 pos++;
4383
4384                 while (1) {
4385                         if (s[pos] == '\'') {
4386                                 pos++;
4387                                 if (s[pos] != '\'')
4388                                         break;
4389                                 pos++;  /* A double quote */
4390                         } else if (s[pos] == '\0')
4391                                 /* Multiline quoted atoms are ignored. */
4392                                 return -1;
4393                         else if (s[pos] == '\\') {
4394                                 if (s[pos + 1] == '\0')
4395                                         return -1;
4396                                 pos += 2;
4397                         } else
4398                                 pos++;
4399                 }
4400                 return pos - origpos;
4401         } else
4402                 return -1;
4403 }
4404 \f
4405 /*
4406  * Support for Erlang  --  Anders Lindgren, Feb 1996.
4407  *
4408  * Generates tags for functions, defines, and records.
4409  *
4410  * Assumes that Erlang functions start at column 0.
4411  */
4412 int erlang_func PP((char *s, char *last));
4413 void erlang_attribute PP((char *s));
4414 int erlang_atom PP((char *s, int pos));
4415
4416 void Erlang_functions(inf)
4417 FILE *inf;
4418 {
4419         char *cp, *last;
4420         int len;
4421         int allocated;
4422
4423         allocated = 0;
4424         len = 0;
4425         last = NULL;
4426
4427         LOOP_ON_INPUT_LINES(inf, lb, cp) {
4428                 if (cp[0] == '\0')      /* Empty line */
4429                         continue;
4430                 else if (isspace(cp[0]))        /* Not function nor attribute */
4431                         continue;
4432                 else if (cp[0] == '%')  /* comment */
4433                         continue;
4434                 else if (cp[0] == '"')  /* Sometimes, strings start in column one */
4435                         continue;
4436                 else if (cp[0] == '-') {        /* attribute, e.g. "-define" */
4437                         erlang_attribute(cp);
4438                         free(last);
4439                         last = NULL;
4440                 } else if ((len = erlang_func(cp, last)) > 0) {
4441                         /*
4442                          * Function.  Store the function name so that we only
4443                          * generates a tag for the first clause.
4444                          */
4445                         if (last == NULL)
4446                                 last = xnew(len + 1, char);
4447                         else if (len + 1 > allocated)
4448                                 last = xrnew(last, len + 1, char);
4449                         allocated = len + 1;
4450                         strncpy(last, cp, len);
4451                         last[len] = '\0';
4452                 }
4453         }
4454         free(last);
4455 }
4456
4457 /*
4458  * A function definition is added if it matches:
4459  *     <beginning of line><Erlang Atom><whitespace>(
4460  *
4461  * It is added to the tags database if it doesn't match the
4462  * name of the previous clause header.
4463  *
4464  * Return the size of the name of the function, or 0 if no function
4465  * was found.
4466  */
4467 int erlang_func(s, last)
4468 char *s;
4469 char *last;                     /* Name of last clause. */
4470 {
4471         int pos;
4472         int len;
4473
4474         pos = erlang_atom(s, 0);
4475         if (pos < 1)
4476                 return 0;
4477
4478         len = pos;
4479         pos = skip_spaces(s + pos) - s;
4480
4481         /* Save only the first clause. */
4482         if (s[pos++] == '(' && (last == NULL || len != (int)strlen(last)
4483                                 || !strneq(s, last, len))) {
4484                 pfnote((CTAGS) ? savenstr(s, len) : NULL, TRUE,
4485                        s, pos, lineno, linecharno);
4486                 return len;
4487         }
4488
4489         return 0;
4490 }
4491
4492 /*
4493  * Handle attributes.  Currently, tags are generated for defines
4494  * and records.
4495  *
4496  * They are on the form:
4497  * -define(foo, bar).
4498  * -define(Foo(M, N), M+N).
4499  * -record(graph, {vtab = notable, cyclic = true}).
4500  */
4501 void erlang_attribute(s)
4502 char *s;
4503 {
4504         int pos;
4505         int len;
4506
4507         if (strneq(s, "-define", 7) || strneq(s, "-record", 7)) {
4508                 pos = skip_spaces(s + 7) - s;
4509                 if (s[pos++] == '(') {
4510                         pos = skip_spaces(s + pos) - s;
4511                         len = erlang_atom(s, pos);
4512                         if (len != 0)
4513                                 pfnote((CTAGS) ? savenstr(&s[pos], len) : NULL,
4514                                        TRUE, s, pos + len, lineno, linecharno);
4515                 }
4516         }
4517         return;
4518 }
4519
4520 /*
4521  * Consume an Erlang atom (or variable).
4522  * Return the number of bytes consumed, or -1 if there was an error.
4523  */
4524 int erlang_atom(s, pos)
4525 char *s;
4526 int pos;
4527 {
4528         int origpos;
4529
4530         origpos = pos;
4531
4532         if (isalpha(s[pos]) || s[pos] == '_') {
4533                 /* The atom is unquoted. */
4534                 pos++;
4535                 while (isalnum(s[pos]) || s[pos] == '_')
4536                         pos++;
4537                 return pos - origpos;
4538         } else if (s[pos] == '\'') {
4539                 pos++;
4540
4541                 while (1) {
4542                         if (s[pos] == '\'') {
4543                                 pos++;
4544                                 break;
4545                         } else if (s[pos] == '\0')
4546                                 /* Multiline quoted atoms are ignored. */
4547                                 return -1;
4548                         else if (s[pos] == '\\') {
4549                                 if (s[pos + 1] == '\0')
4550                                         return -1;
4551                                 pos += 2;
4552                         } else
4553                                 pos++;
4554                 }
4555                 return pos - origpos;
4556         } else
4557                 return -1;
4558 }
4559 \f
4560 #ifdef ETAGS_REGEXPS
4561
4562 /* Take a string like "/blah/" and turn it into "blah", making sure
4563    that the first and last characters are the same, and handling
4564    quoted separator characters.  Actually, stops on the occurrence of
4565    an unquoted separator.  Also turns "\t" into a Tab character.
4566    Returns pointer to terminating separator.  Works in place.  Null
4567    terminates name string. */
4568 char *scan_separators PP((char *name));
4569 char *scan_separators(name)
4570 char *name;
4571 {
4572         char sep = name[0];
4573         char *copyto = name;
4574         bool quoted = FALSE;
4575
4576         for (++name; *name != '\0'; ++name) {
4577                 if (quoted) {
4578                         if (*name == 't')
4579                                 *copyto++ = '\t';
4580                         else if (*name == sep)
4581                                 *copyto++ = sep;
4582                         else {
4583                                 /* Something else is quoted, so preserve the quote. */
4584                                 *copyto++ = '\\';
4585                                 *copyto++ = *name;
4586                         }
4587                         quoted = FALSE;
4588                 } else if (*name == '\\')
4589                         quoted = TRUE;
4590                 else if (*name == sep)
4591                         break;
4592                 else
4593                         *copyto++ = *name;
4594         }
4595
4596         /* Terminate copied string. */
4597         *copyto = '\0';
4598         return name;
4599 }
4600
4601 /* Look at the argument of --regex or --no-regex and do the right
4602    thing.  Same for each line of a regexp file. */
4603 void analyse_regex(regex_arg)
4604 char *regex_arg;
4605 {
4606         if (regex_arg == NULL) {
4607                 free_patterns();        /* --no-regex: remove existing regexps */
4608                 return;
4609         }
4610         /* A real --regexp option or a line in a regexp file. */
4611         switch (regex_arg[0]) {
4612                 /* Comments in regexp file or null arg to --regex. */
4613         case '\0':
4614         case ' ':
4615         case '\t':
4616                 break;
4617
4618                 /* Read a regex file.  This is recursive and may result in a
4619                    loop, which will stop when the file descriptors are exhausted. */
4620         case '@':
4621                 {
4622                         FILE *regexfp;
4623                         linebuffer regexbuf;
4624                         char *regexfile = regex_arg + 1;
4625
4626                         /* regexfile is a file containing regexps, one per line. */
4627                         regexfp = fopen(regexfile, "r");
4628                         if (regexfp == NULL) {
4629                                 pfatal(regexfile);
4630                                 return;
4631                         }
4632                         initbuffer(&regexbuf);
4633                         while (readline_internal(&regexbuf, regexfp) > 0)
4634                                 analyse_regex(regexbuf.buffer);
4635                         free(regexbuf.buffer);
4636                         fclose(regexfp);
4637                 }
4638                 break;
4639
4640                 /* Regexp to be used for a specific language only. */
4641         case '{':
4642                 {
4643                         language *lang;
4644                         char *lang_name = regex_arg + 1;
4645                         char *cp;
4646
4647                         for (cp = lang_name; *cp != '}'; cp++)
4648                                 if (*cp == '\0') {
4649                                         error
4650                                             ("unterminated language name in regex: %s",
4651                                              regex_arg);
4652                                         return;
4653                                 }
4654                         *cp = '\0';
4655                         lang = get_language_from_name(lang_name);
4656                         if (lang == NULL)
4657                                 return;
4658                         add_regex(cp + 1, lang);
4659                 }
4660                 break;
4661
4662                 /* Regexp to be used for any language. */
4663         default:
4664                 add_regex(regex_arg, NULL);
4665                 break;
4666         }
4667 }
4668
4669 /* Turn a name, which is an ed-style (but Emacs syntax) regular
4670    expression, into a real regular expression by compiling it. */
4671 void add_regex(regexp_pattern, lang)
4672 char *regexp_pattern;
4673 language *lang;
4674 {
4675         char *name;
4676         const char *err;
4677         struct re_pattern_buffer *patbuf;
4678         pattern *pp;
4679
4680         if (regexp_pattern[strlen(regexp_pattern) - 1] != regexp_pattern[0]) {
4681                 error("%s: unterminated regexp", regexp_pattern);
4682                 return;
4683         }
4684         name = scan_separators(regexp_pattern);
4685         if (regexp_pattern[0] == '\0') {
4686                 error("null regexp", (char *)NULL);
4687                 return;
4688         }
4689         (void)scan_separators(name);
4690
4691         patbuf = xnew(1, struct re_pattern_buffer);
4692         patbuf->translate = NULL;
4693         patbuf->fastmap = NULL;
4694         patbuf->buffer = NULL;
4695         patbuf->allocated = 0;
4696
4697         err =
4698             re_compile_pattern(regexp_pattern, strlen(regexp_pattern), patbuf);
4699         if (err != NULL) {
4700                 error("%s while compiling pattern", err);
4701                 return;
4702         }
4703
4704         pp = p_head;
4705         p_head = xnew(1, pattern);
4706         p_head->regex = savestr(regexp_pattern);
4707         p_head->p_next = pp;
4708         p_head->language = lang;
4709         p_head->pattern = patbuf;
4710         p_head->name_pattern = savestr(name);
4711         p_head->error_signaled = FALSE;
4712 }
4713
4714 /*
4715  * Do the substitutions indicated by the regular expression and
4716  * arguments.
4717  */
4718 char *substitute PP((char *in, char *out, struct re_registers * regs));
4719 char *substitute(in, out, regs)
4720 char *in, *out;
4721 struct re_registers *regs;
4722 {
4723         char *result, *t;
4724         int size, dig, diglen;
4725
4726         result = NULL;
4727         size = strlen(out);
4728
4729         /* Pass 1: figure out how much to allocate by finding all \N strings. */
4730         if (out[size - 1] == '\\')
4731                 fatal("pattern error in \"%s\"", out);
4732         for (t = etags_strchr(out, '\\');
4733              t != NULL; t = etags_strchr(t + 2, '\\'))
4734                 if (isdigit(t[1])) {
4735                         dig = t[1] - '0';
4736                         diglen = regs->end[dig] - regs->start[dig];
4737                         size += diglen - 2;
4738                 } else
4739                         size -= 1;
4740
4741         /* Allocate space and do the substitutions. */
4742         result = xnew(size + 1, char);
4743
4744         for (t = result; *out != '\0'; out++)
4745                 if (*out == '\\' && isdigit(*++out)) {
4746                         /* Using "dig2" satisfies my debugger.  Bleah. */
4747                         dig = *out - '0';
4748                         diglen = regs->end[dig] - regs->start[dig];
4749                         strncpy(t, in + regs->start[dig], diglen);
4750                         t += diglen;
4751                 } else
4752                         *t++ = *out;
4753         *t = '\0';
4754
4755         if (DEBUG && (t > result + size || t - result != strlen(result)))
4756                 abort();
4757
4758         return result;
4759 }
4760
4761 /* Deallocate all patterns. */
4762 void free_patterns()
4763 {
4764         pattern *pp;
4765         while (p_head != NULL) {
4766                 pp = p_head->p_next;
4767                 free(p_head->regex);
4768                 free(p_head->name_pattern);
4769                 free(p_head);
4770                 p_head = pp;
4771         }
4772         return;
4773 }
4774 \f
4775 #endif                          /* ETAGS_REGEXPS */
4776 /* Initialize a linebuffer for use */
4777 void initbuffer(lbp)
4778 linebuffer *lbp;
4779 {
4780         lbp->size = 200;
4781         lbp->buffer = xnew(200, char);
4782 }
4783
4784 /*
4785  * Read a line of text from `stream' into `lbp', excluding the
4786  * newline or CR-NL, if any.  Return the number of characters read from
4787  * `stream', which is the length of the line including the newline.
4788  *
4789  * On DOS or Windows we do not count the CR character, if any, before the
4790  * NL, in the returned length; this mirrors the behavior of emacs on those
4791  * platforms (for text files, it translates CR-NL to NL as it reads in the
4792  * file).
4793  */
4794 long readline_internal(lbp, stream)
4795 linebuffer *lbp;
4796 register FILE *stream;
4797 {
4798         char *buffer = lbp->buffer;
4799         register char *p = lbp->buffer;
4800         register char *pend;
4801         int chars_deleted;
4802
4803         pend = p + lbp->size;   /* Separate to avoid 386/IX compiler bug.  */
4804
4805         while (1) {
4806                 register int c = getc(stream);
4807                 if (p == pend) {
4808                         /* We're at the end of linebuffer: expand it. */
4809                         lbp->size *= 2;
4810                         buffer = xrnew(buffer, lbp->size, char);
4811                         p += buffer - lbp->buffer;
4812                         pend = buffer + lbp->size;
4813                         lbp->buffer = buffer;
4814                 }
4815                 if (c == EOF) {
4816                         *p = '\0';
4817                         chars_deleted = 0;
4818                         break;
4819                 }
4820                 if (c == '\n') {
4821                         if (p > buffer && p[-1] == '\r') {
4822                                 p -= 1;
4823                                 chars_deleted = 2;
4824                         } else {
4825                                 chars_deleted = 1;
4826                         }
4827                         *p = '\0';
4828                         break;
4829                 }
4830                 *p++ = c;
4831         }
4832         lbp->len = p - buffer;
4833
4834         return lbp->len + chars_deleted;
4835 }
4836
4837 /*
4838  * Like readline_internal, above, but in addition try to match the
4839  * input line against relevant regular expressions.
4840  */
4841 long readline(lbp, stream)
4842 linebuffer *lbp;
4843 FILE *stream;
4844 {
4845         /* Read new line. */
4846         long result = readline_internal(lbp, stream);
4847 #ifdef ETAGS_REGEXPS
4848         int match;
4849         pattern *pp;
4850
4851         /* Match against relevant patterns. */
4852         if (lbp->len > 0)
4853                 for (pp = p_head; pp != NULL; pp = pp->p_next) {
4854                         /* Only use generic regexps or those for the current language. */
4855                         if (pp->language != NULL && pp->language != curlang)
4856                                 continue;
4857
4858                         match =
4859                             re_match(pp->pattern, lbp->buffer, lbp->len, 0,
4860                                      &pp->regs);
4861                         switch (match) {
4862                         case -2:
4863                                 /* Some error. */
4864                                 if (!pp->error_signaled) {
4865                                         error("error while matching \"%s\"",
4866                                               pp->regex);
4867                                         pp->error_signaled = TRUE;
4868                                 }
4869                                 break;
4870                         case -1:
4871                                 /* No match. */
4872                                 break;
4873                         default:
4874                                 /* Match occurred.  Construct a tag. */
4875                                 if (pp->name_pattern[0] != '\0') {
4876                                         /* Make a named tag. */
4877                                         char *name = substitute(lbp->buffer,
4878                                                                 pp->
4879                                                                 name_pattern,
4880                                                                 &pp->regs);
4881                                         if (name != NULL)
4882                                                 pfnote(name, TRUE, lbp->buffer,
4883                                                        match, lineno,
4884                                                        linecharno);
4885                                 } else {
4886                                         /* Make an unnamed tag. */
4887                                         pfnote((char *)NULL, TRUE,
4888                                                lbp->buffer, match, lineno,
4889                                                linecharno);
4890                                 }
4891                                 break;
4892                         }
4893                 }
4894 #endif                          /* ETAGS_REGEXPS */
4895
4896         return result;
4897 }
4898 \f
4899 /*
4900  * Return a pointer to a space of size strlen(cp)+1 allocated
4901  * with xnew where the string CP has been copied.
4902  */
4903 char *savestr(cp)
4904 char *cp;
4905 {
4906         return savenstr(cp, strlen(cp));
4907 }
4908
4909 /*
4910  * Return a pointer to a space of size LEN+1 allocated with xnew where
4911  * the string CP has been copied for at most the first LEN characters.
4912  */
4913 char *savenstr(cp, len)
4914 char *cp;
4915 int len;
4916 {
4917         register char *dp;
4918
4919         dp = xnew(len + 1, char);
4920         strncpy(dp, cp, len);
4921         dp[len] = '\0';
4922         return dp;
4923 }
4924
4925 /*
4926  * Return the ptr in sp at which the character c last
4927  * appears; NULL if not found
4928  *
4929  * Identical to System V strrchr, included for portability.
4930  */
4931 char *etags_strrchr(sp, c)
4932 register char *sp;
4933 register int c;
4934 {
4935         register char *r;
4936
4937         r = NULL;
4938         do {
4939                 if (*sp == c)
4940                         r = sp;
4941         } while (*sp++);
4942         return r;
4943 }
4944
4945 /*
4946  * Return the ptr in sp at which the character c first
4947  * appears; NULL if not found
4948  *
4949  * Identical to System V strchr, included for portability.
4950  */
4951 char *etags_strchr(sp, c)
4952 register char *sp;
4953 register int c;
4954 {
4955         do {
4956                 if (*sp == c)
4957                         return sp;
4958         } while (*sp++);
4959         return NULL;
4960 }
4961
4962 /* Skip spaces, return new pointer. */
4963 char *skip_spaces(cp)
4964 char *cp;
4965 {
4966         while (isspace(*cp))    /* isspace('\0')==FALSE */
4967                 cp++;
4968         return cp;
4969 }
4970
4971 /* Skip non spaces, return new pointer. */
4972 char *skip_non_spaces(cp)
4973 char *cp;
4974 {
4975         while (!iswhite(*cp))   /* iswhite('\0')==TRUE */
4976                 cp++;
4977         return cp;
4978 }
4979
4980 /* Print error message and exit.  */
4981 void fatal(s1, s2)
4982 char *s1, *s2;
4983 {
4984         error(s1, s2);
4985         exit(BAD);
4986 }
4987
4988 void pfatal(s1)
4989 char *s1;
4990 {
4991         perror(s1);
4992         exit(BAD);
4993 }
4994
4995 void suggest_asking_for_help()
4996 {
4997         fprintf(stderr, "\tTry `%s %s' for a complete list of options.\n",
4998                 progname,
4999 #ifdef LONG_OPTIONS
5000                 "--help"
5001 #else
5002                 "-h"
5003 #endif
5004             );
5005         exit(BAD);
5006 }
5007
5008 /* Print error message.  `s1' is printf control string, `s2' is arg for it. */
5009 void error(s1, s2)
5010 const char *s1, *s2;
5011 {
5012         fprintf(stderr, "%s: ", progname);
5013         fprintf(stderr, s1, s2);
5014         fprintf(stderr, "\n");
5015 }
5016
5017 /* Return a newly-allocated string whose contents
5018    concatenate those of s1, s2, s3.  */
5019 char *concat(s1, s2, s3)
5020 char *s1, *s2, *s3;
5021 {
5022         int len1 = strlen(s1), len2 = strlen(s2), len3 = strlen(s3);
5023         char *result = xnew(len1 + len2 + len3 + 1, char);
5024
5025         strcpy(result, s1);
5026         strcpy(result + len1, s2);
5027         strcpy(result + len1 + len2, s3);
5028         result[len1 + len2 + len3] = '\0';
5029
5030         return result;
5031 }
5032 \f
5033 /* Does the same work as the system V getcwd, but does not need to
5034    guess the buffer size in advance. */
5035 char *etags_getcwd()
5036 {
5037 #ifdef HAVE_GETCWD
5038         int bufsize = 200;
5039         char *path = xnew(bufsize, char);
5040
5041         while (getcwd(path, bufsize) == NULL) {
5042                 if (errno != ERANGE)
5043                         pfatal("getcwd");
5044                 bufsize *= 2;
5045                 free(path);
5046                 path = xnew(bufsize, char);
5047         }
5048
5049         canonicalize_filename(path);
5050         return path;
5051
5052 #else                           /* not HAVE_GETCWD */
5053         linebuffer path;
5054         FILE *pipe;
5055
5056         initbuffer(&path);
5057         pipe = (FILE *) popen("pwd 2>/dev/null", "r");
5058         if (pipe == NULL || readline_internal(&path, pipe) == 0)
5059                 pfatal("pwd");
5060         pclose(pipe);
5061
5062         return path.buffer;
5063 #endif                          /* not HAVE_GETCWD */
5064 }
5065
5066 /* Return a newly allocated string containing the file name of FILE
5067    relative to the absolute directory DIR (which should end with a slash). */
5068 char *relative_filename(file, dir)
5069 char *file, *dir;
5070 {
5071         char *fp, *dp, *afn, *res;
5072         int i;
5073         ssize_t res_left;
5074
5075         /* Find the common root of file and dir (with a trailing slash). */
5076         afn = absolute_filename(file, cwd);
5077         fp = afn;
5078         dp = dir;
5079         while (*fp++ == *dp++)
5080                 continue;
5081         fp--, dp--;             /* back to the first differing char */
5082         do                      /* look at the equal chars until '/' */
5083                 fp--, dp--;
5084         while (*fp != '/');
5085         fp ++; /* Advance past the '/' */
5086
5087         /* Build a sequence of "../" strings for the resulting relative file name. */
5088         i = 0;
5089         while ((dp = etags_strchr(dp + 1, '/')) != NULL)
5090                 i += 1;
5091         res_left = 3 * i + strlen(fp);
5092         res = xnew( res_left + 1, char);
5093         res[0] = '\0';
5094         for ( ; i-- > 0 ; res_left -= 4 )
5095                 strncat(res, "../", res_left );
5096
5097         /* Add the file name relative to the common root of file and dir. */
5098         strncat(res, fp, res_left);
5099         free(afn);
5100
5101         return res;
5102 }
5103
5104 /* Return a newly allocated string containing the absolute file name
5105    of FILE given DIR (which should end with a slash). */
5106 char *absolute_filename(file, dir)
5107 char *file, *dir;
5108 {
5109         char *slashp, *cp, *res;
5110
5111         if (filename_is_absolute(file))
5112                 res = savestr(file);
5113         else
5114                 res = concat(dir, file, "");
5115
5116         /* Delete the "/dirname/.." and "/." substrings. */
5117         slashp = etags_strchr(res, '/');
5118         while (slashp != NULL && slashp[0] != '\0') {
5119                 if (slashp[1] == '.') {
5120                         if (slashp[2] == '.'
5121                             && (slashp[3] == '/' || slashp[3] == '\0')) {
5122                                 cp = slashp;
5123                                 do
5124                                         cp--;
5125                                 while (cp >= res && !filename_is_absolute(cp));
5126                                 if (cp < res)
5127                                         cp = slashp;    /* the absolute name begins with "/.." */
5128                                 strcpy(cp, slashp + 3);
5129                                 slashp = cp;
5130                                 continue;
5131                         } else if (slashp[2] == '/' || slashp[2] == '\0') {
5132                                 strcpy(slashp, slashp + 2);
5133                                 continue;
5134                         }
5135                 }
5136
5137                 slashp = etags_strchr(slashp + 1, '/');
5138         }
5139
5140         if (res[0] == '\0')
5141                 return savestr("/");
5142         else
5143                 return res;
5144 }
5145
5146 /* Return a newly allocated string containing the absolute
5147    file name of dir where FILE resides given DIR (which should
5148    end with a slash). */
5149 char *absolute_dirname(file, dir)
5150 char *file, *dir;
5151 {
5152         char *slashp, *res;
5153         char save;
5154
5155         canonicalize_filename(file);
5156         slashp = etags_strrchr(file, '/');
5157         if (slashp == NULL)
5158                 return savestr(dir);
5159         save = slashp[1];
5160         slashp[1] = '\0';
5161         res = absolute_filename(file, dir);
5162         slashp[1] = save;
5163
5164         return res;
5165 }
5166
5167 /* Whether the argument string is an absolute file name.  The argument
5168    string must have been canonicalized with canonicalize_filename. */
5169 bool filename_is_absolute(fn)
5170 char *fn;
5171 {
5172         return (fn[0] == '/');
5173 }
5174
5175 /* Translate backslashes into slashes.  Works in place. */
5176 void canonicalize_filename(fn)
5177 register char *fn;
5178 {
5179         /* No action. */
5180 }
5181
5182 /* Increase the size of a linebuffer. */
5183 void grow_linebuffer(lbp, toksize)
5184 linebuffer *lbp;
5185 int toksize;
5186 {
5187         while (lbp->size < toksize)
5188                 lbp->size *= 2;
5189         lbp->buffer = xrnew(lbp->buffer, lbp->size, char);
5190 }
5191
5192 /* Like malloc but get fatal error if memory is exhausted.  */
5193 long *xmalloc(size)
5194 unsigned int size;
5195 {
5196         long *result = (long *)malloc(size);
5197         if (result == NULL)
5198                 fatal("virtual memory exhausted", (char *)NULL);
5199         return result;
5200 }
5201
5202 long *xrealloc(ptr, size)
5203 char *ptr;
5204 unsigned int size;
5205 {
5206         long *result = (long *)realloc(ptr, size);
5207         if (result == NULL)
5208                 fatal("virtual memory exhausted", (char *)NULL);
5209         return result;
5210 }