]> code.delx.au - gnu-emacs/blobdiff - src/fileio.c
coding.c (decode_coding_iso_2022): When an invalid escape sequence is encountered...
[gnu-emacs] / src / fileio.c
index fb6ecfedeb44f3c6ddce6905ed847df8f22c8511..1049522e5a973cfdf97a363f2968bf7c9ae1eb80 100644 (file)
@@ -81,6 +81,25 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #define DRIVE_LETTER(x) c_tolower (x)
 #endif
 
+#ifdef HAVE_POSIX_ACL
+/* FIXME: this macro was copied from gnulib's private acl-internal.h
+   header file.  */
+/* Recognize some common errors such as from an NFS mount that does
+   not support ACLs, even when local drives do.  */
+#if defined __APPLE__ && defined __MACH__ /* Mac OS X */
+#define ACL_NOT_WELL_SUPPORTED(Err)                                    \
+  ((Err) == ENOTSUP || (Err) == ENOSYS || (Err) == EINVAL || (Err) == EBUSY || (Err) == ENOENT)
+#elif defined EOPNOTSUPP /* Tru64 NFS */
+#define ACL_NOT_WELL_SUPPORTED(Err)                                    \
+  ((Err) == ENOTSUP || (Err) == ENOSYS || (Err) == EINVAL || (Err) == EBUSY || (Err) == EOPNOTSUPP)
+#elif defined WINDOWSNT
+#define ACL_NOT_WELL_SUPPORTED(Err)  ((Err) == ENOTSUP)
+#else
+#define ACL_NOT_WELL_SUPPORTED(Err)                                    \
+  ((Err) == ENOTSUP || (Err) == ENOSYS || (Err) == EINVAL || (Err) == EBUSY)
+#endif
+#endif /* HAVE_POSIX_ACL */
+
 #include "systime.h"
 #include <allocator.h>
 #include <careadlinkat.h>
@@ -133,9 +152,6 @@ static Lisp_Object Qwrite_region_annotate_functions;
    is added here.  */
 static Lisp_Object Vwrite_region_annotation_buffers;
 
-#ifdef HAVE_FSYNC
-#endif
-
 static Lisp_Object Qdelete_by_moving_to_trash;
 
 /* Lisp function for moving files to trash.  */
@@ -251,7 +267,7 @@ static Lisp_Object Qfile_acl;
 static Lisp_Object Qset_file_acl;
 static Lisp_Object Qfile_newer_than_file_p;
 Lisp_Object Qinsert_file_contents;
-Lisp_Object Qchoose_write_coding_system;
+static Lisp_Object Qchoose_write_coding_system;
 Lisp_Object Qwrite_region;
 static Lisp_Object Qverify_visited_file_modtime;
 static Lisp_Object Qset_visited_file_modtime;
@@ -383,11 +399,13 @@ Given a Unix syntax file name, returns a string ending in slash.  */)
 
       if (getdefdir (c_toupper (*beg) - 'A' + 1, r))
        {
-         if (!IS_DIRECTORY_SEP (res[strlen (res) - 1]))
+         size_t l = strlen (res);
+
+         if (l > 3 || !IS_DIRECTORY_SEP (res[l - 1]))
            strcat (res, "/");
          beg = res;
          p = beg + strlen (beg);
-         dostounix_filename (beg);
+         dostounix_filename (beg, 0);
          tem_fn = make_specified_string (beg, -1, p - beg,
                                          STRING_MULTIBYTE (filename));
        }
@@ -397,13 +415,16 @@ Given a Unix syntax file name, returns a string ending in slash.  */)
     }
   else if (STRING_MULTIBYTE (filename))
     {
-      tem_fn = ENCODE_FILE (make_specified_string (beg, -1, p - beg, 1));
-      dostounix_filename (SSDATA (tem_fn));
-      tem_fn = DECODE_FILE (tem_fn);
+      tem_fn = make_specified_string (beg, -1, p - beg, 1);
+      dostounix_filename (SSDATA (tem_fn), 1);
+#ifdef WINDOWSNT
+      if (!NILP (Vw32_downcase_file_names))
+       tem_fn = Fdowncase (tem_fn);
+#endif
     }
   else
     {
-      dostounix_filename (beg);
+      dostounix_filename (beg, 0);
       tem_fn = make_specified_string (beg, -1, p - beg, 0);
     }
   return tem_fn;
