]> code.delx.au - gnu-emacs/blobdiff - lib-src/etags.c
*** empty log message ***
[gnu-emacs] / lib-src / etags.c
index 950389545acdc73536927c25a4a8b8c4d7c4c8e0..0ba1ccf861f767db70447f68c3daed51b577d50e 100644 (file)
@@ -1,5 +1,5 @@
 /* Tags file maker to go with GNU Emacs           -*- coding: latin-1 -*-
-   Copyright (C) 1984, 1987-1989, 1993-1995, 1998-2001
+   Copyright (C) 1984, 1987-1989, 1993-1995, 1998-2001, 2002
    Free Software Foundation, Inc. and Ken Arnold
 
 This file is not considered part of GNU Emacs.
@@ -33,7 +33,7 @@ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
  *     Francesco Potortì <pot@gnu.org> has maintained it since 1993.
  */
 
-char pot_etags_version[] = "@(#) pot revision number is 14.35";
+char pot_etags_version[] = "@(#) pot revision number is 15.16";
 
 #define        TRUE    1
 #define        FALSE   0
@@ -228,29 +228,43 @@ typedef void Lang_function __P((FILE *));
 
 typedef struct
 {
-  char *suffix;
-  char *command;               /* Takes one arg and decompresses to stdout */
+  char *suffix;                        /* file name suffix for this compressor */
+  char *command;               /* takes one arg and decompresses to stdout */
 } compressor;
 
 typedef struct
 {
-  char *name;
-  Lang_function *function;
-  char **filenames;
-  char **suffixes;
-  char **interpreters;
+  char *name;                  /* language name */
+  bool metasource;             /* source used to generate other sources */
+  Lang_function *function;     /* parse function */
+  char **filenames;            /* names of this language's files */
+  char **suffixes;             /* name suffixes of this language's files */
+  char **interpreters;         /* interpreters for this language */
 } language;
 
+typedef struct fdesc
+{
+  struct fdesc *next;          /* for the linked list */
+  char *infname;               /* uncompressed input file name */
+  char *infabsname;            /* absolute uncompressed input file name */
+  char *infabsdir;             /* absolute dir of input file */
+  char *taggedfname;           /* file name to write in tagfile */
+  language *lang;              /* language of file */
+  char *prop;                  /* file properties to write in tagfile */
+  bool usecharno;              /* etags tags shall contain char number */
+} fdesc;
+
 typedef struct node_st
