New unwind-protect flavors to better type-check C callbacks.
[bpt/emacs.git] / src / fileio.c
index 89ae89e..1b5208e 100644 (file)
@@ -159,11 +159,18 @@ static bool e_write (int, Lisp_Object, ptrdiff_t, ptrdiff_t,
                     struct coding_system *);
 
 \f
+/* Signal a file-access failure.  STRING describes the failure,
+   NAME the file involved, and ERRORNO the errno value.
+
+   If NAME is neither null nor a pair, package it up as a singleton
+   list before reporting it; this saves report_file_errno's caller the
+   trouble of preserving errno before calling list1.  */
+
 void
-report_file_error (const char *string, Lisp_Object data)
+report_file_errno (char const *string, Lisp_Object name, int errorno)
 {
+  Lisp_Object data = CONSP (name) || NILP (name) ? name : list1 (name);
   Lisp_Object errstring;
-  int errorno = errno;
   char *str;
 
   synchronize_system_messages_locale ();
@@ -196,21 +203,28 @@ report_file_error (const char *string, Lisp_Object data)
       }
 }
 
-Lisp_Object
-close_file_unwind (Lisp_Object fd)
+/* Signal a file-access failure that set errno.  STRING describes the
+   failure, NAME the file involved.  */
+
+void
+report_file_error (char const *string, Lisp_Object name)
 {
-  emacs_close (XFASTINT (fd));
-  return Qnil;
+  report_file_errno (string, name, errno);
+}
+
+void
+close_file_unwind (int fd)
+{
+  emacs_close (fd);
 }
 
 /* Restore point, having saved it as a marker.  */
 
-Lisp_Object
+void
 restore_point_unwind (Lisp_Object location)
 {
   Fgoto_char (location);
   Fset_marker (location, Qnil, Qnil);
-  return Qnil;
 }
 
 \f
@@ -741,7 +755,7 @@ make_temp_name (Lisp_Object prefix, bool base64_p)
               dog-slow, but also useless since eventually nil would
               have to be returned anyway.  */
            report_file_error ("Cannot create temporary name for prefix",
-                              Fcons (prefix, Qnil));
+                              prefix);
          /* not reached */
        }
     }
@@ -2011,7 +2025,7 @@ entries (depending on how Emacs was built).  */)
     {
       acl = acl_get_file (SDATA (encoded_file), ACL_TYPE_ACCESS);
       if (acl == NULL && acl_errno_valid (errno))
-       report_file_error ("Getting ACL", Fcons (file, Qnil));
+       report_file_error ("Getting ACL", file);
     }
   if (!CopyFile (SDATA (encoded_file),
                 SDATA (encoded_newname),
@@ -2019,11 +2033,8 @@ entries (depending on how Emacs was built).  */)
     {
       /* CopyFile doesn't set errno when it fails.  By far the most
         "popular" reason is that the target is read-only.  */
-      if (GetLastError () == 5)
-       errno = EACCES;
-      else
-       errno = EPERM;
-      report_file_error ("Copying file", Fcons (file, Fcons (newname, Qnil)));
+      report_file_errno ("Copying file", list2 (file, newname),
+                        GetLastError () == 5 ? EACCES : EPERM);
     }
   /* CopyFile retains the timestamp by default.  */
   else if (NILP (keep_time))
@@ -2053,7 +2064,7 @@ entries (depending on how Emacs was built).  */)
       bool fail =
        acl_set_file (SDATA (encoded_newname), ACL_TYPE_ACCESS, acl) != 0;
       if (fail && acl_errno_valid (errno))
-       report_file_error ("Setting ACL", Fcons (newname, Qnil));
+       report_file_error ("Setting ACL", newname);
 
       acl_free (acl);
     }
@@ -2063,12 +2074,12 @@ entries (depending on how Emacs was built).  */)
   immediate_quit = 0;
 
   if (ifd < 0)
-    report_file_error ("Opening input file", Fcons (file, Qnil));
+    report_file_error ("Opening input file", file);
 
-  record_unwind_protect (close_file_unwind, make_number (ifd));
+  record_unwind_protect_int (close_file_unwind, ifd);
 
   if (fstat (ifd, &st) != 0)
