(Fexpand_file_name) [WINDOWSNT]: Be careful not to
[bpt/emacs.git] / src / fileio.c
index 97691a4..aef7a01 100644 (file)
@@ -1,5 +1,5 @@
 /* File IO for GNU Emacs.
-   Copyright (C) 1985,86,87,88,93,94,95,96,1997 Free Software Foundation, Inc.
+   Copyright (C) 1985,86,87,88,93,94,95,96,97,1998 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -50,15 +50,6 @@ Boston, MA 02111-1307, USA.  */
 #include <pwd.h>
 #endif
 
-#ifdef MSDOS
-#include "msdos.h"
-#include <sys/param.h>
-#if __DJGPP__ >= 2
-#include <fcntl.h>
-#include <string.h>
-#endif
-#endif
-
 #include <ctype.h>
 
 #ifdef VMS
@@ -104,6 +95,15 @@ extern char *strerror ();
 #include <fcntl.h>
 #endif /* not WINDOWSNT */
 
+#ifdef MSDOS
+#include "msdos.h"
+#include <sys/param.h>
+#if __DJGPP__ >= 2
+#include <fcntl.h>
+#include <string.h>
+#endif
+#endif
+
 #ifdef DOS_NT
 #define CORRECT_DIR_SEPS(s) \
   do { if ('/' == DIRECTORY_SEP) dostounix_filename (s); \
@@ -152,14 +152,6 @@ extern char *strerror ();
 #define min(a, b) ((a) < (b) ? (a) : (b))
 #define max(a, b) ((a) > (b) ? (a) : (b))
 
-/* Encode the file name NAME using the specified coding system
-   for file names, if any.  */
-#define ENCODE_FILE(name)                                      \
-  (! NILP (Vfile_name_coding_system)                           \
-   && XFASTINT (Vfile_name_coding_system) != 0                 \
-   ? Fencode_coding_string (name, Vfile_name_coding_system, Qt)        \
-   : name)
-
 /* Nonzero during writing of auto-save files */
 int auto_saving;
 
@@ -170,6 +162,10 @@ int auto_save_mode_bits;
 /* Coding system for file names, or nil if none.  */
 Lisp_Object Vfile_name_coding_system;
 
+/* Coding system for file names used only when
+   Vfile_name_coding_system is nil.  */
+Lisp_Object Vdefault_file_name_coding_system;
+
 /* Alist of elements (REGEXP . HANDLER) for file names
    whose I/O is done with a special handler.  */
 Lisp_Object Vfile_name_handler_alist;
@@ -379,7 +375,7 @@ on VMS, perhaps instead a string ending in `:', `]' or `>'.")
 #ifdef DOS_NT
   beg = strcpy (alloca (strlen (beg) + 1), beg);
 #endif
-  p = beg + XSTRING (filename)->size;
+  p = beg + STRING_BYTES (XSTRING (filename));
 
   while (p != beg && !IS_DIRECTORY_SEP (p[-1])
 #ifdef VMS
@@ -436,7 +432,7 @@ or the entire name if it contains no slash.")
     return call2 (handler, Qfile_name_nondirectory, filename);
 
   beg = XSTRING (filename)->data;
-  end = p = beg + XSTRING (filename)->size;
+  end = p = beg + STRING_BYTES (XSTRING (filename));
 
   while (p != beg && !IS_DIRECTORY_SEP (p[-1])
 #ifdef VMS
@@ -486,6 +482,14 @@ file_name_as_directory (out, in)
 
   strcpy (out, in);
 
+  if (size < 0)
+    {
+      out[0] = '.';
+      out[1] = '/';
+      out[2] = 0;
+      return out;
+    }
+
 #ifdef VMS
   /* Is it already a directory string? */
   if (in[size] == ':' || in[size] == ']' || in[size] == '>')
@@ -582,7 +586,7 @@ On VMS, converts \"[X]FOO.DIR\" to \"[X.FOO]\", etc.")
   if (!NILP (handler))
     return call2 (handler, Qfile_name_as_directory, file);
 
-  buf = (char *) alloca (XSTRING (file)->size + 10);
+  buf = (char *) alloca (STRING_BYTES (XSTRING (file)) + 10);
   return build_string (file_name_as_directory (buf, XSTRING (file)->data));
 }
 \f
@@ -596,6 +600,7 @@ On VMS, converts \"[X]FOO.DIR\" to \"[X.FOO]\", etc.")
  * Value is nonzero if the string output is different from the input.
  */
 
+int
 directory_file_name (src, dst)
      char *src, *dst;
 {
@@ -777,37 +782,126 @@ it returns a file name such as \"[X]Y.DIR.1\".")
   /* 20 extra chars is insufficient for VMS, since we might perform a
      logical name translation. an equivalence string can be up to 255
      chars long, so grab that much extra space...  - sss */
-  buf = (char *) alloca (XSTRING (directory)->size + 20 + 255);
+  buf = (char *) alloca (STRING_BYTES (XSTRING (directory)) + 20 + 255);
 #else
-  buf = (char *) alloca (XSTRING (directory)->size + 20);
+  buf = (char *) alloca (STRING_BYTES (XSTRING (directory)) + 20);
 #endif
   directory_file_name (XSTRING (directory)->data, buf);
   return build_string (buf);
 }
 