-{                              /* sorting structure            */
-  char *name;                  /* function or type name        */
-  char *file;                  /* file name                    */
-  bool is_func;                        /* use pattern or line no       */
-  bool been_warned;            /* set if noticed dup           */
-  int lno;                     /* line number tag is on        */
+{                              /* sorting structure */
+  struct node_st *left, *right;        /* left and right sons */
+  fdesc *fdp;                  /* description of file to whom tag belongs */
+  char *name;                  /* tag name */
+  char *pat;                   /* search pattern */
+  bool valid;                  /* write this tag on the tag file */
+  bool is_func;                        /* function tag: use pattern in CTAGS mode */
+  bool been_warned;            /* warning already given for duplicated tag */
+  int lno;                     /* line number tag is on */
   long cno;                    /* character number line starts on */
-  char *pat;                   /* search pattern               */
-  struct node_st *left, *right;        /* left and right sons          */
 } node;
 
 /*
@@ -267,6 +281,35 @@ typedef struct
   char *buffer;
 } linebuffer;
 
+/* Used to support mixing of --lang and file names. */
+typedef struct
+{
+  enum {
+    at_language,               /* a language specification */
+    at_regexp,                 /* a regular expression */
+    at_icregexp,               /* same, but with case ignored */
+    at_filename                        /* a file name */
+  } arg_type;                  /* argument type */
+  language *lang;              /* language associated with the argument */
+  char *what;                  /* the argument itself */
+} argument;
+
+#ifdef ETAGS_REGEXPS
+/* Structure defining a regular expression. */
+typedef struct pattern
+{
+  struct pattern *p_next;
+  language *lang;
+  char *regex;
+  struct re_pattern_buffer *pat;
+  struct re_registers regs;
+  char *name_pattern;
+  bool error_signaled;
+  bool ignore_case;
+} pattern;
+#endif /* ETAGS_REGEXPS */
+
+
 /* Many compilers barf on this:
        Lang_function Ada_funcs;
    so let's write it this way */
@@ -299,13 +342,11 @@ static void print_language_names __P((void));
 static void print_version __P((void));
 static void print_help __P((void));
 int main __P((int, char **));
-static int number_len __P((long));
 
 static compressor *get_compressor_from_suffix __P((char *, char **));
 static language *get_language_from_langname __P((const char *));
 static language *get_language_from_interpreter __P((char *));
-static language *get_language_from_filename __P((char *));
-static int total_size_of_entries __P((node *));
+static language *get_language_from_filename __P((char *, bool));
 static long readline __P((linebuffer *, FILE *));
 static long readline_internal __P((linebuffer *, FILE *));
 static bool nocase_tail __P((char *));
@@ -324,11 +365,13 @@ static void add_node __P((node *, node **));
 
 static void init __P((void));
 static void initbuffer __P((linebuffer *));
-static void find_entries __P((char *, FILE *));
+static void process_file __P((char *, language *));
+static void find_entries __P((FILE *));
 static void free_tree __P((node *));
+static void free_fdesc __P((fdesc *));
 static void pfnote __P((char *, bool, char *, int, int, long));
 static void new_pfnote __P((char *, int, bool, char *, int, int, long));
-static void process_file __P((char *));
+static void invalidate_nodes __P((fdesc *, node **));
 static void put_entries __P((node *));
 
 static char *concat __P((char *, char *, char *));
@@ -338,6 +381,7 @@ static char *savenstr __P((char *, int));
 static char *savestr __P((char *));
 static char *etags_strchr __P((const char *, int));
 static char *etags_strrchr __P((const char *, int));
+static bool strcaseeq __P((const char *, const char *));
 static char *etags_getcwd __P((void));
 static char *relative_filename __P((char *, char *));
 static char *absolute_filename __P((char *, char *));
@@ -345,33 +389,35 @@ static char *absolute_dirname __P((char *, char *));
 static bool filename_is_absolute __P((char *f));
 static void canonicalize_filename __P((char *));
 static void linebuffer_setlen __P((linebuffer *, int));
-PTR xmalloc __P((unsigned int));
-PTR xrealloc __P((char *, unsigned int));
+static PTR xmalloc __P((unsigned int));
+static PTR xrealloc __P((char *, unsigned int));
 
 \f
-char searchar = '/';           /* use /.../ searches */
+static char searchar = '/';    /* use /.../ searches */
 
-char *tagfile;                 /* output file */
-char *progname;                        /* name this program was invoked with */
-char *cwd;                     /* current working directory */
-char *tagfiledir;              /* directory of tagfile */
-FILE *tagf;                    /* ioptr for tags file */
+static char *tagfile;          /* output file */
+static char *progname;         /* name this program was invoked with */
+static char *cwd;              /* current working directory */
+static char *tagfiledir;       /* directory of tagfile */
+static FILE *tagf;             /* ioptr for tags file */
 
-char *curfile;                 /* current input file name */
-language *curlang;             /* current language */
+static fdesc *fdhead;          /* head of file description list */
+static fdesc *curfdp;          /* current file description */
+static int lineno;             /* line number of current line */
+static long charno;            /* current character number */
+static long linecharno;                /* charno of start of current line */
+static char *dbp;              /* pointer to start of current tag */
 
-int lineno;                    /* line number of current line */
-long charno;                   /* current character number */
-long linecharno;               /* charno of start of current line */
-char *dbp;                     /* pointer to start of current tag */
+static const int invalidcharno = -1;
 
-node *head;                    /* the head of the binary tree of tags */
+static node *nodehead;         /* the head of the binary tree of tags */
+static node *last_node;                /* the last node created */
 
-linebuffer lb;                 /* the current line */
+static linebuffer lb;          /* the current line */
 
 /* boolean "functions" (see init)      */
-bool _wht[CHARS], _nin[CHARS], _itk[CHARS], _btk[CHARS], _etk[CHARS];
-char
+static bool _wht[CHARS], _nin[CHARS], _itk[CHARS], _btk[CHARS], _etk[CHARS];
+static char
   /* white chars */
   *white = " \f\t\n\r\v",
   /* not in a name */
@@ -383,86 +429,80 @@ char
   /* valid in-token chars */
   *midtk = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz$0123456789";
 
-bool append_to_tagfile;                /* -a: append to tags */
-/* The following four default to TRUE for etags, but to FALSE for ctags.  */
-bool typedefs;                 /* -t: create tags for C and Ada typedefs */
-bool typedefs_or_cplusplus;    /* -T: create tags for C typedefs, level */
+static bool append_to_tagfile; /* -a: append to tags */
+/* The next four default to TRUE for etags, but to FALSE for ctags.  */
+static bool typedefs;          /* -t: create tags for C and Ada typedefs */
+static bool typedefs_or_cplusplus; /* -T: create tags for C typedefs, level */
                                /* 0 struct/enum/union decls, and C++ */
                                /* member functions. */
-bool constantypedefs;          /* -d: create tags for C #define, enum */
+static bool constantypedefs;   /* -d: create tags for C #define, enum */
                                /* constants and variables. */
                                /* -D: opposite of -d.  Default under ctags. */
-bool declarations;             /* --declarations: tag them and extern in C&Co*/
-bool globals;                  /* create tags for global variables */
-bool members;                  /* create tags for C member variables */
-bool update;                   /* -u: update tags */
-bool vgrind_style;             /* -v: create vgrind style index output */
-bool no_warnings;              /* -w: suppress warnings */
-bool cxref_style;              /* -x: create cxref style output */
-bool cplusplus;                        /* .[hc] means C++, not C */
-bool noindentypedefs;          /* -I: ignore indentation in C */
-bool packages_only;            /* --packages-only: in Ada, only tag packages*/
-
-#ifdef LONG_OPTIONS
-struct option longopts[] =
-{
-  { "packages-only",      no_argument,      &packages_only, TRUE  },
-  { "append",            no_argument,       NULL,           'a'   },
-  { "backward-search",   no_argument,       NULL,           'B'   },
-  { "c++",               no_argument,       NULL,           'C'   },
-  { "cxref",             no_argument,       NULL,           'x'   },
-  { "defines",           no_argument,       NULL,           'd'   },
-  { "declarations",      no_argument,       &declarations,  TRUE  },
-  { "no-defines",        no_argument,       NULL,           'D'   },
-  { "globals",           no_argument,       &globals,       TRUE  },
-  { "no-globals",        no_argument,       &globals,       FALSE },
-  { "help",              no_argument,       NULL,           'h'   },
-  { "help",              no_argument,       NULL,           'H'   },
-  { "ignore-indentation", no_argument,      NULL,           'I'   },
-  { "include",           required_argument, NULL,           'i'   },
-  { "language",           required_argument, NULL,                  'l'   },
-  { "members",           no_argument,       &members,       TRUE  },
-  { "no-members",        no_argument,       &members,       FALSE },
-  { "no-warn",           no_argument,       NULL,           'w'   },
-  { "output",            required_argument, NULL,           'o'   },
-#ifdef ETAGS_REGEXPS
-  { "regex",             required_argument, NULL,           'r'   },
-  { "no-regex",                  no_argument,       NULL,           'R'   },
-  { "ignore-case-regex",  required_argument, NULL,          'c'   },
-#endif /* ETAGS_REGEXPS */
-  { "typedefs",                  no_argument,       NULL,           't'   },
-  { "typedefs-and-c++",          no_argument,       NULL,           'T'   },
-  { "update",            no_argument,       NULL,           'u'   },
-  { "version",           no_argument,       NULL,           'V'   },
-  { "vgrind",            no_argument,       NULL,           'v'   },
-  { NULL }
-};
-#endif /* LONG_OPTIONS */
+static bool globals;           /* create tags for global variables */
+static bool declarations;      /* --declarations: tag them and extern in C&Co*/
+static bool members;           /* create tags for C member variables */
+static bool no_line_directive; /* ignore #line directives */
+static bool update;            /* -u: update tags */
+static bool vgrind_style;      /* -v: create vgrind style index output */
+static bool no_warnings;       /* -w: suppress warnings */
+static bool cxref_style;       /* -x: create cxref style output */
+static bool cplusplus;         /* .[hc] means C++, not C */
+static bool noindentypedefs;   /* -I: ignore indentation in C */
+static bool packages_only;     /* --packages-only: in Ada, only tag packages*/
 
 #ifdef ETAGS_REGEXPS
-/* Structure defining a regular expression.  Elements are
-   the compiled pattern, and the name string. */
-typedef struct pattern
-{
-  struct pattern *p_next;
-  language *lang;
-  char *regex;
-  struct re_pattern_buffer *pat;
-  struct re_registers regs;
-  char *name_pattern;
-  bool error_signaled;
-} pattern;
-
 /* List of all regexps. */
-pattern *p_head = NULL;
+static pattern *p_head;
 
 /* How many characters in the character set.  (From regex.c.)  */
 #define CHAR_SET_SIZE 256
 /* Translation table for case-insensitive matching. */
-char lc_trans[CHAR_SET_SIZE];
+static char lc_trans[CHAR_SET_SIZE];
+#endif /* ETAGS_REGEXPS */
+
+#ifdef LONG_OPTIONS
+static struct option longopts[] =
+{
+  { "packages-only",      no_argument,      &packages_only,     TRUE  },
+  { "c++",               no_argument,       NULL,               'C'   },
+  { "declarations",      no_argument,       &declarations,      TRUE  },
+  { "no-line-directive",  no_argument,      &no_line_directive, TRUE  },
+  { "help",              no_argument,       NULL,               'h'   },
+  { "help",              no_argument,       NULL,               'H'   },
+  { "ignore-indentation", no_argument,      NULL,               'I'   },
+  { "language",           required_argument, NULL,                      'l'   },
+  { "members",           no_argument,       &members,           TRUE  },
+  { "no-members",        no_argument,       &members,           FALSE },
+  { "output",            required_argument, NULL,               'o'   },
+#ifdef ETAGS_REGEXPS
+  { "regex",             required_argument, NULL,               'r'   },
+  { "no-regex",                  no_argument,       NULL,               'R'   },
+  { "ignore-case-regex",  required_argument, NULL,              'c'   },
 #endif /* ETAGS_REGEXPS */
+  { "version",           no_argument,       NULL,               'V'   },
+
+#if CTAGS /* Etags options */
+  { "backward-search",   no_argument,       NULL,               'B'   },
+  { "cxref",             no_argument,       NULL,               'x'   },
+  { "defines",           no_argument,       NULL,               'd'   },
+  { "globals",           no_argument,       &globals,           TRUE  },
+  { "typedefs",                  no_argument,       NULL,               't'   },
+  { "typedefs-and-c++",          no_argument,       NULL,               'T'   },
+  { "update",            no_argument,       NULL,               'u'   },
+  { "vgrind",            no_argument,       NULL,               'v'   },
+  { "no-warn",           no_argument,       NULL,               'w'   },
+
+#else /* Ctags options */
+  { "append",            no_argument,       NULL,               'a'   },
+  { "no-defines",        no_argument,       NULL,               'D'   },
+  { "no-globals",        no_argument,       &globals,           FALSE },
+  { "include",           required_argument, NULL,               'i'   },
+#endif
+  { NULL }
+};
+#endif /* LONG_OPTIONS */
 
-compressor compressors[] =
+static compressor compressors[] =
 {
   { "z", "gzip -d -c"},
   { "Z", "gzip -d -c"},
@@ -476,95 +516,94 @@ compressor compressors[] =
  * Language stuff.
  */
 
-/* Non-NULL if language fixed. */
-language *forced_lang = NULL;
-
 /* Ada code */
-char *Ada_suffixes [] =
+static char *Ada_suffixes [] =
   { "ads", "adb", "ada", NULL };
 
 /* Assembly code */
-char *Asm_suffixes [] = { "a", /* Unix assembler */
-                         "asm", /* Microcontroller assembly */
-                         "def", /* BSO/Tasking definition includes  */
-                         "inc", /* Microcontroller include files */
-                         "ins", /* Microcontroller include files */
-                         "s", "sa", /* Unix assembler */
-                         "S",   /* cpp-processed Unix assembler */
-                         "src", /* BSO/Tasking C compiler output */
-                         NULL
-                       };
+static char *Asm_suffixes [] =
+  { "a",       /* Unix assembler */
+    "asm", /* Microcontroller assembly */
+    "def", /* BSO/Tasking definition includes  */
+    "inc", /* Microcontroller include files */
+    "ins", /* Microcontroller include files */
+    "s", "sa", /* Unix assembler */
+    "S",   /* cpp-processed Unix assembler */
+    "src", /* BSO/Tasking C compiler output */
+    NULL
+  };
 
 /* Note that .c and .h can be considered C++, if the --c++ flag was
    given, or if the `class' keyowrd is met inside the file.
    That is why default_C_entries is called for these. */
-char *default_C_suffixes [] =
+static char *default_C_suffixes [] =
   { "c", "h", NULL };
 
-char *Cplusplus_suffixes [] =
+static char *Cplusplus_suffixes [] =
   { "C", "c++", "cc", "cpp", "cxx", "H", "h++", "hh", "hpp", "hxx",
     "M",                       /* Objective C++ */
     "pdb",                     /* Postscript with C syntax */
     NULL };
 
-char *Cjava_suffixes [] =
+static char *Cjava_suffixes [] =
   { "java", NULL };
 
-char *Cobol_suffixes [] =
+static char *Cobol_suffixes [] =
   { "COB", "cob", NULL };
 
-char *Cstar_suffixes [] =
+static char *Cstar_suffixes [] =
   { "cs", "hs", NULL };
 
-char *Erlang_suffixes [] =
+static char *Erlang_suffixes [] =
   { "erl", "hrl", NULL };
 
-char *Fortran_suffixes [] =
+static char *Fortran_suffixes [] =
   { "F", "f", "f90", "for", NULL };
 
-char *Lisp_suffixes [] =
+static char *Lisp_suffixes [] =
   { "cl", "clisp", "el", "l", "lisp", "LSP", "lsp", "ml", NULL };
 
-char *Makefile_filenames [] =
+static char *Makefile_filenames [] =
   { "Makefile", "makefile", "GNUMakefile", "Makefile.in", "Makefile.am", NULL};
 
-char *Pascal_suffixes [] =
+static char *Pascal_suffixes [] =
   { "p", "pas", NULL };
 
-char *Perl_suffixes [] =
+static char *Perl_suffixes [] =
   { "pl", "pm", NULL };
-char *Perl_interpreters [] =
+
+static char *Perl_interpreters [] =
   { "perl", "@PERL@", NULL };
 
-char *PHP_suffixes [] =
+static char *PHP_suffixes [] =
   { "php", "php3", "php4", NULL };
 
-char *plain_C_suffixes [] =
+static char *plain_C_suffixes [] =
   { "lm",                      /* Objective lex file */
     "m",                       /* Objective C file */
     "pc",                      /* Pro*C file */
      NULL };
 
-char *Postscript_suffixes [] =
+static char *Postscript_suffixes [] =
   { "ps", "psw", NULL };       /* .psw is for PSWrap */
 
-char *Prolog_suffixes [] =
+static char *Prolog_suffixes [] =
   { "prolog", NULL };
 
-char *Python_suffixes [] =
+static char *Python_suffixes [] =
   { "py", NULL };
 
 /* Can't do the `SCM' or `scm' prefix with a version number. */
-char *Scheme_suffixes [] =
+static char *Scheme_suffixes [] =
   { "oak", "sch", "scheme", "SCM", "scm", "SM", "sm", "ss", "t", NULL };
 
-char *TeX_suffixes [] =
+static char *TeX_suffixes [] =
   { "bib", "clo", "cls", "ltx", "sty", "TeX", "tex", NULL };
 
-char *Texinfo_suffixes [] =
+static char *Texinfo_suffixes [] =
   { "texi", "texinfo", "txi", NULL };
 
-char *Yacc_suffixes [] =
+static char *Yacc_suffixes [] =
   { "y", "y++", "ym", "yxx", "yy", NULL }; /* .ym is Objective yacc file */
 
 /*
@@ -574,33 +613,33 @@ char *Yacc_suffixes [] =
  * name.  I just didn't.
  */
 
-language lang_names [] =
+static language lang_names [] =
 {
-  { "ada",               Ada_funcs,            NULL, Ada_suffixes,             NULL },
-  { "asm",               Asm_labels,           NULL, Asm_suffixes,             NULL },
-  { "c",                 default_C_entries,    NULL, default_C_suffixes,       NULL },
-  { "c++",               Cplusplus_entries,    NULL, Cplusplus_suffixes,       NULL },
-  { "c*",                Cstar_entries,        NULL, Cstar_suffixes,           NULL },
-  { "cobol",             Cobol_paragraphs,     NULL, Cobol_suffixes,           NULL },
-  { "erlang",            Erlang_functions,     NULL, Erlang_suffixes,          NULL },
-  { "fortran",           Fortran_functions,    NULL, Fortran_suffixes,         NULL },
-  { "java",              Cjava_entries,        NULL, Cjava_suffixes,           NULL },
-  { "lisp",              Lisp_functions,       NULL, Lisp_suffixes,            NULL },
-  { "makefile",   Makefile_targets,     Makefile_filenames, NULL,      NULL },
-  { "pascal",            Pascal_functions,     NULL, Pascal_suffixes,          NULL },
-  { "perl",              Perl_functions,     NULL, Perl_suffixes, Perl_interpreters },
-  { "php",       PHP_functions,        NULL, PHP_suffixes,             NULL },
-  { "postscript", Postscript_functions, NULL, Postscript_suffixes,     NULL },
-  { "proc",              plain_C_entries,      NULL, plain_C_suffixes,         NULL },
-  { "prolog",            Prolog_functions,     NULL, Prolog_suffixes,          NULL },
-  { "python",            Python_functions,     NULL, Python_suffixes,          NULL },
-  { "scheme",            Scheme_functions,     NULL, Scheme_suffixes,          NULL },
-  { "tex",               TeX_commands,         NULL, TeX_suffixes,             NULL },
-  { "texinfo",           Texinfo_nodes,        NULL, Texinfo_suffixes,         NULL },
-  { "yacc",              Yacc_entries,         NULL, Yacc_suffixes,            NULL },
-  { "auto", NULL },             /* default guessing scheme */
-  { "none", just_read_file },   /* regexp matching only */
-  { NULL, NULL }                /* end of list */
+  { "ada",      FALSE, Ada_funcs,            NULL, Ada_suffixes,        NULL },
+  { "asm",      FALSE, Asm_labels,           NULL, Asm_suffixes,        NULL },
+  { "c",        FALSE, default_C_entries,    NULL, default_C_suffixes,  NULL },
+  { "c++",      FALSE, Cplusplus_entries,    NULL, Cplusplus_suffixes,  NULL },
+  { "c*",       FALSE, Cstar_entries,        NULL, Cstar_suffixes,      NULL },
+  { "cobol",    FALSE, Cobol_paragraphs,     NULL, Cobol_suffixes,      NULL },
+  { "erlang",   FALSE, Erlang_functions,     NULL, Erlang_suffixes,     NULL },
+  { "fortran",  FALSE, Fortran_functions,    NULL, Fortran_suffixes,    NULL },
+  { "java",     FALSE, Cjava_entries,        NULL, Cjava_suffixes,      NULL },
+  { "lisp",     FALSE, Lisp_functions,       NULL, Lisp_suffixes,       NULL },
+  { "makefile", FALSE, Makefile_targets,     Makefile_filenames, NULL,  NULL },
+  { "pascal",   FALSE, Pascal_functions,     NULL, Pascal_suffixes,     NULL },
+  { "perl",     FALSE, Perl_functions,NULL, Perl_suffixes, Perl_interpreters },
+  { "php",      FALSE, PHP_functions,        NULL, PHP_suffixes,        NULL },
+  { "postscript",FALSE, Postscript_functions,NULL, Postscript_suffixes, NULL },
+  { "proc",     FALSE, plain_C_entries,      NULL, plain_C_suffixes,    NULL },
+  { "prolog",   FALSE, Prolog_functions,     NULL, Prolog_suffixes,     NULL },
+  { "python",   FALSE, Python_functions,     NULL, Python_suffixes,     NULL },
+  { "scheme",   FALSE, Scheme_functions,     NULL, Scheme_suffixes,     NULL },
+  { "tex",      FALSE, TeX_commands,         NULL, TeX_suffixes,        NULL },
+  { "texinfo",  FALSE, Texinfo_nodes,        NULL, Texinfo_suffixes,    NULL },
+  { "yacc",      TRUE, Yacc_entries,         NULL, Yacc_suffixes,       NULL },
+  { "auto", FALSE, NULL },             /* default guessing scheme */
+  { "none", FALSE, just_read_file },   /* regexp matching only */
+  { NULL, FALSE, NULL }                /* end of list */
 };
 
 \f
@@ -643,7 +682,7 @@ static void
 print_version ()
 {
   printf ("%s (%s %s)\n", (CTAGS) ? "ctags" : "etags", EMACS_NAME, VERSION);
-  puts ("Copyright (C) 1999 Free Software Foundation, Inc. and Ken Arnold");
+  puts ("Copyright (C) 2002 Free Software Foundation, Inc. and Ken Arnold");
   puts ("This program is distributed under the same terms as Emacs");
 
   exit (GOOD);
@@ -661,17 +700,16 @@ These are the options accepted by %s.\n", progname, progname);
   puts ("Long option names do not work with this executable, as it is not\n\
 linked with GNU getopt.");
 #endif /* LONG_OPTIONS */
-  puts ("A - as file name means read names from stdin (one per line).");
-  if (!CTAGS)
-    printf ("  Absolute names are stored in the output file as they are.\n\
-Relative ones are stored relative to the output file's directory.");
-  puts ("\n");
+  puts ("  A - as file name means read names from stdin (one per line).\n\
+Absolute names are stored in the output file as they are.\n\
+Relative ones are stored relative to the output file's directory.\n");
 
-  puts ("-a, --append\n\
+  if (!CTAGS)
+    puts ("-a, --append\n\
         Append tag entries to existing tags file.");
 
   puts ("--packages-only\n\
-        For Ada files, only generate tags for packages .");
+        For Ada files, only generate tags for packages.");
 
   if (CTAGS)
     puts ("-B, --backward-search\n\
@@ -703,15 +741,14 @@ Relative ones are stored relative to the output file's directory.");
        This makes the tags file smaller.");
 
   if (!CTAGS)
-    {
-      puts ("-i FILE, --include=FILE\n\
+    puts ("-i FILE, --include=FILE\n\
         Include a note in tag file indicating that, when searching for\n\
         a tag, one should also consult the tags file FILE after\n\
         checking the current file.");
-      puts ("-l LANG, --language=LANG\n\
+
+  puts ("-l LANG, --language=LANG\n\
         Force the following files to be considered as written in the\n\
        named language up to the next --language=LANG option.");
-    }
 
   if (CTAGS)
     puts ("--globals\n\
@@ -752,13 +789,19 @@ Relative ones are stored relative to the output file's directory.");
       puts ("-T, --typedefs-and-c++\n\
         Generate tag entries for C typedefs, C struct/enum/union tags,\n\
         and C++ member functions.");
-      puts ("-u, --update\n\
+    }
+
+  if (CTAGS)
+    puts ("-u, --update\n\
         Update the tag entries for the given files, leaving tag\n\
         entries for other files in place.  Currently, this is\n\
         implemented by deleting the existing entries for the given\n\
         files and then rewriting the new entries at the end of the\n\
         tags file.  It is often faster to simply rebuild the entire\n\
         tag file than to use this.");
+
+  if (CTAGS)
+    {
       puts ("-v, --vgrind\n\
         Generates an index of items intended for human consumption,\n\
         similar to the output of vgrind.  The index is sorted, and\n\
@@ -787,22 +830,6 @@ Relative ones are stored relative to the output file's directory.");
 }
 
 \f
-enum argument_type
-{
-  at_language,
-  at_regexp,
-  at_filename,
-  at_icregexp
-};
-
-/* This structure helps us allow mixing of --lang and file names. */
-typedef struct
-{
-  enum argument_type arg_type;
-  char *what;
-  language *lang;              /* language of the regexp */
-} argument;
-
 #ifdef VMS                     /* VMS specific functions */
 
 #define        EOS     '\0'
@@ -936,7 +963,6 @@ main (argc, argv)
   int i;
   unsigned int nincluded_files;
   char **included_files;
-  char *this_file;
   argument *argbuffer;
   int current_arg, file_count;
   linebuffer filename_lb;
@@ -975,25 +1001,25 @@ main (argc, argv)
     {
       typedefs = typedefs_or_cplusplus = constantypedefs = TRUE;
       globals = TRUE;
-      declarations = FALSE;
-      members = FALSE;
     }
 
   while (1)
     {
       int opt;
-      char *optstring;
+      char *optstring = "-";
 
 #ifdef ETAGS_REGEXPS
-      optstring = "-aCdDf:Il:o:r:c:RStTi:BuvxwVhH";
-#else
-      optstring = "-aCdDf:Il:o:StTi:BuvxwVhH";
+      optstring = "-r:Rc:";
 #endif /* ETAGS_REGEXPS */
 
 #ifndef LONG_OPTIONS
       optstring = optstring + 1;
 #endif /* LONG_OPTIONS */
 
+      optstring = concat (optstring,
+                         "Cf:Il:o:SVhH",
+                         (CTAGS) ? "BxdtTuvw" : "aDi:");
+
       opt = getopt_long (argc, argv, optstring, longopts, 0);
       if (opt == EOF)
        break;
@@ -1014,10 +1040,7 @@ main (argc, argv)
          break;
 
          /* Common options. */
-       case 'a': append_to_tagfile = TRUE;     break;
        case 'C': cplusplus = TRUE;             break;
-       case 'd': constantypedefs = TRUE;       break;
-       case 'D': constantypedefs = FALSE;      break;
        case 'f':               /* for compatibility with old makefiles */
        case 'o':
          if (tagfile)
@@ -1042,7 +1065,6 @@ main (argc, argv)
              }
          }
          break;
-#ifdef ETAGS_REGEXPS
        case 'r':
          argbuffer[current_arg].arg_type = at_regexp;
          argbuffer[current_arg].what = optarg;
@@ -1058,7 +1080,6 @@ main (argc, argv)
          argbuffer[current_arg].what = optarg;
          ++current_arg;
          break;
-#endif /* ETAGS_REGEXPS */
        case 'V':
          print_version ();
          break;
@@ -1066,25 +1087,21 @@ main (argc, argv)
        case 'H':
          print_help ();
          break;
-       case 't':
-         typedefs = TRUE;
-         break;
-       case 'T':
-         typedefs = typedefs_or_cplusplus = TRUE;
-         break;
-#if (!CTAGS)
+
          /* Etags options */
-       case 'i':
-         included_files[nincluded_files++] = optarg;
-         break;
-#else /* CTAGS */
+       case 'a': append_to_tagfile = TRUE;                     break;
+       case 'D': constantypedefs = FALSE;                      break;
+       case 'i': included_files[nincluded_files++] = optarg;   break;
+
          /* Ctags options. */
-       case 'B': searchar = '?';       break;
-       case 'u': update = TRUE;        break;
-       case 'v': vgrind_style = TRUE;  /*FALLTHRU*/
-       case 'x': cxref_style = TRUE;   break;
-       case 'w': no_warnings = TRUE;   break;
-#endif /* CTAGS */
+       case 'B': searchar = '?';                               break;
+       case 'd': constantypedefs = TRUE;                       break;
+       case 't': typedefs = TRUE;                              break;
+       case 'T': typedefs = typedefs_or_cplusplus = TRUE;      break;
+       case 'u': update = TRUE;                                break;
+       case 'v': vgrind_style = TRUE;                    /*FALLTHRU*/
+       case 'x': cxref_style = TRUE;                           break;
+       case 'w': no_warnings = TRUE;                           break;
        default:
          suggest_asking_for_help ();
        }
@@ -1146,10 +1163,13 @@ main (argc, argv)
    */
   for (i = 0; i < current_arg; ++i)
     {
+      static language *lang;   /* non-NULL if language is forced */
+      char *this_file;
+
       switch (argbuffer[i].arg_type)
        {
        case at_language:
-         forced_lang = argbuffer[i].lang;
+         lang = argbuffer[i].lang;
          break;
 #ifdef ETAGS_REGEXPS
        case at_regexp:
@@ -1179,9 +1199,9 @@ main (argc, argv)
                 (one per line) and use them. */
              if (streq (this_file, "-"))
                while (readline_internal (&filename_lb, stdin) > 0)
-                 process_file (filename_lb.buffer);
+                 process_file (filename_lb.buffer, lang);
              else
-               process_file (this_file);
+               process_file (this_file, lang);
 #ifdef VMS
            }
 #endif
@@ -1193,22 +1213,17 @@ main (argc, argv)
   free_patterns ();
 #endif /* ETAGS_REGEXPS */
 
-  if (!CTAGS)
-    {
-      while (nincluded_files-- > 0)
-       fprintf (tagf, "\f\n%s,include\n", *included_files++);
-
-      fclose (tagf);
-      exit (GOOD);
-    }
-
-  /* If CTAGS, we are here.  process_file did not write the tags yet,
-     because we want them ordered.  Let's do it now. */
-  if (cxref_style)
+  if (!CTAGS || cxref_style)
     {
-      put_entries (head);
-      free_tree (head);
-      head = NULL;
+      put_entries (nodehead);
+      free_tree (nodehead);
+      nodehead = NULL;
+      if (!CTAGS)
+       while (nincluded_files-- > 0)
+         fprintf (tagf, "\f\n%s,include\n", *included_files++);
+
+      if (fclose (tagf) == EOF)
+       pfatal (tagfile);
       exit (GOOD);
     }
 
@@ -1231,10 +1246,11 @@ main (argc, argv)
   tagf = fopen (tagfile, append_to_tagfile ? "a" : "w");
   if (tagf == NULL)
     pfatal (tagfile);
-  put_entries (head);
-  free_tree (head);
-  head = NULL;
-  fclose (tagf);
+  put_entries (nodehead);
+  free_tree (nodehead);
+  nodehead = NULL;
+  if (fclose (tagf) == EOF)
+    pfatal (tagfile);
 
   if (update)
     {
@@ -1246,7 +1262,6 @@ main (argc, argv)
 }
 
 
-
 /*
  * Return a compressor given the file name.  If EXTPTR is non-zero,
  * return a pointer into FILE where the compressor-specific
@@ -1339,8 +1354,9 @@ get_language_from_interpreter (interpreter)
  * Return a language given the file name.
  */
 static language *
-get_language_from_filename (file)
+get_language_from_filename (file, case_sensitive)
      char *file;
+     bool case_sensitive;
 {
   language *lang;
   char **name, **ext, *suffix;
@@ -1349,7 +1365,9 @@ get_language_from_filename (file)
   for (lang = lang_names; lang->name != NULL; lang++)
     if (lang->filenames != NULL)
       for (name = lang->filenames; *name != NULL; name++)
-       if (streq (*name, file))
+       if ((case_sensitive)
+           ? streq (*name, file)
+           : strcaseeq (*name, file))
          return lang;
 
   /* If not found, try suffix after last dot. */
@@ -1360,25 +1378,30 @@ get_language_from_filename (file)
   for (lang = lang_names; lang->name != NULL; lang++)
     if (lang->suffixes != NULL)
       for (ext = lang->suffixes; *ext != NULL; ext++)
-       if (streq (*ext, suffix))
+       if ((case_sensitive)
+           ? streq (*ext, suffix)
+           : strcaseeq (*ext, suffix))
          return lang;
   return NULL;
 }
 
-
-
+\f
 /*
  * This routine is called on each file argument.
  */
 static void
-process_file (file)
+process_file (file, lang)
      char *file;
+     language *lang;
 {
   struct stat stat_buf;
   FILE *inf;
+  static const fdesc emptyfdesc;
+  fdesc *fdp;
   compressor *compr;
   char *compressed_name, *uncompressed_name;
   char *ext, *real_name;
+  int retval;
 
 
   canonicalize_filename (file);
@@ -1398,25 +1421,14 @@ process_file (file)
       uncompressed_name = savenstr (file, ext - file);
     }
 
-  /* If the canonicalised uncompressed name has already be dealt with,
-     skip it silently, else add it to the list. */
-  {
-    typedef struct processed_file
+  /* If the canonicalized uncompressed name
+     has already been dealt with, skip it silently. */
+  for (fdp = fdhead; fdp != NULL; fdp = fdp->next)
     {
-      char *filename;
-      struct processed_file *next;
-    } processed_file;
-    static processed_file *pf_head = NULL;
-    register processed_file *fnp;
-
-    for (fnp = pf_head; fnp != NULL; fnp = fnp->next)
-      if (streq (uncompressed_name, fnp->filename))
-       goto exit;
-    fnp = pf_head;
-    pf_head = xnew (1, struct processed_file);
-    pf_head->filename = savestr (uncompressed_name);
-    pf_head->next = fnp;
-  }
+      assert (fdp->infname != NULL);
+      if (streq (uncompressed_name, fdp->infname))
+       goto cleanup;
+    }
 
   if (stat (real_name, &stat_buf) != 0)
     {
@@ -1463,14 +1475,14 @@ process_file (file)
       if (real_name == NULL)
        {
          perror (file);
-         goto exit;
+         goto cleanup;
        }
     } /* try with a different name */
 
   if (!S_ISREG (stat_buf.st_mode))
     {
       error ("skipping %s: it is not a regular file.", real_name);
-      goto exit;
+      goto cleanup;
     }
   if (real_name == compressed_name)
     {
@@ -1483,41 +1495,82 @@ process_file (file)
   if (inf == NULL)
     {
       perror (real_name);
-      goto exit;
+      goto cleanup;
+    }
+
+  /* Create a new input file description entry. */
+  fdp = xnew (1, fdesc);
+  *fdp = emptyfdesc;
+  fdp->next = fdhead;
+  fdp->infname = savestr (uncompressed_name);
+  fdp->lang = lang;
+  fdp->infabsname = absolute_filename (uncompressed_name, cwd);
+  fdp->infabsdir = absolute_dirname (uncompressed_name, cwd);
+  if (filename_is_absolute (uncompressed_name))
+    {
+      /* file is an absolute file name.  Canonicalize it. */
+      fdp->taggedfname = absolute_filename (uncompressed_name, NULL);
     }
+  else
+    {
+      /* file is a file name relative to cwd.  Make it relative
+        to the directory of the tags file. */
+      fdp->taggedfname = relative_filename (uncompressed_name, tagfiledir);
+    }
+  fdp->usecharno = TRUE;       /* use char position when making tags */
+  fdp->prop = NULL;
+
+  fdhead = fdp;
+  curfdp = fdhead;             /* the current file description */
 
-  find_entries (uncompressed_name, inf);
+  find_entries (inf);
 
   if (real_name == compressed_name)
-    pclose (inf);
+    retval = pclose (inf);
   else
-    fclose (inf);
-
-  if (!CTAGS)
+    retval = fclose (inf);
+  if (retval < 0)
+    pfatal (file);
+
+  /* If not Ctags, and if this is not metasource and if it contained no #line
+     directives, we can write the tags and free all nodes pointing to
+     curfdp. */
+  if (!CTAGS
+      && curfdp->usecharno     /* no #line directives in this file */
+      && !curfdp->lang->metasource)
     {
-      char *filename;
+      node *np, *prev;
 
-      if (filename_is_absolute (uncompressed_name))
-       {
-         /* file is an absolute file name.  Canonicalise it. */
-         filename = absolute_filename (uncompressed_name, cwd);
-       }
-      else
+      /* Look for the head of the sublist relative to this file.  See add_node
+        for the structure of the node tree. */
+      prev = NULL;
+      for (np = nodehead; np != NULL; prev = np, np = np->left)
+       if (np->fdp == curfdp)
+         break;
+
+      /* If we generated tags for this file, write and delete them. */
+      if (np != NULL)
        {
-         /* file is a file name relative to cwd.  Make it relative
-            to the directory of the tags file. */
-         filename = relative_filename (uncompressed_name, tagfiledir);
+         /* This is the head of the last sublist, if any.  The following
+            instructions depend on this being true. */
+         assert (np->left == NULL);
+
+         assert (fdhead == curfdp);
+         assert (last_node->fdp == curfdp);
+         put_entries (np);     /* write tags for file curfdp->taggedfname */
+         free_tree (np);       /* remove the written nodes */
+         if (prev == NULL)
+           nodehead = NULL;    /* no nodes left */
+         else
+           prev->left = NULL;  /* delete the pointer to the sublist */
        }
-      fprintf (tagf, "\f\n%s,%d\n", filename, total_size_of_entries (head));
-      free (filename);
-      put_entries (head);
-      free_tree (head);
-      head = NULL;
     }
 
- exit:
-  if (compressed_name) free(compressed_name);
-  if (uncompressed_name) free(uncompressed_name);
+ cleanup:
+  if (compressed_name) free (compressed_name);
+  if (uncompressed_name) free (uncompressed_name);
+  last_node = NULL;
+  curfdp = NULL;
   return;
 }
 
@@ -1552,44 +1605,35 @@ init ()
  * This routine opens the specified file and calls the function
  * which finds the function and type definitions.
  */
-node *last_node = NULL;
-
 static void
-find_entries (file, inf)
-     char *file;
+find_entries (inf)
      FILE *inf;
 {
   char *cp;
-  language *lang;
   node *old_last_node;
-
-  /* Memory leakage here: the string pointed by curfile is
-     never released, because curfile is copied into np->file
-     for each node, to be used in CTAGS mode.  The amount of
-     memory leaked here is the sum of the lengths of the
-     file names. */
-  curfile = savestr (file);
+  language *lang = curfdp->lang;
+  Lang_function *parser = NULL;
 
   /* If user specified a language, use it. */
-  lang = forced_lang;
   if (lang != NULL && lang->function != NULL)
     {
-      curlang = lang;
-      lang->function (inf);
-      return;
+      parser = lang->function;
     }
 
-  /* Try to guess the language given the file name. */
-  lang = get_language_from_filename (file);
-  if (lang != NULL && lang->function != NULL)
+  /* Else try to guess the language given the file name. */
+  if (parser == NULL)
     {
-      curlang = lang;
-      lang->function (inf);
-      return;
+      lang = get_language_from_filename (curfdp->infname, TRUE);
+      if (lang != NULL && lang->function != NULL)
+       {
+         curfdp->lang = lang;
+         parser = lang->function;
+       }
     }
 
-  /* Look for sharp-bang as the first two characters. */
-  if (readline_internal (&lb, inf) > 0
+  /* Else look for sharp-bang as the first two characters. */
+  if (parser == NULL
+      && readline_internal (&lb, inf) > 0
       && lb.len >= 2
       && lb.buffer[0] == '#'
       && lb.buffer[1] == '!')
@@ -1612,29 +1656,79 @@ find_entries (file, inf)
          lang = get_language_from_interpreter (lp);
          if (lang != NULL && lang->function != NULL)
            {
-             curlang = lang;
-             lang->function (inf);
-             return;
+             curfdp->lang = lang;
+             parser = lang->function;
            }
        }
     }
+
   /* We rewind here, even if inf may be a pipe.  We fail if the
      length of the first line is longer than the pipe block size,
      which is unlikely. */
-  rewind (inf);
+  if (parser == NULL)
+    rewind (inf);
+
+  /* Else try to guess the language given the case insensitive file name. */
+  if (parser == NULL)
+    {
+      lang = get_language_from_filename (curfdp->infname, FALSE);
+      if (lang != NULL && lang->function != NULL)
+       {
+         curfdp->lang = lang;
+         parser = lang->function;
+       }
+    }
 
-  /* Try Fortran. */
+  if (!no_line_directive
+      && curfdp->lang != NULL && curfdp->lang->metasource)
+    /* It may be that this is a bingo.y file, and we already parsed a bingo.c
+       file, or anyway we parsed a file that is automatically generated from
+       this one.  If this is the case, the bingo.c file contained #line
+       directives that generated tags pointing to this file.  Let's delete
+       them all before parsing this file, which is the real source. */
+    {
+      fdesc **fdpp = &fdhead;
+      while (*fdpp != NULL)
+       if (*fdpp != curfdp
+           && streq ((*fdpp)->taggedfname, curfdp->taggedfname))
+         /* We found one of those!  We must delete both the file description
+            and all tags referring to it. */
+         {
+           fdesc *badfdp = *fdpp;
+
+           if (DEBUG)
+             fprintf (stderr,
+                      "Removing references to \"%s\" obtained from \"%s\"\n",
+                      badfdp->taggedfname, badfdp->infname);
+
+           /* Delete the tags referring to badfdp. */
+           invalidate_nodes (badfdp, &nodehead);
+
+           *fdpp = badfdp->next; /* remove the bad description from the list */
+           free_fdesc (badfdp);
+         }
+       else
+         fdpp = &(*fdpp)->next; /* advance the list pointer */
+    }
+
+  if (parser != NULL)
+    {
+      parser (inf);
+      return;
+    }
+
+  /* Else try Fortran. */
   old_last_node = last_node;
-  curlang = get_language_from_langname ("fortran");
+  curfdp->lang = get_language_from_langname ("fortran");
   Fortran_functions (inf);
 
-  /* No Fortran entries found.  Try C. */
   if (old_last_node == last_node)
+    /* No Fortran entries found.  Try C. */
     {
       /* We do not tag if rewind fails.
         Only the file name will be recorded in the tags file. */
       rewind (inf);
-      curlang = get_language_from_langname (cplusplus ? "c++" : "c");
+      curfdp->lang = get_language_from_langname (cplusplus ? "c++" : "c");
       default_C_entries (inf);
     }
   return;
@@ -1661,24 +1755,28 @@ pfnote (name, is_func, linestart, linelen, lno, cno)
   /* If ctags mode, change name "main" to M<thisfilename>. */
   if (CTAGS && !cxref_style && streq (name, "main"))
     {
-      register char *fp = etags_strrchr (curfile, '/');
-      np->name = concat ("M", fp == NULL ? curfile : fp + 1, "");
+      register char *fp = etags_strrchr (curfdp->taggedfname, '/');
+      np->name = concat ("M", fp == NULL ? curfdp->taggedfname : fp + 1, "");
       fp = etags_strrchr (np->name, '.');
       if (fp != NULL && fp[1] != '\0' && fp[2] == '\0')
        fp[0] = '\0';
     }
   else
     np->name = name;
+  np->valid = TRUE;
   np->been_warned = FALSE;
-  np->file = curfile;
+  np->fdp = curfdp;
   np->is_func = is_func;
   np->lno = lno;
-  /* Our char numbers are 0-base, because of C language tradition?
-     ctags compatibility?  old versions compatibility?   I don't know.
-     Anyway, since emacs's are 1-base we expect etags.el to take care
-     of the difference.  If we wanted to have 1-based numbers, we would
-     uncomment the +1 below. */
-  np->cno = cno /* + 1 */ ;
+  if (np->fdp->usecharno)
+    /* Our char numbers are 0-base, because of C language tradition?
+       ctags compatibility?  old versions compatibility?   I don't know.
+       Anyway, since emacs's are 1-base we expect etags.el to take care
+       of the difference.  If we wanted to have 1-based numbers, we would
+       uncomment the +1 below. */
+    np->cno = cno /* + 1 */ ;
+  else
+    np->cno = invalidcharno;
   np->left = np->right = NULL;
   if (CTAGS && !cxref_style)
     {
@@ -1690,7 +1788,7 @@ pfnote (name, is_func, linestart, linelen, lno, cno)
   else
     np->pat = savenstr (linestart, linelen);
 
-  add_node (np, &head);
+  add_node (np, &nodehead);
 }
 
 /*
@@ -1769,11 +1867,27 @@ free_tree (np)
     }
 }
 
+/*
+ * free_fdesc ()
+ *     delete a file description
+ */
+static void
+free_fdesc (fdp)
+     register fdesc *fdp;
+{
+  if (fdp->infname != NULL) free (fdp->infname);
+  if (fdp->infabsname != NULL) free (fdp->infabsname);
+  if (fdp->infabsdir != NULL) free (fdp->infabsdir);
+  if (fdp->taggedfname != NULL) free (fdp->taggedfname);
+  if (fdp->prop != NULL) free (fdp->prop);
+  free (fdp);
+}
+
 /*
  * add_node ()
- *     Adds a node to the tree of nodes.  In etags mode, we don't keep
- *     it sorted; we just keep a linear list.  In ctags mode, maintain
- *     an ordered tree, with no attempt at balancing.
+ *     Adds a node to the tree of nodes.  In etags mode, sort by file
+ *     name.  In ctags mode, sort by tag name.  Make no attempt at
+ *     balancing.
  *
  *     add_node is the only function allowed to add nodes, so it can
  *     maintain state.
@@ -1793,13 +1907,31 @@ add_node (np, cur_node_p)
     }
 
   if (!CTAGS)
+    /* Etags Mode */
     {
-      /* Etags Mode */
-      if (last_node == NULL)
-       fatal ("internal error in add_node", (char *)NULL);
-      last_node->right = np;
-      last_node = np;
-    }
+      /* For each file name, tags are in a linked sublist on the right
+        pointer.  The first tags of different files are a linked list
+        on the left pointer.  last_node points to the end of the last
+        used sublist. */
+      if (last_node != NULL && last_node->fdp == np->fdp)
+       {
+         /* Let's use the same sublist as the last added node. */
+         assert (last_node->right == NULL);
+         last_node->right = np;
+         last_node = np;
+       }
+      else if (cur_node->fdp == np->fdp)
+       {
+         /* Scanning the list we found the head of a sublist which is
+            good for us.  Let's scan this sublist. */
+         add_node (np, &cur_node->right);
+       }
+      else
+       /* The head of this sublist is not good for us.  Let's try the
+          next one. */
+       add_node (np, &cur_node->left);
+    } /* if ETAGS mode */
+
   else
     {
       /* Ctags Mode */
@@ -1811,12 +1943,12 @@ add_node (np, cur_node_p)
        */
       if (!dif)
        {
-         if (streq (np->file, cur_node->file))
+         if (np->fdp == cur_node->fdp)
            {
              if (!no_warnings)
                {
                  fprintf (stderr, "Duplicate entry in file %s, line %d: %s\n",
-                          np->file, lineno, np->name);
+                          np->fdp->infname, lineno, np->name);
                  fprintf (stderr, "Second entry ignored\n");
                }
            }
@@ -1825,7 +1957,7 @@ add_node (np, cur_node_p)
              fprintf
                (stderr,
                 "Duplicate entry in files %s and %s: %s (Warning only)\n",
-                np->file, cur_node->file, np->name);
+                np->fdp->infname, cur_node->fdp->infname, np->name);
              cur_node->been_warned = TRUE;
            }
          return;
@@ -1833,77 +1965,51 @@ add_node (np, cur_node_p)
 
       /* Actually add the node */
       add_node (np, dif < 0 ? &cur_node->left : &cur_node->right);
-    }
+    } /* if CTAGS mode */
 }
 
