OSDN Git Service

* reload1.c (move2add_last_cc0): New.
[pf3gnuchains/gcc-fork.git] / gcc / cppfiles.c
index 197a8bc..1ff34ff 100644 (file)
@@ -1,6 +1,6 @@
 /* Part of CPP library.  (include file handling)
    Copyright (C) 1986, 1987, 1989, 1992, 1993, 1994, 1995, 1998,
-   1999, 2000, 2001 Free Software Foundation, Inc.
+   1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
    Written by Per Bothner, 1994.
    Based on CCCP program by Paul Rubin, June 1986
    Adapted to ANSI C, Richard Stallman, Jan 1987
@@ -22,17 +22,50 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 #include "config.h"
 #include "system.h"
+#include <dirent.h>
+#include "coretypes.h"
+#include "tm.h"
 #include "cpplib.h"
 #include "cpphash.h"
 #include "intl.h"
 #include "mkdeps.h"
 #include "splay-tree.h"
+#ifdef ENABLE_VALGRIND_CHECKING
+# ifdef HAVE_MEMCHECK_H
+# include <memcheck.h>
+# else
+# include <valgrind.h>
+# endif
+#else
+/* Avoid #ifdef:s when we can help it.  */
+#define VALGRIND_DISCARD(x)
+#endif
 
 #ifdef HAVE_MMAP_FILE
 # include <sys/mman.h>
 # ifndef MMAP_THRESHOLD
 #  define MMAP_THRESHOLD 3 /* Minimum page count to mmap the file.  */
 # endif
+# if MMAP_THRESHOLD
+#  define TEST_THRESHOLD(size, pagesize) \
+     (size / pagesize >= MMAP_THRESHOLD && (size % pagesize) != 0)
+   /* Use mmap if the file is big enough to be worth it (controlled
+      by MMAP_THRESHOLD) and if we can safely count on there being
+      at least one readable NUL byte after the end of the file's
+      contents.  This is true for all tested operating systems when
+      the file size is not an exact multiple of the page size.  */
+#  ifndef __CYGWIN__
+#   define SHOULD_MMAP(size, pagesize) TEST_THRESHOLD (size, pagesize)
+#  else
+#   define WIN32_LEAN_AND_MEAN
+#   include <windows.h>
+    /* Cygwin can't correctly emulate mmap under Windows 9x style systems so
+       disallow use of mmap on those systems.  Windows 9x does not zero fill
+       memory at EOF and beyond, as required.  */
+#   define SHOULD_MMAP(size, pagesize) ((GetVersion() & 0x80000000) \
+                                       ? 0 : TEST_THRESHOLD (size, pagesize))
+#  endif
+# endif
 
 #else  /* No MMAP_FILE */
 #  undef MMAP_THRESHOLD
@@ -57,51 +90,74 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 #undef strcmp
 
 /* This structure is used for the table of all includes.  */
-struct include_file
-{
+struct include_file {
   const char *name;            /* actual path name of file */
+  const char *header_name;     /* the original header found */
   const cpp_hashnode *cmacro;  /* macro, if any, preventing reinclusion.  */
-  const struct search_path *foundhere;
+  const struct cpp_path *foundhere;
                                /* location in search path where file was
                                   found, for #include_next and sysp.  */
   const unsigned char *buffer; /* pointer to cached file contents */
   struct stat st;              /* copy of stat(2) data for file */
   int fd;                      /* fd open on file (short term storage only) */
+  int err_no;                  /* errno obtained if opening a file failed */
   unsigned short include_count;        /* number of times file has been read */
   unsigned short refcnt;       /* number of stacked buffers using this file */
   unsigned char mapped;                /* file buffer is mmapped */
-  unsigned char defined;       /* cmacro prevents inclusion in this state */
+  unsigned char pch;           /* 0: file not known to be a PCH.
+                                  1: file is a PCH 
+                                     (on return from find_include_file).
+                                  2: file is not and never will be a valid
+                                     precompiled header.
+                                  3: file is always a valid precompiled
+                                     header.  */
 };
 
+/* Variable length record files on VMS will have a stat size that includes
+   record control characters that won't be included in the read size.  */
+#ifdef VMS
+# define FAB_C_VAR 2 /* variable length records (see Starlet fabdef.h) */
+# define STAT_SIZE_TOO_BIG(ST) ((ST).st_fab_rfm == FAB_C_VAR)
+#else
+# define STAT_SIZE_TOO_BIG(ST) 0
+#endif
+
 /* The cmacro works like this: If it's NULL, the file is to be
    included again.  If it's NEVER_REREAD, the file is never to be
    included again.  Otherwise it is a macro hashnode, and the file is
-   to be included again if the macro is defined or not as specified by
-   DEFINED.  */
-#define NEVER_REREAD ((const cpp_hashnode *)-1)
+   to be included again if the macro is defined.  */
+#define NEVER_REREAD ((const cpp_hashnode *) -1)
 #define DO_NOT_REREAD(inc) \
 ((inc)->cmacro && ((inc)->cmacro == NEVER_REREAD \
-                  || ((inc)->cmacro->type == NT_MACRO) == (inc)->defined))
+                  || (inc)->cmacro->type == NT_MACRO))
+#define NO_INCLUDE_PATH ((struct include_file *) -1)
+#define INCLUDE_PCH_P(F) (((F)->pch & 1) != 0)
 
 static struct file_name_map *read_name_map
                                PARAMS ((cpp_reader *, const char *));
 static char *read_filename_string PARAMS ((int, FILE *));
 static char *remap_filename    PARAMS ((cpp_reader *, char *,
-                                        struct search_path *));
-static struct search_path *search_from PARAMS ((cpp_reader *,
-                                               struct include_file *));
-static struct include_file *find_include_file
-                               PARAMS ((cpp_reader *, const char *,
-                                        struct search_path *));
+                                        struct cpp_path *));
+static struct cpp_path *search_from PARAMS ((cpp_reader *,
+                                               enum include_type));
+static struct include_file *
+       find_include_file PARAMS ((cpp_reader *, const cpp_token *,
+                                  enum include_type));
 static struct include_file *open_file PARAMS ((cpp_reader *, const char *));
-static void read_include_file  PARAMS ((cpp_reader *, struct include_file *));
-static void stack_include_file PARAMS ((cpp_reader *, struct include_file *));
+static struct include_file *validate_pch PARAMS ((cpp_reader *,
+                                                 const char *,
+                                                 const char *));
+static struct include_file *open_file_pch PARAMS ((cpp_reader *, 
+                                                  const char *));
+static int read_include_file   PARAMS ((cpp_reader *, struct include_file *));
+static bool stack_include_file PARAMS ((cpp_reader *, struct include_file *));
 static void purge_cache        PARAMS ((struct include_file *));
 static void destroy_node       PARAMS ((splay_tree_value));
 static int report_missing_guard                PARAMS ((splay_tree_node, void *));
 static splay_tree_node find_or_create_entry PARAMS ((cpp_reader *,
                                                     const char *));
 static void handle_missing_header PARAMS ((cpp_reader *, const char *, int));
