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