OSDN Git Service

Add framework support for darwin.
[pf3gnuchains/gcc-fork.git] / gcc / cppfiles.c
index 11fc325..d31f61b 100644 (file)
@@ -1,10 +1,11 @@
-/* Part of CPP library.  (include file handling)
+/* Part of CPP library.  File handling.
    Copyright (C) 1986, 1987, 1989, 1992, 1993, 1994, 1995, 1998,
-   1999, 2000, 2001 Free Software Foundation, Inc.
+   1999, 2000, 2001, 2002, 2003, 2004 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
    Split out of cpplib.c, Zack Weinberg, Oct 1998
+   Reimplemented, Neil Booth, Jul 2003
 
 This program is free software; you can redistribute it and/or modify it
 under the terms of the GNU General Public License as published by the
@@ -26,344 +27,479 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 #include "cpphash.h"
 #include "intl.h"
 #include "mkdeps.h"
-#include "splay-tree.h"
+#include "hashtab.h"
+#include "md5.h"
+#include <dirent.h>
 
-#ifdef HAVE_MMAP_FILE
-# include <sys/mman.h>
-# ifndef MMAP_THRESHOLD
-#  define MMAP_THRESHOLD 3 /* Minimum page count to mmap the file.  */
-# endif
+/* 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_RELIABLE(ST) ((ST).st_fab_rfm != FAB_C_VAR)
+#else
+# define STAT_SIZE_RELIABLE(ST) true
+#endif
 
-#else  /* No MMAP_FILE */
-#  undef MMAP_THRESHOLD
-#  define MMAP_THRESHOLD 0
+#ifdef __DJGPP__
+  /* For DJGPP redirected input is opened in text mode.  */
+#  define set_stdin_to_binary_mode() \
+     if (! isatty (0)) setmode (0, O_BINARY)
+#else
+#  define set_stdin_to_binary_mode() /* Nothing */
 #endif
 
 #ifndef O_BINARY
 # define O_BINARY 0
 #endif
 
-/* If errno is inspected immediately after a system call fails, it will be
-   nonzero, and no error number will ever be zero.  */
-#ifndef ENOENT
-# define ENOENT 0
-#endif
-#ifndef ENOTDIR
-# define ENOTDIR 0
-#endif
+/* This structure represents a file searched for by CPP, whether it
+   exists or not.  An instance may be pointed to by more than one
+   file_hash_entry; at present no reference count is kept.  */
+struct _cpp_file
+{
+  /* Filename as given to #include or command line switch.  */
+  const char *name;
 
-/* Suppress warning about function macros used w/o arguments in traditional
-   C.  It is unlikely that glibc's strcmp macro helps this file at all.  */
-#undef strcmp
+  /* The full path used to find the file.  */
+  const char *path;
 
-/* This structure is used for the table of all includes.  */
-struct include_file
-{
-  const char *name;            /* actual path name of file */
-  const cpp_hashnode *cmacro;  /* macro, if any, preventing reinclusion.  */
-  const struct search_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 */
-};
+  /* The full path of the pch file.  */
+  const char *pchname;
 
-/* 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 file's path with the basename stripped.  NULL if it hasn't
+     been calculated yet.  */
+  const char *dir_name;
 
-/* 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.  */
-#define NEVER_REREAD ((const cpp_hashnode *)-1)
-#define DO_NOT_REREAD(inc) \
-((inc)->cmacro && ((inc)->cmacro == NEVER_REREAD \
-                  || (inc)->cmacro->type == NT_MACRO))
-#define NO_INCLUDE_PATH ((struct include_file *) -1)
-
-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 *,
-                                               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 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
-   file we tried to open but failed; this saves system calls since we
-   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.
-   The path string has been malloced, as is automatically freed by
-   registering free () as the splay tree key deletion function.
-
-   A node's value is a pointer to a struct include_file, and is never
-   NULL.  */
-void
-_cpp_init_includes (pfile)
-     cpp_reader *pfile;
-{
-  pfile->all_include_files
-    = splay_tree_new ((splay_tree_compare_fn) strcmp,
-                     (splay_tree_delete_key_fn) free,
-                     destroy_node);
-}
+  /* Chain through all files.  */
+  struct _cpp_file *next_file;
 
-/* Tear down the splay tree.  */
-void
-_cpp_cleanup_includes (pfile)
-     cpp_reader *pfile;
-{
-  splay_tree_delete (pfile->all_include_files);
-}
+  /* The contents of NAME after calling read_file().  */
+  const uchar *buffer;
 
-/* Free a node.  The path string is automatically freed.  */
-static void
-destroy_node (v)
-     splay_tree_value v;
-{
-  struct include_file *f = (struct include_file *)v;
+  /* The macro, if any, preventing re-inclusion.  */
+  const cpp_hashnode *cmacro;
 
-  if (f)
-    {
-      purge_cache (f);
-      free (f);
-    }
-}
+  /* The directory in the search path where FILE was found.  Used for
+     #include_next and determining whether a header is a system
+     header.  */
+  cpp_dir *dir;
 
-/* Mark a file to not be reread (e.g. #import, read failure).  */
-void
-_cpp_never_reread (file)
-     struct include_file *file;
+  /* As filled in by stat(2) for the file.  */
+  struct stat st;
+
+  /* File descriptor.  Invalid if -1, otherwise open.  */
+  int fd;
+
+  /* Zero if this file was successfully opened and stat()-ed,
+     otherwise errno obtained from failure.  */
+  int err_no;
+
+  /* Number of times the file has been stacked for preprocessing.  */
+  unsigned short stack_count;
+
+  /* If opened with #import or contains #pragma once.  */
+  bool once_only;
+
+  /* If read() failed before.  */
+  bool dont_read;
+
+  /* If this file is the main file.  */
+  bool main_file;
+
+  /* If BUFFER above contains the true contents of the file.  */
+  bool buffer_valid;
+
+  /* File is a PCH (on return from find_include_file).  */
+  bool pch;
+};
+
+/* A singly-linked list for all searches for a given file name, with
+   its head pointed to by a slot in FILE_HASH.  The file name is what
+   appeared between the quotes in a #include directive; it can be
+   determined implicitly from the hash table location or explicitly
+   from FILE->name.
+
+   FILE is a structure containing details about the file that was
+   found with that search, or details of how the search failed.
+
+   START_DIR is the starting location of the search in the include
+   chain.  The current directories for "" includes are also hashed in
+   the hash table and therefore unique.  Files that are looked up
+   without using a search path, such as absolute filenames and file
+   names from the command line share a special starting directory so
+   they don't cause cache hits with normal include-chain lookups.
+
+   If START_DIR is NULL then the entry is for a directory, not a file,
+   and the directory is in DIR.  Since the starting point in a file
+   lookup chain is never NULL, this means that simple pointer
+   comparisons against START_DIR can be made to determine cache hits
+   in file lookups.
+
+   If a cache lookup fails because of e.g. an extra "./" in the path,
+   then nothing will break.  It is just less efficient as CPP will
+   have to do more work re-preprocessing the file, and/or comparing
+   its contents against earlier once-only files.
+*/
+struct file_hash_entry
 {
-  file->cmacro = NEVER_REREAD;
-}
+  struct file_hash_entry *next;
+  cpp_dir *start_dir;
+  union
+  {
+    _cpp_file *file;
+    cpp_dir *dir;
+  } u;
+};
 