-    report_file_error ("Input file status", Fcons (file, Qnil));
+    report_file_error ("Input file status", file);
 
   if (!NILP (preserve_extended_attributes))
     {
@@ -2077,53 +2088,42 @@ entries (depending on how Emacs was built).  */)
        {
          conlength = fgetfilecon (ifd, &con);
          if (conlength == -1)
-           report_file_error ("Doing fgetfilecon", Fcons (file, Qnil));
+           report_file_error ("Doing fgetfilecon", file);
        }
 #endif
     }
 
   if (out_st.st_mode != 0
       && st.st_dev == out_st.st_dev && st.st_ino == out_st.st_ino)
-    {
-      errno = 0;
-      report_file_error ("Input and output files are the same",
-                        Fcons (file, Fcons (newname, Qnil)));
-    }
+    report_file_errno ("Input and output files are the same",
+                      list2 (file, newname), 0);
 
   /* We can copy only regular files.  */
   if (!S_ISREG (st.st_mode))
-    {
-      /* Get a better looking error message. */
-      errno = S_ISDIR (st.st_mode) ? EISDIR : EINVAL;
-      report_file_error ("Non-regular file", Fcons (file, Qnil));
-    }
+    report_file_errno ("Non-regular file", file,
+                      S_ISDIR (st.st_mode) ? EISDIR : EINVAL);
 
-#ifdef MSDOS
-  /* System's default file type was set to binary by _fmode in emacs.c.  */
-  ofd = emacs_open (SDATA (encoded_newname),
-                   O_WRONLY | O_TRUNC | O_CREAT
-                   | (NILP (ok_if_already_exists) ? O_EXCL : 0),
-                   S_IREAD | S_IWRITE);
-#else  /* not MSDOS */
   {
-    mode_t new_mask = !NILP (preserve_uid_gid) ? 0600 : 0666;
-    new_mask &= st.st_mode;
+#ifndef MSDOS
+    int new_mask = st.st_mode & (!NILP (preserve_uid_gid) ? 0600 : 0666);
+#else
+    int new_mask = S_IREAD | S_IWRITE;
+#endif
     ofd = emacs_open (SSDATA (encoded_newname),
                      (O_WRONLY | O_TRUNC | O_CREAT
                       | (NILP (ok_if_already_exists) ? O_EXCL : 0)),
                      new_mask);
   }
-#endif /* not MSDOS */
   if (ofd < 0)
-    report_file_error ("Opening output file", Fcons (newname, Qnil));
+    report_file_error ("Opening output file", newname);
 
-  record_unwind_protect (close_file_unwind, make_number (ofd));
+  record_unwind_protect_int (close_file_unwind, ofd);
 
   immediate_quit = 1;
   QUIT;
   while ((n = emacs_read (ifd, buf, sizeof buf)) > 0)
-    if (emacs_write (ofd, buf, n) != n)
-      report_file_error ("I/O error", Fcons (newname, Qnil));
+    if (emacs_write_sig (ofd, buf, n) != n)
+      report_file_error ("Write error", newname);
   immediate_quit = 0;
 
 #ifndef MSDOS
@@ -2151,8 +2151,8 @@ entries (depending on how Emacs was built).  */)
                         st.st_mode & mode_mask)
            : fchmod (ofd, st.st_mode & mode_mask))
       {
-      case -2: report_file_error ("Copying permissions from", list1 (file));
-      case -1: report_file_error ("Copying permissions to", list1 (newname));
+      case -2: report_file_error ("Copying permissions from", file);
+      case -1: report_file_error ("Copying permissions to", newname);
       }
   }
 #endif /* not MSDOS */
@@ -2164,7 +2164,7 @@ entries (depending on how Emacs was built).  */)
       bool fail = fsetfilecon (ofd, con) != 0;
       /* See http://debbugs.gnu.org/11245 for ENOTSUP.  */
       if (fail && errno != ENOTSUP)
-       report_file_error ("Doing fsetfilecon", Fcons (newname, Qnil));
+       report_file_error ("Doing fsetfilecon", newname);
 
       freecon (con);
     }
@@ -2180,7 +2180,7 @@ entries (depending on how Emacs was built).  */)
     }
 
   if (emacs_close (ofd) < 0)
-    report_file_error ("I/O error", Fcons (newname, Qnil));
+    report_file_error ("Write error", newname);
 
   emacs_close (ifd);
 
