]> gcc.gnu.org Git - gcc.git/blobdiff - gcc/cppfiles.c
c-parse.in (language_string): Constify.
[gcc.git] / gcc / cppfiles.c
index 559cc118985aee683d3315463ff1a31d4fa4a587..1e70d3e6c529bb91ef295c9f104b450d3030fe3b 100644 (file)
@@ -1,5 +1,5 @@
 /* Part of CPP library.  (include file handling)
-   Copyright (C) 1986, 87, 89, 92 - 95, 1998 Free Software Foundation, Inc.
+   Copyright (C) 1986, 87, 89, 92 - 95, 98, 1999 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
@@ -25,337 +25,382 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
 #include "config.h"
 #include "system.h"
-#include "gansidecl.h"
 #include "cpplib.h"
 
 /* The entry points to this file are: find_include_file, finclude,
-   append_include_chain, deps_output, and file_cleanup.
+   include_hash, 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 *,
+static struct include_hash *redundant_include_p
+                                       PROTO ((cpp_reader *,
+                                               struct include_hash *,
                                                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 struct file_name_map *read_name_map     PROTO ((cpp_reader *,
+                                                       const char *));
 static char *read_filename_string      PROTO ((int, FILE *));
-static int open_include_file           PROTO ((cpp_reader *, char *,
+static char *remap_filename            PROTO ((cpp_reader *, char *,
                                                struct file_name_list *));
-static int safe_read                   PROTO ((int, char *, int));
+static long read_and_prescan           PROTO ((cpp_reader *, cpp_buffer *,
+                                               int, size_t));
+static struct file_name_list *actual_directory PROTO ((cpp_reader *, char *));
 
-/* Not safe to prototype these. */
-extern char *xmalloc();
-extern char *xrealloc();
+static void initialize_input_buffer    PROTO ((cpp_reader *, int,
+                                               struct stat *));
 
-/* 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 0
+static void hack_vms_include_specification PROTO ((char *));
+#endif
+
+/* Windows does not natively support inodes, and neither does MSDOS.
+   VMS has non-numeric inodes. */
+#ifdef VMS
+#define INO_T_EQ(a, b) (!bcmp((char *) &(a), (char *) &(b), sizeof (a)))
+#elif (defined _WIN32 && !defined CYGWIN && ! defined (_UWIN)) \
+       || defined __MSDOS__
+#define INO_T_EQ(a, b) 0
+#else
+#define INO_T_EQ(a, b) ((a) == (b))
+#endif
+
+/* Merge the four include chains together in the order quote, bracket,
+   system, after.  Remove duplicate dirs (as determined by
+   INO_T_EQ()).  The system_include and after_include chains are never
+   referred to again after this function; all access is through the
+   bracket_include path.
+
+   For the future: Check if the directory is empty (but
+   how?) and possibly preload the include hash. */
 
 void
-append_include_chain (pfile, first, last)
-     cpp_reader *pfile;
-     struct file_name_list *first, *last;
+merge_include_chains (opts)
+     struct cpp_options *opts;
 {
-  struct cpp_options *opts = CPP_OPTIONS (pfile);
-  struct file_name_list *dir;
+  struct file_name_list *prev, *cur, *other;
+  struct file_name_list *quote, *brack, *systm, *after;
+  struct file_name_list *qtail, *btail, *stail, *atail;
+
+  qtail = opts->pending->quote_tail;
+  btail = opts->pending->brack_tail;
+  stail = opts->pending->systm_tail;
+  atail = opts->pending->after_tail;
+
+  quote = opts->pending->quote_head;
+  brack = opts->pending->brack_head;
+  systm = opts->pending->systm_head;
+  after = opts->pending->after_head;
+
+  /* Paste together bracket, system, and after include chains. */
+  if (stail)
+    stail->next = after;
+  else
+    systm = after;
+  if (btail)
+    btail->next = systm;
+  else
+    brack = systm;
 
-  if (!first || !last)
-    return;
+  /* This is a bit tricky.
+     First we drop dupes from the quote-include list.
+     Then we drop dupes from the bracket-include list.
+     Finally, if qtail and brack are the same directory,
+     we cut out qtail.
 
-  if (opts->include == 0)
-    opts->include = first;
-  else
-    opts->last_include->next = first;
+     We can't just merge the lists and then uniquify them because
+     then we may lose directories from the <> search path that should
+     be there; consider -Ifoo -Ibar -I- -Ifoo -Iquux. It is however
+     safe to treat -Ibar -Ifoo -I- -Ifoo -Iquux as if written
+     -Ibar -I- -Ifoo -Iquux.
 
-  if (opts->first_bracket_include == 0)
-    opts->first_bracket_include = first;
+     Note that this algorithm is quadratic in the number of -I switches,
+     which is acceptable since there aren't usually that many of them.  */
 
-  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;
-  }
+  for (cur = quote, prev = NULL; cur; cur = cur->next)
+    {
+      for (other = quote; other != cur; other = other->next)
+        if (INO_T_EQ (cur->ino, other->ino)
+           && cur->dev == other->dev)
+          {
+           if (opts->verbose)
+             cpp_notice ("ignoring duplicate directory `%s'\n", cur->name);
+
+           prev->next = cur->next;
+           free (cur->name);
+           free (cur);
+           cur = prev;
+           break;
+         }
+      prev = cur;
+    }
+  qtail = prev;
+
+  for (cur = brack; cur; cur = cur->next)
+    {
+      for (other = brack; other != cur; other = other->next)
+        if (INO_T_EQ (cur->ino, other->ino)
+           && cur->dev == other->dev)
+          {
+           if (opts->verbose)
+             cpp_notice ("ignoring duplicate directory `%s'\n", cur->name);
+
+           prev->next = cur->next;
+           free (cur->name);
+           free (cur);
+           cur = prev;
+           break;
+         }
+      prev = cur;
+    }
+
+  if (quote)
+    {
+      if (INO_T_EQ (qtail->ino, brack->ino) && qtail->dev == brack->dev)
+        {
+         if (quote == qtail)
+           {
+             if (opts->verbose)
+               cpp_notice ("ignoring duplicate directory `%s'\n",
+                           quote->name);
 
-  last->next = NULL;
-  opts->last_include = last;
+             free (quote->name);
+             free (quote);
+             quote = brack;
+           }
+         else
+           {
+             cur = quote;
+             while (cur->next != qtail)
+                 cur = cur->next;
+             cur->next = brack;
+             if (opts->verbose)
+               cpp_notice ("ignoring duplicate directory `%s'\n",
+                           qtail->name);
+
+             free (qtail->name);
+             free (qtail);
+           }
+       }
+      else
+         qtail->next = brack;
+    }
+  else
+      quote = brack;
+
+  opts->quote_include = quote;
+  opts->bracket_include = brack;
 }
 