+static char make_temp_name_tbl[64] =
+{
+  'A','B','C','D','E','F','G','H',
+  'I','J','K','L','M','N','O','P',
+  'Q','R','S','T','U','V','W','X',
+  'Y','Z','a','b','c','d','e','f',
+  'g','h','i','j','k','l','m','n',
+  'o','p','q','r','s','t','u','v',
+  'w','x','y','z','0','1','2','3',
+  '4','5','6','7','8','9','-','_'
+};
+static unsigned make_temp_name_count, make_temp_name_count_initialized_p;
+
 DEFUN ("make-temp-name", Fmake_temp_name, Smake_temp_name, 1, 1, 0,
   "Generate temporary file name (string) starting with PREFIX (a string).\n\
 The Emacs process number forms part of the result,\n\
 so there is no danger of generating a name being used by another process.\n\
+\n\
 In addition, this function makes an attempt to choose a name\n\
-which has no existing file.")
+which has no existing file.  To make this work,\n\
+PREFIX should be an absolute file name.")
   (prefix)
      Lisp_Object prefix;
 {
   Lisp_Object val;
-#ifdef MSDOS
-  /* Don't use too many characters of the restricted 8+3 DOS
-     filename space.  */
-  val = concat2 (prefix, build_string ("a.XXX"));
+  int len;
+  int pid;
+  unsigned char *p, *data;
+  char pidbuf[20];
+  int pidlen;
+
+  CHECK_STRING (prefix, 0);
+
+  /* VAL is created by adding 6 characters to PREFIX.  The first
+     three are the PID of this process, in base 64, and the second
+     three are incremented if the file already exists.  This ensures
+     262144 unique file names per PID per PREFIX.  */
+
+  pid = (int) getpid ();
+
+#ifdef HAVE_LONG_FILE_NAMES
+  sprintf (pidbuf, "%d", pid);
+  pidlen = strlen (pidbuf);
 #else
-  val = concat2 (prefix, build_string ("XXXXXX"));
-#endif
-  mktemp (XSTRING (val)->data);
-#ifdef DOS_NT
-  CORRECT_DIR_SEPS (XSTRING (val)->data);
+  pidbuf[0] = make_temp_name_tbl[pid & 63], pid >>= 6;
+  pidbuf[1] = make_temp_name_tbl[pid & 63], pid >>= 6;
+  pidbuf[2] = make_temp_name_tbl[pid & 63], pid >>= 6;
+  pidlen = 3;
 #endif
-  return val;
+
+  len = XSTRING (prefix)->size;
+  val = make_uninit_string (len + 3 + pidlen);
+  data = XSTRING (val)->data;
+  bcopy(XSTRING (prefix)->data, data, len);
+  p = data + len;
+
+  bcopy (pidbuf, p, pidlen);
+  p += pidlen;
+
+  /* Here we try to minimize useless stat'ing when this function is
+     invoked many times successively with the same PREFIX.  We achieve
+     this by initializing count to a random value, and incrementing it
+     afterwards.
+
+     We don't want make-temp-name to be called while dumping,
+     because then make_temp_name_count_initialized_p would get set
+     and then make_temp_name_count would not be set when Emacs starts.  */
+
+  if (!make_temp_name_count_initialized_p)
+    {
+      make_temp_name_count = (unsigned) time (NULL);
+      make_temp_name_count_initialized_p = 1;
+    }
+
+  while (1)
+    {
+      struct stat ignored;
+      unsigned num = make_temp_name_count;
+
+      p[0] = make_temp_name_tbl[num & 63], num >>= 6;
+      p[1] = make_temp_name_tbl[num & 63], num >>= 6;
+      p[2] = make_temp_name_tbl[num & 63], num >>= 6;
+
+      /* Poor man's congruential RN generator.  Replace with
+         ++make_temp_name_count for debugging.  */
+      make_temp_name_count += 25229;
+      make_temp_name_count %= 225307;
+
+      if (stat (data, &ignored) < 0)
+       {
+         /* We want to return only if errno is ENOENT.  */
+         if (errno == ENOENT)
+           return val;
+         else
+           /* The error here is dubious, but there is little else we
+              can do.  The alternatives are to return nil, which is
+              as bad as (and in many cases worse than) throwing the
+              error, or to ignore the error, which will likely result
+              in looping through 225307 stat's, which is not only
+              dog-slow, but also useless since it will fallback to
+              the errow below, anyway.  */
+           report_file_error ("Cannot create temporary name for prefix `%s'",
+                              Fcons (prefix, Qnil));
+         /* not reached */
+       }
+    }
+
+  error ("Cannot create temporary name for prefix `%s'",
+        XSTRING (prefix)->data);
+  return Qnil;
 }
+
 \f
 DEFUN ("expand-file-name", Fexpand_file_name, Sexpand_file_name, 1, 2, 0,
   "Convert filename NAME to absolute, and canonicalize it.\n\
@@ -841,6 +935,7 @@ See also the function `substitute-in-file-name'.")
 #ifdef DOS_NT
   int drive = 0;
   int collapse_newdir = 1;
+  int is_escaped = 0;
 #endif /* DOS_NT */
   int length;
   Lisp_Object handler;
@@ -884,7 +979,7 @@ See also the function `substitute-in-file-name'.")
         is needed at all) without requiring it to be expanded now.  */
 #ifdef DOS_NT
       /* Detect MSDOS file names with drive specifiers.  */
-      && ! (IS_DRIVE (o[0]) && (IS_DEVICE_SEP (o[1]) && IS_DIRECTORY_SEP (o[2])))
+      && ! (IS_DRIVE (o[0]) && IS_DEVICE_SEP (o[1]) && IS_DIRECTORY_SEP (o[2]))
 #ifdef WINDOWSNT
       /* Detect Windows file names in UNC format.  */
       && ! (IS_DIRECTORY_SEP (o[0]) && IS_DIRECTORY_SEP (o[1]))
@@ -918,33 +1013,21 @@ See also the function `substitute-in-file-name'.")
      a local copy to modify, even if there ends up being no change. */
   nm = strcpy (alloca (strlen (nm) + 1), nm);
 
+  /* Note if special escape prefix is present, but remove for now.  */
+  if (nm[0] == '/' && nm[1] == ':')
+    {
+      is_escaped = 1;
+      nm += 2;
+    }
+
   /* Find and remove drive specifier if present; this makes nm absolute
-     even if the rest of the name appears to be relative. */
-  {
-    unsigned char *colon = rindex (nm, ':');
-
-    if (colon)
-      /* Only recognize colon as part of drive specifier if there is a
-        single alphabetic character preceeding the colon (and if the
-        character before the drive letter, if present, is a directory
-        separator); this is to support the remote system syntax used by
-        ange-ftp, and the "po:username" syntax for POP mailboxes. */
-    look_again:
-      if (nm == colon)
-       nm++;
-      else if (IS_DRIVE (colon[-1])
-              && (colon == nm + 1 || IS_DIRECTORY_SEP (colon[-2])))
-       {
-         drive = colon[-1];
-         nm = colon + 1;
-       }
-      else
-       {
-         while (--colon >= nm)
-           if (colon[0] == ':')
-             goto look_again;
-       }
-  }
+     even if the rest of the name appears to be relative.  Only look for
+     drive specifier at the beginning.  */
+  if (IS_DRIVE (nm[0]) && IS_DEVICE_SEP (nm[1]))
+    {
+      drive = nm[0];
+      nm += 2;
+    }
 
 #ifdef WINDOWSNT
   /* If we see "c://somedir", we want to strip the first slash after the
@@ -969,10 +1052,10 @@ See also the function `substitute-in-file-name'.")
   if (
       IS_DIRECTORY_SEP (nm[0])
 #ifdef MSDOS
-      && drive
+      && drive && !is_escaped
 #endif
 #ifdef WINDOWSNT
-      && (drive || IS_DIRECTORY_SEP (nm[1]))
+      && (drive || IS_DIRECTORY_SEP (nm[1])) && !is_escaped
 #endif
 #ifdef VMS
       || index (nm, ':')
@@ -1218,6 +1301,14 @@ See also the function `substitute-in-file-name'.")
       && !newdir)
     {
       newdir = XSTRING (default_directory)->data;
+#ifdef DOS_NT
+      /* Note if special escape prefix is present, but remove for now.  */
+      if (newdir[0] == '/' && newdir[1] == ':')
+       {
+         is_escaped = 1;
+         newdir += 2;
+       }
+#endif
     }
 
 #ifdef DOS_NT
