Tue Apr 15 16:09:15 1997 Francesco Potorti` <F.Potorti@cnuce.cnr.it>
[bpt/emacs.git] / lib-src / etags.c
index 35e396c..8b21906 100644 (file)
@@ -31,7 +31,7 @@ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
  *     Francesco Potorti` (F.Potorti@cnuce.cnr.it) is the current maintainer.
  */
 
-char pot_etags_version[] = "@(#) pot revision number is 11.66";
+char pot_etags_version[] = "@(#) pot revision number is $Revision: 11.84 $";
 
 #define        TRUE    1
 #define        FALSE   0
@@ -54,6 +54,11 @@ char pot_etags_version[] = "@(#) pot revision number is 11.66";
 # define MAXPATHLEN _MAX_PATH
 #endif
 
+#if !defined (MSDOS) && !defined (WINDOWSNT) && defined (STDC_HEADERS)
+#include <stdlib.h>
+#include <string.h>
+#endif
+
 #ifdef HAVE_CONFIG_H
 # include <config.h>
   /* On some systems, Emacs defines static as nothing for the sake
@@ -81,7 +86,7 @@ extern int errno;
 #endif /* ETAGS_REGEXPS */
 
 /* Define CTAGS to make the program "ctags" compatible with the usual one.
- Let it undefined to make the program "etags", which makes emacs-style
+ Leave it undefined to make the program "etags", which makes emacs-style
  tag tables and tags typedefs, #defines and struct/union/enum by default. */
 #ifdef CTAGS
 # undef  CTAGS
@@ -104,10 +109,12 @@ extern int errno;
 #define C_STAR 0x00003         /* C* */
 #define YACC   0x10000         /* yacc file */
 
-#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 streq(s,t)     ((DEBUG && (s) == NULL && (t) == NULL   \
+                         && (abort (), 1)) || !strcmp (s, t))
+#define strneq(s,t,n)  ((DEBUG && (s) == NULL && (t) == NULL   \
+                         && (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    */
@@ -127,7 +134,13 @@ extern int errno;
  *
  * SYNOPSIS:   Type *xnew (int n, Type);
  */
-#define xnew(n,Type)   ((Type *) xmalloc ((n) * sizeof (Type)))
+#ifdef chkmalloc
+# include "chkmalloc.h"
+# define xnew(n,Type)  ((Type *) trace_xmalloc (__FILE__, __LINE__, \
+                                                (n) * sizeof (Type)))
+#else
+# define xnew(n,Type)  ((Type *) xmalloc ((n) * sizeof (Type)))
+#endif
 
 typedef int logical;
 
@@ -150,6 +163,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 ();
@@ -232,9 +246,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;
@@ -268,7 +279,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 */
@@ -450,7 +461,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);
 }
@@ -479,11 +492,11 @@ are.  Relative ones are stored relative to the output file's directory.");
 
   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)
     {
@@ -549,6 +562,9 @@ are.  Relative ones are stored relative to the output file's directory.");
 
   print_language_names ();
 
+  puts ("");
+  puts ("Report bugs to bug-gnu-emacs@prep.ai.mit.edu");
+
   exit (GOOD);
 }
 
@@ -560,7 +576,7 @@ enum argument_type
   at_filename
 };
 
