(Fdelete_file): Undo Sep 16 change.
[bpt/emacs.git] / src / fileio.c
index 24f53e9..a06032a 100644 (file)
@@ -1,5 +1,5 @@
 /* File IO for GNU Emacs.
-   Copyright (C) 1985, 1986, 1987, 1988, 1992, 1993 Free Software Foundation, Inc.
+   Copyright (C) 1985, 1986, 1987, 1988, 1993, 1994 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -17,21 +17,38 @@ You should have received a copy of the GNU General Public License
 along with GNU Emacs; see the file COPYING.  If not, write to
 the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 
-#include "config.h"
+#include <config.h>
 
 #include <sys/types.h>
 #include <sys/stat.h>
 
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#if !defined (S_ISLNK) && defined (S_IFLNK)
+#  define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
+#endif
+
+#if !defined (S_ISREG) && defined (S_IFREG)
+#  define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
+#endif
+
 #ifdef VMS
 #include "vms-pwd.h"
 #else
 #include <pwd.h>
 #endif
 
+#ifdef MSDOS
+#include "msdos.h"
+#include <sys/param.h>
+#endif
+
 #include <ctype.h>
 
 #ifdef VMS
-#include "dir.h"
+#include "vmsdir.h"
 #include <perror.h>
 #include <stddef.h>
 #include <string.h>
@@ -41,11 +58,9 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 
 #ifndef vax11c
 extern int errno;
-extern char *sys_errlist[];
-extern int sys_nerr;
 #endif
 
-#define err_str(a) ((a) < sys_nerr ? sys_errlist[a] : "unknown error")
+extern char *strerror ();
 
 #ifdef APOLLO
 #include <sys/time.h>
@@ -76,14 +91,20 @@ extern int sys_nerr;
 #ifdef HPUX
 #include <netio.h>
 #ifndef HPUX8
+#ifndef HPUX9
 #include <errnet.h>
 #endif
 #endif
+#endif
 
 #ifndef O_WRONLY
 #define O_WRONLY 1
 #endif
 
+#ifndef O_RDONLY
+#define O_RDONLY 0
+#endif
+
 #define min(a, b) ((a) < (b) ? (a) : (b))
 #define max(a, b) ((a) > (b) ? (a) : (b))
 
@@ -98,6 +119,19 @@ int auto_save_mode_bits;
    whose I/O is done with a special handler.  */
 Lisp_Object Vfile_name_handler_alist;
 
+/* Functions to be called to process text properties in inserted file.  */
+Lisp_Object Vafter_insert_file_functions;
+
+/* Functions to be called to create text property annotations for file.  */
+Lisp_Object Vwrite_region_annotate_functions;
+
+/* During build_annotations, each time an annotation function is called,
+   this holds the annotations made by the previous functions.  */
+Lisp_Object Vwrite_region_annotations_so_far;
+
+/* File name in which we write a list of all our auto save files.  */
+Lisp_Object Vauto_save_list_file_name;
+
 /* Nonzero means, when reading a filename in the minibuffer,
  start out by inserting the default directory into the minibuffer. */
 int insert_default_directory;
@@ -106,20 +140,29 @@ int insert_default_directory;
    Zero means use var format.  */
 int vms_stmlf_recfm;
 
+/* These variables describe handlers that have "already" had a chance
+   to handle the current operation.
+
+   Vinhibit_file_name_handlers is a list of file name handlers.
+   Vinhibit_file_name_operation is the operation being handled.
+   If we try to handle that operation, we ignore those handlers.  */
+
+static Lisp_Object Vinhibit_file_name_handlers;
+static Lisp_Object Vinhibit_file_name_operation;
+
 Lisp_Object Qfile_error, Qfile_already_exists;
 
 Lisp_Object Qfile_name_history;
 
+Lisp_Object Qcar_less_than_car;
+
 report_file_error (string, data)
      char *string;
      Lisp_Object data;
 {
   Lisp_Object errstring;
 
-  if (errno >= 0 && errno < sys_nerr)
-    errstring = build_string (sys_errlist[errno]);
-  else
-    errstring = build_string ("undocumented error code");
+  errstring = build_string (strerror (errno));
 
   /* System error messages are capitalized.  Downcase the initial
      unless it is followed by a slash.  */
@@ -136,6 +179,15 @@ close_file_unwind (fd)
 {
   close (XFASTINT (fd));
 }
+
+/* Restore point, having saved it as a marker.  */
+
+restore_point_unwind (location)
+     Lisp_Object location; 
+{
+  SET_PT (marker_position (location));
+  Fset_marker (location, Qnil, Qnil);
+}
 \f
 Lisp_Object Qexpand_file_name;
 Lisp_Object Qdirectory_file_name;
@@ -144,7 +196,7 @@ Lisp_Object Qfile_name_nondirectory;
 Lisp_Object Qunhandled_file_name_directory;
 Lisp_Object Qfile_name_as_directory;
 Lisp_Object Qcopy_file;
-Lisp_Object Qmake_directory;
+Lisp_Object Qmake_directory_internal;
 Lisp_Object Qdelete_directory;
 Lisp_Object Qdelete_file;
 Lisp_Object Qrename_file;
@@ -163,32 +215,48 @@ Lisp_Object Qfile_newer_than_file_p;
 Lisp_Object Qinsert_file_contents;
 Lisp_Object Qwrite_region;
 Lisp_Object Qverify_visited_file_modtime;
+Lisp_Object Qset_visited_file_modtime;
 
-DEFUN ("find-file-name-handler", Ffind_file_name_handler, Sfind_file_name_handler, 1, 1, 0,
-  "Return FILENAME's handler function, if its syntax is handled specially.\n\
+DEFUN ("find-file-name-handler", Ffind_file_name_handler, Sfind_file_name_handler, 2, 2, 0,
+  "Return FILENAME's handler function for OPERATION, if it has one.\n\
 Otherwise, return nil.\n\
 A file name is handled if one of the regular expressions in\n\
-`file-name-handler-alist' matches it.")
-  (filename)
-    Lisp_Object filename;
+`file-name-handler-alist' matches it.\n\n\
+If OPERATION equals `inhibit-file-name-operation', then we ignore\n\
+any handlers that are members of `inhibit-file-name-handlers',\n\
+but we still do run any other handlers.  This lets handlers\n\
+use the standard functions without calling themselves recursively.")
+  (filename, operation)
+    Lisp_Object filename, operation;
 {
   /* This function must not munge the match data.  */
-  Lisp_Object chain;
+  Lisp_Object chain, inhibited_handlers;
 
   CHECK_STRING (filename, 0);
 
-  for (chain = Vfile_name_handler_alist; XTYPE (chain) == Lisp_Cons;
+  if (EQ (operation, Vinhibit_file_name_operation))
+    inhibited_handlers = Vinhibit_file_name_handlers;
+  else
+    inhibited_handlers = Qnil;
+
+  for (chain = Vfile_name_handler_alist; CONSP (chain);
        chain = XCONS (chain)->cdr)
     {
       Lisp_Object elt;
       elt = XCONS (chain)->car;
-      if (XTYPE (elt) == Lisp_Cons)
+      if (CONSP (elt))
        {
          Lisp_Object string;
          string = XCONS (elt)->car;
-         if (XTYPE (string) == Lisp_String
-             && fast_string_match (string, filename) >= 0)
-           return XCONS (elt)->cdr;
+         if (STRINGP (string) && fast_string_match (string, filename) >= 0)
+           {
+             Lisp_Object handler, tem;
+
+             handler = XCONS (elt)->cdr;
+             tem = Fmemq (handler, inhibited_handlers);
+             if (NILP (tem))
+               return handler;
+           }
        }
 
       QUIT;
@@ -214,10 +282,13 @@ on VMS, perhaps instead a string ending in `:', `]' or `>'.")
 
   /* If the file name has special constructs in it,
      call the corresponding file handler.  */
-  handler = Ffind_file_name_handler (file);
+  handler = Ffind_file_name_handler (file, Qfile_name_directory);
   if (!NILP (handler))
     return call2 (handler, Qfile_name_directory, file);
 
+#ifdef FILE_SYSTEM_CASE
+  file = FILE_SYSTEM_CASE (file);
+#endif
   beg = XSTRING (file)->data;
   p = beg + XSTRING (file)->size;
 
@@ -225,10 +296,31 @@ on VMS, perhaps instead a string ending in `:', `]' or `>'.")
 #ifdef VMS
         && p[-1] != ':' && p[-1] != ']' && p[-1] != '>'
 #endif /* VMS */
+#ifdef MSDOS
+        && p[-1] != ':' && p[-1] != '\\'
+#endif
         ) p--;
 
   if (p == beg)
     return Qnil;
+#ifdef MSDOS
+  /* Expansion of "c:" to drive and default directory.  */
+  if (p == beg + 2 && beg[1] == ':')
+    {
+      int drive = (*beg) - 'a';
+      /* MAXPATHLEN+1 is guaranteed to be enough space for getdefdir.  */
+      unsigned char *res = alloca (MAXPATHLEN + 5);
+      if (getdefdir (drive + 1, res + 2)) 
+       {
+         res[0] = drive + 'a';
+         res[1] = ':';
+         if (res[strlen (res) - 1] != '/')
+           strcat (res, "/");
+         beg = res;
+         p = beg + strlen (beg);
+       }
+    }
+#endif
   return make_string (beg, p - beg);
 }
 
@@ -248,7 +340,7 @@ or the entire name if it contains no slash.")
 
   /* If the file name has special constructs in it,
      call the corresponding file handler.  */
-  handler = Ffind_file_name_handler (file);
+  handler = Ffind_file_name_handler (file, Qfile_name_nondirectory);
   if (!NILP (handler))
     return call2 (handler, Qfile_name_nondirectory, file);
 
@@ -259,6 +351,9 @@ or the entire name if it contains no slash.")
 #ifdef VMS
         && p[-1] != ':' && p[-1] != ']' && p[-1] != '>'
 #endif /* VMS */
+#ifdef MSDOS
+        && p[-1] != ':' && p[-1] != '\\'
+#endif
         ) p--;
 
   return make_string (p, end - p);
@@ -279,7 +374,7 @@ get a current directory to run processes in.")
 
   /* If the file name has special constructs in it,
      call the corresponding file handler.  */
-  handler = Ffind_file_name_handler (filename);
+  handler = Ffind_file_name_handler (filename, Qunhandled_file_name_directory);
   if (!NILP (handler))
     return call2 (handler, Qunhandled_file_name_directory, filename);
 
@@ -354,7 +449,11 @@ file_name_as_directory (out, in)
     }
 #else /* not VMS */
   /* For Unix syntax, Append a slash if necessary */
+#ifdef MSDOS
+  if (out[size] != ':' && out[size] != '/' && out[size] != '\\')
+#else
   if (out[size] != '/')
+#endif
     strcat (out, "/");
 #endif /* not VMS */
   return out;
@@ -381,7 +480,7 @@ On VMS, converts \"[X]FOO.DIR\" to \"[X.FOO]\", etc.")
 
   /* If the file name has special constructs in it,
      call the corresponding file handler.  */
-  handler = Ffind_file_name_handler (file);
+  handler = Ffind_file_name_handler (file, Qfile_name_as_directory);
   if (!NILP (handler))
     return call2 (handler, Qfile_name_as_directory, file);
 
@@ -530,7 +629,14 @@ directory_file_name (src, dst)
   /* Process as Unix format: just remove any final slash.
      But leave "/" unchanged; do not change it to "".  */
   strcpy (dst, src);
-  if (slen > 1 && dst[slen - 1] == '/')
+  if (slen > 1 
+#ifdef MSDOS
+      && (dst[slen - 1] == '/' || dst[slen - 1] == '/')
+      && dst[slen - 2] != ':'
+#else
+      && dst[slen - 1] == '/'
+#endif
+      )
     dst[slen - 1] = 0;
   return 1;
 }