@@ -507,17 +528,7 @@ file_name_as_directory (char *dst, const char *src, ptrdiff_t srclen,
       srclen++;
     }
 #ifdef DOS_NT
-  if (multibyte)
-    {
-      Lisp_Object tem_fn = make_specified_string (dst, -1, srclen, 1);
-
-      tem_fn = ENCODE_FILE (tem_fn);
-      dostounix_filename (SSDATA (tem_fn));
-      tem_fn = DECODE_FILE (tem_fn);
-      memcpy (dst, SSDATA (tem_fn), (srclen = SBYTES (tem_fn)) + 1);
-    }
-  else
-    dostounix_filename (dst);
+  dostounix_filename (dst, multibyte);
 #endif
   return srclen;
 }
@@ -552,6 +563,10 @@ For a Unix-syntax file name, just appends a slash.  */)
       error ("Invalid handler in `file-name-handler-alist'");
     }
 
+#ifdef WINDOWSNT
+  if (!NILP (Vw32_downcase_file_names))
+    file = Fdowncase (file);
+#endif
   buf = alloca (SBYTES (file) + 10);
   length = file_name_as_directory (buf, SSDATA (file), SBYTES (file),
                                   STRING_MULTIBYTE (file));
@@ -580,17 +595,7 @@ directory_file_name (char *dst, char *src, ptrdiff_t srclen, bool multibyte)
       srclen--;
     }
 #ifdef DOS_NT
-  if (multibyte)
-    {
-      Lisp_Object tem_fn = make_specified_string (dst, -1, srclen, 1);
-
-      tem_fn = ENCODE_FILE (tem_fn);
-      dostounix_filename (SSDATA (tem_fn));
-      tem_fn = DECODE_FILE (tem_fn);
-      memcpy (dst, SSDATA (tem_fn), (srclen = SBYTES (tem_fn)) + 1);
-    }
-  else
-    dostounix_filename (dst);
+  dostounix_filename (dst, multibyte);
 #endif
   return srclen;
 }
@@ -625,6 +630,10 @@ In Unix-syntax, this function just removes the final slash.  */)
       error ("Invalid handler in `file-name-handler-alist'");
     }
 
+#ifdef WINDOWSNT
+  if (!NILP (Vw32_downcase_file_names))
+    directory = Fdowncase (directory);
+#endif
   buf = alloca (SBYTES (directory) + 20);
   length = directory_file_name (buf, SSDATA (directory), SBYTES (directory),
                                STRING_MULTIBYTE (directory));
@@ -925,6 +934,11 @@ filesystem tree, not (expand-file-name ".."  dirname).  */)
        }
     }
 
+#ifdef WINDOWSNT
+  if (!NILP (Vw32_downcase_file_names))
+    default_directory = Fdowncase (default_directory);
+#endif
+
   /* Make a local copy of nm[] to protect it from GC in DECODE_FILE below.  */
   nm = alloca (SBYTES (name) + 1);
   memcpy (nm, SSDATA (name), SBYTES (name) + 1);
@@ -1008,18 +1022,7 @@ filesystem tree, not (expand-file-name ".."  dirname).  */)
 #ifdef DOS_NT
          /* Make sure directories are all separated with /, but
             avoid allocation of a new string when not required. */
-         if (multibyte)
-           {
-             Lisp_Object tem_name = make_specified_string (nm, -1, strlen (nm),
-                                                           multibyte);
-
-             tem_name = ENCODE_FILE (tem_name);
-             dostounix_filename (SSDATA (tem_name));
-             tem_name = DECODE_FILE (tem_name);
-             memcpy (nm, SSDATA (tem_name), SBYTES (tem_name) + 1);
-           }
-         else
-           dostounix_filename (nm);
+         dostounix_filename (nm, multibyte);
 #ifdef WINDOWSNT
          if (IS_DIRECTORY_SEP (nm[1]))
            {
@@ -1037,6 +1040,10 @@ filesystem tree, not (expand-file-name ".."  dirname).  */)
              temp[0] = DRIVE_LETTER (drive);
              name = concat2 (build_string (temp), name);
            }
+#ifdef WINDOWSNT
+         if (!NILP (Vw32_downcase_file_names))
+           name = Fdowncase (name);
+#endif
          return name;
 #else /* not DOS_NT */
          if (strcmp (nm, SSDATA (name)) == 0)
@@ -1357,8 +1364,8 @@ filesystem tree, not (expand-file-name ".."  dirname).  */)
 #ifdef WINDOWSNT
            char *prev_o = o;
 #endif