-/* This structure helps us allow mixing of --lang and filenames. */
+/* This structure helps us allow mixing of --lang and file names. */
 typedef struct
 {
   enum argument_type arg_type;
@@ -582,7 +598,7 @@ typedef struct      {
 
 /*
  v1.05 nmm 26-Jun-86 fn_exp - expand specification of list of file names
- returning in each successive call the next filename matching the input
+ returning in each successive call the next file name matching the input
  spec. The function expects that each in_spec passed
  to it will be processed to completion; in particular, up to and
  including the call following that in which the last matching name
@@ -591,7 +607,7 @@ typedef struct      {
  If an error occurs, on return out_spec contains the value
  of in_spec when the error occurred.
 
- With each successive filename returned in out_spec, the
+ With each successive file name returned in out_spec, the
  function's return value is one. When there are no more matching
  names the function returns zero. If on the first call no file
  matches in_spec, or there is any other error, -1 is returned.
@@ -670,7 +686,7 @@ gfnames (arg, p_error)
 system (cmd)
      char *cmd;
 {
-  fprintf (stderr, "system() function not implemented under VMS\n");
+  error ("%s", "system() function not implemented under VMS");
 }
 #endif
 
@@ -699,11 +715,11 @@ main (argc, argv)
      char *argv[];
 {
   int i;
-  unsigned int nincluded_files = 0;
-  char **included_files = xnew (argc, char *);
+  unsigned int nincluded_files;
+  char **included_files;
   char *this_file;
   argument *argbuffer;
-  int current_arg = 0, file_count = 0;
+  int current_arg, file_count;
   struct linebuffer filename_lb;
 #ifdef VMS
   logical got_err;
@@ -714,6 +730,10 @@ main (argc, argv)
 #endif /* DOS_NT */
 
   progname = argv[0];
+  nincluded_files = 0;
+  included_files = xnew (argc, char *);
+  current_arg = 0;
+  file_count = 0;
 
   /* Allocate enough no matter what happens.  Overkill, but each one
      is small. */
@@ -726,7 +746,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;
@@ -747,7 +767,7 @@ main (argc, argv)
          break;
 
        case 1:
-         /* This means that a filename has been seen.  Record it. */
+         /* This means that a file name has been seen.  Record it. */
          argbuffer[current_arg].arg_type = at_filename;
          argbuffer[current_arg].what = optarg;
          ++current_arg;
@@ -771,8 +791,7 @@ main (argc, argv)
        case 'o':
          if (tagfile)
            {
-             fprintf (stderr, "%s: -%c option may only be given once.\n",
-                      progname, opt);
+             error ("-%c option may only be given once.", opt);
              suggest_asking_for_help ();
            }
          tagfile = optarg;
@@ -849,7 +868,7 @@ main (argc, argv)
 
   if (nincluded_files == 0 && file_count == 0)
     {
-      fprintf (stderr, "%s: No input files specified.\n", progname);
+      error ("%s", "No input files specified.");
       suggest_asking_for_help ();
     }
 
@@ -947,9 +966,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);
     }
@@ -965,7 +981,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", (char *)NULL);
        }
       append_to_tagfile = TRUE;
     }
@@ -982,7 +998,7 @@ main (argc, argv)
       sprintf (cmd, "sort %s -o %s", tagfile, tagfile);
       exit (system (cmd));
     }
-  exit (GOOD);
+  return GOOD;
 }
 
 
@@ -1002,8 +1018,7 @@ get_language_from_name (name)
          return lang->function;
       }
 
-  fprintf (stderr, "%s: language \"%s\" not recognized.\n",
-          progname, optarg);
+  error ("language \"%s\" not recognized.", optarg);
   suggest_asking_for_help ();
 
   /* This point should never be reached.  The function should either
@@ -1078,12 +1093,12 @@ process_file (file)
 
   if (stat (file, &stat_buf) == 0 && !S_ISREG (stat_buf.st_mode))
     {
-      fprintf (stderr, "Skipping %s: it is not a regular file.\n", file);
+      error ("Skipping %s: it is not a regular file.", file);
       return;
     }
   if (streq (file, tagfile) && !streq (tagfile, "-"))
     {
-      fprintf (stderr, "Skipping inclusion of %s in self.\n", file);
+      error ("Skipping inclusion of %s in self.", file);
       return;
     }
   inf = fopen (file, "r");
@@ -1101,12 +1116,12 @@ process_file (file)
 
       if (absolutefn (file))
        {
-         /* file is an absolute filename.  Canonicalise it. */
+         /* file is an absolute file name.  Canonicalise it. */
          filename = absolute_filename (file, cwd);
        }
       else
        {
-         /* file is a filename relative to cwd.  Make it relative
+         /* file is a file name relative to cwd.  Make it relative
             to the directory of the tags file. */
          filename = relative_filename (file, tagfiledir);
        }
@@ -1337,7 +1352,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", (char *)NULL);
       last_node->right = node;
       last_node = node;
     }
@@ -1403,7 +1418,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.", (char *)NULL);
 
       if (cxref_style)
        {
@@ -1664,12 +1679,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;
@@ -1782,10 +1797,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
@@ -1845,7 +1860,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.", (char *)NULL);
     }
 
   /*
@@ -1895,11 +1910,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)
     {
@@ -1913,6 +1923,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
@@ -1932,7 +1943,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;
@@ -1946,9 +1970,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:
@@ -1984,7 +2006,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;
@@ -1999,7 +2021,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;
        }
@@ -2018,14 +2040,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;
@@ -2039,9 +2063,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
 {
@@ -2086,16 +2110,28 @@ 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 \
-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)
+
+void
+make_C_tag (isfun, tokp)
+     logical isfun;
+     TOKEN *tokp;
+{
+  char *name = NULL;
+
+  /* This function should never be called when tok.valid is FALSE, but
+     we must protect against invalid input or internal errors. */
+  if (tokp->valid)
+    {
+      if (CTAGS || tokp->named)
+       name = savestr (token_name.buffer);
+      pfnote (name, isfun,
+             tokp->buffer, tokp->linelen, tokp->lineno, tokp->linepos);
+      tokp->valid = FALSE;
+    }
+  else if (DEBUG)
+    abort ();
+}
+
 
 void
 C_entries (c_ext, inf)