@@ -2226,7 +2226,7 @@ DEFUN ("make-directory-internal", Fmake_directory_internal,
 #else
   if (mkdir (dir, 0777 & ~auto_saving_dir_umask) != 0)
 #endif
-    report_file_error ("Creating directory", list1 (directory));
+    report_file_error ("Creating directory", directory);
 
   return Qnil;
 }
@@ -2245,7 +2245,7 @@ DEFUN ("delete-directory-internal", Fdelete_directory_internal,
   dir = SSDATA (encoded_dir);
 
   if (rmdir (dir) != 0)
-    report_file_error ("Removing directory", list1 (directory));
+    report_file_error ("Removing directory", directory);
 
   return Qnil;
 }
@@ -2288,7 +2288,7 @@ With a prefix argument, TRASH is nil.  */)
   encoded_file = ENCODE_FILE (filename);
 
   if (unlink (SSDATA (encoded_file)) < 0)
-    report_file_error ("Removing old name", list1 (filename));
+    report_file_error ("Removing old name", filename);
   return Qnil;
 }
 
@@ -2609,7 +2609,11 @@ Use `file-symlink-p' to test for such links.  */)
      call the corresponding file handler.  */
   handler = Ffind_file_name_handler (absname, Qfile_exists_p);
   if (!NILP (handler))
-    return call2 (handler, Qfile_exists_p, absname);
+    {
+      Lisp_Object result = call2 (handler, Qfile_exists_p, absname);
+      errno = 0;
+      return result;
+    }
 
   absname = ENCODE_FILE (absname);
 
@@ -2706,7 +2710,6 @@ If there is no error, returns nil.  */)
   (Lisp_Object filename, Lisp_Object string)
 {
   Lisp_Object handler, encoded_filename, absname;
-  int fd;
 
   CHECK_STRING (filename);
   absname = Fexpand_file_name (filename, Qnil);
@@ -2721,10 +2724,8 @@ If there is no error, returns nil.  */)
 
   encoded_filename = ENCODE_FILE (absname);
 
-  fd = emacs_open (SSDATA (encoded_filename), O_RDONLY, 0);
-  if (fd < 0)
-    report_file_error (SSDATA (string), Fcons (filename, Qnil));
-  emacs_close (fd);
+  if (faccessat (AT_FDCWD, SSDATA (encoded_filename), R_OK, AT_EACCESS) != 0)
+    report_file_error (SSDATA (string), filename);
 
   return Qnil;
 }
@@ -2833,7 +2834,11 @@ searchable directory.  */)
      call the corresponding file handler.  */
   handler = Ffind_file_name_handler (absname, Qfile_accessible_directory_p);
   if (!NILP (handler))
-    return call2 (handler, Qfile_accessible_directory_p, absname);
+    {
+      Lisp_Object r = call2 (handler, Qfile_accessible_directory_p, absname);
+      errno = 0;
+      return r;
+    }
 
   absname = ENCODE_FILE (absname);
   return file_accessible_directory_p (SSDATA (absname)) ? Qt : Qnil;
@@ -3055,14 +3060,14 @@ or if Emacs was not compiled with SELinux support.  */)
                  != 0);
           /* See http://debbugs.gnu.org/11245 for ENOTSUP.  */
          if (fail && errno != ENOTSUP)
-           report_file_error ("Doing lsetfilecon", Fcons (absname, Qnil));
+           report_file_error ("Doing lsetfilecon", absname);
 
          context_free (parsed_con);
          freecon (con);
          return fail ? Qnil : Qt;
        }
       else
-       report_file_error ("Doing lgetfilecon", Fcons (absname, Qnil));
+       report_file_error ("Doing lgetfilecon", absname);
     }
 #endif
 
@@ -3152,7 +3157,7 @@ support.  */)
       acl = acl_from_text (SSDATA (acl_string));
       if (acl == NULL)
        {
-         report_file_error ("Converting ACL", Fcons (absname, Qnil));
+         report_file_error ("Converting ACL", absname);
          return Qnil;
        }
 
@@ -3162,7 +3167,7 @@ support.  */)
                            acl)
              != 0);
       if (fail && acl_errno_valid (errno))
-       report_file_error ("Setting ACL", Fcons (absname, Qnil));
+       report_file_error ("Setting ACL", absname);
 
       acl_free (acl);
       return fail ? Qnil : Qt;
@@ -3222,7 +3227,7 @@ symbolic notation, like the `chmod' command from GNU Coreutils.  */)
   encoded_absname = ENCODE_FILE (absname);
 
   if (chmod (SSDATA (encoded_absname), XINT (mode) & 07777) < 0)