-/* 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.  */
+/* Look up or add an entry to the table of all includes.  This table
+ is indexed by the name as it appears in the #include line.  The
+ ->next_this_file chain stores all different files with the same
+ #include name (there are at least three ways this can happen).  The
+ hash function could probably be improved a bit. */
 
-void
-deps_output (pfile, string, spacer)
+struct include_hash *
+include_hash (pfile, fname, add)
      cpp_reader *pfile;
-     char *string;
-     int spacer;
+     char *fname;
+     int add;
 {
-  int size;
+  unsigned int hash = 0;
+  struct include_hash *l, *m;
+  char *f = fname;
 
-  if (!*string)
-    return;
+  while (*f)
+    hash += *f++;
 
-#ifdef VMS
-  hack_vms_include_specification (string);
-#endif
+  l = pfile->all_include_files[hash % ALL_INCLUDE_HASHSIZE];
+  m = 0;
+  for (; l; m = l, l = l->next)
+    if (!strcmp (l->nshort, fname))
+      return l;
 
-  size = strlen (string);
+  if (!add)
+    return 0;
+  
+  l = (struct include_hash *) xmalloc (sizeof (struct include_hash));
+  l->next = NULL;
+  l->next_this_file = NULL;
+  l->foundhere = NULL;
+  l->buf = NULL;
+  l->limit = NULL;
+  if (m)
+    m->next = l;
+  else
+    pfile->all_include_files[hash % ALL_INCLUDE_HASHSIZE] = l;
+  
+  return l;
+}
 
-#ifndef MAX_OUTPUT_COLUMNS
-#define MAX_OUTPUT_COLUMNS 72
-#endif
-  if (spacer
-      && pfile->deps_column > 0
-      && (pfile->deps_column + size) > MAX_OUTPUT_COLUMNS)
-    {
-      deps_output (pfile, " \\\n  ", 0);
-      pfile->deps_column = 0;
-    }
+/* Return 0 if the file pointed to by IHASH has never been included before,
+         -1 if it has been included before and need not be again,
+        or a pointer to an IHASH entry which is the file to be reread.
+   "Never before" is with respect to the position in ILIST.
 
-  if (pfile->deps_size + size + 8 > pfile->deps_allocated_size)
-    {
-      pfile->deps_allocated_size = (pfile->deps_size + size + 50) * 2;
-      pfile->deps_buffer = (char *) xrealloc (pfile->deps_buffer,
-                                             pfile->deps_allocated_size);
-    }
-  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;
+   This will not detect redundancies involving odd uses of the
+   `current directory' rule for "" includes.  They aren't quite
+   pathological, but I think they are rare enough not to worry about.
+   The simplest example is:
+
+   top.c:
+   #include "a/a.h"
+   #include "b/b.h"
+
+   a/a.h:
+   #include "../b/b.h"
+
+   and the problem is that for `current directory' includes,
+   ihash->foundhere is not on any of the global include chains,
+   so the test below (i->foundhere == l) may be false even when
+   the directories are in fact the same.  */
+
+static struct include_hash *
+redundant_include_p (pfile, ihash, ilist)
+     cpp_reader *pfile;
+     struct include_hash *ihash;
+     struct file_name_list *ilist;
+{
+  struct file_name_list *l;
+  struct include_hash *i;
+
+  if (! ihash->foundhere)
+    return 0;
+
+  for (i = ihash; i; i = i->next_this_file)
+    for (l = ilist; l; l = l->next)
+       if (i->foundhere == l)
+        /* The control_macro works like this: If it's NULL, the file
+           is to be included again.  If it's "", the file is never to
+           be included again.  If it's a string, the file is not to be
+           included again if the string is the name of a defined macro. */
+        return (i->control_macro
+                && (i->control_macro[0] == '\0'
+                    || cpp_lookup (pfile, i->control_macro, -1, -1)))
+            ? (struct include_hash *)-1 : i;
+
+  return 0;
 }
 
 static int
 file_cleanup (pbuf, pfile)
      cpp_buffer *pbuf;
