Commit 2809012c authored by Ken Brown's avatar Ken Brown
Browse files

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)

* doc/lispref/files.texi (Truenames): Document
'file-name-case-insensitive-p'.
parent 462804da
......@@ -1140,6 +1140,23 @@ appropriate manner. If @var{file1} or @var{file2} does not exist, the
return value is unspecified.
@end defun
@defun file-name-case-insensitive-p filename
Sometimes file names or their parts need to be compared as strings, in
which case it's important to know whether the underlying filesystem is
case-insensitive. This function returns @code{t} if file
@var{filename} is on a case-insensitive filesystem. It always returns
@code{t} on MS-DOS and MS-Windows. On Cygwin and Mac OS X,
filesystems may or may not be case-insensitive, and the function tries
to determine case-sensitivity by a runtime test. If the test is
inconclusive, the function returns @code{t} on Cygwin and @code{nil}
on Mac OS X.
Currently this function always returns @code{nil} on platforms other
than MS-DOS, MS-Windows, Cygwin, and Mac OS X. It does not detect
case-insensitivity of mounted filesystems, such as Samba shares or
NFS-mounted Windows volumes.
@end defun
@defun file-in-directory-p file dir
This function returns @code{t} if @var{file} is a file in directory
@var{dir}, or in a subdirectory of @var{dir}. It also returns
......
......@@ -61,6 +61,10 @@ affected by this, as SGI stopped supporting IRIX in December 2013.
* Changes in Emacs 26.1
+++
** The new function 'file-name-case-insensitive-p' tests whether a
given file is on a case-insensitive filesystem.
+++
** The new user variable 'electric-quote-chars' provides a list
of curved quotes for 'electric-quote-mode', allowing user to choose
......
......@@ -1801,13 +1801,14 @@ Optional arg HOW-TO determines how to treat the target.
(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 (car fn-list))
(eq op-symbol 'move)
dired-one-file
(string= (downcase
......
......@@ -25,6 +25,10 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#include <sys/stat.h>
#include <unistd.h>
#ifdef DARWIN_OS
#include <sys/attr.h>
#endif
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif
......@@ -2232,6 +2236,72 @@ internal_delete_file (Lisp_Object filename)
return NILP (tem);
}
/* Filesystems are case-sensitive on all supported systems except
MS-Windows, MS-DOS, Cygwin, and Mac 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 Mac OS X.
FIXME: Mounted filesystems on Posix hosts, like Samba shares or
NFS-mounted Windows volumes, might be case-insensitive. Can we
detect this? */
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
int res = pathconf (filename, _PC_CASE_INSENSITIVE);
if (res < 0)
return 1;
return res > 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)
{
Lisp_Object handler;
CHECK_STRING (filename);
filename = Fexpand_file_name (filename, Qnil);
/* If the file name has special constructs in it,
call the corresponding file handler. */
handler = Ffind_file_name_handler (filename, Qfile_name_case_insensitive_p);
if (!NILP (handler))
return call2 (handler, Qfile_name_case_insensitive_p, filename);
filename = ENCODE_FILE (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.
......@@ -2251,12 +2321,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));
......@@ -2277,14 +2346,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 ((!(file_name_case_insensitive_p (SSDATA (encoded_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 +5903,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");
......@@ -6099,6 +6167,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);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment