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