(byte-compile-lambda):
[bpt/emacs.git] / src / fileio.c
index f977ee0..23a0d8d 100644 (file)
@@ -17,6 +17,7 @@ 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 <sys/types.h>
 #include <sys/stat.h>
@@ -34,8 +35,6 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #include <perror.h>
 #include <stddef.h>
 #include <string.h>
-#else
-#include <sys/dir.h>
 #endif
 
 #include <errno.h>
@@ -52,8 +51,8 @@ extern int sys_nerr;
 #include <sys/time.h>
 #endif
 
-#include "config.h"
 #include "lisp.h"
+#include "intervals.h"
 #include "buffer.h"
 #include "window.h"
 
@@ -87,6 +86,10 @@ int auto_saving;
    a new file with the same mode as the original */
 int auto_save_mode_bits;
 
+/* Alist of elements (REGEXP . HANDLER) for file names 
+   whose I/O is done with a special handler.  */
+Lisp_Object Vfile_name_handler_alist;
+
 /* Nonzero means, when reading a filename in the minibuffer,
  start out by inserting the default directory into the minibuffer. */
 int insert_default_directory;
@@ -97,6 +100,8 @@ int vms_stmlf_recfm;
 
 Lisp_Object Qfile_error, Qfile_already_exists;
 
+Lisp_Object Qfile_name_history;
+
 report_file_error (string, data)
      char *string;
      Lisp_Object data;
@@ -124,6 +129,57 @@ close_file_unwind (fd)
   close (XFASTINT (fd));
 }
 \f
+Lisp_Object Qexpand_file_name;
+Lisp_Object Qdirectory_file_name;
+Lisp_Object Qfile_name_directory;
+Lisp_Object Qfile_name_nondirectory;
+Lisp_Object Qfile_name_as_directory;
+Lisp_Object Qcopy_file;
+Lisp_Object Qmake_directory;
+Lisp_Object Qdelete_directory;
+Lisp_Object Qdelete_file;
+Lisp_Object Qrename_file;
+Lisp_Object Qadd_name_to_file;
+Lisp_Object Qmake_symbolic_link;
+Lisp_Object Qfile_exists_p;
+Lisp_Object Qfile_executable_p;
+Lisp_Object Qfile_readable_p;
+Lisp_Object Qfile_symlink_p;
+Lisp_Object Qfile_writable_p;
+Lisp_Object Qfile_directory_p;
+Lisp_Object Qfile_accessible_directory_p;
+Lisp_Object Qfile_modes;
+Lisp_Object Qset_file_modes;
+Lisp_Object Qfile_newer_than_file_p;
+Lisp_Object Qinsert_file_contents;
+Lisp_Object Qwrite_region;
+Lisp_Object Qverify_visited_file_modtime;
+
+/* If FILENAME is handled specially on account of its syntax,
+   return its handler function.  Otherwise, return nil.  */
+
+Lisp_Object
+find_file_handler (filename)
+     Lisp_Object filename;
+{
+  Lisp_Object chain;
+  for (chain = Vfile_name_handler_alist; XTYPE (chain) == Lisp_Cons;
+       chain = XCONS (chain)->cdr)
+    {
+      Lisp_Object elt;
+      elt = XCONS (chain)->car;
+      if (XTYPE (elt) == Lisp_Cons)
+       {
+         Lisp_Object string;
+         string = XCONS (elt)->car;
+         if (XTYPE (string) == Lisp_String
+             && fast_string_match (string, filename) >= 0)
+           return XCONS (elt)->cdr;
+       }
+    }
+  return Qnil;
+}
+\f
 DEFUN ("file-name-directory", Ffile_name_directory, Sfile_name_directory,
   1, 1, 0,
   "Return the directory component in file name NAME.\n\
@@ -136,9 +192,16 @@ on VMS, perhaps instead a string ending in `:', `]' or `>'.")
 {
   register unsigned char *beg;
   register unsigned char *p;
+  Lisp_Object handler;
 
   CHECK_STRING (file, 0);
 
+  /* If the file name has special constructs in it,
+     call the corresponding file handler.  */
+  handler = find_file_handler (file);
+  if (!NILP (handler))
+    return call2 (handler, Qfile_name_directory, file);
+
   beg = XSTRING (file)->data;
   p = beg + XSTRING (file)->size;
 
@@ -163,9 +226,16 @@ or the entire name if it contains no slash.")
      Lisp_Object file;
 {
   register unsigned char *beg, *p, *end;
+  Lisp_Object handler;
 
   CHECK_STRING (file, 0);
 
+  /* If the file name has special constructs in it,
+     call the corresponding file handler.  */
+  handler = find_file_handler (file);
+  if (!NILP (handler))
+    return call2 (handler, Qfile_name_nondirectory, file);
+
   beg = XSTRING (file)->data;
   end = p = beg + XSTRING (file)->size;
 
@@ -264,10 +334,18 @@ On VMS, converts \"[X]FOO.DIR\" to \"[X.FOO]\", etc.")
      Lisp_Object file;
 {
   char *buf;
+  Lisp_Object handler;
 
   CHECK_STRING (file, 0);
   if (NILP (file))
     return Qnil;
+
+  /* If the file name has special constructs in it,
+     call the corresponding file handler.  */
+  handler = find_file_handler (file);
+  if (!NILP (handler))
+    return call2 (handler, Qfile_name_as_directory, file);
+
   buf = (char *) alloca (XSTRING (file)->size + 10);
   return build_string (file_name_as_directory (buf, XSTRING (file)->data));
 }
@@ -390,9 +468,11 @@ directory_file_name (src, dst)
                  && (ptr[rlen] == ']' || ptr[rlen] == '>')
                  && ptr[rlen - 1] == '.')
                {
-                 ptr[rlen - 1] = ']';
-                 ptr[rlen] = '\0';
-                 return directory_file_name (ptr, dst);
+                 char * buf = (char *) alloca (strlen (ptr) + 1);
+                 strcpy (buf, ptr);
+                 buf[rlen - 1] = ']';
+                 buf[rlen] = '\0';
+                 return directory_file_name (buf, dst);
                }
              else
                dst[slen - 1] = ':';
