]> code.delx.au - gnu-emacs/blobdiff - src/fileio.c
Delete the code that was trying to define BSD "right"
[gnu-emacs] / src / fileio.c
index 9b09b0e32c06c1573deaa8722b93bbccff2d3423..9caa355dac5316b8338ee124720c80da5b8960bc 100644 (file)
@@ -92,6 +92,8 @@ extern char *strerror ();
 #include "lisp.h"
 #include "intervals.h"
 #include "buffer.h"
+#include "charset.h"
+#include "coding.h"
 #include "window.h"
 
 #ifdef WINDOWSNT
@@ -205,7 +207,7 @@ extern int minibuf_level;
 static Lisp_Object Vinhibit_file_name_handlers;
 static Lisp_Object Vinhibit_file_name_operation;
 
-Lisp_Object Qfile_error, Qfile_already_exists;
+Lisp_Object Qfile_error, Qfile_already_exists, Qfile_date_error;
 
 Lisp_Object Qfile_name_history;
 
@@ -1249,7 +1251,7 @@ See also the function `substitute-in-file-name'.")
       /* Get rid of any slash at the end of newdir, unless newdir is
        just // (an incomplete UNC name). */
       length = strlen (newdir);
-      if (IS_DIRECTORY_SEP (newdir[length - 1])
+      if (length > 0 && IS_DIRECTORY_SEP (newdir[length - 1])
 #ifdef WINDOWSNT
          && !(length == 2 && IS_DIRECTORY_SEP (newdir[0]))
 #endif
@@ -1377,7 +1379,8 @@ See also the function `substitute-in-file-name'.")
        {
          while (o != target && (--o) && !IS_DIRECTORY_SEP (*o))
            ;
-         if (o == target && IS_ANY_SEP (*o))
+         /* Keep initial / only if this is the whole name.  */
+         if (o == target && IS_ANY_SEP (*o) && p[3] == 0)
            ++o;
          p += 3;
        }