@@ -1293,9 +1384,9 @@ See also the function `substitute-in-file-name'.")
   if (newdir)
     {
       /* Get rid of any slash at the end of newdir, unless newdir is
-        just // (an incomplete UNC name).  */
+        just / or // (an incomplete UNC name).  */
       length = strlen (newdir);
-      if (length > 0 && IS_DIRECTORY_SEP (newdir[length - 1])
+      if (length > 1 && IS_DIRECTORY_SEP (newdir[length - 1])
 #ifdef WINDOWSNT
          && !(length == 2 && IS_DIRECTORY_SEP (newdir[0]))
 #endif
@@ -1314,10 +1405,11 @@ See also the function `substitute-in-file-name'.")
   /* Now concatenate the directory and name to new space in the stack frame */
   tlen += strlen (nm) + 1;
 #ifdef DOS_NT
-  /* Add reserved space for drive name.  (The Microsoft x86 compiler
+  /* Reserve space for drive specifier and escape prefix, since either
+     or both may need to be inserted.  (The Microsoft x86 compiler
      produces incorrect code if the following two lines are combined.)  */
-  target = (unsigned char *) alloca (tlen + 2);
-  target += 2;
+  target = (unsigned char *) alloca (tlen + 4);
+  target += 4;
 #else  /* not DOS_NT */
   target = (unsigned char *) alloca (tlen);
 #endif /* not DOS_NT */
@@ -1327,7 +1419,18 @@ See also the function `substitute-in-file-name'.")
     {
 #ifndef VMS
       if (nm[0] == 0 || IS_DIRECTORY_SEP (nm[0]))
-       strcpy (target, newdir);
+       {
+#ifdef WINDOWSNT
+         /* If newdir is effectively "C:/", then the drive letter will have
+            been stripped and newdir will be "/".  Concatenating with an
+            absolute directory in nm produces "//", which will then be
+            incorrectly treated as a network share.  Ignore newdir in
+            this case (keeping the drive letter).  */
+         if (!(drive && nm[0] && IS_DIRECTORY_SEP (newdir[0]) 
+               && newdir[1] == '\0'))
+#endif
+           strcpy (target, newdir);
+       }
       else
 #endif
        file_name_as_directory (target, newdir);
@@ -1436,6 +1539,13 @@ See also the function `substitute-in-file-name'.")
       target[0] = DRIVE_LETTER (drive);
       target[1] = ':';
     }
+  /* Reinsert the escape prefix if required.  */
+  if (is_escaped)
+    {
+      target -= 2;
+      target[0] = '/';
+      target[1] = ':';
+    }
   CORRECT_DIR_SEPS (target);
 #endif /* DOS_NT */
 
@@ -1802,7 +1912,7 @@ duplicates what `expand-file-name' does.")
   CORRECT_DIR_SEPS (nm);
   substituted = (strcmp (nm, XSTRING (filename)->data) != 0);
 #endif
-  endp = nm + XSTRING (filename)->size;
+  endp = nm + STRING_BYTES (XSTRING (filename));
 
   /* If /~ or // appears, discard everything through first slash.  */
 
@@ -1895,7 +2005,7 @@ duplicates what `expand-file-name' does.")
 
   /* If substitution required, recopy the string and do it */
   /* Make space in stack frame for the new copy */
-  xnm = (unsigned char *) alloca (XSTRING (filename)->size + total + 1);
+  xnm = (unsigned char *) alloca (STRING_BYTES (XSTRING (filename)) + total + 1);
   x = xnm;
 
   /* Copy the rest of the name through, replacing $ constructs with values */
