OSDN Git Service

* reload1.c (move2add_last_cc0): New.
[pf3gnuchains/gcc-fork.git] / gcc / cppfiles.c
index 94562ab..1ff34ff 100644 (file)
@@ -1,5 +1,6 @@
 /* Part of CPP library.  (include file handling)
-   Copyright (C) 1986, 87, 89, 92 - 95, 1998 Free Software Foundation, Inc.
+   Copyright (C) 1986, 1987, 1989, 1992, 1993, 1994, 1995, 1998,
+   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
@@ -17,344 +18,974 @@ GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
-Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
- In other words, you are welcome to use, share and improve this program.
- You are forbidden to forbid anyone else to use, share and improve
- what you give them.   Help stamp out software-hoarding!  */
+Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 #include "config.h"
 #include "system.h"
+#include <dirent.h>
+#include "coretypes.h"
+#include "tm.h"
 #include "cpplib.h"
+#include "cpphash.h"
+#include "intl.h"
+#include "mkdeps.h"
+#include "splay-tree.h"
+#ifdef ENABLE_VALGRIND_CHECKING
+# ifdef HAVE_MEMCHECK_H
+# include <memcheck.h>
+# else
+# include <valgrind.h>
+# endif
+#else
+/* Avoid #ifdef:s when we can help it.  */
+#define VALGRIND_DISCARD(x)
+#endif
+
+#ifdef HAVE_MMAP_FILE
+# include <sys/mman.h>
+# ifndef MMAP_THRESHOLD
+#  define MMAP_THRESHOLD 3 /* Minimum page count to mmap the file.  */
+# endif
+# if MMAP_THRESHOLD
+#  define TEST_THRESHOLD(size, pagesize) \
+     (size / pagesize >= MMAP_THRESHOLD && (size % pagesize) != 0)
+   /* Use mmap if the file is big enough to be worth it (controlled
+      by MMAP_THRESHOLD) and if we can safely count on there being
+      at least one readable NUL byte after the end of the file's
+      contents.  This is true for all tested operating systems when
+      the file size is not an exact multiple of the page size.  */
+#  ifndef __CYGWIN__
+#   define SHOULD_MMAP(size, pagesize) TEST_THRESHOLD (size, pagesize)
+#  else
+#   define WIN32_LEAN_AND_MEAN
+#   include <windows.h>
+    /* Cygwin can't correctly emulate mmap under Windows 9x style systems so
+       disallow use of mmap on those systems.  Windows 9x does not zero fill
+       memory at EOF and beyond, as required.  */
+#   define SHOULD_MMAP(size, pagesize) ((GetVersion() & 0x80000000) \
+                                       ? 0 : TEST_THRESHOLD (size, pagesize))
+#  endif
+# endif
+
+#else  /* No MMAP_FILE */
+#  undef MMAP_THRESHOLD
+#  define MMAP_THRESHOLD 0
+#endif
+
+#ifndef O_BINARY
+# define O_BINARY 0
+#endif
 
-/* The entry points to this file are: find_include_file, finclude,
-   append_include_chain, deps_output, and file_cleanup.
-   file_cleanup is only called through CPP_BUFFER(pfile)->cleanup,
-   so it's static anyway. */
-
-static void add_import                 PROTO ((cpp_reader *, int, char *));
-static int lookup_import               PROTO ((cpp_reader *, char *,
-                                               struct file_name_list *));
-static int redundant_include_p         PROTO ((cpp_reader *, char *));
-static struct file_name_map *read_name_map     PROTO ((cpp_reader *, char *));
-static char *read_filename_string      PROTO ((int, FILE *));
-static int open_include_file           PROTO ((cpp_reader *, char *,
-                                               struct file_name_list *));
-static int safe_read                   PROTO ((int, char *, int));
-
-/* Not safe to prototype these. */
-extern char *xmalloc();
-extern char *xrealloc();
-
-/* Append a chain of `struct file_name_list's
-   to the end of the main include chain.
-   FIRST is the beginning of the chain to append, and LAST is the end.  */
+/* 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
+
+/* 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 {
+  const char *name;            /* actual path name of file */
+  const char *header_name;     /* the original header found */
+  const cpp_hashnode *cmacro;  /* macro, if any, preventing reinclusion.  */
+  const struct cpp_path *foundhere;
+                               /* location in search path where file was
+                                  found, for #include_next and sysp.  */
+  const unsigned char *buffer; /* pointer to cached file contents */
+  struct stat st;              /* copy of stat(2) data for file */
+  int fd;                      /* fd open on file (short term storage only) */
+  int err_no;                  /* errno obtained if opening a file failed */
+  unsigned short include_count;        /* number of times file has been read */
+  unsigned short refcnt;       /* number of stacked buffers using this file */
+  unsigned char mapped;                /* file buffer is mmapped */
+  unsigned char pch;           /* 0: file not known to be a PCH.
+                                  1: file is a PCH 
+                                     (on return from find_include_file).
+                                  2: file is not and never will be a valid
+                                     precompiled header.
+                                  3: file is always a valid precompiled
+                                     header.  */
+};
+
+/* Variable length record files on VMS will have a stat size that includes
+   record control characters that won't be included in the read size.  */
+#ifdef VMS
+# define FAB_C_VAR 2 /* variable length records (see Starlet fabdef.h) */
+# define STAT_SIZE_TOO_BIG(ST) ((ST).st_fab_rfm == FAB_C_VAR)
+#else
+# define STAT_SIZE_TOO_BIG(ST) 0
+#endif
 
+/* The cmacro works like this: If it's NULL, the file is to be
+   included again.  If it's NEVER_REREAD, the file is never to be
+   included again.  Otherwise it is a macro hashnode, and the file is
+   to be included again if the macro is defined.  */
+#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)
+#define INCLUDE_PCH_P(F) (((F)->pch & 1) != 0)
+
+static struct file_name_map *read_name_map
+                               PARAMS ((cpp_reader *, const char *));
+static char *read_filename_string PARAMS ((int, FILE *));
+static char *remap_filename    PARAMS ((cpp_reader *, char *,
+                                        struct cpp_path *));
+static struct cpp_path *search_from PARAMS ((cpp_reader *,
+                                               enum include_type));
+static struct include_file *
+       find_include_file PARAMS ((cpp_reader *, const cpp_token *,
+                                  enum include_type));
+static struct include_file *open_file PARAMS ((cpp_reader *, const char *));
+static struct include_file *validate_pch PARAMS ((cpp_reader *,
+                                                 const char *,
+                                                 const char *));
+static struct include_file *open_file_pch PARAMS ((cpp_reader *, 
+                                                  const char *));
+static int read_include_file   PARAMS ((cpp_reader *, struct include_file *));
+static bool stack_include_file PARAMS ((cpp_reader *, struct include_file *));
+static void purge_cache        PARAMS ((struct include_file *));
+static void destroy_node       PARAMS ((splay_tree_value));
+static int report_missing_guard                PARAMS ((splay_tree_node, void *));
+static splay_tree_node find_or_create_entry PARAMS ((cpp_reader *,
+                                                    const char *));
+static void handle_missing_header PARAMS ((cpp_reader *, const char *, int));
+static int remove_component_p PARAMS ((const char *));
+
+/* Set up the splay tree we use to store information about all the
+   file names seen in this compilation.  We also have entries for each
+   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_path.  The path name may or may not be absolute.
+   The path string has been malloced, as is automatically freed by
+   registering free () as the splay tree key deletion function.
+
+   A node's value is a pointer to a struct include_file, and is never
+   NULL.  */
 void
-append_include_chain (pfile, first, last)
+_cpp_init_includes (pfile)
      cpp_reader *pfile;