+static int remove_component_p PARAMS ((const char *));
 
 /* Set up the splay tree we use to store information about all the
    file names seen in this compilation.  We also have entries for each
@@ -109,7 +165,7 @@ static void handle_missing_header PARAMS ((cpp_reader *, const char *, int));
    don't try to open it again in future.
 
    The key of each node is the file name, after processing by
-   _cpp_simplify_pathname.  The path name may or may not be absolute.
+   cpp_simplify_path.  The path name may or may not be absolute.
    The path string has been malloced, as is automatically freed by
    registering free () as the splay tree key deletion function.
 
@@ -138,7 +194,7 @@ static void
 destroy_node (v)
      splay_tree_value v;
 {
-  struct include_file *f = (struct include_file *)v;
+  struct include_file *f = (struct include_file *) v;
 
   if (f)
     {
@@ -155,7 +211,8 @@ _cpp_never_reread (file)
   file->cmacro = NEVER_REREAD;
 }
 
-/* Lookup a simplified filename, and create an entry if none exists.  */
+/* Lookup a filename, which is simplified after making a copy, and
+   create an entry if none exists.  */
 static splay_tree_node
 find_or_create_entry (pfile, fname)
      cpp_reader *pfile;
@@ -163,12 +220,18 @@ find_or_create_entry (pfile, fname)
 {
   splay_tree_node node;
   struct include_file *file;
+  char *name = xstrdup (fname);
 
-  node = splay_tree_lookup (pfile->all_include_files, (splay_tree_key) fname);
-  if (! node)
+  cpp_simplify_path (name);
+  node = splay_tree_lookup (pfile->all_include_files, (splay_tree_key) name);
+  if (node)
+    free (name);
+  else
     {
       file = xcnew (struct include_file);
-      file->name = xstrdup (fname);
+      file->name = name;
+      file->header_name = name;
+      file->err_no = errno;
       node = splay_tree_insert (pfile->all_include_files,
                                (splay_tree_key) file->name,
                                (splay_tree_value) file);
@@ -177,8 +240,7 @@ find_or_create_entry (pfile, fname)
   return node;
 }
 
-/* Enter a simplified file name in the splay tree, for the sake of
-   cpp_included ().  */
+/* Enter a file name in the splay tree, for the sake of cpp_included.  */
 void
 _cpp_fake_include (pfile, fname)
      cpp_reader *pfile;
@@ -196,7 +258,6 @@ _cpp_fake_include (pfile, fname)
 
    Returns an include_file structure with an open file descriptor on
    success, or NULL on failure.  */
-
 static struct include_file *
 open_file (pfile, filename)
      cpp_reader *pfile;
@@ -205,15 +266,18 @@ open_file (pfile, filename)
   splay_tree_node nd = find_or_create_entry (pfile, filename);
   struct include_file *file = (struct include_file *) nd->value;
 
-  /* Don't retry opening if we failed previously.  */
-  if (file->fd == -2)
-    return 0;
+  if (file->err_no)
+    {
+      /* Ugh.  handle_missing_header () needs errno to be set.  */
+      errno = file->err_no;
+      return 0;
+    }
 
-  /* Don't reopen an idempotent file. */
+  /* Don't reopen an idempotent file.  */
   if (DO_NOT_REREAD (file))
     return file;
-      
-  /* Don't reopen one which is already loaded. */
+
+  /* Don't reopen one which is already loaded.  */
   if (file->buffer != NULL)
     return file;
 
@@ -232,82 +296,194 @@ open_file (pfile, filename)
      Special case: the empty string is translated to stdin.  */
 
   if (filename[0] == '\0')
-    file->fd = 0;
+    {
+      file->fd = 0;
+#ifdef __DJGPP__
+      /* For DJGPP redirected input is opened in text mode. Change it
+         to binary mode.  */
+      if (! isatty (file->fd))
+       setmode (file->fd, O_BINARY);
+#endif
+    }
   else
-    file->fd = open (filename, O_RDONLY | O_NOCTTY | O_BINARY, 0666);
+    file->fd = open (file->name, O_RDONLY | O_NOCTTY | O_BINARY, 0666);
 
   if (file->fd != -1 && fstat (file->fd, &file->st) == 0)
     {
-      /* Mark a regular, zero-length file never-reread now.  */
-      if (S_ISREG (file->st.st_mode) && file->st.st_size == 0)
-        {
-         _cpp_never_reread (file);
-         close (file->fd);
-         file->fd = -1;
-       }
+      if (!S_ISDIR (file->st.st_mode))
+       return file;
+
+      /* If it's a directory, we return null and continue the search
+        as the file we're looking for may appear elsewhere in the
+        search path.  */
+      errno = ENOENT;
+      close (file->fd);
+      file->fd = -1;
+    }
+
+  file->err_no = errno;
+  return 0;
+}
 
+static struct include_file *
+validate_pch (pfile, filename, pchname)
+     cpp_reader *pfile;
+     const char *filename;
+     const char *pchname;
+{
+  struct include_file * file;
+  
+  file = open_file (pfile, pchname);
+  if (file == NULL)
+    return NULL;
+  if ((file->pch & 2) == 0)
+    file->pch = pfile->cb.valid_pch (pfile, pchname, file->fd);
+  if (INCLUDE_PCH_P (file))
+    {
+      char *f = xstrdup (filename);
+      cpp_simplify_path (f);
+      file->header_name = f;
       return file;
     }
+  close (file->fd);
+  file->fd = -1;
+  return NULL;
+}
 
-  /* Don't issue an error message if the file doesn't exist.  */
-  if (errno != ENOENT && errno != ENOTDIR)
-    cpp_error_from_errno (pfile, filename);
 
-  /* Create a negative node for this path, and return null.  */
-  file->fd = -2;
+/* Like open_file, but also look for a precompiled header if (a) one exists
+   and (b) it is valid.  */
+static struct include_file *
+open_file_pch (pfile, filename)
+     cpp_reader *pfile;
+     const char *filename;
+{
+  if (filename[0] != '\0'
+      && pfile->cb.valid_pch != NULL)
+    {
+      size_t namelen = strlen (filename);
+      char *pchname = alloca (namelen + 5);
+      struct include_file * file;
+      splay_tree_node nd;
+      
+      memcpy (pchname, filename, namelen);
+      memcpy (pchname + namelen, ".gch", 5);
 
-  return 0;
-}
+      nd = find_or_create_entry (pfile, pchname);
+      file = (struct include_file *) nd->value;
 
-/* Place the file referenced by INC into a new buffer on PFILE's
-   stack.  If there are errors, or the file should not be re-included,
-   a null buffer is pushed.  */
+      if (file != NULL)
+       {
+         if (stat (file->name, &file->st) == 0 && S_ISDIR (file->st.st_mode))
+           {
+             DIR * thedir;
+             struct dirent *d;
+             size_t subname_len = namelen + 64;
+             char *subname = xmalloc (subname_len);
+             
+             thedir = opendir (pchname);
+             if (thedir == NULL)
+               return NULL;
+             memcpy (subname, pchname, namelen + 4);
+             subname[namelen+4] = '/';
+             while ((d = readdir (thedir)) != NULL)
+               {
+                 if (strlen (d->d_name) + namelen + 7 > subname_len)
+                   {
+                     subname_len = strlen (d->d_name) + namelen + 64;
+                     subname = xrealloc (subname, subname_len);
+                   }
+                 strcpy (subname + namelen + 5, d->d_name);
+                 file = validate_pch (pfile, filename, subname);
+                 if (file)
+                   break;
+               }
+             closedir (thedir);
+             free (subname);
+           }
+         else
+           file = validate_pch (pfile, filename, pchname);
+         if (file)
+           return file;
+       }
+    }
+  return open_file (pfile, filename);
+}
 
