OSDN Git Service

* builtins.def: Alphabetize.
[pf3gnuchains/gcc-fork.git] / gcc / cppfiles.c
index c480cb4..cd3498e 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 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,309 +27,394 @@ 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 <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
-
-#ifndef INCLUDE_LEN_FUDGE
-# define INCLUDE_LEN_FUDGE 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
-#ifndef ENOMEM
-# define ENOMEM 0
-#endif
-
-/* 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
 
-/* This structure is used for the table of all includes.  */
-struct include_file
+/* 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.  */
+typedef struct _cpp_file _cpp_file;
+struct _cpp_file
 {
-  const char *name;            /* actual path name of file */
-  const cpp_hashnode *cmacro;  /* macro, if any, preventing reinclusion.  */
-  const struct file_name_list *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) */
-  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 */
-};
+  /* Filename as given to #include or command line switch.  */
+  const char *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 or not as specified by
-   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))
-
-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 file_name_list *));
-static struct file_name_list *actual_directory
-                               PARAMS ((cpp_reader *, const char *));
-static struct include_file *find_include_file
-                               PARAMS ((cpp_reader *, const char *,
-                                        struct file_name_list *));
-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 void purge_cache        PARAMS ((struct include_file *));
-static void destroy_include_file_node  PARAMS ((splay_tree_value));
-static int report_missing_guard                PARAMS ((splay_tree_node, void *));
-
-/* We use a splay tree to store information about all the include
-   files seen in this compilation.  The key of each tree node is the
-   physical path to the file.  The value is 0 if the file does not
-   exist, or a struct include_file pointer.  */
+  /* The full path used to find the file.  */
+  const char *path;
 
-static void
-destroy_include_file_node (v)
-     splay_tree_value v;
-{
-  struct include_file *f = (struct include_file *)v;
+  /* The full path of the pch file.  */
+  const char *pchname;
 
-  if (f)
-    {
-      purge_cache (f);
-      free (f);  /* The tree is registered with free to free f->name.  */
-    }
-}
+  /* The file's path with the basename stripped, malloced.  NULL if it
+     hasn't been calculated yet.  */
+  const char *dir_name;
 
-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_include_file_node);
-}
+  /* Chain through #import-ed files or those  containing #pragma once.  */
+  struct _cpp_file *once_only_next;
 
-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;
 
-/* Mark a file to not be reread (e.g. #import, read failure).  */
-void
-_cpp_never_reread (file)
-     struct include_file *file;
-{
-  file->cmacro = NEVER_REREAD;
-}
+  /* The macro, if any, preventing re-inclusion.  */
+  const cpp_hashnode *cmacro;
 
-/* Put a file name in the splay tree, for the sake of cpp_included ().
-   Assume that FNAME has already had its path simplified.  */
-void
-_cpp_fake_include (pfile, fname)
-     cpp_reader *pfile;
-     const char *fname;
-{
-  splay_tree_node nd;
+  /* The directory in the search path where FILE was found.  Used for
+     #include_next and determining whether a header is a system
+     header.  Is NULL if the file was given as an absolute path, or
+     opened with read_file.  */
+  cpp_dir *dir;
 
-  nd = splay_tree_lookup (pfile->all_include_files, (splay_tree_key) fname);
-  if (! nd)
-    {
-      struct include_file *file = xcnew (struct include_file);
-      file->name = xstrdup (fname);
-      splay_tree_insert (pfile->all_include_files,
-                        (splay_tree_key) file->name,
-                        (splay_tree_value) file);
-    }
-}
+  /* As filled in by stat(2) for the file.  */
+  struct stat st;
 
-/* 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.
+  /* File descriptor.  Invalid if -1, otherwise open.  */
+  int fd;
 
-   Returns an include_file structure with an open file descriptor on
-   success, or NULL on failure.  */
+  /* Zero if this file was successfully opened and stat()-ed,
+     otherwise errno obtained from failure.  */
+  int err_no;
 