-     cpp_reader *pfile ATTRIBUTE_UNUSED;
+     cpp_reader *pfile;
 {
   if (pbuf->buf)
     {
       free (pbuf->buf);
       pbuf->buf = 0;
     }
+  if (pfile->system_include_depth)
+    pfile->system_include_depth--;
   return 0;
 }
 
+/* Search for include file FNAME in the include chain starting at
+   SEARCH_START.  Return -2 if this file doesn't need to be included
+   (because it was included already and it's marked idempotent),
+   -1 if an error occurred, or a file descriptor open on the file.
+   *IHASH is set to point to the include hash entry for this file, and
+   *BEFORE is 1 if the file was included before (but needs to be read
+   again). */
 int
-find_include_file (pfile, fbeg, flen, fname,
-                  importing, search_start, foundhere)
+find_include_file (pfile, fname, search_start, ihash, before)
      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_hash **ihash;
+     int *before;
 {
-  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);
-#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 */
-  }
-  else
-  {
-    /* Search directory path, trying to open the file.
-       Copy each filename tried into FNAME.  */
+  struct file_name_list *l;
+  struct include_hash *ih, *jh;
+  int f, len;
+  char *name;
+  
+  ih = include_hash (pfile, fname, 1);
+  jh = redundant_include_p (pfile, ih,
+                           fname[0] == '/' ? ABSOLUTE_PATH : search_start);
 
-    for (searchptr = search_start; searchptr; searchptr = searchptr->next)
+  if (jh != 0)
     {
-      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++] = '/';
-      }
+      *before = 1;
+      *ihash = jh;
 
-      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))
+      if (jh == (struct include_hash *)-1)
        return -2;
-      if (importing)
-       f = lookup_import (pfile, fname, searchptr);
       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;
+       return open (jh->name, O_RDONLY, 0666);
     }
-  }
 
-  if (f < 0)
+  if (ih->foundhere)
+    /* A file is already known by this name, but it's not the same file.
+       Allocate another include_hash block and add it to the next_this_file
+       chain. */
     {
-      /* A file that was not found.  */
-      bcopy (fbeg, fname, flen);
-      fname[flen] = 0;
+      jh = (struct include_hash *)xmalloc (sizeof (struct include_hash));
+      while (ih->next_this_file) ih = ih->next_this_file;
+
+      ih->next_this_file = jh;
+      jh = ih;
+      ih = ih->next_this_file;
 
-      return -1;
+      ih->next = NULL;
+      ih->next_this_file = NULL;
+      ih->buf = NULL;
+      ih->limit = NULL;
     }
-  else
+  *before = 0;
+  *ihash = ih;
+  ih->nshort = xstrdup (fname);
+  ih->control_macro = NULL;
+  
+  /* If the pathname is absolute, just open it. */ 
+  if (fname[0] == '/')
     {
-      /* Check to see if this include file is a once-only include file.
-        If so, give up.  */
-
-      struct file_name_list *ptr;
-
-      for (ptr = pfile->dont_repeat_files; ptr; ptr = ptr->next)
-         if (!strcmp (ptr->fname, fname))
-           {
-             close (f);
-             return -2;                /* This file was once'd.  */
-           }
+      ih->foundhere = ABSOLUTE_PATH;
+      ih->name = ih->nshort;
+      return open (ih->name, O_RDONLY, 0666);
     }
 
-    /* Record file on "seen" list for #import.  */
-    add_import (pfile, f, fname);
-
-    *foundhere = searchptr;
-    return f;
-}
-
-/* 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.  */
-
-static int
-redundant_include_p (pfile, name)
-     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;
-  return 0;
-}
-
+  /* Search directory path, trying to open the file. */
 
+  len = strlen (fname);
+  name = xmalloc (len + pfile->max_include_len + 2 + INCLUDE_LEN_FUDGE);
 