-\f
+/*
+ * invalidate_nodes ()
+ *     Scan the node tree and invalidate all nodes pointing to the
+ *     given file description (CTAGS case) or free them (ETAGS case).
+ */
 static void
-put_entries (np)
-     register node *np;
+invalidate_nodes (badfdp, npp)
+     fdesc *badfdp;
+     node **npp;
 {
-  register char *sp;
+  node *np = *npp;
 
   if (np == NULL)
     return;
 
-  /* Output subentries that precede this one */
-  put_entries (np->left);
-
-  /* Output this entry */
-
-  if (!CTAGS)
+  if (CTAGS)
     {
-      if (np->name != NULL)
-       fprintf (tagf, "%s\177%s\001%d,%ld\n",
-                np->pat, np->name, np->lno, np->cno);
-      else
-       fprintf (tagf, "%s\177%d,%ld\n",
-                np->pat, np->lno, np->cno);
+      if (np->left != NULL)
+       invalidate_nodes (badfdp, &np->left);
+      if (np->fdp == badfdp)
+       np-> valid = FALSE;
+      if (np->right != NULL)
+       invalidate_nodes (badfdp, &np->right);
     }
   else
     {
-      if (np->name == NULL)
-       error ("internal error: NULL name in ctags mode.", (char *)NULL);
-
-      if (cxref_style)
-       {
-         if (vgrind_style)
-           fprintf (stdout, "%s %s %d\n",
-                    np->name, np->file, (np->lno + 63) / 64);
-         else
-           fprintf (stdout, "%-16s %3d %-16s %s\n",
-                    np->name, np->lno, np->file, np->pat);
-       }
-      else
+      node **next = &np->left;
+      if (np->fdp == badfdp)
        {
-         fprintf (tagf, "%s\t%s\t", np->name, np->file);
-
-         if (np->is_func)
-           {                   /* a function */
-             putc (searchar, tagf);
-             putc ('^', tagf);
-
-             for (sp = np->pat; *sp; sp++)
-               {
-                 if (*sp == '\\' || *sp == searchar)
-                   putc ('\\', tagf);
-                 putc (*sp, tagf);
-               }
-             putc (searchar, tagf);
-           }
-         else
-           {                   /* a typedef; text pattern inadequate */
-             fprintf (tagf, "%d", np->lno);
-           }
-         putc ('\n', tagf);
+         *npp = *next;         /* detach the sublist from the list */
+         np->left = NULL;      /* isolate it */
+         free_tree (np);       /* free it */
        }
+      invalidate_nodes (badfdp, next);
     }
-
-  /* Output subentries that follow this one */
-  put_entries (np->right);
 }
 