@@ -1945,14 +2055,13 @@ duplicates what `expand-file-name' does.")
               convert what we substitute into multibyte.  */
            unsigned char workbuf[4], *str;
            int len;
-           extern int nonascii_insert_offset;
 
            while (*o)
              {
                int c = *o++;
-               if (c >= 0200)
+               c = unibyte_char_to_multibyte (c);
+               if (! SINGLE_BYTE_CHAR_P (c))
                  {
-                   c += nonascii_insert_offset;
                    len = CHAR_STRING (c, workbuf, str);
                    bcopy (str, x, len);
                    x += len;
@@ -2015,7 +2124,7 @@ expand_and_dir_to_file (filename, defdir)
   absname = Fexpand_file_name (filename, defdir);
 #ifdef VMS
   {
-    register int c = XSTRING (absname)->data[XSTRING (absname)->size - 1];
+    register int c = XSTRING (absname)->data[STRING_BYTES (XSTRING (absname)) - 1];
     if (c == ':' || c == ']' || c == '>')
       absname = Fdirectory_file_name (absname);
   }
@@ -2023,8 +2132,8 @@ expand_and_dir_to_file (filename, defdir)
   /* Remove final slash, if any (unless this is the root dir).
      stat behaves differently depending!  */
   if (XSTRING (absname)->size > 1
-      && IS_DIRECTORY_SEP (XSTRING (absname)->data[XSTRING (absname)->size - 1])
-      && !IS_DEVICE_SEP (XSTRING (absname)->data[XSTRING (absname)->size-2]))
+      && IS_DIRECTORY_SEP (XSTRING (absname)->data[STRING_BYTES (XSTRING (absname)) - 1])
+      && !IS_DEVICE_SEP (XSTRING (absname)->data[STRING_BYTES (XSTRING (absname))-2]))
     /* We cannot take shortcuts; they might be wrong for magic file names.  */
     absname = Fdirectory_file_name (absname);
 #endif
@@ -2036,31 +2145,42 @@ expand_and_dir_to_file (filename, defdir)
    and bypass the error if the user says to go ahead.
    QUERYSTRING is a name for the action that is being considered
    to alter the file.
+
    *STATPTR is used to store the stat information if the file exists.
-   If the file does not exist, STATPTR->st_mode is set to 0.  */
+   If the file does not exist, STATPTR->st_mode is set to 0.
+   If STATPTR is null, we don't store into it.
+
+   If QUICK is nonzero, we ask for y or n, not yes or no.  */
 
 void
-barf_or_query_if_file_exists (absname, querystring, interactive, statptr)
+barf_or_query_if_file_exists (absname, querystring, interactive, statptr, quick)
      Lisp_Object absname;
      unsigned char *querystring;
      int interactive;
      struct stat *statptr;
+     int quick;
 {
-  register Lisp_Object tem;
+  register Lisp_Object tem, encoded_filename;
   struct stat statbuf;
   struct gcpro gcpro1;
 
+  encoded_filename = ENCODE_FILE (absname);
+
   /* stat is a good way to tell whether the file exists,
      regardless of what access permissions it has.  */
-  if (stat (XSTRING (absname)->data, &statbuf) >= 0)
+  if (stat (XSTRING (encoded_filename)->data, &statbuf) >= 0)
     {
       if (! interactive)
        Fsignal (Qfile_already_exists,
                 Fcons (build_string ("File already exists"),
                        Fcons (absname, Qnil)));
       GCPRO1 (absname);
-      tem = do_yes_or_no_p (format1 ("File %s already exists; %s anyway? ",
-                                    XSTRING (absname)->data, querystring));
+      tem = format1 ("File %s already exists; %s anyway? ",
+                    XSTRING (absname)->data, querystring);
+      if (quick)
+       tem = Fy_or_n_p (tem);
+      else
+       tem = do_yes_or_no_p (tem);
       UNGCPRO;
       if (NILP (tem))
        Fsignal (Qfile_already_exists,
@@ -2123,7 +2243,7 @@ A prefix arg makes KEEP-TIME non-nil.")
   if (NILP (ok_if_already_exists)
       || INTEGERP (ok_if_already_exists))
     barf_or_query_if_file_exists (encoded_newname, "copy to it",
-                                 INTEGERP (ok_if_already_exists), &out_st);
+                                 INTEGERP (ok_if_already_exists), &out_st, 0);
   else if (stat (XSTRING (encoded_newname)->data, &out_st) < 0)
     out_st.st_mode = 0;
 
@@ -2137,7 +2257,7 @@ A prefix arg makes KEEP-TIME non-nil.")
      copyable by us. */
   input_file_statable_p = (fstat (ifd, &st) >= 0);
 
-#if !defined (MSDOS) || __DJGPP__ > 1
+#if !defined (DOS_NT) || __DJGPP__ > 1
   if (out_st.st_mode != 0
       && st.st_dev == out_st.st_dev && st.st_ino == out_st.st_ino)
     {
@@ -2361,7 +2481,7 @@ This is what happens in interactive use with M-x.")
   if (NILP (ok_if_already_exists)
       || INTEGERP (ok_if_already_exists))
     barf_or_query_if_file_exists (encoded_newname, "rename to it",
-                                 INTEGERP (ok_if_already_exists), 0);
+                                 INTEGERP (ok_if_already_exists), 0, 0);
 #ifndef BSD4_1
   if (0 > rename (XSTRING (encoded_file)->data, XSTRING (encoded_newname)->data))
 #else
@@ -2436,7 +2556,7 @@ This is what happens in interactive use with M-x.")
   if (NILP (ok_if_already_exists)
       || INTEGERP (ok_if_already_exists))
     barf_or_query_if_file_exists (encoded_newname, "make it a new name",
-                                 INTEGERP (ok_if_already_exists), 0);
+                                 INTEGERP (ok_if_already_exists), 0, 0);
 
   unlink (XSTRING (newname)->data);
   if (0 > link (XSTRING (encoded_file)->data, XSTRING (encoded_newname)->data))
@@ -2503,7 +2623,7 @@ This happens for interactive use with M-x.")
   if (NILP (ok_if_already_exists)
       || INTEGERP (ok_if_already_exists))
     barf_or_query_if_file_exists (encoded_linkname, "make it a link",
-                                 INTEGERP (ok_if_already_exists), 0);
+                                 INTEGERP (ok_if_already_exists), 0, 0);
   if (0 > symlink (XSTRING (encoded_filename)->data,
                   XSTRING (encoded_linkname)->data))
     {
@@ -2873,7 +2993,8 @@ Otherwise returns nil.")
     }
   val = make_string (buf, valsize);
   xfree (buf);
-  return Fdecode_coding_string (val, Vfile_name_coding_system, Qt);
+  val = DECODE_FILE (val);
+  return val;
 #else /* not S_IFLNK */
   return Qnil;
 #endif /* not S_IFLNK */
@@ -3141,7 +3262,6 @@ This does code conversion according to the value of\n\
   struct stat st;
   register int fd;
   int inserted = 0;
-  int inserted_chars = 0;
   register int how_much;
   register int unprocessed;
   int count = specpdl_ptr - specpdl;
@@ -3150,7 +3270,7 @@ This does code conversion according to the value of\n\
   Lisp_Object p;
   int total;
   int not_regular = 0;
-  char read_buf[READ_BUF_SIZE];
+  unsigned char read_buf[READ_BUF_SIZE];
   struct coding_system coding;
   unsigned char buffer[1 << 14];
   int replace_handled = 0;
@@ -3259,27 +3379,39 @@ This does code conversion according to the value of\n\
 
   /* Decide the coding-system of the file.  */
   {
-    Lisp_Object val = Qnil;
+    Lisp_Object val;
+    val = Qnil;
 
     if (!NILP (Vcoding_system_for_read))
       val = Vcoding_system_for_read;
-    else if (NILP (current_buffer->enable_multibyte_characters))
-      val = Qemacs_mule;
-    else
+    else if (! NILP (replace))
+      /* In REPLACE mode, we can use the same coding system
+        that was used to visit the file.  */
+      val = current_buffer->buffer_file_coding_system;
+    else if (! not_regular)
       {
+       /* Don't try looking inside a file for a coding system specification
+          if it is not seekable.  */
        if (! NILP (Vset_auto_coding_function))
          {
            /* Find a coding system specified in the heading two lines
               or in the tailing several lines of the file.  We assume
               that the 1K-byte and 3K-byte for heading and tailing
               respectively are sufficient fot this purpose.  */
-           int how_many, nread;
+           int nread;
+           int beginning_of_end, end_of_beginning;
 
            if (st.st_size <= (1024 * 4))
-             nread = read (fd, read_buf, 1024 * 4);
+             {
+               nread = read (fd, read_buf, 1024 * 4);
+               end_of_beginning = nread;
+               beginning_of_end = 0;
+             }
            else
              {
                nread = read (fd, read_buf, 1024);
+               end_of_beginning = nread;
+               beginning_of_end = nread;
                if (nread >= 0)
                  {
                    if (lseek (fd, st.st_size - (1024 * 3), 0) < 0)
@@ -3288,17 +3420,83 @@ This does code conversion according to the value of\n\
                    nread += read (fd, read_buf + nread, 1024 * 3);
                  }
              }
-        
+
            if (nread < 0)
              error ("IO error reading %s: %s",
                     XSTRING (orig_filename)->data, strerror (errno));
            else if (nread > 0)
              {
+               int i;
+               int possible_spec = 0;
+               unsigned char *p, *p1;
                Lisp_Object tem;
-               /* Always make this a unibyte string
-                  because we have not yet decoded it.  */
-               tem = make_unibyte_string (read_buf, nread);
-               val = call1 (Vset_auto_coding_function, tem);
+               unsigned char *copy = (unsigned char *) alloca (nread + 1);
+
+               /* Make a copy of the contents of read_buf in COPY, 
+                  and convert it to lower case so we can compare
+                  more efficiently.  */
+               bcopy (read_buf, copy, nread);
+               for (i = 0; i < nread; i++)
+                 copy[i] = DOWNCASE (copy[i]);
+               /* Ensure various comparisons fail at end of data.  */
+               copy[nread] = 0;
+
+               /* Now test quickly whether the file contains a -*- line.  */
+               p = copy;
+               while (*p != '\n' && p - copy < end_of_beginning)
+                 p++;
+               if (copy[0] == '#' && copy[1] == '!')
+                 while (*p != '\n' && p - copy < end_of_beginning)
+                   p++;
+               p1 = copy;
+               while (p - p1 >= 3)
+                 {
+                   if (p1[0] == '-' && p1[1] == '*' && p1[2] == '-')
+                     {
+                       while (p - p1 >= 7)
+                         {
+                           if (! bcmp ("coding:", p1, 7))
+                             {
+                               possible_spec = 1;
+                               goto win;
+                             }
+                           p1++;
+                         }
+                       break;
+                     }
+                   p1++;
+                 }
+
+               /* Test quickly whether the file
+                  contains a local variables list.  */
+               p = &copy[nread - 1];
+               p1 = &copy[beginning_of_end];
+               while (p > p1)
+                 {
+                   if (p[0] == '\n' && p[1] == '\f')
+                     break;
+                   p--;
+                 }
+               p1 = &copy[nread];
+               while (p1 - p >= 16)
+                 {
+                   if (! bcmp ("local variables:", p, 16))
+                     {
+                       possible_spec = 1;
+                       break;
+                     }
+                   p++;
+                 }
+             win:
+
+               if (possible_spec)
+                 {
+                   /* Always make this a unibyte string
+                      because we have not yet decoded it.  */ 
+                   tem = make_unibyte_string (read_buf, nread);
+                   val = call1 (Vset_auto_coding_function, tem);
+                 }
+
                /* Rewind the file for the actual read done later.  */
                if (lseek (fd, 0, 0) < 0)
                  report_file_error ("Setting file position",
@@ -3309,13 +3507,27 @@ This does code conversion according to the value of\n\
          {
            Lisp_Object args[6], coding_systems;
 
-           args[0] = Qinsert_file_contents, args[1] = orig_filename,
-             args[2] = visit, args[3] = beg, args[4] = end, args[5] = replace;
+           args[0] = Qinsert_file_contents, args[1] = orig_filename;
+           args[2] = visit, args[3] = beg, args[4] = end, args[5] = replace;
            coding_systems = Ffind_operation_coding_system (6, args);
-           if (CONSP (coding_systems)) val = XCONS (coding_systems)->car;
+           if (CONSP (coding_systems))
+             val = XCONS (coding_systems)->car;
          }
       }
-    setup_coding_system (Fcheck_coding_system (val), &coding);
+
+    if (NILP (Vcoding_system_for_read)
+       && NILP (current_buffer->enable_multibyte_characters))
+      {
+       /* We must suppress all text conversion except for end-of-line
+          conversion.  */
+       struct coding_system coding_temp;
+
+       setup_coding_system (Fcheck_coding_system (val), &coding_temp);
+       setup_coding_system (Qraw_text, &coding);
+       coding.eol_type = coding_temp.eol_type;
+      }
+    else
+      setup_coding_system (Fcheck_coding_system (val), &coding);
   }
 
   /* If requested, replace the accessible part of the buffer
@@ -3333,7 +3545,9 @@ This does code conversion according to the value of\n\
      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_DECODING (&coding))
+      && ! CODING_REQUIRE_DECODING (&coding)
+      && (coding.eol_type == CODING_EOL_UNDECIDED
+         || coding.eol_type == CODING_EOL_LF))
     {
       /* same_at_start and same_at_end count bytes,
         because file access counts bytes
@@ -3456,8 +3670,7 @@ This does code conversion according to the value of\n\
              if (same_at_end > same_at_start
                  && FETCH_BYTE (same_at_end - 1) >= 0200
                  && ! NILP (current_buffer->enable_multibyte_characters)
-                 && (CODING_REQUIRE_DECODING (&coding)
-                     || CODING_REQUIRE_DETECTION (&coding)))
+                 && (CODING_MAY_REQUIRE_DECODING (&coding)))
                giveup_match_end = 1;
              break;
            }
@@ -3557,10 +3770,9 @@ This does code conversion according to the value of\n\
 
          how_much += this;
 
-         if (CODING_REQUIRE_DECODING (&coding)
-             || CODING_REQUIRE_DETECTION (&coding))
+         if (CODING_MAY_REQUIRE_DECODING (&coding))
            {
-             int require, produced, consumed;
+             int require, result;
 
              this += unprocessed;
 
@@ -3575,22 +3787,21 @@ This does code conversion according to the value of\n\
 
              /* 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);
+               coding.mode |= CODING_MODE_LAST_BLOCK;
+             result = decode_coding (&coding, read_buf,
+                                     conversion_buffer + inserted,
+                                     this, bufsize - inserted);
 
              /* Save for next iteration whatever we didn't convert.  */
-             unprocessed = this - consumed;
-             bcopy (read_buf + consumed, read_buf, unprocessed);
-             this = produced;
+             unprocessed = this - coding.consumed;
+             bcopy (read_buf + coding.consumed, read_buf, unprocessed);
+             this = coding.produced;
            }
 
          inserted += this;
        }
 
-      /* At this point, INSERTED is how many characters
+      /* At this point, INSERTED is how many characters (i.e. bytes)
         are present in CONVERSION_BUFFER.
         HOW_MUCH should equal TOTAL,
         or should be <= 0 if we couldn't read the file.  */
@@ -3652,7 +3863,14 @@ This does code conversion according to the value of\n\
         we are taking from the file.  */
       inserted -= (Z_BYTE - same_at_end) + (same_at_start - BEG_BYTE);
       del_range_byte (same_at_start, same_at_end, 0);
-      SET_PT_BOTH (GPT, GPT_BYTE);
+      if (same_at_end != same_at_start)
+       SET_PT_BOTH (GPT, GPT_BYTE);
+      else
+       {
+         /* Insert from the file at the proper position.  */
+         temp = BYTE_TO_CHAR (same_at_start);
+         SET_PT_BOTH (temp, same_at_start);
+       }
 
       insert_1 (conversion_buffer + same_at_start - BEG_BYTE, inserted,
                0, 0, 0);
@@ -3694,35 +3912,44 @@ This does code conversion according to the value of\n\
     }
 
   /* 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.  */
+     far for a regular file, and not changed for a special file.  But,
+     before exiting the loop, it is set to a negative value if I/O
+     error occurs.  */
   how_much = 0;
   /* Total bytes inserted.  */
   inserted = 0;
-  /* Bytes not processed in the previous loop because short gap size.  */
-  unprocessed = 0;
+  /* Here, we don't do code conversion in the loop.  It is done by
+     code_convert_region after all data are read into the buffer.  */
   while (how_much < total)
     {
        /* try is reserved in some compilers (Microsoft C) */
-      int trytry = min (total - how_much, READ_BUF_SIZE - unprocessed);
-      char *destination = (! (CODING_REQUIRE_DECODING (&coding)
-                             || CODING_REQUIRE_DETECTION (&coding))
-                          ? (char *) (BYTE_POS_ADDR (PT_BYTE + inserted - 1) + 1)
-                          : read_buf + unprocessed);
-      int this, this_chars;
+      int trytry = min (total - how_much, READ_BUF_SIZE);
+      int this;
+
+      /* For a special file, GAP_SIZE should be checked every time.  */
+      if (not_regular && GAP_SIZE < trytry)
+       make_gap (total - GAP_SIZE);
 
       /* Allow quitting out of the actual I/O.  */
       immediate_quit = 1;
       QUIT;
-      this = read (fd, destination, trytry);
+      this = read (fd, BYTE_POS_ADDR (PT_BYTE + inserted - 1) + 1, trytry);
       immediate_quit = 0;
 
-      if (this < 0 || this + unprocessed == 0)
+      if (this <= 0)
        {
          how_much = this;
          break;
        }
 
+      GAP_SIZE -= this;
+      GPT_BYTE += this;
+      ZV_BYTE += this;
+      Z_BYTE += this;
+      GPT += this;
+      ZV += this;
+      Z += this;
+
       /* 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,
@@ -3730,99 +3957,63 @@ This does code conversion according to the value of\n\
         (INSERTED is where we count the number of characters inserted.)  */
       if (! not_regular)
        how_much += this;
-
-      this_chars = this;
-      if (CODING_REQUIRE_DECODING (&coding)
-         || CODING_REQUIRE_DETECTION (&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,
-                                   BYTE_POS_ADDR (PT_BYTE + inserted - 1) + 1,
-                                   this, GAP_SIZE, &consumed);
-         if (produced > 0) 
-           {
-             Lisp_Object temp;
-
-             XSET (temp, Lisp_Int, Z_BYTE + produced);
-             if (Z_BYTE + produced != XINT (temp))
-               {
-                 how_much = -2;
-                 break;
-               }
-           }
-         unprocessed = this - consumed;
-         bcopy (read_buf + consumed, read_buf, unprocessed);
-         this = produced;
-         this_chars = chars_in_text (BYTE_POS_ADDR (PT_BYTE + inserted - 1) + 1,
-                                     produced);
-       }
-
-      GAP_SIZE -= this;
-      GPT_BYTE += this;
-      ZV_BYTE += this;
-      Z_BYTE += this;
-      GPT += this_chars;
-      ZV += this_chars;
-      Z += this_chars;
-
-      if (GAP_SIZE > 0)
-       /* Put an anchor to ensure multi-byte form ends at gap.  */
-       *GPT_ADDR = 0;
       inserted += this;
-      inserted_chars += this_chars;
     }
 
-#ifdef DOS_NT
-  /* Use the conversion type to determine buffer-file-type
-     (find-buffer-file-type is now used to help determine the
-     conversion).  */
-  if (coding.eol_type != CODING_EOL_UNDECIDED 
-      && coding.eol_type != CODING_EOL_LF)
-    current_buffer->buffer_file_type = Qnil;
-  else
-    current_buffer->buffer_file_type = Qt;
-#endif
-
-  if (inserted > 0)
-    {
-      record_insert (PT, inserted_chars);
-
-      /* Only defined if Emacs is compiled with USE_TEXT_PROPERTIES */
-      offset_intervals (current_buffer, PT, inserted_chars);
-      MODIFF++;
-    }
+  if (GAP_SIZE > 0)
+    /* Put an anchor to ensure multi-byte form ends at gap.  */
+    *GPT_ADDR = 0;
 
   close (fd);
 
   /* Discard the unwind protect for closing the file.  */
   specpdl_ptr--;
 
-  if (how_much == -1)
+  if (how_much < 0)
     error ("IO error reading %s: %s",
           XSTRING (orig_filename)->data, strerror (errno));
-  else if (how_much == -2)
-    error ("maximum buffer size exceeded");
+
+  if (inserted > 0)
+    {
+      if (CODING_MAY_REQUIRE_DECODING (&coding))
+       {
+         /* Here, we don't have to consider byte combining (see the
+             comment below) because code_convert_region takes care of
+             it.  */
+         code_convert_region (PT, PT_BYTE, PT + inserted, PT_BYTE + inserted,
+                              &coding, 0, 0);
+         inserted = (NILP (current_buffer->enable_multibyte_characters)
+                     ? coding.produced : coding.produced_char);
+       }
+      else if (!NILP (current_buffer->enable_multibyte_characters))
+       {
+         int inserted_byte = inserted;
+
+         /* There's a possibility that we must combine bytes at the
+            head (resp. the tail) of the just inserted text with the
+            bytes before (resp. after) the gap to form a single
+            character.  */
+         inserted = multibyte_chars_in_text (GPT_ADDR - inserted, inserted);
+         adjust_after_insert (PT, PT_BYTE,
+                              PT + inserted_byte, PT_BYTE + inserted_byte,
+                              inserted);
+       }
+      else
+       adjust_after_insert (PT, PT_BYTE, PT + inserted, PT_BYTE + inserted,
+                            inserted);
+
+#ifdef DOS_NT
+      /* Use the conversion type to determine buffer-file-type
+        (find-buffer-file-type is now used to help determine the
+        conversion).  */
+      if ((coding.eol_type == CODING_EOL_UNDECIDED 
+          || coding.eol_type == CODING_EOL_LF)
+         && ! CODING_REQUIRE_DECODING (&coding))
+       current_buffer->buffer_file_type = Qt;
+      else
+       current_buffer->buffer_file_type = Qnil;
+#endif
+    }
 
   set_coding_system = 1;
 
@@ -3865,20 +4056,20 @@ This does code conversion according to the value of\n\
     }
 
   /* Decode file format */
-  if (inserted_chars > 0)
+  if (inserted > 0)
     {
       insval = call3 (Qformat_decode,
-                     Qnil, make_number (inserted_chars), visit);
+                     Qnil, make_number (inserted), visit);
       CHECK_NUMBER (insval, 0);
-      inserted_chars = XFASTINT (insval);
+      inserted = XFASTINT (insval);
     }
 
   /* 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_chars > 0 && total > 0
+  if (inserted > 0 && total > 0
       && (NILP (visit) || !NILP (replace)))
-    signal_after_change (PT, 0, inserted_chars);
+    signal_after_change (PT, 0, inserted);
 
   if (set_coding_system)
     Vlast_coding_system_used = coding.symbol;
@@ -3886,16 +4077,13 @@ This does code conversion according to the value of\n\
   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_chars));
+         insval = call1 (Fcar (p), make_number (inserted));
          if (!NILP (insval))
            {
              CHECK_NUMBER (insval, 0);
-             inserted_chars = XFASTINT (insval);
+             inserted = XFASTINT (insval);
            }
          QUIT;
          p = Fcdr (p);
@@ -3935,8 +4123,8 @@ build_annotations_unwind (buf)
   return Qnil;
 }
 
-DEFUN ("write-region", Fwrite_region, Swrite_region, 3, 6,
-  "r\nFWrite region to file: ",
+DEFUN ("write-region", Fwrite_region, Swrite_region, 3, 7,
+  "r\nFWrite region to file: \ni\ni\ni\np",
   "Write current region into specified file.\n\
 When called from a program, takes three arguments:\n\
 START, END and FILENAME.  START and END are buffer positions.\n\
@@ -3952,10 +4140,12 @@ If VISIT is neither t nor nil nor a string,\n\
   that means do not print the \"Wrote file\" message.\n\
 The optional sixth arg LOCKNAME, if non-nil, specifies the name to\n\
   use for locking and unlocking, overriding FILENAME and VISIT.\n\
+The optional seventh arg CONFIRM, if non-nil, says ask for confirmation\n\
+  before overwriting an existing file.\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, lockname)
-     Lisp_Object start, end, filename, append, visit, lockname;
+  (start, end, filename, append, visit, lockname, confirm)
+     Lisp_Object start, end, filename, append, visit, lockname, confirm;
 {
   register int desc;
   int failure;
@@ -4004,7 +4194,7 @@ to the file, instead of any buffer contents, and END is ignored.")
           had better write it out with the same coding system even if
           `enable-multibyte-characters' is nil.
 
