(Fexpand_file_name) [WINDOWSNT]: Be careful not to
[bpt/emacs.git] / src / fileio.c
index 4f0fcd1..aef7a01 100644 (file)
@@ -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_byte;
+  end = p = beg + STRING_BYTES (XSTRING (filename));
 
   while (p != beg && !IS_DIRECTORY_SEP (p[-1])
 #ifdef VMS
@@ -488,8 +484,9 @@ file_name_as_directory (out, in)
 
   if (size < 0)
     {
-      out[0] = '/';
-      out[1] = 0;
+      out[0] = '.';
+      out[1] = '/';
+      out[2] = 0;
       return out;
     }
 
@@ -589,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
@@ -603,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;
 {
@@ -784,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\
@@ -848,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;
@@ -891,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]))
@@ -925,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
@@ -976,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, ':')
@@ -1225,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
@@ -1300,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
@@ -1321,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 */
@@ -1334,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);
@@ -1443,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 */
 
@@ -1809,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.  */
 
@@ -1902,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 */
@@ -2021,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);
   }
@@ -2029,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
@@ -2045,30 +2148,39 @@ expand_and_dir_to_file (filename, defdir)
 
    *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 STATPTR is null, we don't store into it.  */
+   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,
@@ -2131,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;
 
@@ -2145,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)
     {
@@ -2369,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
@@ -2444,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))
@@ -2511,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))
     {
@@ -2881,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 */
@@ -3157,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;
@@ -3266,25 +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
+    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)
@@ -3293,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",
@@ -3314,10 +3507,11 @@ 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;
          }
       }
 
@@ -3351,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
@@ -3667,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);
@@ -3774,40 +3977,42 @@ This does code conversion according to the value of\n\
     {
       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)
-       current_buffer->buffer_file_type = Qnil;
-      else
+      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
-
-      record_insert (PT, inserted);
-
-      /* Only defined if Emacs is compiled with USE_TEXT_PROPERTIES */
-      offset_intervals (current_buffer, PT, inserted);
-      MODIFF++;
-
-      if (! NILP (coding.post_read_conversion))
-       {
-         Lisp_Object val;
-
-         val = call1 (coding.post_read_conversion, make_number (inserted));
-         if (!NILP (val))
-           {
-             CHECK_NUMBER (val, 0);
-             inserted = XFASTINT (val);
-           }
-       }
     }
 
   set_coding_system = 1;
@@ -4020,7 +4225,7 @@ to the file, instead of any buffer contents, and END is ignored.")
               ? XCONS (coding_systems)->cdr
               : current_buffer->buffer_file_coding_system);
        /* Confirm that VAL can surely encode the current region.  */
-       if (Ffboundp (Vselect_safe_coding_system_function))
+       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); 
@@ -4035,7 +4240,7 @@ to the file, instead of any buffer contents, and END is ignored.")
   filename = Fexpand_file_name (filename, Qnil);
 
   if (! NILP (confirm))
-    barf_or_query_if_file_exists (filename, "overwrite", 1, 0);
+    barf_or_query_if_file_exists (filename, "overwrite", 1, 0, 1);
 
   if (STRINGP (visit))
     visit_file = Fexpand_file_name (visit, Qnil);
@@ -4237,7 +4442,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_byte, 0, &annotations,
+                            STRING_BYTES (XSTRING (start)), 0, &annotations,
                             &coding);
       save_errno = errno;
     }
@@ -4507,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_byte,
+         if (0 > e_write (desc, XSTRING (tem)->data, STRING_BYTES (XSTRING (tem)),
                           coding))
            return -1;
        }
       *annot = Fcdr (*annot);
     }
+  return 0;
 }
 
 #ifndef WRITE_BUF_SIZE
@@ -4790,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);
          }
 
@@ -4928,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--)
@@ -5069,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] = '~';
     }
 
@@ -5163,6 +5369,7 @@ DIR defaults to current buffer's directory default.")
   return val;
 }
 \f
+void
 syms_of_fileio ()
 {
   Qexpand_file_name = intern ("expand-file-name");
@@ -5244,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\