-/* Length of a number's decimal representation. */
+\f
+static int total_size_of_entries __P((node *));
+static int number_len __P((long));
+
+/* Length of a non-negative number's decimal representation. */
 static int
 number_len (num)
      long num;
@@ -1916,35 +2022,115 @@ number_len (num)
 
 /*
  * Return total number of characters that put_entries will output for
- * the nodes in the subtree of the specified node.  Works only if
- * we are not ctags, but called only in that case.  This count
- * is irrelevant with the new tags.el, but is still supplied for
- * backward compatibility.
+ * the nodes in the linked list at the right of the specified node.
+ * This count is irrelevant with etags.el since emacs 19.34 at least,
+ * but is still supplied for backward compatibility.
  */
 static int
 total_size_of_entries (np)
      register node *np;
 {
-  register int total;
-
-  if (np == NULL)
-    return 0;
+  register int total = 0;
 
-  for (total = 0; np != NULL; np = np->right)
+  for (; np != NULL; np = np->right)
     {
-      /* Count left subentries. */
-      total += total_size_of_entries (np->left);
-
-      /* Count this entry */
-      total += strlen (np->pat) + 1;
-      total += number_len ((long) np->lno) + 1 + number_len (np->cno) + 1;
+      total += strlen (np->pat) + 1;           /* pat\177 */
       if (np->name != NULL)
-       total += 1 + strlen (np->name); /* \001name */
+       total += strlen (np->name) + 1;         /* name\001 */
+      total += number_len ((long) np->lno) + 1;        /* lno, */
+      if (np->cno != invalidcharno)            /* cno */
+       total += number_len (np->cno);
+      total += 1;                              /* newline */
     }
 
   return total;
 }
 
