From 1ea6720e9d2e34c7ae3632068dde4af13718517b Mon Sep 17 00:00:00 2001 From: Ken Brown Date: Sat, 22 Oct 2016 19:10:18 -0400 Subject: [PATCH] Check case-sensitivity when renaming files * src/fileio.c (file_name_case_insensitive_p) (Ffile_name_case_insensitive_p): New functions. (Frename_file): Allow renames that simply change case when the FILE argument is on a case-insensitive filesystem. (Bug#24441) * lisp/dired-aux.el (dired-do-create-files): Use 'file-name-case-insensitive-p' instead of 'system-type' to check for case-insensitivity. (Bug#24441) --- lisp/dired-aux.el | 14 +++++----- src/fileio.c | 78 +++++++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 72 insertions(+), 20 deletions(-) diff --git a/lisp/dired-aux.el b/lisp/dired-aux.el index d25352e..a94c90b 100644 --- a/lisp/dired-aux.el +++ b/lisp/dired-aux.el @@ -1799,13 +1799,15 @@ dired-do-create-files (concat (if dired-one-file op1 operation) " %s to: ") target-dir op-symbol arg rfn-list default)))) (into-dir (cond ((null how-to) - ;; Allow DOS/Windows users to change the letter - ;; case of a directory. If we don't test these - ;; conditions up front, file-directory-p below - ;; will return t because the filesystem is - ;; case-insensitive, and Emacs will try to move + ;; Allow users to change the letter case of + ;; a directory on a case-insensitive + ;; filesystem. If we don't test these + ;; conditions up front, file-directory-p + ;; below will return t on a case-insensitive + ;; filesystem, and Emacs will try to move ;; foo -> foo/foo, which fails. - (if (and (memq system-type '(ms-dos windows-nt cygwin)) + (if (and (file-name-case-insensitive-p + (expand-file-name (car fn-list))) (eq op-symbol 'move) dired-one-file (string= (downcase diff --git a/src/fileio.c b/src/fileio.c index 6026d8e..aa09feb 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -25,6 +25,10 @@ along with GNU Emacs. If not, see . */ #include #include +#ifdef DARWIN_OS +#include +#endif + #ifdef HAVE_PWD_H #include #endif @@ -2231,6 +2235,53 @@ internal_delete_file (Lisp_Object filename) return NILP (tem); } +/* Filesystems are case-sensitive on all supported systems except + MS-Windows, MS-DOS, Cygwin, and OS X. They are always + case-insensitive on the first two, but they may or may not be + case-insensitive on Cygwin and OS X. The following function + attempts to provide a runtime test on those two systems. If the + test is not conclusive, we assume case-insensitivity on Cygwin and + case-sensitivity on OS X. */ +static bool +file_name_case_insensitive_p (const char *filename) +{ +#ifdef DOS_NT + return 1; +#elif defined CYGWIN +/* As of Cygwin-2.6.1, pathconf supports _PC_CASE_INSENSITIVE. */ +# ifdef _PC_CASE_INSENSITIVE + return pathconf (filename, _PC_CASE_INSENSITIVE) > 0; +# else + return 1; +# endif +#elif defined DARWIN_OS + /* The following is based on + http://lists.apple.com/archives/darwin-dev/2007/Apr/msg00010.html. */ + struct attrlist alist; + unsigned char buffer[sizeof (vol_capabilities_attr_t) + sizeof (size_t)]; + + memset (&alist, 0, sizeof (alist)); + alist.volattr = ATTR_VOL_CAPABILITIES; + if (getattrlist (filename, &alist, buffer, sizeof (buffer), 0) + || !(alist.volattr & ATTR_VOL_CAPABILITIES)) + return 0; + vol_capabilities_attr_t *vcaps = buffer; + return !(vcaps->capabilities[0] & VOL_CAP_FMT_CASE_SENSITIVE); +#else + return 0; +#endif +} + +DEFUN ("file-name-case-insensitive-p", Ffile_name_case_insensitive_p, + Sfile_name_case_insensitive_p, 1, 1, 0, + doc: /* Return t if file FILENAME is on a case-insensitive filesystem. +The arg must be a string. */) + (Lisp_Object filename) +{ + CHECK_STRING (filename); + return file_name_case_insensitive_p (SSDATA (filename)) ? Qt : Qnil; +} + DEFUN ("rename-file", Frename_file, Srename_file, 2, 3, "fRename file: \nGRename %s to file: \np", doc: /* Rename FILE as NEWNAME. Both args must be strings. @@ -2250,12 +2301,11 @@ This is what happens in interactive use with M-x. */) file = Fexpand_file_name (file, Qnil); if ((!NILP (Ffile_directory_p (newname))) -#ifdef DOS_NT - /* If the file names are identical but for the case, - don't attempt to move directory to itself. */ - && (NILP (Fstring_equal (Fdowncase (file), Fdowncase (newname)))) -#endif - ) + /* If the filesystem is case-insensitive and the file names are + identical but for the case, don't attempt to move directory + to itself. */ + && (NILP (Ffile_name_case_insensitive_p (file)) + || NILP (Fstring_equal (Fdowncase (file), Fdowncase (newname))))) { Lisp_Object fname = (NILP (Ffile_directory_p (file)) ? file : Fdirectory_file_name (file)); @@ -2276,14 +2326,12 @@ This is what happens in interactive use with M-x. */) encoded_file = ENCODE_FILE (file); encoded_newname = ENCODE_FILE (newname); -#ifdef DOS_NT - /* If the file names are identical but for the case, don't ask for - confirmation: they simply want to change the letter-case of the - file name. */ - if (NILP (Fstring_equal (Fdowncase (file), Fdowncase (newname)))) -#endif - if (NILP (ok_if_already_exists) - || INTEGERP (ok_if_already_exists)) + /* If the filesystem is case-insensitive and the file names are + identical but for the case, don't ask for confirmation: they + simply want to change the letter-case of the file name. */ + if ((NILP (Ffile_name_case_insensitive_p (file)) + || NILP (Fstring_equal (Fdowncase (file), Fdowncase (newname)))) + && ((NILP (ok_if_already_exists) || INTEGERP (ok_if_already_exists)))) barf_or_query_if_file_exists (newname, false, "rename to it", INTEGERP (ok_if_already_exists), false); if (rename (SSDATA (encoded_file), SSDATA (encoded_newname)) < 0) @@ -5836,6 +5884,7 @@ syms_of_fileio (void) DEFSYM (Qmake_directory_internal, "make-directory-internal"); DEFSYM (Qmake_directory, "make-directory"); DEFSYM (Qdelete_file, "delete-file"); + DEFSYM (Qfile_name_case_insensitive_p, "file-name-case-insensitive-p"); DEFSYM (Qrename_file, "rename-file"); DEFSYM (Qadd_name_to_file, "add-name-to-file"); DEFSYM (Qmake_symbolic_link, "make-symbolic-link"); @@ -6093,6 +6142,7 @@ This includes interactive calls to `delete-file' and defsubr (&Smake_directory_internal); defsubr (&Sdelete_directory_internal); defsubr (&Sdelete_file); + defsubr (&Sfile_name_case_insensitive_p); defsubr (&Srename_file); defsubr (&Sadd_name_to_file); defsubr (&Smake_symbolic_link); -- 2.8.3