@@ -429,11 +509,19 @@ it returns a file name such as \"[X]Y.DIR.1\".")
      Lisp_Object directory;
 {
   char *buf;
+  Lisp_Object handler;
 
   CHECK_STRING (directory, 0);
 
   if (NILP (directory))
     return Qnil;
+
+  /* If the file name has special constructs in it,
+     call the corresponding file handler.  */
+  handler = find_file_handler (directory);
+  if (!NILP (handler))
+    return call2 (handler, Qdirectory_file_name, directory);
+
 #ifdef VMS
   /* 20 extra chars is insufficient for VMS, since we might perform a
      logical name translation. an equivalence string can be up to 255
@@ -489,9 +577,16 @@ See also the function `substitute-in-file-name'.")
   int lbrack = 0, rbrack = 0;
   int dots = 0;
 #endif /* VMS */
+  Lisp_Object handler;
   
   CHECK_STRING (name, 0);
 
+  /* If the file name has special constructs in it,
+     call the corresponding file handler.  */
+  handler = find_file_handler (name);
+  if (!NILP (handler))
+    return call3 (handler, Qexpand_file_name, name, defalt);
+
 #ifdef VMS
   /* Filenames on VMS are always upper case.  */
   name = Fupcase (name);
@@ -621,7 +716,7 @@ See also the function `substitute-in-file-name'.")
 #ifdef VMS
        || nm[1] == ':'
 #endif /* VMS */
-       || nm[1] == 0)/* ~/filename */
+       || nm[1] == 0)/* ~ by itself */
       {
        if (!(newdir = (unsigned char *) egetenv ("HOME")))
          newdir = (unsigned char *) "";
@@ -642,15 +737,18 @@ See also the function `substitute-in-file-name'.")
        o [p - nm] = 0;
 
        pw = (struct passwd *) getpwnam (o + 1);
-       if (!pw)
-         error ("\"%s\" isn't a registered user", o + 1);
-
+       if (pw)
+         {
+           newdir = (unsigned char *) pw -> pw_dir;
 #ifdef VMS
-       nm = p + 1;             /* skip the terminator */
+           nm = p + 1;         /* skip the terminator */
 #else
-       nm = p;
+           nm = p;
 #endif /* VMS */
-       newdir = (unsigned char *) pw -> pw_dir;
+         }
+
+       /* If we don't find a user of that name, leave the name
+          unchanged; don't move nm forward to p.  */
       }
 
   if (nm[0] != '/'
@@ -794,7 +892,8 @@ See also the function `substitute-in-file-name'.")
   return make_string (target, o - target);
 }
 #if 0
-DEFUN ("expand-file-name", Fexpand_file_name, Sexpand_file_name, 1, 2, 0,
+/* Changed this DEFUN to a DEAFUN, so as not to confuse `make-docfile'.
+DEAFUN ("expand-file-name", Fexpand_file_name, Sexpand_file_name, 1, 2, 0,
   "Convert FILENAME to absolute, and canonicalize it.\n\
 Second arg DEFAULT is directory to start with if FILENAME is relative\n\
  (does not start with slash); if DEFAULT is nil or missing,\n\
@@ -1310,6 +1409,13 @@ duplicates what `expand-file-name' does.")
 #endif /* not VMS */
 }
 \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.  */
+
 Lisp_Object
 expand_and_dir_to_file (filename, defdir)
      Lisp_Object filename, defdir;
@@ -1379,6 +1485,7 @@ A prefix arg makes KEEP-TIME non-nil.")
   int ifd, ofd, n;
   char buf[16 * 1024];
   struct stat st;
+  Lisp_Object handler;
   struct gcpro gcpro1, gcpro2;
   int count = specpdl_ptr - specpdl;
 
@@ -1387,6 +1494,17 @@ A prefix arg makes KEEP-TIME non-nil.")
   CHECK_STRING (newname, 1);
   filename = Fexpand_file_name (filename, Qnil);
   newname = Fexpand_file_name (newname, Qnil);
