]> code.delx.au - gnu-emacs/blobdiff - lib-src/etags.c
gethostbyname() may return a pointer to static data, which is
[gnu-emacs] / lib-src / etags.c
index 6939fb14f66ce5437db1c710f65aa07c91cd732c..d44494f931f45fc0bc67dbb8d60dfe11b39403f5 100644 (file)
@@ -28,10 +28,10 @@ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
  *     Francesco Potorti` reorganised C and C++ based on work by Joe Wells.
  *     Regexp tags by Tom Tromey.
  *
- *     Francesco Potorti` (pot@cnuce.cnr.it) is the current maintainer.
+ *     Francesco Potorti` (F.Potorti@cnuce.cnr.it) is the current maintainer.
  */
 
-char pot_etags_version[] = "@(#) pot revision number is 11.53";
+char pot_etags_version[] = "@(#) pot revision number is 11.77";
 
 #define        TRUE    1
 #define        FALSE   0
@@ -41,22 +41,29 @@ char pot_etags_version[] = "@(#) pot revision number is 11.53";
 #endif
 
 #ifdef MSDOS
-#include <fcntl.h>
-#include <sys/param.h>
+# include <string.h>
+# include <fcntl.h>
+# include <sys/param.h>
 #endif /* MSDOS */
 
 #ifdef WINDOWSNT
+# include <stdlib.h>
+# include <fcntl.h>
+# include <string.h>
+# include <io.h>
+# define MAXPATHLEN _MAX_PATH
+#endif
+
+#if !defined (MSDOS) && !defined (WINDOWSNT) && defined (STDC_HEADERS)
 #include <stdlib.h>
-#include <fcntl.h>
 #include <string.h>
-#define MAXPATHLEN _MAX_PATH
 #endif
 
 #ifdef HAVE_CONFIG_H
-#include <config.h>
-/* On some systems, Emacs defines static as nothing for the sake
-   of unexec.  We don't want that here since we don't use unexec. */
-#undef static
+# include <config.h>
+  /* On some systems, Emacs defines static as nothing for the sake
+     of unexec.  We don't want that here since we don't use unexec. */
+# undef static
 #endif
 
 #include <stdio.h>
@@ -75,7 +82,7 @@ extern int errno;
 #include <getopt.h>
 
 #ifdef ETAGS_REGEXPS
-#include <regex.h>
+# include <regex.h>
 #endif /* ETAGS_REGEXPS */
 
 /* Define CTAGS to make the program "ctags" compatible with the usual one.
@@ -90,11 +97,11 @@ extern int errno;
 
 /* Exit codes for success and failure.  */
 #ifdef VMS
-#define        GOOD    1
-#define BAD    0
+# define       GOOD    1
+# define       BAD     0
 #else
-#define        GOOD    0
-#define        BAD     1
+# define       GOOD    0
+# define       BAD     1
 #endif
 
 /* C extensions. */
@@ -105,7 +112,7 @@ extern int errno;
 #define streq(s,t)     ((DEBUG &&!(s)&&!(t)&&(abort(),1)) || !strcmp(s,t))
 #define strneq(s,t,n)  ((DEBUG &&!(s)&&!(t)&&(abort(),1)) || !strncmp(s,t,n))
 
-#define lowcase(c)     tolower ((unsigned char)c)
+#define lowcase(c)     tolower ((char)c)
 
 #define        iswhite(arg)    (_wht[arg])     /* T if char is white           */
 #define        begtoken(arg)   (_btk[arg])     /* T if char can start token    */
@@ -113,7 +120,8 @@ extern int errno;
 #define        endtoken(arg)   (_etk[arg])     /* T if char ends tokens        */
 
 #ifdef DOS_NT
-# define absolutefn(fn) (fn[0] == '/' || (isalpha (fn[0]) && fn[1] == ':'))
+# define absolutefn(fn) (fn[0] == '/' \
+                        || (fn[1] == ':' && fn[2] == '/'))
 #else
 # define absolutefn(fn) (fn[0] == '/')
 #endif
@@ -147,6 +155,7 @@ char *savenstr (), *savestr ();
 char *etags_strchr (), *etags_strrchr ();
 char *etags_getcwd ();
 char *relative_filename (), *absolute_filename (), *absolute_dirname ();
+void grow_linebuffer ();
 long *xmalloc (), *xrealloc ();
 
 typedef void Lang_function ();
@@ -156,6 +165,7 @@ Lang_function default_C_entries;
 Lang_function C_entries;
 Lang_function Cplusplus_entries;
 Lang_function Cstar_entries;
+Lang_function Erlang_functions;
 Lang_function Fortran_functions;
 Lang_function Yacc_entries;
 Lang_function Lisp_functions;
@@ -172,6 +182,7 @@ void default_C_entries ();
 void plain_C_entries ();
 void Cplusplus_entries ();
 void Cstar_entries ();
+void Erlang_functions ();
 void Fortran_functions ();
 void Yacc_entries ();
 void Lisp_functions ();
@@ -211,9 +222,7 @@ char searchar = '/';                /* use /.../ searches */
 
 int lineno;                    /* line number of current line */
 long charno;                   /* current character number */
-
-long linecharno;               /* charno of start of line; not used by C,
-                                  but by every other language. */
+long linecharno;               /* charno of start of line */
 
 char *curfile;                 /* current input file name */
 char *tagfile;                 /* output file */
@@ -229,9 +238,6 @@ NODE *head;                 /* the head of the binary tree of tags */
  * `readline' reads a line from a stream into a linebuffer and works
  * regardless of the length of the line.
  */