@@ -1919,7 +1922,7 @@ duplicates what `expand-file-name' does.")
         || IS_DIRECTORY_SEP (p[0])
 #endif /* not (APOLLO || WINDOWSNT) */
         )
-       && p != nm && IS_DIRECTORY_SEP (p[-1]))
+       && p != xnm && IS_DIRECTORY_SEP (p[-1]))
       xnm = p;
 #ifdef DOS_NT
     else if (IS_DRIVE (p[0]) && p[1] == ':'
@@ -2127,7 +2130,9 @@ A prefix arg makes KEEP-TIME non-nil.")
          EMACS_SET_SECS_USECS (atime, st.st_atime, 0);
          EMACS_SET_SECS_USECS (mtime, st.st_mtime, 0);
          if (set_file_times (XSTRING (newname)->data, atime, mtime))
-           report_file_error ("I/O error", Fcons (newname, Qnil));
+           Fsignal (Qfile_date_error,
+                    Fcons (build_string ("Cannot set file date"),
+                           Fcons (newname, Qnil)));
        }
 #ifndef MSDOS
       chmod (XSTRING (newname)->data, st.st_mode & 07777);
@@ -2987,6 +2992,10 @@ otherwise, if FILE2 does not exist, the answer is t.")
 Lisp_Object Qfind_buffer_file_type;
 #endif /* DOS_NT */
 
+#ifndef READ_BUF_SIZE
+#define READ_BUF_SIZE (64 << 10)
+#endif
+
 DEFUN ("insert-file-contents", Finsert_file_contents, Sinsert_file_contents,
   1, 5, 0,
   "Insert contents of file FILENAME after point.\n\
@@ -2994,15 +3003,21 @@ 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.\n\n\
+before the error is signaled.\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\
+\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.")
+and (2) it puts less data in the undo list.\n\
+When REPLACE is non-nil, the value is the number of characters actually read,\n\
+which is often less than the number of characters to be read.\n\
+This does code conversion according to the value of\n\
+  `coding-system-for-read' or `coding-system-alist', and sets the variable\n\
+  `last-coding-system-used' to the coding system actually used.")
   (filename, visit, beg, end, replace)
      Lisp_Object filename, visit, beg, end, replace;
 {
@@ -3010,12 +3025,17 @@ and (2) it puts less data in the undo list.")
   register int fd;
   register int inserted = 0;
   register int how_much;
+  register int unprocessed;
   int count = specpdl_ptr - specpdl;
   struct gcpro gcpro1, gcpro2, gcpro3;
   Lisp_Object handler, val, insval;
   Lisp_Object p;
   int total;
   int not_regular = 0;
+  char read_buf[READ_BUF_SIZE];
+  struct coding_system coding;
+  unsigned char buffer[1 << 14];
+  int replace_handled = 0;
 
   if (current_buffer->base_buffer && ! NILP (visit))
     error ("Cannot do file visiting in an indirect buffer");
@@ -3041,6 +3061,23 @@ and (2) it puts less data in the undo list.")
       goto handled;
     }
 
+  /* Decide the coding-system of the file.  */
+  {
+    Lisp_Object val = Vcoding_system_for_read;
+    if (NILP (current_buffer->enable_multibyte_characters))
+      val = Qnil;
+    else if (NILP (val))
+      {
+       Lisp_Object args[6], coding_systems;
+
+       args[0] = Qinsert_file_contents, args[1] = filename, args[2] = visit,
+         args[3] = beg, args[4] = end, args[5] = replace;
+       coding_systems = Ffind_coding_system (6, args);
+       val = CONSP (coding_systems) ? XCONS (coding_systems)->car : Qnil;
+      }
+    setup_coding_system (Fcheck_coding_system (val), &coding);
+  }
+
   fd = -1;
 
 #ifndef APOLLO
@@ -3065,13 +3102,15 @@ and (2) it puts less data in the undo list.")
      least signal an error.  */
   if (!S_ISREG (st.st_mode))
     {
-      if (NILP (visit))
+      not_regular = 1;
+
+      if (! NILP (visit))
+       goto notfound;
+
+      if (! NILP (replace) || ! NILP (beg) || ! NILP (end))
        Fsignal (Qfile_error,
                 Fcons (build_string ("not a regular file"),
                        Fcons (filename, Qnil)));
-
-      not_regular = 1;
-      goto notfound;
     }
 #endif
 
@@ -3086,7 +3125,7 @@ and (2) it puts less data in the undo list.")
   record_unwind_protect (close_file_unwind, make_number (fd));
 
   /* Supposedly happens on VMS.  */
-  if (st.st_size < 0)
+  if (! not_regular && st.st_size < 0)
     error ("File size is negative");
 
   if (!NILP (beg) || !NILP (end))
@@ -3102,30 +3141,42 @@ and (2) it puts less data in the undo list.")
     CHECK_NUMBER (end, 0);
   else
     {
-      XSETINT (end, st.st_size);
-      if (XINT (end) != st.st_size)
-       error ("maximum buffer size exceeded");
+      if (! not_regular)
+       {
+         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 DOS_NT
-  /* On MSDOS, replace mode doesn't really work, except for binary files,
-     and it's not worth supporting just for them.  */
-  if (!NILP (replace))
+     that preserves markers pointing to the unchanged parts.
+
+     Here we implement this feature in an optimized way
+     for the case where code conversion is NOT needed.
+     The following if-statement handles the case of conversion
+     in a less optimal way.
+
+     If the code conversion is "automatic" then we try using this
+     method and hope for the best.
+     But if we discover the need for conversion, we give up on this method
+     and let the following if-statement handle the replace job.  */
+  if (!NILP (replace)
+      && (! CODING_REQUIRE_CONVERSION (&coding)
+         || (coding.type == coding_type_automatic
+             && ! CODING_REQUIRE_TEXT_CONVERSION (&coding))
+         || (coding.eol_type == CODING_EOL_AUTOMATIC
+             && ! CODING_REQUIRE_EOL_CONVERSION (&coding))))
     {
-      replace = Qnil;
-      del_range_1 (BEGV, ZV, 0);
-    }
-#else /* not DOS_NT */
-  if (!NILP (replace))
-    {
-      unsigned char buffer[1 << 14];
       int same_at_start = BEGV;
       int same_at_end = ZV;
       int overlap;
+      /* There is still a possibility we will find the need to do code
+        conversion.  If that happens, we set this variable to 1 to
+        give up on handling REPLACE in the optimized way.  */
+      int giveup_match_end = 0;
 
       if (XINT (beg) != 0)
        {
@@ -3148,9 +3199,30 @@ and (2) it puts less data in the undo list.")
                   XSTRING (filename)->data, strerror (errno));
          else if (nread == 0)
            break;
+
+         if (coding.type == coding_type_automatic)
+           detect_coding (&coding, buffer, nread);
+         if (CODING_REQUIRE_TEXT_CONVERSION (&coding))
+           /* We found that the file should be decoded somehow.
+               Let's give up here.  */
+           {
+             giveup_match_end = 1;
+             break;
+           }
+
+         if (coding.eol_type == CODING_EOL_AUTOMATIC)
+           detect_eol (&coding, buffer, nread);
+         if (CODING_REQUIRE_EOL_CONVERSION (&coding))
+           /* We found that the format of eol should be decoded.
+               Let's give up here.  */
+           {
+             giveup_match_end = 1;
+             break;
+           }
+
          bufpos = 0;
          while (bufpos < nread && same_at_start < ZV
-                && FETCH_CHAR (same_at_start) == buffer[bufpos])
+                && FETCH_BYTE (same_at_start) == buffer[bufpos])
            same_at_start++, bufpos++;
          /* If we found a discrepancy, stop the scan.
             Otherwise loop around and scan the next bufferful.  */
@@ -3171,8 +3243,9 @@ and (2) it puts less data in the undo list.")
       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)
+        match the text at the end of the buffer.  But, if we have
+        already found that decoding is necessary, don't waste time.  */
+      while (!giveup_match_end)
        {
          int total_read, nread, bufpos, curpos, trial;
 
@@ -3202,44 +3275,224 @@ and (2) it puts less data in the undo list.")
          /* 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])
+                && FETCH_BYTE (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 bufferful.  */
          if (bufpos != 0)
-           break;
-         /* If display current starts at beginning of line,
+           {
+             /* If this discrepancy is because of code conversion,
+                we cannot use this method; giveup and try the other.  */
+             if (same_at_end > same_at_start
+                 && FETCH_BYTE (same_at_end - 1) >= 0200
+                 && ! NILP (current_buffer->enable_multibyte_characters))
+               giveup_match_end = 1;
+             break;
+           }
+       }
+      immediate_quit = 0;
+
+      if (! giveup_match_end)
+       {
+         /* We win!  We can handle REPLACE the optimized way.  */
+
+         /* 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.  */
+         XSETFASTINT (beg, XINT (beg) + (same_at_start - BEGV));
+         XSETFASTINT (end, XINT (end) - (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);
+
+         /* If display currently starts at beginning of line,
             keep it that way.  */
          if (XBUFFER (XWINDOW (selected_window)->buffer) == current_buffer)
            XWINDOW (selected_window)->start_at_line_beg = Fbolp ();
+
+         replace_handled = 1;
        }
-      immediate_quit = 0;
+    }
+
+  /* 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.
+
+     Here we implement this feature for the case where code conversion
+     is needed, in a simple way that needs a lot of memory.
+     The preceding if-statement handles the case of no conversion
+     in a more optimized way.  */
+  if (!NILP (replace) && ! replace_handled)
+    {
+      int same_at_start = BEGV;
+      int same_at_end = ZV;
+      int overlap;
+      int bufpos;
+      /* Make sure that the gap is large enough.  */
+      int bufsize = 2 * st.st_size;
+      unsigned char *conversion_buffer = (unsigned char *) malloc (bufsize);
+
+      /* First read the whole file, performing code conversion into
+        CONVERSION_BUFFER.  */
+
+      if (lseek (fd, XINT (beg), 0) < 0)
+       {
+         free (conversion_buffer);
+         report_file_error ("Setting file position",
+                            Fcons (filename, Qnil));
+       }
+
+      total = st.st_size;      /* Total bytes in the file.  */
+      how_much = 0;            /* Bytes read from file so far.  */
+      inserted = 0;            /* Bytes put into CONVERSION_BUFFER so far.  */
+      unprocessed = 0;         /* Bytes not processed in previous loop.  */
+
+      while (how_much < total)
+       {
+         /* try is reserved in some compilers (Microsoft C) */
+         int trytry = min (total - how_much, READ_BUF_SIZE - unprocessed);
+         char *destination = read_buf + unprocessed;
+         int this;
+
+         /* Allow quitting out of the actual I/O.  */
+         immediate_quit = 1;
+         QUIT;
+         this = read (fd, destination, trytry);
+         immediate_quit = 0;
+
+         if (this < 0 || this + unprocessed == 0)
+           {
+             how_much = this;
+             break;
+           }
+
+         how_much += this;
+
+         if (CODING_REQUIRE_CONVERSION (&coding))
+           {
+             int require, produced, consumed;
+
+             this += unprocessed;
+
+             /* If we are using more space than estimated,
+                make CONVERSION_BUFFER bigger.  */
+             require = decoding_buffer_size (&coding, this);
+             if (inserted + require + 2 * (total - how_much) > bufsize)
+               {
+                 bufsize = inserted + require + 2 * (total - how_much);
+                 conversion_buffer = (unsigned char *) realloc (conversion_buffer, bufsize);
+               }
+
+             /* Convert this batch with results in CONVERSION_BUFFER.  */
+             if (how_much >= total)  /* This is the last block.  */
+               coding.last_block = 1;
+             produced = decode_coding (&coding, read_buf,
+                                       conversion_buffer + inserted,
+                                       this, bufsize - inserted,
+                                       &consumed);
+
+             /* Save for next iteration whatever we didn't convert.  */
+             unprocessed = this - consumed;
+             bcopy (read_buf + consumed, read_buf, unprocessed);
+             this = produced;
+           }
+
+         inserted += this;
+       }
+
+      /* At this point, INSERTED is how many characters
+        are present in CONVERSION_BUFFER.
+        HOW_MUCH should equal TOTAL,
+        or should be <= 0 if we couldn't read the file.  */
+
+      if (how_much < 0)
+       {
+         free (conversion_buffer);
+
+         if (how_much == -1)
+           error ("IO error reading %s: %s",
+                  XSTRING (filename)->data, strerror (errno));
+         else if (how_much == -2)
+           error ("maximum buffer size exceeded");
+       }
+
+      /* Compare the beginning of the converted file
+        with the buffer text.  */
+
+      bufpos = 0;
+      while (bufpos < inserted && same_at_start < same_at_end
+            && FETCH_BYTE (same_at_start) == conversion_buffer[bufpos])
+       same_at_start++, bufpos++;
+
+      /* If the file matches the buffer completely,
+        there's no need to replace anything.  */
+
+      if (bufpos == inserted)
+       {
+         free (conversion_buffer);
+         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;
+       }
+
+      /* Scan this bufferful from the end, comparing with
+        the Emacs buffer.  */
+      bufpos = inserted;
+
+      /* 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_BYTE (same_at_end - 1) == conversion_buffer[bufpos - 1])
+       same_at_end--, bufpos--;
 
       /* Don't try to reuse the same piece of text twice.  */
-      overlap = same_at_start - BEGV - (same_at_end + st.st_size - ZV);
+      overlap = same_at_start - BEGV - (same_at_end + inserted - ZV);
       if (overlap > 0)
        same_at_end += overlap;
 
-      /* Arrange to read only the nonmatching middle part of the file.  */
-      XSETFASTINT (beg, XINT (beg) + (same_at_start - BEGV));
-      XSETFASTINT (end, XINT (end) - (ZV - same_at_end));
+      /* If display currently starts at beginning of line,
+        keep it that way.  */
+      if (XBUFFER (XWINDOW (selected_window)->buffer) == current_buffer)
+       XWINDOW (selected_window)->start_at_line_beg = Fbolp ();
 
+      /* Replace the chars that we need to replace,
+        and update INSERTED to equal the number of bytes
+        we are taking from the file.  */
+      inserted -= (Z - same_at_end) + (same_at_start - BEG);
+      move_gap (same_at_start);
       del_range_1 (same_at_start, same_at_end, 0);
-      /* Insert from the file at the proper position.  */
       SET_PT (same_at_start);
+      insert_1 (conversion_buffer + same_at_start - BEG, inserted, 0, 0);
+
+      free (conversion_buffer);
+      close (fd);
+      specpdl_ptr--;
+
+      goto handled;
     }
-#endif /* not DOS_NT */
 
-  total = XINT (end) - XINT (beg);
+  if (! not_regular)
+    {
+      register Lisp_Object temp;
 
-  {
-    register Lisp_Object temp;
+      total = XINT (end) - XINT (beg);
 
-    /* Make sure point-max won't overflow after this insertion.  */
-    XSETINT (temp, total);
-    if (total != XINT (temp))
-      error ("maximum buffer size exceeded");
-  }
+      /* Make sure point-max won't overflow after this insertion.  */
+      XSETINT (temp, total);
+      if (total != XINT (temp))
+       error ("Maximum buffer size exceeded");
+    }
+  else
+    /* For a special file, all we can do is guess.  */
+    total = READ_BUF_SIZE;
 
   if (NILP (visit) && total > 0)
     prepare_to_modify_buffer (PT, PT);
@@ -3254,32 +3507,100 @@ and (2) it puts less data in the undo list.")
        report_file_error ("Setting file position", Fcons (filename, Qnil));
     }
 
+  /* In the following loop, HOW_MUCH contains the total bytes read so
+     far.  Before exiting the loop, it is set to -1 if I/O error
+     occurs, set to -2 if the maximum buffer size is exceeded.  */
   how_much = 0;
-  while (inserted < total)
+  /* Total bytes inserted.  */
+  inserted = 0;
+  /* Bytes not processed in the previous loop because short gap size.  */
+  unprocessed = 0;
+  while (how_much < total)
     {
        /* try is reserved in some compilers (Microsoft C) */
-      int trytry = min (total - inserted, 64 << 10);
+      int trytry = min (total - how_much, READ_BUF_SIZE - unprocessed);
+      char *destination = (CODING_REQUIRE_CONVERSION (&coding)
+                          ? read_buf + unprocessed
+                          : (char *) (POS_ADDR (PT + inserted - 1) + 1));
       int this;
 
       /* Allow quitting out of the actual I/O.  */
       immediate_quit = 1;
       QUIT;
-      this = read (fd, &FETCH_CHAR (PT + inserted - 1) + 1, trytry);
+      this = read (fd, destination, trytry);
       immediate_quit = 0;
 
-      if (this <= 0)
+      if (this < 0 || this + unprocessed == 0)
        {
          how_much = this;
          break;
        }
 
+      /* For a regular file, where TOTAL is the real size,
+        count HOW_MUCH to compare with it.
+        For a special file, where TOTAL is just a buffer size,
+        so don't bother counting in HOW_MUCH.
+        (INSERTED is where we count the number of characters inserted.)  */
+      if (! not_regular)
+       how_much += this;
+
+      if (CODING_REQUIRE_CONVERSION (&coding))
+       {
+         int require, produced, consumed;
+
+         this += unprocessed;
+         /* Make sure that the gap is large enough.  */
+         require = decoding_buffer_size (&coding, this);
+         if (GAP_SIZE < require)
+           make_gap (require - GAP_SIZE);
+
+         if (! not_regular)
+           {
+             if (how_much >= total)  /* This is the last block.  */
+               coding.last_block = 1;
+           }
+         else
+           {
+             /* If we encounter EOF, say it is the last block.  (The
+                data this will apply to is the UNPROCESSED characters
+                carried over from the last batch.)  */
+             if (this == 0)
+               coding.last_block = 1;
+           }
+
+         produced = decode_coding (&coding, read_buf,
+                                   POS_ADDR (PT + inserted - 1) + 1,
+                                   this, GAP_SIZE, &consumed);
+         if (produced > 0) 
+           {
+             Lisp_Object temp;
+
+             XSET (temp, Lisp_Int, Z + produced);
+             if (Z + produced != XINT (temp))
+               {
+                 how_much = -2;
+                 break;
+               }
+           }
+         unprocessed = this - consumed;
+         bcopy (read_buf + consumed, read_buf, unprocessed);
+         this = produced;
+       }
+
       GPT += this;
       GAP_SIZE -= this;
       ZV += this;
       Z += this;
+      if (GAP_SIZE > 0)
+       /* Put an anchor to ensure multi-byte form ends at gap.  */
+       *GPT_ADDR = 0;
       inserted += this;
     }
 
+  /* We don't have to consider file type of MSDOS because all files
+     are read as binary and end-of-line format has already been
+     decoded appropriately.  */
+#if 0
 #ifdef DOS_NT
   /* 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
@@ -3290,7 +3611,7 @@ and (2) it puts less data in the undo list.")
     if (NILP (current_buffer->buffer_file_type))
       {
        int reduced_size
-         = inserted - crlf_to_lf (inserted, &FETCH_CHAR (PT - 1) + 1);
+         = inserted - crlf_to_lf (inserted, POS_ADDR (PT - 1) + 1);
        ZV -= reduced_size;
        Z -= reduced_size;
        GPT -= reduced_size;
@@ -3299,6 +3620,7 @@ and (2) it puts less data in the undo list.")
       }
   }
 #endif /* DOS_NT */
+#endif /* 0 */
 
   if (inserted > 0)
     {
@@ -3314,9 +3636,11 @@ and (2) it puts less data in the undo list.")
   /* Discard the unwind protect for closing the file.  */
   specpdl_ptr--;
 
-  if (how_much < 0)
+  if (how_much == -1)
     error ("IO error reading %s: %s",
           XSTRING (filename)->data, strerror (errno));
+  else if (how_much == -2)
+    error ("maximum buffer size exceeded");
 
  notfound:
  handled:
@@ -3365,12 +3689,19 @@ and (2) it puts less data in the undo list.")
       inserted = XFASTINT (insval);
     }
 
-  if (inserted > 0 && NILP (visit) && total > 0)
+  /* Call after-change hooks for the inserted text, aside from the case
+     of normal visiting (not with REPLACE), which is done in a new buffer
+     "before" the buffer is changed.  */
+  if (inserted > 0 && total > 0
+      && (NILP (visit) || !NILP (replace)))
     signal_after_change (PT, 0, inserted);
 
   if (inserted > 0)
     {
       p = Vafter_insert_file_functions;
+      if (!NILP (coding.post_read_conversion))
+       p = Fcons (coding.post_read_conversion, p);
+
       while (!NILP (p))
        {
          insval = call1 (Fcar (p), make_number (inserted));
@@ -3395,7 +3726,11 @@ and (2) it puts less data in the undo list.")
 static Lisp_Object build_annotations ();
 
 /* If build_annotations switched buffers, switch back to BUF.
-   Kill the temporary buffer that was selected in the meantime.  */
+   Kill the temporary buffer that was selected in the meantime.
+
+   Since this kill only the last temporary buffer, some buffers remain
+   not killed if build_annotations switched buffers more than once.
+   -- K.Handa */
 
 static Lisp_Object
 build_annotations_unwind (buf)
@@ -3429,7 +3764,10 @@ If VISIT is neither t nor nil nor a string,\n\
 The optional sixth arg LOCKNAME, if non-nil, specifies the name to\n\
   use for locking and unlocking, overriding FILENAME and VISIT.\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.")
+to the file, instead of any buffer contents, and END is ignored.\n\
+This does code conversion according to the value of\n\
+ `coding-system-for-write' or `coding-system-alist', and sets the variable\n\
+ `last-coding-system-used' to the coding system actually used.")
   (start, end, filename, append, visit, lockname)
      Lisp_Object start, end, filename, append, visit, lockname;
 {
@@ -3454,6 +3792,7 @@ to the file, instead of any buffer contents, and END is ignored.")
   int buffer_file_type
     = NILP (current_buffer->buffer_file_type) ? O_TEXT : O_BINARY;
 #endif /* DOS_NT */
+  struct coding_system coding;
 
   if (current_buffer->base_buffer && ! NILP (visit))
     error ("Cannot do file visiting in an indirect buffer");
@@ -3461,7 +3800,7 @@ to the file, instead of any buffer contents, and END is ignored.")
   if (!NILP (start) && !STRINGP (start))
     validate_region (&start, &end);
 
-  GCPRO3 (filename, visit, lockname);
+  GCPRO4 (start, filename, visit, lockname);
   filename = Fexpand_file_name (filename, Qnil);
   if (STRINGP (visit))
     visit_file = Fexpand_file_name (visit, Qnil);
@@ -3502,6 +3841,38 @@ to the file, instead of any buffer contents, and END is ignored.")
       return val;
     }
 
+  /* Decide the coding-system to be encoded to.  */
+  {
+    Lisp_Object val;
+
+    if (auto_saving || NILP (current_buffer->enable_multibyte_characters))
+      val = Qnil;
+    else if (!NILP (Vcoding_system_for_write))
+      val = Vcoding_system_for_write;
+    else if (!NILP (Flocal_variable_if_set_p (Qbuffer_file_coding_system,
+                                             Qnil)))
+      val = Fsymbol_value (Qbuffer_file_coding_system);
+    else
+      {
+       Lisp_Object args[7], coding_systems;
+
+       args[0] = Qwrite_region, args[1] = start, args[2] = end,
+         args[3] = filename, args[4] = append, args[5] = visit,
+         args[6] = lockname;
+       coding_systems = Ffind_coding_system (7, args);
+       val = (CONSP (coding_systems)
+              ? XCONS (coding_systems)->cdr
+              : Fsymbol_value (Qbuffer_file_coding_system));
+      }
+    setup_coding_system (Fcheck_coding_system (val), &coding); 
+    if (!STRINGP (start) && !NILP (current_buffer->selective_display))
+      coding.selective = 1;
+#ifdef DOS_NT
+    if (!NILP (current_buffer->buffer_file_type))
+      coding.eol_type = CODING_EOL_LF;
+#endif /* DOS_NT */
+  }
+
   /* Special kludge to simplify auto-saving.  */
   if (NILP (start))
     {
@@ -3513,7 +3884,7 @@ to the file, instead of any buffer contents, and END is ignored.")
   count1 = specpdl_ptr - specpdl;
 
   given_buffer = current_buffer;
-  annotations = build_annotations (start, end);
+  annotations = build_annotations (start, end, coding.pre_write_conversion);
   if (current_buffer != given_buffer)
     {
       start = BEGV;
@@ -3646,7 +4017,7 @@ to the file, instead of any buffer contents, and END is ignored.")
   if (STRINGP (start))
     {
       failure = 0 > a_write (desc, XSTRING (start)->data,
-                            XSTRING (start)->size, 0, &annotations);
+                            XSTRING (start)->size, 0, &annotations, &coding);
       save_errno = errno;
     }
   else if (XINT (start) != XINT (end))
@@ -3656,8 +4027,9 @@ to the file, instead of any buffer contents, and END is ignored.")
        {
          register int end1 = XINT (end);
          tem = XINT (start);
-         failure = 0 > a_write (desc, &FETCH_CHAR (tem),
-                                min (GPT, end1) - tem, tem, &annotations);
+         failure = 0 > a_write (desc, POS_ADDR (tem),
+                                min (GPT, end1) - tem, tem, &annotations,
+                                &coding);
          nwritten += min (GPT, end1) - tem;
          save_errno = errno;
        }
@@ -3666,8 +4038,8 @@ to the file, instead of any buffer contents, and END is ignored.")
        {
          tem = XINT (start);
          tem = max (tem, GPT);
-         failure = 0 > a_write (desc, &FETCH_CHAR (tem), XINT (end) - tem,
-                                tem, &annotations);
+         failure = 0 > a_write (desc, POS_ADDR (tem), XINT (end) - tem,
+                                tem, &annotations, &coding);
          nwritten += XINT (end) - tem;
          save_errno = errno;
        }
@@ -3675,7 +4047,15 @@ to the file, instead of any buffer contents, and END is ignored.")
   else
     {
       /* If file was empty, still need to write the annotations */
-      failure = 0 > a_write (desc, "", 0, XINT (start), &annotations);
+      failure = 0 > a_write (desc, "", 0, XINT (start), &annotations, &coding);
+      save_errno = errno;
+    }
+
+  if (coding.require_flushing)
+    {
+      /* We have to flush out a data. */
+      coding.last_block = 1;
+      failure = 0 > e_write (desc, "", 0, &coding);
       save_errno = errno;
     }
 
@@ -3784,8 +4164,8 @@ DEFUN ("car-less-than-car", Fcar_less_than_car, Scar_less_than_car, 2, 2, 0,
    as save-excursion would do.  */
 
 static Lisp_Object
-build_annotations (start, end)
-     Lisp_Object start, end;
+build_annotations (start, end, pre_write_conversion)
+     Lisp_Object start, end, pre_write_conversion;
 {
   Lisp_Object annotations;
   Lisp_Object p, res;
@@ -3839,6 +4219,24 @@ build_annotations (start, end)
       annotations = merge (annotations, res, Qcar_less_than_car);
       p = Fcdr (p);
     }
+
+  /* At last, do the same for the function PRE_WRITE_CONVERSION
+     implied by the current coding-system.  */
+  if (!NILP (pre_write_conversion))
+    {
+      struct buffer *given_buffer = current_buffer;
+      Vwrite_region_annotations_so_far = annotations;
+      res = call2 (pre_write_conversion, start, end);
+      if (current_buffer != given_buffer)
+       {
+         start = BEGV;
+         end = ZV;
+         annotations = Qnil;
+       }
+      Flength (res);
+      annotations = merge (annotations, res, Qcar_less_than_car);
+    }
+
   UNGCPRO;
   return annotations;
 }
@@ -3853,12 +4251,13 @@ build_annotations (start, end)
    The return value is negative in case of system call failure.  */
 
 int
-a_write (desc, addr, len, pos, annot)
+a_write (desc, addr, len, pos, annot, coding)
      int desc;
      register char *addr;
      register int len;
      int pos;
      Lisp_Object *annot;
+     struct coding_system *coding;
 {
   Lisp_Object tem;
   int nextpos;
@@ -3870,10 +4269,10 @@ a_write (desc, addr, len, pos, annot)
       if (INTEGERP (tem) && XINT (tem) >= pos && XFASTINT (tem) <= lastpos)
        nextpos = XFASTINT (tem);
       else
-       return e_write (desc, addr, lastpos - pos);
+       return e_write (desc, addr, lastpos - pos, coding);
       if (nextpos > pos)
        {
-         if (0 > e_write (desc, addr, nextpos - pos))
+         if (0 > e_write (desc, addr, nextpos - pos, coding))
            return -1;
          addr += nextpos - pos;
          pos = nextpos;
@@ -3881,43 +4280,50 @@ a_write (desc, addr, len, pos, annot)
       tem = Fcdr (Fcar (*annot));
       if (STRINGP (tem))
        {
-         if (0 > e_write (desc, XSTRING (tem)->data, XSTRING (tem)->size))
+         if (0 > e_write (desc, XSTRING (tem)->data, XSTRING (tem)->size,
+                          coding))
            return -1;
        }
       *annot = Fcdr (*annot);
     }
 }
 
+#ifndef WRITE_BUF_SIZE
+#define WRITE_BUF_SIZE (16 * 1024)
+#endif
+
 int
-e_write (desc, addr, len)
+e_write (desc, addr, len, coding)
      int desc;
      register char *addr;
      register int len;
+     struct coding_system *coding;
 {
-  char buf[16 * 1024];
-  register char *p, *end;
+  char buf[WRITE_BUF_SIZE];
+  int produced, consumed;
 
-  if (!EQ (current_buffer->selective_display, Qt))
-    return write (desc, addr, len) - len;
-  else
+  /* We used to have a code for handling selective display here.  But,
+     now it is handled within encode_coding.  */
+  while (1)
     {
-      p = buf;
-      end = p + sizeof buf;
-      while (len--)
+      produced = encode_coding (coding, addr, buf, len, WRITE_BUF_SIZE,
+                               &consumed);
+      len -= consumed, addr += consumed;
+      if (produced == 0 && len > 0)
        {
-         if (p == end)
-           {
-             if (write (desc, buf, sizeof buf) != sizeof buf)
-               return -1;
-             p = buf;
-           }
-         *p = *addr++;
-         if (*p++ == '\015')
-           p[-1] = '\n';
+         /* There was a carry over because of invalid codes in the source.
+            We just write out them as is.  */
+         bcopy (addr, buf, len);
+         produced = len;
+         len = 0;
        }
-      if (p != buf)
-       if (write (desc, buf, p - buf) != p - buf)
-         return -1;
+      if (produced > 0)
+       {
+         produced -= write (desc, buf, produced);
+         if (produced) return -1;
+       }
+      if (len <= 0)
+       break;
     }
   return 0;
 }
@@ -4448,7 +4854,7 @@ DIR defaults to current buffer's directory default.")
   GCPRO2 (insdef, default_filename);
   val = Fcompleting_read (prompt, intern ("read-file-name-internal"),
                          dir, mustmatch, insdef1,
-                         Qfile_name_history);
+                         Qfile_name_history, default_filename);
 
 #ifdef VMS
   unbind_to (count, Qnil);
@@ -4604,6 +5010,8 @@ syms_of_fileio ()
   staticpro (&Qfile_error);
   Qfile_already_exists = intern ("file-already-exists");
   staticpro (&Qfile_already_exists);
+  Qfile_date_error = intern ("file-date-error");
+  staticpro (&Qfile_date_error);
 
 #ifdef DOS_NT
   Qfind_buffer_file_type = intern ("find-buffer-file-type");
@@ -4636,6 +5044,12 @@ same format as a regular save would use.");
   Fput (Qfile_already_exists, Qerror_message,
        build_string ("File already exists"));
 
+  Fput (Qfile_date_error, Qerror_conditions,
+       Fcons (Qfile_date_error,
+              Fcons (Qfile_error, Fcons (Qerror, Qnil))));
+  Fput (Qfile_date_error, Qerror_message,
+       build_string ("Cannot set file date"));
+
   DEFVAR_BOOL ("insert-default-directory", &insert_default_directory,
     "*Non-nil means when reading a filename start with default dir in minibuffer.");
   insert_default_directory = 1;