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