-    report_file_error ("Doing chmod", Fcons (absname, Qnil));
+    report_file_error ("Doing chmod", absname);
 
   return Qnil;
 }
@@ -3288,7 +3293,7 @@ Use the current time if TIMESTAMP is nil.  TIMESTAMP is in the format of
         if (file_directory_p (SSDATA (encoded_absname)))
           return Qnil;
 #endif
-        report_file_error ("Setting file times", Fcons (absname, Qnil));
+        report_file_error ("Setting file times", absname);
       }
   }
 
@@ -3345,7 +3350,7 @@ otherwise, if FILE2 does not exist, the answer is t.  */)
   if (stat (SSDATA (absname2), &st2) < 0)
     return Qt;
 
-  return (EMACS_TIME_GT (get_stat_mtime (&st1), get_stat_mtime (&st2))
+  return (EMACS_TIME_LT (get_stat_mtime (&st2), get_stat_mtime (&st1))
          ? Qt : Qnil);
 }
 \f
@@ -3370,7 +3375,7 @@ verify (READ_BUF_SIZE <= INT_MAX);
        o remove all text properties.
        o set back the buffer multibyteness.  */
 
-static Lisp_Object
+static void
 decide_coding_unwind (Lisp_Object unwind_data)
 {
   Lisp_Object multibyte, undo_list, buffer;
@@ -3389,8 +3394,6 @@ decide_coding_unwind (Lisp_Object unwind_data)
   /* Now we are safe to change the buffer's multibyteness directly.  */
   bset_enable_multibyte_characters (current_buffer, multibyte);
   bset_undo_list (current_buffer, undo_list);
-
-  return Qnil;
 }
 
 /* Read from a non-regular file.  STATE is a Lisp_Save_Value
@@ -3554,7 +3557,7 @@ by calling `format-decode', which see.  */)
     {
       save_errno = errno;
       if (NILP (visit))
-       report_file_error ("Opening input file", Fcons (orig_filename, Qnil));
+       report_file_error ("Opening input file", orig_filename);
       mtime = time_error_value (save_errno);
       st.st_size = -1;
       if (!NILP (Vcoding_system_for_read))
@@ -3566,10 +3569,10 @@ by calling `format-decode', which see.  */)
   if (!NILP (replace))
     record_unwind_protect (restore_point_unwind, Fpoint_marker ());
 
-  record_unwind_protect (close_file_unwind, make_number (fd));
+  record_unwind_protect_int (close_file_unwind, fd);
 
   if (fstat (fd, &st) != 0)
-    report_file_error ("Input file status", Fcons (orig_filename, Qnil));
+    report_file_error ("Input file status", orig_filename);
   mtime = get_stat_mtime (&st);
 
   /* This code will need to be changed in order to work on named
@@ -3683,15 +3686,14 @@ by calling `format-decode', which see.  */)
                      int ntail;
                      if (lseek (fd, - (1024 * 3), SEEK_END) < 0)
                        report_file_error ("Setting file position",
-                                          Fcons (orig_filename, Qnil));
+                                          orig_filename);
                      ntail = emacs_read (fd, read_buf + nread, 1024 * 3);
                      nread = ntail < 0 ? ntail : nread + ntail;
                    }
                }
 
              if (nread < 0)