-          If is is not set locally, we anyway have to convert EOL
+          If it is not set locally, we anyway have to convert EOL
           format if the default value of `buffer-file-coding-system'
           tells that it is not Unix-like (LF only) format.  */
        val = current_buffer->buffer_file_coding_system;
@@ -4016,7 +4206,7 @@ to the file, instead of any buffer contents, and END is ignored.")
            if (coding_temp.eol_type == CODING_EOL_CRLF
                || coding_temp.eol_type == CODING_EOL_CR)
              {
-               setup_coding_system (Qemacs_mule, &coding);
+               setup_coding_system (Qraw_text, &coding);
                coding.eol_type = coding_temp.eol_type;
                goto done_setup_coding;
              }
@@ -4027,24 +4217,31 @@ to the file, instead of any buffer contents, and END is ignored.")
       {
        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;
+       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_operation_coding_system (7, args);
        val = (CONSP (coding_systems) && !NILP (XCONS (coding_systems)->cdr)
               ? XCONS (coding_systems)->cdr
               : current_buffer->buffer_file_coding_system);
+       /* Confirm that VAL can surely encode the current region.  */
+       if (!NILP (Ffboundp (Vselect_safe_coding_system_function)))
+         val = call3 (Vselect_safe_coding_system_function, start, end, val);
       }
     setup_coding_system (Fcheck_coding_system (val), &coding); 
 
   done_setup_coding:
     if (!STRINGP (start) && !NILP (current_buffer->selective_display))
-      coding.selective = 1;
+      coding.mode |= CODING_MODE_SELECTIVE_DISPLAY;
   }
 
   Vlast_coding_system_used = coding.symbol;
 
   filename = Fexpand_file_name (filename, Qnil);
+
+  if (! NILP (confirm))
+    barf_or_query_if_file_exists (filename, "overwrite", 1, 0, 1);
+
   if (STRINGP (visit))
     visit_file = Fexpand_file_name (visit, Qnil);
   else
@@ -4245,7 +4442,8 @@ 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, &coding);
+                            STRING_BYTES (XSTRING (start)), 0, &annotations,
+                            &coding);
       save_errno = errno;
     }
   else if (XINT (start) != XINT (end))
@@ -4273,15 +4471,17 @@ to the file, instead of any buffer contents, and END is ignored.")
   else
     {
       /* If file was empty, still need to write the annotations */
-      coding.last_block = 1;
+      coding.mode |= CODING_MODE_LAST_BLOCK;
       failure = 0 > a_write (desc, "", 0, XINT (start), &annotations, &coding);
       save_errno = errno;
     }
 
-  if (CODING_REQUIRE_FLUSHING (&coding) && !coding.last_block)
+  if (CODING_REQUIRE_FLUSHING (&coding)
+      && !(coding.mode & CODING_MODE_LAST_BLOCK)
+      && ! failure)
     {
       /* We have to flush out a data. */
-      coding.last_block = 1;
+      coding.mode |= CODING_MODE_LAST_BLOCK;
       failure = 0 > e_write (desc, "", 0, &coding);
       save_errno = errno;
     }
@@ -4491,7 +4691,7 @@ a_write (desc, addr, nbytes, bytepos, annot, coding)
   while (NILP (*annot) || CONSP (*annot))
     {
       tem = Fcar_safe (Fcar (*annot));
-      nextpos = 0;
+      nextpos = bytepos - 1;
       if (INTEGERP (tem))
        nextpos = CHAR_TO_BYTE (XFASTINT (tem));
 
@@ -4512,12 +4712,13 @@ a_write (desc, addr, nbytes, bytepos, annot, coding)
       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, STRING_BYTES (XSTRING (tem)),
                           coding))
            return -1;
        }
       *annot = Fcdr (*annot);
     }
+  return 0;
 }
 
 #ifndef WRITE_BUF_SIZE
@@ -4535,19 +4736,17 @@ e_write (desc, addr, nbytes, coding)
      struct coding_system *coding;
 {
   char buf[WRITE_BUF_SIZE];
-  int produced, consumed;
 
   /* We used to have a code for handling selective display here.  But,
      now it is handled within encode_coding.  */
   while (1)
     {
-      produced = encode_coding (coding, addr, buf, nbytes, WRITE_BUF_SIZE,
-                               &consumed);
-      nbytes -= consumed, addr += consumed;
-      if (produced > 0)
+      encode_coding (coding, addr, buf, nbytes, WRITE_BUF_SIZE);
+      nbytes -= coding->consumed, addr += coding->consumed;
+      if (coding->produced > 0)
        {
-         produced -= write (desc, buf, produced);
-         if (produced) return -1;
+         coding->produced -= write (desc, buf, coding->produced);
+         if (coding->produced) return -1;
        }
       if (nbytes <= 0)
        break;
@@ -4686,7 +4885,7 @@ auto_save_1 ()
   return
     Fwrite_region (Qnil, Qnil,
                   current_buffer->auto_save_file_name,
-                  Qnil, Qlambda, Qnil);
+                  Qnil, Qlambda, Qnil, Qnil);
 }
 
 static Lisp_Object
@@ -4797,11 +4996,11 @@ A non-nil CURRENT-ONLY argument means save only current buffer.")
            if (!NILP (b->filename))
              {
                fwrite (XSTRING (b->filename)->data, 1,
-                       XSTRING (b->filename)->size, stream);
+                       STRING_BYTES (XSTRING (b->filename)), stream);
              }
            putc ('\n', stream);
            fwrite (XSTRING (b->auto_save_file_name)->data, 1,
-                   XSTRING (b->auto_save_file_name)->size, stream);
+                   STRING_BYTES (XSTRING (b->auto_save_file_name)), stream);
            putc ('\n', stream);
          }
 
@@ -4935,7 +5134,7 @@ double_dollars (val)
   register int n;
   int osize, count;
 
-  osize = XSTRING (val)->size_byte;
+  osize = STRING_BYTES (XSTRING (val));
 
   /* Count the number of $ characters.  */
   for (n = osize, count = 0, old = XSTRING (val)->data; n > 0; n--)
@@ -5050,6 +5249,8 @@ DIR defaults to current buffer's directory default.")
   Lisp_Object val, insdef, insdef1, tem;
   struct gcpro gcpro1, gcpro2;
   register char *homedir;
+  int replace_in_history = 0;
+  int add_to_history = 0;
   int count;
 
   if (NILP (dir))
@@ -5074,7 +5275,7 @@ DIR defaults to current buffer's directory default.")
       && IS_DIRECTORY_SEP (XSTRING (dir)->data[strlen (homedir)]))
     {
       dir = make_string (XSTRING (dir)->data + strlen (homedir) - 1,
-                        XSTRING (dir)->size - strlen (homedir) + 1);
+                        STRING_BYTES (XSTRING (dir)) - strlen (homedir) + 1);
       XSTRING (dir)->data[0] = '~';
     }
 
@@ -5111,13 +5312,25 @@ DIR defaults to current buffer's directory default.")
   val = Fcompleting_read (prompt, intern ("read-file-name-internal"),
                          dir, mustmatch, insdef1,
                          Qfile_name_history, default_filename, Qnil);
-  /* If Fcompleting_read returned the default string itself
+
+  tem = Fsymbol_value (Qfile_name_history);
+  if (CONSP (tem) && EQ (XCONS (tem)->car, val))
+    replace_in_history = 1;
+
+  /* If Fcompleting_read returned the inserted default string itself
      (rather than a new string with the same contents),
      it has to mean that the user typed RET with the minibuffer empty.
      In that case, we really want to return ""
      so that commands such as set-visited-file-name can distinguish.  */
   if (EQ (val, default_filename))
-    val = build_string ("");
+    {
+      /* In this case, Fcompleting_read has not added an element
+        to the history.  Maybe we should.  */
+      if (! replace_in_history)
+       add_to_history = 1;
+
+      val = build_string ("");
+    }
 
 #ifdef VMS
   unbind_to (count, Qnil);
@@ -5126,81 +5339,37 @@ DIR defaults to current buffer's directory default.")
   UNGCPRO;
   if (NILP (val))
     error ("No file name specified");
+
   tem = Fstring_equal (val, insdef);
+
   if (!NILP (tem) && !NILP (default_filename))
-    return default_filename;
-  if (XSTRING (val)->size == 0 && NILP (insdef))
+    val = default_filename;
+  else if (XSTRING (val)->size == 0 && NILP (insdef))
     {
       if (!NILP (default_filename))
-       return default_filename;
+       val = default_filename;
       else
        error ("No default file name");
     }
-  return Fsubstitute_in_file_name (val);
-}
+  val = Fsubstitute_in_file_name (val);
 
-#if 0                           /* Old version */
-DEFUN ("read-file-name", Fread_file_name, Sread_file_name, 1, 5, 0,
-  /* Don't confuse make-docfile by having two doc strings for this function.
-     make-docfile does not pay attention to #if, for good reason!  */
-  0)
-  (prompt, dir, defalt, mustmatch, initial)
-     Lisp_Object prompt, dir, defalt, mustmatch, initial;
-{
-  Lisp_Object val, insdef, tem;
-  struct gcpro gcpro1, gcpro2;
-  register char *homedir;
-  int count;
-
-  if (NILP (dir))
-    dir = current_buffer->directory;
-  if (NILP (defalt))
-    defalt = current_buffer->filename;
-
-  /* If dir starts with user's homedir, change that to ~. */
-  homedir = (char *) egetenv ("HOME");
-  if (homedir != 0
-      && STRINGP (dir)
-      && !strncmp (homedir, XSTRING (dir)->data, strlen (homedir))
-      && XSTRING (dir)->data[strlen (homedir)] == '/')
+  if (replace_in_history)
+    /* Replace what Fcompleting_read added to the history
+       with what we will actually return.  */
+    XCONS (Fsymbol_value (Qfile_name_history))->car = val;
+  else if (add_to_history)
     {
-      dir = make_string (XSTRING (dir)->data + strlen (homedir) - 1,
-                        XSTRING (dir)->size - strlen (homedir) + 1);
-      XSTRING (dir)->data[0] = '~';
+      /* Add the value to the history--but not if it matches
+        the last value already there.  */
+      tem = Fsymbol_value (Qfile_name_history);
+      if (! CONSP (tem) || NILP (Fequal (XCONS (tem)->car, val)))
+       Fset (Qfile_name_history,
+             Fcons (val, tem));
     }
-
-  if (!NILP (initial))
-    insdef = initial;
-  else if (insert_default_directory)
-    insdef = dir;
-  else
-    insdef = build_string ("");
-
-#ifdef VMS
-  count = specpdl_ptr - specpdl;
-  specbind (intern ("completion-ignore-case"), Qt);
-#endif
-
-  GCPRO2 (insdef, defalt);
-  val = Fcompleting_read (prompt, intern ("read-file-name-internal"),
-                         dir, mustmatch,
-                         insert_default_directory ? insdef : Qnil,
-                         Qfile_name_history, Qnil, Qnil);
-
-#ifdef VMS
-  unbind_to (count, Qnil);
-#endif
-
-  UNGCPRO;
-  if (NILP (val))
-    error ("No file name specified");
-  tem = Fstring_equal (val, insdef);
-  if (!NILP (tem) && !NILP (defalt))
-    return defalt;
-  return Fsubstitute_in_file_name (val);
+  return val;
 }
-#endif /* Old version */
 \f
+void
 syms_of_fileio ()
 {
   Qexpand_file_name = intern ("expand-file-name");
@@ -5282,9 +5451,21 @@ syms_of_fileio ()
 #endif /* DOS_NT */
 
   DEFVAR_LISP ("file-name-coding-system", &Vfile_name_coding_system,
-    "*Coding system for encoding file names.");
+    "*Coding system for encoding file names.\n\
+If it is nil, default-file-name-coding-system (which see) is used.");
   Vfile_name_coding_system = Qnil;
 
+  DEFVAR_LISP ("default-file-name-coding-system",
+              &Vdefault_file_name_coding_system,
+    "Default coding system for encoding file names.\n\
+This variable is used only when file-name-coding-system is nil.\n\
+\n\
+This variable is set/changed by the command set-language-environment.\n\
+User should not set this variable manually,\n\
+instead use file-name-coding-system to get a constant encoding\n\
+of file names regardless of the current language environment.");
+  Vdefault_file_name_coding_system = Qnil;
+
   DEFVAR_LISP ("auto-save-file-format", &Vauto_save_file_format,
     "*Format in which to write auto-save files.\n\
 Should be a list of symbols naming formats that are defined in `format-alist'.\n\