-     struct file_name_list *first, *last;
 {
-  struct cpp_options *opts = CPP_OPTIONS (pfile);
-  struct file_name_list *dir;
-
-  if (!first || !last)
-    return;
+  pfile->all_include_files
+    = splay_tree_new ((splay_tree_compare_fn) strcmp,
+                     (splay_tree_delete_key_fn) free,
+                     destroy_node);
+}
 
-  if (opts->include == 0)
-    opts->include = first;
-  else
-    opts->last_include->next = first;
+/* Tear down the splay tree.  */
+void
+_cpp_cleanup_includes (pfile)
+     cpp_reader *pfile;
+{
+  splay_tree_delete (pfile->all_include_files);
+}
 
-  if (opts->first_bracket_include == 0)
-    opts->first_bracket_include = first;
+/* 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;
 
-  for (dir = first; ; dir = dir->next) {
-    int len = strlen (dir->fname) + INCLUDE_LEN_FUDGE;
-    if (len > pfile->max_include_len)
-      pfile->max_include_len = len;
-    if (dir == last)
-      break;
-  }
+  if (f)
+    {
+      purge_cache (f);
+      free (f);
+    }
+}
 
-  last->next = NULL;
-  opts->last_include = last;
+/* 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;
 }
 
-/* Add output to `deps_buffer' for the -M switch.
-   STRING points to the text to be output.
-   SPACER is ':' for targets, ' ' for dependencies, zero for text
-   to be inserted literally.  */
+/* Lookup a filename, which is simplified after making a copy, and
+   create an entry if none exists.  */
+static splay_tree_node
+find_or_create_entry (pfile, fname)
+     cpp_reader *pfile;
+     const char *fname;
+{
+  splay_tree_node node;
+  struct include_file *file;
+  char *name = xstrdup (fname);
+
+  cpp_simplify_path (name);
+  node = splay_tree_lookup (pfile->all_include_files, (splay_tree_key) name);
+  if (node)
+    free (name);
+  else
+    {
+      file = xcnew (struct include_file);
+      file->name = name;
+      file->header_name = name;
+      file->err_no = errno;
+      node = splay_tree_insert (pfile->all_include_files,
+                               (splay_tree_key) file->name,
+                               (splay_tree_value) file);
+    }
+
+  return node;
+}
 
+/* Enter a file name in the splay tree, for the sake of cpp_included.  */
 void
-deps_output (pfile, string, spacer)
+_cpp_fake_include (pfile, fname)
      cpp_reader *pfile;
-     char *string;
-     int spacer;
+     const char *fname;
 {
-  int size;
+  find_or_create_entry (pfile, fname);
+}
 
-  if (!*string)
-    return;
+/* 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.  */
+static struct include_file *
+open_file (pfile, filename)
+     cpp_reader *pfile;
+     const char *filename;
+{
+  splay_tree_node nd = find_or_create_entry (pfile, filename);
+  struct include_file *file = (struct include_file *) nd->value;
 
-#ifdef VMS
-  hack_vms_include_specification (string);
-#endif
+  if (file->err_no)
+    {
+      /* Ugh.  handle_missing_header () needs errno to be set.  */
+      errno = file->err_no;
+      return 0;
+    }
+
+  /* Don't reopen an idempotent file.  */
+  if (DO_NOT_REREAD (file))
+    return file;
+
+  /* Don't reopen one which is already loaded.  */
+  if (file->buffer != NULL)
+    return file;
+
+  /* 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).
 
-  size = strlen (string);
+     Use the three-argument form of open even though we aren't
+     specifying O_CREAT, to defend against broken system headers.
 
-#ifndef MAX_OUTPUT_COLUMNS
-#define MAX_OUTPUT_COLUMNS 72
+     O_BINARY tells some runtime libraries (notably DJGPP) not to do
+     newline translation; we can handle DOS line breaks just fine
+     ourselves.
+
+     Special case: the empty string is translated to stdin.  */
+
+  if (filename[0] == '\0')
+    {
+      file->fd = 0;
+#ifdef __DJGPP__
+      /* For DJGPP redirected input is opened in text mode. Change it
+         to binary mode.  */
+      if (! isatty (file->fd))
+       setmode (file->fd, O_BINARY);
 #endif
-  if (spacer
-      && pfile->deps_column > 0
-      && (pfile->deps_column + size) > MAX_OUTPUT_COLUMNS)
+    }
+  else
+    file->fd = open (file->name, O_RDONLY | O_NOCTTY | O_BINARY, 0666);
+
+  if (file->fd != -1 && fstat (file->fd, &file->st) == 0)
     {
-      deps_output (pfile, " \\\n  ", 0);
-      pfile->deps_column = 0;
+      if (!S_ISDIR (file->st.st_mode))
+       return file;
+
+      /* If it's a directory, we return null and continue the search
+        as the file we're looking for may appear elsewhere in the
+        search path.  */
+      errno = ENOENT;
+      close (file->fd);
+      file->fd = -1;
     }
 
-  if (pfile->deps_size + size + 8 > pfile->deps_allocated_size)
+  file->err_no = errno;
+  return 0;
+}
+
+static struct include_file *
+validate_pch (pfile, filename, pchname)
+     cpp_reader *pfile;
+     const char *filename;
+     const char *pchname;
+{
+  struct include_file * file;
+  
+  file = open_file (pfile, pchname);
+  if (file == NULL)
+    return NULL;
+  if ((file->pch & 2) == 0)
+    file->pch = pfile->cb.valid_pch (pfile, pchname, file->fd);
+  if (INCLUDE_PCH_P (file))
     {
-      pfile->deps_allocated_size = (pfile->deps_size + size + 50) * 2;
-      pfile->deps_buffer = (char *) xrealloc (pfile->deps_buffer,
-                                             pfile->deps_allocated_size);
+      char *f = xstrdup (filename);
+      cpp_simplify_path (f);
+      file->header_name = f;
+      return file;
     }
-  if (spacer == ' ' && pfile->deps_column > 0)
-    pfile->deps_buffer[pfile->deps_size++] = ' ';
-  bcopy (string, &pfile->deps_buffer[pfile->deps_size], size);
-  pfile->deps_size += size;
-  pfile->deps_column += size;
-  if (spacer == ':')
-    pfile->deps_buffer[pfile->deps_size++] = ':';
-  pfile->deps_buffer[pfile->deps_size] = 0;
+  close (file->fd);
+  file->fd = -1;
+  return NULL;
 }
 
-static int
-file_cleanup (pbuf, pfile)
-     cpp_buffer *pbuf;
-     cpp_reader *pfile ATTRIBUTE_UNUSED;
+
+/* Like open_file, but also look for a precompiled header if (a) one exists
+   and (b) it is valid.  */
+static struct include_file *
+open_file_pch (pfile, filename)
+     cpp_reader *pfile;
+     const char *filename;
 {
-  if (pbuf->buf)
+  if (filename[0] != '\0'
+      && pfile->cb.valid_pch != NULL)
     {
-      free (pbuf->buf);
-      pbuf->buf = 0;
+      size_t namelen = strlen (filename);
+      char *pchname = alloca (namelen + 5);
+      struct include_file * file;
+      splay_tree_node nd;
+      
+      memcpy (pchname, filename, namelen);
+      memcpy (pchname + namelen, ".gch", 5);
+
+      nd = find_or_create_entry (pfile, pchname);
+      file = (struct include_file *) nd->value;
+
+      if (file != NULL)
+       {
+         if (stat (file->name, &file->st) == 0 && S_ISDIR (file->st.st_mode))
+           {
+             DIR * thedir;
+             struct dirent *d;
+             size_t subname_len = namelen + 64;
+             char *subname = xmalloc (subname_len);
+             
+             thedir = opendir (pchname);
+             if (thedir == NULL)
+               return NULL;
+             memcpy (subname, pchname, namelen + 4);
+             subname[namelen+4] = '/';
+             while ((d = readdir (thedir)) != NULL)
+               {
+                 if (strlen (d->d_name) + namelen + 7 > subname_len)
+                   {
+                     subname_len = strlen (d->d_name) + namelen + 64;
+                     subname = xrealloc (subname, subname_len);
+                   }
+                 strcpy (subname + namelen + 5, d->d_name);
+                 file = validate_pch (pfile, filename, subname);
+                 if (file)
+                   break;
+               }
+             closedir (thedir);
+             free (subname);
+           }
+         else
+           file = validate_pch (pfile, filename, pchname);
+         if (file)
+           return file;
+       }
     }
-  return 0;
+  return open_file (pfile, filename);
 }
 
