OSDN Git Service

Correct test results for avoid-bool-define fix
[pf3gnuchains/gcc-fork.git] / gcc / cppfiles.c
index 697ea1b..137882b 100644 (file)
@@ -1,6 +1,6 @@
 /* Part of CPP library.  (include file handling)
    Copyright (C) 1986, 1987, 1989, 1992, 1993, 1994, 1995, 1998,
-   1999, 2000 Free Software Foundation, Inc.
+   1999, 2000, 2001 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
@@ -43,10 +43,6 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 # 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
@@ -55,46 +51,96 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 #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
+{
+  const char *name;            /* actual path name of file */
+  const cpp_hashnode *cmacro;  /* macro, if any, preventing reinclusion.  */
+  const struct search_path *foundhere;
+                               /* location in search path where file was
+                                  found, for #include_next and sysp.  */
+  const unsigned char *buffer; /* pointer to cached file contents */
+  struct stat st;              /* copy of stat(2) data for file */
+  int fd;                      /* fd open on file (short term storage only) */
+  int err_no;                  /* errno obtained if opening a file failed */
+  unsigned short include_count;        /* number of times file has been read */
+  unsigned short refcnt;       /* number of stacked buffers using this file */
+  unsigned char mapped;                /* file buffer is mmapped */
+};
+
+/* The cmacro works like this: If it's NULL, the file is to be
+   included again.  If it's NEVER_REREAD, the file is never to be
+   included again.  Otherwise it is a macro hashnode, and the file is
+   to be included again if the macro is defined.  */
+#define NEVER_REREAD ((const cpp_hashnode *)-1)
+#define DO_NOT_REREAD(inc) \
+((inc)->cmacro && ((inc)->cmacro == NEVER_REREAD \
+                  || (inc)->cmacro->type == NT_MACRO))
+#define NO_INCLUDE_PATH ((struct include_file *) -1)
+
 static struct file_name_map *read_name_map
                                PARAMS ((cpp_reader *, const char *));
 static char *read_filename_string PARAMS ((int, FILE *));
 static char *remap_filename    PARAMS ((cpp_reader *, char *,
-                                        struct 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 *lookup_include_file
-                               PARAMS ((cpp_reader *, const char *));
+                                        struct search_path *));
+static struct search_path *search_from PARAMS ((cpp_reader *,
+                                               enum include_type));
+static struct include_file *
+       find_include_file PARAMS ((cpp_reader *, const cpp_token *,
+                                  enum include_type));
+static struct include_file *open_file PARAMS ((cpp_reader *, const char *));
 static int read_include_file   PARAMS ((cpp_reader *, struct include_file *));
-static int stack_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 void destroy_node       PARAMS ((splay_tree_value));
 static int report_missing_guard                PARAMS ((splay_tree_node, void *));
+static splay_tree_node find_or_create_entry PARAMS ((cpp_reader *,
+                                                    const char *));
+static void handle_missing_header PARAMS ((cpp_reader *, const char *, int));
+static int remove_component_p  PARAMS ((const char *));
+
+/* Set up the splay tree we use to store information about all the
+   file names seen in this compilation.  We also have entries for each
+   file we tried to open but failed; this saves system calls since we
+   don't try to open it again in future.
+
+   The key of each node is the file name, after processing by
+   _cpp_simplify_pathname.  The path name may or may not be absolute.
+   The path string has been malloced, as is automatically freed by
+   registering free () as the splay tree key deletion function.
+
+   A node's value is a pointer to a struct include_file, and is never
+   NULL.  */
+void
+_cpp_init_includes (pfile)
+     cpp_reader *pfile;
+{
+  pfile->all_include_files
+    = splay_tree_new ((splay_tree_compare_fn) strcmp,
+                     (splay_tree_delete_key_fn) free,
+                     destroy_node);
+}
 
-#if 0
-static void hack_vms_include_specification PARAMS ((char *));
-#endif
-
-/* 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.  */
+/* Tear down the splay tree.  */
+void
+_cpp_cleanup_includes (pfile)
+     cpp_reader *pfile;
+{
+  splay_tree_delete (pfile->all_include_files);
+}
 
+/* Free a node.  The path string is automatically freed.  */
 static void
-destroy_include_file_node (v)
+destroy_node (v)
      splay_tree_value v;
 {
   struct include_file *f = (struct include_file *)v;
+
   if (f)
     {
       purge_cache (f);
@@ -102,41 +148,84 @@ destroy_include_file_node (v)
     }
 }
 
+/* Mark a file to not be reread (e.g. #import, read failure).  */
 void