-static struct include_file *
-open_file (pfile, filename)
-     cpp_reader *pfile;
-     const char *filename;
-{
-  splay_tree_node nd;
-  struct include_file *file;
+  /* Number of times the file has been stacked for preprocessing.  */
+  unsigned short stack_count;
+
+  /* If opened with #import.  */
+  bool import;
+
+  /* If contains #pragma once.  */
+  bool pragma_once;
+
+  /* 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;
 
-  nd = splay_tree_lookup (pfile->all_include_files, (splay_tree_key) filename);
+  /* 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.  */
+  uchar 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 implicity from the hash table location or explicitly
+   from FILE->fname.
+
+   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.  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 get confused
+   with normal include-chain lookups in the cache.
+
+   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.
+*/
+struct file_hash_entry
+{
+  struct file_hash_entry *next;
+  cpp_dir *start_dir;
+  union
+  {
+    _cpp_file *file;
+    cpp_dir *dir;
+  } u;
+};
 
-  if (nd)
+static bool open_file (_cpp_file *file);
+static bool pch_open_file (cpp_reader *pfile, _cpp_file *file);
+static bool open_file_in_dir (cpp_reader *pfile, _cpp_file *file);
+static _cpp_file *find_file (cpp_reader *, const char *fname,
+                            cpp_dir *start_dir, bool fake);
+static bool read_file_guts (cpp_reader *pfile, _cpp_file *file);
+static bool read_file (cpp_reader *pfile, _cpp_file *file);
+static bool stack_file (cpp_reader *, _cpp_file *file, bool import);
+static bool once_only_file_p (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 int hash_string_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 bool include_pch_p (_cpp_file *file);
+
+/* 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)
+{
+  if (file->path[0] == '\0')
     {
-      file = (struct include_file *) nd->value;
-
-      /* Don't retry opening if we failed previously.  */
-      if (file->fd == -2)
-       return 0;
-
-      /* 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;
+      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)
     {
-      /* In particular, this clears foundhere.  */
-      file = xcnew (struct include_file);
-      file->name = xstrdup (filename);
-      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;
     }
 
-  /* 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).
+  file->err_no = errno;
 
-     Use the three-argument form of open even though we aren't
-     specifying O_CREAT, to defend against broken system headers.
+  return false;
+}
 
-     O_BINARY tells some runtime libraries (notably DJGPP) not to do
-     newline translation; we can handle DOS line breaks just fine
-     ourselves.
+/* Temporary PCH intercept of opening a file.  */
+static bool
+pch_open_file (cpp_reader *pfile, _cpp_file *file)
+{
+  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;
+
+  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)
+    {
+      DIR *pchdir;
+      struct dirent *d;
+      size_t dlen, plen = len;
 
-     Special case: the empty string is translated to stdin.  */
+      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 (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 (filename[0] == '\0')
-    file->fd = 0;
+  if (valid)
+    file->pchname = pchname;
   else
-    file->fd = open (filename, O_RDONLY | O_NOCTTY | O_BINARY, 0666);
+    free (pchname);
 
-  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;
-       }
+  return valid;
+}
 
-      return file;
-    }
+/* Try to open the path FILE->name appended to FILE->dir.  This is
+   where remap and PCH intercept the file lookup process.  */
+static bool
+open_file_in_dir (cpp_reader *pfile, _cpp_file *file)
+{
+  char *path;
+
+  if (CPP_OPTION (pfile, remap) && (path = remap_filename (pfile, file)))
+    ;
+  else
+    path = append_file_to_dir (file->name, file->dir);
 
-  /* Don't issue an error message if the file doesn't exist.  */
-  if (errno != ENOENT && errno != ENOTDIR)
-    cpp_error_from_errno (pfile, filename);
+  file->path = path;
+  if (pch_open_file (pfile, file))
+    return true;
 
-  /* Create a negative node for this path, and return null.  */
-  file->fd = -2;
+  if (open_file (file))
+    return true;
 
-  return 0;
+  free (path);
+  file->path = NULL;
+  return false;
 }
 
-/* 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.  */
-
-static void
-stack_include_file (pfile, inc)
-     cpp_reader *pfile;
-     struct include_file *inc;
+/* 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 non-zero 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().
+*/
+static _cpp_file *
+find_file (cpp_reader *pfile, const char *fname, cpp_dir *start_dir, bool fake)
 {
-  size_t len = 0;
-  cpp_buffer *fp;
-
-  /* Not in cache?  */
-  if (! inc->buffer)
-    read_include_file (pfile, inc);
-
-  if (! DO_NOT_REREAD (inc))
-    len = inc->st.st_size;
-
-  /* Push a buffer.  */
-  fp = cpp_push_buffer (pfile, inc->buffer, len, BUF_FILE, inc->name);
-  fp->inc = inc;
-  fp->inc->refcnt++;
-  if (inc->foundhere)
-    fp->sysp = inc->foundhere->sysp;
-
-  /* The ->actual_dir field is only used when ignore_srcdir is not in effect;
-     see do_include */
-  if (!CPP_OPTION (pfile, ignore_srcdir))
-    fp->actual_dir = actual_directory (pfile, inc->name);
-
-  /* Initialise controlling macro state.  */
-  pfile->mi_state = MI_OUTSIDE;
-  pfile->mi_cmacro = 0;
-  pfile->include_depth++;
+  struct file_hash_entry *entry, **hash_slot;
+  _cpp_file *file;
 
-  /* Generate the call back.  */
-  fp->lineno = 0;
-  _cpp_do_file_change (pfile, FC_ENTER, 0, 0);
-  fp->lineno = 1;
-}
+  /* Ensure we get no confusion between cached files and directories.  */
+  if (start_dir == NULL)
+    cpp_error (pfile, DL_ICE, "NULL directory in find_file");
 
-/* Read the file referenced by INC into the file cache.
+  hash_slot = (struct file_hash_entry **)
+    htab_find_slot (pfile->file_hash, fname, INSERT);
 
-   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.
+  /* First check the cache before we resort to memory allocation.  */
+  entry = search_cache (*hash_slot, start_dir);
+  if (entry)
+    return entry->u.file;
 