-int
-find_include_file (pfile, fbeg, flen, fname,
-                  importing, search_start, foundhere)
+/* Place the file referenced by INC into a new buffer on the buffer
+   stack, unless there are errors, or the file is not re-included
+   because of e.g. multiple-include guards.  Returns true if a buffer
+   is stacked.  */
+static bool
+stack_include_file (pfile, inc)
      cpp_reader *pfile;
-     char *fbeg;
-     unsigned long flen;
-     char *fname;
-     int importing;
-     struct file_name_list *search_start;
-     struct file_name_list **foundhere;
+     struct include_file *inc;
 {
-  struct file_name_list *searchptr;
-  int f;
-    
-  /* If specified file name is absolute, just open it.  */
-
-  if (*fbeg == '/')
-  {
-    strcpy (fname, fbeg);
-#ifdef VMS
-    hack_vms_include_specification (fname);
+  cpp_buffer *fp;
+  int sysp;
+  const char *filename;
+
+  if (DO_NOT_REREAD (inc))
+    return false;
+
+  sysp = MAX ((pfile->map ? pfile->map->sysp : 0),
+             (inc->foundhere ? inc->foundhere->sysp : 0));
+
+  /* Add the file to the dependencies on its first inclusion.  */
+  if (CPP_OPTION (pfile, deps.style) > !!sysp && !inc->include_count)
+    {
+      if (pfile->buffer || CPP_OPTION (pfile, deps.ignore_main_file) == 0)
+       deps_add_dep (pfile->deps, inc->name);
+    }
+
+  /* PCH files get dealt with immediately.  */
+  if (INCLUDE_PCH_P (inc))
+    {
+      pfile->cb.read_pch (pfile, inc->name, inc->fd, inc->header_name);
+      close (inc->fd);
+      inc->fd = -1;
+      return false;
+    }
+
+  /* Not in cache?  */
+  if (! inc->buffer)
+    {
+      if (read_include_file (pfile, inc))
+       {
+         /* If an error occurs, do not try to read this file again.  */
+         _cpp_never_reread (inc);
+         return false;
+       }
+      /* Mark a regular, zero-length file never-reread.  We read it,
+        NUL-terminate it, and stack it once, so preprocessing a main
+        file of zero length does not raise an error.  */
+      if (S_ISREG (inc->st.st_mode) && inc->st.st_size == 0)
+       _cpp_never_reread (inc);
+      close (inc->fd);
+      inc->fd = -1;
+    }
+
+  if (pfile->buffer)
+    /* We don't want MI guard advice for the main file.  */
+    inc->include_count++;
+
+  /* Push a buffer.  */
+  fp = cpp_push_buffer (pfile, inc->buffer, inc->st.st_size,
+                       /* from_stage3 */ CPP_OPTION (pfile, preprocessed), 0);
+  fp->inc = inc;
+  fp->inc->refcnt++;
+
+  /* Initialize controlling macro state.  */
+  pfile->mi_valid = true;
+  pfile->mi_cmacro = 0;
+
+  /* Generate the call back.  */
+  filename = inc->name;
+  if (*filename == '\0')
+    filename = "<stdin>";
+  _cpp_do_file_change (pfile, LC_ENTER, filename, 1, sysp);
+
+  return true;
+}
+
+/* Read the file referenced by INC into the file cache.
+
+   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.
+
+   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.
+
+   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;
+{
+  ssize_t size, offset, count;
+  uchar *buf;
+#if MMAP_THRESHOLD
+  static int pagesize = -1;
 #endif
-    if (redundant_include_p (pfile, fname))
-      return -2;
-    if (importing)
-      f = lookup_import (pfile, fname, NULL_PTR);
-    else
-      f = open_include_file (pfile, fname, NULL_PTR);
-    if (f == -2)
-      return -2;       /* Already included this file */
-  }
+
+  if (S_ISREG (inc->st.st_mode))
+    {
+      /* 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
+        space.  We can't handle a file that large.  (Anyone with
+        a single source file bigger than 2GB needs to rethink
+        their coding style.)  Some systems (e.g. AIX 4.1) define
+        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, DL_ERROR, "%s is too large", inc->name);
+         goto fail;
+       }
+      size = inc->st.st_size;
+
+      inc->mapped = 0;
+#if MMAP_THRESHOLD
+      if (pagesize == -1)
+       pagesize = getpagesize ();
+
+      if (SHOULD_MMAP (size, pagesize))
+       {
+         buf = (uchar *) mmap (0, size, PROT_READ, MAP_PRIVATE, inc->fd, 0);
+         if (buf == (uchar *) -1)
+           goto perror_fail;
+
+         /* We must tell Valgrind that the byte at buf[size] is actually
+            readable.  Discard the handle to avoid handle leak.  */
+         VALGRIND_DISCARD (VALGRIND_MAKE_READABLE (buf + size, 1));
+
+         inc->mapped = 1;
+       }
+      else
+#endif
+       {
+         buf = (uchar *) 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_error (pfile, DL_WARNING,
+                              "%s is shorter than expected", inc->name);
+                 size = offset;
+                 buf = xrealloc (buf, size + 1);
+                 inc->st.st_size = size;
+                 break;
+               }
+             offset += count;
+           }
+         /* The lexer requires that the buffer be NUL-terminated.  */
+         buf[size] = '\0';
+       }
+    }
+  else if (S_ISBLK (inc->st.st_mode))
+    {
+      cpp_error (pfile, DL_ERROR, "%s is a block device", inc->name);
+      goto fail;
+    }
   else