-               error ("IO error reading %s: %s",
-                      SDATA (orig_filename), emacs_strerror (errno));
+               report_file_error ("Read error", orig_filename);
              else if (nread > 0)
                {
                  struct buffer *prev = current_buffer;
@@ -3727,8 +3729,7 @@ by calling `format-decode', which see.  */)
 
                  /* Rewind the file for the actual read done later.  */
                  if (lseek (fd, 0, SEEK_SET) < 0)
-                   report_file_error ("Setting file position",
-                                      Fcons (orig_filename, Qnil));
+                   report_file_error ("Setting file position", orig_filename);
                }
            }
 
@@ -3794,8 +3795,7 @@ by calling `format-decode', which see.  */)
       if (beg_offset != 0)
        {
          if (lseek (fd, beg_offset, SEEK_SET) < 0)
-           report_file_error ("Setting file position",
-                              Fcons (orig_filename, Qnil));
+           report_file_error ("Setting file position", orig_filename);
        }
 
       immediate_quit = 1;
@@ -3808,8 +3808,7 @@ by calling `format-decode', which see.  */)
 
          nread = emacs_read (fd, read_buf, sizeof read_buf);
          if (nread < 0)
-           error ("IO error reading %s: %s",
-                  SSDATA (orig_filename), emacs_strerror (errno));
+           report_file_error ("Read error", orig_filename);
          else if (nread == 0)
            break;
 
@@ -3867,16 +3866,14 @@ by calling `format-decode', which see.  */)
          /* How much can we scan in the next step?  */
          trial = min (curpos, sizeof read_buf);
          if (lseek (fd, curpos - trial, SEEK_SET) < 0)
-           report_file_error ("Setting file position",
-                              Fcons (orig_filename, Qnil));
+           report_file_error ("Setting file position", orig_filename);
 
          total_read = nread = 0;
          while (total_read < trial)
            {
              nread = emacs_read (fd, read_buf + total_read, trial - total_read);
              if (nread < 0)
-               error ("IO error reading %s: %s",
-                      SDATA (orig_filename), emacs_strerror (errno));
+               report_file_error ("Read error", orig_filename);
              else if (nread == 0)
                break;
              total_read += nread;
@@ -3988,8 +3985,7 @@ by calling `format-decode', which see.  */)
         CONVERSION_BUFFER.  */
 
       if (lseek (fd, beg_offset, SEEK_SET) < 0)
-       report_file_error ("Setting file position",
-                          Fcons (orig_filename, Qnil));
+       report_file_error ("Setting file position", orig_filename);
 
       inserted = 0;            /* Bytes put into CONVERSION_BUFFER so far.  */
       unprocessed = 0;         /* Bytes not processed in previous loop.  */
@@ -4027,8 +4023,7 @@ by calling `format-decode', which see.  */)
       deferred_remove_unwind_protect = 1;
 
       if (this < 0)
-       error ("IO error reading %s: %s",
-              SDATA (orig_filename), emacs_strerror (errno));
+       report_file_error ("Read error", orig_filename);
 
       if (unprocessed > 0)
        {
@@ -4169,8 +4164,7 @@ by calling `format-decode', which see.  */)
   if (beg_offset != 0 || !NILP (replace))
     {
       if (lseek (fd, beg_offset, SEEK_SET) < 0)
-       report_file_error ("Setting file position",
-                          Fcons (orig_filename, Qnil));
+       report_file_error ("Setting file position", orig_filename);
     }
 
   /* In the following loop, HOW_MUCH contains the total bytes read so
@@ -4275,8 +4269,7 @@ by calling `format-decode', which see.  */)
   specpdl_ptr--;
 
   if (how_much < 0)
-    error ("IO error reading %s: %s",
-          SDATA (orig_filename), emacs_strerror (errno));
+    report_file_error ("Read error", orig_filename);
 
   /* Make the text read part of the buffer.  */
   GAP_SIZE -= inserted;
@@ -4575,8 +4568,7 @@ by calling `format-decode', which see.  */)
       && EMACS_NSECS (current_buffer->modtime) == NONEXISTENT_MODTIME_NSECS)
     {
       /* If visiting nonexistent file, return nil.  */
-      errno = save_errno;
-      report_file_error ("Opening input file", Fcons (orig_filename, Qnil));
+      report_file_errno ("Opening input file", orig_filename, save_errno);
     }
 
   if (read_quit)
@@ -4591,11 +4583,10 @@ by calling `format-decode', which see.  */)
 \f
 static Lisp_Object build_annotations (Lisp_Object, Lisp_Object);
 
-static Lisp_Object
+static void
 build_annotations_unwind (Lisp_Object arg)
 {
   Vwrite_region_annotation_buffers = arg;
-  return Qnil;
 }
 
 /* Decide the coding-system to encode the data with.  */
@@ -4632,7 +4623,7 @@ This function is for internal use only.  It may prompt the user.  */ )
          && !NILP (Ffboundp (Vselect_safe_coding_system_function)))
        /* Confirm that VAL can surely encode the current region.  */
        val = call5 (Vselect_safe_coding_system_function,
-                    start, end, Fcons (Qt, Fcons (val, Qnil)),
+                    start, end, list2 (Qt, val),
                     Qnil, filename);
     }
   else