+static void
+put_entries (np)
+     register node *np;
+{
+  register char *sp;
+  static fdesc *fdp = NULL;
+
+  if (np == NULL)
+    return;
+
+  /* Output subentries that precede this one */
+  if (CTAGS)
+    put_entries (np->left);
+
+  /* Output this entry */
+  if (np->valid)
+    {
+      if (!CTAGS)
+       {
+         /* Etags mode */
+         if (fdp != np->fdp)
+           {
+             fdp = np->fdp;
+             fprintf (tagf, "\f\n%s,%d\n",
+                      fdp->taggedfname, total_size_of_entries (np));
+           }
+         fputs (np->pat, tagf);
+         fputc ('\177', tagf);
+         if (np->name != NULL)
+           {
+             fputs (np->name, tagf);
+             fputc ('\001', tagf);
+           }
+         fprintf (tagf, "%d,", np->lno);
+         if (np->cno != invalidcharno)
+           fprintf (tagf, "%ld", np->cno);
+         fputs ("\n", tagf);
+       }
+      else
+       {
+         /* Ctags mode */
+         if (np->name == NULL)
+           error ("internal error: NULL name in ctags mode.", (char *)NULL);
+
+         if (cxref_style)
+           {
+             if (vgrind_style)
+               fprintf (stdout, "%s %s %d\n",
+                        np->name, np->fdp->taggedfname, (np->lno + 63) / 64);
+             else
+               fprintf (stdout, "%-16s %3d %-16s %s\n",
+                        np->name, np->lno, np->fdp->taggedfname, np->pat);
+           }
+         else
+           {
+             fprintf (tagf, "%s\t%s\t", np->name, np->fdp->taggedfname);
+
+             if (np->is_func)
+               {               /* function or #define macro with args */
+                 putc (searchar, tagf);
+                 putc ('^', tagf);
+
+                 for (sp = np->pat; *sp; sp++)
+                   {
+                     if (*sp == '\\' || *sp == searchar)
+                       putc ('\\', tagf);
+                     putc (*sp, tagf);
+                   }
+                 putc (searchar, tagf);
+               }
+             else
+               {               /* anything else; text pattern inadequate */
+                 fprintf (tagf, "%d", np->lno);
+               }
+             putc ('\n', tagf);
+           }
+       }
+    } /* if this node contains a valid tag */
+
+  /* Output subentries that follow this one */
+  put_entries (np->right);
+  if (!CTAGS)
+    put_entries (np->left);
+}
+
 \f
 /* C extensions. */
 #define C_EXT  0x00fff         /* C extensions */