-  {
-    /* Search directory path, trying to open the file.
-       Copy each filename tried into FNAME.  */
+    {
+      /* 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 = (uchar *) xmalloc (size + 1);
+      offset = 0;
+      while ((count = read (inc->fd, buf + offset, size - offset)) > 0)
+       {
+         offset += count;
+         if (offset == size)
+           {
+             size *= 2;
+             buf = xrealloc (buf, size + 1);
+           }
+       }
+      if (count < 0)
+       goto perror_fail;
+
+      if (offset + 1 < size)
+       buf = xrealloc (buf, offset + 1);
 
-    for (searchptr = search_start; searchptr; searchptr = searchptr->next)
+      /* The lexer requires that the buffer be NUL-terminated.  */
+      buf[offset] = '\0';
+      inc->st.st_size = offset;
+    }
+
+  inc->buffer = buf;
+  return 0;
+
+ perror_fail:
+  cpp_errno (pfile, DL_ERROR, inc->name);
+ fail:
+  return 1;
+}
+
+/* Drop INC's buffer from memory, if we are unlikely to need it again.  */
+static void
+purge_cache (inc)
+     struct include_file *inc;
+{
+  if (inc->buffer)
     {
-      unsigned int l = 0;
-      if (searchptr->fname)
-      {
-       /* The empty string in a search path is ignored.
-          This makes it possible to turn off entirely
-          a standard piece of the list.  */
-       if (searchptr->fname[0] == 0)
-         continue;
-
-       l = strlen (searchptr->fname);
-
-       bcopy (searchptr->fname, fname, l);
-       fname[l++] = '/';
-      }
-
-      bcopy (fbeg, &fname[l], flen);
-      fname[flen+l] = '\0';
-#ifdef VMS
-      hack_vms_include_specification (fname);
-#endif /* VMS */
-      /* ??? There are currently 3 separate mechanisms for avoiding processing
-        of redundant include files: #import, #pragma once, and
-        redundant_include_p.  It would be nice if they were unified.  */
-      if (redundant_include_p (pfile, fname))
-       return -2;
-      if (importing)
-       f = lookup_import (pfile, fname, searchptr);
+#if MMAP_THRESHOLD
+      if (inc->mapped)
+       {
+         /* Undo the previous annotation for the
+            known-zero-byte-after-mmap.  Discard the handle to avoid
+            handle leak.  */
+         VALGRIND_DISCARD (VALGRIND_MAKE_NOACCESS (inc->buffer
+                                                   + inc->st.st_size, 1));
+         munmap ((PTR) inc->buffer, inc->st.st_size);
+       }
       else
-       f = open_include_file (pfile, fname, searchptr);
-      if (f == -2)
-       return -2;                      /* Already included this file */
-#ifdef EACCES
-      else if (f == -1 && errno == EACCES)
-       cpp_warning (pfile, "Header file %s exists, but is not readable",
-                    fname);
 #endif
-      if (f >= 0)
-       break;
+       free ((PTR) inc->buffer);
+      inc->buffer = NULL;
+    }
+}
+
+/* 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;
+{
+  struct cpp_path *path;
+  char *name, *n;
+  splay_tree_node nd;
+
+  if (IS_ABSOLUTE_PATHNAME (fname))
+    {
+      /* Just look it up.  */
+      nd = splay_tree_lookup (pfile->all_include_files, (splay_tree_key) fname);
+      return (nd && nd->value);
     }
-  }
 
-  if (f < 0)
+  /* Search directory path for the file.  */
+  name = (char *) alloca (strlen (fname) + pfile->max_include_len + 2);
+  for (path = pfile->quote_include; path; path = path->next)
     {
-      /* A file that was not found.  */
-      bcopy (fbeg, fname, flen);
-      fname[flen] = 0;
+      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;
 
-      return -1;
+      nd = splay_tree_lookup (pfile->all_include_files, (splay_tree_key) n);
+      if (nd && nd->value)
+       return 1;
     }
+  return 0;
+}
+
+/* Search for HEADER.  Return 0 if there is no such file (or it's
+   un-openable), in which case an error code will be in errno.  If
+   there is no include path to use it returns NO_INCLUDE_PATH,
+   otherwise an include_file structure.  If this request originates
+   from a directive of TYPE #include_next, set INCLUDE_NEXT to true.  */
+static struct include_file *
+find_include_file (pfile, header, type)
+     cpp_reader *pfile;
+     const cpp_token *header;
+     enum include_type type;
+{
+  const char *fname = (const char *) header->val.str.text;
+  struct cpp_path *path;
+  struct include_file *file;
+  char *name, *n;
+
+  if (IS_ABSOLUTE_PATHNAME (fname))
+    return open_file_pch (pfile, fname);
+
+  /* For #include_next, skip in the search path past the dir in which
+     the current file was found, but if it was found via an absolute
+     path use the normal search logic.  */
+  if (type == IT_INCLUDE_NEXT && pfile->buffer->inc->foundhere)
+    path = pfile->buffer->inc->foundhere->next;
+  else if (header->type == CPP_HEADER_NAME)
+    path = pfile->bracket_include;
   else
+    path = search_from (pfile, type);
+
+  if (path == NULL)
     {
-      /* Check to see if this include file is a once-only include file.
-        If so, give up.  */
+      cpp_error (pfile, DL_ERROR, "no include path in which to find %s",
+                fname);
+      return NO_INCLUDE_PATH;
+    }
 
-      struct file_name_list *ptr;
+  /* Search directory path for the file.  */
+  name = (char *) alloca (strlen (fname) + pfile->max_include_len + 2);
+  for (; path; path = path->next)
+    {
+      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);
+      else
+       n = name;
 
-      for (ptr = pfile->dont_repeat_files; ptr; ptr = ptr->next)
-         if (!strcmp (ptr->fname, fname))
-           {
-             close (f);
-             return -2;                /* This file was once'd.  */
-           }
+      file = open_file_pch (pfile, n);
+      if (file)
+       {
+         file->foundhere = path;
+         return file;
+       }
     }
 
-    /* Record file on "seen" list for #import.  */
-    add_import (pfile, f, fname);
+  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;
+{
+  int flags = 0;
 
-    *foundhere = searchptr;
-    return f;
+  /* 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);
 }
 
-/* Return nonzero if there is no need to include file NAME
-   because it has already been included and it contains a conditional
-   to make a repeated include do nothing.  */
+/* Allow the client to change the current file.  Used by the front end
+   to achieve pseudo-file names like <built-in>.
+   If REASON is LC_LEAVE, then NEW_NAME must be NULL.  */
+void
+cpp_change_file (pfile, reason, new_name)
+     cpp_reader *pfile;
+     enum lc_reason reason;
+     const char *new_name;
+{
+  _cpp_do_file_change (pfile, reason, new_name, 1, 0);
+}
 
-static int
-redundant_include_p (pfile, name)
+/* Report on all files that might benefit from a multiple include guard.
+   Triggered by -H.  */
+void
+_cpp_report_missing_guards (pfile)
      cpp_reader *pfile;
-     char *name;
 {
-  struct file_name_list *l = pfile->all_include_files;
-  for (; l; l = l->next)
-    if (! strcmp (name, l->fname)
-       && l->control_macro
-       && cpp_lookup (pfile, l->control_macro, -1, -1))
-      return 1;
+  int banner = 0;
+  splay_tree_foreach (pfile->all_include_files, report_missing_guard,
+                     (PTR) &banner);
+}
+
+/* Callback function for splay_tree_foreach().  */
+static int
+report_missing_guard (n, b)
+     splay_tree_node n;
+     void *b;
+{
+  struct include_file *f = (struct include_file *) n->value;
+  int *bannerp = (int *) b;
+
+  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;
 }
 
+/* Create a dependency for file FNAME, or issue an error message as
+   appropriate.  ANGLE_BRACKETS is nonzero if the file was bracketed
+   like <..>.  */
+static void
+handle_missing_header (pfile, fname, angle_brackets)
+     cpp_reader *pfile;
+     const char *fname;
+     int angle_brackets;
+{
+  bool print_dep
+    = CPP_OPTION (pfile, deps.style) > (angle_brackets || pfile->map->sysp);
+
+  if (CPP_OPTION (pfile, deps.missing_files) && print_dep)
+    deps_add_dep (pfile->deps, fname);
+  /* If -M was specified, then don't count this as an error, because
+     we can still produce correct output.  Otherwise, we can't produce
+     correct output, because there may be dependencies we need inside
+     the missing file, and we don't know what directory this missing
+     file exists in.  */
+  else
+    cpp_errno (pfile, CPP_OPTION (pfile, deps.style) && ! print_dep
+              ? DL_WARNING: DL_ERROR, fname);
+}
+
+/* Handles #include-family directives (distinguished by TYPE),
+   including HEADER, and the command line -imacros and -include.
+   Returns true if a buffer was stacked.  */
+bool
+_cpp_execute_include (pfile, header, type)
+     cpp_reader *pfile;
+     const cpp_token *header;
+     enum include_type type;
+{
+  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);
 
-/* Maintain and search list of included files, for #import.  */
+      if (type == IT_IMPORT)
+       _cpp_never_reread (inc);
+    }
 