@@ -4835,7 +4826,7 @@ This calls `write-region-annotate-functions' at the start, and
 
   record_unwind_protect (build_annotations_unwind,
                         Vwrite_region_annotation_buffers);
-  Vwrite_region_annotation_buffers = Fcons (Fcurrent_buffer (), Qnil);
+  Vwrite_region_annotation_buffers = list1 (Fcurrent_buffer ());
   count1 = SPECPDL_INDEX ();
 
   given_buffer = current_buffer;
@@ -4897,29 +4888,27 @@ This calls `write-region-annotate-functions' at the start, and
 
   if (desc < 0)
     {
+      int open_errno = errno;
 #ifdef CLASH_DETECTION
-      save_errno = errno;
       if (!auto_saving) unlock_file (lockname);
-      errno = save_errno;
 #endif /* CLASH_DETECTION */
       UNGCPRO;
-      report_file_error ("Opening output file", Fcons (filename, Qnil));
+      report_file_errno ("Opening output file", filename, open_errno);
     }
 
-  record_unwind_protect (close_file_unwind, make_number (desc));
+  record_unwind_protect_int (close_file_unwind, desc);
 
   if (NUMBERP (append))
     {
       off_t ret = lseek (desc, offset, SEEK_SET);
       if (ret < 0)
        {
+         int lseek_errno = errno;
 #ifdef CLASH_DETECTION
-         save_errno = errno;
          if (!auto_saving) unlock_file (lockname);
-         errno = save_errno;
 #endif /* CLASH_DETECTION */
          UNGCPRO;
-         report_file_error ("Lseek error", Fcons (filename, Qnil));
+         report_file_errno ("Lseek error", filename, lseek_errno);
        }
     }
 
@@ -5072,8 +5061,7 @@ This calls `write-region-annotate-functions' at the start, and
     }
 
   if (! ok)
-    error ("IO error writing %s: %s", SDATA (filename),
-          emacs_strerror (save_errno));
+    report_file_errno ("Write error", filename, save_errno);
 
   if (visiting)
     {
@@ -5317,12 +5305,10 @@ e_write (int desc, Lisp_Object string, ptrdiff_t start, ptrdiff_t end,
 
       if (coding->produced > 0)
        {
-         coding->produced
-           -= emacs_write (desc,
-                           STRINGP (coding->dst_object)
-                           ? SSDATA (coding->dst_object)
-                           : (char *) BYTE_POS_ADDR (coding->dst_pos_byte),
-                           coding->produced);
+         char *buf = (STRINGP (coding->dst_object)
+                      ? SSDATA (coding->dst_object)
+                      : (char *) BYTE_POS_ADDR (coding->dst_pos_byte));
+         coding->produced -= emacs_write_sig (desc, buf, coding->produced);
 
          if (coding->produced)
            return 0;
@@ -5377,36 +5363,19 @@ See Info node `(elisp)Modification Time' for more details.  */)
   return Qnil;
 }
 
-DEFUN ("clear-visited-file-modtime", Fclear_visited_file_modtime,
-       Sclear_visited_file_modtime, 0, 0, 0,
-       doc: /* Clear out records of last mod time of visited file.
-Next attempt to save will certainly not complain of a discrepancy.  */)
-  (void)
-{
-  current_buffer->modtime = make_emacs_time (0, UNKNOWN_MODTIME_NSECS);
-  current_buffer->modtime_size = -1;
-  return Qnil;
-}
-
 DEFUN ("visited-file-modtime", Fvisited_file_modtime,
        Svisited_file_modtime, 0, 0, 0,
        doc: /* Return the current buffer's recorded visited file modification time.
 The value is a list of the form (HIGH LOW USEC PSEC), like the time values that
 `file-attributes' returns.  If the current buffer has no recorded file
 modification time, this function returns 0.  If the visited file
-doesn't exist, HIGH will be -1.
+doesn't exist, return -1.
 See Info node `(elisp)Modification Time' for more details.  */)
   (void)
 {
-  if (EMACS_NSECS (current_buffer->modtime) < 0)
-    {
-      if (EMACS_NSECS (current_buffer->modtime) == NONEXISTENT_MODTIME_NSECS)
-       {
-         /* make_lisp_time won't work here if time_t is unsigned.  */
-         return list4i (-1, 65535, 0, 0);
-       }
-      return make_number (0);
-    }
+  int ns = EMACS_NSECS (current_buffer->modtime);
+  if (ns < 0)
+    return make_number (UNKNOWN_MODTIME_NSECS - ns);
   return make_lisp_time (current_buffer->modtime);
 }
 