-static void
+/* Place the file referenced by INC into a new buffer on the buffer
+   stack, unless there are errors, or the file is not re-included
+   because of e.g. multiple-include guards.  Returns true if a buffer
+   is stacked.  */
+static bool
 stack_include_file (pfile, inc)
      cpp_reader *pfile;
      struct include_file *inc;
 {
-  size_t len = 0;
   cpp_buffer *fp;
-  int sysp, deps_sysp;
+  int sysp;
+  const char *filename;
 
-  /* We'll try removing deps_sysp after the release of 3.0.  */
-  deps_sysp = pfile->system_include_depth != 0;
-  sysp = ((pfile->buffer && pfile->buffer->sysp)
-         || (inc->foundhere && inc->foundhere->sysp));
+  if (DO_NOT_REREAD (inc))
+    return false;
 
-  /* For -M, add the file to the dependencies on its first inclusion.  */
-  if (CPP_OPTION (pfile, print_deps) > deps_sysp && !inc->include_count)
-    deps_add_dep (pfile->deps, inc->name);
+  sysp = MAX ((pfile->map ? pfile->map->sysp : 0),
+             (inc->foundhere ? inc->foundhere->sysp : 0));
 
-  /* We don't want multiple include guard advice for the main file.  */
-  if (pfile->buffer)
-    inc->include_count++;
+  /* Add the file to the dependencies on its first inclusion.  */
+  if (CPP_OPTION (pfile, deps.style) > !!sysp && !inc->include_count)
+    {
+      if (pfile->buffer || CPP_OPTION (pfile, deps.ignore_main_file) == 0)
+       deps_add_dep (pfile->deps, inc->name);
+    }
+
+  /* PCH files get dealt with immediately.  */
+  if (INCLUDE_PCH_P (inc))
+    {
+      pfile->cb.read_pch (pfile, inc->name, inc->fd, inc->header_name);
+      close (inc->fd);
+      inc->fd = -1;
+      return false;
+    }
 
   /* Not in cache?  */
   if (! inc->buffer)
-    read_include_file (pfile, inc);
+    {
+      if (read_include_file (pfile, inc))
+       {
+         /* If an error occurs, do not try to read this file again.  */
+         _cpp_never_reread (inc);
+         return false;
+       }
+      /* Mark a regular, zero-length file never-reread.  We read it,
+        NUL-terminate it, and stack it once, so preprocessing a main
+        file of zero length does not raise an error.  */
+      if (S_ISREG (inc->st.st_mode) && inc->st.st_size == 0)
+       _cpp_never_reread (inc);
+      close (inc->fd);
+      inc->fd = -1;
+    }
 
-  if (! DO_NOT_REREAD (inc))
-    len = inc->st.st_size;
+  if (pfile->buffer)
+    /* We don't want MI guard advice for the main file.  */
+    inc->include_count++;
 
   /* Push a buffer.  */
-  fp = cpp_push_buffer (pfile, inc->buffer, len, BUF_FILE, inc->name);
+  fp = cpp_push_buffer (pfile, inc->buffer, inc->st.st_size,
+                       /* from_stage3 */ CPP_OPTION (pfile, preprocessed), 0);
   fp->inc = inc;
   fp->inc->refcnt++;
-  fp->sysp = sysp;
-  fp->search_from = search_from (pfile, inc);
 
-  /* Initialise controlling macro state.  */
-  pfile->mi_state = MI_OUTSIDE;
+  /* Initialize controlling macro state.  */
+  pfile->mi_valid = true;
   pfile->mi_cmacro = 0;
-  pfile->include_depth++;
 
   /* Generate the call back.  */
-  fp->lineno = 0;
-  _cpp_do_file_change (pfile, FC_ENTER, 0, 0);
-  fp->lineno = 1;
+  filename = inc->name;
+  if (*filename == '\0')
+    filename = "<stdin>";
+  _cpp_do_file_change (pfile, LC_ENTER, filename, 1, sysp);
+
+  return true;
 }
 
 /* Read the file referenced by INC into the file cache.
@@ -315,30 +491,26 @@ stack_include_file (pfile, inc)
    If fd points to a plain file, we might be able to mmap it; we can
    definitely allocate the buffer all at once.  If fd is a pipe or
    terminal, we can't do either.  If fd is something weird, like a
-   block device or a directory, we don't want to read it at all.
+   block device, we don't want to read it at all.
 
    Unfortunately, different systems use different st.st_mode values
    for pipes: some have S_ISFIFO, some S_ISSOCK, some are buggy and
    zero the entire struct stat except a couple fields.  Hence we don't
-   even try to figure out what something is, except for plain files,
-   directories, and block devices.
+   even try to figure out what something is, except for plain files
+   and block devices.
 
    FIXME: Flush file cache and try again if we run out of memory.  */