-_cpp_init_includes (pfile)
+_cpp_never_reread (file)
+     struct include_file *file;
+{
+  file->cmacro = NEVER_REREAD;
+}
+
+/* Lookup a filename, which is simplified after making a copy, and
+   create an entry if none exists.  errno is nonzero iff a (reported)
+   stat() error occurred during simplification.  */
+static splay_tree_node
+find_or_create_entry (pfile, fname)
      cpp_reader *pfile;
+     const char *fname;
 {
-  pfile->all_include_files
-    = splay_tree_new ((splay_tree_compare_fn) strcmp,
-                     (splay_tree_delete_key_fn) free,
-                     destroy_include_file_node);
+  splay_tree_node node;
+  struct include_file *file;
+  char *name = xstrdup (fname);
+
+  _cpp_simplify_pathname (name);
+  node = splay_tree_lookup (pfile->all_include_files, (splay_tree_key) name);
+  if (node)
+    free (name);
+  else
+    {
+      file = xcnew (struct include_file);
+      file->name = name;
+      file->err_no = errno;
+      node = splay_tree_insert (pfile->all_include_files,
+                               (splay_tree_key) file->name,
+                               (splay_tree_value) file);
+    }
+
+  return node;
 }
 
+/* Enter a file name in the splay tree, for the sake of cpp_included.  */
 void
-_cpp_cleanup_includes (pfile)
+_cpp_fake_include (pfile, fname)
      cpp_reader *pfile;
+     const char *fname;
 {
-  splay_tree_delete (pfile->all_include_files);
+  find_or_create_entry (pfile, fname);
 }
 
 /* Given a file name, look it up in the cache; if there is no entry,
-   create one.  Returns 0 if the file doesn't exist or is
-   inaccessible, otherwise the cache 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 *
-lookup_include_file (pfile, filename)
+open_file (pfile, filename)
      cpp_reader *pfile;
      const char *filename;
-{     
-  splay_tree_node nd;
-  struct include_file *file = 0;
-  int fd;
-  struct stat st;
+{
+  splay_tree_node nd = find_or_create_entry (pfile, filename);
+  struct include_file *file = (struct include_file *) nd->value;
 
-  nd = splay_tree_lookup (pfile->all_include_files, (splay_tree_key) filename);
+  if (file->err_no)
+    {
+      /* Ugh.  handle_missing_header () needs errno to be set.  */
+      errno = file->err_no;
+      return 0;
+    }
 
-  if (nd)
-    return (struct include_file *)nd->value;
+  /* 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
@@ -153,80 +242,104 @@ lookup_include_file (pfile, filename)
      Special case: the empty string is translated to stdin.  */
 
   if (filename[0] == '\0')
-    fd = 0;
+    file->fd = 0;
   else
-    fd = open (filename, O_RDONLY|O_NOCTTY|O_BINARY, 0666);
-  if (fd == -1)
-    goto fail;
-
-  if (fstat (fd, &st) < 0)
-    goto fail;
-  
-  file = xcnew (struct include_file);
-  file->name = xstrdup (filename);
-  file->st = st;
-  file->fd = fd;
-
-  /* If the file is plain and zero length, mark it never-reread now.  */
-  if (S_ISREG (st.st_mode) && st.st_size == 0)
-    file->cmacro = NEVER_REREAD;
+    file->fd = open (file->name, O_RDONLY | O_NOCTTY | O_BINARY, 0666);
 
-  splay_tree_insert (pfile->all_include_files,
-                    (splay_tree_key) file->name, (splay_tree_value) file);
-  return file;
+  if (file->fd != -1 && fstat (file->fd, &file->st) == 0)
+    {
+      /* 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.  */
+      if (S_ISDIR (file->st.st_mode))
+       errno = ENOENT;
+      else
+       {
+         /* 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;
+           }
 
- fail:
+         return file;
+       }
+    }
 
   /* Don't issue an error message if the file doesn't exist.  */
+  file->err_no = errno;
   if (errno != ENOENT && errno != ENOTDIR)
-    cpp_error_from_errno (pfile, filename);
+    cpp_error_from_errno (pfile, file->name);
 
-  /* Create a negative node for this path.  */
-  splay_tree_insert (pfile->all_include_files,
-                    (splay_tree_key) xstrdup (filename), 0);
   return 0;
 }
 
-/* Place the file referenced by INC into a new buffer on PFILE's stack.
-   Return 1 if successful, 0 if not.  */
+/* 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 (zero-length) buffer is pushed.  */
 
-static int
+static void
 stack_include_file (pfile, inc)
      cpp_reader *pfile;
      struct include_file *inc;
 {
+  size_t len = 0;
   cpp_buffer *fp;
+  int sysp, deps_sysp;
 
-  if (DO_NOT_REREAD (inc))
-    return 0;
+  /* We'll try removing deps_sysp after the release of 3.0.  */
+  deps_sysp = pfile->system_include_depth != 0;
+  sysp = MAX ((pfile->buffer ? pfile->buffer->sysp : 0),
+             (inc->foundhere ? inc->foundhere->sysp : 0));
 
-  if (inc->buffer == NULL)
-    if (read_include_file (pfile, inc) == 0)
-      return 0;
+  /* For -M, add the file to the dependencies on its first inclusion.  */
+  if (CPP_OPTION (pfile, print_deps) > deps_sysp && !inc->include_count)
+    deps_add_dep (pfile->deps, inc->name);
 
-  fp = cpp_push_buffer (pfile, NULL, 0);
-  if (fp == 0)
-    return 0;
+  /* Not in cache?  */
+  if (! DO_NOT_REREAD (inc) && ! inc->buffer)
+    {
+      /* If an error occurs, do not try to read this file again.  */
+      if (read_include_file (pfile, inc))
+       _cpp_never_reread (inc);
+      close (inc->fd);
+      inc->fd = -1;
+    }
 
-  fp->inc = inc;
-  fp->nominal_fname = inc->name;
-  fp->buf = inc->buffer;
-  fp->rlimit = fp->buf + inc->st.st_size;
-  fp->cur = fp->buf;
-  fp->lineno = 1;
-  fp->line_base = fp->buf;
+  if (! DO_NOT_REREAD (inc))
+    {
+      len = inc->st.st_size;
+      if (pfile->buffer)
+       {
+         /* We don't want MI guard advice for the main file.  */
+         inc->include_count++;
 
-  /* 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);
+         /* Handle -H option.  */
+         if (CPP_OPTION (pfile, print_include_names))
+           {
+             for (fp = pfile->buffer; fp; fp = fp->prev)
+               putc ('.', stderr);
+             fprintf (stderr, " %s\n", inc->name);
+           }
+       }
+    }
 
+  /* Push a buffer.  */
+  fp = cpp_push_buffer (pfile, inc->buffer, len, BUF_FILE, inc->name);
+  fp->inc = inc;
   fp->inc->refcnt++;
+  fp->sysp = sysp;
+
+  /* Initialise controlling macro state.  */
+  pfile->mi_state = MI_OUTSIDE;
+  pfile->mi_cmacro = 0;
   pfile->include_depth++;
-  pfile->input_stack_listing_current = 0;
-  if (pfile->cb.enter_file)
-    (*pfile->cb.enter_file) (pfile);
-  return 1;
+
+  /* Generate the call back.  */
+  fp->lineno = 0;
+  _cpp_do_file_change (pfile, FC_ENTER, 0, 0);
+  fp->lineno = 1;
 }
 
 /* Read the file referenced by INC into the file cache.
@@ -234,13 +347,13 @@ stack_include_file (pfile, inc)
    If fd points to a plain file, we might be able to mmap it; we can
    definitely allocate the buffer all at once.  If fd is a pipe or
    terminal, we can't do either.  If fd is something weird, like a
-   block device or a directory, we don't want to read it at all.
+   block device, we don't want to read it at all.
 
    Unfortunately, different systems use different st.st_mode values
    for pipes: some have S_ISFIFO, some S_ISSOCK, some are buggy and
    zero the entire struct stat except a couple fields.  Hence we don't
-   even try to figure out what something is, except for plain files,
-   directories, and block devices.
+   even try to figure out what something is, except for plain files
+   and block devices.
 
    FIXME: Flush file cache and try again if we run out of memory.  */
 
@@ -308,11 +421,6 @@ read_include_file (pfile, inc)
       cpp_error (pfile, "%s is a block device", inc->name);
       goto fail;
     }
-  else if (S_ISDIR (inc->st.st_mode))
-    {
-      cpp_error (pfile, "%s is a directory", inc->name);
-      goto fail;
-    }
   else
     {
       /* 8 kilobytes is a sensible starting size.  It ought to be
@@ -336,19 +444,13 @@ read_include_file (pfile, inc)
       inc->st.st_size = offset;
     }
 
-  close (inc->fd);
   inc->buffer = buf;
-  inc->fd = -1;
-  return 1;
+  return 0;
 
  perror_fail:
   cpp_error_from_errno (pfile, inc->name);
  fail:
-  /* Do not try to read this file again.  */
-  close (inc->fd);
-  inc->fd = -1;
-  inc->cmacro = NEVER_REREAD;
-  return 0;
+  return 1;
 }
 
 static void
@@ -374,11 +476,11 @@ cpp_included (pfile, fname)
      cpp_reader *pfile;
      const char *fname;
 {
-  struct file_name_list *path;
-  char *name;
+  struct search_path *path;
+  char *name, *n;
   splay_tree_node nd;
 
-  if (fname[0] == '/')
+  if (IS_ABSOLUTE_PATHNAME (fname))
     {
       /* Just look it up.  */
       nd = splay_tree_lookup (pfile->all_include_files, (splay_tree_key) fname);
@@ -386,132 +488,99 @@ cpp_included (pfile, fname)
     }
       
   /* Search directory path for the file.  */
-  name = (char *) alloca (strlen (fname) + pfile->max_include_len
-                         + 2 + INCLUDE_LEN_FUDGE);
+  name = (char *) alloca (strlen (fname) + pfile->max_include_len + 2);
   for (path = CPP_OPTION (pfile, quote_include); path; path = path->next)
     {
-      memcpy (name, path->name, path->nlen);
-      name[path->nlen] = '/';
-      strcpy (&name[path->nlen+1], fname);
-      _cpp_simplify_pathname (name);
+      memcpy (name, path->name, path->len);
+      name[path->len] = '/';
+      strcpy (&name[path->len + 1], fname);
       if (CPP_OPTION (pfile, remap))
-       name = remap_filename (pfile, name, path);
+       n = remap_filename (pfile, name, path);
+      else
+       n = name;
 
-      nd = splay_tree_lookup (pfile->all_include_files, (splay_tree_key) name);
+      nd = splay_tree_lookup (pfile->all_include_files, (splay_tree_key) n);
       if (nd && nd->value)
        return 1;
     }
   return 0;
 }
 
-/* Search for include file FNAME in the include chain starting at
-   SEARCH_START.  Return 0 if there is no such file (or it's un-openable),
-   otherwise an include_file structure.  */
+/* Search for HEADER.  Return 0 if there is no such file (or it's
+   un-openable), in which case an error code will be in errno.  If
+   there is no include path to use it returns NO_INCLUDE_PATH,
+   otherwise an include_file structure.  If this request originates
+   from a #include_next directive, set INCLUDE_NEXT to true.  */
 
 static struct include_file *
-find_include_file (pfile, fname, search_start)
+find_include_file (pfile, header, type)
      cpp_reader *pfile;
-     const char *fname;
-     struct file_name_list *search_start;
+     const cpp_token *header;
+     enum include_type type;
 {
-  struct file_name_list *path;
-  char *name;
+  const char *fname = (const char *) header->val.str.text;
+  struct search_path *path;
   struct include_file *file;
+  char *name, *n;
+
+  if (IS_ABSOLUTE_PATHNAME (fname))
+    return open_file (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 = CPP_OPTION (pfile, bracket_include);
+  else
+    path = search_from (pfile, type);
+
+  if (path == NULL)
+    {
+      cpp_error (pfile, "No include path in which to find %s", fname);
+      return NO_INCLUDE_PATH;
+    }
 
-  if (fname[0] == '/')
-    return lookup_include_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)
+  name = (char *) alloca (strlen (fname) + pfile->max_include_len + 2);
+  for (; path; path = path->next)
     {
-      memcpy (name, path->name, path->nlen);
-      name[path->nlen] = '/';
-      strcpy (&name[path->nlen+1], fname);
-      _cpp_simplify_pathname (name);
+      memcpy (name, path->name, path->len);
+      name[path->len] = '/';
+      strcpy (&name[path->len + 1], fname);
       if (CPP_OPTION (pfile, remap))
-       name = remap_filename (pfile, name, path);
+       n = remap_filename (pfile, name, path);
+      else
+       n = name;
 
-      file = lookup_include_file (pfile, name);
+      file = open_file (pfile, n);
       if (file)
        {
-         file->sysp = path->sysp;
          file->foundhere = path;
          return file;
        }
     }
-  return 0;
-}
 
-/* #line uses this to save artificial file names.  We have to stat the
-   file because an all_include_files entry is always either + or -,
-   there's no 'I don't know' value.  */
-const char *
-_cpp_fake_include (pfile, fname)
-     cpp_reader *pfile;
-     const char *fname;
-{
-  splay_tree_node nd;
-  struct include_file *file;
-  char *name;
-
-  file = find_include_file (pfile, fname, CPP_OPTION (pfile, quote_include));
-  if (file)
-    {
-      if (file->fd > 0)
-       {
-         close (file->fd);
-         file->fd = -1;
-       }
-      return file->name;
-    }
-
-  name = xstrdup (fname);
-  _cpp_simplify_pathname (name);
-
-  /* We cannot just blindly insert a node, because there's still the
-     chance that the node already exists but isn't on the search path.  */
-  nd = splay_tree_lookup (pfile->all_include_files, (splay_tree_key) name);
-  if (nd)
-    {
-      free (name);
-      return (const char *) nd->key;
-    }
-
-  splay_tree_insert (pfile->all_include_files, (splay_tree_key) name, 0);
-  return (const char *)name;
+  return 0;
 }
 
 /* Not everyone who wants to set system-header-ness on a buffer can
-   see the details of struct include_file.  This is an exported interface
-   because fix-header needs it.  */
+   see the details of a buffer.  This is an exported interface because
+   fix-header needs it.  */
 void
-cpp_make_system_header (pfile, pbuf, flag)
+cpp_make_system_header (pfile, syshdr, externc)
      cpp_reader *pfile;
-     cpp_buffer *pbuf;
-     int flag;
+     int syshdr, externc;
 {
-  if (flag < 0 || flag > 2)
-    cpp_ice (pfile, "cpp_make_system_header: bad flag %d\n", flag);
-  else if (!pbuf->inc)
-    cpp_ice (pfile, "cpp_make_system_header called on non-file buffer");
-  else
-    pbuf->inc->sysp = flag;
-}
-
-const char *
-cpp_syshdr_flags (pfile, pbuf)
-     cpp_reader *pfile ATTRIBUTE_UNUSED;
-     cpp_buffer *pbuf;
-{
-#ifndef NO_IMPLICIT_EXTERN_C
-  if (CPP_OPTION (pfile, cplusplus) && pbuf->inc->sysp == 2)
-    return " 3 4";
-#endif
-  if (pbuf->inc->sysp)
-    return " 3";
-  return "";
+  int flags = 0;
+
+  /* 1 = system header, 2 = system header to be treated as C.  */
+  if (syshdr)
+    flags = 1 + (externc != 0);
+  pfile->buffer->sysp = flags;
+  _cpp_do_file_change (pfile, FC_RENAME, pfile->buffer->nominal_fname,
+                      pfile->buffer->lineno);
 }
 
 /* Report on all files that might benefit from a multiple include guard.
@@ -546,162 +615,113 @@ report_missing_guard (n, b)
   return 0;
 }
 
-#define PRINT_THIS_DEP(p, b) (CPP_PRINT_DEPS(p) > (b||p->system_include_depth))
-void
-_cpp_execute_include (pfile, f, len, no_reinclude, search_start, angle_brackets)
+/* Create a dependency, or issue an error message as appropriate.   */
+static void
+handle_missing_header (pfile, fname, angle_brackets)
      cpp_reader *pfile;
-     const U_CHAR *f;
-     unsigned int len;
-     int no_reinclude;
-     struct file_name_list *search_start;
+     const char *fname;
      int angle_brackets;
 {
-  struct include_file *inc;
-  char *fname;
+  /* We will try making the RHS pfile->buffer->sysp after 3.0.  */
+  int print_dep = CPP_PRINT_DEPS(pfile) > (angle_brackets
+                                          || pfile->system_include_depth);
 
-  if (!search_start)
+  if (CPP_OPTION (pfile, print_deps_missing_files) && print_dep)
     {
-      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;
-    }
-
-  if (!search_start)
-    {
-      cpp_error (pfile, "No include path in which to find %s", f);
-      return;
-    }
-
-  fname = alloca (len + 1);
-  memcpy (fname, f, len);
-  fname[len] = '\0';
-
-  inc = find_include_file (pfile, fname, search_start);
-
-  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++;
-
-      /* Actually process the file.  */
-      if (stack_include_file (pfile, inc))
-       {
-         if (angle_brackets)
-           pfile->system_include_depth++;
-
-         if (no_reinclude)
-           inc->cmacro = NEVER_REREAD;
-
-         /* 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;
-    }
-      
-  if (CPP_OPTION (pfile, print_deps_missing_files)
-      && PRINT_THIS_DEP (pfile, angle_brackets))
-    {
-      if (!angle_brackets)
+      if (!angle_brackets || IS_ABSOLUTE_PATHNAME (fname))
        deps_add_dep (pfile->deps, fname);
       else
        {
-         char *p;
-         struct file_name_list *ptr;
          /* 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);
+            the first system header directory.  */
+         struct search_path *ptr = CPP_OPTION (pfile, bracket_include);
+         char *p;
+         int len = 0, fname_len = strlen (fname);
+
+         if (ptr)
+           len = ptr->len;
 
-         p = (char *) alloca (strlen (ptr->name)
-                              + strlen (fname) + 2);
-         if (*ptr->name != '\0')
+         p = (char *) alloca (len + fname_len + 2);
+         if (len)
            {
-             strcpy (p, ptr->name);
-             strcat (p, "/");
+             memcpy (p, ptr->name, len);
+             p[len++] = '/';
            }
-         strcat (p, fname);
-         _cpp_simplify_pathname (p);
+         memcpy (p + len, fname, fname_len + 1);
          deps_add_dep (pfile->deps, p);
        }
     }
-  /* If -M was specified, and this header file won't be added to
-     the dependency list, then don't count this as an error,
-     because we can still produce correct output.  Otherwise, we
-     can't produce correct output, because there may be
-     dependencies we need inside the missing file, and we don't
-     know what directory this missing file exists in. */
-  else if (CPP_PRINT_DEPS (pfile)
-          && ! PRINT_THIS_DEP (pfile, angle_brackets))
-    cpp_warning (pfile, "No include path in which to find %s", 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.  FIXME: Use a future cpp_diagnotic_with_errno ()
+     for both of these cases.  */
+  else if (CPP_PRINT_DEPS (pfile) && ! print_dep)
+    cpp_warning (pfile, "%s: %s", fname, xstrerror (errno));
   else
     cpp_error_from_errno (pfile, fname);
 }
 
-/* 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.  */
-
+/* Returns non-zero if a buffer was stacked.  */
 int
-_cpp_compare_file_date (pfile, f, len, angle_brackets)
+_cpp_execute_include (pfile, header, type)
      cpp_reader *pfile;
-     const U_CHAR *f;
-     unsigned int len;
-     int angle_brackets;
+     const cpp_token *header;
+     enum include_type type;
 {
-  char *fname;
-  struct file_name_list *search_start;
-  struct include_file *inc;
-  struct include_file *current_include = CPP_BUFFER (pfile)->inc;
-
-  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;
+  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)
+    {
+      if (header->type == CPP_HEADER_NAME)
+       pfile->system_include_depth++;
+
+      stack_include_file (pfile, inc);
 
-  fname = alloca (len + 1);
-  memcpy (fname, f, len);
-  fname[len] = '\0';
-  inc = find_include_file (pfile, fname, search_start);
+      if (type == IT_IMPORT)
+       _cpp_never_reread (inc);
+
+      return 1;
+    }
+
+  return 0;
+}
+
+/* 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;
+{
+  struct include_file *inc = find_include_file (pfile, header, 0);
   
-  if (!inc)
+  if (inc == NULL || inc == NO_INCLUDE_PATH)
     return -1;
+
   if (inc->fd > 0)
     {
       close (inc->fd);
       inc->fd = -1;
     }
     
-  return inc->st.st_mtime > current_include->st.st_mtime;
+  return inc->st.st_mtime > pfile->buffer->inc->st.st_mtime;
 }
 
 
 /* Push an input buffer and load it up with the contents of FNAME.
-   If FNAME is "" or NULL, read standard input.  */
+   If FNAME is "", read standard input.  */
 int
-cpp_read_file (pfile, fname)
+_cpp_read_file (pfile, fname)
      cpp_reader *pfile;
      const char *fname;
 {
-  struct include_file *f;
-
-  if (fname == NULL)
-    fname = "";
-
-  f = lookup_include_file (pfile, fname);
+  struct include_file *f = open_file (pfile, fname);
 
   if (f == NULL)
     {
@@ -709,7 +729,8 @@ cpp_read_file (pfile, fname)
       return 0;
     }
 
-  return stack_include_file (pfile, f);
+  stack_include_file (pfile, f);
+  return 1;
 }
 
 /* Do appropriate cleanup when a file buffer is popped off the input
@@ -725,19 +746,75 @@ _cpp_pop_file_buffer (pfile, buf)
     pfile->system_include_depth--;
   if (pfile->include_depth)
     pfile->include_depth--;
-  if (pfile->potential_control_macro)
-    {
-      if (inc->cmacro != NEVER_REREAD)
-       inc->cmacro = pfile->potential_control_macro;
-      pfile->potential_control_macro = 0;
-    }
-  pfile->input_stack_listing_current = 0;
+
+  /* Record the inclusion-preventing macro, which could be NULL
+     meaning no controlling macro, if we haven't got it already.  */
+  if (pfile->mi_state == MI_OUTSIDE && inc->cmacro == NULL)
+    inc->cmacro = pfile->mi_cmacro;
+
+  /* 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);
 }
 
+/* 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 search_path *
+search_from (pfile, type)
+     cpp_reader *pfile;
+     enum include_type type;
+{
+  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 -I- was given.  */
+  if (CPP_OPTION (pfile, ignore_srcdir))
+    return CPP_OPTION (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, and duplicating it when faking
+            buffers in cpp_push_buffer.  Drop a trailing '/'.  */
+         buffer->dir.name = buffer->inc->name;
+         if (dlen > 1)
+           dlen--;
+       }
+      else
+       {
+       use_cwd:
+         buffer->dir.name = ".";
+         dlen = 1;
+       }
+
+      if (dlen > pfile->max_include_len)
+       pfile->max_include_len = dlen;
+
+      buffer->dir.len = dlen;
+      buffer->dir.next = CPP_OPTION (pfile, quote_include);
+      buffer->dir.sysp = buffer->sysp;
+    }
+
+  return &buffer->dir;
+}
+
 /* The file_name_map structure holds a mapping of file names for a
    particular directory.  This mapping is read from the file named
    FILE_NAME_MAP_FILE in that directory.  Such a file can be used to
@@ -807,6 +884,7 @@ read_name_map (pfile, dirname)
   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))
@@ -815,6 +893,8 @@ read_name_map (pfile, dirname)
   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);
@@ -823,9 +903,9 @@ 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 = (struct file_name_map *)-1;
-  else
+
+  /* Silently return NULL if we cannot open.  */
+  if (f)
     {
       int ch;
       int dirlen = strlen (dirname);
@@ -847,7 +927,7 @@ 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
            {
@@ -868,32 +948,39 @@ read_name_map (pfile, dirname)
       fclose (f);
     }
   
+  /* Add this information to the cache.  */
   map_list_ptr->map_list_next = CPP_OPTION (pfile, map_list);
   CPP_OPTION (pfile, map_list) = map_list_ptr;
 
   return map_list_ptr->map_list_map;
 }  
 
-/* Remap NAME based on the file_name_map (if any) for LOC. */
-
+/* Remap an unsimplified path NAME based on the file_name_map (if any)
+   for LOC.  */
 static char *
 remap_filename (pfile, name, loc)
      cpp_reader *pfile;
      char *name;
-     struct file_name_list *loc;
+     struct search_path *loc;
 {
   struct file_name_map *map;
-  const char *from, *p, *dir;
+  const char *from, *p;
+  char *dir;
 
   if (! loc->name_map)
-    loc->name_map = read_name_map (pfile,
-                                  loc->name
-                                  ? loc->name : ".");
-
-  if (loc->name_map == (struct file_name_map *)-1)
-    return name;
+    {
+      /* 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;
+    }
   
-  from = name + strlen (loc->name) + 1;
+  /* This works since NAME has not been simplified yet.  */
+  from = name + loc->len + 1;
   
   for (map = loc->name_map; map; map = map->map_next)
     if (!strcmp (map->map_from, from))
@@ -905,91 +992,48 @@ remap_filename (pfile, name, loc)
      /usr/include/sys/header.gcc.  */
   p = strrchr (name, '/');
   if (!p)
-    p = name;
-  if (loc && loc->name
-      && strlen (loc->name) == (size_t) (p - name)
-      && !strncmp (loc->name, name, p - name))
-    /* FILENAME is in SEARCHPTR, which we've already checked.  */
     return name;
 
+  /* We know p != name as absolute paths don't call remap_filename.  */
   if (p == name)
-    {
-      dir = ".";
-      from = name;
-    }
-  else
-    {
-      char * newdir = (char *) alloca (p - name + 1);
-      memcpy (newdir, name, p - name);
-      newdir[p - name] = '\0';
-      dir = newdir;
-      from = p + 1;
-    }
+    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, name))
+    if (! strcmp (map->map_from, from))
       return map->map_to;
 
   return name;
 }
 
-/* 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. */
-
-static struct file_name_list *
-actual_directory (pfile, fname)
-     cpp_reader *pfile;
-     const char *fname;
+/* Returns true if it is safe to remove the final component of path,
+   when it is followed by a ".." component.  We use lstat to avoid
+   symlinks if we have it.  If not, we can still catch errors with
+   stat ().  */
+static int
+remove_component_p (path)
+     const char *path;
 {
-  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;
-    }
+  struct stat s;
+  int result;
+
+#ifdef HAVE_LSTAT
+  result = lstat (path, &s);
+#else
+  result = stat (path, &s);
+#endif
+
+  /* There's no guarantee that errno will be unchanged, even on
+     success.  Cygwin's lstat(), for example, will often set errno to
+     ENOSYS.  In case of success, reset errno to zero.  */
+  if (result == 0)
+    errno = 0;
 