-/* Maintain and search list of included files, for #import.  */
-
-/* Hash a file name for import_hash_table.  */
-
-static int 
-import_hash (f)
-     char *f;
-{
-  int val = 0;
-
-  while (*f) val += *f++;
-  return (val%IMPORT_HASH_SIZE);
-}
-
-/* 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.  */
+  for (l = search_start; l; l = l->next)
+    {
+      bcopy (l->name, name, l->nlen);
+      name[l->nlen] = '/';
+      strcpy (&name[l->nlen+1], fname);
+      simplify_pathname (name);
+      if (CPP_OPTIONS (pfile)->remap)
+       name = remap_filename (pfile, name, l);
+
+      f = open (name, O_RDONLY|O_NONBLOCK|O_NOCTTY, 0666);
+#ifdef EACCES
+      if (f == -1 && errno == EACCES)
+       {
+         cpp_error(pfile, "included file `%s' exists but is not readable",
+                   name);
+         return -1;
+       }
+#endif
 
-static int
-lookup_import (pfile, filename, searchptr)
-     cpp_reader *pfile;
-     char *filename;
-     struct file_name_list *searchptr;
-{
-  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;
+      if (f >= 0)
+        {
+         ih->foundhere = l;
+         ih->name = xrealloc (name, strlen (name)+1);
+         return f;
+        }
     }
-  }
-  return fd;                   /* Not found, return open file */
-}
-
-/* Add the file FNAME, open on descriptor FD, to import_hash_table.  */
-
-static void
-add_import (pfile, fd, fname)
-     cpp_reader *pfile;
-     int fd;
-     char *fname;
-{
-  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;
+  
+    if (jh)
+      {
+       jh->next_this_file = NULL;
+       free (ih);
+      }
+    free (name);
+    *ihash = (struct include_hash *)-1;
+    return -1;
 }
 
 /* The file_name_map structure holds a mapping of file names for a
@@ -421,7 +466,7 @@ struct file_name_map_list
 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;
   char *name;
@@ -434,8 +479,7 @@ 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 = savestring (dirname);
-  map_list_ptr->map_list_map = NULL;
+  map_list_ptr->map_list_name = xstrdup (dirname);
 
   name = (char *) alloca (strlen (dirname) + strlen (FILE_NAME_MAP_FILE) + 2);
   strcpy (name, dirname);
@@ -444,7 +488,7 @@ read_name_map (pfile, dirname)
   strcat (name, FILE_NAME_MAP_FILE);
   f = fopen (name, "r");
   if (!f)
-    map_list_ptr->map_list_map = NULL;
+    map_list_ptr->map_list_map = (struct file_name_map *)-1;
   else
     {
       int ch;
@@ -494,261 +538,799 @@ read_name_map (pfile, dirname)
   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.  */
+/* Remap NAME based on the file_name_map (if any) for LOC. */
 
-static int
-open_include_file (pfile, filename, searchptr)
+static char *
+remap_filename (pfile, name, loc)
      cpp_reader *pfile;