-   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.
+  file = make_cpp_file (pfile, start_dir, fname);
 
-   FIXME: Flush file cache and try again if we run out of memory.  */
+  /* Try each path in the include chain.  */
+  for (; !fake ;)
+    {
+      if (open_file_in_dir (pfile, file))
+       break;
 
-static void
-read_include_file (pfile, inc)
-     cpp_reader *pfile;
-     struct include_file *inc;
-{
-  ssize_t size, offset, count;
-  U_CHAR *buf;
-#if MMAP_THRESHOLD
-  static int pagesize = -1;
-#endif
+      if (file->err_no != ENOENT || (file->dir = file->dir->next) == NULL)
+       {
+         open_file_failed (pfile, file);
+         break;
+       }
+
+      /* 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;
+
+      entry = search_cache (*hash_slot, file->dir);
+      if (entry)
+       {
+         /* Cache for START_DIR too, sharing the _cpp_file structure.  */
+         free ((char *) file->name);
+         free (file);
+         file = entry->u.file;
+         break;
+       }
+    }
+
+  /* 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 file;
+}
+
+/* Read a file into FILE->buffer, returning true on success.
+
+   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.
 
-  if (DO_NOT_REREAD (inc))
-    return;
+   FIXME: Flush file cache and try again if we run out of memory.  */
+static bool
+read_file_guts (cpp_reader *pfile, _cpp_file *file)
+{
+  ssize_t size, total, count;
+  uchar *buf;
+  bool regular;
+  
+  if (S_ISBLK (file->st.st_mode))
+    {
+      cpp_error (pfile, DL_ERROR, "%s is a block device", file->name);
+      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
@@ -338,501 +424,640 @@ 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))
+      if (file->st.st_size > INTTYPE_MAXIMUM (ssize_t))
        {
-         cpp_error (pfile, "%s is too large", inc->name);
-         goto fail;
+         cpp_error (pfile, DL_ERROR, "%s is too large", file->name);
+         return false;
        }
-      size = inc->st.st_size;
 
-      inc->mapped = 0;
-#if MMAP_THRESHOLD
-      if (pagesize == -1)
-       pagesize = getpagesize ();
+      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)
+    {
+      total += count;
 
-      if (size / pagesize >= MMAP_THRESHOLD)
-       {
-         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 (total == size)
        {
-         buf = (U_CHAR *) xmalloc (size);
-         offset = 0;
-         while (offset < size)
-           {
-             count = read (inc->fd, buf + offset, size - offset);
-             if (count < 0)
-               goto perror_fail;
-             if (count == 0)
-               {
-                 cpp_warning (pfile, "%s is shorter than expected", inc->name);
-                 break;
-               }
-             offset += count;
-           }
+         if (regular)
+           break;
+         size *= 2;
+         buf = xrealloc (buf, size + 1);
        }
     }
-  else if (S_ISBLK (inc->st.st_mode))
+
+  if (count < 0)
     {
-      cpp_error (pfile, "%s is a block device", inc->name);
-      goto fail;
+      cpp_errno (pfile, DL_ERROR, file->name);
+      return false;
     }
-  else if (S_ISDIR (inc->st.st_mode))
+
+  if (regular && total != size && STAT_SIZE_RELIABLE (file->st))
+    cpp_error (pfile, DL_WARNING, "%s is shorter than expected", file->name);
+
+  /* Shrink buffer if we allocated substantially too much.  */
+  if (total + 4096 < size)
+    buf = xrealloc (buf, total + 1);
+
+  /* The lexer requires that the buffer be \n-terminated.  */
+  buf[total] = '\n';
+
+  file->buffer = buf;
+  file->st.st_size = total;
+  file->buffer_valid = true;
+
+  return true;
+}
+
+/* Convenience wrapper around read_file_guts that opens the file if
+   necessary and closes the file desciptor after reading.  FILE must
+   have been passed through find_file() at some stage.  */
+static bool
+read_file (cpp_reader *pfile, _cpp_file *file)
+{
+  /* Skip if the file had a header guard and the macro is defined.  */
+  if (file->cmacro && file->cmacro->type == NT_MACRO)
+    return false;
+
+  /* PCH files get dealt with immediately.  */
+  if (include_pch_p (file))
     {
-      cpp_error (pfile, "%s is a directory", inc->name);
-      goto fail;
+      pfile->cb.read_pch (pfile, file->path, file->fd, file->pchname);
+      close (file->fd);
+      file->fd = -1;
+      return false;
     }
-  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 = (U_CHAR *) xmalloc (size);
-      offset = 0;
-      while ((count = read (inc->fd, buf + offset, size - offset)) > 0)
-       {
-         offset += count;
-         if (offset == size)
-           buf = xrealloc (buf, (size *= 2));
-       }
-      if (count < 0)
-       goto perror_fail;
 
-      if (offset < size)
-       buf = xrealloc (buf, offset);
-      inc->st.st_size = offset;
+  /* 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))
+    {
+      open_file_failed (pfile, file);
+      return false;
     }
 
-  close (inc->fd);
-  inc->buffer = buf;
-  inc->fd = -1;
-  return;
-
- perror_fail:
-  cpp_error_from_errno (pfile, inc->name);
- fail:
-  /* Do not try to read this file again.  */
-  close (inc->fd);
-  inc->fd = -1;
-  _cpp_never_reread (inc);
-  return;
+  file->dont_read = !read_file_guts (pfile, file);
+  close (file->fd);
+  file->fd = -1;
+
+  return !file->dont_read;
 }
 