@@ -2033,7 +2219,7 @@ PSEUDO,           0,      st_C_gnumacro
 #DEFVAR_,      0,      st_C_gnumacro
 %]
 and replace lines between %< and %> with its output,
-then make in_word_set static. */
+then make in_word_set and C_stab_entry static. */
 /*%<*/
 /* C code produced by gperf version 2.7.1 (19981006 egcs) */
 /* Command-line: gperf -c -k 1,3 -o -p -r -t  */
@@ -2218,7 +2404,7 @@ C_symtype (str, len, c_ext)
  * C functions and variables are recognized using a simple
  * finite automaton.  fvdef is its state variable.
  */
-enum
+static enum
 {
   fvnone,                      /* nothing seen */
   fdefunkey,                   /* Emacs DEFUN keyword seen */
@@ -2232,13 +2418,13 @@ enum
   vignore                      /* var-like: ignore until ';' */
 } fvdef;
 
-bool fvextern;                 /* func or var: extern keyword seen; */
+static bool fvextern;          /* func or var: extern keyword seen; */
 
 /*
  * typedefs are recognized using a simple finite automaton.
  * typdef is its state variable.
  */
-enum
+static enum
 {
   tnone,                       /* nothing seen */
   tkeyseen,                    /* typedef keyword seen */
@@ -2253,7 +2439,7 @@ enum
  * using another simple finite automaton.  `structdef' is its state
  * variable.
  */
-enum
+static enum
 {
   snone,                       /* nothing seen yet,
                                   or in struct body if cblev > 0 */
@@ -2266,12 +2452,12 @@ enum
 /*
  * When objdef is different from onone, objtag is the name of the class.
  */
-char *objtag = "<uninited>";
+static char *objtag = "<uninited>";
 
 /*
  * Yet another little state machine to deal with preprocessor lines.
  */
-enum
+static enum
 {
   dnone,                       /* nothing seen */
   dsharpseen,                  /* '#' seen as first char on line */
@@ -2283,7 +2469,7 @@ enum
  * State machine for Objective C protocols and implementations.
  * Idea by Tom R.Hageman <tom@basil.icce.rug.nl> (1995)
  */
-enum
+static enum
 {
   onone,                       /* nothing seen */
   oprotocol,                   /* @interface or @protocol seen */
@@ -2304,7 +2490,7 @@ enum
  * Use this structure to keep info about the token read, and how it
  * should be tagged.  Used by the make_C_tag function to build a tag.
  */
-struct tok
+static struct tok
 {
   bool valid;
   bool named;
@@ -2314,7 +2500,7 @@ struct tok
   long linepos;
   char *line;
 } token;                       /* latest token read */
-linebuffer token_name;         /* its name */
+static linebuffer token_name;  /* its name */
 
 /*
  * Variables and functions for dealing with nested structures.
@@ -2324,7 +2510,7 @@ static void pushclass_above __P((int, char *, int));
 static void popclass_above __P((int));
 static void write_classname __P((linebuffer *, char *qualifier));
 
-struct {
+static struct {
   char **cname;                        /* nested class names */
   int *cblev;                  /* nested class curly brace level */
   int nl;                      /* class nesting level (elements used) */
@@ -2711,7 +2897,7 @@ consider_token (str, len, c, c_extp, cblev, parlev, is_func_or_var)
  * the line currently read.  By keeping two line buffers, and switching
  * them at end of line, it is possible to use those pointers.
  */
-struct
+static struct
 {
   long linepos;
   linebuffer lb;
@@ -4027,8 +4213,8 @@ Perl_functions (inf)
 
          /* Perhaps I should back cp up one character, so the TAGS table
             doesn't mention (and so depend upon) the following char. */
-         pfnote ((CTAGS) ? savenstr (lb.buffer, cp-lb.buffer) : varname,
-                 FALSE, lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
+         pfnote (varname, FALSE,
+                 lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
        }
     }
 }
@@ -4036,8 +4222,9 @@ Perl_functions (inf)
 
 /*
  * Python support
- * Look for /^def[ \t\n]+[^ \t\n(:]+/ or /^class[ \t\n]+[^ \t\n(:]+/
+ * Look for /^[\t]*def[ \t\n]+[^ \t\n(:]+/ or /^class[ \t\n]+[^ \t\n(:]+/
  * Idea by Eric S. Raymond <esr@thyrsus.com> (1997)
+ * More ideas by seb bacon <seb@jamkit.com> (2002)
  */
 static void
 Python_functions (inf)
@@ -4046,13 +4233,17 @@ Python_functions (inf)
   register char *cp;
 
   LOOP_ON_INPUT_LINES (inf, lb, cp)
-    if (LOOKING_AT (cp, "def") || LOOKING_AT (cp, "class"))
-      {
-       while (!notinname (*cp) && *cp != ':')
-         cp++;
-       pfnote (NULL, TRUE,
-               lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
-      }
+    {
+      cp = skip_spaces (cp);
+      if (LOOKING_AT (cp, "def") || LOOKING_AT (cp, "class"))
+       {
+         char *name = cp;
+         while (!notinname (*cp) && *cp != ':')
+           cp++;
+         pfnote (savenstr (name, cp-name), TRUE,
+                 lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
+       }
+    }
 }
 
 \f
@@ -4070,18 +4261,19 @@ static void
 PHP_functions (inf)
      FILE *inf;
 {
-  register char *cp;
+  register char *cp, *name;
   bool search_identifier = FALSE;
 
   LOOP_ON_INPUT_LINES (inf, lb, cp)
     {
       cp = skip_spaces (cp);
+      name = cp;
       if (search_identifier
          && *cp != '\0')
        {
          while (!notinname (*cp))
            cp++;
-         pfnote (NULL, TRUE,
+         pfnote (savenstr (name, cp-name), TRUE,
                  lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
          search_identifier = FALSE;
        }
@@ -4091,9 +4283,10 @@ PHP_functions (inf)
            cp = skip_spaces (cp+1);
          if(*cp != '\0')
            {
+             name = cp;
              while (!notinname (*cp))
                cp++;
-             pfnote (NULL, TRUE,
+             pfnote (savenstr (name, cp-name), TRUE,
                      lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
            }
          else
@@ -4103,9 +4296,10 @@ PHP_functions (inf)
        {
          if (*cp != '\0')
            {
+             name = cp;
              while (*cp != '\0' && !iswhite (*cp))
                cp++;
-             pfnote (NULL, FALSE,
+             pfnote (savenstr (name, cp-name), FALSE,
                      lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
            }
          else
@@ -4117,18 +4311,20 @@ PHP_functions (inf)
               && (*cp == '"' || *cp == '\''))
        {
          char quote = *cp++;
+         name = cp;
          while (*cp != quote && *cp != '\0')
            cp++;
-         pfnote (NULL, FALSE,
+         pfnote (savenstr (name, cp-name), FALSE,
                  lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
        }
       else if (members
               && LOOKING_AT (cp, "var")
               && *cp == '$')
        {
+         name = cp;
          while (!notinname(*cp))
            cp++;
-         pfnote (NULL, FALSE,
+         pfnote (savenstr (name, cp-name), FALSE,
                  lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
        }
     }
@@ -4507,12 +4703,12 @@ struct TEX_tabent
   int len;
 };
 
-struct TEX_tabent *TEX_toktab = NULL;  /* Table with tag tokens */
+static struct TEX_tabent *TEX_toktab = NULL; /* Table with tag tokens */
 
 /* Default set of control sequences to put into TEX_toktab.
    The value of environment var TEXTAGS is prepended to this.  */
 
-char *TEX_defenv = "\
+static char *TEX_defenv = "\
 :chapter:section:subsection:subsubsection:eqno:label:ref:cite:bibitem\
 :part:appendix:entry:index";
 
@@ -4520,9 +4716,9 @@ static void TEX_mode __P((FILE *));
 static struct TEX_tabent *TEX_decode_env __P((char *, char *));
 static int TEX_Token __P((char *));
 
-char TEX_esc = '\\';
-char TEX_opgrp = '{';
-char TEX_clgrp = '}';
+static char TEX_esc = '\\';
+static char TEX_opgrp = '{';
+static char TEX_clgrp = '}';
 
 /*
  * TeX/LaTeX scanning loop.
@@ -4554,13 +4750,16 @@ TeX_commands (inf)
          i = TEX_Token (lasthit);
          if (i >= 0)
            {
-             /* We seem to include the TeX command in the tag name.
              register char *p;
-             for (p = lasthit + TEX_toktab[i].len;
-                  *p != '\0' && *p != TEX_clgrp;
+             for (lasthit += TEX_toktab[i].len;
+                  *lasthit == TEX_esc || *lasthit == TEX_opgrp;
+                  lasthit++)
+               continue;
+             for (p = lasthit;
+                  !iswhite (*p) && *p != TEX_opgrp && *p != TEX_clgrp;
                   p++)
-               continue; */
-             pfnote (/*savenstr (lasthit, p-lasthit)*/ (char *)NULL, TRUE,
+               continue;
+             pfnote (savenstr (lasthit, p-lasthit), TRUE,
                      lb.buffer, lb.len, lineno, linecharno);
              break;            /* We only tag a line once */
            }
@@ -5211,6 +5410,7 @@ add_regex (regexp_pattern, ignore_case, lang)
   p_head->pat = patbuf;
   p_head->name_pattern = savestr (name);
   p_head->error_signaled = FALSE;
+  p_head->ignore_case = ignore_case;
 }
 
 /*
@@ -5406,51 +5606,168 @@ readline (lbp, stream)
 {
   /* Read new line. */
   long result = readline_internal (lbp, stream);
+
+  /* Honour #line directives. */
+  if (!no_line_directive)
+    {
+      static bool discard_until_line_directive;
+
+      /* Check whether this is a #line directive. */
+      if (result > 12 && strneq (lbp->buffer, "#line ", 6))
+       {
+         int start, lno;
+
+         if (DEBUG) start = 0; /* shut up the compiler */
+         if (sscanf (lbp->buffer, "#line %d \"%n", &lno, &start) == 1)
+           {
+             char *endp = lbp->buffer + start;
+
+             assert (start > 0);
+             while ((endp = etags_strchr (endp, '"')) != NULL
+                    && endp[-1] == '\\')
+               endp++;
+             if (endp != NULL)
+               /* Ok, this is a real #line directive.  Let's deal with it. */
+               {
+                 char *taggedabsname;  /* absolute name of original file */
+                 char *taggedfname;    /* name of original file as given */
+                 char *name;           /* temp var */
+
+                 discard_until_line_directive = FALSE; /* found it */
+                 name = lbp->buffer + start;
+                 *endp = '\0';
+                 canonicalize_filename (name); /* for DOS */
+                 taggedabsname = absolute_filename (name, curfdp->infabsdir);
+                 if (filename_is_absolute (name)
+                     || filename_is_absolute (curfdp->infname))
+                   taggedfname = savestr (taggedabsname);
+                 else
+                   taggedfname = relative_filename (taggedabsname,tagfiledir);
+
+                 if (streq (curfdp->taggedfname, taggedfname))
+                   /* The #line directive is only a line number change.  We
+                      deal with this afterwards. */
+                   free (taggedfname);
+                 else
+                   /* The tags following this #line directive should be
+                      attributed to taggedfname.  In order to do this, set
+                      curfdp accordingly. */
+                   {
+                     fdesc *fdp; /* file description pointer */
+
+                     /* Go look for a file description already set up for the
+                        file indicated in the #line directive.  If there is
+                        one, use it from now until the next #line
+                        directive. */
+                     for (fdp = fdhead; fdp != NULL; fdp = fdp->next)
+                       if (streq (fdp->infname, curfdp->infname)
+                           && streq (fdp->taggedfname, taggedfname))
+                         /* If we remove the second test above (after the &&)
+                            then all entries pertaining to the same file are
+                            coalesced in the tags file.  If we use it, then
+                            entries pertaining to the same file but generated
+                            from different files (via #line directives) will
+                            go into separate sections in the tags file.  These
+                            alternatives look equivalent.  The first one
+                            destroys some apparently useless information. */
+                         {
+                           curfdp = fdp;
+                           free (taggedfname);
+                           break;
+                         }
+                     /* Else, if we already tagged the real file, skip all
+                        input lines until the next #line directive. */
+                     if (fdp == NULL) /* not found */
+                       for (fdp = fdhead; fdp != NULL; fdp = fdp->next)
+                         if (streq (fdp->infabsname, taggedabsname))
+                           {
+                             discard_until_line_directive = TRUE;
+                             free (taggedfname);
+                             break;
+                           }
+                     /* Else create a new file description and use that from
+                        now on, until the next #line directive. */
+                     if (fdp == NULL) /* not found */
+                       {
+                         fdp = fdhead;
+                         fdhead = xnew (1, fdesc);
+                         *fdhead = *curfdp; /* copy curr. file description */
+                         fdhead->next = fdp;
+                         fdhead->infname = savestr (curfdp->infname);
+                         fdhead->infabsname = savestr (curfdp->infabsname);
+                         fdhead->infabsdir = savestr (curfdp->infabsdir);
+                         fdhead->taggedfname = taggedfname;
+                         fdhead->usecharno = FALSE;
+                         curfdp = fdhead;
+                       }
+                   }
+                 free (taggedabsname);
+                 lineno = lno;
+                 return readline (lbp, stream);
+               } /* if a real #line directive */
+           } /* if #line is followed by a a number */
+       } /* if line begins with "#line " */
+
+      /* If we are here, no #line directive was found. */
+      if (discard_until_line_directive)
+       {
+         if (result > 0)
+           /* Do a tail recursion on ourselves, thus discarding the contents
+              of the line buffer. */
+           return readline (lbp, stream);
+         /* End of file. */
+         discard_until_line_directive = FALSE;
+         return 0;
+       }
+    } /* if #line directives should be considered */
+
 #ifdef ETAGS_REGEXPS
-  int match;
-  pattern *pp;
+  {
+    int match;
+    pattern *pp;
 
-  /* Match against relevant patterns. */
-  if (lbp->len > 0)
-    for (pp = p_head; pp != NULL; pp = pp->p_next)
-      {
-       /* Only use generic regexps or those for the current language. */
-       if (pp->lang != NULL && pp->lang != curlang)
-         continue;
+    /* Match against relevant patterns. */
+    if (lbp->len > 0)
+      for (pp = p_head; pp != NULL; pp = pp->p_next)
+       {
+         /* Only use generic regexps or those for the current language. */
+         if (pp->lang != NULL && pp->lang != fdhead->lang)
+           continue;
 
-       match = re_match (pp->pat, lbp->buffer, lbp->len, 0, &pp->regs);
-       switch (match)
-         {
-         case -2:
-           /* Some error. */
-           if (!pp->error_signaled)
-             {
-               error ("error while matching \"%s\"", pp->regex);
-               pp->error_signaled = TRUE;
-             }
-           break;
-         case -1:
-           /* No match. */
-           break;
-         default:
-           /* Match occurred.  Construct a tag. */
-           if (pp->name_pattern[0] != '\0')
-             {
-               /* Make a named tag. */
-               char *name = substitute (lbp->buffer,
-                                        pp->name_pattern, &pp->regs);
-               if (name != NULL)
-                 pfnote (name, TRUE, lbp->buffer, match, lineno, linecharno);
-             }
-           else
-             {
-               /* Make an unnamed tag. */
-               pfnote ((char *)NULL, TRUE,
-                       lbp->buffer, match, lineno, linecharno);
-             }
-           break;
-         }
-      }
+         match = re_match (pp->pat, lbp->buffer, lbp->len, 0, &pp->regs);
+         switch (match)
+           {
+           case -2:
+             /* Some error. */
+             if (!pp->error_signaled)
+               {
+                 error ("error while matching \"%s\"", pp->regex);
+                 pp->error_signaled = TRUE;
+               }
+             break;
+           case -1:
+             /* No match. */
+             break;
+           default:
+             /* Match occurred.  Construct a tag. */
+             if (pp->name_pattern[0] != '\0')
+               {
+                 /* Make a named tag. */
+                 char *name = substitute (lbp->buffer,
+                                          pp->name_pattern, &pp->regs);
+                 if (name != NULL)
+                   pfnote (name, TRUE, lbp->buffer, match, lineno, linecharno);
+               }
+             else
+               {
+                 /* Make an unnamed tag. */
+                 pfnote ((char *)NULL, TRUE,
+                         lbp->buffer, match, lineno, linecharno);
+               }
+             break;
+           }
+       }
+  }
 #endif /* ETAGS_REGEXPS */
 
   return result;
@@ -5507,7 +5824,6 @@ etags_strrchr (sp, c)
   return (char *)r;
 }
 
-
 /*
  * Return the ptr in sp at which the character c first
  * appears; NULL if not found
@@ -5527,6 +5843,26 @@ etags_strchr (sp, c)
   return NULL;
 }
 
+/*
+ * Return TRUE if the two strings are equal, ignoring case for alphabetic
+ * characters.
+ *
+ * Analogous to BSD's strcasecmp, included for portability.
+ */
+static bool
+strcaseeq (s1, s2)
+     register const char *s1;
+     register const char *s2;
+{
+  while (*s1 != '\0'
+        && (ISALPHA (*s1) && ISALPHA (*s2)
+            ? lowcase (*s1) == lowcase (*s2)
+            : *s1 == *s2))
+    s1++, s2++;
+
+  return (*s1 == *s2);
+}
+
 /* Skip spaces, return new pointer. */
 static char *
 skip_spaces (cp)
@@ -5826,7 +6162,7 @@ linebuffer_setlen (lbp, toksize)
 }
 
 /* Like malloc but get fatal error if memory is exhausted.  */
-PTR
+static PTR
 xmalloc (size)
      unsigned int size;
 {
@@ -5836,7 +6172,7 @@ xmalloc (size)
   return result;
 }
 
-PTR
+static PTR
 xrealloc (ptr, size)
      char *ptr;
      unsigned int size;
@@ -5852,6 +6188,7 @@ xrealloc (ptr, size)
  * c-indentation-style: gnu
  * indent-tabs-mode: t
  * tab-width: 8
- * c-font-lock-extra-types: ("FILE" "bool" "language" "linebuffer")
+ * fill-column: 79
+ * c-font-lock-extra-types: ("FILE" "bool" "language" "linebuffer" "fdesc" "node")
  * End:
  */