@@ -557,7 +663,7 @@ it returns a file name such as \"[X]Y.DIR.1\".")
 
   /* If the file name has special constructs in it,
      call the corresponding file handler.  */
-  handler = Ffind_file_name_handler (directory);
+  handler = Ffind_file_name_handler (directory, Qdirectory_file_name);
   if (!NILP (handler))
     return call2 (handler, Qdirectory_file_name, directory);
 
@@ -615,13 +721,18 @@ See also the function `substitute-in-file-name'.")
   int lbrack = 0, rbrack = 0;
   int dots = 0;
 #endif /* VMS */
+#ifdef MSDOS   /* Demacs 1.1.2 91/10/20 Manabu Higashida */
+  int drive = -1;
+  int relpath = 0;
+  unsigned char *tmp, *defdir;
+#endif
   Lisp_Object handler;
   
   CHECK_STRING (name, 0);
 
   /* If the file name has special constructs in it,
      call the corresponding file handler.  */
-  handler = Ffind_file_name_handler (name);
+  handler = Ffind_file_name_handler (name, Qexpand_file_name);
   if (!NILP (handler))
     return call3 (handler, Qexpand_file_name, name, defalt);
 
@@ -655,9 +766,35 @@ See also the function `substitute-in-file-name'.")
   /* Filenames on VMS are always upper case.  */
   name = Fupcase (name);
 #endif
+#ifdef FILE_SYSTEM_CASE
+  name = FILE_SYSTEM_CASE (name);
+#endif
 
   nm = XSTRING (name)->data;
   
+#ifdef MSDOS
+  /* First map all backslashes to slashes.  */
+  dostounix_filename (nm = strcpy (alloca (strlen (nm) + 1), nm));
+
+  /* Now strip drive name. */
+  {
+    unsigned char *colon = rindex (nm, ':');
+    if (colon)
+      if (nm == colon)
+       nm++;
+      else
+       {
+         drive = tolower (colon[-1]) - 'a';
+         nm = colon + 1;
+         if (*nm != '/')
+           {
+             defdir = alloca (MAXPATHLEN + 1);
+             relpath = getdefdir (drive + 1, defdir);
+           }
+       }       
+  }
+#endif
+
   /* If nm is absolute, flush ...// and detect /./ and /../.
      If no /./ or /../ we can return right away. */
   if (
@@ -784,9 +921,11 @@ See also the function `substitute-in-file-name'.")
          if (index (nm, '/'))
            return build_string (sys_translate_unix (nm));
 #endif /* VMS */
+#ifndef MSDOS
          if (nm == XSTRING (name)->data)
            return name;
          return build_string (nm);
+#endif
        }
     }
 
@@ -804,6 +943,9 @@ See also the function `substitute-in-file-name'.")
        {
          if (!(newdir = (unsigned char *) egetenv ("HOME")))
            newdir = (unsigned char *) "";
+#ifdef MSDOS
+         dostounix_filename (newdir);
+#endif
          nm++;
 #ifdef VMS
          nm++;                 /* Don't leave the slash in nm.  */
@@ -840,11 +982,18 @@ See also the function `substitute-in-file-name'.")
 #ifdef VMS
       && !index (nm, ':')
 #endif /* not VMS */
+#ifdef MSDOS
+      && drive == -1
+#endif
       && !newdir)
     {
       newdir = XSTRING (defalt)->data;
     }
 
+#ifdef MSDOS
+  if (newdir == 0 && relpath)
+    newdir = defdir; 
+#endif
   if (newdir != 0)
     {
       /* Get rid of any slash at the end of newdir.  */
@@ -852,6 +1001,9 @@ See also the function `substitute-in-file-name'.")
       /* Adding `length > 1 &&' makes ~ expand into / when homedir
         is the root dir.  People disagree about whether that is right.
         Anyway, we can't take the risk of this change now.  */
+#ifdef MSDOS
+      if (newdir[1] != ':' && length > 1)
+#endif
       if (newdir[length - 1] == '/')
        {
          unsigned char *temp = (unsigned char *) alloca (length);
@@ -866,7 +1018,12 @@ See also the function `substitute-in-file-name'.")
 
   /* Now concatenate the directory and name to new space in the stack frame */
   tlen += strlen (nm) + 1;
+#ifdef MSDOS
+  /* Add reserved space for drive name.  */
+  target = (unsigned char *) alloca (tlen + 2) + 2;
+#else
   target = (unsigned char *) alloca (tlen);
+#endif
   *target = 0;
 
   if (newdir)
@@ -982,6 +1139,16 @@ See also the function `substitute-in-file-name'.")
 #endif /* not VMS */
     }
 
+#ifdef MSDOS
+  /* at last, set drive name. */
+  if (target[1] != ':')
+    {
+      target -= 2;
+      target[0] = (drive < 0 ? getdisk () : drive) + 'a';
+      target[1] = ':';
+    }
+#endif
+
   return make_string (target, o - target);
 }
 #if 0
@@ -1332,6 +1499,10 @@ duplicates what `expand-file-name' does.")
   CHECK_STRING (string, 0);
 
   nm = XSTRING (string)->data;
+#ifdef MSDOS
+  dostounix_filename (nm = strcpy (alloca (strlen (nm) + 1), nm));
+  substituted = !strcmp (nm, XSTRING (string)->data);
+#endif
   endp = nm + XSTRING (string)->size;
 
   /* If /~ or // appears, discard everything through first slash. */
@@ -1358,6 +1529,13 @@ duplicates what `expand-file-name' does.")
          nm = p;
          substituted = 1;
        }
+#ifdef MSDOS
+      if (p[0] && p[1] == ':')
+       {
+         nm = p;
+         substituted = 1;
+       }
+#endif /* MSDOS */
     }
 
 #ifdef VMS
@@ -1401,16 +1579,12 @@ duplicates what `expand-file-name' does.")
        target = (unsigned char *) alloca (s - o + 1);
        strncpy (target, o, s - o);
        target[s - o] = 0;
+#ifdef MSDOS
+       strupr (target); /* $home == $HOME etc.  */
+#endif
 
        /* Get variable value */
        o = (unsigned char *) egetenv (target);
-/* The presence of this code makes vax 5.0 crash, for reasons yet unknown */
-#if 0
-#ifdef USG
-       if (!o && !strcmp (target, "USER"))
-         o = egetenv ("LOGNAME");
-#endif /* USG */
-#endif /* 0 */
        if (!o) goto badvar;
        total += strlen (o);
        substituted = 1;
@@ -1456,16 +1630,12 @@ duplicates what `expand-file-name' does.")
        target = (unsigned char *) alloca (s - o + 1);
        strncpy (target, o, s - o);
        target[s - o] = 0;
+#ifdef MSDOS
+       strupr (target); /* $home == $HOME etc.  */
+#endif
 
        /* Get variable value */
        o = (unsigned char *) egetenv (target);
-/* The presence of this code makes vax 5.0 crash, for reasons yet unknown */
-#if 0
-#ifdef USG
-       if (!o && !strcmp (target, "USER"))
-         o = egetenv ("LOGNAME");
-#endif /* USG */
-#endif /* 0 */
        if (!o)
          goto badvar;
 
@@ -1488,6 +1658,10 @@ duplicates what `expand-file-name' does.")
         )
        && p != nm && p[-1] == '/')
       xnm = p;
+#ifdef MSDOS
+    else if (p[0] && p[1] == ':')
+       xnm = p;
+#endif
 
   return make_string (xnm, x - xnm);
 
@@ -1503,11 +1677,7 @@ duplicates what `expand-file-name' does.")
 }
 \f
 /* A slightly faster and more convenient way to get
-   (directory-file-name (expand-file-name FOO)).  The return value may
-   have had its last character zapped with a '\0' character, meaning
-   that it is acceptable to system calls, but not to other lisp
-   functions.  Callers should make sure that the return value doesn't
-   escape.  */
+   (directory-file-name (expand-file-name FOO)).  */
 
 Lisp_Object
 expand_and_dir_to_file (filename, defdir)
@@ -1527,24 +1697,25 @@ expand_and_dir_to_file (filename, defdir)
      stat behaves differently depending!  */
   if (XSTRING (abspath)->size > 1
       && XSTRING (abspath)->data[XSTRING (abspath)->size - 1] == '/')
-    {
-      if (EQ (abspath, filename))
-       abspath = Fcopy_sequence (abspath);
-      XSTRING (abspath)->data[XSTRING (abspath)->size - 1] = 0;
-    }
+    /* We cannot take shortcuts; they might be wrong for magic file names.  */
+    abspath = Fdirectory_file_name (abspath);
 #endif
   return abspath;
 }
 \f
+void
 barf_or_query_if_file_exists (absname, querystring, interactive)
      Lisp_Object absname;
      unsigned char *querystring;
      int interactive;
 {
   register Lisp_Object tem;
+  struct stat statbuf;
   struct gcpro gcpro1;
 
-  if (access (XSTRING (absname)->data, 4) >= 0)
+  /* stat is a good way to tell whether the file exists,
+     regardless of what access permissions it has.  */
+  if (stat (XSTRING (absname)->data, &statbuf) >= 0)
     {
       if (! interactive)
        Fsignal (Qfile_already_exists,
@@ -1581,6 +1752,7 @@ A prefix arg makes KEEP-TIME non-nil.")
   Lisp_Object handler;
   struct gcpro gcpro1, gcpro2;
   int count = specpdl_ptr - specpdl;
+  int input_file_statable_p;
 
   GCPRO2 (filename, newname);
   CHECK_STRING (filename, 0);
@@ -1590,30 +1762,53 @@ A prefix arg makes KEEP-TIME non-nil.")
 
   /* If the input file name has special constructs in it,
      call the corresponding file handler.  */
-  handler = Ffind_file_name_handler (filename);
-  if (!NILP (handler))
-    return call3 (handler, Qcopy_file, filename, newname);
+  handler = Ffind_file_name_handler (filename, Qcopy_file);
   /* Likewise for output file name.  */
-  handler = Ffind_file_name_handler (newname);
+  if (NILP (handler))
+    handler = Ffind_file_name_handler (newname, Qcopy_file);
   if (!NILP (handler))
-    return call3 (handler, Qcopy_file, filename, newname);
+    RETURN_UNGCPRO (call5 (handler, Qcopy_file, filename, newname,
+                          ok_if_already_exists, keep_date));
 
   if (NILP (ok_if_already_exists)
-      || XTYPE (ok_if_already_exists) == Lisp_Int)
+      || INTEGERP (ok_if_already_exists))
     barf_or_query_if_file_exists (newname, "copy to it",
-                                 XTYPE (ok_if_already_exists) == Lisp_Int);
+                                 INTEGERP (ok_if_already_exists));
 
-  ifd = open (XSTRING (filename)->data, 0);
+  ifd = open (XSTRING (filename)->data, O_RDONLY);
   if (ifd < 0)
     report_file_error ("Opening input file", Fcons (filename, Qnil));
 
   record_unwind_protect (close_file_unwind, make_number (ifd));
 
+  /* We can only copy regular files and symbolic links.  Other files are not
+     copyable by us. */
+  input_file_statable_p = (fstat (ifd, &st) >= 0);
+
+#if defined (S_ISREG) && defined (S_ISLNK)
+  if (input_file_statable_p)
+    {
+      if (!(S_ISREG (st.st_mode)) && !(S_ISLNK (st.st_mode)))
+       {
+#if defined (EISDIR)
+         /* Get a better looking error message. */
+         errno = EISDIR;
+#endif /* EISDIR */
+       report_file_error ("Non-regular file", Fcons (filename, Qnil));
+       }
+    }
+#endif /* S_ISREG && S_ISLNK */
+
 #ifdef VMS
   /* Create the copy file with the same record format as the input file */
   ofd = sys_creat (XSTRING (newname)->data, 0666, ifd);
 #else
+#ifdef MSDOS
+  /* System's default file type was set to binary by _fmode in emacs.c.  */
+  ofd = creat (XSTRING (newname)->data, S_IREAD | S_IWRITE);
+#else /* not MSDOS */
   ofd = creat (XSTRING (newname)->data, 0666);
+#endif /* not MSDOS */
 #endif /* VMS */
   if (ofd < 0)
       report_file_error ("Opening output file", Fcons (newname, Qnil));
@@ -1627,14 +1822,19 @@ A prefix arg makes KEEP-TIME non-nil.")
        report_file_error ("I/O error", Fcons (newname, Qnil));
   immediate_quit = 0;
 
-  if (fstat (ifd, &st) >= 0)
+  /* Closing the output clobbers the file times on some systems.  */
+  if (close (ofd) < 0)
+    report_file_error ("I/O error", Fcons (newname, Qnil));
+
+  if (input_file_statable_p)
     {
       if (!NILP (keep_date))
        {
          EMACS_TIME atime, mtime;
          EMACS_SET_SECS_USECS (atime, st.st_atime, 0);
          EMACS_SET_SECS_USECS (mtime, st.st_mtime, 0);
-         EMACS_SET_UTIMES (XSTRING (newname)->data, atime, mtime);
+         if (set_file_times (XSTRING (newname)->data, atime, mtime))
+           report_file_error ("I/O error", Fcons (newname, Qnil));
        }
 #ifdef APOLLO
       if (!egetenv ("USE_DOMAIN_ACLS"))
@@ -1642,13 +1842,11 @@ A prefix arg makes KEEP-TIME non-nil.")
        chmod (XSTRING (newname)->data, st.st_mode & 07777);
     }
 
+  close (ifd);
+
   /* Discard the unwind protects.  */
   specpdl_ptr = specpdl + count;
 
-  close (ifd);
-  if (close (ofd) < 0)
-    report_file_error ("I/O error", Fcons (newname, Qnil));
-
   UNGCPRO;
   return Qnil;
 }