-/* Lookup a filename, which is simplified after making a copy, and
-   create an entry if none exists.  errno is nonzero iff a (reported)
-   stat() error occurred during simplification.  */
-static splay_tree_node
-find_or_create_entry (pfile, fname)
-     cpp_reader *pfile;
-     const char *fname;
+static bool open_file (_cpp_file *file);
+static bool pch_open_file (cpp_reader *pfile, _cpp_file *file,
+                          bool *invalid_pch);
+static bool find_file_in_dir (cpp_reader *pfile, _cpp_file *file,
+                             bool *invalid_pch);
+static bool read_file_guts (cpp_reader *pfile, _cpp_file *file);
+static bool read_file (cpp_reader *pfile, _cpp_file *file);
+static bool should_stack_file (cpp_reader *, _cpp_file *file, bool import);
+static struct cpp_dir *search_path_head (cpp_reader *, const char *fname,
+                                int angle_brackets, enum include_type);
+static const char *dir_name_of_file (_cpp_file *file);
+static void open_file_failed (cpp_reader *pfile, _cpp_file *file);
+static struct file_hash_entry *search_cache (struct file_hash_entry *head,
+                                            const cpp_dir *start_dir);
+static _cpp_file *make_cpp_file (cpp_reader *, cpp_dir *, const char *fname);
+static cpp_dir *make_cpp_dir (cpp_reader *, const char *dir_name, int sysp);
+static void allocate_file_hash_entries (cpp_reader *pfile);
+static struct file_hash_entry *new_file_hash_entry (cpp_reader *pfile);
+static int report_missing_guard (void **slot, void *b);
+static hashval_t file_hash_hash (const void *p);
+static int file_hash_eq (const void *p, const void *q);
+static char *read_filename_string (int ch, FILE *f);
+static void read_name_map (cpp_dir *dir);
+static char *remap_filename (cpp_reader *pfile, _cpp_file *file);
+static char *append_file_to_dir (const char *fname, cpp_dir *dir);
+static bool validate_pch (cpp_reader *, _cpp_file *file, const char *pchname);
+static int pchf_adder (void **slot, void *data);
+static int pchf_save_compare (const void *e1, const void *e2);
+static int pchf_compare (const void *d_p, const void *e_p);
+static bool check_file_against_entries (cpp_reader *, _cpp_file *, bool);
+
+/* Given a filename in FILE->PATH, with the empty string interpreted
+   as <stdin>, open it.
+
+   On success FILE contains an open file descriptor and stat
+   information for the file.  On failure the file descriptor is -1 and
+   the appropriate errno is also stored in FILE.  Returns TRUE iff
+   successful.
+
+   We used to open files in nonblocking mode, but that caused more
+   problems than it solved.  Do take care not to acquire a controlling
+   terminal by mistake (this can't happen on sane systems, but
+   paranoia is a virtue).
+
+   Use the three-argument form of open even though we aren't
+   specifying O_CREAT, to defend against broken system headers.
+
+   O_BINARY tells some runtime libraries (notably DJGPP) not to do
+   newline translation; we can handle DOS line breaks just fine
+   ourselves.  */
+static bool
+open_file (_cpp_file *file)
 {
-  splay_tree_node node;
-  struct include_file *file;
-  char *name = xstrdup (fname);
-
-  _cpp_simplify_pathname (name);
-  node = splay_tree_lookup (pfile->all_include_files, (splay_tree_key) name);
-  if (node)
-    free (name);
+  if (file->path[0] == '\0')
+    {
+      file->fd = 0;
+      set_stdin_to_binary_mode ();
+    }
   else
+    file->fd = open (file->path, O_RDONLY | O_NOCTTY | O_BINARY, 0666);
+
+  if (file->fd != -1)
     {
-      file = xcnew (struct include_file);
-      file->name = name;
-      file->err_no = errno;
-      node = splay_tree_insert (pfile->all_include_files,
-                               (splay_tree_key) file->name,
-                               (splay_tree_value) file);
+      if (fstat (file->fd, &file->st) == 0)
+       {
+         if (!S_ISDIR (file->st.st_mode))
+           {
+             file->err_no = 0;
+             return true;
+           }
+
+         /* Ignore a directory and continue the search.  The file we're
+            looking for may be elsewhere in the search path.  */
+         errno = ENOENT;
+       }
+
+      close (file->fd);
+      file->fd = -1;
     }
+  else if (errno == ENOTDIR)
+    errno = ENOENT;
 
-  return node;
-}
+  file->err_no = errno;
 
-/* Enter a file name in the splay tree, for the sake of cpp_included.  */
-void
-_cpp_fake_include (pfile, fname)
-     cpp_reader *pfile;
-     const char *fname;
-{
-  find_or_create_entry (pfile, fname);
+  return false;
 }
 
-/* Given a file name, look it up in the cache; if there is no entry,
-   create one with a non-NULL value (regardless of success in opening
-   the file).  If the file doesn't exist or is inaccessible, this
-   entry is flagged so we don't attempt to open it again in the
-   future.  If the file isn't open, open it.  The empty string is
-   interpreted as stdin.
-
-   Returns an include_file structure with an open file descriptor on
-   success, or NULL on failure.  */
+/* Temporary PCH intercept of opening a file.  Try to find a PCH file
+   based on FILE->name and FILE->dir, and test those found for
+   validity using PFILE->cb.valid_pch.  Return true iff a valid file is
+   found.  Set *INVALID_PCH if a PCH file is found but wasn't valid.  */
 
-static struct include_file *
-open_file (pfile, filename)
-     cpp_reader *pfile;
-     const char *filename;
+static bool
+pch_open_file (cpp_reader *pfile, _cpp_file *file, bool *invalid_pch)
 {
-  splay_tree_node nd = find_or_create_entry (pfile, filename);
-  struct include_file *file = (struct include_file *) nd->value;
+  static const char extension[] = ".gch";
+  const char *path = file->path;
+  size_t len, flen;
+  char *pchname;
+  struct stat st;
+  bool valid = false;
+
+  /* No PCH on <stdin> or if not requested.  */
+  if (file->name[0] == '\0' || !pfile->cb.valid_pch)
+    return false;
 
-  if (file->err_no)
+  flen = strlen (path);
+  len = flen + sizeof (extension);
+  pchname = xmalloc (len);
+  memcpy (pchname, path, flen);
+  memcpy (pchname + flen, extension, sizeof (extension));
+
+  if (stat (pchname, &st) == 0)
     {
-      /* Ugh.  handle_missing_header () needs errno to be set.  */
-      errno = file->err_no;
-      return 0;
-    }
+      DIR *pchdir;
+      struct dirent *d;
+      size_t dlen, plen = len;
 
-  /* Don't reopen an idempotent file.  */
-  if (DO_NOT_REREAD (file))
-    return file;
-      
-  /* Don't reopen one which is already loaded.  */
-  if (file->buffer != NULL)
-    return file;
+      if (!S_ISDIR (st.st_mode))
+       valid = validate_pch (pfile, file, pchname);
+      else if ((pchdir = opendir (pchname)) != NULL)
+       {
+         pchname[plen - 1] = '/';
+         while ((d = readdir (pchdir)) != NULL)
+           {
+             dlen = strlen (d->d_name) + 1;
+             if ((strcmp (d->d_name, ".") == 0)
+                 || (strcmp (d->d_name, "..") == 0))
+               continue;
+             if (dlen + plen > len)
+               {
+                 len += dlen + 64;
+                 pchname = xrealloc (pchname, len);
+               }
+             memcpy (pchname + plen, d->d_name, dlen);
+             valid = validate_pch (pfile, file, pchname);
+             if (valid)
+               break;
+           }
+         closedir (pchdir);
+       }
+      if (valid)
+       file->pch = true;
+      else
+       *invalid_pch = true;
+    }
 
-  /* We used to open files in nonblocking mode, but that caused more
-     problems than it solved.  Do take care not to acquire a
-     controlling terminal by mistake (this can't happen on sane
-     systems, but paranoia is a virtue).
+  if (valid)
+    file->pchname = pchname;
+  else
+    free (pchname);
 
-     Use the three-argument form of open even though we aren't
-     specifying O_CREAT, to defend against broken system headers.
+  return valid;
+}
 
-     O_BINARY tells some runtime libraries (notably DJGPP) not to do
-     newline translation; we can handle DOS line breaks just fine
-     ourselves.
+/* Try to open the path FILE->name appended to FILE->dir.  This is
+   where remap and PCH intercept the file lookup process.  Return true
+   if the file was found, whether or not the open was successful.
+   Set *INVALID_PCH to true if a PCH file is found but wasn't valid.  */
 