-     char *filename;
-     struct file_name_list *searchptr;
+     char *name;
+     struct file_name_list *loc;
 {
-  if (CPP_OPTIONS (pfile)->remap)
+  struct file_name_map *map;
+  const char *from, *p, *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;
+  
+  from = name + strlen (loc->name) + 1;
+  
+  for (map = loc->name_map; map; map = map->map_next)
+    if (!strcmp (map->map_from, from))
+      return map->map_to;
+
+  /* Try to find a mapping file for the particular directory we are
+     looking in.  Thus #include <sys/types.h> will look up sys/types.h
+     in /usr/include/header.gcc and look up types.h in
+     /usr/include/sys/header.gcc.  */
+  p = rindex (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;
+
+  if (p == name)
     {
-      register struct file_name_map *map;
-      register char *from;
-      char *p, *dir;
+      dir = ".";
+      from = name;
+    }
+  else
+    {
+      char * newdir = (char *) alloca (p - name + 1);
+      bcopy (name, newdir, p - name);
+      newdir[p - name] = '\0';
+      dir = newdir;
+      from = p + 1;
+    }
+  
+  for (map = read_name_map (pfile, dir); map; map = map->map_next)
+    if (! strcmp (map->map_from, name))
+      return map->map_to;
+
+  return name;
+}
+
+/* Read the contents of FD into the buffer on the top of PFILE's stack.
+   IHASH points to the include hash entry for the file associated with
+   FD.
+
+   The caller is responsible for the cpp_push_buffer.  */
+
+int
+finclude (pfile, fd, ihash)
+     cpp_reader *pfile;
+     int fd;
+     struct include_hash *ihash;
+{
+  struct stat st;
+  size_t st_size;
+  long length;
+  cpp_buffer *fp;
+
+  if (fstat (fd, &st) < 0)
+    goto perror_fail;
+  if (fcntl (fd, F_SETFL, 0) == -1)  /* turn off nonblocking mode */
+    goto perror_fail;
 
-      if (searchptr && ! searchptr->got_name_map)
+  fp = CPP_BUFFER (pfile);
+
+  /* If fd points to a plain file, we know how big it is, so we can
+     allocate the buffer all at once.  If fd is a pipe or terminal, we
+     can't.  Most C source files are 4k or less, so we guess that.  If
+     fd is something weird, like a block device or a directory, 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 the
+     mess below.
+
+     In all cases, read_and_prescan will resize the buffer if it
+     turns out there's more data than we thought.  */
+
+  if (S_ISREG (st.st_mode))
+    {
+      /* off_t might have a wider range than size_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 4GB needs to rethink
+        their coding style.)  */
+      st_size = (size_t) st.st_size;
+      if ((unsigned HOST_WIDEST_INT) st_size
+         != (unsigned HOST_WIDEST_INT) st.st_size)
        {
-         searchptr->name_map = read_name_map (pfile,
-                                              searchptr->fname
-                                              ? searchptr->fname : ".");
-         searchptr->got_name_map = 1;
+         cpp_error (pfile, "file `%s' is too large", ihash->name);
+         goto fail;
        }
+    }
+  else if (S_ISFIFO (st.st_mode) || S_ISSOCK (st.st_mode)
+          /* Permit any kind of character device: the sensible ones are
+             ttys and /dev/null, but weeding out the others is too hard.  */
+          || S_ISCHR (st.st_mode)
+          /* Some 4.x (x<4) derivatives have a bug that makes fstat() of a
+             socket or pipe return a stat struct with most fields zeroed.  */
+          || (st.st_mode == 0 && st.st_nlink == 0 && st.st_size == 0))
+    {
+      /* Cannot get its file size before reading.  4k is a decent
+         first guess. */
+      st_size = 4096;
+    }
+  else
+    {
+      cpp_error (pfile, "`%s' is not a file, pipe, or tty", ihash->name);
+      goto fail;
+    }
+
+  if (pfile->input_buffer == NULL)
+    initialize_input_buffer (pfile, fd, &st);
+
+  /* Read the file, converting end-of-line characters and trigraphs
+     (if enabled). */
+  fp->ihash = ihash;
+  fp->nominal_fname = fp->fname = ihash->name;
+  length = read_and_prescan (pfile, fp, fd, st_size);
+  if (length < 0)
+    goto fail;
+  if (length == 0)
+    ihash->control_macro = "";  /* never re-include */
+
+  close (fd);
+  fp->rlimit = fp->alimit = fp->buf + length;
+  fp->cur = fp->buf;
+  if (ihash->foundhere != ABSOLUTE_PATH)
+      fp->system_header_p = ihash->foundhere->sysp;
+  fp->lineno = 1;
+  fp->colno = 1;
+  fp->line_base = fp->buf;
+  fp->cleanup = file_cleanup;
+
+  /* The ->actual_dir field is only used when ignore_srcdir is not in effect;
+     see do_include */
+  if (!CPP_OPTIONS (pfile)->ignore_srcdir)
+    fp->actual_dir = actual_directory (pfile, fp->fname);
 
-      /* First check the mapping for the directory we are using.  */
-      if (searchptr && searchptr->name_map)
+  pfile->input_stack_listing_current = 0;
+  return 1;
+
+ perror_fail:
+  cpp_error_from_errno (pfile, ihash->name);
+ fail:
+  cpp_pop_buffer (pfile);
+  close (fd);
+  return 0;
+}
+
+/* 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;
+     char *fname;
+{
+  char *last_slash, *dir;
+  size_t dlen;
+  struct file_name_list *x;
+  
+  dir = xstrdup (fname);
+  last_slash = rindex (dir, '/');
+  if (last_slash)
+    {
+      if (last_slash == dir)
+        {
+         dlen = 1;
+         last_slash[1] = '\0';
+       }
+      else
+       {
+         dlen = last_slash - dir;
+         *last_slash = '\0';
+       }
+    }
+  else
+    {
+      dir[0] = '.';
+      dir[1] = '\0';
+      dlen = 1;
+    }
+
+  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_OPTIONS (pfile)->quote_include;
+  x->alloc = pfile->actual_dirs;
+  x->sysp = CPP_BUFFER (pfile)->system_header_p;
+  x->name_map = NULL;
+
+  pfile->actual_dirs = x;
+  return x;
+}
+
+/* Determine the current line and column.  Used only by read_and_prescan. */
+static void
+find_position (start, limit, linep, colp)
+     U_CHAR *start;
+     U_CHAR *limit;
+     unsigned long *linep;
+     unsigned long *colp;
+{
+  unsigned long line = *linep, col = 0;
+  while (start < limit)
+    {
+      U_CHAR ch = *start++;
+      if (ch == '\n' || ch == '\r')
+       line++, col = 1;
+      else
+       col++;
+    }
+  *linep = line, *colp = col;
+}
+
+/* Read the entire contents of file DESC into buffer BUF.  LEN is how
+   much memory to allocate initially; more will be allocated if
+   necessary.  Convert end-of-line markers (\n, \r, \r\n, \n\r) to
+   canonical form (\n).  If enabled, convert and/or warn about
+   trigraphs.  Convert backslash-newline to a one-character escape
+   (\r) and remove it from "embarrassing" places (i.e. the middle of a
+   token).  If there is no newline at the end of the file, add one and
+   warn.  Returns -1 on failure, or the actual length of the data to
+   be scanned.
+
+   This function does a lot of work, and can be a serious performance
+   bottleneck.  It has been tuned heavily; make sure you understand it
+   before hacking.  The common case - no trigraphs, Unix style line
+   breaks, backslash-newline set off by whitespace, newline at EOF -
+   has been optimized at the expense of the others.  The performance
+   penalty for DOS style line breaks (\r\n) is about 15%.
+   
+   Warnings lose particularly heavily since we have to determine the
+   line number, which involves scanning from the beginning of the file
+   or from the last warning.  The penalty for the absence of a newline
+   at the end of reload1.c is about 60%.  (reload1.c is 329k.)
+
+   If your file has more than one kind of end-of-line marker, you
+   will get messed-up line numbering.  */
+
+/* Table of characters that can't be handled in the inner loop.
+   Keep these contiguous to optimize the performance of the code generated
+   for the switch that uses them.  */
+#define SPECCASE_EMPTY     0
+#define SPECCASE_NUL       1
+#define SPECCASE_CR        2
+#define SPECCASE_BACKSLASH 3
+#define SPECCASE_QUESTION  4
+
+static long
+read_and_prescan (pfile, fp, desc, len)
+     cpp_reader *pfile;
+     cpp_buffer *fp;
+     int desc;
+     size_t len;
+{
+  U_CHAR *buf = (U_CHAR *) xmalloc (len);
+  U_CHAR *ip, *op, *line_base;
+  U_CHAR *ibase;
+  U_CHAR *speccase = pfile->input_speccase;
+  unsigned long line;
+  unsigned int deferred_newlines;
+  int count;
+  size_t offset;
+
+  offset = 0;
+  op = buf;
+  line_base = buf;
+  line = 1;
+  ibase = pfile->input_buffer + 2;
+  deferred_newlines = 0;
+
+  for (;;)
+    {
+    read_next:
+
+      count = read (desc, pfile->input_buffer + 2, pfile->input_buffer_len);
+      if (count < 0)
+       goto error;
+      else if (count == 0)
+       break;
+
+      offset += count;
+      ip = ibase;
+      ibase = pfile->input_buffer + 2;
+      ibase[count] = ibase[count+1] = '\0';
+
+      if (offset > len)
        {
-         from = filename;
-         if (searchptr->fname)
-           from += strlen (searchptr->fname) + 1;
-         for (map = searchptr->name_map; map; map = map->map_next)
+         size_t delta_op;
+         size_t delta_line_base;
+         len *= 2;
+         if (offset > len)
+           /* len overflowed.
+              This could happen if the file is larger than half the
+              maximum address space of the machine. */
+           goto too_big;
+
+         delta_op = op - buf;
+         delta_line_base = line_base - buf;
+         buf = (U_CHAR *) xrealloc (buf, len);
+         op = buf + delta_op;
+         line_base = buf + delta_line_base;
+       }
+
+      for (;;)
+       {
+         unsigned int span = 0;
+
+         /* Deal with \-newline in the middle of a token. */
+         if (deferred_newlines)
            {
-             if (! strcmp (map->map_from, from))
+             while (speccase[ip[span]] == SPECCASE_EMPTY
+                    && ip[span] != '\n'
+                    && ip[span] != '\t'
+                    && ip[span] != ' ')
+               span++;
+             memcpy (op, ip, span);
+             op += span;
+             ip += span;
+             if (*ip == '\n' || *ip == '\t'
+                 || *ip == ' ' || *ip == ' ')
+               while (deferred_newlines)
+                 deferred_newlines--, *op++ = '\r';
+             span = 0;
+           }
+
+         /* Copy as much as we can without special treatment. */
+         while (speccase[ip[span]] == SPECCASE_EMPTY) span++;
+         memcpy (op, ip, span);
+         op += span;
+         ip += span;
+
+         switch (speccase[*ip++])
+           {
+           case SPECCASE_NUL:  /* \0 */
+             ibase[-1] = op[-1];
+             goto read_next;
+
+           case SPECCASE_CR:  /* \r */
+             if (*ip == '\n')
+               ip++;
+             else if (*ip == '\0')
+               {
+                 *--ibase = '\r';
+                 goto read_next;
+               }
+             else if (ip[-2] == '\n')
+               continue;
+             *op++ = '\n';
+             break;
+
+           case SPECCASE_BACKSLASH:  /* \ */
+           backslash:
+           {
+             /* If we're at the end of the intermediate buffer,
+                we have to shift the backslash down to the start
+                and come back next pass. */
+             if (*ip == '\0')
                {
-                 /* Found a match.  */
-                 return open (map->map_to, O_RDONLY, 0666);
+                 *--ibase = '\\';
+                 goto read_next;
                }
+             else if (*ip == '\n')
+               {
+                 ip++;
+                 if (*ip == '\r') ip++;
+                 if (*ip == '\n' || *ip == '\t' || *ip == ' ')
+                   *op++ = '\r';
+                 else if (op[-1] == '\t' || op[-1] == ' '
+                          || op[-1] == '\r' || op[-1] == '\n')
+                   *op++ = '\r';
+                 else
+                   deferred_newlines++;
+                 line++;
+                 line_base = op;
+               }
+             else if (*ip == '\r')
+               {
+                 ip++;
+                 if (*ip == '\n') ip++;
+                 else if (*ip == '\0')
+                   {
+                     *--ibase = '\r';
+                     *--ibase = '\\';
+                     goto read_next;
+                   }
+                 else if (*ip == '\r' || *ip == '\t' || *ip == ' ')
+                   *op++ = '\r';
+                 else
+                   deferred_newlines++;
+                 line++;
+                 line_base = op;
+               }
+             else
+               *op++ = '\\';
+           }
+           break;
+
+           case SPECCASE_QUESTION: /* ? */
+             {
+               unsigned int d;
+               /* If we're at the end of the intermediate buffer,
+                  we have to shift the ?'s down to the start and
+                  come back next pass. */
+               d = ip[0];
+               if (d == '\0')
+                 {
+                   *--ibase = '?';
+                   goto read_next;
+                 }
+               if (d != '?')
+                 {
+                   *op++ = '?';
+                   break;
+                 }
+               d = ip[1];
+               if (d == '\0')
+                 {
+                   *--ibase = '?';
+                   *--ibase = '?';
+                   goto read_next;
+                 }
+               if (!trigraph_table[d])
+                 {
+                   *op++ = '?';
+                   break;
+                 }
+
+               if (CPP_OPTIONS (pfile)->warn_trigraphs)
+                 {
+                   unsigned long col;
+                   find_position (line_base, op, &line, &col);
+                   line_base = op - col;
+                   cpp_warning_with_line (pfile, line, col,
+                                          "trigraph ??%c encountered", d);
+                 }
+               if (CPP_OPTIONS (pfile)->trigraphs)
+                 {
+                   if (trigraph_table[d] == '\\')
+                     goto backslash;
+                   else
+                     *op++ = trigraph_table[d];
+                 }
+               else
+                 {
+                   *op++ = '?';
+                   *op++ = '?';
+                   *op++ = d;
+                 }
+               ip += 2;
+             }
            }
        }
+    }
 
-      /* 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);
-       }
+  if (offset == 0)
+    return 0;
 
-      if (p == filename)
+  /* Deal with pushed-back chars at true EOF.
+     This may be any of:  ?? ? \ \r \n \\r \\n.
+     \r must become \n, \\r or \\n must become \r.
+     We know we have space already. */
+  if (ibase == pfile->input_buffer)
+    {
+      if (*ibase == '?')
        {
-         dir = ".";
-         from = filename;
+         *op++ = '?';
+         *op++ = '?';
        }
       else
+       *op++ = '\r';
+    }
+  else if (ibase == pfile->input_buffer + 1)
+    {
+      if (*ibase == '\r')
+       *op++ = '\n';
+      else
+       *op++ = *ibase;
+    }
+
+  if (op[-1] != '\n')
+    {
+      unsigned long col;
+      find_position (line_base, op, &line, &col);
+      cpp_warning_with_line (pfile, line, col, "no newline at end of file\n");
+      if (offset + 1 > len)
        {
-         dir = (char *) alloca (p - filename + 1);
-         bcopy (filename, dir, p - filename);
-         dir[p - filename] = '\0';
-         from = p + 1;
+         len += 1;
+         if (offset + 1 > len)
+           goto too_big;
+         buf = (U_CHAR *) xrealloc (buf, len);
+         op = buf + offset;
        }
-      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);
+      *op++ = '\n';
     }
 
-  return open (filename, O_RDONLY, 0666);
-}
+  fp->buf = ((len - offset < 20) ? buf : (U_CHAR *)xrealloc (buf, op - buf));
+  return op - buf;
 