@@ -1665,9 +1863,9 @@ DEFUN ("make-directory-internal", Fmake_directory_internal,
   CHECK_STRING (dirname, 0);
   dirname = Fexpand_file_name (dirname, Qnil);
 
-  handler = Ffind_file_name_handler (dirname);
+  handler = Ffind_file_name_handler (dirname, Qmake_directory_internal);
   if (!NILP (handler))
-    return call3 (handler, Qmake_directory, dirname, Qnil);
+    return call2 (handler, Qmake_directory_internal, dirname);
 
   dir = XSTRING (dirname)->data;
 
@@ -1678,7 +1876,7 @@ DEFUN ("make-directory-internal", Fmake_directory_internal,
 }
 
 DEFUN ("delete-directory", Fdelete_directory, Sdelete_directory, 1, 1, "FDelete directory: ",
-  "Delete a directory.  One argument, a file name string.")
+  "Delete a directory.  One argument, a file name or directory name string.")
   (dirname)
      Lisp_Object dirname;
 {
@@ -1686,10 +1884,10 @@ DEFUN ("delete-directory", Fdelete_directory, Sdelete_directory, 1, 1, "FDelete
   Lisp_Object handler;
 
   CHECK_STRING (dirname, 0);
-  dirname = Fexpand_file_name (dirname, Qnil);
+  dirname = Fdirectory_file_name (Fexpand_file_name (dirname, Qnil));
   dir = XSTRING (dirname)->data;
 
-  handler = Ffind_file_name_handler (dirname);
+  handler = Ffind_file_name_handler (dirname, Qdelete_directory);
   if (!NILP (handler))
     return call2 (handler, Qdelete_directory, dirname);
 
@@ -1709,7 +1907,7 @@ If file has multiple names, it continues to exist with the other names.")
   CHECK_STRING (filename, 0);
   filename = Fexpand_file_name (filename, Qnil);
 
-  handler = Ffind_file_name_handler (filename);
+  handler = Ffind_file_name_handler (filename, Qdelete_file);
   if (!NILP (handler))
     return call2 (handler, Qdelete_file, filename);
 
@@ -1743,14 +1941,17 @@ This is what happens in interactive use with M-x.")
 
   /* If the file name has special constructs in it,
      call the corresponding file handler.  */
-  handler = Ffind_file_name_handler (filename);
+  handler = Ffind_file_name_handler (filename, Qrename_file);
+  if (NILP (handler))
+    handler = Ffind_file_name_handler (newname, Qrename_file);
   if (!NILP (handler))
-    return call3 (handler, Qrename_file, filename, newname);
+    RETURN_UNGCPRO (call4 (handler, Qrename_file,
+                          filename, newname, ok_if_already_exists));
 
   if (NILP (ok_if_already_exists)
-      || XTYPE (ok_if_already_exists) == Lisp_Int)
+      || INTEGERP (ok_if_already_exists))
     barf_or_query_if_file_exists (newname, "rename to it",
-                                 XTYPE (ok_if_already_exists) == Lisp_Int);
+                                 INTEGERP (ok_if_already_exists));
 #ifndef BSD4_1
   if (0 > rename (XSTRING (filename)->data, XSTRING (newname)->data))
 #else
@@ -1760,7 +1961,10 @@ This is what happens in interactive use with M-x.")
     {
       if (errno == EXDEV)
        {
-         Fcopy_file (filename, newname, ok_if_already_exists, Qt);
+         Fcopy_file (filename, newname,
+                     /* We have already prompted if it was an integer,
+                        so don't have copy-file prompt again.  */
+                     NILP (ok_if_already_exists) ? Qnil : Qt, Qt);
          Fdelete_file (filename);
        }
       else
@@ -1802,14 +2006,15 @@ This is what happens in interactive use with M-x.")
 
   /* If the file name has special constructs in it,
      call the corresponding file handler.  */
-  handler = Ffind_file_name_handler (filename);
+  handler = Ffind_file_name_handler (filename, Qadd_name_to_file);
   if (!NILP (handler))
-    return call3 (handler, Qadd_name_to_file, filename, newname);
+    RETURN_UNGCPRO (call4 (handler, Qadd_name_to_file, filename,
+                          newname, ok_if_already_exists));
 
   if (NILP (ok_if_already_exists)
-      || XTYPE (ok_if_already_exists) == Lisp_Int)
+      || INTEGERP (ok_if_already_exists))
     barf_or_query_if_file_exists (newname, "make it a new name",
-                                 XTYPE (ok_if_already_exists) == Lisp_Int);
+                                 INTEGERP (ok_if_already_exists));
   unlink (XSTRING (newname)->data);
   if (0 > link (XSTRING (filename)->data, XSTRING (newname)->data))
     {
@@ -1830,9 +2035,9 @@ This is what happens in interactive use with M-x.")
 DEFUN ("make-symbolic-link", Fmake_symbolic_link, Smake_symbolic_link, 2, 3,
   "FMake symbolic link to file: \nFMake symbolic link to file %s: \np",
   "Make a symbolic link to FILENAME, named LINKNAME.  Both args strings.\n\
-Signals a `file-already-exists' error if a file NEWNAME already exists\n\
+Signals a `file-already-exists' error if a file LINKNAME already exists\n\
 unless optional third argument OK-IF-ALREADY-EXISTS is non-nil.\n\
-A number as third arg means request confirmation if NEWNAME already exists.\n\
+A number as third arg means request confirmation if LINKNAME already exists.\n\
 This happens for interactive use with M-x.")
   (filename, linkname, ok_if_already_exists)
      Lisp_Object filename, linkname, ok_if_already_exists;
@@ -1846,21 +2051,24 @@ This happens for interactive use with M-x.")
   GCPRO2 (filename, linkname);
   CHECK_STRING (filename, 0);
   CHECK_STRING (linkname, 1);
-#if 0 /* This made it impossible to make a link to a relative name.  */
-  filename = Fexpand_file_name (filename, Qnil);
-#endif
+  /* If the link target has a ~, we must expand it to get
+     a truly valid file name.  Otherwise, do not expand;
+     we want to permit links to relative file names.  */
+  if (XSTRING (filename)->data[0] == '~')
+    filename = Fexpand_file_name (filename, Qnil);
   linkname = Fexpand_file_name (linkname, Qnil);
 
   /* If the file name has special constructs in it,
      call the corresponding file handler.  */
-  handler = Ffind_file_name_handler (filename);
+  handler = Ffind_file_name_handler (filename, Qmake_symbolic_link);
   if (!NILP (handler))
-    return call3 (handler, Qmake_symbolic_link, filename, linkname);
+    RETURN_UNGCPRO (call4 (handler, Qmake_symbolic_link, filename,
+                          linkname, ok_if_already_exists));
 
   if (NILP (ok_if_already_exists)
-      || XTYPE (ok_if_already_exists) == Lisp_Int)
+      || INTEGERP (ok_if_already_exists))
     barf_or_query_if_file_exists (linkname, "make it a link",
-                                 XTYPE (ok_if_already_exists) == Lisp_Int);
+                                 INTEGERP (ok_if_already_exists));
   if (0 > symlink (XSTRING (filename)->data, XSTRING (linkname)->data))
     {
       /* If we didn't complain already, silently delete existing file.  */
@@ -1868,7 +2076,10 @@ This happens for interactive use with M-x.")
        {
          unlink (XSTRING (linkname)->data);
          if (0 <= symlink (XSTRING (filename)->data, XSTRING (linkname)->data))
-           return Qnil;
+           {
+             UNGCPRO;
+             return Qnil;
+           }
        }
 
 #ifdef NO_ARG_ARRAY
@@ -1950,11 +2161,72 @@ On Unix, this is a name starting with a `/' or a `~'.")
       || (*ptr == '[' && (ptr[1] != '-' || (ptr[2] != '.' && ptr[2] != ']'))
          && ptr[1] != '.')
 #endif /* VMS */
+#ifdef MSDOS
+      || (*ptr != 0 && ptr[1] == ':' && (ptr[2] == '/' || ptr[2] == '\\'))
+#endif
       )
     return Qt;
   else
     return Qnil;
 }