@@ -5417,12 +5386,22 @@ Useful if the buffer was not read from the file normally
 or if the file itself has been changed for some known benign reason.
 An argument specifies the modification time value to use
 \(instead of that of the visited file), in the form of a list
-\(HIGH LOW USEC PSEC) as returned by `current-time'.  */)
-  (Lisp_Object time_list)
+\(HIGH LOW USEC PSEC) or an integer flag as returned by
+`visited-file-modtime'.  */)
+  (Lisp_Object time_flag)
 {
-  if (!NILP (time_list))
+  if (!NILP (time_flag))
     {
-      current_buffer->modtime = lisp_time_argument (time_list);
+      EMACS_TIME mtime;
+      if (INTEGERP (time_flag))
+       {
+         CHECK_RANGED_INTEGER (time_flag, -1, 0);
+         mtime = make_emacs_time (0, UNKNOWN_MODTIME_NSECS - XINT (time_flag));
+       }
+      else
+       mtime = lisp_time_argument (time_flag);
+
+      current_buffer->modtime = mtime;
       current_buffer->modtime_size = -1;
     }
   else
@@ -5508,11 +5487,18 @@ auto_save_1 (void)
                   Qnil, Qnil);
 }
 
-static Lisp_Object
-do_auto_save_unwind (Lisp_Object arg)  /* used as unwind-protect function */
+struct auto_save_unwind
+{
+  FILE *stream;
+  bool auto_raise;
+};
 
+static void
+do_auto_save_unwind (void *arg)
 {
-  FILE *stream = XSAVE_POINTER (arg, 0);
+  struct auto_save_unwind *p = arg;
+  FILE *stream = p->stream;
+  minibuffer_auto_raise = p->auto_raise;
   auto_saving = 0;
   if (stream != NULL)
     {
@@ -5520,15 +5506,6 @@ do_auto_save_unwind (Lisp_Object arg)  /* used as unwind-protect function */
       fclose (stream);
       unblock_input ();
     }
-  return Qnil;
-}
-
-static Lisp_Object
-do_auto_save_unwind_1 (Lisp_Object value)  /* used as unwind-protect function */
-
-{
-  minibuffer_auto_raise = XINT (value);
-  return Qnil;
 }
 
 static Lisp_Object
@@ -5571,6 +5548,7 @@ A non-nil CURRENT-ONLY argument means save only current buffer.  */)
   ptrdiff_t count = SPECPDL_INDEX ();
   bool orig_minibuffer_auto_raise = minibuffer_auto_raise;
   bool old_message_p = 0;
+  struct auto_save_unwind auto_save_unwind;
   struct gcpro gcpro1, gcpro2;
 
   if (max_specpdl_size < specpdl_size + 40)
@@ -5582,7 +5560,7 @@ A non-nil CURRENT-ONLY argument means save only current buffer.  */)
   if (NILP (no_message))
     {
       old_message_p = push_message ();
-      record_unwind_protect (pop_message_unwind, Qnil);
+      record_unwind_protect_void (pop_message_unwind);
     }
 
   /* Ordinarily don't quit within this function,
@@ -5621,10 +5599,9 @@ A non-nil CURRENT-ONLY argument means save only current buffer.  */)
       stream = emacs_fopen (SSDATA (listfile), "w");
     }
 
-  record_unwind_protect (do_auto_save_unwind,
-                        make_save_pointer (stream));
-  record_unwind_protect (do_auto_save_unwind_1,
-                        make_number (minibuffer_auto_raise));
+  auto_save_unwind.stream = stream;
+  auto_save_unwind.auto_raise = minibuffer_auto_raise;
+  record_unwind_protect_ptr (do_auto_save_unwind, &auto_save_unwind);
   minibuffer_auto_raise = 0;
   auto_saving = 1;
   auto_save_error_occurred = 0;
@@ -6123,7 +6100,6 @@ This includes interactive calls to `delete-file' and
   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);