-/* Hash a file name for import_hash_table.  */
+  return stacked;
+}
 
-static int 
-import_hash (f)
-     char *f;
+/* Locate HEADER, and determine whether it is newer than the current
+   file.  If it cannot be located or dated, return -1, if it is newer
+   newer, return 1, otherwise 0.  */
+int
+_cpp_compare_file_date (pfile, header)
+     cpp_reader *pfile;
+     const cpp_token *header;
 {
-  int val = 0;
+  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;
+    }
 
-  while (*f) val += *f++;
-  return (val%IMPORT_HASH_SIZE);
+  return inc->st.st_mtime > pfile->buffer->inc->st.st_mtime;
 }
 
-/* Search for file FILENAME in import_hash_table.
-   Return -2 if found, either a matching name or a matching inode.
-   Otherwise, open the file and return a file descriptor if successful
-   or -1 if unsuccessful.  */
 
-static int
-lookup_import (pfile, filename, searchptr)
+/* 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;
-     char *filename;
-     struct file_name_list *searchptr;
+     const char *fname;
 {
-  struct import_file *i;
-  int h;
-  int hashval;
-  struct stat sb;
-  int fd;
-
-  hashval = import_hash (filename);
-
-  /* Attempt to find file in list of already included files */
-  i = pfile->import_hash_table[hashval];
-
-  while (i) {
-    if (!strcmp (filename, i->name))
-      return -2;               /* return found */
-    i = i->next;
-  }
-  /* Open it and try a match on inode/dev */
-  fd = open_include_file (pfile, filename, searchptr);
-  if (fd < 0)
-    return fd;
-  fstat (fd, &sb);
-  for (h = 0; h < IMPORT_HASH_SIZE; h++) {
-    i = pfile->import_hash_table[h];
-    while (i) {
-      /* Compare the inode and the device.
-        Supposedly on some systems the inode is not a scalar.  */
-      if (!bcmp ((char *) &i->inode, (char *) &sb.st_ino, sizeof (sb.st_ino))
-         && i->dev == sb.st_dev) {
-        close (fd);
-        return -2;             /* return found */
-      }
-      i = i->next;
+  /* This uses open_file, because we don't allow a PCH to be used as
+     the toplevel compilation (that would prevent re-compiling an
+     existing PCH without deleting it first).  */
+  struct include_file *f = open_file (pfile, fname);
+
+  if (f == NULL)
+    {
+      cpp_errno (pfile, DL_ERROR, fname);
+      return false;
     }
-  }
-  return fd;                   /* Not found, return open file */
+
+  return stack_include_file (pfile, f);
 }
 
-/* Add the file FNAME, open on descriptor FD, to import_hash_table.  */
+/* Pushes the given file onto the buffer stack.  Returns nonzero if
+   successful.  */
+bool
+cpp_push_include (pfile, filename)
+     cpp_reader *pfile;
+     const char *filename;
+{
+  cpp_token header;
 
-static void
-add_import (pfile, fd, fname)
+  header.type = CPP_STRING;
+  header.val.str.text = (const unsigned char *) filename;
+  header.val.str.len = strlen (filename);
+  /* Make the command line directive take up a line.  */
+  pfile->line++;
+
+  return _cpp_execute_include (pfile, &header, IT_CMDLINE);
+}
+
+/* Do appropriate cleanup when a file INC's buffer is popped off the
+   input stack.  */
+void
+_cpp_pop_file_buffer (pfile, inc)
+     cpp_reader *pfile;
+     struct include_file *inc;
+{
+  /* Record the inclusion-preventing macro, which could be NULL
+     meaning no controlling macro.  */
+  if (pfile->mi_valid && inc->cmacro == NULL)
+    inc->cmacro = pfile->mi_cmacro;
+
+  /* Invalidate control macros in the #including file.  */
+  pfile->mi_valid = false;
+
+  inc->refcnt--;
+  if (inc->refcnt == 0 && DO_NOT_REREAD (inc))
+    purge_cache (inc);
+}
+
+/* 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.
+
+   If we're handling -include or -imacros, use the "" chain, but with
+   the preprocessor's cwd prepended.  */
+static struct cpp_path *
+search_from (pfile, type)
      cpp_reader *pfile;