-static void
-purge_cache (inc)
-     struct include_file *inc;
+/* 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.  */
+static bool
+stack_file (cpp_reader *pfile, _cpp_file *file, bool import)
 {
-  if (inc->buffer)
+  cpp_buffer *buffer;
+  int sysp;
+  const char *fname;
+
+  if (once_only_file_p (pfile, file, import))
+      return false;
+
+  sysp = MAX ((pfile->map ? pfile->map->sysp : 0),
+             (file->dir ? file->dir->sysp : 0));
+
+  /* Add the file to the dependencies on its first inclusion.  */
+  if (CPP_OPTION (pfile, deps.style) > !!sysp && !file->stack_count)
     {
-#if MMAP_THRESHOLD
-      if (inc->mapped)
-       munmap ((PTR) inc->buffer, inc->st.st_size);
-      else
-#endif
-       free ((PTR) inc->buffer);
-      inc->buffer = NULL;
+      if (!file->main_file || !CPP_OPTION (pfile, deps.ignore_main_file))
+       deps_add_dep (pfile->deps, file->path);
     }
+
+  if (!read_file (pfile, file))
+    return false;
+
+  /* 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), 0);
+  buffer->file = file;
+
+  /* Initialize controlling macro state.  */
+  pfile->mi_valid = true;
+  pfile->mi_cmacro = 0;
+
+  /* Generate the call back.  */
+  fname = file->name;
+  _cpp_do_file_change (pfile, LC_ENTER, fname, 1, sysp);
+
+  return true;
 }
 
-/* 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 has been previously read and should not be
+   read again.  */
+static bool
+once_only_file_p (cpp_reader *pfile, _cpp_file *file, bool import)
 {
-  struct file_name_list *path;
-  char *name;
-  splay_tree_node nd;
+  _cpp_file *f;
+
+  /* Nothing to check if this isn't #import and there haven't been any
+     #pragma_once directives.  */
+  if (!import && !pfile->saw_pragma_once)
+    return false;
 
-  if (IS_ABSOLUTE_PATHNAME (fname))
+  /* Did this file contain #pragma once?  */
+  if (file->pragma_once)
+    return true;
+
+  /* Are we #import-ing a previously #import-ed file?  */
+  if (import)
     {
-      /* Just look it up.  */
-      nd = splay_tree_lookup (pfile->all_include_files, (splay_tree_key) fname);
-      return (nd && nd->value);
+      if (file->import)
+       return true;
+      _cpp_mark_file_once_only (pfile, file, true);
     }
-      
-  /* Search directory path for the file.  */
-  name = (char *) alloca (strlen (fname) + pfile->max_include_len
-                         + 2 + INCLUDE_LEN_FUDGE);
-  for (path = CPP_OPTION (pfile, quote_include); path; path = path->next)
+
+  /* Read the file contents now.  stack_file would do it later, and
+     we're smart enough to not do it twice, so this is no loss.  */
+  if (!read_file (pfile, file))
+    return false;
+
+  /* We may have #imported it under a different name, though.  Look
+     for likely candidates and compare file contents to be sure.  */
+  for (f = pfile->once_only_files; f; f = f->once_only_next)
     {
-      memcpy (name, path->name, path->nlen);
-      name[path->nlen] = '/';
-      strcpy (&name[path->nlen+1], fname);
-      _cpp_simplify_pathname (name);
-      if (CPP_OPTION (pfile, remap))
-       name = remap_filename (pfile, name, path);
-
-      nd = splay_tree_lookup (pfile->all_include_files, (splay_tree_key) name);
-      if (nd && nd->value)
-       return 1;
+      if (f == file)
+       continue;
+
+      if (!f->pragma_once && !(f->import && import))
+       continue;
+
+      if (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))
+       return true;
     }
-  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.  */
+  return false;
+}
 