-           while (o != target && (--o) && !IS_DIRECTORY_SEP (*o))
-             ;
+           while (o != target && (--o, !IS_DIRECTORY_SEP (*o)))
+             continue;
 #ifdef WINDOWSNT
            /* Don't go below server level in UNC filenames.  */
            if (o == target + 1 && IS_DIRECTORY_SEP (*o)
@@ -1400,14 +1407,11 @@ filesystem tree, not (expand-file-name ".."  dirname).  */)
        target[1] = ':';
       }
     result = make_specified_string (target, -1, o - target, multibyte);
-    if (multibyte)
-      {
-       result = ENCODE_FILE (result);
-       dostounix_filename (SSDATA (result));
-       result = DECODE_FILE (result);
-      }
-    else
-      dostounix_filename (SSDATA (result));
+    dostounix_filename (SSDATA (result), multibyte);
+#ifdef WINDOWSNT
+    if (!NILP (Vw32_downcase_file_names))
+      result = Fdowncase (result);
+#endif
 #else  /* !DOS_NT */
     result = make_specified_string (target, -1, o - target, multibyte);
 #endif /* !DOS_NT */
@@ -1689,24 +1693,8 @@ those `/' is discarded.  */)
   memcpy (nm, SDATA (filename), SBYTES (filename) + 1);
 
 #ifdef DOS_NT
-  if (multibyte)
-    {
-      Lisp_Object encoded_filename = ENCODE_FILE (filename);
-      Lisp_Object tem_fn;
-
-      dostounix_filename (SDATA (encoded_filename));
-      tem_fn = DECODE_FILE (encoded_filename);
-      nm = alloca (SBYTES (tem_fn) + 1);
-      memcpy (nm, SDATA (tem_fn), SBYTES (tem_fn) + 1);
-      substituted = (memcmp (nm, SDATA (filename), SBYTES (filename)) != 0);
-      if (substituted)
-       filename = tem_fn;
-    }
-  else
-    {
-      dostounix_filename (nm);
-      substituted = (memcmp (nm, SDATA (filename), SBYTES (filename)) != 0);
-    }
+  dostounix_filename (nm, multibyte);
+  substituted = (memcmp (nm, SDATA (filename), SBYTES (filename)) != 0);
 #endif
   endp = nm + SBYTES (filename);
 
@@ -1741,8 +1729,9 @@ those `/' is discarded.  */)
        else if (*p == '{')
          {
            o = ++p;
-           while (p != endp && *p != '}') p++;
-           if (*p != '}') goto missingclose;
+           p = memchr (p, '}', endp - p);
+           if (! p)
+             goto missingclose;
            s = p;
          }
        else
@@ -1780,7 +1769,13 @@ those `/' is discarded.  */)
       }
 
   if (!substituted)
-    return filename;
+    {
+#ifdef WINDOWSNT
+      if (!NILP (Vw32_downcase_file_names))
+       filename = Fdowncase (filename);
+#endif
+      return filename;
+    }
 
   /* If substitution required, recopy the string and do it.  */
   /* Make space in stack frame for the new copy.  */
@@ -1804,8 +1799,9 @@ those `/' is discarded.  */)
        else if (*p == '{')
          {
            o = ++p;
-           while (p != endp && *p != '}') p++;
-           if (*p != '}') goto missingclose;
+           p = memchr (p, '}', endp - p);
+           if (! p)
+             goto missingclose;
            s = p++;
          }
        else
@@ -1819,9 +1815,6 @@ those `/' is discarded.  */)
        target = alloca (s - o + 1);
        memcpy (target, o, s - o);
        target[s - o] = 0;
-#ifdef DOS_NT
-       strupr (target); /* $home == $HOME etc.  */
-#endif /* DOS_NT */
 
        /* Get variable value.  */
        o = egetenv (target);
@@ -1858,6 +1851,16 @@ those `/' is discarded.  */)
        need to quote some $ to $$ first.  */
     xnm = p;
 
+#ifdef WINDOWSNT
+  if (!NILP (Vw32_downcase_file_names))
+    {
+      Lisp_Object xname = make_specified_string (xnm, -1, x - xnm, multibyte);
+
+      xname = Fdowncase (xname);
+      return xname;
+    }
+  else
+#endif
   return make_specified_string (xnm, -1, x - xnm, multibyte);
 
  badsubst:
@@ -1866,9 +1869,6 @@ those `/' is discarded.  */)
   error ("Missing \"}\" in environment-variable substitution");
  badvar:
   error ("Substituting nonexistent environment variable \"%s\"", target);
-
-  /* NOTREACHED */
-  return Qnil;
 }
 \f
 /* A slightly faster and more convenient way to get
@@ -2030,7 +2030,7 @@ entries (depending on how Emacs was built).  */)
     {
 #ifdef HAVE_POSIX_ACL
       acl = acl_get_file (SDATA (encoded_file), ACL_TYPE_ACCESS);
-      if (acl == NULL && errno != ENOTSUP)
+      if (acl == NULL && !ACL_NOT_WELL_SUPPORTED (errno))
        report_file_error ("Getting ACL", Fcons (file, Qnil));
 #endif
     }
@@ -2074,7 +2074,7 @@ entries (depending on how Emacs was built).  */)
     {
       bool fail =
        acl_set_file (SDATA (encoded_newname), ACL_TYPE_ACCESS, acl) != 0;
-      if (fail && errno != ENOTSUP)
+      if (fail && !ACL_NOT_WELL_SUPPORTED (errno))
        report_file_error ("Setting ACL", Fcons (newname, Qnil));
 
       acl_free (acl);
@@ -2106,7 +2106,7 @@ entries (depending on how Emacs was built).  */)
 
 #ifdef HAVE_POSIX_ACL
       acl = acl_get_fd (ifd);
-      if (acl == NULL && errno != ENOTSUP)
+      if (acl == NULL && !ACL_NOT_WELL_SUPPORTED (errno))
        report_file_error ("Getting ACL", Fcons (file, Qnil));
 #endif
     }
@@ -2195,7 +2195,7 @@ entries (depending on how Emacs was built).  */)
   if (acl != NULL)
     {
       bool fail = acl_set_fd (ofd, acl) != 0;
-      if (fail && errno != ENOTSUP)
+      if (fail && !ACL_NOT_WELL_SUPPORTED (errno))
        report_file_error ("Setting ACL", Fcons (newname, Qnil));
 
       acl_free (acl);
@@ -3193,7 +3193,7 @@ support.  */)
       fail = (acl_set_file (SSDATA (encoded_absname), ACL_TYPE_ACCESS,
                            acl)
              != 0);
-      if (fail && errno != ENOTSUP)
+      if (fail && !ACL_NOT_WELL_SUPPORTED (errno))
        report_file_error ("Setting ACL", Fcons (absname, Qnil));
 
       acl_free (acl);
@@ -3321,7 +3321,6 @@ Use the current time if TIMESTAMP is nil.  TIMESTAMP is in the format of
           return Qnil;
 #endif
         report_file_error ("Setting file times", Fcons (absname, Qnil));
-        return Qnil;
       }
   }
 
@@ -3978,7 +3977,7 @@ by calling `format-decode', which see.  */)
 
          /* If display currently starts at beginning of line,
             keep it that way.  */
-         if (XBUFFER (XWINDOW (selected_window)->buffer) == current_buffer)
+         if (XBUFFER (XWINDOW (selected_window)->contents) == current_buffer)
            XWINDOW (selected_window)->start_at_line_beg = !NILP (Fbolp ());
 
          replace_handled = 1;
@@ -4128,7 +4127,7 @@ by calling `format-decode', which see.  */)
 
       /* If display currently starts at beginning of line,
         keep it that way.  */
-      if (XBUFFER (XWINDOW (selected_window)->buffer) == current_buffer)
+      if (XBUFFER (XWINDOW (selected_window)->contents) == current_buffer)
        XWINDOW (selected_window)->start_at_line_beg = !NILP (Fbolp ());
 
       /* Replace the chars that we need to replace,
@@ -4238,7 +4237,8 @@ by calling `format-decode', which see.  */)
               to be signaled after decoding the text we read.  */
            nbytes = internal_condition_case_1
              (read_non_regular,
-              make_save_value ("iii", (ptrdiff_t) fd, inserted, trytry),
+              make_save_value (SAVE_TYPE_INT_INT_INT, (ptrdiff_t) fd,
+                               inserted, trytry),
               Qerror, read_non_regular_quit);
 
            if (NILP (nbytes))