+
+  /* If the input file name has special constructs in it,
+     call the corresponding file handler.  */
+  handler = find_file_handler (filename);
+  if (!NILP (handler))
+    return call3 (handler, Qcopy_file, filename, newname);
+  /* Likewise for output file name.  */
+  handler = find_file_handler (newname);
+  if (!NILP (handler))
+    return call3 (handler, Qcopy_file, filename, newname);
+
   if (NILP (ok_if_already_exists)
       || XTYPE (ok_if_already_exists) == Lisp_Int)
     barf_or_query_if_file_exists (newname, "copy to it",
@@ -1448,28 +1566,39 @@ DEFUN ("make-directory", Fmake_directory, Smake_directory, 1, 1, "FMake director
      Lisp_Object dirname;
 {
   unsigned char *dir;
+  Lisp_Object handler;
 
   CHECK_STRING (dirname, 0);
   dirname = Fexpand_file_name (dirname, Qnil);
+
+  handler = find_file_handler (dirname);
+  if (!NILP (handler))
+    return call2 (handler, Qmake_directory, dirname);
   dir = XSTRING (dirname)->data;
 
   if (mkdir (dir, 0777) != 0)
     report_file_error ("Creating directory", Flist (1, &dirname));
 
-    return Qnil;
+  return Qnil;
 }
 
-DEFUN ("remove-directory", Fremove_directory, Sremove_directory, 1, 1, "FRemove directory: ",
-  "Remove a directory.  One argument, a file name string.")
+DEFUN ("delete-directory", Fdelete_directory, Sdelete_directory, 1, 1, "FDelete directory: ",
+  "Delete a directory.  One argument, a file name string.")
   (dirname)
      Lisp_Object dirname;
 {
   unsigned char *dir;
+  Lisp_Object handler;
 
   CHECK_STRING (dirname, 0);
   dirname = Fexpand_file_name (dirname, Qnil);
   dir = XSTRING (dirname)->data;
 
+  handler = find_file_handler (dirname);
+  if (!NILP (handler))
+    return call2 (handler, Qdelete_directory, dirname);
+
   if (rmdir (dir) != 0)
     report_file_error ("Removing directory", Flist (1, &dirname));
 
@@ -1482,8 +1611,14 @@ If file has multiple names, it continues to exist with the other names.")
   (filename)
      Lisp_Object filename;
 {
+  Lisp_Object handler;
   CHECK_STRING (filename, 0);
   filename = Fexpand_file_name (filename, Qnil);
+
+  handler = find_file_handler (filename);
+  if (!NILP (handler))
+    return call2 (handler, Qdelete_file, filename);
+
   if (0 > unlink (XSTRING (filename)->data))
     report_file_error ("Removing old name", Flist (1, &filename));
   return Qnil;
@@ -1503,6 +1638,7 @@ This is what happens in interactive use with M-x.")
 #ifdef NO_ARG_ARRAY
   Lisp_Object args[2];
 #endif
+  Lisp_Object handler;
   struct gcpro gcpro1, gcpro2;
 
   GCPRO2 (filename, newname);
@@ -1510,6 +1646,13 @@ This is what happens in interactive use with M-x.")
   CHECK_STRING (newname, 1);
   filename = Fexpand_file_name (filename, Qnil);
   newname = Fexpand_file_name (newname, Qnil);
+
+  /* If the file name has special constructs in it,
+     call the corresponding file handler.  */
+  handler = find_file_handler (filename);
+  if (!NILP (handler))
+    return call3 (handler, Qrename_file, filename, newname);
+
   if (NILP (ok_if_already_exists)
       || XTYPE (ok_if_already_exists) == Lisp_Int)
     barf_or_query_if_file_exists (newname, "rename to it",
@@ -1554,6 +1697,7 @@ This is what happens in interactive use with M-x.")
 #ifdef NO_ARG_ARRAY
   Lisp_Object args[2];
 #endif
+  Lisp_Object handler;
   struct gcpro gcpro1, gcpro2;
 
   GCPRO2 (filename, newname);
@@ -1561,6 +1705,13 @@ This is what happens in interactive use with M-x.")
   CHECK_STRING (newname, 1);
   filename = Fexpand_file_name (filename, Qnil);
   newname = Fexpand_file_name (newname, Qnil);
+
+  /* If the file name has special constructs in it,
+     call the corresponding file handler.  */
+  handler = find_file_handler (filename);
+  if (!NILP (handler))
+    return call3 (handler, Qadd_name_to_file, filename, newname);
+
   if (NILP (ok_if_already_exists)
       || XTYPE (ok_if_already_exists) == Lisp_Int)
     barf_or_query_if_file_exists (newname, "make it a new name",
@@ -1589,38 +1740,46 @@ Signals a `file-already-exists' error if a file NEWNAME 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\
 This happens for interactive use with M-x.")
-  (filename, newname, ok_if_already_exists)
-     Lisp_Object filename, newname, ok_if_already_exists;
+  (filename, linkname, ok_if_already_exists)
+     Lisp_Object filename, linkname, ok_if_already_exists;
 {
 #ifdef NO_ARG_ARRAY
   Lisp_Object args[2];
 #endif
+  Lisp_Object handler;
   struct gcpro gcpro1, gcpro2;
 
-  GCPRO2 (filename, newname);
+  GCPRO2 (filename, linkname);
   CHECK_STRING (filename, 0);
-  CHECK_STRING (newname, 1);
+  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
-  newname = Fexpand_file_name (newname, Qnil);
+  linkname = Fexpand_file_name (linkname, Qnil);
+
+  /* If the file name has special constructs in it,
+     call the corresponding file handler.  */
+  handler = find_file_handler (filename);
+  if (!NILP (handler))
+    return call3 (handler, Qmake_symbolic_link, filename, linkname);
+
   if (NILP (ok_if_already_exists)
       || XTYPE (ok_if_already_exists) == Lisp_Int)
-    barf_or_query_if_file_exists (newname, "make it a link",
+    barf_or_query_if_file_exists (linkname, "make it a link",
                                  XTYPE (ok_if_already_exists) == Lisp_Int);
-  if (0 > symlink (XSTRING (filename)->data, XSTRING (newname)->data))
+  if (0 > symlink (XSTRING (filename)->data, XSTRING (linkname)->data))
     {
       /* If we didn't complain already, silently delete existing file.  */
       if (errno == EEXIST)
        {
          unlink (XSTRING (filename)->data);
-         if (0 <= symlink (XSTRING (filename)->data, XSTRING (newname)->data))
+         if (0 <= symlink (XSTRING (filename)->data, XSTRING (linkname)->data))
            return Qnil;
        }
 
 #ifdef NO_ARG_ARRAY
       args[0] = filename;
-      args[1] = newname;
+      args[1] = linkname;
       report_file_error ("Making symbolic link", Flist (2, args));
 #else
       report_file_error ("Making symbolic link", Flist (2, &filename));
@@ -1710,9 +1869,17 @@ See also `file-readable-p' and `file-attributes'.")
      Lisp_Object filename;
 {
   Lisp_Object abspath;
+  Lisp_Object handler;
 
   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 = find_file_handler (abspath);
+  if (!NILP (handler))
+    return call2 (handler, Qfile_exists_p, abspath);
+
   return (access (XSTRING (abspath)->data, 0) >= 0) ? Qt : Qnil;
 }
 
@@ -1724,9 +1891,17 @@ For directories this means you can change to that directory.")
 
 {
   Lisp_Object abspath;
+  Lisp_Object handler;
 
   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 = find_file_handler (abspath);
+  if (!NILP (handler))
+    return call2 (handler, Qfile_executable_p, abspath);
+
   return (access (XSTRING (abspath)->data, 1) >= 0) ? Qt : Qnil;
 }
 
@@ -1737,9 +1912,17 @@ See also `file-exists-p' and `file-attributes'.")
      Lisp_Object filename;
 {
   Lisp_Object abspath;
+  Lisp_Object handler;
 
   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 = find_file_handler (abspath);
+  if (!NILP (handler))
+    return call2 (handler, Qfile_readable_p, abspath);
+
   return (access (XSTRING (abspath)->data, 4) >= 0) ? Qt : Qnil;
 }
 
@@ -1755,10 +1938,17 @@ Otherwise returns NIL.")
   int bufsize;
   int valsize;
   Lisp_Object val;
+  Lisp_Object handler;
 
   CHECK_STRING (filename, 0);
   filename = Fexpand_file_name (filename, Qnil);
 
+  /* If the file name has special constructs in it,
+     call the corresponding file handler.  */
+  handler = find_file_handler (filename);
+  if (!NILP (handler))
+    return call2 (handler, Qfile_symlink_p, filename);
+
   bufsize = 100;
   while (1)
     {
@@ -1791,9 +1981,17 @@ DEFUN ("file-writable-p", Ffile_writable_p, Sfile_writable_p, 1, 1, 0,
      Lisp_Object filename;
 {
   Lisp_Object abspath, dir;
+  Lisp_Object handler;
 
   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 = find_file_handler (abspath);
+  if (!NILP (handler))
+    return call2 (handler, Qfile_writable_p, abspath);
+
   if (access (XSTRING (abspath)->data, 0) >= 0)
     return (access (XSTRING (abspath)->data, 2) >= 0) ? Qt : Qnil;
   dir = Ffile_name_directory (abspath);
@@ -1814,9 +2012,16 @@ if the directory so specified exists and really is a directory.")
 {
   register Lisp_Object abspath;
   struct stat st;
+  Lisp_Object handler;
 
   abspath = expand_and_dir_to_file (filename, current_buffer->directory);
 
+  /* If the file name has special constructs in it,
+     call the corresponding file handler.  */
+  handler = find_file_handler (abspath);
+  if (!NILP (handler))
+    return call2 (handler, Qfile_directory_p, abspath);
+
   if (stat (XSTRING (abspath)->data, &st) < 0)
     return Qnil;
   return (st.st_mode & S_IFMT) == S_IFDIR ? Qt : Qnil;
@@ -1832,6 +2037,14 @@ searchable directory.")
   (filename)
      Lisp_Object filename;
 {
+  Lisp_Object handler;
+
+  /* If the file name has special constructs in it,
+     call the corresponding file handler.  */
+  handler = find_file_handler (filename);
+  if (!NILP (handler))
+    return call2 (handler, Qfile_accessible_directory_p, filename);
+
   if (NILP (Ffile_directory_p (filename))
       || NILP (Ffile_executable_p (filename)))
     return Qnil;
@@ -1846,9 +2059,16 @@ DEFUN ("file-modes", Ffile_modes, Sfile_modes, 1, 1, 0,
 {
   Lisp_Object abspath;
   struct stat st;
+  Lisp_Object handler;
 
   abspath = expand_and_dir_to_file (filename, current_buffer->directory);
 
+  /* If the file name has special constructs in it,
+     call the corresponding file handler.  */
+  handler = find_file_handler (abspath);
+  if (!NILP (handler))
+    return call2 (handler, Qfile_modes, abspath);
+
   if (stat (XSTRING (abspath)->data, &st) < 0)
     return Qnil;
   return make_number (st.st_mode & 07777);
@@ -1861,10 +2081,17 @@ Only the 12 low bits of MODE are used.")
      Lisp_Object filename, mode;
 {
   Lisp_Object abspath;
+  Lisp_Object handler;
 
   abspath = Fexpand_file_name (filename, current_buffer->directory);
   CHECK_NUMBER (mode, 1);
 
+  /* If the file name has special constructs in it,
+     call the corresponding file handler.  */
+  handler = find_file_handler (abspath);
+  if (!NILP (handler))
+    return call3 (handler, Qset_file_modes, abspath, mode);
+
 #ifndef APOLLO
   if (chmod (XSTRING (abspath)->data, XINT (mode)) < 0)
     report_file_error ("Doing chmod", Fcons (abspath, Qnil));
@@ -1930,6 +2157,18 @@ created files will not have that permission enabled.")
   return mask;
 }
 
+#ifdef unix
+
+DEFUN ("unix-sync", Funix_sync, Sunix_sync, 0, 0, "",
+  "Tell Unix to finish all pending disk updates.")
+  ()
+{
+  sync ();
+  return Qnil;
+}
+
+#endif /* unix */
+
 DEFUN ("file-newer-than-file-p", Ffile_newer_than_file_p, Sfile_newer_than_file_p, 2, 2, 0,
   "Return t if file FILE1 is newer than file FILE2.\n\
 If FILE1 does not exist, the answer is nil;\n\
@@ -1937,23 +2176,33 @@ otherwise, if FILE2 does not exist, the answer is t.")
   (file1, file2)
      Lisp_Object file1, file2;
 {
-  Lisp_Object abspath;
+  Lisp_Object abspath1, abspath2;
   struct stat st;
   int mtime1;
+  Lisp_Object handler;
+  struct gcpro gcpro1, gcpro2;
 
   CHECK_STRING (file1, 0);
   CHECK_STRING (file2, 0);
 
-  abspath = expand_and_dir_to_file (file1, current_buffer->directory);
+  abspath1 = Qnil;
+  GCPRO2 (abspath1, file2);
+  abspath1 = expand_and_dir_to_file (file1, current_buffer->directory);
+  abspath2 = expand_and_dir_to_file (file2, current_buffer->directory);
+  UNGCPRO;
+
+  /* If the file name has special constructs in it,
+     call the corresponding file handler.  */
+  handler = find_file_handler (abspath1);
+  if (!NILP (handler))
+    return call3 (handler, Qfile_newer_than_file_p, abspath1, abspath2);
 
-  if (stat (XSTRING (abspath)->data, &st) < 0)
+  if (stat (XSTRING (abspath1)->data, &st) < 0)
     return Qnil;
 
   mtime1 = st.st_mtime;
 
-  abspath = expand_and_dir_to_file (file2, current_buffer->directory);
-
-  if (stat (XSTRING (abspath)->data, &st) < 0)
+  if (stat (XSTRING (abspath2)->data, &st) < 0)
     return Qt;
 
   return (mtime1 > st.st_mtime) ? Qt : Qnil;
@@ -1976,7 +2225,10 @@ before the error is signaled.")
   register int how_much;
   int count = specpdl_ptr - specpdl;
   struct gcpro gcpro1;
-  
+  Lisp_Object handler, val;
+
+  val = Qnil;
+
   GCPRO1 (filename);
   if (!NILP (current_buffer->read_only))
     Fbarf_if_buffer_read_only();
@@ -1984,6 +2236,16 @@ before the error is signaled.")
   CHECK_STRING (filename, 0);
   filename = Fexpand_file_name (filename, Qnil);
 
+  /* If the file name has special constructs in it,
+     call the corresponding file handler.  */
+  handler = find_file_handler (filename);
+  if (!NILP (handler))
+    {
+      val = call3 (handler, Qinsert_file_contents, filename, visit);
+      st.st_mtime = 0;
+      goto handled;
+    }
+
   fd = -1;
 
 #ifndef APOLLO
@@ -2004,9 +2266,20 @@ before the error is signaled.")
 
   record_unwind_protect (close_file_unwind, make_number (fd));
 
+#ifdef S_IFSOCK
+  /* 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)
+    Fsignal (Qfile_error,
+            Fcons (build_string ("reading from named pipe"),
+                   Fcons (filename, Qnil)));
+#endif
+
   /* Supposedly happens on VMS.  */
   if (st.st_size < 0)
     error ("File size is negative");
+
   {
     register Lisp_Object temp;
 
@@ -2048,8 +2321,13 @@ before the error is signaled.")
     }
 
   if (inserted > 0)
-    MODIFF++;
-  record_insert (point, inserted);
+    {
+      record_insert (point, inserted);
+
+      /* Only defined if Emacs is compiled with USE_TEXT_PROPERTIES */
+      offset_intervals (current_buffer, point, inserted);
+      MODIFF++;
+    }
 
   close (fd);
 
@@ -2061,6 +2339,7 @@ before the error is signaled.")
           XSTRING (filename)->data, err_str (errno));
 
  notfound:
+ handled:
 
   if (!NILP (visit))
     {
@@ -2073,18 +2352,23 @@ before the error is signaled.")
       current_buffer->auto_save_modified = MODIFF;
       XFASTINT (current_buffer->save_length) = Z - BEG;
 #ifdef CLASH_DETECTION
-      if (!NILP (current_buffer->filename))
-       unlock_file (current_buffer->filename);
-      unlock_file (filename);
+      if (NILP (handler))
+       {
+         if (!NILP (current_buffer->filename))
+           unlock_file (current_buffer->filename);
+         unlock_file (filename);
+       }
 #endif /* CLASH_DETECTION */
       current_buffer->filename = filename;
       /* If visiting nonexistent file, return nil.  */
-      if (st.st_mtime == -1)
+      if (current_buffer->modtime == -1)
        report_file_error ("Opening input file", Fcons (filename, Qnil));
     }
 
   signal_after_change (point, 0, inserted);
   
+  if (!NILP (val))
+    RETURN_UNGCPRO (val);
   RETURN_UNGCPRO (Fcons (filename,
                         Fcons (make_number (inserted),
                                Qnil)));
@@ -2102,6 +2386,9 @@ Optional fifth argument VISIT if t means\n\
   and mark buffer not modified.\n\
 If VISIT is neither t nor nil, it means do not print\n\
   the \"Wrote file\" message.\n\
+If VISIT is a string, it is a second file name;\n\
+  the output goes to FILENAME, but the buffer is marked as visiting VISIT.\n\
+  VISIT is also the file name to lock and unlock for clash detection.\n\
 Kludgy feature: if START is a string, then that string is written\n\
 to the file, instead of any buffer contents, and END is ignored.")
   (start, end, filename, append, visit)
@@ -2117,6 +2404,9 @@ to the file, instead of any buffer contents, and END is ignored.")
 #ifdef VMS
   unsigned char *fname = 0;    /* If non-0, original filename (must rename) */
 #endif /* VMS */
+  Lisp_Object handler;
+  Lisp_Object visit_file = XTYPE (visit) == Lisp_String ? visit : filename;
+  struct gcpro gcpro1, gcpro2, gcpro3, gcpro4;
 
   /* Special kludge to simplify auto-saving */
   if (NILP (start))
@@ -2127,14 +2417,46 @@ to the file, instead of any buffer contents, and END is ignored.")
   else if (XTYPE (start) != Lisp_String)
     validate_region (&start, &end);
 
+  GCPRO4 (start, filename, visit, visit_file);
   filename = Fexpand_file_name (filename, Qnil);
-  fn = XSTRING (filename)->data;
+
+  /* If the file name has special constructs in it,
+     call the corresponding file handler.  */
+  handler = find_file_handler (filename);
+
+  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)
+       {
+         current_buffer->modtime = 0;
+         current_buffer->save_modified = MODIFF;
+         XFASTINT (current_buffer->save_length) = Z - BEG;
+         current_buffer->filename = visit_file;
+       }
+      UNGCPRO;
+      return val;
+    }
 
 #ifdef CLASH_DETECTION
   if (!auto_saving)
-    lock_file (filename);
+    lock_file (visit_file);
 #endif /* CLASH_DETECTION */
 
+  fn = XSTRING (filename)->data;
   desc = -1;
   if (!NILP (append))
     desc = open (fn, O_WRONLY);
@@ -2189,11 +2511,13 @@ to the file, instead of any buffer contents, and END is ignored.")
   desc = creat (fn, auto_saving ? auto_save_mode_bits : 0666);
 #endif /* not VMS */
 
+  UNGCPRO;
+
   if (desc < 0)
     {
 #ifdef CLASH_DETECTION
       save_errno = errno;
-      if (!auto_saving) unlock_file (filename);
+      if (!auto_saving) unlock_file (visit_file);
       errno = save_errno;
 #endif /* CLASH_DETECTION */
       report_file_error ("Opening output file", Fcons (filename, Qnil));
@@ -2205,7 +2529,7 @@ to the file, instead of any buffer contents, and END is ignored.")
     if (lseek (desc, 0, 2) < 0)
       {
 #ifdef CLASH_DETECTION
-       if (!auto_saving) unlock_file (filename);
+       if (!auto_saving) unlock_file (visit_file);
 #endif /* CLASH_DETECTION */
        report_file_error ("Lseek error", Fcons (filename, Qnil));
       }
@@ -2313,29 +2637,29 @@ to the file, instead of any buffer contents, and END is ignored.")
 
 #ifdef CLASH_DETECTION
   if (!auto_saving)
-    unlock_file (filename);
+    unlock_file (visit_file);
 #endif /* CLASH_DETECTION */
 
   /* Do this before reporting IO error
      to avoid a "file has changed on disk" warning on
      next attempt to save.  */
-  if (EQ (visit, Qt))
+  if (EQ (visit, Qt) || XTYPE (visit) == Lisp_String)
     current_buffer->modtime = st.st_mtime;
 
   if (failure)
     error ("IO error writing %s: %s", fn, err_str (save_errno));
 
-  if (EQ (visit, Qt))
+  if (EQ (visit, Qt) || XTYPE (visit) == Lisp_String)
     {
       current_buffer->save_modified = MODIFF;
       XFASTINT (current_buffer->save_length) = Z - BEG;
-      current_buffer->filename = filename;
+      current_buffer->filename = visit_file;
     }
   else if (!NILP (visit))
     return Qnil;
 
   if (!auto_saving)
-    message ("Wrote %s", fn);
+    message ("Wrote %s", XSTRING (visit_file)->data);
 
   return Qnil;
 }
@@ -2383,6 +2707,7 @@ This means that the file has not been changed since it was visited or saved.")
 {
   struct buffer *b;
   struct stat st;
+  Lisp_Object handler;
 
   CHECK_BUFFER (buf, 0);
   b = XBUFFER (buf);
@@ -2390,6 +2715,12 @@ This means that the file has not been changed since it was visited or saved.")
   if (XTYPE (b->filename) != Lisp_String) return Qt;
   if (b->modtime == 0) return Qt;
 
+  /* If the file name has special constructs in it,
+     call the corresponding file handler.  */
+  handler = find_file_handler (b->filename);
+  if (!NILP (handler))
+    return call2 (handler, Qverify_visited_file_modtime, buf);
+
   if (stat (XSTRING (b->filename)->data, &st) < 0)
     {
       /* If the file doesn't exist now and didn't exist before,
@@ -2427,10 +2758,17 @@ or if the file itself has been changed for some known benign reason.")
 {
   register Lisp_Object filename;
   struct stat st;
+  Lisp_Object handler;
 
   filename = Fexpand_file_name (current_buffer->filename, Qnil);
+
+  /* If the file name has special constructs in it,
+     call the corresponding file handler.  */
+  handler = find_file_handler (filename);
+  if (!NILP (handler))
+    current_buffer->modtime = 0;
   
-  if (stat (XSTRING (filename)->data, &st) >= 0)
+  else if (stat (XSTRING (filename)->data, &st) >= 0)
     current_buffer->modtime = st.st_mtime;
 
   return Qnil;
@@ -2443,11 +2781,11 @@ auto_save_error ()
 
   ring_bell ();
   message ("Autosaving...error for %s", name);
-  Fsleep_for (make_number (1));
+  Fsleep_for (make_number (1), Qnil);
   message ("Autosaving...error!for %s", name);
-  Fsleep_for (make_number (1));
+  Fsleep_for (make_number (1), Qnil);
   message ("Autosaving...error for %s", name);
-  Fsleep_for (make_number (1));
+  Fsleep_for (make_number (1), Qnil);
   return Qnil;
 }
 
@@ -2527,7 +2865,7 @@ Non-nil second argument means save only current buffer.")
              b->auto_save_file_name = Qnil;
              /* Prevent warning from repeating if user does so.  */
              XFASTINT (b->save_length) = 0;
-             Fsleep_for (make_number (1));
+             Fsleep_for (make_number (1), Qnil);
              continue;
            }
          set_buffer_internal (b);
@@ -2541,8 +2879,8 @@ Non-nil second argument means save only current buffer.")
        }
     }
 
-  if (auto_saved)
-    record_auto_save ();
+  /* Prevent another auto save till enough input events come in.  */
+  record_auto_save ();
 
   if (auto_saved && NILP (nomsg))
     message1 (omessage ? omessage : "Auto-saving...done");
@@ -2582,36 +2920,46 @@ DEFUN ("read-file-name-internal", Fread_file_name_internal, Sread_file_name_inte
      lambda for verify final value */
 {
   Lisp_Object name, specdir, realdir, val, orig_string;
+  int changed;
+  struct gcpro gcpro1, gcpro2, gcpro3, gcpro4;
+
+  realdir = dir;
+  name = string;
+  orig_string = Qnil;
+  specdir = Qnil;
+  changed = 0;
+  /* No need to protect ACTION--we only compare it with t and nil.  */
+  GCPRO4 (string, realdir, name, specdir);
 
   if (XSTRING (string)->size == 0)
     {
-      orig_string = Qnil; 
-      name = string;
-      realdir = dir;
       if (EQ (action, Qlambda))
-       return Qnil;
+       {
+         UNGCPRO;
+         return Qnil;
+       }
     }
   else
     {
       orig_string = string;
       string = Fsubstitute_in_file_name (string);
+      changed = NILP (Fstring_equal (string, orig_string));
       name = Ffile_name_nondirectory (string);
-      realdir = Ffile_name_directory (string);
-      if (NILP (realdir))
-       realdir = dir;
-      else
-       realdir = Fexpand_file_name (realdir, dir);
+      val = Ffile_name_directory (string);
+      if (! NILP (val))
+       realdir = Fexpand_file_name (val, realdir);
     }
 
   if (NILP (action))
     {
       specdir = Ffile_name_directory (string);
       val = Ffile_name_completion (name, realdir);
+      UNGCPRO;
       if (XTYPE (val) != Lisp_String)
        {
-         if (NILP (Fstring_equal (string, orig_string)))
+         if (changed)
            return string;
-         return (val);
+         return val;
        }
 
       if (!NILP (specdir))
@@ -2643,8 +2991,9 @@ DEFUN ("read-file-name-internal", Fread_file_name_internal, Sread_file_name_inte
          }
       }
 #endif /* Not VMS */
-      return (val);
+      return val;
     }
+  UNGCPRO;
 
   if (EQ (action, Qt))
     return Ffile_name_all_completions (name, realdir);
@@ -2670,7 +3019,7 @@ DIR defaults to current buffer's directory default.")
   (prompt, dir, defalt, mustmatch, initial)
      Lisp_Object prompt, dir, defalt, mustmatch, initial;
 {
-  Lisp_Object val, insdef, tem, backup_n;
+  Lisp_Object val, insdef, insdef1, tem;
   struct gcpro gcpro1, gcpro2;
   register char *homedir;
   int count;
@@ -2695,23 +3044,20 @@ DIR defaults to current buffer's directory default.")
   if (insert_default_directory)
     {
       insdef = dir;
+      insdef1 = dir;
       if (!NILP (initial))
        {
-         Lisp_Object args[2];
+         Lisp_Object args[2], pos;
 
          args[0] = insdef;
          args[1] = initial;
          insdef = Fconcat (2, args);
-         backup_n = make_number (- (XSTRING (initial)->size));
+         pos = make_number (XSTRING (dir)->size);
+         insdef1 = Fcons (insdef, pos);
        }
-      else
-       backup_n = Qnil;
     }
   else
-    {
-      insdef = build_string ("");
-      backup_n = Qnil;
-    }
+    insdef = Qnil, insdef1 = Qnil;
 
 #ifdef VMS
   count = specpdl_ptr - specpdl;
@@ -2720,8 +3066,8 @@ DIR defaults to current buffer's directory default.")
 
   GCPRO2 (insdef, defalt);
   val = Fcompleting_read (prompt, intern ("read-file-name-internal"),
-                         dir, mustmatch,
-                         insert_default_directory ? insdef : Qnil, backup_n);
+                         dir, mustmatch, insdef1,
+                         Qfile_name_history);
 
 #ifdef VMS
   unbind_to (count, Qnil);
@@ -2786,7 +3132,8 @@ DIR defaults to current buffer's directory default.")
   GCPRO2 (insdef, defalt);
   val = Fcompleting_read (prompt, intern ("read-file-name-internal"),
                          dir, mustmatch,
-                         insert_default_directory ? insdef : Qnil, Qnil);
+                         insert_default_directory ? insdef : Qnil,
+                         Qfile_name_history);
 
 #ifdef VMS
   unbind_to (count, Qnil);
@@ -2804,6 +3151,57 @@ DIR defaults to current buffer's directory default.")
 \f
 syms_of_fileio ()
 {
+  Qexpand_file_name = intern ("expand-file-name");
+  Qdirectory_file_name = intern ("directory-file-name");
+  Qfile_name_directory = intern ("file-name-directory");
+  Qfile_name_nondirectory = intern ("file-name-nondirectory");
+  Qfile_name_as_directory = intern ("file-name-as-directory");
+  Qcopy_file = intern ("copy-file");
+  Qmake_directory = intern ("make-directory");
+  Qdelete_directory = intern ("delete-directory");
+  Qdelete_file = intern ("delete-file");
+  Qrename_file = intern ("rename-file");
+  Qadd_name_to_file = intern ("add-name-to-file");
+  Qmake_symbolic_link = intern ("make-symbolic-link");
+  Qfile_exists_p = intern ("file-exists-p");
+  Qfile_executable_p = intern ("file-executable-p");
+  Qfile_readable_p = intern ("file-readable-p");
+  Qfile_symlink_p = intern ("file-symlink-p");
+  Qfile_writable_p = intern ("file-writable-p");
+  Qfile_directory_p = intern ("file-directory-p");
+  Qfile_accessible_directory_p = intern ("file-accessible-directory-p");
+  Qfile_modes = intern ("file-modes");
+  Qset_file_modes = intern ("set-file-modes");
+  Qfile_newer_than_file_p = intern ("file-newer-than-file-p");
+  Qinsert_file_contents = intern ("insert-file-contents");
+  Qwrite_region = intern ("write-region");
+  Qverify_visited_file_modtime = intern ("verify-visited-file-modtime");
+
+  Qfile_name_history = intern ("file-name-history");
+  Fset (Qfile_name_history, Qnil);
+
+  staticpro (&Qcopy_file);
+  staticpro (&Qmake_directory);
+  staticpro (&Qdelete_directory);
+  staticpro (&Qdelete_file);
+  staticpro (&Qrename_file);
+  staticpro (&Qadd_name_to_file);
+  staticpro (&Qmake_symbolic_link);
+  staticpro (&Qfile_exists_p);
+  staticpro (&Qfile_executable_p);
+  staticpro (&Qfile_readable_p);
+  staticpro (&Qfile_symlink_p);
+  staticpro (&Qfile_writable_p);
+  staticpro (&Qfile_directory_p);
+  staticpro (&Qfile_accessible_directory_p);
+  staticpro (&Qfile_modes);
+  staticpro (&Qset_file_modes);
+  staticpro (&Qfile_newer_than_file_p);
+  staticpro (&Qinsert_file_contents);
+  staticpro (&Qwrite_region);
+  staticpro (&Qverify_visited_file_modtime);
+  staticpro (&Qfile_name_history);
+
   Qfile_error = intern ("file-error");
   staticpro (&Qfile_error);
   Qfile_already_exists = intern("file-already-exists");
@@ -2829,6 +3227,19 @@ syms_of_fileio ()
 nil means use format `var'.  This variable is meaningful only on VMS.");
   vms_stmlf_recfm = 0;
 
+  DEFVAR_LISP ("file-name-handler-alist", &Vfile_name_handler_alist,
+    "*Alist of elements (REGEXP . HANDLER) for file names handled specially.\n\
+If a file name matches REGEXP, then all I/O on that file is done by calling\n\
+HANDLER.\n\
+\n\
+The first argument given to HANDLER is the name of the I/O primitive\n\
+to be handled; the remaining arguments are the arguments that were\n\
+passed to that primitive.  For example, if you do\n\
+    (file-exists-p FILENAME)\n\
+and FILENAME is handled by HANDLER, then HANDLER is called like this:\n\
+    (funcall HANDLER 'file-exists-p FILENAME)");
+  Vfile_name_handler_alist = Qnil;
+
   defsubr (&Sfile_name_directory);
   defsubr (&Sfile_name_nondirectory);
   defsubr (&Sfile_name_as_directory);
@@ -2838,7 +3249,7 @@ nil means use format `var'.  This variable is meaningful only on VMS.");
   defsubr (&Ssubstitute_in_file_name);
   defsubr (&Scopy_file);
   defsubr (&Smake_directory);
-  defsubr (&Sremove_directory);
+  defsubr (&Sdelete_directory);
   defsubr (&Sdelete_file);
   defsubr (&Srename_file);
   defsubr (&Sadd_name_to_file);
@@ -2875,4 +3286,8 @@ nil means use format `var'.  This variable is meaningful only on VMS.");
 
   defsubr (&Sread_file_name_internal);
   defsubr (&Sread_file_name);
+
+#ifdef unix
+  defsubr (&Sunix_sync);
+#endif
 }