-static struct include_file *
-find_include_file (pfile, fname, search_start)
-     cpp_reader *pfile;
-     const char *fname;
-     struct file_name_list *search_start;
+/* Mark FILE to be included once only.  IMPORT is true if because of
+   #import, otherwise it is assumed to be #pragma once.  */
+void
+_cpp_mark_file_once_only (cpp_reader *pfile, _cpp_file *file, bool import)
 {
-  struct file_name_list *path;
-  char *name;
-  struct include_file *file;
-
-  if (IS_ABSOLUTE_PATHNAME (fname))
-    return open_file (pfile, fname);
-      
-  /* Search directory path for the file.  */
-  name = (char *) alloca (strlen (fname) + pfile->max_include_len
-                         + 2 + INCLUDE_LEN_FUDGE);
-  for (path = search_start; path; path = path->next)
+  if (import)
+    file->import = true;
+  else
     {
-      memcpy (name, path->name, path->nlen);
-      name[path->nlen] = '/';
-      strcpy (&name[path->nlen+1], fname);
-      _cpp_simplify_pathname (name);
-      if (CPP_OPTION (pfile, remap))
-       name = remap_filename (pfile, name, path);
-
-      file = open_file (pfile, name);
-      if (file)
-       {
-         file->foundhere = path;
-         return file;
-       }
+      pfile->saw_pragma_once = true;
+      file->pragma_once = true;
+    }
+
+  /* Put it on the once-only list if it's not on there already (an
+     earlier #include with a #pragma once might have put it on there
+     already).  */
+  if (file->once_only_next == NULL)
+    {
+      file->once_only_next = pfile->once_only_files;
+      pfile->once_only_files = file;
     }
-  return 0;
 }
 
-/* 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;
+/* Return the directory from which searching for FNAME should start,
+   condiering 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)
 {
-  int flags = 0;
+  cpp_dir *dir;
+  _cpp_file *file;
 
-  /* 1 = system header, 2 = system header to be treated as C.  */
-  if (syshdr)
-    flags = 1 + (externc != 0);
-  pfile->buffer->sysp = flags;
+  if (IS_ABSOLUTE_PATH (fname))
+    return &pfile->no_search_path;
+
+  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 && 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
+    return make_cpp_dir (pfile, dir_name_of_file (file), pfile->map->sysp);
+
+  if (dir == NULL)
+    cpp_error (pfile, DL_ERROR,
+              "no include path in which to search for %s", fname);
+
+  return dir;
 }
 
-/* Report on all files that might benefit from a multiple include guard.
-   Triggered by -H.  */
-void
-_cpp_report_missing_guards (pfile)
-     cpp_reader *pfile;
+/* Strip the basename from the file's path.  It ends with a slash if
+   of non-zero 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)
 {
-  int banner = 0;
-  splay_tree_foreach (pfile->all_include_files, report_missing_guard,
-                     (PTR) &banner);
+  if (!file->dir_name)
+    {
+      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;
+    }
+
+  return file->dir_name;
 }
 
-static int
-report_missing_guard (n, b)
-     splay_tree_node n;
-     void *b;
+/* Push an input buffer with the contents of FNAME, the empty string
+   for standard input.  Return true if a buffer was stacked.  */
+bool
+cpp_stack_file (cpp_reader *pfile, const char *fname)
 {
-  struct include_file *f = (struct include_file *) n->value;
-  int *bannerp = (int *)b;
+  struct cpp_dir *dir = &pfile->no_search_path;
 
-  if (f && f->cmacro == 0 && f->include_count == 1)
-    {
-      if (*bannerp == 0)
-       {
-         fputs (_("Multiple include guards may be useful for:\n"), stderr);
-         *bannerp = 1;
-       }
-      fputs (f->name, stderr);
-      putc ('\n', stderr);
-    }
-  return 0;
+  return stack_file (pfile, find_file (pfile, fname, dir, false), false);
 }
 