@@ -4979,20 +4979,23 @@ This calls `write-region-annotate-functions' at the start, and
 
   immediate_quit = 0;
 
-#ifdef HAVE_FSYNC
-  /* Note fsync appears to change the modtime on BSD4.2 (both vax and sun).
+  /* fsync appears to change the modtime on BSD4.2.
      Disk full in NFS may be reported here.  */
   /* mib says that closing the file will try to write as fast as NFS can do
      it, and that means the fsync here is not crucial for autosave files.  */
-  if (!auto_saving && !write_region_inhibit_fsync && fsync (desc) < 0)
+  if (!auto_saving && !write_region_inhibit_fsync)
     {
-      /* If fsync fails with EINTR, don't treat that as serious.  Also
+      /* Transfer data and metadata to disk, retrying if interrupted.  Also,
         ignore EINVAL which happens when fsync is not supported on this
         file.  */
-      if (errno != EINTR && errno != EINVAL)
-       ok = 0, save_errno = errno;
+      while (fsync (desc) != 0)
+       if (errno != EINTR)
+         {
+           if (errno != EINVAL)
+             ok = 0, save_errno = errno;
+           break;
+         }
     }
-#endif
 
   modtime = invalid_emacs_time ();
   if (visiting)
@@ -5029,17 +5032,28 @@ This calls `write-region-annotate-functions' at the start, and
       && ! (valid_timestamp_file_system && st.st_dev == timestamp_file_system))
     {
       int desc1 = emacs_open (fn, O_WRONLY | O_BINARY, 0);
-      if (0 <= desc1)
+      if (desc1 >= 0)
        {
          struct stat st1;
          if (fstat (desc1, &st1) == 0
              && st.st_dev == st1.st_dev && st.st_ino == st1.st_ino)
            {
+             /* Use the heuristic if it appears to be valid.  With neither
+                O_EXCL nor O_TRUNC, if Emacs happened to write nothing to the
+                file, the time stamp won't change.  Also, some non-POSIX
+                systems don't update an empty file's time stamp when
+                truncating it.  Finally, file systems with 100 ns or worse
+                resolution sometimes seem to have bugs: on a system with ns
+                resolution, checking ns % 100 incorrectly avoids the heuristic
+                1% of the time, but the problem should be temporary as we will
+                try again on the next time stamp.  */
+             bool use_heuristic
+               = ((open_flags & (O_EXCL | O_TRUNC)) != 0
+                  && st.st_size != 0
+                  && EMACS_NSECS (modtime) % 100 != 0);
+
              EMACS_TIME modtime1 = get_stat_mtime (&st1);
-             /* If neither O_EXCL nor O_TRUNC is used, and Emacs happened to
-                write nothing to the file, the file's time stamp won't change
-                so it should not be used in this heuristic.  */
-             if ((open_flags & (O_EXCL | O_TRUNC)) != 0
+             if (use_heuristic
                  && EMACS_TIME_EQ (modtime, modtime1)
                  && st.st_size == st1.st_size)
                {
@@ -5418,8 +5432,7 @@ See Info node `(elisp)Modification Time' for more details.  */)
       if (EMACS_NSECS (current_buffer->modtime) == NONEXISTENT_MODTIME_NSECS)
        {
          /* make_lisp_time won't work here if time_t is unsigned.  */
-         return list4 (make_number (-1), make_number (65535),
-                       make_number (0), make_number (0));
+         return list4i (-1, 65535, 0, 0);
        }
       return make_number (0);
     }
@@ -5822,7 +5835,7 @@ before any other event (mouse or keypress) is handled.  */)
   if ((NILP (last_nonmenu_event) || CONSP (last_nonmenu_event))
       && use_dialog_box
       && use_file_dialog
-      && have_menus_p ())
+      && window_system_available (SELECTED_FRAME ()))
     return Qt;
 #endif
   return Qnil;
@@ -6056,13 +6069,11 @@ in the buffer; this is the default behavior, because the auto-save
 file is usually more useful if it contains the deleted text.  */);
   Vauto_save_include_big_deletions = Qnil;
 
-#ifdef HAVE_FSYNC
   DEFVAR_BOOL ("write-region-inhibit-fsync", write_region_inhibit_fsync,
               doc: /* Non-nil means don't call fsync in `write-region'.
 This variable affects calls to `write-region' as well as save commands.
 A non-nil value may result in data loss!  */);
   write_region_inhibit_fsync = 0;
-#endif
 
   DEFVAR_BOOL ("delete-by-moving-to-trash", delete_by_moving_to_trash,
                doc: /* Specifies whether to use the system's trash can.