-
-static void
+static int
 read_include_file (pfile, inc)
      cpp_reader *pfile;
      struct include_file *inc;
 {
   ssize_t size, offset, count;
-  U_CHAR *buf;
+  uchar *buf;
 #if MMAP_THRESHOLD
   static int pagesize = -1;
 #endif
 
-  if (DO_NOT_REREAD (inc))
-    return;
-
   if (S_ISREG (inc->st.st_mode))
     {
       /* off_t might have a wider range than ssize_t - in other words,
@@ -351,7 +523,7 @@ read_include_file (pfile, inc)
         does not bite us.  */
       if (inc->st.st_size > INTTYPE_MAXIMUM (ssize_t))
        {
-         cpp_error (pfile, "%s is too large", inc->name);
+         cpp_error (pfile, DL_ERROR, "%s is too large", inc->name);
          goto fail;
        }
       size = inc->st.st_size;
@@ -361,17 +533,22 @@ read_include_file (pfile, inc)
       if (pagesize == -1)
        pagesize = getpagesize ();
 
-      if (size / pagesize >= MMAP_THRESHOLD)
+      if (SHOULD_MMAP (size, pagesize))
        {
-         buf = (U_CHAR *) mmap (0, size, PROT_READ, MAP_PRIVATE, inc->fd, 0);
-         if (buf == (U_CHAR *)-1)
+         buf = (uchar *) mmap (0, size, PROT_READ, MAP_PRIVATE, inc->fd, 0);
+         if (buf == (uchar *) -1)
            goto perror_fail;
+
+         /* We must tell Valgrind that the byte at buf[size] is actually
+            readable.  Discard the handle to avoid handle leak.  */
+         VALGRIND_DISCARD (VALGRIND_MAKE_READABLE (buf + size, 1));
+
          inc->mapped = 1;
        }
       else
 #endif
        {
-         buf = (U_CHAR *) xmalloc (size);
+         buf = (uchar *) xmalloc (size + 1);
          offset = 0;
          while (offset < size)
            {
@@ -380,21 +557,23 @@ read_include_file (pfile, inc)
                goto perror_fail;
              if (count == 0)
                {
-                 cpp_warning (pfile, "%s is shorter than expected", inc->name);
+                 if (!STAT_SIZE_TOO_BIG (inc->st))
+                   cpp_error (pfile, DL_WARNING,
+                              "%s is shorter than expected", inc->name);
+                 size = offset;
+                 buf = xrealloc (buf, size + 1);
+                 inc->st.st_size = size;
                  break;
                }
              offset += count;
            }
+         /* The lexer requires that the buffer be NUL-terminated.  */
+         buf[size] = '\0';
        }
     }
   else if (S_ISBLK (inc->st.st_mode))
     {
-      cpp_error (pfile, "%s is a block device", inc->name);
-      goto fail;
-    }
-  else if (S_ISDIR (inc->st.st_mode))
-    {
-      cpp_error (pfile, "%s is a directory", inc->name);
+      cpp_error (pfile, DL_ERROR, "%s is a block device", inc->name);
       goto fail;
     }
   else
@@ -404,37 +583,38 @@ read_include_file (pfile, inc)
         bigger than the majority of C source files.  */
       size = 8 * 1024;
 
-      buf = (U_CHAR *) xmalloc (size);
+      buf = (uchar *) xmalloc (size + 1);
       offset = 0;
       while ((count = read (inc->fd, buf + offset, size - offset)) > 0)
        {
          offset += count;
          if (offset == size)
-           buf = xrealloc (buf, (size *= 2));
+           {
+             size *= 2;
+             buf = xrealloc (buf, size + 1);
+           }
        }
       if (count < 0)
        goto perror_fail;
 
-      if (offset < size)
-       buf = xrealloc (buf, offset);
+      if (offset + 1 < size)
+       buf = xrealloc (buf, offset + 1);
+
+      /* The lexer requires that the buffer be NUL-terminated.  */
+      buf[offset] = '\0';
       inc->st.st_size = offset;
     }
 
-  close (inc->fd);
   inc->buffer = buf;
-  inc->fd = -1;
-  return;
+  return 0;
 
  perror_fail:
-  cpp_error_from_errno (pfile, inc->name);
+  cpp_errno (pfile, DL_ERROR, inc->name);
  fail:
-  /* Do not try to read this file again.  */
-  close (inc->fd);
-  inc->fd = -1;
-  _cpp_never_reread (inc);
-  return;
+  return 1;
 }
 
+/* Drop INC's buffer from memory, if we are unlikely to need it again.  */
 static void
 purge_cache (inc)
      struct include_file *inc;
@@ -443,7 +623,14 @@ purge_cache (inc)
     {
 #if MMAP_THRESHOLD
       if (inc->mapped)
-       munmap ((PTR) inc->buffer, inc->st.st_size);
+       {
+         /* Undo the previous annotation for the
+            known-zero-byte-after-mmap.  Discard the handle to avoid
+            handle leak.  */
+         VALGRIND_DISCARD (VALGRIND_MAKE_NOACCESS (inc->buffer
+                                                   + inc->st.st_size, 1));
+         munmap ((PTR) inc->buffer, inc->st.st_size);
+       }
       else
 #endif
        free ((PTR) inc->buffer);
@@ -458,8 +645,8 @@ cpp_included (pfile, fname)
      cpp_reader *pfile;
      const char *fname;
 {
-  struct search_path *path;
-  char *name;
+  struct cpp_path *path;
+  char *name, *n;
   splay_tree_node nd;
 
   if (IS_ABSOLUTE_PATHNAME (fname))
@@ -468,54 +655,80 @@ cpp_included (pfile, fname)
       nd = splay_tree_lookup (pfile->all_include_files, (splay_tree_key) fname);
       return (nd && nd->value);
     }
-      
+
   /* Search directory path for the file.  */
   name = (char *) alloca (strlen (fname) + pfile->max_include_len + 2);
-  for (path = CPP_OPTION (pfile, quote_include); path; path = path->next)
+  for (path = pfile->quote_include; path; path = path->next)
     {
       memcpy (name, path->name, path->len);
       name[path->len] = '/';
       strcpy (&name[path->len + 1], fname);
-      _cpp_simplify_pathname (name);
       if (CPP_OPTION (pfile, remap))
-       name = remap_filename (pfile, name, path);
+       n = remap_filename (pfile, name, path);
+      else
+       n = name;
 
-      nd = splay_tree_lookup (pfile->all_include_files, (splay_tree_key) name);
+      nd = splay_tree_lookup (pfile->all_include_files, (splay_tree_key) n);
       if (nd && nd->value)
        return 1;
     }
   return 0;
 }
 
-/* Search for include file FNAME in the include chain starting at
-   SEARCH_START.  Return 0 if there is no such file (or it's un-openable),
-   otherwise an include_file structure.  */
-
+/* Search for HEADER.  Return 0 if there is no such file (or it's
+   un-openable), in which case an error code will be in errno.  If
+   there is no include path to use it returns NO_INCLUDE_PATH,
+   otherwise an include_file structure.  If this request originates
+   from a directive of TYPE #include_next, set INCLUDE_NEXT to true.  */
 static struct include_file *
-find_include_file (pfile, fname, search_start)
+find_include_file (pfile, header, type)
      cpp_reader *pfile;