-     Special case: the empty string is translated to stdin.  */
+static bool
+find_file_in_dir (cpp_reader *pfile, _cpp_file *file, bool *invalid_pch)
+{
+  char *path;
 
-  if (filename[0] == '\0')
-    file->fd = 0;
+  if (CPP_OPTION (pfile, remap) && (path = remap_filename (pfile, file)))
+    ;
   else
-    file->fd = open (file->name, O_RDONLY | O_NOCTTY | O_BINARY, 0666);
+    if (file->dir->construct)
+      path = file->dir->construct (file->name, file->dir);
+    else
+      path = append_file_to_dir (file->name, file->dir);
 
-  if (file->fd != -1 && fstat (file->fd, &file->st) == 0)
+  if (path)
     {
-      if (!S_ISDIR (file->st.st_mode))
-       return file;
+      file->path = path;
+      if (pch_open_file (pfile, file, invalid_pch))
+       return true;
 
-      /* 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;
+      if (open_file (file))
+       return true;
+
+      if (file->err_no != ENOENT)
+       {
+         open_file_failed (pfile, file);
+         return true;
+       }
+
+      free (path);
+      file->path = file->name;
+    }
+  else
+    {
+      file->err_no = ENOENT; 
+      file->path = NULL;
     }
 
-  file->err_no = errno;
-  return 0;
+  return false;
 }
 
-/* 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.  */
-
+/* Return tue iff the missing_header callback found the given HEADER.  */
 static bool
-stack_include_file (pfile, inc)
-     cpp_reader *pfile;
-     struct include_file *inc;
+search_path_exhausted (cpp_reader *pfile, const char *header, _cpp_file *file)
 {
-  cpp_buffer *fp;
-  int sysp;
-  const char *filename;
+  missing_header_cb func = pfile->cb.missing_header;
 
-  if (DO_NOT_REREAD (inc))
-    return false;
+  /* When the regular search path doesn't work, try context dependent
+     headers search paths.  */
+  if (func
+      && file->dir == NULL)
+    {
+      if ((file->path = func (pfile, header)) != NULL)
+       {
+         if (open_file (file))
+           return true;
+         free ((void *)file->path);
+       }
+      file->path = file->name;
+    }
+
+  return false;
+}
+
+bool
+_cpp_find_failed (_cpp_file *file)
+{
+  return file->err_no != 0;
+}
+
+/* Given a filename FNAME search for such a file in the include path
+   starting from START_DIR.  If FNAME is the empty string it is
+   interpreted as STDIN if START_DIR is PFILE->no_seach_path.
+
+   If the file is not found in the file cache fall back to the O/S and
+   add the result to our cache.
+
+   If the file was not found in the filesystem, or there was an error
+   opening it, then ERR_NO is nonzero and FD is -1.  If the file was
+   found, then ERR_NO is zero and FD could be -1 or an open file
+   descriptor.  FD can be -1 if the file was found in the cache and
+   had previously been closed.  To open it again pass the return value
+   to open_file().
+*/
+_cpp_file *
+_cpp_find_file (cpp_reader *pfile, const char *fname, cpp_dir *start_dir, bool fake)
+{
+  struct file_hash_entry *entry, **hash_slot;
+  _cpp_file *file;
+  bool invalid_pch = false;
+
+  /* Ensure we get no confusion between cached files and directories.  */
+  if (start_dir == NULL)
+    cpp_error (pfile, CPP_DL_ICE, "NULL directory in find_file");
 
-  sysp = MAX ((pfile->map ? pfile->map->sysp : 0),
-             (inc->foundhere ? inc->foundhere->sysp : 0));
+  hash_slot = (struct file_hash_entry **)
+    htab_find_slot_with_hash (pfile->file_hash, fname,
+                             htab_hash_string (fname),
+                             INSERT);
 
-  /* For -M, add the file to the dependencies on its first inclusion.  */
-  if (CPP_OPTION (pfile, print_deps) > sysp && !inc->include_count)
-    deps_add_dep (pfile->deps, inc->name);
+  /* First check the cache before we resort to memory allocation.  */
+  entry = search_cache (*hash_slot, start_dir);
+  if (entry)
+    return entry->u.file;
 
-  /* Not in cache?  */
-  if (! inc->buffer)
+  file = make_cpp_file (pfile, start_dir, fname);
+
+  /* Try each path in the include chain.  */
+  for (; !fake ;)
     {
-      if (read_include_file (pfile, inc))
+      if (find_file_in_dir (pfile, file, &invalid_pch))
+       break;
+
+      file->dir = file->dir->next;
+      if (file->dir == NULL)
        {
-         /* If an error occurs, do not try to read this file again.  */
-         _cpp_never_reread (inc);
-         return false;
+         if (search_path_exhausted (pfile, fname, file))
+           return file;
+
+         open_file_failed (pfile, file);
+         if (invalid_pch)
+           {
+             cpp_error (pfile, CPP_DL_ERROR,
+              "one or more PCH files were found, but they were invalid");
+             if (!cpp_get_options (pfile)->warn_invalid_pch)
+               cpp_error (pfile, CPP_DL_ERROR,
+                          "use -Winvalid-pch for more information");
+           }
+         break;
        }
-      /* 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 (pfile->buffer)
-    /* We don't want MI guard advice for the main file.  */
-    inc->include_count++;
+      /* Only check the cache for the starting location (done above)
+        and the quote and bracket chain heads because there are no
+        other possible starting points for searches.  */
+      if (file->dir != pfile->bracket_include
+         && file->dir != pfile->quote_include)
+       continue;
 
-  /* Push a buffer.  */
-  fp = cpp_push_buffer (pfile, inc->buffer, inc->st.st_size,
-                       /* from_stage3 */ CPP_OPTION (pfile, preprocessed), 0);
-  fp->inc = inc;
-  fp->inc->refcnt++;
+      entry = search_cache (*hash_slot, file->dir);
+      if (entry)
+       break;
+    }
 
-  /* Initialise controlling macro state.  */
-  pfile->mi_valid = true;
-  pfile->mi_cmacro = 0;
+  if (entry)
+    {
+      /* Cache for START_DIR too, sharing the _cpp_file structure.  */
+      free ((char *) file->name);
+      free (file);
+      file = entry->u.file;
+    }
+  else
+    {
+      /* This is a new file; put it in the list.  */
+      file->next_file = pfile->all_files;
+      pfile->all_files = file;
+    }
 
-  /* Generate the call back.  */
-  filename = inc->name;
-  if (*filename == '\0')
-    filename = "<stdin>";
-  _cpp_do_file_change (pfile, LC_ENTER, filename, 1, sysp);
+  /* Store this new result in the hash table.  */
+  entry = new_file_hash_entry (pfile);
+  entry->next = *hash_slot;
+  entry->start_dir = start_dir;
+  entry->u.file = file;
+  *hash_slot = entry;
 
-  return true;
+  return file;
 }
 
-/* Read the file referenced by INC into the file cache.
-
-   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, we don't want to read it at all.
+/* Read a file into FILE->buffer, returning true on success.
 
-   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
-   and block devices.
+   If FILE->fd is something weird, like a block device, we don't want
+   to read it at all.  Don't even try to figure out what something is,
+   except for plain files and block devices, since there is no
+   reliable portable way of doing this.
 
    FIXME: Flush file cache and try again if we run out of memory.  */
-
-static int
-read_include_file (pfile, inc)
-     cpp_reader *pfile;
-     struct include_file *inc;
+static bool
+read_file_guts (cpp_reader *pfile, _cpp_file *file)
 {
-  ssize_t size, offset, count;
-  U_CHAR *buf;
-#if MMAP_THRESHOLD
-  static int pagesize = -1;
-#endif
+  ssize_t size, total, count;
+  uchar *buf;
+  bool regular;
+
+  if (S_ISBLK (file->st.st_mode))
+    {
+      cpp_error (pfile, CPP_DL_ERROR, "%s is a block device", file->path);
+      return false;
+    }
 
-  if (S_ISREG (inc->st.st_mode))
+  regular = S_ISREG (file->st.st_mode);
+  if (regular)
     {
       /* off_t might have a wider range than ssize_t - in other words,
         the max size of a file might be bigger than the address
@@ -373,501 +509,654 @@ read_include_file (pfile, inc)
         SSIZE_MAX to be much smaller than the actual range of the
         type.  Use INTTYPE_MAXIMUM unconditionally to ensure this
         does not bite us.  */
-      if (inc->st.st_size > INTTYPE_MAXIMUM (ssize_t))
-       {
-         cpp_error (pfile, "%s is too large", inc->name);
-         goto fail;
-       }
-      size = inc->st.st_size;
-
-      inc->mapped = 0;
-#if MMAP_THRESHOLD
-      if (pagesize == -1)
-       pagesize = getpagesize ();
-
-      /* 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.  */
-      if (size / pagesize >= MMAP_THRESHOLD
-         && (size % pagesize) != 0)
-       {
-         buf = (U_CHAR *) mmap (0, size, PROT_READ, MAP_PRIVATE, inc->fd, 0);
-         if (buf == (U_CHAR *)-1)
-           goto perror_fail;
-         inc->mapped = 1;
-       }
-      else
-#endif
+      if (file->st.st_size > INTTYPE_MAXIMUM (ssize_t))
        {
-         buf = (U_CHAR *) xmalloc (size + 1);
-         offset = 0;
-         while (offset < size)
-           {
-             count = read (inc->fd, buf + offset, size - offset);
-             if (count < 0)
-               goto perror_fail;
-             if (count == 0)
-               {
-                 if (!STAT_SIZE_TOO_BIG (inc->st))
-                   cpp_warning
-                     (pfile, "%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';
+         cpp_error (pfile, CPP_DL_ERROR, "%s is too large", file->path);
+         return false;
        }
-    }
-  else if (S_ISBLK (inc->st.st_mode))
-    {
-      cpp_error (pfile, "%s is a block device", inc->name);
-      goto fail;
+
+      size = file->st.st_size;
     }
   else
+    /* 8 kilobytes is a sensible starting size.  It ought to be bigger
+       than the kernel pipe buffer, and it's definitely bigger than
+       the majority of C source files.  */
+    size = 8 * 1024;
+
+  buf = xmalloc (size + 1);
+  total = 0;
+  while ((count = read (file->fd, buf + total, size - total)) > 0)
     {
-      /* 8 kilobytes is a sensible starting size.  It ought to be
-        bigger than the kernel pipe buffer, and it's definitely
-        bigger than the majority of C source files.  */
-      size = 8 * 1024;
-
-      buf = (U_CHAR *) xmalloc (size + 1);
-      offset = 0;
-      while ((count = read (inc->fd, buf + offset, size - offset)) > 0)
+      total += count;
+
+      if (total == size)
        {
-         offset += count;
-         if (offset == size)
-           {
-             size *= 2;
-             buf = xrealloc (buf, size + 1);
-           }
+         if (regular)
+           break;
+         size *= 2;
+         buf = xrealloc (buf, size + 1);
        }
-      if (count < 0)
-       goto perror_fail;
-
-      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;
+  if (count < 0)
+    {
+      cpp_errno (pfile, CPP_DL_ERROR, file->path);
+      return false;
     }
 
-  inc->buffer = buf;
-  return 0;
+  if (regular && total != size && STAT_SIZE_RELIABLE (file->st))
+    cpp_error (pfile, CPP_DL_WARNING,
+              "%s is shorter than expected", file->path);
 
- perror_fail:
-  cpp_error_from_errno (pfile, inc->name);
- fail:
-  return 1;
+  file->buffer = _cpp_convert_input (pfile, CPP_OPTION (pfile, input_charset),
+                                    buf, size, total, &file->st.st_size);
+  file->buffer_valid = true;
+
+  return true;
 }
 
-static void
-purge_cache (inc)
-     struct include_file *inc;
+/* Convenience wrapper around read_file_guts that opens the file if
+   necessary and closes the file descriptor after reading.  FILE must
+   have been passed through find_file() at some stage.  */
+static bool
+read_file (cpp_reader *pfile, _cpp_file *file)
 {
-  if (inc->buffer)
+  /* If we already have its contents in memory, succeed immediately.  */
+  if (file->buffer_valid)
+    return true;
+
+  /* If an earlier read failed for some reason don't try again.  */
+  if (file->dont_read || file->err_no)
+    return false;
+
+  if (file->fd == -1 && !open_file (file))
     {
-#if MMAP_THRESHOLD
-      if (inc->mapped)
-       munmap ((PTR) inc->buffer, inc->st.st_size);
-      else
-#endif
-       free ((PTR) inc->buffer);
-      inc->buffer = NULL;
+      open_file_failed (pfile, file);
+      return false;
     }
+
+  file->dont_read = !read_file_guts (pfile, file);
+  close (file->fd);
+  file->fd = -1;
+
+  return !file->dont_read;
 }
 
-/* Return 1 if the file named by FNAME has been included before in
-   any context, 0 otherwise.  */
-int
-cpp_included (pfile, fname)
-     cpp_reader *pfile;
-     const char *fname;
+/* Returns TRUE if FILE's contents have been successfully placed in
+   FILE->buffer and the file should be stacked, otherwise false.  */
+static bool
+should_stack_file (cpp_reader *pfile, _cpp_file *file, bool import)
 {
-  struct search_path *path;
-  char *name, *n;
-  splay_tree_node nd;
+  _cpp_file *f;
 
-  if (IS_ABSOLUTE_PATHNAME (fname))
+  /* Skip once-only files.  */
+  if (file->once_only)
+    return false;
+
+  /* We must mark the file once-only if #import now, before header
+     guard checks.  Otherwise, undefining the header guard might
+     cause the file to be re-stacked.  */
+  if (import)
     {
-      /* Just look it up.  */
-      nd = splay_tree_lookup (pfile->all_include_files, (splay_tree_key) fname);
-      return (nd && nd->value);
+      _cpp_mark_file_once_only (pfile, file);
+
+      /* Don't stack files that have been stacked before.  */
+      if (file->stack_count)
+       return false;
     }
-      
-  /* 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)
+
+  /* Skip if the file had a header guard and the macro is defined.
+     PCH relies on this appearing before the PCH handler below.  */
+  if (file->cmacro && file->cmacro->type == NT_MACRO)
+    return false;
+
+  /* Handle PCH files immediately; don't stack them.  */
+  if (file->pch)
     {
-      memcpy (name, path->name, path->len);
-      name[path->len] = '/';
-      strcpy (&name[path->len + 1], fname);
-      if (CPP_OPTION (pfile, remap))
-       n = remap_filename (pfile, name, path);
-      else
-       n = name;
+      pfile->cb.read_pch (pfile, file->path, file->fd, file->pchname);
+      close (file->fd);
+      file->fd = -1;
+      return false;
+    }
 
-      nd = splay_tree_lookup (pfile->all_include_files, (splay_tree_key) n);
-      if (nd && nd->value)
-       return 1;
+  if (!read_file (pfile, file))
+    return false;
+
+  /* Check the file against the PCH file.  This is done before
+     checking against files we've already seen, since it may save on
+     I/O.  */
+  if (check_file_against_entries (pfile, file, import))
+    {
+      /* If this isn't a #import, but yet we can't include the file,
+        that means that it was #import-ed in the PCH file,
+        so we can never include it again.  */
+      if (! import)
+       _cpp_mark_file_once_only (pfile, file);
+      return false;
     }
-  return 0;
+
+  /* Now we've read the file's contents, we can stack it if there
+     are no once-only files.  */
+  if (!pfile->seen_once_only)
+    return true;
+
+  /* We may have read the file under a different name.  Look
+     for likely candidates and compare file contents to be sure.  */
+  for (f = pfile->all_files; f; f = f->next_file)
+    {
+      if (f == file)
+       continue;
+
+      if ((import || f->once_only)
+         && f->err_no == 0
+         && f->st.st_mtime == file->st.st_mtime
+         && f->st.st_size == file->st.st_size
+         && read_file (pfile, f)
+         /* Size might have changed in read_file().  */
+         && f->st.st_size == file->st.st_size
+         && !memcmp (f->buffer, file->buffer, f->st.st_size))
+       break;
+    }
+
+  return f == NULL;
 }
 
-/* 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 #include_next directive, set INCLUDE_NEXT to true.  */
-
-static struct include_file *
-find_include_file (pfile, header, type)
-     cpp_reader *pfile;
-     const cpp_token *header;
-     enum include_type type;
+/* Place the file referenced by FILE into a new buffer on the buffer
+   stack if possible.  IMPORT is true if this stacking attempt is
+   because of a #import directive.  Returns true if a buffer is
+   stacked.  */
+bool
+_cpp_stack_file (cpp_reader *pfile, _cpp_file *file, bool import)
 {
-  const char *fname = (const char *) header->val.str.text;
-  struct search_path *path;
-  struct include_file *file;
-  char *name, *n;
+  cpp_buffer *buffer;
+  int sysp;
+
+  if (!should_stack_file (pfile, file, import))
+      return false;
+
+  if (pfile->buffer == NULL || file->dir == NULL)
+    sysp = 0;
+  else
+    sysp = MAX (pfile->buffer->sysp,  file->dir->sysp);
+
+  /* Add the file to the dependencies on its first inclusion.  */
+  if (CPP_OPTION (pfile, deps.style) > !!sysp && !file->stack_count)
+    {
+      if (!file->main_file || !CPP_OPTION (pfile, deps.ignore_main_file))
+       deps_add_dep (pfile->deps, file->path);
+    }
+
+  /* Clear buffer_valid since _cpp_clean_line messes it up.  */
+  file->buffer_valid = false;
+  file->stack_count++;
+
+  /* Stack the buffer.  */
+  buffer = cpp_push_buffer (pfile, file->buffer, file->st.st_size,
+                           CPP_OPTION (pfile, preprocessed));
+  buffer->file = file;
+  buffer->sysp = sysp;
+
+  /* Initialize controlling macro state.  */
+  pfile->mi_valid = true;
+  pfile->mi_cmacro = 0;
+
+  /* Generate the call back.  */
+  _cpp_do_file_change (pfile, LC_ENTER, file->path, 1, sysp);
+
+  return true;
+}
+
+/* Mark FILE to be included once only.  */
+void
+_cpp_mark_file_once_only (cpp_reader *pfile, _cpp_file *file)
+{
+  pfile->seen_once_only = true;
+  file->once_only = true;
+}
+
+/* Return the directory from which searching for FNAME should start,
+   considering the directive TYPE and ANGLE_BRACKETS.  If there is
+   nothing left in the path, returns NULL.  */
+static struct cpp_dir *
+search_path_head (cpp_reader *pfile, const char *fname, int angle_brackets,
+                 enum include_type type)
+{
+  cpp_dir *dir;
+  _cpp_file *file;
+
+  if (IS_ABSOLUTE_PATH (fname))
+    return &pfile->no_search_path;
 
-  if (IS_ABSOLUTE_PATHNAME (fname))
-    return open_file (pfile, fname);
+  /* pfile->buffer is NULL when processing an -include command-line flag.  */
+  file = pfile->buffer == NULL ? pfile->main_file : pfile->buffer->file;
 
   /* 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 = CPP_OPTION (pfile, bracket_include);
+  if (type == IT_INCLUDE_NEXT && file->dir)
+    dir = file->dir->next;
+  else if (angle_brackets)
+    dir = pfile->bracket_include;
+  else if (type == IT_CMDLINE)
+    /* -include and -imacros use the #include "" chain with the
+       preprocessor's cwd prepended.  */
+    return make_cpp_dir (pfile, "./", false);
+  else if (pfile->quote_ignores_source_dir)
+    dir = pfile->quote_include;
   else
-    path = search_from (pfile, type);
+    return make_cpp_dir (pfile, dir_name_of_file (file),
+                        pfile->buffer ? pfile->buffer->sysp : 0);
 
-  if (path == NULL)
+  if (dir == NULL)
+    cpp_error (pfile, CPP_DL_ERROR,
+              "no include path in which to search for %s", fname);
+
+  return dir;
+}
+
+/* Strip the basename from the file's path.  It ends with a slash if
+   of nonzero length.  Note that this procedure also works for
+   <stdin>, which is represented by the empty string.  */
+static const char *
+dir_name_of_file (_cpp_file *file)
+{
+  if (!file->dir_name)
     {
-      cpp_error (pfile, "no include path in which to find %s", fname);
-      return NO_INCLUDE_PATH;
+      size_t len = lbasename (file->path) - file->path;
+      char *dir_name = xmalloc (len + 1);
+
+      memcpy (dir_name, file->path, len);
+      dir_name[len] = '\0';
+      file->dir_name = dir_name;
     }
 
-  /* Search directory path for the file.  */
-  name = (char *) alloca (strlen (fname) + pfile->max_include_len + 2);
-  for (; path; path = path->next)
+  return file->dir_name;
+}
+
+/* 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_stack_include (cpp_reader *pfile, const char *fname, int angle_brackets,
+                   enum include_type type)
+{
+  struct cpp_dir *dir;
+  _cpp_file *file;
+
+  dir = search_path_head (pfile, fname, angle_brackets, type);
+  if (!dir)
+    return false;
+
+  file = _cpp_find_file (pfile, fname, dir, false);
+
+  /* Compensate for the increment in linemap_add.  In the case of a
+     normal #include, we're currently at the start of the line
+     *following* the #include.  A separate source_location for this
+     location makes no sense (until we do the LC_LEAVE), and
+     complicates LAST_SOURCE_LINE_LOCATION.  This does not apply if we
+     found a PCH file (in which case linemap_add is not called) or we
+     were included from the command-line.  */
+  if (! file->pch && file->err_no == 0 && type != IT_CMDLINE)
+    pfile->line_table->highest_location--;
+
+  return _cpp_stack_file (pfile, file, type == IT_IMPORT);
+}
+
+/* Could not open FILE.  The complication is dependency output.  */
+static void
+open_file_failed (cpp_reader *pfile, _cpp_file *file)
+{
+  int sysp = pfile->line > 1 && pfile->buffer ? pfile->buffer->sysp : 0;
+  bool print_dep = CPP_OPTION (pfile, deps.style) > !!sysp;
+
+  errno = file->err_no;
+  if (print_dep && CPP_OPTION (pfile, deps.missing_files) && errno == ENOENT)
+    deps_add_dep (pfile->deps, file->name);
+  else
     {
-      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))
-       n = remap_filename (pfile, name, path);
+      /* If we are outputting dependencies but not for this file then
+        don't error because we can still produce correct output.  */
+      if (CPP_OPTION (pfile, deps.style) && ! print_dep)
+       cpp_errno (pfile, CPP_DL_WARNING, file->path);
       else
-       n = name;
-
-      file = open_file (pfile, n);
-      if (file)
-       {
-         file->foundhere = path;
-         return file;
-       }
+       cpp_errno (pfile, CPP_DL_ERROR, file->path);
     }
+}
 
-  return 0;
+/* Search in the chain beginning at HEAD for a file whose search path
+   started at START_DIR != NULL.  */
+static struct file_hash_entry *
+search_cache (struct file_hash_entry *head, const cpp_dir *start_dir)
+{
+  while (head && head->start_dir != start_dir)
+    head = head->next;
+
+  return head;
+}
+
+/* Allocate a new _cpp_file structure.  */
+static _cpp_file *
+make_cpp_file (cpp_reader *pfile, cpp_dir *dir, const char *fname)
+{
+  _cpp_file *file;
+
+  file = xcalloc (1, sizeof (_cpp_file));
+  file->main_file = !pfile->buffer;
+  file->fd = -1;
+  file->dir = dir;
+  file->name = xstrdup (fname);
+
+  return file;
+}
+
+/* A hash of directory names.  The directory names are the path names
+   of files which contain a #include "", the included file name is
+   appended to this directories.
+
+   To avoid duplicate entries we follow the convention that all
+   non-empty directory names should end in a '/'.  DIR_NAME must be
+   stored in permanently allocated memory.  */
+static cpp_dir *
+make_cpp_dir (cpp_reader *pfile, const char *dir_name, int sysp)
+{
+  struct file_hash_entry *entry, **hash_slot;
+  cpp_dir *dir;
+
+  hash_slot = (struct file_hash_entry **)
+    htab_find_slot_with_hash (pfile->file_hash, dir_name,
+                             htab_hash_string (dir_name),
+                             INSERT);
+
+  /* Have we already hashed this directory?  */
+  for (entry = *hash_slot; entry; entry = entry->next)
+    if (entry->start_dir == NULL)
+      return entry->u.dir;
+
+  dir = xcalloc (1, sizeof (cpp_dir));
+  dir->next = pfile->quote_include;
+  dir->name = (char *) dir_name;
+  dir->len = strlen (dir_name);
+  dir->sysp = sysp;
+  dir->construct = 0;
+
+  /* Store this new result in the hash table.  */
+  entry = new_file_hash_entry (pfile);
+  entry->next = *hash_slot;
+  entry->start_dir = NULL;
+  entry->u.dir = dir;
+  *hash_slot = entry;
+
+  return dir;
+}
+
+/* Create a new block of memory for file hash entries.  */
+static void
+allocate_file_hash_entries (cpp_reader *pfile)
+{
+  pfile->file_hash_entries_used = 0;
+  pfile->file_hash_entries_allocated = 127;
+  pfile->file_hash_entries = xmalloc
+    (pfile->file_hash_entries_allocated * sizeof (struct file_hash_entry));
+}
+
+/* Return a new file hash entry.  */
+static struct file_hash_entry *
+new_file_hash_entry (cpp_reader *pfile)
+{
+  if (pfile->file_hash_entries_used == pfile->file_hash_entries_allocated)
+    allocate_file_hash_entries (pfile);
+
+  return &pfile->file_hash_entries[pfile->file_hash_entries_used++];
+}
+
+/* Returns TRUE if a file FNAME has ever been successfully opened.
+   This routine is not intended to correctly handle filenames aliased
+   by links or redundant . or .. traversals etc.  */
+bool
+cpp_included (cpp_reader *pfile, const char *fname)
+{
+  struct file_hash_entry *entry;
+
+  entry = htab_find_with_hash (pfile->file_hash, fname,
+                              htab_hash_string (fname));
+
+  while (entry && (entry->start_dir == NULL || entry->u.file->err_no))
+    entry = entry->next;
+
+  return entry != NULL;
+}
+
+/* Calculate the hash value of a file hash entry P.  */
+
+static hashval_t
+file_hash_hash (const void *p)
+{
+  struct file_hash_entry *entry = (struct file_hash_entry *) p;
+  const char *hname;
+  if (entry->start_dir)
+    hname = entry->u.file->name;
+  else
+    hname = entry->u.dir->name;
+
+  return htab_hash_string (hname);
+}
+
+/* Compare a string Q against a file hash entry P.  */
+static int
+file_hash_eq (const void *p, const void *q)
+{
+  struct file_hash_entry *entry = (struct file_hash_entry *) p;
+  const char *fname = (const char *) q;
+  const char *hname;
+
+  if (entry->start_dir)
+    hname = entry->u.file->name;
+  else
+    hname = entry->u.dir->name;
+
+  return strcmp (hname, fname) == 0;
+}
+
+/* Initialize everything in this source file.  */
+void
+_cpp_init_files (cpp_reader *pfile)
+{
+  pfile->file_hash = htab_create_alloc (127, file_hash_hash, file_hash_eq,
+                                       NULL, xcalloc, free);
+  allocate_file_hash_entries (pfile);
+}
+
+/* Finalize everything in this source file.  */
+void
+_cpp_cleanup_files (cpp_reader *pfile)
+{
+  htab_delete (pfile->file_hash);
+}
+
+/* Enter a file name in the hash for the sake of cpp_included.  */
+void
+_cpp_fake_include (cpp_reader *pfile, const char *fname)
+{
+  _cpp_find_file (pfile, fname, pfile->buffer->file->dir, true);
 }
 
 /* Not everyone who wants to set system-header-ness on a buffer can
    see the details of a buffer.  This is an exported interface because
    fix-header needs it.  */
 void
-cpp_make_system_header (pfile, syshdr, externc)
-     cpp_reader *pfile;
-     int syshdr, externc;
+cpp_make_system_header (cpp_reader *pfile, int syshdr, int externc)
 {
   int flags = 0;
+  const struct line_map *map = linemap_lookup (pfile->line_table, pfile->line);
 
   /* 1 = system header, 2 = system header to be treated as C.  */
   if (syshdr)
     flags = 1 + (externc != 0);
-  _cpp_do_file_change (pfile, LC_RENAME, pfile->map->to_file,
-                      SOURCE_LINE (pfile->map, pfile->line), flags);
+  pfile->buffer->sysp = flags;
+  _cpp_do_file_change (pfile, LC_RENAME, map->to_file,
+                      SOURCE_LINE (map, pfile->line), flags);
 }
 
-/* Report on all files that might benefit from a multiple include guard.
-   Triggered by -H.  */
+/* 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_report_missing_guards (pfile)
-     cpp_reader *pfile;
+cpp_change_file (cpp_reader *pfile, enum lc_reason reason,
+                const char *new_name)
 {
-  int banner = 0;
-  splay_tree_foreach (pfile->all_include_files, report_missing_guard,
-                     (PTR) &banner);
+  _cpp_do_file_change (pfile, reason, new_name, 1, 0);
 }
 
+/* Callback function for htab_traverse.  */
 static int
-report_missing_guard (n, b)
-     splay_tree_node n;
-     void *b;
+report_missing_guard (void **slot, void *b)
 {
-  struct include_file *f = (struct include_file *) n->value;
-  int *bannerp = (int *)b;
+  struct file_hash_entry *entry = (struct file_hash_entry *) *slot;
+  int *bannerp = (int *) b;
 
-  if (f && f->cmacro == 0 && f->include_count == 1)
+  /* Skip directories.  */
+  if (entry->start_dir != NULL)
     {
-      if (*bannerp == 0)
-       {
-         fputs (_("Multiple include guards may be useful for:\n"), stderr);
-         *bannerp = 1;
-       }
-      fputs (f->name, stderr);
-      putc ('\n', stderr);
-    }
-  return 0;
-}
-
-/* Create a dependency, or issue an error message as appropriate.  */
-static void
-handle_missing_header (pfile, fname, angle_brackets)
-     cpp_reader *pfile;
-     const char *fname;
-     int angle_brackets;
-{
-  int print_dep = CPP_PRINT_DEPS(pfile) > (angle_brackets || pfile->map->sysp);
+      _cpp_file *file = entry->u.file;
 
-  if (CPP_OPTION (pfile, print_deps_missing_files) && print_dep)
-    {
-      if (!angle_brackets || IS_ABSOLUTE_PATHNAME (fname))
-       deps_add_dep (pfile->deps, fname);
-      else
+      /* We don't want MI guard advice for the main file.  */
+      if (file->cmacro == NULL && file->stack_count == 1 && !file->main_file)
        {
-         /* 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)
+         if (*bannerp == 0)
            {
-             memcpy (p, ptr->name, len);
-             p[len++] = '/';
+             fputs (_("Multiple include guards may be useful for:\n"),
+                    stderr);
+             *bannerp = 1;
            }
-         memcpy (p + len, fname, fname_len + 1);
-         deps_add_dep (pfile->deps, p);
+
+         fputs (entry->u.file->path, stderr);
+         putc ('\n', stderr);
        }
     }
-  /* 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.  FIXME: Use a future cpp_diagnostic_with_errno ()
-     for both of these cases.  */
-  else if (CPP_PRINT_DEPS (pfile) && ! print_dep)
-    cpp_warning (pfile, "%s: %s", fname, xstrerror (errno));
-  else
-    cpp_error_from_errno (pfile, fname);
+
+  return 0;
 }
 
-/* Handles #include-family directives, 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;
-     enum include_type type;
+/* Report on all files that might benefit from a multiple include guard.
+   Triggered by -H.  */
+void
+_cpp_report_missing_guards (cpp_reader *pfile)
 {
-  bool stacked = false;
-  struct include_file *inc = find_include_file (pfile, header, type);
-
-  if (inc == 0)
-    handle_missing_header (pfile, (const char *) header->val.str.text,
-                          header->type == CPP_HEADER_NAME);
-  else if (inc != NO_INCLUDE_PATH)
-    {
-      stacked = stack_include_file (pfile, inc);
-
-      if (type == IT_IMPORT)
-       _cpp_never_reread (inc);
-    }
+  int banner = 0;
 
-  return stacked;
+  htab_traverse (pfile->file_hash, report_missing_guard, &banner);
 }
 
 /* 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
+   file.  If it cannot be located or dated, return -1, if it is
    newer, return 1, otherwise 0.  */
 int
-_cpp_compare_file_date (pfile, header)
-     cpp_reader *pfile;
-     const cpp_token *header;
+_cpp_compare_file_date (cpp_reader *pfile, const char *fname,
+                       int angle_brackets)
 {
-  struct include_file *inc = find_include_file (pfile, header, 0);
-  
-  if (inc == NULL || inc == NO_INCLUDE_PATH)
-    return -1;
-
-  if (inc->fd > 0)
-    {
-      close (inc->fd);
-      inc->fd = -1;
-    }
-    
-  return inc->st.st_mtime > pfile->buffer->inc->st.st_mtime;
-}
+  _cpp_file *file;
+  struct cpp_dir *dir;
 
+  dir = search_path_head (pfile, fname, angle_brackets, IT_INCLUDE);
+  if (!dir)
+    return -1;
 
-/* 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;
-{
-  struct include_file *f = open_file (pfile, fname);
+  file = _cpp_find_file (pfile, fname, dir, false);
+  if (file->err_no)
+    return -1;
 
-  if (f == NULL)
+  if (file->fd != -1)
     {
-      cpp_error_from_errno (pfile, fname);
-      return false;
+      close (file->fd);
+      file->fd = -1;
     }
 
-  return stack_include_file (pfile, f);
+  return file->st.st_mtime > pfile->buffer->file->st.st_mtime;
 }
 
-/* Do appropriate cleanup when a file buffer is popped off the input
-   stack.  Push the next -include file, if any remain.  */
+/* Pushes the given file onto the buffer stack.  Returns nonzero if
+   successful.  */
 bool
-_cpp_pop_file_buffer (pfile, inc)
-     cpp_reader *pfile;
-     struct include_file *inc;
+cpp_push_include (cpp_reader *pfile, const char *fname)
 {
-  bool pushed = false;
+  return _cpp_stack_include (pfile, fname, false, IT_CMDLINE);
+}
 
+/* Do appropriate cleanup when a file INC's buffer is popped off the
+   input stack.  */
+void
+_cpp_pop_file_buffer (cpp_reader *pfile, _cpp_file *file)
+{
   /* 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;
+  if (pfile->mi_valid && file->cmacro == NULL)
+    file->cmacro = pfile->mi_cmacro;
 
   /* Invalidate control macros in the #including file.  */
   pfile->mi_valid = false;
 
-  inc->refcnt--;
-  if (inc->refcnt == 0 && DO_NOT_REREAD (inc))
-    purge_cache (inc);
-
-  /* Don't generate a callback for popping the main file.  */
-  if (pfile->buffer)
+  if (file->buffer)
     {
-      _cpp_do_file_change (pfile, LC_LEAVE, 0, 0, 0);
-
-      /* Finally, push the next -included file, if any.  */
-      if (!pfile->buffer->prev)
-       pushed = _cpp_push_next_buffer (pfile);
+      free ((void *) file->buffer);
+      file->buffer = NULL;
     }
-
-  return pushed;
 }
 
-/* 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.
+/* 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 we're handling -include or -imacros, use the "" chain, but with
-   the preprocessor's cwd prepended.  */
-static struct search_path *
-search_from (pfile, type)
-     cpp_reader *pfile;
-     enum include_type type;
+   If BRACKET does not lie in the QUOTE chain, it is set to QUOTE.  */
+void
+cpp_set_include_chains (cpp_reader *pfile, cpp_dir *quote, cpp_dir *bracket,
+                       int quote_ignores_source_dir)
 {
-  cpp_buffer *buffer = pfile->buffer;
-  unsigned int dlen;
+  pfile->quote_include = quote;
+  pfile->bracket_include = quote;
+  pfile->quote_ignores_source_dir = quote_ignores_source_dir;
 
-  /* Command line uses the cwd, and does not cache the result.  */
-  if (type == IT_CMDLINE)
-    goto use_cwd;
-
-  /* Ignore the current file's directory if -I- was given.  */
-  if (CPP_OPTION (pfile, ignore_srcdir))
-    return CPP_OPTION (pfile, quote_include);
-
-  if (! buffer->search_cached)
+  for (; quote; quote = quote->next)
     {
-      buffer->search_cached = 1;
-
-      dlen = lbasename (buffer->inc->name) - buffer->inc->name;
-
-      if (dlen)
-       {
-         /* We don't guarantee NAME is null-terminated.  This saves
-            allocating and freeing memory.  Drop a trailing '/'.  */
-         buffer->dir.name = buffer->inc->name;
-         if (dlen > 1)
-           dlen--;
-       }
-      else
-       {
-       use_cwd:
-         buffer->dir.name = ".";
-         dlen = 1;
-       }
-
-      if (dlen > pfile->max_include_len)
-       pfile->max_include_len = dlen;
-
-      buffer->dir.len = dlen;
-      buffer->dir.next = CPP_OPTION (pfile, quote_include);
-      buffer->dir.sysp = pfile->map->sysp;
+      quote->name_map = NULL;
+      quote->len = strlen (quote->name);
+      if (quote == bracket)
+       pfile->bracket_include = bracket;
     }
-
-  return &buffer->dir;
 }
 
-/* The file_name_map structure holds a mapping of file names for a
-   particular directory.  This mapping is read from the file named
-   FILE_NAME_MAP_FILE in that directory.  Such a file can be used to
-   map filenames on a file system with severe filename restrictions,
-   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
+/* Append the file name to the directory to create the path, but don't
+   turn / into // or // into ///; // may be a namespace escape.  */
+static char *
+append_file_to_dir (const char *fname, cpp_dir *dir)
 {
-  struct file_name_map *map_next;
-  char *map_from;
-  char *map_to;
-};
+  size_t dlen, flen;
+  char *path;
 
-#define FILE_NAME_MAP_FILE "header.gcc"
+  dlen = dir->len;
+  flen = strlen (fname);
+  path = xmalloc (dlen + 1 + flen + 1);
+  memcpy (path, dir->name, dlen);
+  if (dlen && path[dlen - 1] != '/')
+    path[dlen++] = '/';
+  memcpy (&path[dlen], fname, flen + 1);
 
-/* Read a space delimited string of unlimited length from a stdio
-   file.  */
+  return path;
+}
 
+/* Read a space delimited string of unlimited length from a stdio
+   file F.  */
 static char *
-read_filename_string (ch, f)
-     int ch;
-     FILE *f;
+read_filename_string (int ch, FILE *f)
 {
   char *alloc, *set;
   int len;
 
   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)
            {
@@ -883,305 +1172,384 @@ read_filename_string (ch, f)
   return alloc;
 }
 
-/* This structure holds a linked list of file name maps, one per directory.  */
-
-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;
+/* Read the file name map file for DIR.  */
+static void
+read_name_map (cpp_dir *dir)
 {
-  struct file_name_map_list *map_list_ptr;
+  static const char FILE_NAME_MAP_FILE[] = "header.gcc";
   char *name;
   FILE *f;
-
-  /* Check the cache of directories, and mappings in their remap file.  */
-  for (map_list_ptr = CPP_OPTION (pfile, map_list); map_list_ptr;
-       map_list_ptr = map_list_ptr->map_list_next)
-    if (! strcmp (map_list_ptr->map_list_name, dirname))
-      return map_list_ptr->map_list_map;
-
-  map_list_ptr = ((struct file_name_map_list *)
-                 xmalloc (sizeof (struct file_name_map_list)));
-  map_list_ptr->map_list_name = xstrdup (dirname);
-
-  /* The end of the list ends in NULL.  */
-  map_list_ptr->map_list_map = NULL;
-
-  name = (char *) alloca (strlen (dirname) + strlen (FILE_NAME_MAP_FILE) + 2);
-  strcpy (name, dirname);
-  if (*dirname)
-    strcat (name, "/");
-  strcat (name, FILE_NAME_MAP_FILE);
+  size_t len, count = 0, room = 9;
+
+  len = dir->len;
+  name = alloca (len + sizeof (FILE_NAME_MAP_FILE) + 1);
+  memcpy (name, dir->name, len);
+  if (len && name[len - 1] != '/')
+    name[len++] = '/';
+  strcpy (name + len, FILE_NAME_MAP_FILE);
   f = fopen (name, "r");
 
+  dir->name_map = xmalloc (room * sizeof (char *));
+
   /* Silently return NULL if we cannot open.  */
   if (f)
     {
       int ch;
-      int dirlen = strlen (dirname);
 
       while ((ch = getc (f)) != EOF)
        {
-         char *from, *to;
-         struct file_name_map *ptr;
+         char *to;
 
-         if (is_space(ch))
+         if (is_space (ch))
            continue;
-         from = read_filename_string (ch, f);
-         while ((ch = getc (f)) != EOF && is_hspace(ch))
-           ;
-         to = read_filename_string (ch, f);
 
-         ptr = ((struct file_name_map *)
-                xmalloc (sizeof (struct file_name_map)));
-         ptr->map_from = from;
+         if (count + 2 > room)
+           {
+             room += 8;
+             dir->name_map = xrealloc (dir->name_map, room * sizeof (char *));
+           }
+
+         dir->name_map[count] = read_filename_string (ch, f);
+         while ((ch = getc (f)) != EOF && is_hspace (ch))
+           ;
 
-         /* Make the real filename absolute.  */
-         if (IS_ABSOLUTE_PATHNAME (to))
-           ptr->map_to = to;
+         to = read_filename_string (ch, f);
+         if (IS_ABSOLUTE_PATH (to))
+           dir->name_map[count + 1] = 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);
+             dir->name_map[count + 1] = append_file_to_dir (to, dir);
              free (to);
-           }         
-
-         ptr->map_next = map_list_ptr->map_list_map;
-         map_list_ptr->map_list_map = ptr;
+           }
 
+         count += 2;
          while ((ch = getc (f)) != '\n')
            if (ch == EOF)
              break;
        }
+
       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;
-}  
+  /* Terminate the list of maps.  */
+  dir->name_map[count] = NULL;
+}
 
-/* Remap an unsimplified path NAME based on the file_name_map (if any)
-   for LOC.  */
+/* Remap a FILE's name based on the file_name_map, if any, for
+   FILE->dir.  If the file name has any directory separators,
+   recursively check those directories too.  */
 static char *
-remap_filename (pfile, name, loc)
-     cpp_reader *pfile;
-     char *name;
-     struct search_path *loc;
+remap_filename (cpp_reader *pfile, _cpp_file *file)
 {
-  struct file_name_map *map;
-  const char *from, *p;
-  char *dir;
+  const char *fname, *p;
+  char *new_dir;
+  cpp_dir *dir;
+  size_t index, len;
 
-  if (! loc->name_map)
+  dir = file->dir;
+  fname = file->name;
+
+  for (;;)
     {
-      /* 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;
+      if (!dir->name_map)
+       read_name_map (dir);
+
+      for (index = 0; dir->name_map[index]; index += 2)
+       if (!strcmp (dir->name_map[index], fname))
+           return xstrdup (dir->name_map[index + 1]);
+
+      p = strchr (fname, '/');
+      if (!p || p == fname)
+       return NULL;
+
+      len = dir->len + (p - fname + 1);
+      new_dir = xmalloc (len + 1);
+      memcpy (new_dir, dir->name, dir->len);
+      memcpy (new_dir + dir->len, fname, p - fname + 1);
+      new_dir[len] = '\0';
+
+      dir = make_cpp_dir (pfile, new_dir, dir->sysp);
+      fname = p + 1;
     }
-  
-  /* 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;
-
-  /* Try to find a mapping file for the particular directory we are
-     looking in.  Thus #include <sys/types.h> will look up sys/types.h
-     in /usr/include/header.gcc and look up types.h in
-     /usr/include/sys/header.gcc.  */
-  p = strrchr (name, '/');
-  if (!p)
-    return name;
-
-  /* We know p != name as absolute paths don't call remap_filename.  */
-  if (p == name)
-    cpp_ice (pfile, "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;
-
-  return name;
 }
 
-/* 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;
+/* Returns true if PCHNAME is a valid PCH file for FILE.  */
+static bool
+validate_pch (cpp_reader *pfile, _cpp_file *file, const char *pchname)
 {
-  struct stat s;
-  int result;
+  const char *saved_path = file->path;
+  bool valid = false;
 
-#ifdef HAVE_LSTAT
-  result = lstat (path, &s);
-#else
-  result = stat (path, &s);
-#endif
+  file->path = pchname;
+  if (open_file (file))
+    {
+      valid = 1 & pfile->cb.valid_pch (pfile, pchname, file->fd);
 
-  /* 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;
+      if (!valid)
+       {
+         close (file->fd);
+         file->fd = -1;
+       }
 
-  return result == 0 && S_ISDIR (s.st_mode);
+      if (CPP_OPTION (pfile, print_include_names))
+       {
+         unsigned int i;
+         for (i = 1; i < pfile->line_table->depth; i++)
+           putc ('.', stderr);
+         fprintf (stderr, "%c %s\n",
+                  valid ? '!' : 'x', pchname);
+       }
+    }
+
+  file->path = saved_path;
+  return valid;
 }
 
-/* Simplify a path name in place, deleting redundant components.  This
-   reduces OS overhead and guarantees that equivalent paths compare
-   the same (modulo symlinks).
+/* Get the path associated with the _cpp_file F.  The path includes
+   the base name from the include directive and the directory it was
+   found in via the search path.  */
 
-   Transforms made:
-   foo/bar/../quux     foo/quux
-   foo/./bar           foo/bar
-   foo//bar            foo/bar
-   /../quux            /quux
-   //quux              //quux  (POSIX allows leading // as a namespace escape)
+const char *
+cpp_get_path (struct _cpp_file *f)
+{
+  return f->path;
+}
 
-   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 ().  */
+/* Get the cpp_buffer currently associated with the cpp_reader
+   PFILE.  */
 
-char *
-_cpp_simplify_pathname (path)
-    char *path;
+cpp_buffer *
+cpp_get_buffer (cpp_reader *pfile)
 {
-#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 path;
-
-#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
-  from = to = path;
-#endif
-    
-  /* Remove redundant leading /s.  */
-  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++;
-       }
-    }
+  return pfile->buffer;
+}
 
-  base = orig_base = to;
-  for (;;)
-    {
-      int move_base = 0;
+/* Get the _cpp_file associated with the cpp_buffer B.  */
 
-      while (*from == '/')
-       from++;
+_cpp_file *
+cpp_get_file (cpp_buffer *b)
+{
+  return b->file;
+}
 
-      if (*from == '\0')
-       break;
+/* Get the previous cpp_buffer given a cpp_buffer B.  The previous
+   buffer is the buffer that included the given buffer.  */
 
-      if (*from == '.')
+cpp_buffer *
+cpp_get_prev (cpp_buffer *b)
+{
+  return b->prev;
+}
+\f
+/* This datastructure holds the list of header files that were seen
+   while the PCH was being built.  The 'entries' field is kept sorted
+   in memcmp() order; yes, this means that on little-endian systems,
+   it's sorted initially by the least-significant byte of 'size', but
+   that's OK.  The code does rely on having entries with the same size
+   next to each other.  */
+
+struct pchf_data {
+  /* Number of pchf_entry structures.  */
+  size_t count;
+
+  /* Are there any values with once_only set?
+     This is used as an optimisation, it means we don't have to search
+     the structure if we're processing a regular #include.  */
+  bool have_once_only;
+
+  struct pchf_entry {
+    /* The size of this file.  This is used to save running a MD5 checksum
+       if the sizes don't match.  */
+    off_t size;
+    /* The MD5 checksum of this file.  */
+    unsigned char sum[16];
+    /* Is this file to be included only once?  */
+    bool once_only;
+  } entries[1];
+};
+
+static struct pchf_data *pchf;
+
+/* Data for pchf_addr.  */
+struct pchf_adder_info
+{
+  cpp_reader *pfile;
+  struct pchf_data *d;
+};
+
+/* A hash traversal function to add entries into DATA->D.  */
+
+static int
+pchf_adder (void **slot, void *data)
+{
+  struct file_hash_entry *h = (struct file_hash_entry *) *slot;
+  struct pchf_adder_info *i = (struct pchf_adder_info *) data;
+
+  if (h->start_dir != NULL && h->u.file->stack_count != 0)
+    {
+      struct pchf_data *d = i->d;
+      _cpp_file *f = h->u.file;
+      size_t count = d->count++;
+
+      /* This should probably never happen, since if a read error occurred
+        the PCH file shouldn't be written...  */
+      if (f->dont_read || f->err_no)
+       return 1;
+
+      d->entries[count].once_only = f->once_only;
+      d->have_once_only |= f->once_only;
+      if (f->buffer_valid)
+         md5_buffer ((const char *)f->buffer,
+                     f->st.st_size, d->entries[count].sum);
+      else
        {
-         if (from[1] == '\0')
-           break;
-         if (from[1] == '/')
-           {
-             from += 2;
-             continue;
-           }
-         else if (from[1] == '.' && (from[2] == '/' || from[2] == '\0'))
+         FILE *ff;
+         int oldfd = f->fd;
+
+         if (!open_file (f))
            {
-             /* Don't simplify if there was no previous component.  */
-             if (absolute && orig_base == to)
-               {
-                 from += 2;
-                 continue;
-               }
-             /* Don't simplify if the previous component was "../",
-                or if an error has already occurred with (l)stat.  */
-             if (base != to && errno == 0)
-               {
-                 /* 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;
+             open_file_failed (i->pfile, f);
+             return 0;
            }
+         ff = fdopen (f->fd, "rb");
+         md5_stream (ff, d->entries[count].sum);
+         fclose (ff);
+         f->fd = oldfd;
        }
+      d->entries[count].size = f->st.st_size;
+    }
+  return 1;
+}
+
+/* A qsort ordering function for pchf_entry structures.  */
+
+static int
+pchf_save_compare (const void *e1, const void *e2)
+{
+  return memcmp (e1, e2, sizeof (struct pchf_entry));
+}
+
+/* Create and write to F a pchf_data structure.  */
+
+bool
+_cpp_save_file_entries (cpp_reader *pfile, FILE *f)
+{
+  size_t count = 0;
+  struct pchf_data *result;
+  size_t result_size;
+  struct pchf_adder_info pai;
+
+  count = htab_elements (pfile->file_hash);
+  result_size = (sizeof (struct pchf_data)
+                + sizeof (struct pchf_entry) * (count - 1));
+  result = xcalloc (result_size, 1);
+
+  result->count = 0;
+  result->have_once_only = false;
+
+  pai.pfile = pfile;
+  pai.d = result;
+  htab_traverse (pfile->file_hash, pchf_adder, &pai);
+
+  result_size = (sizeof (struct pchf_data)
+                 + sizeof (struct pchf_entry) * (result->count - 1));
+
+  qsort (result->entries, result->count, sizeof (struct pchf_entry),
+        pchf_save_compare);
+
+  return fwrite (result, result_size, 1, f) == 1;
+}
+
+/* Read the pchf_data structure from F.  */
+
+bool
+_cpp_read_file_entries (cpp_reader *pfile ATTRIBUTE_UNUSED, FILE *f)
+{
+  struct pchf_data d;
+
+  if (fread (&d, sizeof (struct pchf_data) - sizeof (struct pchf_entry), 1, f)
+       != 1)
+    return false;
+
+  pchf = xmalloc (sizeof (struct pchf_data)
+                 + sizeof (struct pchf_entry) * (d.count - 1));
+  memcpy (pchf, &d, sizeof (struct pchf_data) - sizeof (struct pchf_entry));
+  if (fread (pchf->entries, sizeof (struct pchf_entry), d.count, f)
+      != d.count)
+    return false;
+  return true;
+}
+
+/* The parameters for pchf_compare.  */
+
+struct pchf_compare_data
+{
+  /* The size of the file we're looking for.  */
+  off_t size;
 
-      /* Add the component separator.  */
-      if (to > orig_base)
-       *to++ = '/';
+  /* The MD5 checksum of the file, if it's been computed.  */
+  unsigned char sum[16];
 
-      /* Copy this component until the trailing null or '/'.  */
-      while (*from != '\0' && *from != '/')
-       *to++ = *from++;
+  /* Is SUM valid?  */
+  bool sum_computed;
+
+  /* Do we need to worry about entries that don't have ONCE_ONLY set?  */
+  bool check_included;
+
+  /* The file that we're searching for.  */
+  _cpp_file *f;
+};
+
+/* bsearch comparison function; look for D_P in E_P.  */
+
+static int
+pchf_compare (const void *d_p, const void *e_p)
+{
+  const struct pchf_entry *e = (const struct pchf_entry *)e_p;
+  struct pchf_compare_data *d = (struct pchf_compare_data *)d_p;
+  int result;
 
-      if (move_base)
-       base = to;
+  result = memcmp (&d->size, &e->size, sizeof (off_t));
+  if (result != 0)
+    return result;
+
+  if (! d->sum_computed)
+    {
+      _cpp_file *const f = d->f;
+
+      md5_buffer ((const char *)f->buffer, f->st.st_size, d->sum);
+      d->sum_computed = true;
     }
-    
-  /* Change the empty string to "." so that it is not treated as stdin.
-     Null terminate.  */
-  if (to == path)
-    *to++ = '.';
-  *to = '\0';
 
-  return path;
-#else /* VMS  */
-  errno = 0;
-  return path;
-#endif /* !VMS  */
+  result = memcmp (d->sum, e->sum, 16);
+  if (result != 0)
+    return result;
+
+  if (d->check_included || e->once_only)
+    return 0;
+  else
+    return 1;
+}
+
+/* Check that F is not in a list read from a PCH file (if any).
+   Assumes that f->buffer_valid is true.  Return TRUE if the file
+   should not be read.  */
+
+static bool
+check_file_against_entries (cpp_reader *pfile ATTRIBUTE_UNUSED,
+                           _cpp_file *f,
+                           bool check_included)
+{
+  struct pchf_compare_data d;
+
+  if (pchf == NULL
+      || (! check_included && ! pchf->have_once_only))
+    return false;
+
+  d.size = f->st.st_size;
+  d.sum_computed = false;
+  d.f = f;
+  d.check_included = check_included;
+  return bsearch (&d, pchf->entries, pchf->count, sizeof (struct pchf_entry),
+                 pchf_compare) != NULL;
 }