@@ -2268,7 +2304,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)
@@ -2299,7 +2336,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, "::");
@@ -2310,7 +2347,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, "(");
@@ -2327,7 +2364,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';
@@ -2355,7 +2392,7 @@ C_entries (c_ext, inf)
                                switch_line_buffers ();
                            }
                          else
-                           make_tag (is_func);
+                           make_C_tag (is_func, &tok);
                        }
                      midtoken = FALSE;
                    }
@@ -2377,7 +2414,7 @@ C_entries (c_ext, inf)
                      funcdef = finlist;
                      continue;
                    case flistseen:
-                     make_tag (TRUE);
+                     make_C_tag (TRUE, &tok);
                      funcdef = fignore;
                      break;
                    case ftagseen:
@@ -2412,13 +2449,13 @@ C_entries (c_ext, inf)
            {
            case  otagseen:
              objdef = oignore;
-             make_tag (TRUE);
+             make_C_tag (TRUE, &tok);
              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;
            }
@@ -2430,7 +2467,7 @@ C_entries (c_ext, inf)
              case ftagseen:
                if (yacc_rules)
                  {
-                   make_tag (FALSE);
+                   make_C_tag (FALSE, &tok);
                    funcdef = fignore;
                  }
                break;
@@ -2446,7 +2483,7 @@ C_entries (c_ext, inf)
            switch (typdef)
              {
              case tend:
-               make_tag (FALSE);
+               make_C_tag (FALSE, &tok);
                /* FALLTHRU */
              default:
                typdef = tnone;
@@ -2469,7 +2506,7 @@ C_entries (c_ext, inf)
            {
            case omethodtag:
            case omethodparm:
-             make_tag (TRUE);
+             make_C_tag (TRUE, &tok);
              objdef = oinbody;
              break;
            }
@@ -2484,7 +2521,7 @@ C_entries (c_ext, inf)
          if (cblev == 0 && typdef == tend)
            {
              typdef = tignore;
-             make_tag (FALSE);
+             make_C_tag (FALSE, &tok);
              break;
            }
          if (funcdef != finlist && funcdef != fignore)
@@ -2507,10 +2544,10 @@ C_entries (c_ext, inf)
                  /* Make sure that the next char is not a '*'.
                     This handles constructs like:
                     typedef void OperatorFun (int fun); */
-                 if (*lp != '*')
+                 if (tok.valid && *lp != '*')
                    {
                      typdef = tignore;
-                     make_tag (FALSE);
+                     make_C_tag (FALSE, &tok);
                    }
                  break;
                } /* switch (typdef) */
@@ -2529,7 +2566,7 @@ C_entries (c_ext, inf)
            break;
          if (objdef == ocatseen && parlev == 1)
            {
-             make_tag (TRUE);
+             make_C_tag (TRUE, &tok);
              objdef = oignore;
            }
          if (--parlev == 0)
@@ -2544,7 +2581,7 @@ C_entries (c_ext, inf)
              if (cblev == 0 && typdef == tend)
                {
                  typdef = tignore;
-                 make_tag (FALSE);
+                 make_C_tag (FALSE, &tok);
                }
            }
          else if (parlev < 0)  /* can happen due to ill-conceived #if's. */