-     const char *fname;
-     struct search_path *search_start;
+     const cpp_token *header;
+     enum include_type type;
 {
-  struct search_path *path;
-  char *name;
+  const char *fname = (const char *) header->val.str.text;
+  struct cpp_path *path;
   struct include_file *file;
+  char *name, *n;
 
   if (IS_ABSOLUTE_PATHNAME (fname))
-    return open_file (pfile, fname);
-      
+    return open_file_pch (pfile, fname);
+
+  /* For #include_next, skip in the search path past the dir in which
+     the current file was found, but if it was found via an absolute
+     path use the normal search logic.  */
+  if (type == IT_INCLUDE_NEXT && pfile->buffer->inc->foundhere)
+    path = pfile->buffer->inc->foundhere->next;
+  else if (header->type == CPP_HEADER_NAME)
+    path = pfile->bracket_include;
+  else
+    path = search_from (pfile, type);
+
+  if (path == NULL)
+    {
+      cpp_error (pfile, DL_ERROR, "no include path in which to find %s",
+                fname);
+      return NO_INCLUDE_PATH;
+    }
+
   /* Search directory path for the file.  */
   name = (char *) alloca (strlen (fname) + pfile->max_include_len + 2);
-  for (path = search_start; path; path = path->next)
+  for (; path; path = path->next)
     {
-      memcpy (name, path->name, path->len);
-      name[path->len] = '/';
-      strcpy (&name[path->len + 1], fname);
-      _cpp_simplify_pathname (name);
+      int len = path->len;
+      memcpy (name, path->name, len);
+      /* Don't turn / into // or // into ///; // may be a namespace
+        escape.  */
+      if (name[len-1] == '/')
+       len--;
+      name[len] = '/';
+      strcpy (&name[len + 1], fname);
       if (CPP_OPTION (pfile, remap))
-       name = remap_filename (pfile, name, path);
+       n = remap_filename (pfile, name, path);
+      else
+       n = name;
 
-      file = open_file (pfile, name);
+      file = open_file_pch (pfile, n);
       if (file)
        {
          file->foundhere = path;
@@ -539,9 +752,20 @@ cpp_make_system_header (pfile, syshdr, externc)
   /* 1 = system header, 2 = system header to be treated as C.  */
   if (syshdr)
     flags = 1 + (externc != 0);
-  pfile->buffer->sysp = flags;
-  _cpp_do_file_change (pfile, FC_RENAME, pfile->buffer->nominal_fname,
-                      pfile->buffer->lineno);
+  _cpp_do_file_change (pfile, LC_RENAME, pfile->map->to_file,
+                      SOURCE_LINE (pfile->map, pfile->line), flags);
+}
+
+/* Allow the client to change the current file.  Used by the front end
+   to achieve pseudo-file names like <built-in>.
+   If REASON is LC_LEAVE, then NEW_NAME must be NULL.  */
+void
+cpp_change_file (pfile, reason, new_name)
+     cpp_reader *pfile;
+     enum lc_reason reason;
+     const char *new_name;
+{
+  _cpp_do_file_change (pfile, reason, new_name, 1, 0);
 }
 
 /* Report on all files that might benefit from a multiple include guard.
@@ -555,13 +779,14 @@ _cpp_report_missing_guards (pfile)
                      (PTR) &banner);
 }
 
+/* Callback function for splay_tree_foreach().  */
 static int
 report_missing_guard (n, b)
      splay_tree_node n;
      void *b;
 {
   struct include_file *f = (struct include_file *) n->value;
-  int *bannerp = (int *)b;
+  int *bannerp = (int *) b;
 
   if (f && f->cmacro == 0 && f->include_count == 1)
     {
@@ -576,215 +801,133 @@ report_missing_guard (n, b)
   return 0;
 }
 
-/* Create a dependency, or issue an error message as appropriate.   */
+/* Create a dependency for file FNAME, or issue an error message as
+   appropriate.  ANGLE_BRACKETS is nonzero if the file was bracketed
+   like <..>.  */
 static void
 handle_missing_header (pfile, fname, angle_brackets)
      cpp_reader *pfile;
      const char *fname;
      int angle_brackets;
 {
-  /* We will try making the RHS pfile->buffer->sysp after 3.0.  */
-  int print_dep = CPP_PRINT_DEPS(pfile) > (angle_brackets
-                                          || pfile->system_include_depth);
-  if (CPP_OPTION (pfile, print_deps_missing_files) && print_dep)
-    {
-      if (!angle_brackets || IS_ABSOLUTE_PATHNAME (fname))
-       deps_add_dep (pfile->deps, fname);
-      else
-       {
-         /* If requested as a system header, assume it belongs in
-            the first system header directory.  */
-         struct search_path *ptr = CPP_OPTION (pfile, bracket_include);
-         char *p;
-         int len = 0, fname_len = strlen (fname);
-
-         if (ptr)
-           len = ptr->len;
-
-         p = (char *) alloca (len + fname_len + 2);
-         if (len)
-           {
-             memcpy (p, ptr->name, len);
-             p[len++] = '/';
-           }
-         memcpy (p + len, fname, fname_len + 1);
-         _cpp_simplify_pathname (p);
-         deps_add_dep (pfile->deps, p);
-       }
-    }
-  /* If -M was specified, and this header file won't be added to
-     the dependency list, then don't count this as an error,
-     because we can still produce correct output.  Otherwise, we
-     can't produce correct output, because there may be
-     dependencies we need inside the missing file, and we don't
-     know what directory this missing file exists in. */
-  else if (CPP_PRINT_DEPS (pfile) && ! print_dep)
-    cpp_warning (pfile, "No include path in which to find %s", fname);
+  bool print_dep
+    = CPP_OPTION (pfile, deps.style) > (angle_brackets || pfile->map->sysp);
+
+  if (CPP_OPTION (pfile, deps.missing_files) && print_dep)
+    deps_add_dep (pfile->deps, fname);
+  /* If -M was specified, then don't count this as an error, because
+     we can still produce correct output.  Otherwise, we can't produce
+     correct output, because there may be dependencies we need inside
+     the missing file, and we don't know what directory this missing
+     file exists in.  */
   else
-    cpp_error_from_errno (pfile, fname);
+    cpp_errno (pfile, CPP_OPTION (pfile, deps.style) && ! print_dep
+              ? DL_WARNING: DL_ERROR, fname);
 }
 
-void
-_cpp_execute_include (pfile, header, no_reinclude, include_next)
+/* Handles #include-family directives (distinguished by TYPE),
+   including HEADER, and the command line -imacros and -include.
+   Returns true if a buffer was stacked.  */
+bool
+_cpp_execute_include (pfile, header, type)
      cpp_reader *pfile;
      const cpp_token *header;
-     int no_reinclude;
-     int include_next;
+     enum include_type type;
 {
-  struct search_path *search_start = 0;
-  unsigned int angle_brackets = header->type == CPP_HEADER_NAME;
-  const char *fname = (const char *) header->val.str.text;
-  struct include_file *inc;
+  bool stacked = false;
+  struct include_file *inc = find_include_file (pfile, header, type);
 
-  /* Help protect #include or similar from recursion.  */
-  if (pfile->buffer_stack_depth >= CPP_STACK_MAX)
+  if (inc == 0)
+    handle_missing_header (pfile, (const char *) header->val.str.text,
+                          header->type == CPP_HEADER_NAME);
+  else if (inc != NO_INCLUDE_PATH)
     {
-      cpp_fatal (pfile, "#include nested too deeply");
-      return;
-    }
+      stacked = stack_include_file (pfile, inc);
 
-  /* Check we've tidied up #include before entering the buffer.  */
-  if (pfile->context->prev)
-    {
-      cpp_ice (pfile, "attempt to push file buffer with contexts stacked");
-      return;
+      if (type == IT_IMPORT)
+       _cpp_never_reread (inc);
     }
 
-  /* For #include_next, skip in the search path past the dir in which
-     the current file was found.  If this is the last directory in the
-     search path, don't include anything.  If the current file was
-     specified with an absolute path, use the normal search logic.  If
-     this is the primary source file, use the normal search logic and
-     generate a warning.  */
-  if (include_next)
-    {
-      if (! pfile->buffer->prev)
-       cpp_warning (pfile, "#include_next in primary source file");
-      else
-       {
-         if (pfile->buffer->inc->foundhere)
-           {
-             search_start = pfile->buffer->inc->foundhere->next;
-             if (! search_start)
-               return;
-           }
-       }
-    }
-
-  if (!search_start)
-    {
-      if (angle_brackets)
-       search_start = CPP_OPTION (pfile, bracket_include);
-      else
-       search_start = pfile->buffer->search_from;
-
-      if (!search_start)
-       {
-         cpp_error (pfile, "No include path in which to find %s", fname);
-         return;
-       }
-    }
-
-  inc = find_include_file (pfile, fname, search_start);
-  if (inc)
-    {
-      if (angle_brackets)
-       pfile->system_include_depth++;
-
-      stack_include_file (pfile, inc);
-
-      if (! DO_NOT_REREAD (inc))
-       {
-         if (no_reinclude)
-           _cpp_never_reread (inc);
-
-         /* Handle -H option.  */
-         if (CPP_OPTION (pfile, print_include_names))
-           {
-             cpp_buffer *fp = pfile->buffer;
-             while ((fp = fp->prev) != NULL)
-               putc ('.', stderr);
-             fprintf (stderr, " %s\n", inc->name);
-           }
-       }
-    }
-  else
-    handle_missing_header (pfile, fname, angle_brackets);
+  return stacked;
 }
 
-/* Locate file F, and determine whether it is newer than PFILE. Return -1,
-   if F cannot be located or dated, 1, if it is newer and 0 if older.  */
+/* Locate HEADER, and determine whether it is newer than the current
+   file.  If it cannot be located or dated, return -1, if it is newer
+   newer, return 1, otherwise 0.  */
 int
-_cpp_compare_file_date (pfile, f)
+_cpp_compare_file_date (pfile, header)
      cpp_reader *pfile;
-     const cpp_token *f;
+     const cpp_token *header;
 {
-  const char *fname = (const char *) f->val.str.text;
-  struct search_path *search_start;
-  struct include_file *inc;
-
-  if (f->type == CPP_HEADER_NAME)
-    search_start = CPP_OPTION (pfile, bracket_include);
-  else
-    search_start = pfile->buffer->search_from;
+  struct include_file *inc = find_include_file (pfile, header, 0);
 
-  inc = find_include_file (pfile, fname, search_start);
-  
-  if (!inc)
+  if (inc == NULL || inc == NO_INCLUDE_PATH)
     return -1;
+
   if (inc->fd > 0)
     {
       close (inc->fd);
       inc->fd = -1;
     }
-    
-  return inc->st.st_mtime > CPP_BUFFER (pfile)->inc->st.st_mtime;
+
+  return inc->st.st_mtime > pfile->buffer->inc->st.st_mtime;
 }
 
 
-/* Push an input buffer and load it up with the contents of FNAME.
-   If FNAME is "", read standard input.  */
-int
+/* Push an input buffer and load it up with the contents of FNAME.  If
+   FNAME is "", read standard input.  Return true if a buffer was
+   stacked.  */
+bool
 _cpp_read_file (pfile, fname)
      cpp_reader *pfile;
      const char *fname;
 {
+  /* This uses open_file, because we don't allow a PCH to be used as
+     the toplevel compilation (that would prevent re-compiling an
+     existing PCH without deleting it first).  */
   struct include_file *f = open_file (pfile, fname);
 
   if (f == NULL)
     {
-      cpp_error_from_errno (pfile, fname);
-      return 0;
+      cpp_errno (pfile, DL_ERROR, fname);
+      return false;
     }
 
-  stack_include_file (pfile, f);
-  return 1;
+  return stack_include_file (pfile, f);
 }
 
-/* Do appropriate cleanup when a file buffer is popped off the input
-   stack.  */
-void
-_cpp_pop_file_buffer (pfile, buf)
+/* Pushes the given file onto the buffer stack.  Returns nonzero if
+   successful.  */
+bool
+cpp_push_include (pfile, filename)
      cpp_reader *pfile;
-     cpp_buffer *buf;
+     const char *filename;
 {
-  struct include_file *inc = buf->inc;
+  cpp_token header;
 
-  if (pfile->system_include_depth)
-    pfile->system_include_depth--;
-  if (pfile->include_depth)
-    pfile->include_depth--;
+  header.type = CPP_STRING;
+  header.val.str.text = (const unsigned char *) filename;
+  header.val.str.len = strlen (filename);
+  /* Make the command line directive take up a line.  */
+  pfile->line++;
 
-  /* Record the inclusion-preventing macro and its definedness.  */
-  if (pfile->mi_state == MI_OUTSIDE && inc->cmacro != NEVER_REREAD)
-    {
-      /* This could be NULL meaning no controlling macro.  */
-      inc->cmacro = pfile->mi_cmacro;
-      inc->defined = 1;
-    }
+  return _cpp_execute_include (pfile, &header, IT_CMDLINE);
+}
+
+/* Do appropriate cleanup when a file INC's buffer is popped off the
+   input stack.  */
+void
+_cpp_pop_file_buffer (pfile, inc)
+     cpp_reader *pfile;
+     struct include_file *inc;
+{
+  /* Record the inclusion-preventing macro, which could be NULL
+     meaning no controlling macro.  */
+  if (pfile->mi_valid && inc->cmacro == NULL)
+    inc->cmacro = pfile->mi_cmacro;
 
   /* Invalidate control macros in the #including file.  */
-  pfile->mi_state = MI_FAILED;
+  pfile->mi_valid = false;
 
   inc->refcnt--;
   if (inc->refcnt == 0 && DO_NOT_REREAD (inc))
@@ -793,41 +936,54 @@ _cpp_pop_file_buffer (pfile, buf)
 
 /* Returns the first place in the include chain to start searching for
    "" includes.  This involves stripping away the basename of the
-   current file, unless -I- was specified.  */
-static struct search_path *
-search_from (pfile, inc)
+   current file, unless -I- was specified.
+
+   If we're handling -include or -imacros, use the "" chain, but with
+   the preprocessor's cwd prepended.  */
+static struct cpp_path *
+search_from (pfile, type)
      cpp_reader *pfile;
-     struct include_file *inc;
+     enum include_type type;
 {
   cpp_buffer *buffer = pfile->buffer;
   unsigned int dlen;
 
-  /* Ignore the current file's directory if -I- was given.  */
-  if (CPP_OPTION (pfile, ignore_srcdir))
-    return CPP_OPTION (pfile, quote_include);
+  /* Command line uses the cwd, and does not cache the result.  */
+  if (type == IT_CMDLINE)
+    goto use_cwd;
 
-  dlen = lbasename (inc->name) - inc->name;
-  if (dlen)
-    {
-      /* We don't guarantee NAME is null-terminated.  This saves
-        allocating and freeing memory, and duplicating it when faking
-        buffers in cpp_push_buffer.  Drop a trailing '/'.  */
-      buffer->dir.name = inc->name;
-      if (dlen > 1)
-       dlen--;
-    }
-  else
+  /* Ignore the current file's directory?  */
+  if (pfile->quote_ignores_source_dir)
+    return pfile->quote_include;
+
+  if (! buffer->search_cached)
     {
-      buffer->dir.name = ".";
-      dlen = 1;
-    }
+      buffer->search_cached = 1;
 
-  if (dlen > pfile->max_include_len)
-    pfile->max_include_len = dlen;
+      dlen = lbasename (buffer->inc->name) - buffer->inc->name;
 
-  buffer->dir.len = dlen;
-  buffer->dir.next = CPP_OPTION (pfile, quote_include);
-  buffer->dir.sysp = buffer->sysp;
+      if (dlen)
+       {
+         /* We don't guarantee NAME is null-terminated.  This saves
+            allocating and freeing memory.  Drop a trailing '/'.  */
+         buffer->dir.name = (char *) buffer->inc->name;
+         if (dlen > 1)
+           dlen--;
+       }
+      else
+       {
+       use_cwd:
+         buffer->dir.name = (char *) ".";
+         dlen = 1;
+       }
+
+      if (dlen > pfile->max_include_len)
+       pfile->max_include_len = dlen;
+
+      buffer->dir.len = dlen;
+      buffer->dir.next = pfile->quote_include;
+      buffer->dir.sysp = pfile->map->sysp;
+    }
 
   return &buffer->dir;
 }
@@ -839,9 +995,7 @@ search_from (pfile, inc)
    such as DOS.  The format of the file name map file is just a series
    of lines with two tokens on each line.  The first token is the name
    to map, and the second token is the actual name to use.  */
-
-struct file_name_map
-{
+struct file_name_map {
   struct file_name_map *map_next;
   char *map_from;
   char *map_to;
@@ -850,8 +1004,7 @@ struct file_name_map
 #define FILE_NAME_MAP_FILE "header.gcc"
 
 /* Read a space delimited string of unlimited length from a stdio
-   file.  */
-
+   file F.  */
 static char *
 read_filename_string (ch, f)
      int ch;
@@ -862,10 +1015,10 @@ read_filename_string (ch, f)
 
   len = 20;
   set = alloc = xmalloc (len + 1);
-  if (! is_space(ch))
+  if (! is_space (ch))
     {
       *set++ = ch;
-      while ((ch = getc (f)) != EOF && ! is_space(ch))
+      while ((ch = getc (f)) != EOF && ! is_space (ch))
        {
          if (set - alloc == len)
            {
@@ -882,22 +1035,19 @@ read_filename_string (ch, f)
 }
 
 /* This structure holds a linked list of file name maps, one per directory.  */
-
-struct file_name_map_list
-{
+struct file_name_map_list {
   struct file_name_map_list *map_list_next;
   char *map_list_name;
   struct file_name_map *map_list_map;
 };
 
 /* Read the file name map file for DIRNAME.  */
-
 static struct file_name_map *
 read_name_map (pfile, dirname)
      cpp_reader *pfile;
      const char *dirname;
 {
-  register struct file_name_map_list *map_list_ptr;
+  struct file_name_map_list *map_list_ptr;
   char *name;
   FILE *f;
 
@@ -925,17 +1075,16 @@ read_name_map (pfile, dirname)
   if (f)
     {
       int ch;
-      int dirlen = strlen (dirname);
 
       while ((ch = getc (f)) != EOF)
        {
          char *from, *to;
          struct file_name_map *ptr;
 
-         if (is_space(ch))
+         if (is_space (ch))
            continue;
          from = read_filename_string (ch, f);
-         while ((ch = getc (f)) != EOF && is_hspace(ch))
+         while ((ch = getc (f)) != EOF && is_hspace (ch))
            ;
          to = read_filename_string (ch, f);
 
@@ -948,12 +1097,9 @@ read_name_map (pfile, dirname)
            ptr->map_to = to;
          else
            {
-             ptr->map_to = xmalloc (dirlen + strlen (to) + 2);
-             strcpy (ptr->map_to, dirname);
-             ptr->map_to[dirlen] = '/';
-             strcpy (ptr->map_to + dirlen + 1, to);
+             ptr->map_to = concat (dirname, "/", to, NULL);
              free (to);
-           }         
+           }
 
          ptr->map_next = map_list_ptr->map_list_map;
          map_list_ptr->map_list_map = ptr;
@@ -964,41 +1110,41 @@ read_name_map (pfile, dirname)
        }
       fclose (f);
     }
-  
+
   /* Add this information to the cache.  */
   map_list_ptr->map_list_next = CPP_OPTION (pfile, map_list);
   CPP_OPTION (pfile, map_list) = map_list_ptr;
 
   return map_list_ptr->map_list_map;
-}  
-
-/* Remap NAME based on the file_name_map (if any) for LOC. */
+}
 
+/* Remap an unsimplified path NAME based on the file_name_map (if any)
+   for LOC.  */
 static char *
 remap_filename (pfile, name, loc)
      cpp_reader *pfile;
      char *name;
-     struct search_path *loc;
+     struct cpp_path *loc;
 {
   struct file_name_map *map;
   const char *from, *p;
-  char *dir, *dname;
-
-  /* Get a null-terminated path.  */
-  dname = alloca (loc->len + 1);
-  memcpy (dname, loc->name, loc->len);
-  dname[loc->len] = '\0';
+  char *dir;
 
   if (! loc->name_map)
     {
+      /* Get a null-terminated path.  */
+      char *dname = alloca (loc->len + 1);
+      memcpy (dname, loc->name, loc->len);
+      dname[loc->len] = '\0';
+
       loc->name_map = read_name_map (pfile, dname);
       if (! loc->name_map)
        return name;
     }
-  
-  /* FIXME: this doesn't look right - NAME has been simplified.  */
+
+  /* This works since NAME has not been simplified yet.  */
   from = name + loc->len + 1;
-  
+
   for (map = loc->name_map; map; map = map->map_next)
     if (!strcmp (map->map_from, from))
       return map->map_to;
@@ -1013,13 +1159,13 @@ remap_filename (pfile, name, loc)
 
   /* We know p != name as absolute paths don't call remap_filename.  */
   if (p == name)
-    cpp_ice (pfile, "absolute file name in remap_filename");
+    cpp_error (pfile, DL_ICE, "absolute file name in remap_filename");
 
   dir = (char *) alloca (p - name + 1);
   memcpy (dir, name, p - name);
   dir[p - name] = '\0';
   from = p + 1;
-  
+
   for (map = read_name_map (pfile, dir); map; map = map->map_next)
     if (! strcmp (map->map_from, from))
       return map->map_to;
@@ -1027,6 +1173,59 @@ remap_filename (pfile, name, loc)
   return name;
 }
 
+/* Set the include chain for "" to QUOTE, for <> to BRACKET.  If
+   QUOTE_IGNORES_SOURCE_DIR, then "" includes do not look in the
+   directory of the including file.
+
+   If BRACKET does not lie in the QUOTE chain, it is set to QUOTE.  */
+void
+cpp_set_include_chains (pfile, quote, bracket, quote_ignores_source_dir)
+     cpp_reader *pfile;
+     cpp_path *quote, *bracket;
+     int quote_ignores_source_dir;
+{
+  pfile->quote_include = quote;
+  pfile->bracket_include = quote;
+  pfile->quote_ignores_source_dir = quote_ignores_source_dir;
+  pfile->max_include_len = 0;
+
+  for (; quote; quote = quote->next)
+    {
+      quote->name_map = NULL;
+      quote->len = strlen (quote->name);
+      if (quote->len > pfile->max_include_len)
+       pfile->max_include_len = quote->len;
+      if (quote == bracket)
+       pfile->bracket_include = bracket;
+    }
+}
+
+/* Returns true if it is safe to remove the final component of path,
+   when it is followed by a ".." component.  We use lstat to avoid
+   symlinks if we have it.  If not, we can still catch errors with
+   stat ().  */
+static int
+remove_component_p (path)
+     const char *path;
+{
+  struct stat s;
+  int result;
+
+#ifdef HAVE_LSTAT
+  result = lstat (path, &s);
+#else
+  result = stat (path, &s);
+#endif
+
+  /* There's no guarantee that errno will be unchanged, even on
+     success.  Cygwin's lstat(), for example, will often set errno to
+     ENOSYS.  In case of success, reset errno to zero.  */
+  if (result == 0)
+    errno = 0;
+
+  return result == 0 && S_ISDIR (s.st_mode);
+}
+
 /* Simplify a path name in place, deleting redundant components.  This
    reduces OS overhead and guarantees that equivalent paths compare
    the same (modulo symlinks).
@@ -1038,124 +1237,118 @@ remap_filename (pfile, name, loc)
    /../quux            /quux
    //quux              //quux  (POSIX allows leading // as a namespace escape)
 
-   Guarantees no trailing slashes. All transforms reduce the length
-   of the string.
- */
+   Guarantees no trailing slashes.  All transforms reduce the length
+   of the string.  Returns PATH.  errno is 0 if no error occurred;
  nonzero if an error occurred when using stat () or lstat ().  */
 void
-_cpp_simplify_pathname (path)
-    char *path;
+cpp_simplify_path (path)
+     char *path ATTRIBUTE_UNUSED;
 {
-    char *from, *to;
-    char *base;
-    int absolute = 0;
+#ifndef VMS
+  char *from, *to;
+  char *base, *orig_base;
+  int absolute = 0;
+
+  errno = 0;
+  /* Don't overflow the empty path by putting a '.' in it below.  */
+  if (*path == '\0')
+    return;
 
 #if defined (HAVE_DOS_BASED_FILE_SYSTEM)
-    /* Convert all backslashes to slashes. */
-    for (from = path; *from; from++)
-       if (*from == '\\') *from = '/';
-    
-    /* Skip over leading drive letter if present. */
-    if (ISALPHA (path[0]) && path[1] == ':')
-       from = to = &path[2];
-    else
-       from = to = path;
-#else
+  /* Convert all backslashes to slashes.  */
+  for (from = path; *from; from++)
+    if (*from == '\\') *from = '/';
+
+  /* Skip over leading drive letter if present.  */
+  if (ISALPHA (path[0]) && path[1] == ':')
+    from = to = &path[2];
+  else
     from = to = path;
+#else
+  from = to = path;
 #endif
-    
-    /* Remove redundant initial /s.  */
-    if (*from == '/')
+
+  /* Remove redundant leading /s.  */
+  if (*from == '/')
     {
-       absolute = 1;
-       to++;
-       from++;
-       if (*from == '/')
+      absolute = 1;
+      to++;
+      from++;
+      if (*from == '/')
        {
-           if (*++from == '/')
-               /* 3 or more initial /s are equivalent to 1 /.  */
-               while (*++from == '/');
-           else
-               /* On some hosts // differs from /; Posix allows this.  */
-               to++;
+         if (*++from == '/')
+           /* 3 or more initial /s are equivalent to 1 /.  */
+           while (*++from == '/');
+         else
+           /* On some hosts // differs from /; Posix allows this.  */
+           to++;
        }
     }
-    base = to;
-    
-    for (;;)
+
+  base = orig_base = to;
+  for (;;)
     {
-       while (*from == '/')
-           from++;
-
-       if (from[0] == '.' && from[1] == '/')
-           from += 2;
-       else if (from[0] == '.' && from[1] == '\0')
-           goto done;
-       else if (from[0] == '.' && from[1] == '.' && from[2] == '/')
+      int move_base = 0;
+
+      while (*from == '/')
+       from++;
+
+      if (*from == '\0')
+       break;
+
+      if (*from == '.')
        {
-           if (base == to)
+         if (from[1] == '\0')
+           break;
+         if (from[1] == '/')
            {
-               if (absolute)
-                   from += 3;
-               else
-               {
-                   *to++ = *from++;
-                   *to++ = *from++;
-                   *to++ = *from++;
-                   base = to;
-               }
-           }
-           else
-           {
-               to -= 2;
-               while (to > base && *to != '/') to--;
-               if (*to == '/')
-                   to++;
-               from += 3;
+             from += 2;
+             continue;
            }
-       }
-       else if (from[0] == '.' && from[1] == '.' && from[2] == '\0')
-       {
-           if (base == to)
+         else if (from[1] == '.' && (from[2] == '/' || from[2] == '\0'))
            {
-               if (!absolute)
+             /* Don't simplify if there was no previous component.  */
+             if (absolute && orig_base == to)
                {
-                   *to++ = *from++;
-                   *to++ = *from++;
+                 from += 2;
+                 continue;
                }
-           }
-           else
-           {
-               to -= 2;
-               while (to > base && *to != '/') to--;
-               if (*to == '/')
-                   to++;
-           }
-           goto done;
-       }
-       else
-           /* Copy this component and trailing /, if any.  */
-           while ((*to++ = *from++) != '/')
-           {
-               if (!to[-1])
+             /* Don't simplify if the previous component was "../",
+                or if an error has already occurred with (l)stat.  */
+             if (base != to && errno == 0)
                {
-                   to--;
-                   goto done;
+                 /* We don't back up if it's a symlink.  */
+                 *to = '\0';
+                 if (remove_component_p (path))
+                   {
+                     while (to > base && *to != '/')
+                       to--;
+                     from += 2;
+                     continue;
+                   }
                }
+             move_base = 1;
            }
-       
+       }
+
+      /* Add the component separator.  */
+      if (to > orig_base)
+       *to++ = '/';
+
+      /* Copy this component until the trailing null or '/'.  */
+      while (*from != '\0' && *from != '/')
+       *to++ = *from++;
+
+      if (move_base)
+       base = to;
     }
-    
- done:
-    /* Trim trailing slash */
-    if (to[0] == '/' && (!absolute || to > path+1))
-       to--;
-
-    /* Change the empty string to "." so that stat() on the result
-       will always work. */
-    if (to == path)
-      *to++ = '.';
-    
-    *to = '\0';
 
-    return;
+  /* Change the empty string to "." so that it is not treated as stdin.
+     Null terminate.  */
+  if (to == path)
+    *to++ = '.';
+  *to = '\0';
+#else  /* VMS */
+  errno = 0;
+#endif /* !VMS  */
 }