-     int fd;
-     char *fname;
+     enum include_type type;
 {
-  struct import_file *i;
-  int hashval;
-  struct stat sb;
-
-  hashval = import_hash (fname);
-  fstat (fd, &sb);
-  i = (struct import_file *)xmalloc (sizeof (struct import_file));
-  i->name = (char *)xmalloc (strlen (fname)+1);
-  strcpy (i->name, fname);
-  bcopy ((char *) &sb.st_ino, (char *) &i->inode, sizeof (sb.st_ino));
-  i->dev = sb.st_dev;
-  i->next = pfile->import_hash_table[hashval];
-  pfile->import_hash_table[hashval] = i;
+  cpp_buffer *buffer = pfile->buffer;
+  unsigned int dlen;
+
+  /* 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 (pfile->quote_ignores_source_dir)
+    return pfile->quote_include;
+
+  if (! buffer->search_cached)
+    {
+      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 = (char *) buffer->inc->name;
+         if (dlen > 1)
+           dlen--;
+       }
+      else
+       {
+       use_cwd:
+         buffer->dir.name = (char *) ".";
+         dlen = 1;
+       }
+
+      if (dlen > pfile->max_include_len)
+       pfile->max_include_len = dlen;
+
+      buffer->dir.len = dlen;
+      buffer->dir.next = pfile->quote_include;
+      buffer->dir.sysp = pfile->map->sysp;
+    }
+
+  return &buffer->dir;
 }
 
 /* The file_name_map structure holds a mapping of file names for a
@@ -364,9 +995,7 @@ add_import (pfile, fd, fname)
    such as DOS.  The format of the file name map file is just a series
    of lines with two tokens on each line.  The first token is the name
    to map, and the second token is the actual name to use.  */
-
-struct file_name_map
-{
+struct file_name_map {
   struct file_name_map *map_next;
   char *map_from;
   char *map_to;
@@ -375,8 +1004,7 @@ struct file_name_map
 #define FILE_NAME_MAP_FILE "header.gcc"
 
 /* Read a space delimited string of unlimited length from a stdio
-   file.  */
-
+   file F.  */
 static char *
 read_filename_string (ch, f)
      int ch;
@@ -387,10 +1015,10 @@ read_filename_string (ch, f)
 
   len = 20;
   set = alloc = xmalloc (len + 1);
-  if (! is_space[ch])
+  if (! is_space (ch))
     {
       *set++ = ch;
-      while ((ch = getc (f)) != EOF && ! is_space[ch])
+      while ((ch = getc (f)) != EOF && ! is_space (ch))
        {
          if (set - alloc == len)
            {
@@ -407,33 +1035,33 @@ read_filename_string (ch, f)
 }
 
 /* This structure holds a linked list of file name maps, one per directory.  */
-
-struct file_name_map_list
-{
+struct file_name_map_list {
   struct file_name_map_list *map_list_next;
   char *map_list_name;
   struct file_name_map *map_list_map;
 };
 
 /* Read the file name map file for DIRNAME.  */
-
 static struct file_name_map *
 read_name_map (pfile, dirname)
      cpp_reader *pfile;
-     char *dirname;
+     const char *dirname;
 {
-  register struct file_name_map_list *map_list_ptr;
+  struct file_name_map_list *map_list_ptr;
   char *name;
   FILE *f;
 
-  for (map_list_ptr = CPP_OPTIONS (pfile)->map_list; map_list_ptr;
+  /* 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 = savestring (dirname);
+  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);
@@ -442,22 +1070,21 @@ read_name_map (pfile, dirname)
     strcat (name, "/");
   strcat (name, FILE_NAME_MAP_FILE);
   f = fopen (name, "r");
-  if (!f)
-    map_list_ptr->map_list_map = NULL;
-  else
+
+  /* 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;
 
-         if (is_space[ch])
+         if (is_space (ch))
            continue;
          from = read_filename_string (ch, f);
-         while ((ch = getc (f)) != EOF && is_hor_space[ch])
+         while ((ch = getc (f)) != EOF && is_hspace (ch))
            ;
          to = read_filename_string (ch, f);
 
@@ -466,16 +1093,13 @@ read_name_map (pfile, dirname)
          ptr->map_from = from;
 
          /* Make the real filename absolute.  */
-         if (*to == '/')
+         if (IS_ABSOLUTE_PATHNAME (to))
            ptr->map_to = to;
          else
            {
-             ptr->map_to = xmalloc (dirlen + strlen (to) + 2);
-             strcpy (ptr->map_to, dirname);
-             ptr->map_to[dirlen] = '/';
-             strcpy (ptr->map_to + dirlen + 1, to);
+             ptr->map_to = concat (dirname, "/", to, NULL);
              free (to);
-           }         
+           }
 
          ptr->map_next = map_list_ptr->map_list_map;
          map_list_ptr->map_list_map = ptr;
@@ -486,537 +1110,245 @@ read_name_map (pfile, dirname)
        }
       fclose (f);
     }
-  
-  map_list_ptr->map_list_next = CPP_OPTIONS (pfile)->map_list;
-  CPP_OPTIONS (pfile)->map_list = map_list_ptr;
-
-  return map_list_ptr->map_list_map;
-}  
-
-/* Try to open include file FILENAME.  SEARCHPTR is the directory
-   being tried from the include file search path.  This function maps
-   filenames on file systems based on information read by
-   read_name_map.  */
-
-static int
-open_include_file (pfile, filename, searchptr)
-     cpp_reader *pfile;
-     char *filename;
-     struct file_name_list *searchptr;
-{
-  if (CPP_OPTIONS (pfile)->remap)
-    {
-      register struct file_name_map *map;
-      register char *from;
-      char *p, *dir;
-
-      if (searchptr && ! searchptr->got_name_map)
-       {
-         searchptr->name_map = read_name_map (pfile,
-                                              searchptr->fname
-                                              ? searchptr->fname : ".");
-         searchptr->got_name_map = 1;
-       }
-
-      /* First check the mapping for the directory we are using.  */
-      if (searchptr && searchptr->name_map)
-       {
-         from = filename;
-         if (searchptr->fname)
-           from += strlen (searchptr->fname) + 1;
-         for (map = searchptr->name_map; map; map = map->map_next)
-           {
-             if (! strcmp (map->map_from, from))
-               {
-                 /* Found a match.  */
-                 return open (map->map_to, O_RDONLY, 0666);
-               }
-           }
-       }
 
-      /* 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 = rindex (filename, '/');
-      if (! p)
-       p = filename;
-      if (searchptr
-         && searchptr->fname
-         && strlen (searchptr->fname) == (size_t) (p - filename)
-         && ! strncmp (searchptr->fname, filename, p - filename))
-       {
-         /* FILENAME is in SEARCHPTR, which we've already checked.  */
-         return open (filename, O_RDONLY, 0666);
-       }
+  /* 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;
 
-      if (p == filename)
-       {
-         dir = ".";
-         from = filename;
-       }
-      else
-       {
-         dir = (char *) alloca (p - filename + 1);
-         bcopy (filename, dir, p - filename);
-         dir[p - filename] = '\0';
-         from = p + 1;
-       }
-      for (map = read_name_map (pfile, dir); map; map = map->map_next)
-       if (! strcmp (map->map_from, from))
-         return open (map->map_to, O_RDONLY, 0666);
-    }
-
-  return open (filename, O_RDONLY, 0666);
+  return map_list_ptr->map_list_map;
 }
 
-/* Process the contents of include file FNAME, already open on descriptor F,
-   with output to OP.
-   SYSTEM_HEADER_P is 1 if this file resides in any one of the known
-   "system" include directories (as decided by the `is_system_include'
-   function above).
-   DIRPTR is the link in the dir path through which this file was found,
-   or 0 if the file name was absolute or via the current directory.
-   Return 1 on success, 0 on failure.
-
-   The caller is responsible for the cpp_push_buffer.  */
-
-int
-finclude (pfile, f, fname, system_header_p, dirptr)
+/* Remap an unsimplified path NAME based on the file_name_map (if any)
+   for LOC.  */
+static char *
+remap_filename (pfile, name, loc)
      cpp_reader *pfile;
-     int f;
-     char *fname;
-     int system_header_p;
-     struct file_name_list *dirptr;
+     char *name;
+     struct cpp_path *loc;
 {
-  struct stat st;
-  size_t st_size;
-  long i;
-  int length;
-  cpp_buffer *fp;                      /* For input stack frame */
-#if 0
-  int missing_newline = 0;
-#endif
+  struct file_name_map *map;
+  const char *from, *p;
+  char *dir;
 
-  if (fstat (f, &st) < 0)
+  if (! loc->name_map)
     {
-      cpp_perror_with_name (pfile, fname);
-      close (f);
-      cpp_pop_buffer (pfile);
-      return 0;
+      /* 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;
     }
 
-  fp = CPP_BUFFER (pfile);
-  fp->nominal_fname = fp->fname = fname;
-#if 0
-  fp->length = 0;
-#endif
-  fp->dir = dirptr;
-  fp->system_header_p = system_header_p;
-  fp->lineno = 1;
-  fp->colno = 1;
-  fp->cleanup = file_cleanup;
-
-  if (S_ISREG (st.st_mode)) {
-    st_size = (size_t) st.st_size;
-    if (st_size != st.st_size || st_size + 2 < st_size) {
-      cpp_error (pfile, "file `%s' too large", fname);
-      close (f);
-      return 0;
-    }
-    fp->buf = (U_CHAR *) xmalloc (st_size + 2);
-    fp->alimit = fp->buf + st_size + 2;
-    fp->cur = fp->buf;
-
-    /* Read the file contents, knowing that st_size is an upper bound
-       on the number of bytes we can read.  */
-    length = safe_read (f, fp->buf, st_size);
-    fp->rlimit = fp->buf + length;
-    if (length < 0) goto nope;
-  }
-  else if (S_ISDIR (st.st_mode)) {
-    cpp_error (pfile, "directory `%s' specified in #include", fname);
-    close (f);
-    return 0;
-  } else {
-    /* Cannot count its file size before reading.
-       First read the entire file into heap and
-       copy them into buffer on stack.  */
-
-    size_t bsize = 2000;
-
-    st_size = 0;
-    fp->buf = (U_CHAR *) xmalloc (bsize + 2);
-
-    for (;;) {
-      i = safe_read (f, fp->buf + st_size, bsize - st_size);
-      if (i < 0)
-       goto nope;      /* error! */
-      st_size += i;
-      if (st_size != bsize)
-       break;  /* End of file */
-      bsize *= 2;
-      fp->buf = (U_CHAR *) xrealloc (fp->buf, bsize + 2);
-    }
-    fp->cur = fp->buf;
-    length = st_size;
-  }
-
-  if ((length > 0 && fp->buf[length - 1] != '\n')
-      /* Backslash-newline at end is not good enough.  */
-      || (length > 1 && fp->buf[length - 2] == '\\')) {
-    fp->buf[length++] = '\n';
-#if 0
-    missing_newline = 1;
-#endif
-  }
-  fp->buf[length] = '\0';
-  fp->rlimit = fp->buf + length;
-
-  /* Close descriptor now, so nesting does not use lots of descriptors.  */
-  close (f);
+  /* This works since NAME has not been simplified yet.  */
+  from = name + loc->len + 1;
 
-  /* Must do this before calling trigraph_pcp, so that the correct file name
-     will be printed in warning messages.  */
+  for (map = loc->name_map; map; map = map->map_next)
+    if (!strcmp (map->map_from, from))
+      return map->map_to;
 
-  pfile->input_stack_listing_current = 0;
+  /* 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;
 
-#if 0
-  if (!no_trigraphs)
-    trigraph_pcp (fp);
-#endif
+  /* We know p != name as absolute paths don't call remap_filename.  */
+  if (p == name)
+    cpp_error (pfile, DL_ICE, "absolute file name in remap_filename");
 
-#if 0
-  rescan (op, 0);
+  dir = (char *) alloca (p - name + 1);
+  memcpy (dir, name, p - name);
+  dir[p - name] = '\0';
+  from = p + 1;
 
-  if (missing_newline)
-    fp->lineno--;
+  for (map = read_name_map (pfile, dir); map; map = map->map_next)
+    if (! strcmp (map->map_from, from))
+      return map->map_to;
 
-  if (CPP_PEDANTIC (pfile) && missing_newline)
-    pedwarn ("file does not end in newline");
+  return name;
+}
 
-  indepth--;
-  input_file_stack_tick++;
-  free (fp->buf);
-#endif
-  return 1;
+/* 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.
 
- nope:
+   If BRACKET does not lie in the QUOTE chain, it is set to QUOTE.  */
+void
+cpp_set_include_chains (pfile, quote, bracket, quote_ignores_source_dir)
+     cpp_reader *pfile;
+     cpp_path *quote, *bracket;
+     int quote_ignores_source_dir;
+{
+  pfile->quote_include = quote;
+  pfile->bracket_include = quote;
+  pfile->quote_ignores_source_dir = quote_ignores_source_dir;
+  pfile->max_include_len = 0;
 
-  cpp_perror_with_name (pfile, fname);
-  close (f);
-  free (fp->buf);
-  return 1;
+  for (; quote; quote = quote->next)
+    {
+      quote->name_map = NULL;
+      quote->len = strlen (quote->name);
+      if (quote->len > pfile->max_include_len)
+       pfile->max_include_len = quote->len;
+      if (quote == bracket)
+       pfile->bracket_include = bracket;
+    }
 }
 
-/* Read LEN bytes at PTR from descriptor DESC, for file FILENAME,
-   retrying if necessary.  If MAX_READ_LEN is defined, read at most
-   that bytes at a time.  Return a negative value if an error occurs,
-   otherwise return the actual number of bytes read,
-   which must be LEN unless end-of-file was reached.  */
-
+/* 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
-safe_read (desc, ptr, len)
-     int desc;
-     char *ptr;
-     int len;
+remove_component_p (path)
+     const char *path;
 {
-  int left, rcount, nchars;
-
-  left = len;
-  while (left > 0) {
-    rcount = left;
-#ifdef MAX_READ_LEN
-    if (rcount > MAX_READ_LEN)
-      rcount = MAX_READ_LEN;
-#endif
-    nchars = read (desc, ptr, rcount);
-    if (nchars < 0)
-      {
-#ifdef EINTR
-       if (errno == EINTR)
-         continue;
-#endif
-       return nchars;
-      }
-    if (nchars == 0)
-      break;
-    ptr += nchars;
-    left -= nchars;
-  }
-  return len - left;
-}
-
-#ifdef VMS
-
-/* Under VMS we need to fix up the "include" specification filename.
+  struct stat s;
+  int result;
 
-   Rules for possible conversions
+#ifdef HAVE_LSTAT
+  result = lstat (path, &s);
+#else
+  result = stat (path, &s);
+#endif
 
-       fullname                tried paths
+  /* 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;
 
-       name                    name
-       ./dir/name              [.dir]name
-       /dir/name               dir:name
-       /name                   [000000]name, name
-       dir/name                dir:[000000]name, dir:name, dir/name
-       dir1/dir2/name          dir1:[dir2]name, dir1:[000000.dir2]name
-       path:/name              path:[000000]name, path:name
-       path:/dir/name          path:[000000.dir]name, path:[dir]name
-       path:dir/name           path:[dir]name
-       [path]:[dir]name        [path.dir]name
-       path/[dir]name          [path.dir]name
+  return result == 0 && S_ISDIR (s.st_mode);
+}
 
-   The path:/name input is constructed when expanding <> includes. */
+/* 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)
 
-static void
-hack_vms_include_specification (fullname)
-     char *fullname;
+   Guarantees no trailing slashes.  All transforms reduce the length
+   of the string.  Returns PATH.  errno is 0 if no error occurred;
+   nonzero if an error occurred when using stat () or lstat ().  */
+void
+cpp_simplify_path (path)
+     char *path ATTRIBUTE_UNUSED;
 {
-  register char *basename, *unixname, *local_ptr, *first_slash;
-  int f, check_filename_before_returning, must_revert;
-  char Local[512];
-
-  check_filename_before_returning = 0;
-  must_revert = 0;
-  /* See if we can find a 1st slash. If not, there's no path information.  */
-  first_slash = index (fullname, '/');
-  if (first_slash == 0)
-    return 0;                          /* Nothing to do!!! */
-
-  /* construct device spec if none given.  */
-
-  if (index (fullname, ':') == 0)
-    {
-
-      /* If fullname has a slash, take it as device spec.  */
-
-      if (first_slash == fullname)
-       {
-         first_slash = index (fullname+1, '/');        /* 2nd slash ? */
-         if (first_slash)
-           *first_slash = ':';                         /* make device spec  */
-         for (basename = fullname; *basename != 0; basename++)
-           *basename = *(basename+1);                  /* remove leading slash  */
-       }
-      else if ((first_slash[-1] != '.')                /* keep ':/', './' */
-           && (first_slash[-1] != ':')
-           && (first_slash[-1] != ']'))        /* or a vms path  */
-       {
-         *first_slash = ':';
-       }
-      else if ((first_slash[1] == '[')         /* skip './' in './[dir'  */
-           && (first_slash[-1] == '.'))
-       fullname += 2;
-    }
-
-  /* Get part after first ':' (basename[-1] == ':')
-     or last '/' (basename[-1] == '/').  */
-
-  basename = base_name (fullname);
-
-  local_ptr = Local;                   /* initialize */
-
-  /* We are trying to do a number of things here.  First of all, we are
-     trying to hammer the filenames into a standard format, such that later
-     processing can handle them.
-     
-     If the file name contains something like [dir.], then it recognizes this
-     as a root, and strips the ".]".  Later processing will add whatever is
-     needed to get things working properly.
-     
-     If no device is specified, then the first directory name is taken to be
-     a device name (or a rooted logical).  */
+#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;
 
-  /* Point to the UNIX filename part (which needs to be fixed!)
-     but skip vms path information.
-     [basename != fullname since first_slash != 0].  */
+#if defined (HAVE_DOS_BASED_FILE_SYSTEM)
+  /* Convert all backslashes to slashes.  */
+  for (from = path; *from; from++)
+    if (*from == '\\') *from = '/';
 
-  if ((basename[-1] == ':')            /* vms path spec.  */
-      || (basename[-1] == ']')
-      || (basename[-1] == '>'))
-    unixname = basename;
+  /* Skip over leading drive letter if present.  */
+  if (ISALPHA (path[0]) && path[1] == ':')
+    from = to = &path[2];
   else
-    unixname = fullname;
-
-  if (*unixname == '/')
-    unixname++;
-
-  /* If the directory spec is not rooted, we can just copy
-     the UNIX filename part and we are done.  */
+    from = to = path;
+#else
+  from = to = path;
+#endif
 
-  if (((basename - fullname) > 1)
-     && (  (basename[-1] == ']')
-        || (basename[-1] == '>')))
+  /* Remove redundant leading /s.  */
+  if (*from == '/')
     {
-      if (basename[-2] != '.')
+      absolute = 1;
+      to++;
+      from++;
+      if (*from == '/')
        {
-
-       /* The VMS part ends in a `]', and the preceding character is not a `.'.
-          -> PATH]:/name (basename = '/name', unixname = 'name')
-          We strip the `]', and then splice the two parts of the name in the
-          usual way.  Given the default locations for include files in cccp.c,
-          we will only use this code if the user specifies alternate locations
-          with the /include (-I) switch on the command line.  */
-
-         basename -= 1;        /* Strip "]" */
-         unixname--;           /* backspace */
-       }
-      else
-       {
-
-       /* The VMS part has a ".]" at the end, and this will not do.  Later
-          processing will add a second directory spec, and this would be a syntax
-          error.  Thus we strip the ".]", and thus merge the directory specs.
-          We also backspace unixname, so that it points to a '/'.  This inhibits the
-          generation of the 000000 root directory spec (which does not belong here
-          in this case).  */
-
-         basename -= 2;        /* Strip ".]" */
-         unixname--;           /* backspace */
+         if (*++from == '/')
+           /* 3 or more initial /s are equivalent to 1 /.  */
+           while (*++from == '/');
+         else
+           /* On some hosts // differs from /; Posix allows this.  */
+           to++;
        }
     }
 
-  else
-
+  base = orig_base = to;
+  for (;;)
     {
+      int move_base = 0;
 
-      /* We drop in here if there is no VMS style directory specification yet.
-         If there is no device specification either, we make the first dir a
-         device and try that.  If we do not do this, then we will be essentially
-         searching the users default directory (as if they did a #include "asdf.h").
-        
-         Then all we need to do is to push a '[' into the output string. Later
-         processing will fill this in, and close the bracket.  */
-
-      if ((unixname != fullname)       /* vms path spec found.  */
-        && (basename[-1] != ':'))
-       *local_ptr++ = ':';             /* dev not in spec.  take first dir */
+      while (*from == '/')
+       from++;
 
-      *local_ptr++ = '[';              /* Open the directory specification */
-    }
-
-    if (unixname == fullname)          /* no vms dir spec.  */
-      {
-       must_revert = 1;
-       if ((first_slash != 0)          /* unix dir spec.  */
-           && (*unixname != '/')       /* not beginning with '/'  */
-           && (*unixname != '.'))      /* or './' or '../'  */
-         *local_ptr++ = '.';           /* dir is local !  */
-      }
-
-  /* at this point we assume that we have the device spec, and (at least
-     the opening "[" for a directory specification.  We may have directories
-     specified already.
-
-     If there are no other slashes then the filename will be
-     in the "root" directory.  Otherwise, we need to add
-     directory specifications.  */
-
-  if (index (unixname, '/') == 0)
-    {
-      /* if no directories specified yet and none are following.  */
-      if (local_ptr[-1] == '[')
-       {
-         /* Just add "000000]" as the directory string */
-         strcpy (local_ptr, "000000]");
-         local_ptr += strlen (local_ptr);
-         check_filename_before_returning = 1; /* we might need to fool with this later */
-       }
-    }
-  else
-    {
+      if (*from == '\0')
+       break;
 
-      /* As long as there are still subdirectories to add, do them.  */
-      while (index (unixname, '/') != 0)
+      if (*from == '.')
        {
-         /* If this token is "." we can ignore it
-              if it's not at the beginning of a path.  */
-         if ((unixname[0] == '.') && (unixname[1] == '/'))
+         if (from[1] == '\0')
+           break;
+         if (from[1] == '/')
            {
-             /* remove it at beginning of path.  */
-             if (  ((unixname == fullname)             /* no device spec  */
-                   && (fullname+2 != basename))        /* starts with ./ */
-                                                       /* or  */
-                || ((basename[-1] == ':')              /* device spec  */
-                   && (unixname-1 == basename)))       /* and ./ afterwards  */
-               *local_ptr++ = '.';                     /* make '[.' start of path.  */
-             unixname += 2;
+             from += 2;
              continue;
            }
-
-         /* Add a subdirectory spec. Do not duplicate "." */
-         if (  local_ptr[-1] != '.'
-            && local_ptr[-1] != '['
-            && local_ptr[-1] != '<')
-           *local_ptr++ = '.';
-
-         /* If this is ".." then the spec becomes "-" */
-         if (  (unixname[0] == '.')
-            && (unixname[1] == '.')
-            && (unixname[2] == '/'))
+         else if (from[1] == '.' && (from[2] == '/' || from[2] == '\0'))
            {
-             /* Add "-" and skip the ".." */
-             if ((local_ptr[-1] == '.')
-                 && (local_ptr[-2] == '['))
-               local_ptr--;                    /* prevent [.-  */
-             *local_ptr++ = '-';
-             unixname += 3;
-             continue;
+             /* 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;
            }
-
-         /* Copy the subdirectory */
-         while (*unixname != '/')
-           *local_ptr++= *unixname++;
-
-         unixname++;                   /* Skip the "/" */
        }
 
-      /* Close the directory specification */
-      if (local_ptr[-1] == '.')                /* no trailing periods */
-       local_ptr--;
-
-      if (local_ptr[-1] == '[')                /* no dir needed */
-       local_ptr--;
-      else
-       *local_ptr++ = ']';
-    }
+      /* Add the component separator.  */
+      if (to > orig_base)
+       *to++ = '/';
 
-  /* Now add the filename.  */
-
-  while (*unixname)
-    *local_ptr++ = *unixname++;
-  *local_ptr = 0;
-
-  /* Now append it to the original VMS spec.  */
-
-  strcpy ((must_revert==1)?fullname:basename, Local);
-
-  /* If we put a [000000] in the filename, try to open it first. If this fails,
-     remove the [000000], and return that name.  This provides flexibility
-     to the user in that they can use both rooted and non-rooted logical names
-     to point to the location of the file.  */
-
-  if (check_filename_before_returning)
-    {
-      f = open (fullname, O_RDONLY, 0666);
-      if (f >= 0)
-       {
-         /* The file name is OK as it is, so return it as is.  */
-         close (f);
-         return 1;
-       }
-
-      /* The filename did not work.  Try to remove the [000000] from the name,
-        and return it.  */
-
-      basename = index (fullname, '[');
-      local_ptr = index (fullname, ']') + 1;
-      strcpy (basename, local_ptr);            /* this gets rid of it */
+      /* Copy this component until the trailing null or '/'.  */
+      while (*from != '\0' && *from != '/')
+       *to++ = *from++;
 
+      if (move_base)
+       base = to;
     }
 
-  return 1;
+  /* Change the empty string to "." so that it is not treated as stdin.
+     Null terminate.  */
+  if (to == path)
+    *to++ = '.';
+  *to = '\0';
+#else  /* VMS */
+  errno = 0;
+#endif /* !VMS  */
 }
-#endif /* VMS */