-/* 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.
+ too_big:
+  cpp_error (pfile, "file is too large (>%lu bytes)\n", (unsigned long)offset);
+  free (buf);
+  return -1;
 
-   The caller is responsible for the cpp_push_buffer.  */
+ error:
+  cpp_error_from_errno (pfile, fp->fname);
+  free (buf);
+  return -1;
+}
 
-int
-finclude (pfile, f, fname, system_header_p, dirptr)
+/* Initialize the `input_buffer' and `input_speccase' tables.
+   These are only used by read_and_prescan, but they're large and
+   somewhat expensive to set up, so we want them allocated once for
+   the duration of the cpp run.  */
+
+static void
+initialize_input_buffer (pfile, fd, st)
      cpp_reader *pfile;
-     int f;
-     char *fname;
-     int system_header_p;
-     struct file_name_list *dirptr;
+     int fd;
+     struct stat *st;
 {
-  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
-
-  if (fstat (f, &st) < 0)
+  long pipe_buf;
+  U_CHAR *tmp;
+
+  /* Table of characters that cannot be handled by the
+     read_and_prescan inner loop.  The number of non-EMPTY entries
+     should be as small as humanly possible.  */
+
+  tmp = (U_CHAR *) xmalloc (1 << CHAR_BIT);
+  memset (tmp, SPECCASE_EMPTY, 1 << CHAR_BIT);
+  tmp['\0'] = SPECCASE_NUL;
+  tmp['\r'] = SPECCASE_CR;
+  tmp['\\'] = SPECCASE_BACKSLASH;
+  if (CPP_OPTIONS (pfile)->trigraphs || CPP_OPTIONS (pfile)->warn_trigraphs)
+    tmp['?'] = SPECCASE_QUESTION;
+
+  pfile->input_speccase = tmp;
+
+  /* Determine the appropriate size for the input buffer.  Normal C
+     source files are smaller than eight K.  If we are reading a pipe,
+     we want to make sure the input buffer is bigger than the kernel's
+     pipe buffer.  */
+  pipe_buf = -1;
+
+  if (! S_ISREG (st->st_mode))
     {
-      cpp_perror_with_name (pfile, fname);
-      close (f);
-      cpp_pop_buffer (pfile);
-      return 0;
-    }
-
-  fp = CPP_BUFFER (pfile);
-  fp->nominal_fname = fp->fname = fname;
-#if 0
-  fp->length = 0;
+#ifdef _PC_PIPE_BUF
+      pipe_buf = fpathconf (fd, _PC_PIPE_BUF);
 #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;
+      if (pipe_buf == -1)
+       {
+#ifdef PIPE_BUF
+         pipe_buf = PIPE_BUF;
+#else
+         pipe_buf = 8192;
 #endif
-  }
-  fp->buf[length] = '\0';
-  fp->rlimit = fp->buf + length;
-
-  /* Close descriptor now, so nesting does not use lots of descriptors.  */
-  close (f);
+       }
+    }
 