-#define GROW_LINEBUFFER(buf,toksize)                                   \
-while (buf.size < toksize)                                             \
-  buf.buffer = (char *) xrealloc (buf.buffer, buf.size *= 2)
 struct linebuffer
 {
   long size;
@@ -265,7 +271,7 @@ logical typedefs_and_cplusplus;     /* -T: create tags for typedefs, level */
                                /* 0 struct/enum/union decls, and C++ */
                                /* member functions. */
 logical constantypedefs;       /* -d: create tags for C #define and enum */
-                               /* constants.  Enum consts not implemented. */
+                               /* constants. */
                                /* -D: opposite of -d.  Default under ctags. */
 logical update;                        /* -u: update tags */
 logical vgrind_style;          /* -v: create vgrind style index output */
@@ -347,6 +353,9 @@ char *Cplusplus_suffixes [] =
 char *Cstar_suffixes [] =
   { "cs", "hs", NULL };
 
+char *Erlang_suffixes [] =
+  { "erl", "hrl", NULL };
+
 char *Fortran_suffixes [] =
   { "F", "f", "f90", "for", NULL };
 
@@ -398,6 +407,7 @@ struct lang_entry lang_names [] =
   { "c",       default_C_entries,   default_C_suffixes,          NULL              },
   { "c++",     Cplusplus_entries,   Cplusplus_suffixes,          NULL              },
   { "c*",      Cstar_entries,      Cstar_suffixes,       NULL              },
+  { "erlang",  Erlang_functions,    Erlang_suffixes,     NULL              },
   { "fortran", Fortran_functions,   Fortran_suffixes,    NULL              },
   { "lisp",    Lisp_functions,     Lisp_suffixes,        NULL              },
   { "pascal",  Pascal_functions,    Pascal_suffixes,     NULL              },
@@ -443,7 +453,9 @@ Fortran is tried first; if no tags are found, C is tried next.");
 void
 print_version ()
 {
-  printf ("%s for Emacs version %s\n", (CTAGS) ? "ctags" : "etags", VERSION);
+  printf ("%s (GNU Emacs %s)\n", (CTAGS) ? "ctags" : "etags", VERSION);
+  puts ("Copyright (C) 1996 Free Software Foundation, Inc. and Ken Arnold");
+  puts ("This program is distributed under the same terms as Emacs");
 
   exit (GOOD);
 }
@@ -453,7 +465,11 @@ print_help ()
 {
   printf ("These are the options accepted by %s.  You may use unambiguous\n\
 abbreviations for the long option names.  A - as file name means read\n\
-names from stdin.\n\n", progname);
+names from stdin.", progname);
+  if (!CTAGS)
+    printf ("  Absolute names are stored in the output file as they\n\
+are.  Relative ones are stored relative to the output file's directory.");
+  puts ("\n");
 
   puts ("-a, --append\n\
         Append tag entries to existing tags file.");
@@ -468,11 +484,11 @@ names from stdin.\n\n", progname);
 
   if (CTAGS)
     puts ("-d, --defines\n\
-        Create tag entries for constant C #defines, too.");
+        Create tag entries for C #define constants and enum constants, too.");
   else
     puts ("-D, --no-defines\n\
-        Don't create tag entries for constant C #defines.  This makes\n\
-       the tags file smaller.");
+        Don't create tag entries for C #define constants and enum constants.\n\
+       This makes the tags file smaller.");
 
   if (!CTAGS)
     {
@@ -538,6 +554,9 @@ names from stdin.\n\n", progname);
 
   print_language_names ();
 
+  puts ("");
+  puts ("Report bugs to bug-gnu-emacs@prep.ai.mit.edu");
+
   exit (GOOD);
 }
 
@@ -682,7 +701,7 @@ char *massage_name (s)
 #endif /* VMS */
 
 \f
-void
+int
 main (argc, argv)
      int argc;
      char *argv[];
@@ -715,7 +734,7 @@ main (argc, argv)
 
   /*
    * If etags, always find typedefs and structure tags.  Why not?
-   * Also default is to find macro constants.
+   * Also default is to find macro constants and enum constants.
    */
   if (!CTAGS)
     typedefs = typedefs_and_cplusplus = constantypedefs = TRUE;
@@ -843,19 +862,14 @@ main (argc, argv)
     }
 
   if (tagfile == NULL)
-    {
-      tagfile = CTAGS ? "tags" : "TAGS";
-    }
+    tagfile = CTAGS ? "tags" : "TAGS";
   cwd = etags_getcwd ();       /* the current working directory */
-  strcat (cwd, "/");
+  if (cwd[strlen (cwd) - 1] != '/')
+    cwd = concat (cwd, "/", "");
   if (streq (tagfile, "-"))
-    {
-      tagfiledir = cwd;
-    }
+    tagfiledir = cwd;
   else
-    {
-      tagfiledir = absolute_dirname (tagfile, cwd);
-    }
+    tagfiledir = absolute_dirname (tagfile, cwd);
 
   init ();                     /* set up boolean "functions" */
 
@@ -868,7 +882,15 @@ main (argc, argv)
   if (!CTAGS)
     {
       if (streq (tagfile, "-"))
-       tagf = stdout;
+       {
+         tagf = stdout;
+#ifdef DOS_NT
+         /* Switch redirected `stdout' to binary mode (setting `_fmode'
+            doesn't take effect until after `stdout' is already open). */
+         if (!isatty (fileno (stdout)))
+           setmode (fileno (stdout), O_BINARY);
+#endif /* DOS_NT */
+       }
       else
        tagf = fopen (tagfile, append_to_tagfile ? "a" : "w");
       if (tagf == NULL)
@@ -933,9 +955,6 @@ main (argc, argv)
      because we want them ordered.  Let's do it now. */
   if (cxref_style)
     {
-      tagf = fopen (tagfile, append_to_tagfile ? "a" : "w");
-      if (tagf == NULL)
-       pfatal (tagfile);
       put_entries (head);
       exit (GOOD);
     }
@@ -951,7 +970,7 @@ main (argc, argv)
                   "mv %s OTAGS;fgrep -v '\t%s\t' OTAGS >%s;rm OTAGS",
                   tagfile, argbuffer[i].what, tagfile);
          if (system (cmd) != GOOD)
-           fatal ("failed to execute shell command");
+           fatal ("failed to execute shell command", NULL);
        }
       append_to_tagfile = TRUE;
     }
@@ -968,7 +987,7 @@ main (argc, argv)
       sprintf (cmd, "sort %s -o %s", tagfile, tagfile);
       exit (system (cmd));
     }
-  exit (GOOD);
+  return GOOD;
 }
 
 