-  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 = CPP_BUFFER (pfile)->inc->sysp;
-  x->name_map = NULL;
-
-  pfile->actual_dirs = x;
-  return x;
+  return result == 0 && S_ISDIR (s.st_mode);
 }
 
 /* Simplify a path name in place, deleting redundant components.  This
@@ -1003,400 +1047,122 @@ actual_directory (pfile, fname)
    /../quux            /quux
    //quux              //quux  (POSIX allows leading // as a namespace escape)
 
-   Guarantees no trailing slashes. All transforms reduce the length
-   of the string.
- */
-void
+   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 ().  */
+
+char *
 _cpp_simplify_pathname (path)
     char *path;
 {
-    char *from, *to;
-    char *base;
-    int absolute = 0;
+#ifndef VMS
+  char *from, *to;
+  char *base, *orig_base;
+  int absolute = 0;
+
+  errno = 0;
+  /* Don't overflow the empty path by putting a '.' in it below.  */
+  if (*path == '\0')
+    return path;
 
 #if defined (HAVE_DOS_BASED_FILE_SYSTEM)
-    /* Convert all backslashes to slashes. */
-    for (from = path; *from; from++)
-       if (*from == '\\') *from = '/';
+  /* 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
+  /* 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 (;;)
-    {
-       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 (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;
-           }
-       }
-       else if (from[0] == '.' && from[1] == '.' && from[2] == '\0')
-       {
-           if (base == to)
-           {
-               if (!absolute)
-               {
-                   *to++ = *from++;
-                   *to++ = *from++;
-               }
-           }
-           else
-           {
-               to -= 2;
-               while (to > base && *to != '/') to--;
-               if (*to == '/')
-                   to++;
-           }
-           goto done;
-       }
-       else
-           /* Copy this component and trailing /, if any.  */
-           while ((*to++ = *from++) != '/')
-           {
-               if (!to[-1])
-               {
-                   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;
-}
-
-/* It is not clear when this should be used if at all, so I've
-   disabled it until someone who understands VMS can look at it. */
-#if 0
-
-/* Under VMS we need to fix up the "include" specification filename.
-
-   Rules for possible conversions
-
-       fullname                tried paths
-
-       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
-
-   The path:/name input is constructed when expanding <> includes. */
-
-
-static void
-hack_vms_include_specification (fullname)
-     char *fullname;
-{
-  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 = strchr (fullname, '/');
-  if (first_slash == 0)
-    return 0;                          /* Nothing to do!!! */
-
-  /* construct device spec if none given.  */
-
-  if (strchr (fullname, ':') == 0)
+  /* Remove redundant leading /s.  */
+  if (*from == '/')
     {
-
-      /* If fullname has a slash, take it as device spec.  */
-
-      if (first_slash == fullname)
-       {
-         first_slash = strchr (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).  */
-
-  /* Point to the UNIX filename part (which needs to be fixed!)
-     but skip vms path information.
-     [basename != fullname since first_slash != 0].  */
-
-  if ((basename[-1] == ':')            /* vms path spec.  */
-      || (basename[-1] == ']')
-      || (basename[-1] == '>'))
-    unixname = basename;
-  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.  */
-
-  if (((basename - fullname) > 1)
-     && (  (basename[-1] == ']')
-        || (basename[-1] == '>')))
-    {
-      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,
-          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 */
-
-      *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.
+      while (*from == '/')
+       from++;
 
-     If there are no other slashes then the filename will be
-     in the "root" directory.  Otherwise, we need to add
-     directory specifications.  */
+      if (*from == '\0')
+       break;
 
-  if (strchr (unixname, '/') == 0)
-    {
-      /* if no directories specified yet and none are following.  */
-      if (local_ptr[-1] == '[')
+      if (*from == '.')
        {
-         /* 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
-    {
-
-      /* As long as there are still subdirectories to add, do them.  */
-      while (strchr (unixname, '/') != 0)
-       {
-         /* 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++ = ']';
-    }
-
-  /* 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|O_NONBLOCK);
-      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.  */
+      /* Add the component separator.  */
+      if (to > orig_base)
+       *to++ = '/';
 
-      basename = strchr (fullname, '[');
-      local_ptr = strchr (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';
+
+  return path;
+#else /* VMS  */
+  errno = 0;
+  return path;
+#endif /* !VMS  */
 }
-#endif /* VMS */