-#define PRINT_THIS_DEP(p, b) (CPP_PRINT_DEPS(p) > (b||p->system_include_depth))
-void
-_cpp_execute_include (pfile, header, no_reinclude, include_next)
-     cpp_reader *pfile;
-     const cpp_token *header;
-     int no_reinclude;
-     int 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_stack_include (cpp_reader *pfile, const char *fname, int angle_brackets,
+                   enum include_type type)
 {
-  struct file_name_list *search_start = 0;
-  unsigned int len = header->val.str.len;
-  unsigned int angle_brackets = header->type == CPP_HEADER_NAME;
-  struct include_file *inc;
-  char *fname;
-
-  /* Help protect #include or similar from recursion.  */
-  if (pfile->buffer_stack_depth >= CPP_STACK_MAX)
-    {
-      cpp_fatal (pfile, "#include nested too deeply");
-      return;
-    }
+  struct cpp_dir *dir;
 
-  /* 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;
-    }
+  dir = search_path_head (pfile, fname, angle_brackets, type);
+  if (!dir)
+    return false;
 
-  /* 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)
+  return stack_file (pfile, find_file (pfile, fname, dir, false),
+                    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->map ? pfile->map->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->path);
+  else
     {
-      if (! pfile->buffer->prev)
-       cpp_warning (pfile, "#include_next in primary source file");
+      /* 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, DL_WARNING, file->name);
       else
-       {
-         if (pfile->buffer->inc->foundhere)
-           {
-             search_start = pfile->buffer->inc->foundhere->next;
-             if (! search_start)
-               return;
-           }
-       }
+       cpp_errno (pfile, DL_ERROR, file->name);
     }
+}
 
-  fname = alloca (len + 1);
-  memcpy (fname, header->val.str.text, len);
-  fname[len] = '\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;
 
-  if (!search_start)
-    {
-      if (angle_brackets)
-       search_start = CPP_OPTION (pfile, bracket_include);
-      else if (CPP_OPTION (pfile, ignore_srcdir))
-       search_start = CPP_OPTION (pfile, quote_include);
-      else
-       search_start = CPP_BUFFER (pfile)->actual_dir;
+  return head;
+}
 
-      if (!search_start)
-       {
-         cpp_error (pfile, "No include path in which to find %s", fname);
-         return;
-       }
-    }
+/* Allocate a new _cpp_file structure.  */
+static _cpp_file *
+make_cpp_file (cpp_reader *pfile, cpp_dir *dir, const char *fname)
+{
+  _cpp_file *file;
 
-  inc = find_include_file (pfile, fname, search_start);
+  file = xcalloc (1, sizeof (_cpp_file));
+  file->main_file = !pfile->buffer;
+  file->fd = -1;
+  file->dir = dir;
+  file->name = xstrdup (fname);
 
-  if (inc)
-    {
-      /* For -M, add the file to the dependencies on its first inclusion. */
-      if (!inc->include_count && PRINT_THIS_DEP (pfile, angle_brackets))
-       deps_add_dep (pfile->deps, inc->name);
-      inc->include_count++;
+  return file;
+}
 
-      /* Actually process the file.  */
-      stack_include_file (pfile, inc);
+/* 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.
 
-      if (angle_brackets)
-       pfile->system_include_depth++;
+   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 (pfile->file_hash, 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;
+
+  /* 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;
+}
 
-      if (! DO_NOT_REREAD (inc))
-       {
-         if (no_reinclude)
-           _cpp_never_reread (inc);
+/* 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));
+}
 
-         /* Handle -H option.  */
-         if (CPP_OPTION (pfile, print_include_names))
-           {
-             cpp_buffer *fp = CPP_BUFFER (pfile);
-             while ((fp = CPP_PREV_BUFFER (fp)) != NULL)
-               putc ('.', stderr);
-             fprintf (stderr, " %s\n", inc->name);
-           }
-       }
+/* 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;
-    }
-      
-  if (CPP_OPTION (pfile, print_deps_missing_files)
-      && PRINT_THIS_DEP (pfile, angle_brackets))
+  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 (pfile->file_hash, fname);
+
+  while (entry && (entry->start_dir == NULL || entry->u.file->err_no))
+    entry = entry->next;
+
+  return entry != NULL;
+}
+
+/* Compare a string Q against a file hash entry P.  */
+static int
+hash_string_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, htab_hash_string, hash_string_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)
+{
+  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 (cpp_reader *pfile, int syshdr, int externc)
+{
+  int flags = 0;
+
+  /* 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);
+}
+
+/* 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 (cpp_reader *pfile, enum lc_reason reason,
+                const char *new_name)
+{
+  _cpp_do_file_change (pfile, reason, new_name, 1, 0);
+}
+
+/* Callback function for htab_traverse.  */
+static int
+report_missing_guard (void **slot, void *b)
+{
+  struct file_hash_entry *entry = (struct file_hash_entry *) *slot;
+  int *bannerp = (int *) b;
+
+  /* Skip directories.  */
+  if (entry->start_dir != NULL)
     {
-      if (!angle_brackets || IS_ABSOLUTE_PATHNAME (fname))
-       deps_add_dep (pfile->deps, fname);
-      else
-       {
-         char *p;
-         struct file_name_list *ptr;
-         int len;
-
-         /* If requested as a system header, assume it belongs in
-            the first system header directory. */
-         if (CPP_OPTION (pfile, bracket_include))
-           ptr = CPP_OPTION (pfile, bracket_include);
-         else
-           ptr = CPP_OPTION (pfile, quote_include);
+      _cpp_file *file = entry->u.file;
 
-         len = strlen (ptr->name);
-         p = (char *) alloca (len + strlen (fname) + 2);
-         if (len)
+      /* We don't want MI guard advice for the main file.  */
+      if (file->cmacro == NULL && file->stack_count == 1 && !file->main_file)
+       {
+         if (*bannerp == 0)
            {
-             memcpy (p, ptr->name, len);
-             p[len++] = '/';
+             fputs (_("Multiple include guards may be useful for:\n"),
+                    stderr);
+             *bannerp = 1;
            }
-         strcpy (p + len, fname);
-         _cpp_simplify_pathname (p);
-         deps_add_dep (pfile->deps, p);
+
+         fputs (entry->u.file->path, stderr);
+         putc ('\n', stderr);
        }
     }
-  /* 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_THIS_DEP (pfile, angle_brackets))
-    cpp_warning (pfile, "No include path in which to find %s", fname);
-  else
-    cpp_error_from_errno (pfile, fname);
+
+  return 0;
 }
 
-/* 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.  */
-int
-_cpp_compare_file_date (pfile, f)
-     cpp_reader *pfile;
-     const cpp_token *f;
+/* Report on all files that might benefit from a multiple include guard.
+   Triggered by -H.  */
+void
+_cpp_report_missing_guards (cpp_reader *pfile)
 {
-  unsigned int len = f->val.str.len;
-  char *fname;
-  struct file_name_list *search_start;
-  struct include_file *inc;
-
-  if (f->type == CPP_HEADER_NAME)
-    search_start = CPP_OPTION (pfile, bracket_include);
-  else if (CPP_OPTION (pfile, ignore_srcdir))
-    search_start = CPP_OPTION (pfile, quote_include);
-  else
-    search_start = CPP_BUFFER (pfile)->actual_dir;
+  int banner = 0;
 
-  fname = alloca (len + 1);
-  memcpy (fname, f->val.str.text, len);
-  fname[len] = '\0';
-  inc = find_include_file (pfile, fname, search_start);
-  
-  if (!inc)
-    return -1;
-  if (inc->fd > 0)
-    {
-      close (inc->fd);
-      inc->fd = -1;
-    }
-    
-  return inc->st.st_mtime > CPP_BUFFER (pfile)->inc->st.st_mtime;
+  htab_traverse (pfile->file_hash, report_missing_guard, &banner);
 }
 
-
-/* Push an input buffer and load it up with the contents of FNAME.
-   If FNAME is "" or NULL, read standard input.  */
+/* 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, return 1, otherwise 0.  */
 int
-_cpp_read_file (pfile, fname)
-     cpp_reader *pfile;
-     const char *fname;
+_cpp_compare_file_date (cpp_reader *pfile, const char *fname,
+                       int angle_brackets)
 {
-  struct include_file *f;
+  _cpp_file *file;
+  struct cpp_dir *dir;
 
-  if (fname == NULL)
-    fname = "";
+  dir = search_path_head (pfile, fname, angle_brackets, IT_INCLUDE);
+  if (!dir)
+    return -1;
 
-  f = open_file (pfile, fname);
+  file = 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 0;
+      close (file->fd);
+      file->fd = -1;
     }
 
-  stack_include_file (pfile, f);
-  return 1;
+  return file->st.st_mtime > pfile->buffer->file->st.st_mtime;
+}
+
+/* Pushes the given file onto the buffer stack.  Returns nonzero if
+   successful.  */
+bool
+cpp_push_include (cpp_reader *pfile, const char *fname)
+{
+  /* Make the command line directive take up a line.  */
+  pfile->line++;
+  return _cpp_stack_include (pfile, fname, false, IT_CMDLINE);
 }
 
-/* Do appropriate cleanup when a file buffer is popped off the input
-   stack.  */
+/* Do appropriate cleanup when a file INC's buffer is popped off the
+   input stack.  */
 void
-_cpp_pop_file_buffer (pfile, buf)
-     cpp_reader *pfile;
-     cpp_buffer *buf;
+_cpp_pop_file_buffer (cpp_reader *pfile, _cpp_file *file)
 {
-  struct include_file *inc = buf->inc;
+  /* Record the inclusion-preventing macro, which could be NULL
+     meaning no controlling macro.  */
+  if (pfile->mi_valid && file->cmacro == NULL)
+    file->cmacro = pfile->mi_cmacro;
 
-  if (pfile->system_include_depth)
-    pfile->system_include_depth--;
-  if (pfile->include_depth)
-    pfile->include_depth--;
+  /* Invalidate control macros in the #including file.  */
+  pfile->mi_valid = false;
 
-  /* Record the inclusion-preventing macro and its definedness.  */
-  if (pfile->mi_state == MI_OUTSIDE && inc->cmacro != NEVER_REREAD)
+  if (file->buffer)
     {
-      /* This could be NULL meaning no controlling macro.  */
-      inc->cmacro = pfile->mi_cmacro;
-      inc->defined = 1;
+      free ((void *) file->buffer);
+      file->buffer = NULL;
     }
-
-  /* Invalidate control macros in the #including file.  */
-  pfile->mi_state = MI_FAILED;
-
-  inc->refcnt--;
-  if (inc->refcnt == 0 && DO_NOT_REREAD (inc))
-    purge_cache (inc);
 }
 
-/* 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.  */
+/* 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.
 
-struct file_name_map
+   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)
 {
-  struct file_name_map *map_next;
-  char *map_from;
-  char *map_to;
-};
+  pfile->quote_include = quote;
+  pfile->bracket_include = quote;
+  pfile->quote_ignores_source_dir = quote_ignores_source_dir;
 
-#define FILE_NAME_MAP_FILE "header.gcc"
+  for (; quote; quote = quote->next)
+    {
+      quote->name_map = NULL;
+      quote->len = strlen (quote->name);
+      if (quote == bracket)
+       pfile->bracket_include = bracket;
+    }
+}
 
-/* Read a space delimited string of unlimited length from a stdio
-   file.  */
+/* 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)
+{
+  size_t dlen, flen;
+  char *path;
+
+  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);
+
+  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)
            {
@@ -848,334 +1073,144 @@ 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)
 {
-  register 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;
-}  
-
-/* Remap NAME based on the file_name_map (if any) for LOC. */
+  /* Terminate the list of maps.  */
+  dir->name_map[count] = NULL;
+}
 
+/* 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 file_name_list *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;
+
+  dir = file->dir;
+  fname = file->name;
 
-  if (! loc->name_map)
+  for (;;)
     {
-      loc->name_map = read_name_map (pfile, loc->name ? loc->name : ".");
-      if (! loc->name_map)
-       return name;
-    }
-  
-  from = name + strlen (loc->name) + 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;
+      if (!dir->name_map)
+       read_name_map (dir);
 
-  return name;
-}
+      for (index = 0; dir->name_map[index]; index += 2)
+       if (!strcmp (dir->name_map[index], fname))
+           return xstrdup (dir->name_map[index + 1]);
 
-/* Given a path FNAME, extract the directory component and place it
-   onto the actual_dirs list.  Return a pointer to the allocated
-   file_name_list structure.  These structures are used to implement
-   current-directory "" include searching. */
+      p = strchr (fname, '/');
+      if (!p || p == fname)
+       return NULL;
 
-static struct file_name_list *
-actual_directory (pfile, fname)
-     cpp_reader *pfile;
-     const char *fname;
-{
-  char *last_slash, *dir;
-  size_t dlen;
-  struct file_name_list *x;
-  
-  dir = xstrdup (fname);
-  last_slash = strrchr (dir, '/');
-  if (last_slash)
-    {
-      if (last_slash == dir)
-        {
-         dlen = 1;
-         last_slash[1] = '\0';
-       }
-      else
-       {
-         dlen = last_slash - dir;
-         *last_slash = '\0';
-       }
-    }
-  else
-    {
-      free (dir);
-      dir = xstrdup (".");
-      dlen = 1;
-    }
+      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';
 
-  if (dlen > pfile->max_include_len)
-    pfile->max_include_len = dlen;
-
-  for (x = pfile->actual_dirs; x; x = x->alloc)
-    if (!strcmp (x->name, dir))
-      {
-       free (dir);
-       return x;
-      }
-
-  /* Not found, make a new one. */
-  x = (struct file_name_list *) xmalloc (sizeof (struct file_name_list));
-  x->name = dir;
-  x->nlen = dlen;
-  x->next = CPP_OPTION (pfile, quote_include);
-  x->alloc = pfile->actual_dirs;
-  x->sysp = pfile->buffer->sysp;
-  x->name_map = NULL;
-
-  pfile->actual_dirs = x;
-  return x;
+      dir = make_cpp_dir (pfile, new_dir, dir->sysp);
+      fname = p + 1;
+    }
 }
 
-/* Simplify a path name in place, deleting redundant components.  This
-   reduces OS overhead and guarantees that equivalent paths compare
-   the same (modulo symlinks).
-
-   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)
+/* Return true if FILE is usable by PCH.  */
+static bool
+include_pch_p (_cpp_file *file)
+{
+  return file->pch & 1;
+}
 
-   Guarantees no trailing slashes. All transforms reduce the length
-   of the string.
- */
-void
-_cpp_simplify_pathname (path)
-    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)
 {
-    char *from, *to;
-    char *base;
-    int absolute = 0;
-
-#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 initial /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++;
-       }
-    }
-    base = to;
-    
-    for (;;)
+  const char *saved_path = file->path;
+
+  file->path = pchname;
+  if (open_file (file))
     {
-       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] == '/')
+      if ((file->pch & 2) == 0)
+       file->pch = pfile->cb.valid_pch (pfile, pchname, file->fd);
+
+      if (!include_pch_p (file))
        {
-           if (base == to)
-           {
-               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;
-           }
+         close (file->fd);
+         file->fd = -1;
        }
-       else if (from[0] == '.' && from[1] == '.' && from[2] == '\0')
+
+      if (CPP_OPTION (pfile, print_include_names))
        {
-           if (base == to)
-           {
-               if (!absolute)
-               {
-                   *to++ = *from++;
-                   *to++ = *from++;
-               }
-           }
-           else
-           {
-               to -= 2;
-               while (to > base && *to != '/') to--;
-               if (*to == '/')
-                   to++;
-           }
-           goto done;
+         unsigned int i;
+         for (i = 1; i < pfile->line_maps.depth; i++)
+           putc ('.', stderr);
+         fprintf (stderr, "%c %s\n",
+                  include_pch_p (file) ? '!' : 'x', pchname);
        }
-       else
-           /* Copy this component and trailing /, if any.  */
-           while ((*to++ = *from++) != '/')
-           {
-               if (!to[-1])
-               {
-                   to--;
-                   goto done;
-               }
-           }
-       
     }
-    
- 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;
+  else
+    file->pch = 2;
+
+  file->path = saved_path;
+  return include_pch_p (file);
 }