+\f
+/* Return nonzero if file FILENAME exists and can be executed.  */
+
+static int
+check_executable (filename)
+     char *filename;
+{
+#ifdef __HURD__
+  mach_port_t file;
+  int access_mode;
+
+  file = path_lookup (filename, 0, 0);
+  if (file == MACH_PORT_NULL)
+    /* File can't be opened.  */
+    access_mode = 0;
+  else
+    {
+      file_access (file, &access_mode);
+      mach_port_deallocate (mach_task_self (), file);
+    }
+  return !!(access_mode & O_EXEC);
+#else
+  /* Access isn't quite right because it uses the real uid
+     and we really want to test with the effective uid.
+     But Unix doesn't give us a right way to do it.  */
+  return (access (filename, 1) >= 0);
+#endif
+}
+
+/* Return nonzero if file FILENAME exists and can be written.  */
+
+static int
+check_writable (filename)
+     char *filename;
+{
+#ifdef __HURD__
+  mach_port_t file;
+  int access_mode;
+
+  file = path_lookup (filename, 0, 0);
+  if (file == MACH_PORT_NULL)
+    /* File can't be opened.  */
+    access_mode = 0;
+  else
+    {
+      file_access (file, &access_mode);
+      mach_port_deallocate (mach_task_self (), file);
+    }
+  return !!(access_mode & O_WRITE);
+#else
+  /* Access isn't quite right because it uses the real uid
+     and we really want to test with the effective uid.
+     But Unix doesn't give us a right way to do it.
+     Opening with O_WRONLY could work for an ordinary file,
+     but would lose for directories.  */
+  return (access (filename, 2) >= 0);
+#endif
+}
 
 DEFUN ("file-exists-p", Ffile_exists_p, Sfile_exists_p, 1, 1, 0,
   "Return t if file FILENAME exists.  (This does not mean you can read it.)\n\
@@ -1964,17 +2236,18 @@ See also `file-readable-p' and `file-attributes'.")
 {
   Lisp_Object abspath;
   Lisp_Object handler;
+  struct stat statbuf;
 
   CHECK_STRING (filename, 0);
   abspath = Fexpand_file_name (filename, Qnil);
 
   /* If the file name has special constructs in it,
      call the corresponding file handler.  */
-  handler = Ffind_file_name_handler (abspath);
+  handler = Ffind_file_name_handler (abspath, Qfile_exists_p);
   if (!NILP (handler))
     return call2 (handler, Qfile_exists_p, abspath);
 
-  return (access (XSTRING (abspath)->data, 0) >= 0) ? Qt : Qnil;
+  return (stat (XSTRING (abspath)->data, &statbuf) >= 0) ? Qt : Qnil;
 }
 
 DEFUN ("file-executable-p", Ffile_executable_p, Sfile_executable_p, 1, 1, 0,
@@ -1992,11 +2265,11 @@ For a directory, this means you can access files in that directory.")
 
   /* If the file name has special constructs in it,
      call the corresponding file handler.  */
-  handler = Ffind_file_name_handler (abspath);
+  handler = Ffind_file_name_handler (abspath, Qfile_executable_p);
   if (!NILP (handler))
     return call2 (handler, Qfile_executable_p, abspath);
 
-  return (access (XSTRING (abspath)->data, 1) >= 0) ? Qt : Qnil;
+  return (check_executable (XSTRING (abspath)->data) ? Qt : Qnil);
 }
 
 DEFUN ("file-readable-p", Ffile_readable_p, Sfile_readable_p, 1, 1, 0,
@@ -2007,23 +2280,28 @@ See also `file-exists-p' and `file-attributes'.")
 {
   Lisp_Object abspath;
   Lisp_Object handler;
+  int desc;
 
   CHECK_STRING (filename, 0);
   abspath = Fexpand_file_name (filename, Qnil);
 
   /* If the file name has special constructs in it,
      call the corresponding file handler.  */
-  handler = Ffind_file_name_handler (abspath);
+  handler = Ffind_file_name_handler (abspath, Qfile_readable_p);
   if (!NILP (handler))
     return call2 (handler, Qfile_readable_p, abspath);
 
-  return (access (XSTRING (abspath)->data, 4) >= 0) ? Qt : Qnil;
+  desc = open (XSTRING (abspath)->data, O_RDONLY);
+  if (desc < 0)
+    return Qnil;
+  close (desc);
+  return Qt;
 }
 
 DEFUN ("file-symlink-p", Ffile_symlink_p, Sfile_symlink_p, 1, 1, 0,
-  "If file FILENAME is the name of a symbolic link\n\
-returns the name of the file to which it is linked.\n\
-Otherwise returns NIL.")
+  "Return non-nil if file FILENAME is the name of a symbolic link.\n\
+The value is the name of the file to which it is linked.\n\
+Otherwise returns nil.")
   (filename)
      Lisp_Object filename;
 {
@@ -2039,7 +2317,7 @@ Otherwise returns NIL.")
 
   /* If the file name has special constructs in it,
      call the corresponding file handler.  */
-  handler = Ffind_file_name_handler (filename);
+  handler = Ffind_file_name_handler (filename, Qfile_symlink_p);
   if (!NILP (handler))
     return call2 (handler, Qfile_symlink_p, filename);
 
@@ -2067,30 +2345,6 @@ Otherwise returns NIL.")
 #endif /* not S_IFLNK */
 }
 