@@ -1054,6 +1073,13 @@ process_file (file)
 {
   struct stat stat_buf;
   FILE *inf;
+#ifdef DOS_NT
+  char *p;
+
+  for (p = file; *p != '\0'; p++)
+    if (*p == '\\')
+      *p = '/';
+#endif
 
   if (stat (file, &stat_buf) == 0 && !S_ISREG (stat_buf.st_mode))
     {
@@ -1316,7 +1342,7 @@ add_node (node, cur_node_p)
     {
       /* Etags Mode */
       if (last_node == NULL)
-       fatal ("internal error in add_node", 0);
+       fatal ("internal error in add_node", NULL);
       last_node->right = node;
       last_node = node;
     }
@@ -1382,7 +1408,7 @@ put_entries (node)
   else
     {
       if (node->name == NULL)
-       error ("internal error: NULL name in ctags mode.", 0);
+       error ("internal error: NULL name in ctags mode.", NULL);
 
       if (cxref_style)
        {
@@ -1473,11 +1499,11 @@ total_size_of_entries (node)
 enum sym_type
 {
   st_none, st_C_objprot, st_C_objimpl, st_C_objend, st_C_gnumacro,
-  st_C_struct, st_C_enum, st_C_define, st_C_typedef, st_C_typespec,
+  st_C_struct, st_C_enum, st_C_define, st_C_typedef, st_C_typespec
 };
 
 /* Feed stuff between (but not including) %[ and %] lines to:
-      gperf -c -k1,3 -o -p -r -t
+      gperf -c -k 1,3 -o -p -r -t
 %[
 struct C_stab_entry { char *name; int c_ext; enum sym_type type; }
 %%
@@ -1486,12 +1512,14 @@ struct C_stab_entry { char *name; int c_ext; enum sym_type type; }
 @implementation,0,     st_C_objimpl
 @end,          0,      st_C_objend
 class,         C_PLPL, st_C_struct
+namespace,     C_PLPL, st_C_struct
 domain,        C_STAR, st_C_struct
 union,         0,      st_C_struct
 struct,        0,      st_C_struct
 enum,          0,      st_C_enum
 typedef,       0,      st_C_typedef
 define,        0,      st_C_define
+bool,          C_PLPL, st_C_typespec
 long,          0,      st_C_typespec
 short,         0,      st_C_typespec
 int,           0,      st_C_typespec
@@ -1506,6 +1534,9 @@ extern,   0,      st_C_typespec
 static,        0,      st_C_typespec
 const,         0,      st_C_typespec
 volatile,      0,      st_C_typespec
+explicit,      C_PLPL, st_C_typespec
+mutable,       C_PLPL, st_C_typespec
+typename,      C_PLPL, st_C_typespec
 # DEFUN used in emacs, the next three used in glibc (SYSCALL only for mach).
 DEFUN,         0,      st_C_gnumacro
 SYSCALL,       0,      st_C_gnumacro
@@ -1518,94 +1549,107 @@ PSEUDO,                0,      st_C_gnumacro
 %]
 and replace lines between %< and %> with its output. */
 /*%<*/
-/* C code produced by gperf version 1.8.1 (K&R C version) */
-/* Command-line: gperf -c -k1,3 -o -p -r -t  */
+/* C code produced by gperf version 2.1 (K&R C version) */
+/* Command-line: gperf -c -k 1,3 -o -p -r -t  */
 
 
 struct C_stab_entry { char *name; int c_ext; enum sym_type type; };
 
 #define MIN_WORD_LENGTH 3
 #define MAX_WORD_LENGTH 15
-#define MIN_HASH_VALUE 7
-#define MAX_HASH_VALUE 63
+#define MIN_HASH_VALUE 34
+#define MAX_HASH_VALUE 121
 /*
-   29 keywords
-   57 is the maximum key range
+   34 keywords
+   88 is the maximum key range
 */
 
 static int
 hash (str, len)
-     register char  *str;
-     register int  len;
+     register char *str;
+     register unsigned int  len;
 {
   static unsigned char hash_table[] =
     {
-     63, 63, 63, 63, 63, 63, 63, 63, 63, 63,
-     63, 63, 63, 63, 63, 63, 63, 63, 63, 63,
-     63, 63, 63, 63, 63, 63, 63, 63, 63, 63,
-     63, 63, 63, 63, 63, 63, 63, 63, 63, 63,
-     63, 63, 63, 63, 63, 63, 63, 63, 63, 63,
-     63, 63, 63, 63, 63, 63, 63, 63, 63, 63,
-     63, 63, 63, 63, 17, 63, 63, 63,  4, 14,
-      4, 63, 63, 63, 63, 63, 63, 63, 63, 63,
-      8, 63, 63,  0, 23, 63, 63, 63, 63, 63,
-     63, 63, 63, 63, 63, 63, 63, 28, 63, 28,
-     10, 31, 27, 18, 63,  6, 63, 63, 26,  1,
-     11,  2, 29, 63, 29, 16, 26, 13, 15, 63,
-     63, 63, 63, 63, 63, 63, 63, 63,
+     121, 121, 121, 121, 121, 121, 121, 121, 121, 121,
+     121, 121, 121, 121, 121, 121, 121, 121, 121, 121,
+     121, 121, 121, 121, 121, 121, 121, 121, 121, 121,
+     121, 121, 121, 121, 121, 121, 121, 121, 121, 121,
+     121, 121, 121, 121, 121, 121, 121, 121, 121, 121,
+     121, 121, 121, 121, 121, 121, 121, 121, 121, 121,
+     121, 121, 121, 121,  45, 121, 121, 121,  16,  19,
+      61, 121, 121, 121, 121, 121, 121, 121, 121, 121,
+      10, 121, 121,  20,  53, 121, 121, 121, 121, 121,
+     121, 121, 121, 121, 121, 121, 121,  41,  45,  22,
+      60,  47,  37,  28, 121,  55, 121, 121,  20,  14,
+      29,  30,   5, 121,  50,  59,  30,  54,   6, 121,
+     121, 121, 121, 121, 121, 121, 121, 121,
   };
   return len + hash_table[str[2]] + hash_table[str[0]];
 }
 
 struct C_stab_entry *
-in_word_set  (str, len)
+in_word_set (str, len)
      register char *str;
-     register int len;
+     register unsigned int len;
 {
 
   static struct C_stab_entry  wordlist[] =
     {
-      {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",},
+      {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, 
+      {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, 
+      {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, 
+      {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, 
+      {"volatile",     0,      st_C_typespec},
+      {"PSEUDO",               0,      st_C_gnumacro},
+      {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, 
+      {"typedef",      0,      st_C_typedef},
+      {"typename",     C_PLPL, st_C_typespec},
+      {"",}, {"",}, {"",}, 
       {"SYSCALL",      0,      st_C_gnumacro},
-      {"",}, {"",}, {"",}, {"",}, {"",},
-      {"DEFUN",                0,      st_C_gnumacro},
-      {"",}, {"",}, {"",},
-      {"domain",       C_STAR, st_C_struct},
-      {"",}, {"",}, {"",}, {"",}, {"",},
-      {"short",        0,      st_C_typespec},
-      {"union",        0,      st_C_struct},
+      {"",}, {"",}, {"",}, 
+      {"mutable",      C_PLPL, st_C_typespec},
+      {"namespace",    C_PLPL, st_C_struct},
+      {"long",         0,      st_C_typespec},
+      {"",}, {"",}, 
+      {"const",        0,      st_C_typespec},
+      {"",}, {"",}, {"",}, 
+      {"explicit",     C_PLPL, st_C_typespec},
+      {"",}, {"",}, {"",}, {"",}, 
       {"void",         0,      st_C_typespec},
-      {"",}, {"",},
-      {"PSEUDO",               0,      st_C_gnumacro},
-      {"double",       0,      st_C_typespec},
-      {"",}, {"",},
-      {"@end",                 0,      st_C_objend},
-      {"@implementation", 0,   st_C_objimpl},
+      {"",}, 
+      {"char",         0,      st_C_typespec},
+      {"class",        C_PLPL, st_C_struct},
+      {"",}, {"",}, {"",}, 
       {"float",        0,      st_C_typespec},
-      {"int",          0,      st_C_typespec},
-      {"",},
-      {"unsigned",     0,      st_C_typespec},
+      {"",}, 
+      {"@implementation", 0,   st_C_objimpl},
+      {"auto",         0,      st_C_typespec},
+      {"",}, 
+      {"ENTRY",                0,      st_C_gnumacro},
+      {"@end",                 0,      st_C_objend},
+      {"bool",                 C_PLPL, st_C_typespec},
+      {"domain",       C_STAR, st_C_struct},
+      {"",}, 
+      {"DEFUN",                0,      st_C_gnumacro},
+      {"extern",       0,      st_C_typespec},
       {"@interface",   0,      st_C_objprot},
-      {"",},
+      {"",}, {"",}, {"",}, 
+      {"int",          0,      st_C_typespec},
+      {"",}, {"",}, {"",}, {"",}, 
       {"signed",       0,      st_C_typespec},
-      {"long",         0,      st_C_typespec},
-      {"ENTRY",                0,      st_C_gnumacro},
+      {"short",        0,      st_C_typespec},
+      {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, 
       {"define",       0,      st_C_define},
-      {"const",        0,      st_C_typespec},
-      {"",}, {"",}, {"",},
+      {"@protocol",    0,      st_C_objprot},
       {"enum",         0,      st_C_enum},
-      {"volatile",     0,      st_C_typespec},
       {"static",       0,      st_C_typespec},
+      {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, 
+      {"union",        0,      st_C_struct},
       {"struct",       0,      st_C_struct},
-      {"",}, {"",}, {"",},
-      {"@protocol",    0,      st_C_objprot},
-      {"",}, {"",},
-      {"auto",         0,      st_C_typespec},
-      {"",},
-      {"char",         0,      st_C_typespec},
-      {"class",        C_PLPL, st_C_struct},
-      {"typedef",      0,      st_C_typedef},
-      {"extern",       0,      st_C_typespec},
+      {"",}, {"",}, {"",}, {"",}, 
+      {"double",       0,      st_C_typespec},
+      {"unsigned",     0,      st_C_typespec},
     };
 
   if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
@@ -1625,12 +1669,12 @@ in_word_set  (str, len)
 /*%>*/
 
 enum sym_type
-C_symtype(str, len, c_ext)
+C_symtype (str, len, c_ext)
      char *str;
      int len;
      int c_ext;
 {
-  register struct C_stab_entry *se = in_word_set(str, len);
+  register struct C_stab_entry *se = in_word_set (str, len);
 
   if (se == NULL || (se->c_ext && !(c_ext & se->c_ext)))
     return st_none;
@@ -1720,7 +1764,7 @@ enum
   omethodtag,                  /* after method name */
   omethodcolon,                        /* after method colon */
   omethodparm,                 /* after method parameter */
-  oignore,                     /* wait for @end */
+  oignore                      /* wait for @end */
 } objdef;
 
 /*
@@ -1743,10 +1787,10 @@ int methodlen;
  * consider_token ()
  *     checks to see if the current token is at the start of a
  *     function, or corresponds to a typedef, or is a struct/union/enum
- *     tag.
+ *     tag, or #define, or an enum constant.
  *
- *     *IS_FUNC gets TRUE iff the token is a function or macro with args.
- *     C_EXT is which language we are looking at.
+ *     *IS_FUNC gets TRUE iff the token is a function or #define macro
+ *     with args.  C_EXT is which language we are looking at.
  *
  *     In the future we will need some way to adjust where the end of
  *     the token is; for instance, implementing the C++ keyword
@@ -1806,7 +1850,7 @@ consider_token (str, len, c, c_ext, cblev, parlev, is_func)
     case dignorerest:
       return FALSE;
     default:
-      error ("internal error: definedef value.", 0);
+      error ("internal error: definedef value.", NULL);
     }
 
   /*
@@ -1856,11 +1900,6 @@ consider_token (str, len, c, c_ext, cblev, parlev, is_func)
    * This structdef business is NOT invoked when we are ctags and the
    * file is plain C.  This is because a struct tag may have the same
    * name as another tag, and this loses with ctags.
-   *
-   * This if statement deals with the typdef state machine as
-   * follows: if typdef==ttypedseen and token is struct/union/class/enum,
-   * return FALSE.  All the other code here is for the structdef
-   * state machine.
    */
   switch (toktype)
     {
@@ -1874,6 +1913,7 @@ consider_token (str, len, c, c_ext, cblev, parlev, is_func)
        }
       return FALSE;
     }
+
   if (structdef == skeyseen)
     {
       /* Save the tag for struct/union/class, for functions that may be
@@ -1893,7 +1933,20 @@ consider_token (str, len, c, c_ext, cblev, parlev, is_func)
       return FALSE;
     }
 
-  /* Detect GNU macros. */
+  /* Detect GNU macros.
+
+     DEFUN note for writers of emacs C code:
+      The DEFUN macro, used in emacs C source code, has a first arg
+     that is a string (the lisp function name), and a second arg that
+     is a C function name.  Since etags skips strings, the second arg
+     is tagged.  This is unfortunate, as it would be better to tag the
+     first arg.  The simplest way to deal with this problem would be
+     to name the tag with a name built from the function name, by
+     removing the initial 'F' character and substituting '-' for '_'.
+     Anyway, this assumes that the conventions of naming lisp
+     functions will never change.  Currently, this method is not
+     implemented, so writers of emacs code are recommended to put the
+     first two args of a DEFUN on the same line. */
   if (definedef == dnone && toktype == st_C_gnumacro)
     {
       next_token_is_func = TRUE;
@@ -1907,9 +1960,7 @@ consider_token (str, len, c, c_ext, cblev, parlev, is_func)
       return TRUE;
     }
 
-  /*
-   * Detecting Objective C constructs.
-   */
+  /* Detect Objective C constructs. */
   switch (objdef)
     {
     case onone:
@@ -1945,7 +1996,7 @@ consider_token (str, len, c, c_ext, cblev, parlev, is_func)
        {
          objdef = omethodtag;
          methodlen = len;
-         GROW_LINEBUFFER (token_name, methodlen+1);
+         grow_linebuffer (&token_name, methodlen+1);
          strncpy (token_name.buffer, str, len);
          token_name.buffer[methodlen] = '\0';
          return TRUE;
@@ -1960,7 +2011,7 @@ consider_token (str, len, c, c_ext, cblev, parlev, is_func)
        {
          objdef = omethodtag;
          methodlen += len;
-         GROW_LINEBUFFER (token_name, methodlen+1);
+         grow_linebuffer (&token_name, methodlen+1);
          strncat (token_name.buffer, str, len);
          return TRUE;
        }
@@ -1979,14 +2030,16 @@ consider_token (str, len, c, c_ext, cblev, parlev, is_func)
       return FALSE;
     }
 
-  /* A function? */
+  /* A function or enum constant? */
   switch (toktype)
     {
     case st_C_typespec:
       if (funcdef != finlist && funcdef != fignore)
         funcdef = fnone;               /* should be useless */
       return FALSE;
-    default:
+    case st_none:
+      if (constantypedefs && structdef == sinbody && structtype == st_C_enum)
+       return TRUE;
       if (funcdef == fnone)
        {
          funcdef = ftagseen;
@@ -2000,9 +2053,9 @@ consider_token (str, len, c, c_ext, cblev, parlev, is_func)
 
 /*
  * C_entries ()
- *     This routine finds functions, typedefs, #define's and
- *     struct/union/enum definitions in C syntax and adds them
- *     to the list.
+ *     This routine finds functions, typedefs, #define's, enum
+ *     constants and struct/union/enum definitions in C syntax
+ *     and adds them to the list.
  */
 typedef struct
 {
@@ -2029,6 +2082,7 @@ typedef struct
 do {                                                                   \
   curlinepos = charno;                                                 \
   lineno++;                                                            \
+  linecharno = charno;                                                 \
   charno += readline (&curlb, inf);                                    \
   lp = curlb.buffer;                                                   \
   quotednl = FALSE;                                                    \
@@ -2046,16 +2100,16 @@ do {                                                                    \
   definedef = dnone;                                                   \
 } while (0)
 
-/* Ideally this macro should never be called wihen tok.valid is FALSE,
-   but this would mean that the state machines always guess right. */
-#define make_tag(isfun)  do \
+/* This macro should never be called when tok.valid is FALSE, but
+   we must protect about both invalid input and internal errors. */
+#define make_C_tag(isfun)  do \
 if (tok.valid) {                                                       \
   char *name = NULL;                                                   \
   if (CTAGS || tok.named)                                              \
     name = savestr (token_name.buffer);                                        \
   pfnote (name, isfun, tok.buffer, tok.linelen, tok.lineno, tok.linepos); \
   tok.valid = FALSE;                                                   \
-} while (0)
+} /* else if (DEBUG) abort (); */ while (0)
 
 void
 C_entries (c_ext, inf)
@@ -2228,7 +2282,8 @@ C_entries (c_ext, inf)
       /* Consider token only if some complicated conditions are satisfied. */
       if ((definedef != dnone
           || (cblev == 0 && structdef != scolonseen)
-          || (cblev == 1 && cplpl && structdef == sinbody))
+          || (cblev == 1 && cplpl && structdef == sinbody)
+          || (structdef == sinbody && structtype == st_C_enum))
          && typdef != tignore
          && definedef != dignorerest
          && funcdef != finlist)
@@ -2259,7 +2314,7 @@ C_entries (c_ext, inf)
                              && is_func)
                            /* function defined in C++ class body */
                            {
-                             GROW_LINEBUFFER (token_name,
+                             grow_linebuffer (&token_name,
                                               strlen(structtag)+2+toklen+1);
                              strcpy (token_name.buffer, structtag);
                              strcat (token_name.buffer, "::");
@@ -2270,7 +2325,7 @@ C_entries (c_ext, inf)
                          else if (objdef == ocatseen)
                            /* Objective C category */
                            {
-                             GROW_LINEBUFFER (token_name,
+                             grow_linebuffer (&token_name,
                                               strlen(objtag)+2+toklen+1);
                              strcpy (token_name.buffer, objtag);
                              strcat (token_name.buffer, "(");
@@ -2287,7 +2342,7 @@ C_entries (c_ext, inf)
                            }
                          else
                            {
-                             GROW_LINEBUFFER (token_name, toklen+1);
+                             grow_linebuffer (&token_name, toklen+1);
                              strncpy (token_name.buffer,
                                       newlb.buffer+tokoff, toklen);
                              token_name.buffer[toklen] = '\0';
@@ -2315,7 +2370,7 @@ C_entries (c_ext, inf)
                                switch_line_buffers ();
                            }
                          else
-                           make_tag (is_func);
+                           make_C_tag (is_func);
                        }
                      midtoken = FALSE;
                    }
@@ -2337,7 +2392,7 @@ C_entries (c_ext, inf)
                      funcdef = finlist;
                      continue;
                    case flistseen:
-                     make_tag (TRUE);
+                     make_C_tag (TRUE);
                      funcdef = fignore;
                      break;
                    case ftagseen:
@@ -2372,13 +2427,13 @@ C_entries (c_ext, inf)
            {
            case  otagseen:
              objdef = oignore;
-             make_tag (TRUE);
+             make_C_tag (TRUE);
              break;
            case omethodtag:
            case omethodparm:
              objdef = omethodcolon;
              methodlen += 1;
-             GROW_LINEBUFFER (token_name, methodlen+1);
+             grow_linebuffer (&token_name, methodlen+1);
              strcat (token_name.buffer, ":");
              break;
            }
@@ -2390,7 +2445,7 @@ C_entries (c_ext, inf)
              case ftagseen:
                if (yacc_rules)
                  {
-                   make_tag (FALSE);
+                   make_C_tag (FALSE);
                    funcdef = fignore;
                  }
                break;
@@ -2406,7 +2461,7 @@ C_entries (c_ext, inf)
            switch (typdef)
              {
              case tend:
-               make_tag (FALSE);
+               make_C_tag (FALSE);
                /* FALLTHRU */
              default:
                typdef = tnone;
@@ -2429,7 +2484,7 @@ C_entries (c_ext, inf)
            {
            case omethodtag:
            case omethodparm:
-             make_tag (TRUE);
+             make_C_tag (TRUE);
              objdef = oinbody;
              break;
            }
@@ -2444,7 +2499,7 @@ C_entries (c_ext, inf)
          if (cblev == 0 && typdef == tend)
            {
              typdef = tignore;
-             make_tag (FALSE);
+             make_C_tag (FALSE);
              break;
            }
          if (funcdef != finlist && funcdef != fignore)
@@ -2470,7 +2525,7 @@ C_entries (c_ext, inf)
                  if (*lp != '*')
                    {
                      typdef = tignore;
-                     make_tag (FALSE);
+                     make_C_tag (FALSE);
                    }
                  break;
                } /* switch (typdef) */
@@ -2489,7 +2544,7 @@ C_entries (c_ext, inf)
            break;
          if (objdef == ocatseen && parlev == 1)
            {
-             make_tag (TRUE);
+             make_C_tag (TRUE);
              objdef = oignore;
            }
          if (--parlev == 0)
@@ -2504,7 +2559,7 @@ C_entries (c_ext, inf)
              if (cblev == 0 && typdef == tend)
                {
                  typdef = tignore;
-                 make_tag (FALSE);
+                 make_C_tag (FALSE);
                }
            }
          else if (parlev < 0)  /* can happen due to ill-conceived #if's. */
@@ -2518,19 +2573,19 @@ C_entries (c_ext, inf)
          switch (structdef)
            {
            case skeyseen:      /* unnamed struct */
-             structtag = "_anonymous_";
              structdef = sinbody;
+             structtag = "_anonymous_";
              break;
            case stagseen:
            case scolonseen:    /* named struct */
              structdef = sinbody;
-             make_tag (FALSE);
+             make_C_tag (FALSE);
              break;
            }
          switch (funcdef)
            {
            case flistseen:
-             make_tag (TRUE);
+             make_C_tag (TRUE);
              /* FALLTHRU */
            case fignore:
              funcdef = fnone;
@@ -2539,16 +2594,16 @@ C_entries (c_ext, inf)
              switch (objdef)
                {
                case otagseen:
-                 make_tag (TRUE);
+                 make_C_tag (TRUE);
                  objdef = oignore;
                  break;
                case omethodtag:
                case omethodparm:
-                 make_tag (TRUE);
+                 make_C_tag (TRUE);
                  objdef = oinbody;
                  break;
                default:
-                 /* Neutralize `extern "C" {' grot and look inside structs. */
+                 /* Neutralize `extern "C" {' grot. */
                  if (cblev == 0 && structdef == snone && typdef == tnone)
                    cblev = -1;
                }
@@ -2606,7 +2661,7 @@ C_entries (c_ext, inf)
        case '\0':
          if (objdef == otagseen)
            {
-             make_tag (TRUE);
+             make_C_tag (TRUE);
              objdef = oignore;
            }
          /* If a macro spans multiple lines don't reset its state. */
@@ -2896,13 +2951,6 @@ Perl_functions (inf)
 /* Added by Mosur Mohan, 4/22/88 */
 /* Pascal parsing                */
 
-#define GET_NEW_LINE \
-{ \
-  linecharno = charno; lineno++; \
-  charno += 1 + readline (&lb, inf); \
-  dbp = lb.buffer; \
-}
-
 /*
  *  Locates tags for procedures & functions.  Doesn't do any type- or
  *  var-definitions.  It does look for the keyword "extern" or
@@ -2949,7 +2997,10 @@ Pascal_functions (inf)
       c = *dbp++;
       if (c == '\0')           /* if end of line */
        {
-         GET_NEW_LINE;
+         lineno++;
+         linecharno = charno;
+         charno += readline (&lb, inf);
+         dbp = lb.buffer;
          if (*dbp == '\0')
            continue;
          if (!((found_tag && verify_tag) ||
@@ -3041,7 +3092,7 @@ Pascal_functions (inf)
            continue;
 
          /* save all values for later tagging */
-         GROW_LINEBUFFER (tline, strlen (lb.buffer) + 1);
+         grow_linebuffer (&tline, strlen (lb.buffer) + 1);
          strcpy (tline.buffer, lb.buffer);
          save_lineno = lineno;
          save_lcno = linecharno;
@@ -3451,103 +3502,409 @@ TEX_Token (cp)
   return -1;
 }
 \f
-/* Support for Prolog.  */
+/*
+ * Prolog support (rewritten) by Anders Lindgren, Mar. 96
+ *
+ * Assumes that the predicate starts at column 0.
+ * Only the first clause of a predicate is added. 
+ */
+void
+Prolog_functions (inf)
+     FILE *inf;
+{
+  int prolog_pred ();
+  void prolog_skip_comment ();
+
+  char * last;
+  int len;
+  int allocated;
+
+  allocated = 0;
+  len = 0;
+  last = NULL;
+
+  lineno = 0;
+  linecharno = 0;
+  charno = 0;
+
+  while (!feof (inf))
+    {
+      lineno++;
+      linecharno += charno;
+      charno = readline (&lb, inf);
+      dbp = lb.buffer;
+      if (dbp[0] == '\0')      /* Empty line */
+       continue;
+      else if (isspace (dbp[0])) /* Not a predicate */
+       continue;
+      else if (dbp[0] == '/' && dbp[1] == '*') /* comment. */
+       prolog_skip_comment (&lb, inf);
+      else if (len = prolog_pred (dbp, last)) 
+       {
+         /* Predicate.  Store the function name so that we only
+          * generates a tag for the first clause.  */
+         if (last == NULL)
+           last = xnew(len + 1, char);
+         else if (len + 1 > allocated)
+           last = (char *) xrealloc(last, len + 1);
+         allocated = len + 1;
+         strncpy (last, dbp, len);
+         last[len] = '\0';
+       }
+    }
+}
+
 
-/* Whole head (not only functor, but also arguments)
-   is gotten in compound term. */
 void
-prolog_getit (s)
+prolog_skip_comment (plb, inf)
+     struct linebuffer *plb;
+     FILE *inf;
+{
+  char *cp;
+
+  do
+    {
+      for (cp = plb->buffer; *cp != '\0'; cp++)
+       if (cp[0] == '*' && cp[1] == '/')
+         return;
+      lineno++;
+      linecharno += readline (plb, inf);
+    }
+  while (!feof(inf));
+}
+
+/*
+ * A predicate definition is added if it matches:
+ *     <beginning of line><Prolog Atom><whitespace>(
+ *
+ * It is added to the tags database if it doesn't match the
+ * name of the previous clause header.
+ *
+ * Return the size of the name of the predicate, or 0 if no header
+ * was found.
+ */
+int
+prolog_pred (s, last)
      char *s;
+     char *last;               /* Name of last clause. */
 {
-  char *save_s;
-  int insquote, npar;
+  int prolog_atom();
+  int prolog_white();
 
-  save_s = s;
-  insquote = FALSE;
-  npar = 0;
-  while (1)
+  int pos;
+  int len;
+
+  pos = prolog_atom(s, 0);
+  if (pos < 1)
+    return 0;
+
+  len = pos;
+  pos += prolog_white(s, pos);
+
+  if ((s[pos] == '(') || (s[pos] == '.'))
     {
-      if (s[0] == '\0')                /* syntax error. */
-       return;
-      else if (insquote && s[0] == '\'' && s[1] == '\'')
-       s += 2;
-      else if (s[0] == '\'')
+      if (s[pos] == '(')
+       pos++;
+
+      /* Save only the first clause. */
+      if ((last == NULL) ||
+         (len != strlen(last)) ||
+         (strncmp(s, last, len) != 0))
        {
-         insquote = !insquote;
-         s++;
+         pfnote ((CTAGS) ? savenstr (s, len) : NULL, TRUE,
+                 s, pos, lineno, linecharno);
+         return len;
        }
-      else if (!insquote && s[0] == '(')
+    }
+  return 0;
+}
+
+/*
+ * Consume a Prolog atom.
+ * Return the number of bytes consumed, or -1 if there was an error.
+ *
+ * A prolog atom, in this context, could be one of:
+ * - An alphanumeric sequence, starting with a lower case letter.
+ * - A quoted arbitrary string. Single quotes can escape themselves.
+ *   Backslash quotes everything.
+ */
+int
+prolog_atom (s, pos)
+     char *s;
+     int pos;
+{
+  int origpos;
+
+  origpos = pos;
+
+  if (islower(s[pos]) || (s[pos] == '_'))
+    {
+      /* The atom is unquoted. */
+      pos++;
+      while (isalnum(s[pos]) || (s[pos] == '_'))
        {
-         npar++;
-         s++;
+         pos++;
        }
-      else if (!insquote && s[0] == ')')
+      return pos - origpos;
+    }
+  else if (s[pos] == '\'')
+    {
+      pos++;
+
+      while (1) 
        {
-         npar--;
-         s++;
-         if (npar == 0)
-           break;
-         else if (npar < 0)    /* syntax error. */
-           return;
-       }
-      else if (!insquote && s[0] == '.'
-              && (isspace (s[1]) || s[1] == '\0'))
-       {                       /* fullstop. */
-         if (npar != 0)        /* syntax error. */
-           return;
-         s++;
-         break;
+         if (s[pos] == '\'')
+           {
+             pos++;
+             if (s[pos] != '\'')
+               break;
+             pos++;            /* A double quote */
+           }
+         else if (s[pos] == '\0')
+           /* Multiline quoted atoms are ignored. */
+           return -1;
+         else if (s[pos] == '\\')
+           {
+             if (s[pos+1] == '\0')
+               return -1;
+             pos += 2;
+           }
+         else
+           pos++;
        }
-      else
-       s++;
+      return pos - origpos;
     }
-  pfnote ((CTAGS) ? savenstr (save_s, s-save_s) : NULL, TRUE,
-         save_s, s-save_s, lineno, linecharno);
+  else
+    return -1;
 }
 
-/* It is assumed that prolog predicate starts from column 0. */
+/* Consume whitespace.  Return the number of bytes eaten. */
+int
+prolog_white (s, pos)
+     char *s;
+     int pos;
+{
+  int origpos;
+
+  origpos = pos;
+
+  while (isspace(s[pos]))
+    pos++;
+
+  return pos - origpos;
+}
+\f
+/* 
+ * Support for Erlang  --  Anders Lindgren, Feb 1996.
+ *
+ * Generates tags for functions, defines, and records.
+ *
+ * Assumes that Erlang functions start at column 0.
+ */
 void
-Prolog_functions (inf)
+Erlang_functions (inf)
      FILE *inf;
 {
-  void skip_comment (), prolog_getit ();
+  int erlang_func ();
+  void erlang_attribute ();
+
+  char * last;
+  int len;
+  int allocated;
+
+  allocated = 0;
+  len = 0;
+  last = NULL;
+
+  lineno = 0;
+  linecharno = 0;
+  charno = 0;
 
-  lineno = linecharno = charno = 0;
   while (!feof (inf))
     {
       lineno++;
       linecharno += charno;
-      charno = readline (&lb, inf) + 1;        /* 1 for newline. */
+      charno = readline (&lb, inf);
       dbp = lb.buffer;
-      if (isspace (dbp[0]))    /* not predicate header. */
+      if (dbp[0] == '\0')      /* Empty line */
        continue;
-      else if (dbp[0] == '%')  /* comment. */
+      else if (isspace (dbp[0])) /* Not function nor attribute */
        continue;
-      else if (dbp[0] == '/' && dbp[1] == '*') /* comment. */
-       skip_comment (&lb, inf, &lineno, &linecharno);
-      else                     /* found. */
-       prolog_getit (dbp);
+      else if (dbp[0] == '%')  /* comment */
+       continue;
+      else if (dbp[0] == '"')  /* Sometimes, strings start in column one */
+       continue;
+      else if (dbp[0] == '-')  /* attribute, e.g. "-define" */
+       {
+         erlang_attribute(dbp);
+         last = NULL;
+       }
+      else if (len = erlang_func (dbp, last)) 
+       {
+         /* 
+          * Function.  Store the function name so that we only
+          * generates a tag for the first clause.
+          */
+         if (last == NULL)
+           last = xnew(len + 1, char);
+         else if (len + 1 > allocated)
+           last = (char *) xrealloc(last, len + 1);
+         allocated = len + 1;
+         strncpy (last, dbp, len);
+         last[len] = '\0';
+       }
+    }
+}
+
+
+/*
+ * A function definition is added if it matches:
+ *     <beginning of line><Erlang Atom><whitespace>(
+ *
+ * It is added to the tags database if it doesn't match the
+ * name of the previous clause header.
+ *
+ * Return the size of the name of the function, or 0 if no function
+ * was found.
+ */
+int
+erlang_func (s, last)
+     char *s;
+     char *last;               /* Name of last clause. */
+{
+  int erlang_atom ();
+  int erlang_white ();
+
+  int pos;
+  int len;
+
+  pos = erlang_atom(s, 0);
+  if (pos < 1)
+    return 0;
+
+  len = pos;
+  pos += erlang_white(s, pos);
+
+  if (s[pos++] == '(')
+    {
+      /* Save only the first clause. */
+      if ((last == NULL) ||
+         (len != strlen(last)) ||
+         (strncmp(s, last, len) != 0))
+       {
+         pfnote ((CTAGS) ? savenstr (s, len) : NULL, TRUE,
+                 s, pos, lineno, linecharno);
+         return len;
+       }
     }
+  return 0;
 }
 
+
+/*
+ * Handle attributes.  Currently, tags are generated for defines 
+ * and records.
+ *
+ * They are on the form:
+ * -define(foo, bar).
+ * -define(Foo(M, N), M+N).
+ * -record(graph, {vtab = notable, cyclic = true}).
+ */
 void
-skip_comment (plb, inf, plineno, plinecharno)
-     struct linebuffer *plb;
-     FILE *inf;
-     int *plineno;             /* result */
-     long *plinecharno;                /* result */
+erlang_attribute (s)
+     char *s;
 {
-  char *cp;
+  int erlang_atom ();
+  int erlang_white ();
 
-  do
+  int pos;
+  int len;
+
+  if ((strncmp(s, "-define", 7) == 0) ||
+      (strncmp(s, "-record", 7) == 0))
     {
-      for (cp = plb->buffer; *cp != '\0'; cp++)
-       if (cp[0] == '*' && cp[1] == '/')
-         return;
-      (*plineno)++;
-      *plinecharno += readline (plb, inf) + 1; /* 1 for newline. */
+      pos = 7;
+      pos += erlang_white(s, pos);
+
+      if (s[pos++] == '(') 
+       {
+         pos += erlang_white(s, pos);
+       
+         if (len = erlang_atom(s, pos))
+           {
+             pfnote ((CTAGS) ? savenstr (& s[pos], len) : NULL, TRUE,
+                     s, pos + len, lineno, linecharno);
+           }
+       }
     }
-  while (!feof(inf));
+  return;
+}
+
+
+/*
+ * Consume an Erlang atom (or variable).
+ * Return the number of bytes consumed, or -1 if there was an error.
+ */
+int
+erlang_atom (s, pos)
+     char *s;
+     int pos;
+{
+  int origpos;
+
+  origpos = pos;
+
+  if (isalpha (s[pos]) || s[pos] == '_')
+    {
+      /* The atom is unquoted. */
+      pos++;
+      while (isalnum (s[pos]) || s[pos] == '_')
+       pos++;
+      return pos - origpos;
+    }
+  else if (s[pos] == '\'')
+    {
+      pos++;
+
+      while (1) 
+       {
+         if (s[pos] == '\'')
+           {
+             pos++;
+             break;
+           }
+         else if (s[pos] == '\0')
+           /* Multiline quoted atoms are ignored. */
+           return -1;
+         else if (s[pos] == '\\')
+           {
+             if (s[pos+1] == '\0')
+               return -1;
+             pos += 2;
+           }
+         else
+           pos++;
+       }
+      return pos - origpos;
+    }
+  else
+    return -1;
+}
+
+/* Consume whitespace.  Return the number of bytes eaten */
+int
+erlang_white (s, pos)
+     char *s;
+     int pos;
+{
+  int origpos;
+
+  origpos = pos;
+
+  while (isspace (s[pos]))
+    pos++;
+
+  return pos - origpos;
 }
 \f
 #ifdef ETAGS_REGEXPS
@@ -3614,7 +3971,7 @@ add_regex (regexp_pattern)
 
   if (regexp_pattern[0] == '\0')
     {
-      error ("missing regexp", 0);
+      error ("missing regexp", NULL);
       return;
     }
   if (regexp_pattern[strlen(regexp_pattern)-1] != regexp_pattern[0])
@@ -3625,7 +3982,7 @@ add_regex (regexp_pattern)
   name = scan_separators (regexp_pattern);
   if (regexp_pattern[0] == '\0')
     {
-      error ("null regexp", 0);
+      error ("null regexp", NULL);
       return;
     }
   (void) scan_separators (name);
@@ -3754,6 +4111,7 @@ readline_internal (linebuffer, stream)
        }
       if (c == EOF)
        {
+         *p = '\0';
          chars_deleted = 0;
          break;
        }
@@ -3762,7 +4120,16 @@ readline_internal (linebuffer, stream)
          if (p > buffer && p[-1] == '\r')
            {
              *--p = '\0';
+#ifdef DOS_NT
+            /* Assume CRLF->LF translation will be performed by Emacs
+               when loading this file, so CRs won't appear in the buffer.
+               It would be cleaner to compensate within Emacs;
+               however, Emacs does not know how many CRs were deleted
+               before any given point in the file.  */
+             chars_deleted = 1;
+#else
              chars_deleted = 2;
+#endif
            }
          else
            {
@@ -3843,6 +4210,9 @@ void
 just_read_file (inf)
      FILE *inf;
 {
+  lineno = 0;
+  charno = 0;
+
   while (!feof (inf))
     {
       ++lineno;
@@ -3977,20 +4347,7 @@ concat (s1, s2, s3)
 char *
 etags_getcwd ()
 {
-#ifdef DOS_NT
-  char *p, path[MAXPATHLEN + 1]; /* Fixed size is safe on MSDOS.  */
-
-  getwd (path);
-  p = path;
-  while (*p)
-    if (*p == '\\')
-      *p++ = '/';
-    else
-      *p++ = lowcase (*p);
-
-  return strdup (path);
-#else /* not DOS_NT */
-#if HAVE_GETCWD
+#ifdef HAVE_GETCWD
   int bufsize = 200;
   char *path = xnew (bufsize, char);
 
@@ -4002,8 +4359,32 @@ etags_getcwd ()
       path = xnew (bufsize, char);
     }
 
+#if WINDOWSNT
+  {
+    /* Convert backslashes to slashes.  */
+    char *p;
+    for (p = path; *p != '\0'; p++)
+      if (*p == '\\')
+       *p = '/';
+  }
+#endif
+
   return path;
-#else /* not DOS_NT and not HAVE_GETCWD */
+
+#else /* not HAVE_GETCWD */
+#ifdef MSDOS
+  char *p, path[MAXPATHLEN + 1]; /* Fixed size is safe on MSDOS.  */
+
+  getwd (path);
+
+  for (p = path; *p != '\0'; p++)
+    if (*p == '\\')
+      *p = '/';
+    else
+      *p = lowcase (*p);
+
+  return strdup (path);
+#else /* not MSDOS */
   struct linebuffer path;
   FILE *pipe;
 
@@ -4014,8 +4395,8 @@ etags_getcwd ()
   pclose (pipe);
 
   return path.buffer;
+#endif /* not MSDOS */
 #endif /* not HAVE_GETCWD */
-#endif /* not DOS_NT */
 }
 
 /* Return a newly allocated string containing the filename
@@ -4027,17 +4408,15 @@ relative_filename (file, dir)
 {
   char *fp, *dp, *abs, *res;
 
-  /* Find the common root of file and dir. */
+  /* Find the common root of file and dir (with a trailing slash). */
   abs = absolute_filename (file, cwd);
   fp = abs;
   dp = dir;
   while (*fp++ == *dp++)
     continue;
-  do
-    {
-      fp--;
-      dp--;
-    }
+  fp--, dp--;                  /* back to the first differing char */
+  do                           /* look at the equal chars until / */
+    fp--, dp--;
   while (*fp != '/');
 
   /* Build a sequence of "../" strings for the resulting relative filename. */
@@ -4066,6 +4445,12 @@ absolute_filename (file, cwd)
 
   if (absolutefn (file))
     res = concat (file, "", "");
+#ifdef DOS_NT
+  /* We don't support non-absolute filenames with a drive
+     letter, like `d:NAME' (it's too much hassle).  */
+  else if (file[1] == ':')
+    fatal ("%s: relative filenames with drive letters not supported", file);
+#endif
   else
     res = concat (cwd, file, "");
 
@@ -4081,11 +4466,18 @@ absolute_filename (file, cwd)
              cp = slashp;
              do
                cp--;
-             while (cp >= res && *cp != '/');
+             while (cp >= res && !absolutefn (cp));
              if (*cp == '/')
                {
                  strcpy (cp, slashp + 3);
                }
+#ifdef DOS_NT
+             /* Under MSDOS and NT we get `d:/NAME' as absolute
+                filename, so the luser could say `d:/../NAME'.
+                We silently treat this as `d:/NAME'.  */
+             else if (cp[1] == ':')
+               strcpy (cp + 3, slashp + 4);
+#endif
              else              /* else (cp == res) */
                {
                  if (slashp[3] != '\0')
@@ -4118,6 +4510,13 @@ absolute_dirname (file, cwd)
 {
   char *slashp, *res;
   char save;
+#ifdef DOS_NT
+  char *p;
+
+  for (p = file; *p != '\0'; p++)
+    if (*p == '\\')
+      *p = '/';
+#endif
 
   slashp = etags_strrchr (file, '/');
   if (slashp == NULL)
@@ -4130,6 +4529,17 @@ absolute_dirname (file, cwd)
   return res;
 }
 
+/* Increase the size of a linebuffer. */
+void
+grow_linebuffer (bufp, toksize)
+     struct linebuffer *bufp;
+     int toksize;
+{
+  while (bufp->size < toksize)
+    bufp->size *= 2;
+  bufp->buffer = (char *) xrealloc (bufp->buffer, bufp->size);
+}
+
 /* Like malloc but get fatal error if memory is exhausted.  */
 long *
 xmalloc (size)
@@ -4137,7 +4547,7 @@ xmalloc (size)
 {
   long *result = (long *) malloc (size);
   if (result == NULL)
-    fatal ("virtual memory exhausted", 0);
+    fatal ("virtual memory exhausted", NULL);
   return result;
 }
 
@@ -4148,6 +4558,6 @@ xrealloc (ptr, size)
 {
   long *result =  (long *) realloc (ptr, size);
   if (result == NULL)
-    fatal ("virtual memory exhausted");
+    fatal ("virtual memory exhausted", NULL);
   return result;
 }