Avoid warning about dangling else
[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 }
3541 \f
3542 /* Fortran parsing */
3543
3544 bool tail PP((char *cp));
3545 bool tail(cp)
3546 char *cp;
3547 {
3548         register int len = 0;
3549
3550         while (*cp && lowcase(*cp) == lowcase(dbp[len]))
3551                 cp++, len++;
3552         if (*cp == '\0' && !intoken(dbp[len])) {
3553                 dbp += len;
3554                 return TRUE;
3555         }
3556         return FALSE;
3557 }
3558
3559 void takeprec()
3560 {
3561         dbp = skip_spaces(dbp);
3562         if (*dbp != '*')
3563                 return;
3564         dbp++;
3565         dbp = skip_spaces(dbp);
3566         if (strneq(dbp, "(*)", 3)) {
3567                 dbp += 3;
3568                 return;
3569         }
3570         if (!isdigit(*dbp)) {
3571                 --dbp;          /* force failure */
3572                 return;
3573         }
3574         do
3575                 dbp++;
3576         while (isdigit(*dbp));
3577 }
3578
3579 void getit PP((FILE * inf));
3580 void getit(inf)
3581 FILE *inf;
3582 {
3583         register char *cp;
3584
3585         dbp = skip_spaces(dbp);
3586         if (*dbp == '\0') {
3587                 lineno++;
3588                 linecharno = charno;
3589                 charno += readline(&lb, inf);
3590                 dbp = lb.buffer;
3591                 if (dbp[5] != '&')
3592                         return;
3593                 dbp += 6;
3594                 dbp = skip_spaces(dbp);
3595         }
3596         if (!isalpha(*dbp)
3597             && *dbp != '_' && *dbp != '$')
3598                 return;
3599         for (cp = dbp + 1; *cp && intoken(*cp); cp++)
3600                 continue;
3601         pfnote((CTAGS) ? savenstr(dbp, cp - dbp) : NULL, TRUE,
3602                lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
3603 }
3604
3605 void Fortran_functions(inf)
3606 FILE *inf;
3607 {
3608         LOOP_ON_INPUT_LINES(inf, lb, dbp) {
3609                 if (*dbp == '%')
3610                         dbp++;  /* Ratfor escape to fortran */
3611                 dbp = skip_spaces(dbp);
3612                 if (*dbp == '\0')
3613                         continue;
3614                 switch (lowcase(*dbp)) {
3615                 case 'i':
3616                         if (tail("integer"))
3617                                 takeprec();
3618                         break;
3619                 case 'r':
3620                         if (tail("real"))
3621                                 takeprec();
3622                         break;
3623                 case 'l':
3624                         if (tail("logical"))
3625                                 takeprec();
3626                         break;
3627                 case 'c':
3628                         if (tail("complex") || tail("character"))
3629                                 takeprec();
3630                         break;
3631                 case 'd':
3632                         if (tail("double")) {
3633                                 dbp = skip_spaces(dbp);
3634                                 if (*dbp == '\0')
3635                                         continue;
3636                                 if (tail("precision"))
3637                                         break;
3638                                 continue;
3639                         }
3640                         break;
3641                 default:
3642                         break;
3643                 }
3644                 dbp = skip_spaces(dbp);
3645                 if (*dbp == '\0')
3646                         continue;
3647                 switch (lowcase(*dbp)) {
3648                 case 'f':
3649                         if (tail("function"))
3650                                 getit(inf);
3651                         continue;
3652                 case 's':
3653                         if (tail("subroutine"))
3654                                 getit(inf);
3655                         continue;
3656                 case 'e':
3657                         if (tail("entry"))
3658                                 getit(inf);
3659                         continue;
3660                 case 'p':
3661                         if (tail("program")) {
3662                                 getit(inf);
3663                                 continue;
3664                         }
3665                         if (tail("procedure"))
3666                                 getit(inf);
3667                         continue;
3668                 default:
3669                         break;
3670                 }
3671         }
3672 }
3673 \f
3674 /*
3675  * Bob Weiner, Motorola Inc., 4/3/94
3676  * Unix and microcontroller assembly tag handling
3677  * look for '^[a-zA-Z_.$][a-zA_Z0-9_.$]*[: ^I^J]'
3678  */
3679 void Asm_labels(FILE * inf)
3680 {
3681         register char *cp;
3682
3683         LOOP_ON_INPUT_LINES(inf, lb, cp) {
3684                 /* If first char is alphabetic or one of [_.$], test for colon
3685                    following identifier. */
3686                 if (isalpha(*cp) || *cp == '_' || *cp == '.' || *cp == '$') {
3687                         /* Read past label. */
3688                         cp++;
3689                         while (isalnum(*cp) || *cp == '_' || *cp == '.'
3690                                || *cp == '$')
3691                                 cp++;
3692                         if (*cp == ':' || isspace(*cp)) {
3693                                 /* Found end of label, so copy it and add it to the table. */
3694                                 pfnote((CTAGS) ?
3695                                        savenstr(lb.buffer,
3696                                                 cp - lb.buffer) : NULL, TRUE,
3697                                        lb.buffer, cp - lb.buffer + 1, lineno,
3698                                        linecharno);
3699                         }
3700                 }
3701         }
3702 }
3703 \f
3704 /*
3705  * Perl support by Bart Robinson <lomew@cs.utah.edu>
3706  *              enhanced by Michael Ernst <mernst@alum.mit.edu>
3707  * Perl sub names: look for /^sub[ \t\n]+[^ \t\n{]+/
3708  * Perl variable names: /^(my|local).../
3709  */
3710 void Perl_functions(inf)
3711 FILE *inf;
3712 {
3713         register char *cp;
3714
3715         LOOP_ON_INPUT_LINES(inf, lb, cp) {
3716                 if (*cp++ == 's'
3717                     && *cp++ == 'u' && *cp++ == 'b' && isspace(*cp++)) {
3718                         cp = skip_spaces(cp);
3719                         if (*cp != '\0') {
3720                                 while (*cp != '\0'
3721                                        && !isspace(*cp) && *cp != '{'
3722                                        && *cp != '(')
3723                                         cp++;
3724                                 pfnote((CTAGS) ?
3725                                        savenstr(lb.buffer,
3726                                                 cp - lb.buffer) : NULL, TRUE,
3727                                        lb.buffer, cp - lb.buffer + 1, lineno,
3728                                        linecharno);
3729                         }
3730                 } else if (globals      /* only if tagging global vars is enabled */
3731                            && ((cp = lb.buffer, *cp++ == 'm' && *cp++ == 'y')
3732                                || (cp = lb.buffer,
3733                                    *cp++ == 'l'
3734                                    && *cp++ == 'o'
3735                                    && *cp++ == 'c'
3736                                    && *cp++ == 'a' && *cp++ == 'l'))
3737                            && (*cp == '(' || isspace(*cp))) {
3738                         /* After "my" or "local", but before any following paren or space. */
3739                         char *varname = NULL;
3740
3741                         cp = skip_spaces(cp);
3742                         if (*cp == '$' || *cp == '@' || *cp == '%') {
3743                                 char *varstart = ++cp;
3744                                 while (isalnum(*cp) || *cp == '_')
3745                                         cp++;
3746                                 varname = savenstr(varstart, cp - varstart);
3747                         } else {
3748                                 /* Should be examining a variable list at this point;
3749                                    could insist on seeing an open parenthesis. */
3750                                 while (*cp != '\0' && *cp != ';' && *cp != '='
3751                                        && *cp != ')')
3752                                         cp++;
3753                         }
3754
3755                         /* Perhaps I should back cp up one character, so the TAGS table
3756                            doesn't mention (and so depend upon) the following char. */
3757                         pfnote((CTAGS) ? savenstr(lb.buffer, cp - lb.buffer) :
3758                                varname, FALSE, lb.buffer, cp - lb.buffer + 1,
3759                                lineno, linecharno);
3760                 }
3761         }
3762 }
3763 \f
3764 /*
3765  * Python support by Eric S. Raymond <esr@thyrsus.com>
3766  * Look for /^def[ \t\n]+[^ \t\n(:]+/ or /^class[ \t\n]+[^ \t\n(:]+/
3767  */
3768 void Python_functions(inf)
3769 FILE *inf;
3770 {
3771         register char *cp;
3772
3773         LOOP_ON_INPUT_LINES(inf, lb, cp) {
3774                 if (*cp++ == 'd'
3775                     && *cp++ == 'e' && *cp++ == 'f' && isspace(*cp++)) {
3776                         cp = skip_spaces(cp);
3777                         while (*cp != '\0' && !isspace(*cp) && *cp != '('
3778                                && *cp != ':')
3779                                 cp++;
3780                         pfnote((char *)NULL, TRUE,
3781                                lb.buffer, cp - lb.buffer + 1, lineno,
3782                                linecharno);
3783                 }
3784
3785                 cp = lb.buffer;
3786                 if (*cp++ == 'c'
3787                     && *cp++ == 'l'
3788                     && *cp++ == 'a'
3789                     && *cp++ == 's' && *cp++ == 's' && isspace(*cp++)) {
3790                         cp = skip_spaces(cp);
3791                         while (*cp != '\0' && !isspace(*cp) && *cp != '('
3792                                && *cp != ':')
3793                                 cp++;
3794                         pfnote((char *)NULL, TRUE,
3795                                lb.buffer, cp - lb.buffer + 1, lineno,
3796                                linecharno);
3797                 }
3798         }
3799 }
3800 \f
3801 /* Idea by Corny de Souza
3802  * Cobol tag functions
3803  * We could look for anything that could be a paragraph name.
3804  * i.e. anything that starts in column 8 is one word and ends in a full stop.
3805  */
3806 void Cobol_paragraphs(inf)
3807 FILE *inf;
3808 {
3809         register char *bp, *ep;
3810
3811         LOOP_ON_INPUT_LINES(inf, lb, bp) {
3812                 if (lb.len < 9)
3813                         continue;
3814                 bp += 8;
3815
3816                 /* If eoln, compiler option or comment ignore whole line. */
3817                 if (bp[-1] != ' ' || !isalnum(bp[0]))
3818                         continue;
3819
3820                 for (ep = bp; isalnum(*ep) || *ep == '-'; ep++)
3821                         continue;
3822                 if (*ep++ == '.')
3823                         pfnote((CTAGS) ? savenstr(bp, ep - bp) : NULL, TRUE,
3824                                lb.buffer, ep - lb.buffer + 1, lineno,
3825                                linecharno);
3826         }
3827 }
3828 \f
3829 /* Added by Mosur Mohan, 4/22/88 */
3830 /* Pascal parsing                */
3831
3832 /*
3833  *  Locates tags for procedures & functions.  Doesn't do any type- or
3834  *  var-definitions.  It does look for the keyword "extern" or
3835  *  "forward" immediately following the procedure statement; if found,
3836  *  the tag is skipped.
3837  */
3838 void Pascal_functions(inf)
3839 FILE *inf;
3840 {
3841         linebuffer tline;       /* mostly copied from C_entries */
3842         long save_lcno;
3843         int save_lineno, save_len;
3844         char c, *cp, *namebuf;
3845
3846         bool                    /* each of these flags is TRUE iff: */
3847             incomment,          /* point is inside a comment */
3848             inquote,            /* point is inside '..' string */
3849             get_tagname,        /* point is after PROCEDURE/FUNCTION
3850                                    keyword, so next item = potential tag */
3851             found_tag,          /* point is after a potential tag */
3852             inparms,            /* point is within parameter-list */
3853             verify_tag;         /* point has passed the parm-list, so the
3854                                    next token will determine whether this
3855                                    is a FORWARD/EXTERN to be ignored, or
3856                                    whether it is a real tag */
3857
3858         save_lcno = save_lineno = save_len = 0; /* keep compiler quiet */
3859         namebuf = NULL;         /* keep compiler quiet */
3860         lineno = 0;
3861         charno = 0;
3862         dbp = lb.buffer;
3863         *dbp = '\0';
3864         initbuffer(&tline);
3865
3866         incomment = inquote = FALSE;
3867         found_tag = FALSE;      /* have a proc name; check if extern */
3868         get_tagname = FALSE;    /* have found "procedure" keyword    */
3869         inparms = FALSE;        /* found '(' after "proc"            */
3870         verify_tag = FALSE;     /* check if "extern" is ahead        */
3871
3872         while (!feof(inf)) {    /* long main loop to get next char */
3873                 c = *dbp++;
3874                 if (c == '\0') {        /* if end of line */
3875                         lineno++;
3876                         linecharno = charno;
3877                         charno += readline(&lb, inf);
3878                         dbp = lb.buffer;
3879                         if (*dbp == '\0')
3880                                 continue;
3881                         if (!((found_tag && verify_tag)
3882                               || get_tagname))
3883                                 c = *dbp++;     /* only if don't need *dbp pointing
3884                                                    to the beginning of the name of
3885                                                    the procedure or function */
3886                 }
3887                 if (incomment) {
3888                         if (c == '}')   /* within { } comments */
3889                                 incomment = FALSE;
3890                         else if (c == '*' && *dbp == ')') {     /* within (* *) comments */
3891                                 dbp++;
3892                                 incomment = FALSE;
3893                         }
3894                         continue;
3895                 } else if (inquote) {
3896                         if (c == '\'')
3897                                 inquote = FALSE;
3898                         continue;
3899                 } else
3900                         switch (c) {
3901                         case '\'':
3902                                 inquote = TRUE; /* found first quote */
3903                                 continue;
3904                         case '{':       /* found open { comment */
3905                                 incomment = TRUE;
3906                                 continue;
3907                         case '(':
3908                                 if (*dbp == '*') {      /* found open (* comment */
3909                                         incomment = TRUE;
3910                                         dbp++;
3911                                 } else if (found_tag)   /* found '(' after tag, i.e., parm-list */
3912                                         inparms = TRUE;
3913                                 continue;
3914                         case ')':       /* end of parms list */
3915                                 if (inparms)
3916                                         inparms = FALSE;
3917                                 continue;
3918                         case ';':
3919                                 if (found_tag && !inparms) {    /* end of proc or fn stmt */
3920                                         verify_tag = TRUE;
3921                                         break;
3922                                 }
3923                                 continue;
3924                         default:
3925                                 break;
3926                         }
3927                 if (found_tag && verify_tag && (*dbp != ' ')) {
3928                         /* check if this is an "extern" declaration */
3929                         if (*dbp == '\0')
3930                                 continue;
3931                         if (lowcase(*dbp == 'e')) {
3932                                 if (tail("extern")) {   /* superfluous, really! */
3933                                         found_tag = FALSE;
3934                                         verify_tag = FALSE;
3935                                 }
3936                         } else if (lowcase(*dbp) == 'f') {
3937                                 if (tail("forward")) {  /*  check for forward reference */
3938                                         found_tag = FALSE;
3939                                         verify_tag = FALSE;
3940                                 }
3941                         }
3942                         if (found_tag && verify_tag) {  /* not external proc, so make tag */
3943                                 found_tag = FALSE;
3944                                 verify_tag = FALSE;
3945                                 pfnote(namebuf, TRUE,
3946                                        tline.buffer, save_len, save_lineno,
3947                                        save_lcno);
3948                                 continue;
3949                         }
3950                 }
3951                 if (get_tagname) {      /* grab name of proc or fn */
3952                         if (*dbp == '\0')
3953                                 continue;
3954
3955                         /* save all values for later tagging */
3956                         grow_linebuffer(&tline, lb.len + 1);
3957                         xstrncpy(tline.buffer, lb.buffer, lb.len + 1);
3958                         save_lineno = lineno;
3959                         save_lcno = linecharno;
3960
3961                         /* grab block name */
3962                         for (cp = dbp + 1; *cp != '\0' && !endtoken(*cp); cp++)
3963                                 continue;
3964                         namebuf = (CTAGS) ? savenstr(dbp, cp - dbp) : NULL;
3965                         dbp = cp;       /* set dbp to e-o-token */
3966                         save_len = dbp - lb.buffer + 1;
3967                         get_tagname = FALSE;
3968                         found_tag = TRUE;
3969                         continue;
3970
3971                         /* and proceed to check for "extern" */
3972                 } else if (!incomment && !inquote && !found_tag) {
3973                         /* check for proc/fn keywords */
3974                         switch (lowcase(c)) {
3975                         case 'p':
3976                                 if (tail("rocedure"))   /* c = 'p', dbp has advanced */
3977                                         get_tagname = TRUE;
3978                                 continue;
3979                         case 'f':
3980                                 if (tail("unction"))
3981                                         get_tagname = TRUE;
3982                                 continue;
3983                         default:
3984                                 break;
3985                         }
3986                 }
3987         }                       /* while not eof */
3988
3989         free(tline.buffer);
3990 }
3991 \f
3992 /*
3993  * lisp tag functions
3994  *  look for (def or (DEF, quote or QUOTE
3995  */
3996 int L_isdef PP((char *strp));
3997 int L_isdef(strp)
3998 register char *strp;
3999 {
4000         return ((strp[1] == 'd' || strp[1] == 'D')
4001                 && (strp[2] == 'e' || strp[2] == 'E')
4002                 && (strp[3] == 'f' || strp[3] == 'F'));
4003 }
4004 int L_isquote PP((char *strp));
4005 int L_isquote(strp)
4006 register char *strp;
4007 {
4008         return ((*++strp == 'q' || *strp == 'Q')
4009                 && (*++strp == 'u' || *strp == 'U')
4010                 && (*++strp == 'o' || *strp == 'O')
4011                 && (*++strp == 't' || *strp == 'T')
4012                 && (*++strp == 'e' || *strp == 'E')
4013                 && isspace(*++strp));
4014 }
4015
4016 void L_getit PP((void));
4017 void L_getit()
4018 {
4019         register char *cp;
4020
4021         if (*dbp == '\'')       /* Skip prefix quote */
4022                 dbp++;
4023         else if (*dbp == '(') {
4024                 if (L_isquote(dbp))
4025                         dbp += 7;       /* Skip "(quote " */
4026                 else
4027                         dbp += 1;       /* Skip "(" before name in (defstruct (foo)) */
4028                 dbp = skip_spaces(dbp);
4029         }
4030
4031         for (cp = dbp /*+1 */ ;
4032              *cp != '\0' && *cp != '(' && *cp != ' ' && *cp != ')'; cp++)
4033                 continue;
4034         if (cp == dbp)
4035                 return;
4036
4037         pfnote((CTAGS) ? savenstr(dbp, cp - dbp) : NULL, TRUE,
4038                lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
4039 }
4040
4041 void Lisp_functions(inf)
4042 FILE *inf;
4043 {
4044         LOOP_ON_INPUT_LINES(inf, lb, dbp) {
4045                 if (dbp[0] == '(') {
4046                         if (L_isdef(dbp)) {
4047                                 dbp = skip_non_spaces(dbp);
4048                                 dbp = skip_spaces(dbp);
4049                                 L_getit();
4050                         } else {
4051                                 /* Check for (foo::defmumble name-defined ... */
4052                                 do
4053                                         dbp++;
4054                                 while (*dbp != '\0' && !isspace(*dbp)
4055                                        && *dbp != ':' && *dbp != '('
4056                                        && *dbp != ')');
4057                                 if (*dbp == ':') {
4058                                         do
4059                                                 dbp++;
4060                                         while (*dbp == ':');
4061
4062                                         if (L_isdef(dbp - 1)) {
4063                                                 dbp = skip_non_spaces(dbp);
4064                                                 dbp = skip_spaces(dbp);
4065                                                 L_getit();
4066                                         }
4067                                 }
4068                         }
4069                 }
4070         }
4071 }
4072 \f
4073 /*
4074  * Postscript tag functions
4075  * Just look for lines where the first character is '/'
4076  * Richard Mlynarik <mly@adoc.xerox.com>
4077  */
4078 void Postscript_functions(inf)
4079 FILE *inf;
4080 {
4081         register char *bp, *ep;
4082
4083         LOOP_ON_INPUT_LINES(inf, lb, bp) {
4084                 if (bp[0] == '/') {
4085                         for (ep = bp + 1;
4086                              *ep != '\0' && *ep != ' ' && *ep != '{'; ep++)
4087                                 continue;
4088                         pfnote((CTAGS) ? savenstr(bp, ep - bp) : NULL, TRUE,
4089                                lb.buffer, ep - lb.buffer + 1, lineno,
4090                                linecharno);
4091                 }
4092         }
4093 }
4094 \f
4095 /*
4096  * Scheme tag functions
4097  * look for (def... xyzzy
4098  * look for (def... (xyzzy
4099  * look for (def ... ((...(xyzzy ....
4100  * look for (set! xyzzy
4101  */
4102
4103 void get_scheme PP((void));
4104
4105 void Scheme_functions(inf)
4106 FILE *inf;
4107 {
4108         LOOP_ON_INPUT_LINES(inf, lb, dbp) {
4109                 if (dbp[0] == '(' && (dbp[1] == 'D' || dbp[1] == 'd')
4110                     && (dbp[2] == 'E' || dbp[2] == 'e')
4111                     && (dbp[3] == 'F' || dbp[3] == 'f')) {
4112                         dbp = skip_non_spaces(dbp);
4113                         /* Skip over open parens and white space */
4114                         while (isspace(*dbp) || *dbp == '(')
4115                                 dbp++;
4116                         get_scheme();
4117                 }
4118                 if (dbp[0] == '(' && (dbp[1] == 'S' || dbp[1] == 's')
4119                     && (dbp[2] == 'E' || dbp[2] == 'e')
4120                     && (dbp[3] == 'T' || dbp[3] == 't')
4121                     && (dbp[4] == '!' || dbp[4] == '!')
4122                     && (isspace(dbp[5]))) {
4123                         dbp = skip_non_spaces(dbp);
4124                         dbp = skip_spaces(dbp);
4125                         get_scheme();
4126                 }
4127         }
4128 }
4129
4130 void get_scheme()
4131 {
4132         register char *cp;
4133
4134         if (*dbp == '\0')
4135                 return;
4136         /* Go till you get to white space or a syntactic break */
4137         for (cp = dbp + 1;
4138              *cp != '\0' && *cp != '(' && *cp != ')' && !isspace(*cp); cp++)
4139                 continue;
4140         pfnote((CTAGS) ? savenstr(dbp, cp - dbp) : NULL, TRUE,
4141                lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
4142 }
4143 \f
4144 /* Find tags in TeX and LaTeX input files.  */
4145
4146 /* TEX_toktab is a table of TeX control sequences that define tags.
4147    Each TEX_tabent records one such control sequence.
4148    CONVERT THIS TO USE THE Stab TYPE!! */
4149 struct TEX_tabent {
4150         char *name;
4151         int len;
4152 };
4153
4154 struct TEX_tabent *TEX_toktab = NULL;   /* Table with tag tokens */
4155
4156 /* Default set of control sequences to put into TEX_toktab.
4157    The value of environment var TEXTAGS is prepended to this.  */
4158
4159 char *TEX_defenv = "\
4160 :chapter:section:subsection:subsubsection:eqno:label:ref:cite:bibitem\
4161 :part:appendix:entry:index";
4162
4163 void TEX_mode PP((FILE * inf));
4164 struct TEX_tabent *TEX_decode_env PP((char *evarname, char *defenv));
4165 int TEX_Token PP((char *cp));
4166
4167 char TEX_esc = '\\';
4168 char TEX_opgrp = '{';
4169 char TEX_clgrp = '}';
4170
4171 /*
4172  * TeX/LaTeX scanning loop.
4173  */
4174 void TeX_functions(inf)
4175 FILE *inf;
4176 {
4177         char *cp, *lasthit;
4178         register int i;
4179
4180         /* Select either \ or ! as escape character.  */
4181         TEX_mode(inf);
4182
4183         /* Initialize token table once from environment. */
4184         if (!TEX_toktab)
4185                 TEX_toktab = TEX_decode_env("TEXTAGS", TEX_defenv);
4186
4187         LOOP_ON_INPUT_LINES(inf, lb, cp) {
4188                 lasthit = cp;
4189                 /* Look at each esc in line. */
4190                 while ((cp = etags_strchr(cp, TEX_esc)) != NULL) {
4191                         if (*++cp == '\0')
4192                                 break;
4193                         linecharno += cp - lasthit;
4194                         lasthit = cp;
4195                         i = TEX_Token(lasthit);
4196                         if (i >= 0) {
4197                                 /* We seem to include the TeX command in the tag name.
4198                                    register char *p;
4199                                    for (p = lasthit + TEX_toktab[i].len;
4200                                    *p != '\0' && *p != TEX_clgrp;
4201                                    p++)
4202                                    continue; */
4203                                 pfnote( /*savenstr (lasthit, p-lasthit) */
4204                                     (char *)NULL, TRUE,
4205                                        lb.buffer, lb.len, lineno, linecharno);
4206                                 break;  /* We only tag a line once */
4207                         }
4208                 }
4209         }
4210 }
4211
4212 #define TEX_LESC '\\'
4213 #define TEX_SESC '!'
4214 #define TEX_cmt  '%'
4215
4216 /* Figure out whether TeX's escapechar is '\\' or '!' and set grouping
4217    chars accordingly. */
4218 void TEX_mode(inf)
4219 FILE *inf;
4220 {
4221         int c;
4222
4223         while ((c = getc(inf)) != EOF) {
4224                 /* Skip to next line if we hit the TeX comment char. */
4225                 if (c == TEX_cmt)
4226                         while (c != '\n')
4227                                 c = getc(inf);
4228                 else if (c == TEX_LESC || c == TEX_SESC)
4229                         break;
4230         }
4231
4232         if (c == TEX_LESC) {
4233                 TEX_esc = TEX_LESC;
4234                 TEX_opgrp = '{';
4235                 TEX_clgrp = '}';
4236         } else {
4237                 TEX_esc = TEX_SESC;
4238                 TEX_opgrp = '<';
4239                 TEX_clgrp = '>';
4240         }
4241         rewind(inf);
4242 }
4243
4244 /* Read environment and prepend it to the default string.
4245    Build token table. */
4246 struct TEX_tabent *TEX_decode_env(evarname, defenv)
4247 char *evarname;
4248 char *defenv;
4249 {
4250         register char *env, *p;
4251
4252         struct TEX_tabent *tab;
4253         int size, i;
4254
4255         /* Append default string to environment. */
4256         env = getenv(evarname);
4257         if (!env)
4258                 env = defenv;
4259         else {
4260                 char *oldenv = env;
4261                 env = concat(oldenv, defenv, "");
4262                 free(oldenv);
4263         }
4264
4265         /* Allocate a token table */
4266         for (size = 1, p = env; p;)
4267                 if ((p = etags_strchr(p, ':')) && *++p != '\0')
4268                         size++;
4269         /* Add 1 to leave room for null terminator.  */
4270         tab = xnew(size + 1, struct TEX_tabent);
4271
4272         /* Unpack environment string into token table. Be careful about */
4273         /* zero-length strings (leading ':', "::" and trailing ':') */
4274         for (i = 0; *env;) {
4275                 p = etags_strchr(env, ':');
4276                 if (!p)         /* End of environment string. */
4277                         p = env + strlen(env);
4278                 if (p - env > 0) {      /* Only non-zero strings. */
4279                         tab[i].name = savenstr(env, p - env);
4280                         tab[i].len = strlen(tab[i].name);
4281                         i++;
4282                 }
4283                 if (*p)
4284                         env = p + 1;
4285                 else {
4286                         tab[i].name = NULL;     /* Mark end of table. */
4287                         tab[i].len = 0;
4288                         break;
4289                 }
4290         }
4291         return tab;
4292 }
4293
4294 /* If the text at CP matches one of the tag-defining TeX command names,
4295    return the pointer to the first occurrence of that command in TEX_toktab.
4296    Otherwise return -1.
4297    Keep the capital `T' in `token' for dumb truncating compilers
4298    (this distinguishes it from `TEX_toktab' */
4299 int TEX_Token(cp)
4300 char *cp;
4301 {
4302         int i;
4303
4304         for (i = 0; TEX_toktab[i].len > 0; i++)
4305                 if (strneq(TEX_toktab[i].name, cp, TEX_toktab[i].len))
4306                         return i;
4307         return -1;
4308 }
4309 \f
4310 /*
4311  * Prolog support (rewritten) by Anders Lindgren, Mar. 96
4312  *
4313  * Assumes that the predicate starts at column 0.
4314  * Only the first clause of a predicate is added.
4315  */
4316 int prolog_pred PP((char *s, char *last));
4317 void prolog_skip_comment PP((linebuffer * plb, FILE * inf));
4318 int prolog_atom PP((char *s, int pos));
4319
4320 void Prolog_functions(inf)
4321 FILE *inf;
4322 {
4323         char *cp, *last;
4324         int len;
4325         int allocated;
4326
4327         allocated = 0;
4328         len = 0;
4329         last = NULL;
4330
4331         LOOP_ON_INPUT_LINES(inf, lb, cp) {
4332                 if (cp[0] == '\0')      /* Empty line */
4333                         continue;
4334                 else if (isspace(cp[0]))        /* Not a predicate */
4335                         continue;
4336                 else if (cp[0] == '/' && cp[1] == '*')  /* comment. */
4337                         prolog_skip_comment(&lb, inf);
4338                 else if ((len = prolog_pred(cp, last)) > 0) {
4339                         /* Predicate.  Store the function name so that we only
4340                            generate a tag for the first clause.  */
4341                         if (last == NULL)
4342                                 last = xnew(len + 1, char);
4343                         else if (len + 1 > allocated)
4344                                 last = xrnew(last, len + 1, char);
4345                         allocated = len + 1;
4346                         xstrncpy(last, cp, allocated);
4347                 }
4348         }
4349         free(last);
4350 }
4351
4352 void prolog_skip_comment(plb, inf)
4353 linebuffer *plb;
4354 FILE *inf;
4355 {
4356         char *cp;
4357
4358         do {
4359                 for (cp = plb->buffer; *cp != '\0'; cp++)
4360                         if (cp[0] == '*' && cp[1] == '/')
4361                                 return;
4362                 lineno++;
4363                 linecharno += readline(plb, inf);
4364         }
4365         while (!feof(inf));
4366 }
4367
4368 /*
4369  * A predicate definition is added if it matches:
4370  *     <beginning of line><Prolog Atom><whitespace>(
4371  *
4372  * It is added to the tags database if it doesn't match the
4373  * name of the previous clause header.
4374  *
4375  * Return the size of the name of the predicate, or 0 if no header
4376  * was found.
4377  */
4378 int prolog_pred(s, last)
4379 char *s;
4380 char *last;                     /* Name of last clause. */
4381 {
4382         int pos;
4383         int len;
4384
4385         pos = prolog_atom(s, 0);
4386         if (pos < 1)
4387                 return 0;
4388
4389         len = pos;
4390         pos = skip_spaces(s + pos) - s;
4391
4392         if ((s[pos] == '(') || (s[pos] == '.')) {
4393                 if (s[pos] == '(')
4394                         pos++;
4395
4396                 /* Save only the first clause. */
4397                 if (last == NULL || len != (int)strlen(last)
4398                     || !strneq(s, last, len)) {
4399                         pfnote((CTAGS) ? savenstr(s, len) : NULL, TRUE,
4400                                s, pos, lineno, linecharno);
4401                         return len;
4402                 }
4403         }
4404         return 0;
4405 }
4406
4407 /*
4408  * Consume a Prolog atom.
4409  * Return the number of bytes consumed, or -1 if there was an error.
4410  *
4411  * A prolog atom, in this context, could be one of:
4412  * - An alphanumeric sequence, starting with a lower case letter.
4413  * - A quoted arbitrary string. Single quotes can escape themselves.
4414  *   Backslash quotes everything.
4415  */
4416 int prolog_atom(s, pos)
4417 char *s;
4418 int pos;
4419 {
4420         int origpos;
4421
4422         origpos = pos;
4423
4424         if (islower(s[pos]) || (s[pos] == '_')) {
4425                 /* The atom is unquoted. */
4426                 pos++;
4427                 while (isalnum(s[pos]) || (s[pos] == '_')) {
4428                         pos++;
4429                 }
4430                 return pos - origpos;
4431         } else if (s[pos] == '\'') {
4432                 pos++;
4433
4434                 while (1) {
4435                         if (s[pos] == '\'') {
4436                                 pos++;
4437                                 if (s[pos] != '\'')
4438                                         break;
4439                                 pos++;  /* A double quote */
4440                         } else if (s[pos] == '\0')
4441                                 /* Multiline quoted atoms are ignored. */
4442                                 return -1;
4443                         else if (s[pos] == '\\') {
4444                                 if (s[pos + 1] == '\0')
4445                                         return -1;
4446                                 pos += 2;
4447                         } else
4448                                 pos++;
4449                 }
4450                 return pos - origpos;
4451         } else
4452                 return -1;
4453 }
4454 \f
4455 /*
4456  * Support for Erlang  --  Anders Lindgren, Feb 1996.
4457  *
4458  * Generates tags for functions, defines, and records.
4459  *
4460  * Assumes that Erlang functions start at column 0.
4461  */
4462 int erlang_func PP((char *s, char *last));
4463 void erlang_attribute PP((char *s));
4464 int erlang_atom PP((char *s, int pos));
4465
4466 void Erlang_functions(inf)
4467 FILE *inf;
4468 {
4469         char *cp, *last;
4470         int len;
4471         int allocated;
4472
4473         allocated = 0;
4474         len = 0;
4475         last = NULL;
4476
4477         LOOP_ON_INPUT_LINES(inf, lb, cp) {
4478                 if (cp[0] == '\0')      /* Empty line */
4479                         continue;
4480                 else if (isspace(cp[0]))        /* Not function nor attribute */
4481                         continue;
4482                 else if (cp[0] == '%')  /* comment */
4483                         continue;
4484                 else if (cp[0] == '"')  /* Sometimes, strings start in column one */
4485                         continue;
4486                 else if (cp[0] == '-') {        /* attribute, e.g. "-define" */
4487                         erlang_attribute(cp);
4488                         free(last);
4489                         last = NULL;
4490                 } else if ((len = erlang_func(cp, last)) > 0) {
4491                         /*
4492                          * Function.  Store the function name so that we only
4493                          * generates a tag for the first clause.
4494                          */
4495                         if (last == NULL)
4496                                 last = xnew(len + 1, char);
4497                         else if (len + 1 > allocated)
4498                                 last = xrnew(last, len + 1, char);
4499                         allocated = len + 1;
4500                         xstrncpy(last, cp, allocated);
4501                 }
4502         }
4503         free(last);
4504 }
4505
4506 /*
4507  * A function definition is added if it matches:
4508  *     <beginning of line><Erlang Atom><whitespace>(
4509  *
4510  * It is added to the tags database if it doesn't match the
4511  * name of the previous clause header.
4512  *
4513  * Return the size of the name of the function, or 0 if no function
4514  * was found.
4515  */
4516 int erlang_func(s, last)
4517 char *s;
4518 char *last;                     /* Name of last clause. */
4519 {
4520         int pos;
4521         int len;
4522
4523         pos = erlang_atom(s, 0);
4524         if (pos < 1)
4525                 return 0;
4526
4527         len = pos;
4528         pos = skip_spaces(s + pos) - s;
4529
4530         /* Save only the first clause. */
4531         if (s[pos++] == '(' && (last == NULL || len != (int)strlen(last)
4532                                 || !strneq(s, last, len))) {
4533                 pfnote((CTAGS) ? savenstr(s, len) : NULL, TRUE,
4534                        s, pos, lineno, linecharno);
4535                 return len;
4536         }
4537
4538         return 0;
4539 }
4540
4541 /*
4542  * Handle attributes.  Currently, tags are generated for defines
4543  * and records.
4544  *
4545  * They are on the form:
4546  * -define(foo, bar).
4547  * -define(Foo(M, N), M+N).
4548  * -record(graph, {vtab = notable, cyclic = true}).
4549  */
4550 void erlang_attribute(s)
4551 char *s;
4552 {
4553         int pos;
4554         int len;
4555
4556         if (strneq(s, "-define", 7) || strneq(s, "-record", 7)) {
4557                 pos = skip_spaces(s + 7) - s;
4558                 if (s[pos++] == '(') {
4559                         pos = skip_spaces(s + pos) - s;
4560                         len = erlang_atom(s, pos);
4561                         if (len != 0)
4562                                 pfnote((CTAGS) ? savenstr(&s[pos], len) : NULL,
4563                                        TRUE, s, pos + len, lineno, linecharno);
4564                 }
4565         }
4566         return;
4567 }
4568
4569 /*
4570  * Consume an Erlang atom (or variable).
4571  * Return the number of bytes consumed, or -1 if there was an error.
4572  */
4573 int erlang_atom(s, pos)
4574 char *s;
4575 int pos;
4576 {
4577         int origpos;
4578
4579         origpos = pos;
4580
4581         if (isalpha(s[pos]) || s[pos] == '_') {
4582                 /* The atom is unquoted. */
4583                 pos++;
4584                 while (isalnum(s[pos]) || s[pos] == '_')
4585                         pos++;
4586                 return pos - origpos;
4587         } else if (s[pos] == '\'') {
4588                 pos++;
4589
4590                 while (1) {
4591                         if (s[pos] == '\'') {
4592                                 pos++;
4593                                 break;
4594                         } else if (s[pos] == '\0')
4595                                 /* Multiline quoted atoms are ignored. */
4596                                 return -1;
4597                         else if (s[pos] == '\\') {
4598                                 if (s[pos + 1] == '\0')
4599                                         return -1;
4600                                 pos += 2;
4601                         } else
4602                                 pos++;
4603                 }
4604                 return pos - origpos;
4605         } else
4606                 return -1;
4607 }
4608 \f
4609 #ifdef ETAGS_REGEXPS
4610
4611 /* Take a string like "/blah/" and turn it into "blah", making sure
4612    that the first and last characters are the same, and handling
4613    quoted separator characters.  Actually, stops on the occurrence of
4614    an unquoted separator.  Also turns "\t" into a Tab character.
4615    Returns pointer to terminating separator.  Works in place.  Null
4616    terminates name string. */
4617 char *scan_separators PP((char *name));
4618 char *scan_separators(name)
4619 char *name;
4620 {
4621         char sep = name[0];
4622         char *copyto = name;
4623         bool quoted = FALSE;
4624
4625         for (++name; *name != '\0'; ++name) {
4626                 if (quoted) {
4627                         if (*name == 't')
4628                                 *copyto++ = '\t';
4629                         else if (*name == sep)
4630                                 *copyto++ = sep;
4631                         else {
4632                                 /* Something else is quoted, so preserve the quote. */
4633                                 *copyto++ = '\\';
4634                                 *copyto++ = *name;
4635                         }
4636                         quoted = FALSE;
4637                 } else if (*name == '\\')
4638                         quoted = TRUE;
4639                 else if (*name == sep)
4640                         break;
4641                 else
4642                         *copyto++ = *name;
4643         }
4644
4645         /* Terminate copied string. */
4646         *copyto = '\0';
4647         return name;
4648 }
4649
4650 /* Look at the argument of --regex or --no-regex and do the right
4651    thing.  Same for each line of a regexp file. */
4652 void analyse_regex(regex_arg)
4653 char *regex_arg;
4654 {
4655         if (regex_arg == NULL) {
4656                 free_patterns();        /* --no-regex: remove existing regexps */
4657                 return;
4658         }
4659         /* A real --regexp option or a line in a regexp file. */
4660         switch (regex_arg[0]) {
4661                 /* Comments in regexp file or null arg to --regex. */
4662         case '\0':
4663         case ' ':
4664         case '\t':
4665                 break;
4666
4667                 /* Read a regex file.  This is recursive and may result in a
4668                    loop, which will stop when the file descriptors are exhausted. */
4669         case '@':
4670                 {
4671                         FILE *regexfp;
4672                         linebuffer regexbuf;
4673                         char *regexfile = regex_arg + 1;
4674
4675                         /* regexfile is a file containing regexps, one per line. */
4676                         regexfp = fopen(regexfile, "r");
4677                         if (regexfp == NULL) {
4678                                 pfatal(regexfile);
4679                                 return;
4680                         }
4681                         initbuffer(&regexbuf);
4682                         while (readline_internal(&regexbuf, regexfp) > 0)
4683                                 analyse_regex(regexbuf.buffer);
4684                         free(regexbuf.buffer);
4685                         fclose(regexfp);
4686                 }
4687                 break;
4688
4689                 /* Regexp to be used for a specific language only. */
4690         case '{':
4691                 {
4692                         language *lang;
4693                         char *lang_name = regex_arg + 1;
4694                         char *cp;
4695
4696                         for (cp = lang_name; *cp != '}'; cp++)
4697                                 if (*cp == '\0') {
4698                                         error
4699                                             ("unterminated language name in regex: %s",
4700                                              regex_arg);
4701                                         return;
4702                                 }
4703                         *cp = '\0';
4704                         lang = get_language_from_name(lang_name);
4705                         if (lang == NULL)
4706                                 return;
4707                         add_regex(cp + 1, lang);
4708                 }
4709                 break;
4710
4711                 /* Regexp to be used for any language. */
4712         default:
4713                 add_regex(regex_arg, NULL);
4714                 break;
4715         }
4716 }
4717
4718 /* Turn a name, which is an ed-style (but Emacs syntax) regular
4719    expression, into a real regular expression by compiling it. */
4720 void add_regex(regexp_pattern, lang)
4721 char *regexp_pattern;
4722 language *lang;
4723 {
4724         char *name;
4725         const char *err;
4726         struct re_pattern_buffer *patbuf;
4727         pattern *pp;
4728
4729         if (regexp_pattern[strlen(regexp_pattern) - 1] != regexp_pattern[0]) {
4730                 error("%s: unterminated regexp", regexp_pattern);
4731                 return;
4732         }
4733         name = scan_separators(regexp_pattern);
4734         if (regexp_pattern[0] == '\0') {
4735                 error("null regexp", (char *)NULL);
4736                 return;
4737         }
4738         (void)scan_separators(name);
4739
4740         patbuf = xnew(1, struct re_pattern_buffer);
4741         patbuf->translate = NULL;
4742         patbuf->fastmap = NULL;
4743         patbuf->buffer = NULL;
4744         patbuf->allocated = 0;
4745
4746         err =
4747             re_compile_pattern(regexp_pattern, strlen(regexp_pattern), patbuf);
4748         if (err != NULL) {
4749                 error("%s while compiling pattern", err);
4750                 return;
4751         }
4752
4753         pp = p_head;
4754         p_head = xnew(1, pattern);
4755         p_head->regex = savestr(regexp_pattern);
4756         p_head->p_next = pp;
4757         p_head->language = lang;
4758         p_head->pattern = patbuf;
4759         p_head->name_pattern = savestr(name);
4760         p_head->error_signaled = FALSE;
4761 }
4762
4763 /*
4764  * Do the substitutions indicated by the regular expression and
4765  * arguments.
4766  */
4767 char *substitute PP((char *in, char *out, struct re_registers * regs));
4768 char *substitute(in, out, regs)
4769 char *in, *out;
4770 struct re_registers *regs;
4771 {
4772         char *result, *t;
4773         int size, dig, diglen;
4774
4775         result = NULL;
4776         size = strlen(out);
4777
4778         /* Pass 1: figure out how much to allocate by finding all \N strings. */
4779         if (out[size - 1] == '\\')
4780                 fatal("pattern error in \"%s\"", out);
4781         for (t = etags_strchr(out, '\\');
4782              t != NULL; t = etags_strchr(t + 2, '\\'))
4783                 if (isdigit(t[1])) {
4784                         dig = t[1] - '0';
4785                         diglen = regs->end[dig] - regs->start[dig];
4786                         size += diglen - 2;
4787                 } else
4788                         size -= 1;
4789
4790         /* Allocate space and do the substitutions. */
4791         size_t avail = size + 1;
4792         result = xnew(avail, char);
4793
4794         for (t = result; *out != '\0'; out++)
4795                 if (*out == '\\' && isdigit(*++out)) {
4796                         /* Using "dig2" satisfies my debugger.  Bleah. */
4797                         dig = *out - '0';
4798                         diglen = regs->end[dig] - regs->start[dig];
4799                         xstrncpy(t, in + regs->start[dig], avail);
4800                         t += diglen;
4801                         avail -= diglen;
4802                 } else {
4803                         *t++ = *out;
4804                         avail --;
4805                 }
4806         *t = '\0';
4807
4808         if (DEBUG && (t > result + size || t - result != strlen(result)))
4809                 abort();
4810
4811         return result;
4812 }
4813
4814 /* Deallocate all patterns. */
4815 void free_patterns()
4816 {
4817         pattern *pp;
4818         while (p_head != NULL) {
4819                 pp = p_head->p_next;
4820                 free(p_head->regex);
4821                 free(p_head->name_pattern);
4822                 free(p_head);
4823                 p_head = pp;
4824         }
4825         return;
4826 }
4827 \f
4828 #endif                          /* ETAGS_REGEXPS */
4829 /* Initialize a linebuffer for use */
4830 void initbuffer(lbp)
4831 linebuffer *lbp;
4832 {
4833         lbp->size = 200;
4834         lbp->buffer = xnew(200, char);
4835 }
4836
4837 /*
4838  * Read a line of text from `stream' into `lbp', excluding the
4839  * newline or CR-NL, if any.  Return the number of characters read from
4840  * `stream', which is the length of the line including the newline.
4841  *
4842  * On DOS or Windows we do not count the CR character, if any, before the
4843  * NL, in the returned length; this mirrors the behavior of emacs on those
4844  * platforms (for text files, it translates CR-NL to NL as it reads in the
4845  * file).
4846  */
4847 long readline_internal(lbp, stream)
4848 linebuffer *lbp;
4849 register FILE *stream;
4850 {
4851         char *buffer = lbp->buffer;
4852         register char *p = lbp->buffer;
4853         register char *pend;
4854         int chars_deleted;
4855
4856         pend = p + lbp->size;   /* Separate to avoid 386/IX compiler bug.  */
4857
4858         while (1) {
4859                 register int c = getc(stream);
4860                 if (p == pend) {
4861                         /* We're at the end of linebuffer: expand it. */
4862                         lbp->size *= 2;
4863                         buffer = xrnew(buffer, lbp->size, char);
4864                         p += buffer - lbp->buffer;
4865                         pend = buffer + lbp->size;
4866                         lbp->buffer = buffer;
4867                 }
4868                 if (c == EOF) {
4869                         *p = '\0';
4870                         chars_deleted = 0;
4871                         break;
4872                 }
4873                 if (c == '\n') {
4874                         if (p > buffer && p[-1] == '\r') {
4875                                 p -= 1;
4876                                 chars_deleted = 2;
4877                         } else {
4878                                 chars_deleted = 1;
4879                         }
4880                         *p = '\0';
4881                         break;
4882                 }
4883                 *p++ = c;
4884         }
4885         lbp->len = p - buffer;
4886
4887         return lbp->len + chars_deleted;
4888 }
4889
4890 /*
4891  * Like readline_internal, above, but in addition try to match the
4892  * input line against relevant regular expressions.
4893  */
4894 long readline(lbp, stream)
4895 linebuffer *lbp;
4896 FILE *stream;
4897 {
4898         /* Read new line. */
4899         long result = readline_internal(lbp, stream);
4900 #ifdef ETAGS_REGEXPS
4901         int match;
4902         pattern *pp;
4903
4904         /* Match against relevant patterns. */
4905         if (lbp->len > 0)
4906                 for (pp = p_head; pp != NULL; pp = pp->p_next) {
4907                         /* Only use generic regexps or those for the current language. */
4908                         if (pp->language != NULL && pp->language != curlang)
4909                                 continue;
4910
4911                         match =
4912                             re_match(pp->pattern, lbp->buffer, lbp->len, 0,
4913                                      &pp->regs);
4914                         switch (match) {
4915                         case -2:
4916                                 /* Some error. */
4917                                 if (!pp->error_signaled) {
4918                                         error("error while matching \"%s\"",
4919                                               pp->regex);
4920                                         pp->error_signaled = TRUE;
4921                                 }
4922                                 break;
4923                         case -1:
4924                                 /* No match. */
4925                                 break;
4926                         default:
4927                                 /* Match occurred.  Construct a tag. */
4928                                 if (pp->name_pattern[0] != '\0') {
4929                                         /* Make a named tag. */
4930                                         char *name = substitute(lbp->buffer,
4931                                                                 pp->
4932                                                                 name_pattern,
4933                                                                 &pp->regs);
4934                                         if (name != NULL)
4935                                                 pfnote(name, TRUE, lbp->buffer,
4936                                                        match, lineno,
4937                                                        linecharno);
4938                                 } else {
4939                                         /* Make an unnamed tag. */
4940                                         pfnote((char *)NULL, TRUE,
4941                                                lbp->buffer, match, lineno,
4942                                                linecharno);
4943                                 }
4944                                 break;
4945                         }
4946                 }
4947 #endif                          /* ETAGS_REGEXPS */
4948
4949         return result;
4950 }
4951 \f
4952 /*
4953  * Return a pointer to a space of size strlen(cp)+1 allocated
4954  * with xnew where the string CP has been copied.
4955  */
4956 char *savestr(cp)
4957 char *cp;
4958 {
4959         return savenstr(cp, strlen(cp));
4960 }
4961
4962 /*
4963  * Return a pointer to a space of size LEN+1 allocated with xnew where
4964  * the string CP has been copied for at most the first LEN characters.
4965  */
4966 char *savenstr(cp, len)
4967 char *cp;
4968 int len;
4969 {
4970         register char *dp;
4971
4972         dp = xnew(len + 1, char);
4973         xstrncpy(dp, cp, len+1);
4974         dp[len] = '\0';
4975         return dp;
4976 }
4977
4978 /* Skip spaces, return new pointer. */
4979 char *skip_spaces(cp)
4980 char *cp;
4981 {
4982         while (isspace(*cp))    /* isspace('\0')==FALSE */
4983                 cp++;
4984         return cp;
4985 }
4986
4987 /* Skip non spaces, return new pointer. */
4988 char *skip_non_spaces(cp)
4989 char *cp;
4990 {
4991         while (!iswhite(*cp))   /* iswhite('\0')==TRUE */
4992                 cp++;
4993         return cp;
4994 }
4995
4996 /* Print error message and exit.  */
4997 void fatal(s1, s2)
4998 char *s1, *s2;
4999 {
5000         error(s1, s2);
5001         exit(BAD);
5002 }
5003
5004 void pfatal(s1)
5005 char *s1;
5006 {
5007         perror(s1);
5008         exit(BAD);
5009 }
5010
5011 void suggest_asking_for_help()
5012 {
5013         fprintf(stderr, "\tTry `%s %s' for a complete list of options.\n",
5014                 progname,
5015 #ifdef LONG_OPTIONS
5016                 "--help"
5017 #else
5018                 "-h"
5019 #endif
5020             );
5021         exit(BAD);
5022 }
5023
5024 /* Print error message.  `s1' is printf control string, `s2' is arg for it. */
5025 void error(s1, s2)
5026 const char *s1, *s2;
5027 {
5028         fprintf(stderr, "%s: ", progname);
5029         fprintf(stderr, s1, s2);
5030         fprintf(stderr, "\n");
5031 }
5032
5033 /* Return a newly-allocated string whose contents
5034    concatenate those of s1, s2, s3.  */
5035 char *concat(s1, s2, s3)
5036 char *s1, *s2, *s3;
5037 {
5038         int len1 = strlen(s1), len2 = strlen(s2), len3 = strlen(s3);
5039         char *result = xnew(len1 + len2 + len3 + 1, char);
5040
5041         strcpy(result, s1);
5042         strcpy(result + len1, s2);
5043         strcpy(result + len1 + len2, s3);
5044         result[len1 + len2 + len3] = '\0';
5045
5046         return result;
5047 }
5048 \f
5049 /* Does the same work as the system V getcwd, but does not need to
5050    guess the buffer size in advance. */
5051 char *etags_getcwd()
5052 {
5053 #ifdef HAVE_GETCWD
5054         int bufsize = 200;
5055         char *path = xnew(bufsize, char);
5056
5057         while (getcwd(path, bufsize) == NULL) {
5058                 if (errno != ERANGE)
5059                         pfatal("getcwd");
5060                 bufsize *= 2;
5061                 free(path);
5062                 path = xnew(bufsize, char);
5063         }
5064
5065         canonicalize_filename(path);
5066         return path;
5067
5068 #else                           /* not HAVE_GETCWD */
5069         linebuffer path;
5070         FILE *pipe;
5071
5072         initbuffer(&path);
5073         pipe = (FILE *) popen("pwd 2>/dev/null", "r");
5074         if (pipe == NULL || readline_internal(&path, pipe) == 0)
5075                 pfatal("pwd");
5076         pclose(pipe);
5077
5078         return path.buffer;
5079 #endif                          /* not HAVE_GETCWD */
5080 }
5081
5082 /* Return a newly allocated string containing the file name of FILE
5083    relative to the absolute directory DIR (which should end with a slash). */
5084 char *relative_filename(file, dir)
5085 char *file, *dir;
5086 {
5087         char *fp, *dp, *afn, *res;
5088         int i;
5089         ssize_t res_left;
5090
5091         /* Find the common root of file and dir (with a trailing slash). */
5092         afn = absolute_filename(file, cwd);
5093         fp = afn;
5094         dp = dir;
5095         while (*fp++ == *dp++)
5096                 continue;
5097         fp--, dp--;             /* back to the first differing char */
5098         do                      /* look at the equal chars until '/' */
5099                 fp--, dp--;
5100         while (*fp != '/');
5101         fp ++; /* Advance past the '/' */
5102
5103         /* Build a sequence of "../" strings for the resulting relative file name. */
5104         i = 0;
5105         while ((dp = etags_strchr(dp + 1, '/')) != NULL)
5106                 i += 1;
5107         res_left = 3 * i + strlen(fp);
5108         res = xnew( res_left + 1, char);
5109         res[0] = '\0';
5110         for ( ; i-- > 0 ; res_left -= 4 )
5111                 strncat(res, "../", res_left );
5112
5113         /* Add the file name relative to the common root of file and dir. */
5114         strncat(res, fp, res_left);
5115         free(afn);
5116
5117         return res;
5118 }
5119
5120 /* Return a newly allocated string containing the absolute file name
5121    of FILE given DIR (which should end with a slash). */
5122 char *absolute_filename(file, dir)
5123 char *file, *dir;
5124 {
5125         char *slashp, *cp, *res;
5126
5127         if (filename_is_absolute(file))
5128                 res = savestr(file);
5129         else
5130                 res = concat(dir, file, "");
5131
5132         /* Delete the "/dirname/.." and "/." substrings. */
5133         slashp = etags_strchr(res, '/');
5134         while (slashp != NULL && slashp[0] != '\0') {
5135                 if (slashp[1] == '.') {
5136                         if (slashp[2] == '.'
5137                             && (slashp[3] == '/' || slashp[3] == '\0')) {
5138                                 cp = slashp;
5139                                 do
5140                                         cp--;
5141                                 while (cp >= res && !filename_is_absolute(cp));
5142                                 if (cp < res)
5143                                         cp = slashp;    /* the absolute name begins with "/.." */
5144                                 strcpy(cp, slashp + 3);
5145                                 slashp = cp;
5146                                 continue;
5147                         } else if (slashp[2] == '/' || slashp[2] == '\0') {
5148                                 strcpy(slashp, slashp + 2);
5149                                 continue;
5150                         }
5151                 }
5152
5153                 slashp = etags_strchr(slashp + 1, '/');
5154         }
5155
5156         if (res[0] == '\0')
5157                 return savestr("/");
5158         else
5159                 return res;
5160 }
5161
5162 /* Return a newly allocated string containing the absolute
5163    file name of dir where FILE resides given DIR (which should
5164    end with a slash). */
5165 char *absolute_dirname(file, dir)
5166 char *file, *dir;
5167 {
5168         char *slashp, *res;
5169         char save;
5170
5171         canonicalize_filename(file);
5172         slashp = etags_strrchr(file, '/');
5173         if (slashp == NULL)
5174                 return savestr(dir);
5175         save = slashp[1];
5176         slashp[1] = '\0';
5177         res = absolute_filename(file, dir);
5178         slashp[1] = save;
5179
5180         return res;
5181 }
5182
5183 /* Whether the argument string is an absolute file name.  The argument
5184    string must have been canonicalized with canonicalize_filename. */
5185 bool filename_is_absolute(fn)
5186 char *fn;
5187 {
5188         return (fn[0] == '/');
5189 }
5190
5191 /* Translate backslashes into slashes.  Works in place. */
5192 void canonicalize_filename(fn)
5193 register char *fn;
5194 {
5195         /* No action. */
5196 }
5197
5198 /* Increase the size of a linebuffer. */
5199 void grow_linebuffer(lbp, toksize)
5200 linebuffer *lbp;
5201 int toksize;
5202 {
5203         while (lbp->size < toksize)
5204                 lbp->size *= 2;
5205         lbp->buffer = xrnew(lbp->buffer, lbp->size, char);
5206 }
5207
5208 /* Like malloc but get fatal error if memory is exhausted.  */
5209 long *xmalloc(size)
5210 unsigned int size;
5211 {
5212         long *result = (long *)malloc(size);
5213         if (result == NULL)
5214                 fatal("virtual memory exhausted", (char *)NULL);
5215         return result;
5216 }
5217
5218 long *xrealloc(ptr, size)
5219 char *ptr;
5220 unsigned int size;
5221 {
5222         long *result = (long *)realloc(ptr, size);
5223         if (result == NULL)
5224                 fatal("virtual memory exhausted", (char *)NULL);
5225         return result;
5226 }