-#ifdef SOLARIS_BROKEN_ACCESS
-/* In Solaris 2.1, the readonly-ness of the filesystem is not
-   considered by the access system call.  This is Sun's bug, but we
-   still have to make Emacs work.  */
-
-#include <sys/statvfs.h>
-
-static int
-ro_fsys (path)
-    char *path;
-{
-    struct statvfs statvfsb;
-
-    if (statvfs(path, &statvfsb))
-      return 1;  /* error from statvfs, be conservative and say not wrtable */
-    else
-      /* Otherwise, fsys is ro if bit is set.  */
-      return statvfsb.f_flag & ST_RDONLY;
-}
-#else
-/* But on every other os, access has already done the right thing.  */
-#define ro_fsys(path) 0
-#endif
-
 /* Having this before file-symlink-p mysteriously caused it to be forgotten
    on the RT/PC.  */
 DEFUN ("file-writable-p", Ffile_writable_p, Sfile_writable_p, 1, 1, 0,
@@ -2100,27 +2354,30 @@ DEFUN ("file-writable-p", Ffile_writable_p, Sfile_writable_p, 1, 1, 0,
 {
   Lisp_Object abspath, dir;
   Lisp_Object handler;
+  struct stat statbuf;
 
   CHECK_STRING (filename, 0);
   abspath = Fexpand_file_name (filename, Qnil);
 
   /* If the file name has special constructs in it,
      call the corresponding file handler.  */
-  handler = Ffind_file_name_handler (abspath);
+  handler = Ffind_file_name_handler (abspath, Qfile_writable_p);
   if (!NILP (handler))
     return call2 (handler, Qfile_writable_p, abspath);
 
-  if (access (XSTRING (abspath)->data, 0) >= 0)
-    return ((access (XSTRING (abspath)->data, 2) >= 0
-            && ! ro_fsys (XSTRING (abspath)))
+  if (stat (XSTRING (abspath)->data, &statbuf) >= 0)
+    return (check_writable (XSTRING (abspath)->data)
            ? Qt : Qnil);
   dir = Ffile_name_directory (abspath);
 #ifdef VMS
   if (!NILP (dir))
     dir = Fdirectory_file_name (dir);
 #endif /* VMS */
-  return ((access (!NILP (dir) ? (char *) XSTRING (dir)->data : "", 2) >= 0
-          && ! ro_fsys ((char *) XSTRING (dir)))
+#ifdef MSDOS
+  if (!NILP (dir))
+    dir = Fdirectory_file_name (dir);
+#endif /* MSDOS */
+  return (check_writable (!NILP (dir) ? (char *) XSTRING (dir)->data : "")
          ? Qt : Qnil);
 }
 
@@ -2139,7 +2396,7 @@ if the directory so specified exists and really is a directory.")
 
   /* If the file name has special constructs in it,
      call the corresponding file handler.  */
-  handler = Ffind_file_name_handler (abspath);
+  handler = Ffind_file_name_handler (abspath, Qfile_directory_p);
   if (!NILP (handler))
     return call2 (handler, Qfile_directory_p, abspath);
 
@@ -2159,18 +2416,26 @@ searchable directory.")
      Lisp_Object filename;
 {
   Lisp_Object handler;
+  int tem;
+  struct gcpro gcpro1;
 
   /* If the file name has special constructs in it,
      call the corresponding file handler.  */
-  handler = Ffind_file_name_handler (filename);
+  handler = Ffind_file_name_handler (filename, Qfile_accessible_directory_p);
   if (!NILP (handler))
     return call2 (handler, Qfile_accessible_directory_p, filename);
 
-  if (NILP (Ffile_directory_p (filename))
-      || NILP (Ffile_executable_p (filename)))
-    return Qnil;
-  else
-    return Qt;
+  /* It's an unlikely combination, but yes we really do need to gcpro:
+     Suppose that file-accessible-directory-p has no handler, but
+     file-directory-p does have a handler; this handler causes a GC which
+     relocates the string in `filename'; and finally file-directory-p
+     returns non-nil.  Then we would end up passing a garbaged string
+     to file-executable-p.  */
+  GCPRO1 (filename);
+  tem = (NILP (Ffile_directory_p (filename))
+        || NILP (Ffile_executable_p (filename)));
+  UNGCPRO;
+  return tem ? Qnil : Qt;
 }
 
 DEFUN ("file-modes", Ffile_modes, Sfile_modes, 1, 1, 0,
@@ -2186,12 +2451,25 @@ DEFUN ("file-modes", Ffile_modes, Sfile_modes, 1, 1, 0,
 
   /* If the file name has special constructs in it,
      call the corresponding file handler.  */
-  handler = Ffind_file_name_handler (abspath);
+  handler = Ffind_file_name_handler (abspath, Qfile_modes);
   if (!NILP (handler))
     return call2 (handler, Qfile_modes, abspath);
 
   if (stat (XSTRING (abspath)->data, &st) < 0)
     return Qnil;
+#ifdef MSDOS
+  {
+    int len;
+    char *suffix;
+    if (S_ISREG (st.st_mode)
+       && (len = XSTRING (abspath)->size) >= 5
+       && (stricmp ((suffix = XSTRING (abspath)->data + len-4), ".com") == 0
+           || stricmp (suffix, ".exe") == 0
+           || stricmp (suffix, ".bat") == 0))
+      st.st_mode |= S_IEXEC;
+  }
+#endif /* MSDOS */
+
   return make_number (st.st_mode & 07777);
 }
 
@@ -2209,7 +2487,7 @@ Only the 12 low bits of MODE are used.")
 
   /* If the file name has special constructs in it,
      call the corresponding file handler.  */
-  handler = Ffind_file_name_handler (abspath);
+  handler = Ffind_file_name_handler (abspath, Qset_file_modes);
   if (!NILP (handler))
     return call3 (handler, Qset_file_modes, abspath, mode);
 
@@ -2312,7 +2590,9 @@ otherwise, if FILE2 does not exist, the answer is t.")
 
   /* If the file name has special constructs in it,
      call the corresponding file handler.  */
-  handler = Ffind_file_name_handler (abspath1);
+  handler = Ffind_file_name_handler (abspath1, Qfile_newer_than_file_p);
+  if (NILP (handler))
+    handler = Ffind_file_name_handler (abspath2, Qfile_newer_than_file_p);
   if (!NILP (handler))
     return call3 (handler, Qfile_newer_than_file_p, abspath1, abspath2);
 
@@ -2327,28 +2607,43 @@ otherwise, if FILE2 does not exist, the answer is t.")
   return (mtime1 > st.st_mtime) ? Qt : Qnil;
 }
 \f
+#ifdef MSDOS
+Lisp_Object Qfind_buffer_file_type;
+#endif
+
 DEFUN ("insert-file-contents", Finsert_file_contents, Sinsert_file_contents,
-  1, 2, 0,
+  1, 5, 0,
   "Insert contents of file FILENAME after point.\n\
-Returns list of absolute pathname and length of data inserted.\n\
+Returns list of absolute file name and length of data inserted.\n\
 If second argument VISIT is non-nil, the buffer's visited filename\n\
 and last save file modtime are set, and it is marked unmodified.\n\
 If visiting and the file does not exist, visiting is completed\n\
-before the error is signaled.")
-  (filename, visit)
-     Lisp_Object filename, visit;
+before the error is signaled.\n\n\
+The optional third and fourth arguments BEG and END\n\
+specify what portion of the file to insert.\n\
+If VISIT is non-nil, BEG and END must be nil.\n\
+If optional fifth argument REPLACE is non-nil,\n\
+it means replace the current buffer contents (in the accessible portion)\n\
+with the file contents.  This is better than simply deleting and inserting\n\
+the whole thing because (1) it preserves some marker positions\n\
+and (2) it puts less data in the undo list.")
+  (filename, visit, beg, end, replace)
+     Lisp_Object filename, visit, beg, end, replace;
 {
   struct stat st;
   register int fd;
   register int inserted = 0;
   register int how_much;
   int count = specpdl_ptr - specpdl;
-  struct gcpro gcpro1;
-  Lisp_Object handler, val;
+  struct gcpro gcpro1, gcpro2, gcpro3;
+  Lisp_Object handler, val, insval;
+  Lisp_Object p;
+  int total;
 
   val = Qnil;
+  p = Qnil;
 
-  GCPRO1 (filename);
+  GCPRO3 (filename, val, p);
   if (!NILP (current_buffer->read_only))
     Fbarf_if_buffer_read_only();
 
@@ -2357,25 +2652,25 @@ before the error is signaled.")
 
   /* If the file name has special constructs in it,
      call the corresponding file handler.  */
-  handler = Ffind_file_name_handler (filename);
+  handler = Ffind_file_name_handler (filename, Qinsert_file_contents);
   if (!NILP (handler))
     {
-      val = call3 (handler, Qinsert_file_contents, filename, visit);
-      st.st_mtime = 0;
+      val = call6 (handler, Qinsert_file_contents, filename,
+                  visit, beg, end, replace);
       goto handled;
     }
 
   fd = -1;
 
 #ifndef APOLLO
-  if (stat (XSTRING (filename)->data, &st) < 0
-      || (fd = open (XSTRING (filename)->data, 0)) < 0)
+  if (stat (XSTRING (filename)->data, &st) < 0)
 #else
-  if ((fd = open (XSTRING (filename)->data, 0)) < 0
+  if ((fd = open (XSTRING (filename)->data, O_RDONLY)) < 0
       || fstat (fd, &st) < 0)
 #endif /* not APOLLO */
     {
       if (fd >= 0) close (fd);
+    badopen:
       if (NILP (visit))
        report_file_error ("Opening input file", Fcons (filename, Qnil));
       st.st_mtime = -1;
@@ -2383,41 +2678,190 @@ before the error is signaled.")
       goto notfound;
     }
 
-  record_unwind_protect (close_file_unwind, make_number (fd));
-
-#ifdef S_IFSOCK
+#ifdef S_IFREG
   /* This code will need to be changed in order to work on named
      pipes, and it's probably just not worth it.  So we should at
      least signal an error.  */
-  if ((st.st_mode & S_IFMT) == S_IFSOCK)
+  if (!S_ISREG (st.st_mode))
     Fsignal (Qfile_error,
-            Fcons (build_string ("reading from named pipe"),
+            Fcons (build_string ("not a regular file"),
                    Fcons (filename, Qnil)));
 #endif
 
+  if (fd < 0)
+    if ((fd = open (XSTRING (filename)->data, O_RDONLY)) < 0)
+      goto badopen;
+
+  /* Replacement should preserve point as it preserves markers.  */
+  if (!NILP (replace))
+    record_unwind_protect (restore_point_unwind, Fpoint_marker ());
+
+  record_unwind_protect (close_file_unwind, make_number (fd));
+
   /* Supposedly happens on VMS.  */
   if (st.st_size < 0)
     error ("File size is negative");
 
+  if (!NILP (beg) || !NILP (end))
+    if (!NILP (visit))
+      error ("Attempt to visit less than an entire file");
+
+  if (!NILP (beg))
+    CHECK_NUMBER (beg, 0);
+  else
+    XFASTINT (beg) = 0;
+
+  if (!NILP (end))
+    CHECK_NUMBER (end, 0);
+  else
+    {
+      XSETINT (end, st.st_size);
+      if (XINT (end) != st.st_size)
+       error ("maximum buffer size exceeded");
+    }
+
+  /* If requested, replace the accessible part of the buffer
+     with the file contents.  Avoid replacing text at the
+     beginning or end of the buffer that matches the file contents;
+     that preserves markers pointing to the unchanged parts.  */
+#ifdef MSDOS
+  /* On MSDOS, replace mode doesn't really work, except for binary files,
+     and it's not worth supporting just for them.  */
+  if (!NILP (replace))
+    {
+      replace = Qnil;
+      XFASTINT (beg) = 0;
+      XFASTINT (end) = st.st_size;
+      del_range_1 (BEGV, ZV, 0);
+    }
+#else /* MSDOS */
+  if (!NILP (replace))
+    {
+      unsigned char buffer[1 << 14];
+      int same_at_start = BEGV;
+      int same_at_end = ZV;
+      int overlap;
+
+      immediate_quit = 1;
+      QUIT;
+      /* Count how many chars at the start of the file
+        match the text at the beginning of the buffer.  */
+      while (1)
+       {
+         int nread, bufpos;
+
+         nread = read (fd, buffer, sizeof buffer);
+         if (nread < 0)
+           error ("IO error reading %s: %s",
+                  XSTRING (filename)->data, strerror (errno));
+         else if (nread == 0)
+           break;
+         bufpos = 0;
+         while (bufpos < nread && same_at_start < ZV
+                && FETCH_CHAR (same_at_start) == buffer[bufpos])
+           same_at_start++, bufpos++;
+         /* If we found a discrepancy, stop the scan.
+            Otherwise loop around and scan the next bufferfull.  */
+         if (bufpos != nread)
+           break;
+       }
+      immediate_quit = 0;
+      /* If the file matches the buffer completely,
+        there's no need to replace anything.  */
+      if (same_at_start - BEGV == st.st_size)
+       {
+         close (fd);
+         specpdl_ptr--;
+         /* Truncate the buffer to the size of the file.  */
+         del_range_1 (same_at_start, same_at_end, 0);
+         goto handled;
+       }
+      immediate_quit = 1;
+      QUIT;
+      /* Count how many chars at the end of the file
+        match the text at the end of the buffer.  */
+      while (1)
+       {
+         int total_read, nread, bufpos, curpos, trial;
+
+         /* At what file position are we now scanning?  */
+         curpos = st.st_size - (ZV - same_at_end);
+         /* If the entire file matches the buffer tail, stop the scan.  */
+         if (curpos == 0)
+           break;
+         /* How much can we scan in the next step?  */
+         trial = min (curpos, sizeof buffer);
+         if (lseek (fd, curpos - trial, 0) < 0)
+           report_file_error ("Setting file position",
+                              Fcons (filename, Qnil));
+
+         total_read = 0;
+         while (total_read < trial)
+           {
+             nread = read (fd, buffer + total_read, trial - total_read);
+             if (nread <= 0)
+               error ("IO error reading %s: %s",
+                      XSTRING (filename)->data, strerror (errno));
+             total_read += nread;
+           }
+         /* Scan this bufferfull from the end, comparing with
+            the Emacs buffer.  */
+         bufpos = total_read;
+         /* Compare with same_at_start to avoid counting some buffer text
+            as matching both at the file's beginning and at the end.  */
+         while (bufpos > 0 && same_at_end > same_at_start
+                && FETCH_CHAR (same_at_end - 1) == buffer[bufpos - 1])
+           same_at_end--, bufpos--;
+         /* If we found a discrepancy, stop the scan.
+            Otherwise loop around and scan the preceding bufferfull.  */
+         if (bufpos != 0)
+           break;
+       }
+      immediate_quit = 0;
+
+      /* Don't try to reuse the same piece of text twice.  */
+      overlap = same_at_start - BEGV - (same_at_end + st.st_size - ZV);
+      if (overlap > 0)
+       same_at_end += overlap;
+
+      /* Arrange to read only the nonmatching middle part of the file.  */
+      XFASTINT (beg) = same_at_start - BEGV;
+      XFASTINT (end) = st.st_size - (ZV - same_at_end);
+
+      del_range_1 (same_at_start, same_at_end, 0);
+      /* Insert from the file at the proper position.  */
+      SET_PT (same_at_start);
+    }
+#endif /* MSDOS */
+
+  total = XINT (end) - XINT (beg);
+
   {
     register Lisp_Object temp;
 
     /* Make sure point-max won't overflow after this insertion.  */
-    XSET (temp, Lisp_Int, st.st_size + Z);
-    if (st.st_size + Z != XINT (temp))
+    XSET (temp, Lisp_Int, total);
+    if (total != XINT (temp))
       error ("maximum buffer size exceeded");
   }
 
-  if (NILP (visit))
+  if (NILP (visit) && total > 0)
     prepare_to_modify_buffer (point, point);
 
   move_gap (point);
-  if (GAP_SIZE < st.st_size)
-    make_gap (st.st_size - GAP_SIZE);
-    
-  while (1)
+  if (GAP_SIZE < total)
+    make_gap (total - GAP_SIZE);
+
+  if (XINT (beg) != 0 || !NILP (replace))
+    {
+      if (lseek (fd, XINT (beg), 0) < 0)
+       report_file_error ("Setting file position", Fcons (filename, Qnil));
+    }
+
+  how_much = 0;
+  while (inserted < total)
     {
-      int try = min (st.st_size - inserted, 64 << 10);
+      int try = min (total - inserted, 64 << 10);
       int this;
 
       /* Allow quitting out of the actual I/O.  */
@@ -2439,6 +2883,26 @@ before the error is signaled.")
       inserted += this;
     }
 
+#ifdef MSDOS
+  /* Demacs 1.1.1 91/10/16 HIRANO Satoshi, MW July 1993 */
+  /* Determine file type from name and remove LFs from CR-LFs if the file
+     is deemed to be a text file.  */
+  {
+    current_buffer->buffer_file_type
+      = call1 (Qfind_buffer_file_type, filename);
+    if (NILP (current_buffer->buffer_file_type))
+      {
+       int reduced_size
+         = inserted - crlf_to_lf (inserted, &FETCH_CHAR (point - 1) + 1);
+       ZV -= reduced_size;
+       Z -= reduced_size;
+       GPT -= reduced_size;
+       GAP_SIZE += reduced_size;
+       inserted -= reduced_size;
+      }
+  }
+#endif
+
   if (inserted > 0)
     {
       record_insert (point, inserted);
@@ -2450,23 +2914,30 @@ before the error is signaled.")
 
   close (fd);
 
-  /* Discard the unwind protect */
-  specpdl_ptr = specpdl + count;
+  /* Discard the unwind protect for closing the file.  */
+  specpdl_ptr--;
 
   if (how_much < 0)
     error ("IO error reading %s: %s",
-          XSTRING (filename)->data, err_str (errno));
+          XSTRING (filename)->data, strerror (errno));
 
  notfound:
  handled:
 
   if (!NILP (visit))
     {
-      current_buffer->undo_list = Qnil;
+      if (!EQ (current_buffer->undo_list, Qt))
+       current_buffer->undo_list = Qnil;
 #ifdef APOLLO
       stat (XSTRING (filename)->data, &st);
 #endif
-      current_buffer->modtime = st.st_mtime;
+
+      if (NILP (handler))
+       {
+         current_buffer->modtime = st.st_mtime;
+         current_buffer->filename = filename;
+       }
+
       current_buffer->save_modified = MODIFF;
       current_buffer->auto_save_modified = MODIFF;
       XFASTINT (current_buffer->save_length) = Z - BEG;
@@ -2478,19 +2949,55 @@ before the error is signaled.")
          unlock_file (filename);
        }
 #endif /* CLASH_DETECTION */
-      current_buffer->filename = filename;
       /* If visiting nonexistent file, return nil.  */
       if (current_buffer->modtime == -1)
        report_file_error ("Opening input file", Fcons (filename, Qnil));
     }
 
-  signal_after_change (point, 0, inserted);
+  if (inserted > 0 && NILP (visit) && total > 0)
+    signal_after_change (point, 0, inserted);
   
-  if (!NILP (val))
-    RETURN_UNGCPRO (val);
-  RETURN_UNGCPRO (Fcons (filename,
-                        Fcons (make_number (inserted),
-                               Qnil)));
+  if (inserted > 0)
+    {
+      p = Vafter_insert_file_functions;
+      while (!NILP (p))
+       {
+         insval = call1 (Fcar (p), make_number (inserted));
+         if (!NILP (insval))
+           {
+             CHECK_NUMBER (insval, 0);
+             inserted = XFASTINT (insval);
+           }
+         QUIT;
+         p = Fcdr (p);
+       }
+    }
+
+  if (NILP (val))
+    val = Fcons (filename,
+                Fcons (make_number (inserted),
+                       Qnil));
+
+  RETURN_UNGCPRO (unbind_to (count, val));
+}
+\f
+static Lisp_Object build_annotations ();
+
+/* If build_annotations switched buffers, switch back to BUF.
+   Kill the temporary buffer that was selected in the meantime.  */
+
+static Lisp_Object 
+build_annotations_unwind (buf)
+     Lisp_Object buf;
+{
+  Lisp_Object tembuf;
+
+  if (XBUFFER (buf) == current_buffer)
+    return Qnil;
+  tembuf = Fcurrent_buffer ();
+  Fset_buffer (buf);
+  Fkill_buffer (tembuf);
+  return Qnil;
 }
 
 DEFUN ("write-region", Fwrite_region, Swrite_region, 3, 5,
@@ -2520,53 +3027,54 @@ to the file, instead of any buffer contents, and END is ignored.")
   struct stat st;
   int tem;
   int count = specpdl_ptr - specpdl;
+  int count1;
 #ifdef VMS
   unsigned char *fname = 0;    /* If non-0, original filename (must rename) */
 #endif /* VMS */
   Lisp_Object handler;
   Lisp_Object visit_file;
+  Lisp_Object annotations;
+  int visiting, quietly;
   struct gcpro gcpro1, gcpro2, gcpro3, gcpro4;
+  struct buffer *given_buffer;
+#ifdef MSDOS
+  int buffer_file_type
+    = NILP (current_buffer->buffer_file_type) ? O_TEXT : O_BINARY;
+#endif
 
-  /* Special kludge to simplify auto-saving */
-  if (NILP (start))
-    {
-      XFASTINT (start) = BEG;
-      XFASTINT (end) = Z;
-    }
-  else if (XTYPE (start) != Lisp_String)
+  if (!NILP (start) && !STRINGP (start))
     validate_region (&start, &end);
 
+  GCPRO2 (filename, visit);
   filename = Fexpand_file_name (filename, Qnil);
-  if (XTYPE (visit) == Lisp_String)
+  if (STRINGP (visit))
     visit_file = Fexpand_file_name (visit, Qnil);
   else
     visit_file = filename;
+  UNGCPRO;
+
+  visiting = (EQ (visit, Qt) || STRINGP (visit));
+  quietly = !NILP (visit);
+
+  annotations = Qnil;
 
-  GCPRO4 (start, filename, visit, visit_file);
+  GCPRO4 (start, filename, annotations, visit_file);
 
   /* If the file name has special constructs in it,
      call the corresponding file handler.  */
-  handler = Ffind_file_name_handler (filename);
+  handler = Ffind_file_name_handler (filename, Qwrite_region);
+  /* If FILENAME has no handler, see if VISIT has one.  */
+  if (NILP (handler) && STRINGP (visit))
+    handler = Ffind_file_name_handler (visit, Qwrite_region);    
 
   if (!NILP (handler))
     {
-      Lisp_Object args[7];
       Lisp_Object val;
-      args[0] = handler;
-      args[1] = Qwrite_region;
-      args[2] = start;
-      args[3] = end;
-      args[4] = filename;
-      args[5] = append;
-      args[6] = visit;
-      val = Ffuncall (7, args);
-
-      /* Do this before reporting IO error
-        to avoid a "file has changed on disk" warning on
-        next attempt to save.  */
-      if (EQ (visit, Qt) || XTYPE (visit) == Lisp_String)
+      val = call6 (handler, Qwrite_region, start, end,
+                  filename, append, visit);
+
+      if (visiting)
        {
-         current_buffer->modtime = 0;
          current_buffer->save_modified = MODIFF;
          XFASTINT (current_buffer->save_length) = Z - BEG;
          current_buffer->filename = visit_file;
@@ -2575,6 +3083,24 @@ to the file, instead of any buffer contents, and END is ignored.")
       return val;
     }
 
+  /* Special kludge to simplify auto-saving.  */
+  if (NILP (start))
+    {
+      XFASTINT (start) = BEG;
+      XFASTINT (end) = Z;
+    }
+
+  record_unwind_protect (build_annotations_unwind, Fcurrent_buffer ());
+  count1 = specpdl_ptr - specpdl;
+
+  given_buffer = current_buffer;
+  annotations = build_annotations (start, end);
+  if (current_buffer != given_buffer)
+    {
+      start = BEGV;
+      end = ZV;
+    }
+
 #ifdef CLASH_DETECTION
   if (!auto_saving)
     lock_file (visit_file);
@@ -2583,7 +3109,11 @@ to the file, instead of any buffer contents, and END is ignored.")
   fn = XSTRING (filename)->data;
   desc = -1;
   if (!NILP (append))
+#ifdef MSDOS
+    desc = open (fn, O_WRONLY | buffer_file_type);
+#else
     desc = open (fn, O_WRONLY);
+#endif
 
   if (desc < 0)
 #ifdef VMS
@@ -2592,7 +3122,7 @@ to the file, instead of any buffer contents, and END is ignored.")
        vms_truncate (fn);      /* if fn exists, truncate to zero length */
        desc = open (fn, O_RDWR);
        if (desc < 0)
-         desc = creat_copy_attrs (XTYPE (current_buffer->filename) == Lisp_String
+         desc = creat_copy_attrs (STRINGP (current_buffer->filename)
                                   ? XSTRING (current_buffer->filename)->data : 0,
                                   fn);
       }
@@ -2632,7 +3162,13 @@ to the file, instead of any buffer contents, and END is ignored.")
          desc = creat (fn, 0666);
       }
 #else /* not VMS */
+#ifdef MSDOS
+  desc = open (fn, 
+              O_WRONLY | O_TRUNC | O_CREAT | buffer_file_type, 
+              S_IREAD | S_IWRITE);
+#else /* not MSDOS */
   desc = creat (fn, auto_saving ? auto_save_mode_bits : 0666);
+#endif /* not MSDOS */
 #endif /* not VMS */
 
   UNGCPRO;
@@ -2681,20 +3217,22 @@ to the file, instead of any buffer contents, and END is ignored.")
   failure = 0;
   immediate_quit = 1;
 
-  if (XTYPE (start) == Lisp_String)
+  if (STRINGP (start))
     {
-      failure = 0 > e_write (desc, XSTRING (start)->data,
-                            XSTRING (start)->size);
+      failure = 0 > a_write (desc, XSTRING (start)->data,
+                            XSTRING (start)->size, 0, &annotations);
       save_errno = errno;
     }
   else if (XINT (start) != XINT (end))
     {
+      int nwritten = 0;
       if (XINT (start) < GPT)
        {
          register int end1 = XINT (end);
          tem = XINT (start);
-         failure = 0 > e_write (desc, &FETCH_CHAR (tem),
-                                min (GPT, end1) - tem);
+         failure = 0 > a_write (desc, &FETCH_CHAR (tem),
+                                min (GPT, end1) - tem, tem, &annotations);
+         nwritten += min (GPT, end1) - tem;
          save_errno = errno;
        }
 
@@ -2702,7 +3240,16 @@ to the file, instead of any buffer contents, and END is ignored.")
        {
          tem = XINT (start);
          tem = max (tem, GPT);
-         failure = 0 > e_write (desc, &FETCH_CHAR (tem), XINT (end) - tem);
+         failure = 0 > a_write (desc, &FETCH_CHAR (tem), XINT (end) - tem,
+                                tem, &annotations);
+         nwritten += XINT (end) - tem;
+         save_errno = errno;
+       }
+
+      if (nwritten == 0)
+       {
+         /* If file was empty, still need to write the annotations */
+         failure = 0 > a_write (desc, "", 0, XINT (start), &annotations);
          save_errno = errno;
        }
     }
@@ -2712,7 +3259,9 @@ to the file, instead of any buffer contents, and END is ignored.")
 #ifdef HAVE_FSYNC
   /* Note fsync appears to change the modtime on BSD4.2 (both vax and sun).
      Disk full in NFS may be reported here.  */
-  if (fsync (desc) < 0)
+  /* mib says that closing the file will try to write as fast as NFS can do
+     it, and that means the fsync here is not crucial for autosave files.  */
+  if (!auto_saving && fsync (desc) < 0)
     failure = 1, save_errno = errno;
 #endif
 
@@ -2752,8 +3301,10 @@ to the file, instead of any buffer contents, and END is ignored.")
 #ifndef FOO
   stat (fn, &st);
 #endif
-  /* Discard the unwind protect */
-  specpdl_ptr = specpdl + count;
+  /* Discard the unwind protect for close_file_unwind.  */
+  specpdl_ptr = specpdl + count1;
+  /* Restore the original current buffer.  */
+  visit_file = unbind_to (count, visit_file);
 
 #ifdef CLASH_DETECTION
   if (!auto_saving)
@@ -2763,19 +3314,20 @@ to the file, instead of any buffer contents, and END is ignored.")
   /* Do this before reporting IO error
      to avoid a "file has changed on disk" warning on
      next attempt to save.  */
-  if (EQ (visit, Qt) || XTYPE (visit) == Lisp_String)
+  if (visiting)
     current_buffer->modtime = st.st_mtime;
 
   if (failure)
-    error ("IO error writing %s: %s", fn, err_str (save_errno));
+    error ("IO error writing %s: %s", fn, strerror (save_errno));
 
-  if (EQ (visit, Qt) || XTYPE (visit) == Lisp_String)
+  if (visiting)
     {
       current_buffer->save_modified = MODIFF;
       XFASTINT (current_buffer->save_length) = Z - BEG;
       current_buffer->filename = visit_file;
+      update_mode_lines++;
     }
-  else if (!NILP (visit))
+  else if (quietly)
     return Qnil;
 
   if (!auto_saving)
@@ -2784,6 +3336,104 @@ to the file, instead of any buffer contents, and END is ignored.")
   return Qnil;
 }
 
+Lisp_Object merge ();
+
+DEFUN ("car-less-than-car", Fcar_less_than_car, Scar_less_than_car, 2, 2, 0,
+  "Return t if (car A) is numerically less than (car B).")
+  (a, b)
+     Lisp_Object a, b;
+{
+  return Flss (Fcar (a), Fcar (b));
+}
+
+/* Build the complete list of annotations appropriate for writing out
+   the text between START and END, by calling all the functions in
+   write-region-annotate-functions and merging the lists they return.
+   If one of these functions switches to a different buffer, we assume
+   that buffer contains altered text.  Therefore, the caller must
+   make sure to restore the current buffer in all cases,
+   as save-excursion would do.  */
+
+static Lisp_Object
+build_annotations (start, end)
+     Lisp_Object start, end;
+{
+  Lisp_Object annotations;
+  Lisp_Object p, res;
+  struct gcpro gcpro1, gcpro2;
+
+  annotations = Qnil;
+  p = Vwrite_region_annotate_functions;
+  GCPRO2 (annotations, p);
+  while (!NILP (p))
+    {
+      struct buffer *given_buffer = current_buffer;
+      Vwrite_region_annotations_so_far = annotations;
+      res = call2 (Fcar (p), start, end);
+      /* If the function makes a different buffer current,
+        assume that means this buffer contains altered text to be output.
+        Reset START and END from the buffer bounds
+        and discard all previous annotations because they should have
+        been dealt with by this function.  */
+      if (current_buffer != given_buffer)
+       {
+         start = BEGV;
+         end = ZV;
+         annotations = Qnil;
+       }
+      Flength (res);   /* Check basic validity of return value */
+      annotations = merge (annotations, res, Qcar_less_than_car);
+      p = Fcdr (p);
+    }
+  UNGCPRO;
+  return annotations;
+}
+
+/* Write to descriptor DESC the LEN characters starting at ADDR,
+   assuming they start at position POS in the buffer.
+   Intersperse with them the annotations from *ANNOT
+   (those which fall within the range of positions POS to POS + LEN),
+   each at its appropriate position.
+
+   Modify *ANNOT by discarding elements as we output them.
+   The return value is negative in case of system call failure.  */
+
+int
+a_write (desc, addr, len, pos, annot)
+     int desc;
+     register char *addr;
+     register int len;
+     int pos;
+     Lisp_Object *annot;
+{
+  Lisp_Object tem;
+  int nextpos;
+  int lastpos = pos + len;
+
+  while (NILP (*annot) || CONSP (*annot))
+    {
+      tem = Fcar_safe (Fcar (*annot));
+      if (INTEGERP (tem) && XINT (tem) >= pos && XFASTINT (tem) <= lastpos)
+       nextpos = XFASTINT (tem);
+      else
+       return e_write (desc, addr, lastpos - pos);
+      if (nextpos > pos)
+       {
+         if (0 > e_write (desc, addr, nextpos - pos))
+           return -1;
+         addr += nextpos - pos;
+         pos = nextpos;
+       }
+      tem = Fcdr (Fcar (*annot));
+      if (STRINGP (tem))
+       {
+         if (0 > e_write (desc, XSTRING (tem)->data, XSTRING (tem)->size))
+           return -1;
+       }
+      *annot = Fcdr (*annot);
+    }
+}
+
 int
 e_write (desc, addr, len)
      int desc;
@@ -2832,12 +3482,13 @@ This means that the file has not been changed since it was visited or saved.")
   CHECK_BUFFER (buf, 0);
   b = XBUFFER (buf);
 
-  if (XTYPE (b->filename) != Lisp_String) return Qt;
+  if (!STRINGP (b->filename)) return Qt;
   if (b->modtime == 0) return Qt;
 
   /* If the file name has special constructs in it,
      call the corresponding file handler.  */
-  handler = Ffind_file_name_handler (b->filename);
+  handler = Ffind_file_name_handler (b->filename,
+                                    Qverify_visited_file_modtime);
   if (!NILP (handler))
     return call2 (handler, Qverify_visited_file_modtime, buf);
 
@@ -2902,9 +3553,10 @@ An argument specifies the modification time value to use\n\
 
       /* If the file name has special constructs in it,
         call the corresponding file handler.  */
-      handler = Ffind_file_name_handler (filename);
+      handler = Ffind_file_name_handler (filename, Qset_visited_file_modtime);
       if (!NILP (handler))
-       return call3 (handler, Qfile_name_directory, filename, Qnil);
+       /* The handler can find the file name the same way we did.  */
+       return call2 (handler, Qset_visited_file_modtime, Qnil);
       else if (stat (XSTRING (filename)->data, &st) >= 0)
        current_buffer->modtime = st.st_mtime;
     }
@@ -2915,14 +3567,12 @@ An argument specifies the modification time value to use\n\
 Lisp_Object
 auto_save_error ()
 {
-  unsigned char *name = XSTRING (current_buffer->name)->data;
-
   ring_bell ();
-  message ("Autosaving...error for %s", name);
+  message ("Autosaving...error for %s", XSTRING (current_buffer->name)->data);
   Fsleep_for (make_number (1), Qnil);
-  message ("Autosaving...error!for %s", name);
+  message ("Autosaving...error!for %s", XSTRING (current_buffer->name)->data);
   Fsleep_for (make_number (1), Qnil);
-  message ("Autosaving...error for %s", name);
+  message ("Autosaving...error for %s", XSTRING (current_buffer->name)->data);
   Fsleep_for (make_number (1), Qnil);
   return Qnil;
 }
@@ -2946,13 +3596,22 @@ auto_save_1 ()
                   Qnil, Qlambda);
 }
 
+static Lisp_Object
+do_auto_save_unwind (desc)  /* used as unwind-protect function */
+     Lisp_Object desc;
+{
+  close (XINT (desc));
+  return Qnil;
+}
+
 DEFUN ("do-auto-save", Fdo_auto_save, Sdo_auto_save, 0, 2, "",
   "Auto-save all buffers that need it.\n\
 This is all buffers that have auto-saving enabled\n\
 and are changed since last auto-saved.\n\
 Auto-saving writes the buffer into a file\n\
 so that your editing is not lost if the system crashes.\n\
-This file is not the file you visited; that changes only when you save.\n\n\
+This file is not the file you visited; that changes only when you save.\n\
+Normally we run the normal hook `auto-save-hook' before saving.\n\n\
 Non-nil first argument means do not print any message if successful.\n\
 Non-nil second argument means save only current buffer.")
   (no_message, current_only)
@@ -2962,8 +3621,18 @@ Non-nil second argument means save only current buffer.")
   Lisp_Object tail, buf;
   int auto_saved = 0;
   char *omessage = echo_area_glyphs;
+  int omessage_length = echo_area_glyphs_length;
   extern int minibuf_level;
   int do_handled_files;
+  Lisp_Object oquit;
+  int listdesc;
+  int count = specpdl_ptr - specpdl;
+  int *ptr;
+
+  /* Ordinarily don't quit within this function,
+     but don't make it impossible to quit (in case we get hung in I/O).  */
+  oquit = Vquit_flag;
+  Vquit_flag = Qnil;
 
   /* No GCPRO needed, because (when it matters) all Lisp_Object variables
      point to non-strings reached from Vbuffer_alist.  */
@@ -2972,11 +3641,26 @@ Non-nil second argument means save only current buffer.")
   if (minibuf_level)
     no_message = Qt;
 
-  /* Vrun_hooks is nil before emacs is dumped, and inc-vers.el will
-     eventually call do-auto-save, so don't err here in that case. */
   if (!NILP (Vrun_hooks))
     call1 (Vrun_hooks, intern ("auto-save-hook"));
 
+  if (STRINGP (Vauto_save_list_file_name))
+    {
+#ifdef MSDOS
+      listdesc = open (XSTRING (Vauto_save_list_file_name)->data, 
+                      O_WRONLY | O_TRUNC | O_CREAT | O_TEXT,
+                      S_IREAD | S_IWRITE);
+#else /* not MSDOS */
+      listdesc = creat (XSTRING (Vauto_save_list_file_name)->data, 0666);
+#endif /* not MSDOS */
+    }
+  else
+    listdesc = -1;
+  
+  /* Arrange to close that file whether or not we get an error.  */
+  if (listdesc >= 0)
+    record_unwind_protect (do_auto_save_unwind, make_number (listdesc));
+
   /* First, save all files which don't have handlers.  If Emacs is
      crashing, the handlers may tweak what is causing Emacs to crash
      in the first place, and it would be a shame if Emacs failed to
@@ -2988,20 +3672,42 @@ Non-nil second argument means save only current buffer.")
       {
        buf = XCONS (XCONS (tail)->car)->cdr;
        b = XBUFFER (buf);
+      
+       /* Record all the buffers that have auto save mode
+          in the special file that lists them.  */
+       if (STRINGP (b->auto_save_file_name)
+           && listdesc >= 0 && do_handled_files == 0)
+         {
+           write (listdesc, XSTRING (b->auto_save_file_name)->data,
+                  XSTRING (b->auto_save_file_name)->size);
+           write (listdesc, "\n", 1);
+         }
 
        if (!NILP (current_only)
            && b != current_buffer)
          continue;
-      
+
        /* Check for auto save enabled
           and file changed since last auto save
           and file changed since last real save.  */
-       if (XTYPE (b->auto_save_file_name) == Lisp_String
+       if (STRINGP (b->auto_save_file_name)
            && b->save_modified < BUF_MODIFF (b)
            && b->auto_save_modified < BUF_MODIFF (b)
+           /* -1 means we've turned off autosaving for a while--see below.  */
+           && XINT (b->save_length) >= 0
            && (do_handled_files
-               || NILP (Ffind_file_name_handler (b->auto_save_file_name))))
+               || NILP (Ffind_file_name_handler (b->auto_save_file_name,
+                                                 Qwrite_region))))
          {
+           EMACS_TIME before_time, after_time;
+
+           EMACS_GET_TIME (before_time);
+
+           /* If we had a failure, don't try again for 20 minutes.  */
+           if (b->auto_save_failure_time >= 0
+               && EMACS_SECS (before_time) - b->auto_save_failure_time < 1200)
+             continue;
+
            if ((XFASTINT (b->save_length) * 10
                 > (BUF_Z (b) - BUF_BEG (b)) * 13)
                /* A short file is likely to change a large fraction;
@@ -3014,10 +3720,9 @@ Non-nil second argument means save only current buffer.")
                /* It has shrunk too much; turn off auto-saving here.  */
                message ("Buffer %s has shrunk a lot; auto save turned off there",
                         XSTRING (b->name)->data);
-               /* User can reenable saving with M-x auto-save.  */
-               b->auto_save_file_name = Qnil;
-               /* Prevent warning from repeating if user does so.  */
-               XFASTINT (b->save_length) = 0;
+               /* Turn off auto-saving until there's a real save,
+                  and prevent any more warnings.  */
+               XSET (b->save_length, Lisp_Int, -1);
                Fsleep_for (make_number (1), Qnil);
                continue;
              }
@@ -3029,6 +3734,13 @@ Non-nil second argument means save only current buffer.")
            b->auto_save_modified = BUF_MODIFF (b);
            XFASTINT (current_buffer->save_length) = Z - BEG;
            set_buffer_internal (old);
+
+           EMACS_GET_TIME (after_time);
+
+           /* If auto-save took more than 60 seconds,
+              assume it was an NFS failure that got a timeout.  */
+           if (EMACS_SECS (after_time) - EMACS_SECS (before_time) > 60)
+             b->auto_save_failure_time = EMACS_SECS (after_time);
          }
       }
 