@@ -2558,19 +2595,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, &tok);
              break;
            }
          switch (funcdef)
            {
            case flistseen:
-             make_tag (TRUE);
+             make_C_tag (TRUE, &tok);
              /* FALLTHRU */
            case fignore:
              funcdef = fnone;
@@ -2579,16 +2616,16 @@ C_entries (c_ext, inf)
              switch (objdef)
                {
                case otagseen:
-                 make_tag (TRUE);
+                 make_C_tag (TRUE, &tok);
                  objdef = oignore;
                  break;
                case omethodtag:
                case omethodparm:
-                 make_tag (TRUE);
+                 make_C_tag (TRUE, &tok);
                  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;
                }
@@ -2646,7 +2683,7 @@ C_entries (c_ext, inf)
        case '\0':
          if (objdef == otagseen)
            {
-             make_tag (TRUE);
+             make_C_tag (TRUE, &tok);
              objdef = oignore;
            }
          /* If a macro spans multiple lines don't reset its state. */
@@ -3077,7 +3114,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;
@@ -3349,7 +3386,7 @@ TeX_functions (inf)
          i = TEX_Token (lasthit);
          if (0 <= i)
            {
-             pfnote (NULL, TRUE,
+             pfnote ((char *)NULL, TRUE,
                      lb.buffer, strlen (lb.buffer), lineno, linecharno);
 #if TeX_named_tokens
              TEX_getit (lasthit, TEX_toktab[i].len);
@@ -3523,11 +3560,11 @@ Prolog_functions (inf)
       else if (isspace (dbp[0])) /* Not a predicate */
        continue;
       else if (dbp[0] == '/' && dbp[1] == '*') /* comment. */
-       prolog_skip_comment (&lb, inf, &lineno, &linecharno);
+       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.  */
+            generate a tag for the first clause.  */
          if (last == NULL)
            last = xnew(len + 1, char);
          else if (len + 1 > allocated)
@@ -3956,7 +3993,7 @@ add_regex (regexp_pattern)
 
   if (regexp_pattern[0] == '\0')
     {
-      error ("missing regexp", 0);
+      error ("missing regexp", (char *)NULL);
       return;
     }
   if (regexp_pattern[strlen(regexp_pattern)-1] != regexp_pattern[0])
@@ -3967,7 +4004,7 @@ add_regex (regexp_pattern)
   name = scan_separators (regexp_pattern);
   if (regexp_pattern[0] == '\0')
     {
-      error ("null regexp", 0);
+      error ("null regexp", (char *)NULL);
       return;
     }
   (void) scan_separators (name);
@@ -4006,52 +4043,40 @@ substitute (in, out, regs)
      char *in, *out;
      struct re_registers *regs;
 {
-  char *result = NULL, *t;
-  int size = 0;
+  char *result, *t;
+  int size, i;
+
+  result = NULL;
+  size = strlen (out);
 
   /* Pass 1: figure out how much size to allocate. */
-  for (t = out; *t; ++t)
-    {
-      if (*t == '\\')
-       {
-         ++t;
-         if (!*t)
-           {
-             fprintf (stderr, "%s: pattern substitution ends prematurely\n",
-                      progname);
-             return NULL;
-           }
-         if (isdigit (*t))
-           {
-             int dig = *t - '0';
-             size += regs->end[dig] - regs->start[dig];
-           }
-       }
-    }
+  if (out[strlen (out) - 1] == '\\')
+    fatal ("pattern error in %s", out);
+  for (t = out; *t != '\0'; ++t)
+    if (*t == '\\' && isdigit (*++t))
+      {
+       int dig = *t - '0';
+       size += regs->end[dig] - regs->start[dig] - 2;
+      }
 
   /* Allocate space and do the substitutions. */
   result = xnew (size + 1, char);
-  size = 0;
-  for (; *out; ++out)
+  for (i = 0; *out != '\0'; ++out)
     {
-      if (*out == '\\')
+      if (*out == '\\' && isdigit (*++out))
        {
-         ++out;
-         if (isdigit (*out))
-           {
-             /* Using "dig2" satisfies my debugger.  Bleah. */
-             int dig2 = *out - '0';
-             strncpy (result + size, in + regs->start[dig2],
-                      regs->end[dig2] - regs->start[dig2]);
-             size += regs->end[dig2] - regs->start[dig2];
-           }
-         else
-           result[size++] = *out;
+         /* Using "dig2" satisfies my debugger.  Bleah. */
+         int dig2 = *out - '0';
+         int diglen = regs->end[dig2] - regs->start[dig2];
+         strncpy (result + i, in + regs->start[dig2], diglen);
+         i += diglen;
        }
       else
-       result[size++] = *out;
+       result[i++] = *out;
     }
-  result[size] = '\0';
+  result[i] = '\0';
+  if (DEBUG && i > size)
+    abort ();
 
   return result;
 }
@@ -4106,11 +4131,11 @@ readline_internal (linebuffer, stream)
            {
              *--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.  */
+            /* 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;
@@ -4176,7 +4201,7 @@ readline (linebuffer, stream)
          else
            {
              /* Make an unnamed tag. */
-             pfnote (NULL, TRUE,
+             pfnote ((char *)NULL, TRUE,
                      linebuffer->buffer, match, lineno, linecharno);
            }
          break;
@@ -4332,19 +4357,7 @@ concat (s1, s2, s3)
 char *
 etags_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 */
-#if HAVE_GETCWD
+#ifdef HAVE_GETCWD
   int bufsize = 200;
   char *path = xnew (bufsize, char);
 
@@ -4356,8 +4369,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 MSDOS 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;
 
@@ -4368,11 +4405,11 @@ etags_getcwd ()
   pclose (pipe);
 
   return path.buffer;
-#endif /* not HAVE_GETCWD */
 #endif /* not MSDOS */
+#endif /* not HAVE_GETCWD */
 }
 
-/* Return a newly allocated string containing the filename
+/* Return a newly allocated string containing the file name
    of FILE relative to the absolute directory DIR (which
    should end with a slash). */
 char *
@@ -4380,37 +4417,37 @@ relative_filename (file, dir)
      char *file, *dir;
 {
   char *fp, *dp, *abs, *res;
+  int i;
 
-  /* 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. */
-  for (dp = etags_strchr (dp + 1, '/'), res = "";
-       dp != NULL;
-       dp = etags_strchr (dp + 1, '/'))
-    {
-      res = concat (res, "../", "");
-    }
-
-  /* Add the filename relative to the common root of file and dir. */
-  res = concat (res, fp + 1, "");
+  /* Build a sequence of "../" strings for the resulting relative file name. */
+  i = 0;
+  while ((dp = etags_strchr (dp + 1, '/')) != NULL)
+    i += 1;
+  res = xnew (3*i + strlen (fp + 1) + 1, char);
+  res[0] = '\0';
+  while (i-- > 0)
+    strcat (res, "../");
+
+  /* Add the file name relative to the common root of file and dir. */
+  strcat (res, fp + 1);
   free (abs);
 
   return res;
 }
 
 /* Return a newly allocated string containing the
-   absolute filename of FILE given CWD (which should
+   absolute file name of FILE given CWD (which should
    end with a slash). */
 char *
 absolute_filename (file, cwd)
@@ -4419,12 +4456,12 @@ absolute_filename (file, cwd)
   char *slashp, *cp, *res;
 
   if (absolutefn (file))
-    res = concat (file, "", "");
+    res = savestr (file);
 #ifdef DOS_NT
-  /* We don't support non-absolute filenames with a drive
+  /* We don't support non-absolute file names 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);
+    fatal ("%s: relative file names with drive letters not supported", file);
 #endif
   else
     res = concat (cwd, file, "");
@@ -4442,24 +4479,16 @@ absolute_filename (file, cwd)
              do
                cp--;
              while (cp >= res && !absolutefn (cp));
-             if (*cp == '/')
-               {
-                 strcpy (cp, slashp + 3);
-               }
+             if (cp < res)
+               cp = slashp;    /* the absolute name begins with "/.." */
 #ifdef DOS_NT
              /* Under MSDOS and NT we get `d:/NAME' as absolute
-                filename, so the luser could say `d:/../NAME'.
+                file name, so the luser could say `d:/../NAME'.
                 We silently treat this as `d:/NAME'.  */
-             else if (cp[1] == ':')
-               strcpy (cp + 3, slashp + 4);
+             else if (cp[0] != '/')
+               cp = slashp;
 #endif
-             else              /* else (cp == res) */
-               {
-                 if (slashp[3] != '\0')
-                   strcpy (cp, slashp + 4);
-                 else
-                   return ".";
-               }
+             strcpy (cp, slashp + 3);
              slashp = cp;
              continue;
            }
@@ -4472,12 +4501,15 @@ absolute_filename (file, cwd)
 
       slashp = etags_strchr (slashp + 1, '/');
     }
-
-  return res;
+  
+  if (res[0] == '\0')
+    return savestr ("/");
+  else
+    return res;
 }
 
 /* Return a newly allocated string containing the absolute
-   filename of dir where FILE resides given CWD (which should
+   file name of dir where FILE resides given CWD (which should
    end with a slash). */
 char *
 absolute_dirname (file, cwd)
@@ -4495,7 +4527,7 @@ absolute_dirname (file, cwd)
 
   slashp = etags_strrchr (file, '/');
   if (slashp == NULL)
-    return cwd;
+    return savestr (cwd);
   save = slashp[1];
   slashp[1] = '\0';
   res = absolute_filename (file, cwd);
@@ -4504,6 +4536,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)
@@ -4511,7 +4554,7 @@ xmalloc (size)
 {
   long *result = (long *) malloc (size);
   if (result == NULL)
-    fatal ("virtual memory exhausted", 0);
+    fatal ("virtual memory exhausted", (char *)NULL);
   return result;
 }
 
@@ -4522,6 +4565,6 @@ xrealloc (ptr, size)
 {
   long *result =  (long *) realloc (ptr, size);
   if (result == NULL)
-    fatal ("virtual memory exhausted");
+    fatal ("virtual memory exhausted", (char *)NULL);
   return result;
 }