-  /* Must do this before calling trigraph_pcp, so that the correct file name
-     will be printed in warning messages.  */
+  if (pipe_buf < 8192)
+    pipe_buf = 8192;
+  /* PIPE_BUF bytes of buffer proper, 2 to detect running off the end
+     without address arithmetic all the time, and 2 for pushback in
+     the case there's a potential trigraph or end-of-line digraph at
+     the end of a block. */
 
-  pfile->input_stack_listing_current = 0;
+  tmp = (U_CHAR *) xmalloc (pipe_buf + 2 + 2);
+  pfile->input_buffer = tmp;
+  pfile->input_buffer_len = pipe_buf;
+}
 
-#if 0
-  if (!no_trigraphs)
-    trigraph_pcp (fp);
-#endif
+/* 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.  */
 
-#if 0
-  rescan (op, 0);
+void
+deps_output (pfile, string, spacer)
+     cpp_reader *pfile;
+     char *string;
+     int spacer;
+{
+  int size;
+  int cr = 0;
 
-  if (missing_newline)
-    fp->lineno--;
+  if (!*string)
+    return;
 
-  if (CPP_PEDANTIC (pfile) && missing_newline)
-    pedwarn ("file does not end in newline");
+  size = strlen (string);
 
-  indepth--;
-  input_file_stack_tick++;
-  free (fp->buf);
+#ifndef MAX_OUTPUT_COLUMNS
+#define MAX_OUTPUT_COLUMNS 72
 #endif
-  return 1;
+  if (pfile->deps_column > 0
+      && (pfile->deps_column + size) > MAX_OUTPUT_COLUMNS)
+    {
+      cr = 5;
+      pfile->deps_column = 0;
+    }
 
- nope:
+  if (pfile->deps_size + size + cr + 8 > pfile->deps_allocated_size)
+    {
+      pfile->deps_allocated_size = (pfile->deps_size + size + 50) * 2;
+      pfile->deps_buffer = (char *) xrealloc (pfile->deps_buffer,
+                                             pfile->deps_allocated_size);
+    }
 
-  cpp_perror_with_name (pfile, fname);
-  close (f);
-  free (fp->buf);
-  return 1;
+  if (cr)
+    {
+      bcopy (" \\\n  ", &pfile->deps_buffer[pfile->deps_size], 5);
+      pfile->deps_size += 5;
+    }
+  
+  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;
 }
 
-/* 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.  */
+/* Simplify a path name in place, deleting redundant components.  This
+   reduces OS overhead and guarantees that equivalent paths compare
+   the same (modulo symlinks).
 
-static int
-safe_read (desc, ptr, len)
-     int desc;
-     char *ptr;
-     int len;
+   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)
+
+   Guarantees no trailing slashes. All transforms reduce the length
+   of the string.
+ */
+void
+simplify_pathname (path)
+    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;
+    char *from, *to;
+    char *base;
+    int absolute = 0;
+
+#if defined (HAVE_DOS_BASED_FILE_SYSTEM)
+    /* Convert all backslashes to slashes. */
+    for (from = path; *from; from++)
+       if (*from == '\\') *from = '/';
+    
+    /* Skip over leading drive letter if present. */
+    if (ISALPHA (path[0]) && path[1] == ':')
+       from = to = &path[2];
+    else
+       from = to = path;
+#else
+    from = to = path;
 #endif
-       return nchars;
-      }
-    if (nchars == 0)
-      break;
-    ptr += nchars;
-    left -= nchars;
-  }
-  return len - left;
+    
+    /* 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;
 }
 
-#ifdef VMS
+/* 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.
 
This page took 0.062648 seconds and 5 git commands to generate.