@@ -3036,9 +3748,17 @@ Non-nil second argument means save only current buffer.")
   record_auto_save ();
 
   if (auto_saved && NILP (no_message))
-    message1 (omessage ? omessage : "Auto-saving...done");
+    {
+      if (omessage)
+       message2 (omessage, omessage_length);
+      else
+       message1 ("Auto-saving...done");
+    }
+
+  Vquit_flag = oquit;
 
   auto_saving = 0;
+  unbind_to (count, Qnil);
   return Qnil;
 }
 
@@ -3050,6 +3770,16 @@ No auto-save file will be written until the buffer changes again.")
 {
   current_buffer->auto_save_modified = MODIFF;
   XFASTINT (current_buffer->save_length) = Z - BEG;
+  current_buffer->auto_save_failure_time = -1;
+  return Qnil;
+}
+
+DEFUN ("clear-buffer-auto-save-failure", Fclear_buffer_auto_save_failure,
+  Sclear_buffer_auto_save_failure, 0, 0, 0,
+  "Clear any record of a recent auto-save failure in the current buffer.")
+  ()
+{
+  current_buffer->auto_save_failure_time = -1;
   return Qnil;
 }
 
@@ -3064,6 +3794,38 @@ DEFUN ("recent-auto-save-p", Frecent_auto_save_p, Srecent_auto_save_p,
 /* Reading and completing file names */
 extern Lisp_Object Ffile_name_completion (), Ffile_name_all_completions ();
 
+/* In the string VAL, change each $ to $$ and return the result.  */
+
+static Lisp_Object
+double_dollars (val)
+     Lisp_Object val;
+{
+  register unsigned char *old, *new;
+  register int n;
+  int osize, count;
+
+  osize = XSTRING (val)->size;
+  /* Quote "$" as "$$" to get it past substitute-in-file-name */
+  for (n = osize, count = 0, old = XSTRING (val)->data; n > 0; n--)
+    if (*old++ == '$') count++;
+  if (count > 0)
+    {
+      old = XSTRING (val)->data;
+      val = Fmake_string (make_number (osize + count), make_number (0));
+      new = XSTRING (val)->data;
+      for (n = osize; n > 0; n--)
+       if (*old != '$')
+         *new++ = *old++;
+       else
+         {
+           *new++ = '$';
+           *new++ = '$';
+           old++;
+         }
+    }
+  return val;
+}
+
 DEFUN ("read-file-name-internal", Fread_file_name_internal, Sread_file_name_internal,
   3, 3, 0,
   "Internal subroutine for read-file-name.  Do not call this.")
@@ -3108,43 +3870,20 @@ DEFUN ("read-file-name-internal", Fread_file_name_internal, Sread_file_name_inte
       specdir = Ffile_name_directory (string);
       val = Ffile_name_completion (name, realdir);
       UNGCPRO;
-      if (XTYPE (val) != Lisp_String)
+      if (!STRINGP (val))
        {
          if (changed)
-           return string;
+           return double_dollars (string);
          return val;
        }
 
       if (!NILP (specdir))
        val = concat2 (specdir, val);
 #ifndef VMS
-      {
-       register unsigned char *old, *new;
-       register int n;
-       int osize, count;
-
-       osize = XSTRING (val)->size;
-       /* Quote "$" as "$$" to get it past substitute-in-file-name */
-       for (n = osize, count = 0, old = XSTRING (val)->data; n > 0; n--)
-         if (*old++ == '$') count++;
-       if (count > 0)
-         {
-           old = XSTRING (val)->data;
-           val = Fmake_string (make_number (osize + count), make_number (0));
-           new = XSTRING (val)->data;
-           for (n = osize; n > 0; n--)
-             if (*old != '$')
-               *new++ = *old++;
-             else
-               {
-                 *new++ = '$';
-                 *new++ = '$';
-                 old++;
-               }
-         }
-      }
-#endif /* Not VMS */
+      return double_dollars (val);
+#else /* not VMS */
       return val;
+#endif /* not VMS */
     }
   UNGCPRO;
 
@@ -3164,7 +3903,8 @@ DEFUN ("read-file-name", Fread_file_name, Sread_file_name, 1, 5, 0,
   "Read file name, prompting with PROMPT and completing in directory DIR.\n\
 Value is not expanded---you must call `expand-file-name' yourself.\n\
 Default name to DEFAULT if user enters a null string.\n\
- (If DEFAULT is omitted, the visited file name is used.)\n\
+ (If DEFAULT is omitted, the visited file name is used,\n\
+  except that if INITIAL is specified, that combined with DIR is used.)\n\
 Fourth arg MUSTMATCH non-nil means require existing file's name.\n\
  Non-nil and non-t means also require confirmation after completion.\n\
 Fifth arg INITIAL specifies text to start with.\n\
@@ -3180,12 +3920,17 @@ DIR defaults to current buffer's directory default.")
   if (NILP (dir))
     dir = current_buffer->directory;
   if (NILP (defalt))
-    defalt = current_buffer->filename;
+    {
+      if (! NILP (initial))
+       defalt = Fexpand_file_name (initial, dir);
+      else
+       defalt = current_buffer->filename;
+    }
 
   /* If dir starts with user's homedir, change that to ~. */
   homedir = (char *) egetenv ("HOME");
   if (homedir != 0
-      && XTYPE (dir) == Lisp_String
+      && STRINGP (dir)
       && !strncmp (homedir, XSTRING (dir)->data, strlen (homedir))
       && XSTRING (dir)->data[strlen (homedir)] == '/')
     {
@@ -3197,7 +3942,6 @@ DIR defaults to current buffer's directory default.")
   if (insert_default_directory)
     {
       insdef = dir;
-      insdef1 = dir;
       if (!NILP (initial))
        {
          Lisp_Object args[2], pos;
@@ -3205,9 +3949,16 @@ DIR defaults to current buffer's directory default.")
          args[0] = insdef;
          args[1] = initial;
          insdef = Fconcat (2, args);
-         pos = make_number (XSTRING (dir)->size);
-         insdef1 = Fcons (insdef, pos);
+         pos = make_number (XSTRING (double_dollars (dir))->size);
+         insdef1 = Fcons (double_dollars (insdef), pos);
        }
+      else
+       insdef1 = double_dollars (insdef);
+    }
+  else if (!NILP (initial))
+    {
+      insdef = initial;
+      insdef1 = Fcons (double_dollars (insdef), 0);
     }
   else
     insdef = Qnil, insdef1 = Qnil;
@@ -3232,19 +3983,21 @@ DIR defaults to current buffer's directory default.")
   tem = Fstring_equal (val, insdef);
   if (!NILP (tem) && !NILP (defalt))
     return defalt;
+  if (XSTRING (val)->size == 0 && NILP (insdef))
+    {
+      if (!NILP (defalt))
+       return defalt;
+      else
+       error ("No default file name");
+    }
   return Fsubstitute_in_file_name (val);
 }
 
 #if 0                          /* Old version */
 DEFUN ("read-file-name", Fread_file_name, Sread_file_name, 1, 5, 0,
-  "Read file name, prompting with PROMPT and completing in directory DIR.\n\
-Value is not expanded---you must call `expand-file-name' yourself.\n\
-Default name to DEFAULT if user enters a null string.\n\
- (If DEFAULT is omitted, the visited file name is used.)\n\
-Fourth arg MUSTMATCH non-nil means require existing file's name.\n\
- Non-nil and non-t means also require confirmation after completion.\n\
-Fifth arg INITIAL specifies text to start with.\n\
-DIR defaults to current buffer's directory default.")
+  /* Don't confuse make-docfile by having two doc strings for this function.
+     make-docfile does not pay attention to #if, for good reason!  */
+  0)
   (prompt, dir, defalt, mustmatch, initial)
      Lisp_Object prompt, dir, defalt, mustmatch, initial;
 {
@@ -3261,7 +4014,7 @@ DIR defaults to current buffer's directory default.")
   /* If dir starts with user's homedir, change that to ~. */
   homedir = (char *) egetenv ("HOME");
   if (homedir != 0
-      && XTYPE (dir) == Lisp_String
+      && STRINGP (dir)
       && !strncmp (homedir, XSTRING (dir)->data, strlen (homedir))
       && XSTRING (dir)->data[strlen (homedir)] == '/')
     {
@@ -3311,7 +4064,7 @@ syms_of_fileio ()
   Qunhandled_file_name_directory = intern ("unhandled-file-name-directory");
   Qfile_name_as_directory = intern ("file-name-as-directory");
   Qcopy_file = intern ("copy-file");
-  Qmake_directory = intern ("make-directory");
+  Qmake_directory_internal = intern ("make-directory-internal");
   Qdelete_directory = intern ("delete-directory");
   Qdelete_file = intern ("delete-file");
   Qrename_file = intern ("rename-file");
@@ -3330,6 +4083,7 @@ syms_of_fileio ()
   Qinsert_file_contents = intern ("insert-file-contents");
   Qwrite_region = intern ("write-region");
   Qverify_visited_file_modtime = intern ("verify-visited-file-modtime");
+  Qset_visited_file_modtime = intern ("set-visited-file-modtime");
 
   staticpro (&Qexpand_file_name);
   staticpro (&Qdirectory_file_name);
@@ -3338,7 +4092,7 @@ syms_of_fileio ()
   staticpro (&Qunhandled_file_name_directory);
   staticpro (&Qfile_name_as_directory);
   staticpro (&Qcopy_file);
-  staticpro (&Qmake_directory);
+  staticpro (&Qmake_directory_internal);
   staticpro (&Qdelete_directory);
   staticpro (&Qdelete_file);
   staticpro (&Qrename_file);
@@ -3367,6 +4121,14 @@ syms_of_fileio ()
   Qfile_already_exists = intern("file-already-exists");
   staticpro (&Qfile_already_exists);
 
+#ifdef MSDOS
+  Qfind_buffer_file_type = intern ("find-buffer-file-type");
+  staticpro (&Qfind_buffer_file_type);
+#endif
+
+  Qcar_less_than_car = intern ("car-less-than-car");
+  staticpro (&Qcar_less_than_car);
+
   Fput (Qfile_error, Qerror_conditions,
        Fcons (Qfile_error, Fcons (Qerror, Qnil)));
   Fput (Qfile_error, Qerror_message,
@@ -3402,6 +4164,44 @@ The function `find-file-name-handler' checks this list for a handler\n\
 for its argument.");
   Vfile_name_handler_alist = Qnil;
 
+  DEFVAR_LISP ("after-insert-file-functions", &Vafter_insert_file_functions,
+    "A list of functions to be called at the end of `insert-file-contents'.\n\
+Each is passed one argument, the number of bytes inserted.  It should return\n\
+the new byte count, and leave point the same.  If `insert-file-contents' is\n\
+intercepted by a handler from `file-name-handler-alist', that handler is\n\
+responsible for calling the after-insert-file-functions if appropriate.");
+  Vafter_insert_file_functions = Qnil;
+
+  DEFVAR_LISP ("write-region-annotate-functions", &Vwrite_region_annotate_functions,
+    "A list of functions to be called at the start of `write-region'.\n\
+Each is passed two arguments, START and END as for `write-region'.  It should\n\
+return a list of pairs (POSITION . STRING) of strings to be effectively\n\
+inserted at the specified positions of the file being written (1 means to\n\
+insert before the first byte written).  The POSITIONs must be sorted into\n\
+increasing order.  If there are several functions in the list, the several\n\
+lists are merged destructively.");
+  Vwrite_region_annotate_functions = Qnil;
+
+  DEFVAR_LISP ("write-region-annotations-so-far",
+              &Vwrite_region_annotations_so_far,
+    "When an annotation function is called, this holds the previous annotations.\n\
+These are the annotations made by other annotation functions\n\
+that were already called.  See also `write-region-annotate-functions'.");
+  Vwrite_region_annotations_so_far = Qnil;
+
+  DEFVAR_LISP ("inhibit-file-name-handlers", &Vinhibit_file_name_handlers,
+    "A list of file name handlers that temporarily should not be used.\n\
+This applies only to the operation `inhibit-file-name-operation'.");
+  Vinhibit_file_name_handlers = Qnil;
+
+  DEFVAR_LISP ("inhibit-file-name-operation", &Vinhibit_file_name_operation,
+    "The operation for which `inhibit-file-name-handlers' is applicable.");
+  Vinhibit_file_name_operation = Qnil;
+
+  DEFVAR_LISP ("auto-save-list-file-name", &Vauto_save_list_file_name,
+    "File name in which we write a list of all auto save file names.");
+  Vauto_save_list_file_name = Qnil;
+
   defsubr (&Sfind_file_name_handler);
   defsubr (&Sfile_name_directory);
   defsubr (&Sfile_name_nondirectory);
@@ -3441,12 +4241,14 @@ for its argument.");
   defsubr (&Sfile_newer_than_file_p);
   defsubr (&Sinsert_file_contents);
   defsubr (&Swrite_region);
+  defsubr (&Scar_less_than_car);
   defsubr (&Sverify_visited_file_modtime);
   defsubr (&Sclear_visited_file_modtime);
   defsubr (&Svisited_file_modtime);
   defsubr (&Sset_visited_file_modtime);
   defsubr (&Sdo_auto_save);
   defsubr (&Sset_buffer_auto_saved);
+  defsubr (&Sclear_buffer_auto_save_failure);
   defsubr (&Srecent_auto_save_p);
 
   defsubr (&Sread_file_name_internal);