From 2f5e0b1bf7b0ac4f450847db34d599a072020600 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Mon, 14 Nov 2016 09:08:06 -0800 Subject: [PATCH] Improve case-insensitive checks (Bug#24441) * doc/lispref/files.texi (Truenames): Simplify documentation, to avoid giving too much platform-specific information that may not be accurate anyway. * src/fileio.c (file_name_case_insensitive_p): Use pathconf with _PC_CASE_SENSITIVE if _PC_CASE_INSENSITIVE is not available. Otherwise if one approach fails (e.g., with errno == EINVAL), fall back on an alternative rather than returning false. Try skipping the Darwin code, as it (1) no longer seems to be needed and (2) does not seem to match the Apple documentation. Leave in two alternatives conditionally compiled based on DARWIN_OS_CASE_SENSITIVE_FIXME in case (1) or (2) is incorrect. --- doc/lispref/files.texi | 14 ++----- src/fileio.c | 93 +++++++++++++++++++++++++++--------------- 2 files changed, 64 insertions(+), 43 deletions(-) diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi index 70c7177e064..ab0dcae2d9c 100644 --- a/doc/lispref/files.texi +++ b/doc/lispref/files.texi @@ -1144,17 +1144,9 @@ return value is unspecified. 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. +@var{filename} is on a case-insensitive filesystem. On platforms where +this information is not available, this function guesses based on +common practice. @end defun @defun file-in-directory-p file dir diff --git a/src/fileio.c b/src/fileio.c index f3f8f421618..eec3591ff6e 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -2236,13 +2236,10 @@ 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. +/* Return true if FILENAME is on a case-insensitive file system. + Use a runtime test if available. Otherwise, assume the file system + is case-insensitive on Microsoft-based platforms and case-sensitive + elsewhere. FIXME: Mounted filesystems on Posix hosts, like Samba shares or NFS-mounted Windows volumes, might be case-insensitive. Can we @@ -2251,33 +2248,65 @@ internal_delete_file (Lisp_Object filename) 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 +#ifdef _PC_CASE_INSENSITIVE int res = pathconf (filename, _PC_CASE_INSENSITIVE); - if (res < 0) - return 1; - return res > 0; -# else - return 1; + if (0 < res) + return true; + if (res == 0 || errno != EINVAL) + return false; +#elif defined _PC_CASE_SENSITIVE + int res = pathconf (filename, _PC_CASE_SENSITIVE); + if (res == 0) + return true; + if (0 < res || errno != EINVAL) + return false; +#endif + +#ifdef DARWIN_OS + /* It is not clear whether this section is needed. For now, rely on + pathconf and skip this section. If pathconf does not work, + please recompile Emacs with -DDARWIN_OS_CASE_SENSITIVE_FIXME=1 or + -DDARWIN_OS_CASE_SENSITIVE_FIXME=2, and file a bug report saying + whether this fixed your problem. */ +# ifndef DARWIN_OS_CASE_SENSITIVE_FIXME + int DARWIN_OS_CASE_SENSITIVE_FIXME = 0; # 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); + + if (DARWIN_OS_CASE_SENSITIVE_FIXME == 1) + { + /* This is based on developer.apple.com's getattrlist man page. */ + struct attrlist alist = {.volattr = ATTR_VOL_CAPABILITIES}; + struct vol_capabilities_attr_t vcaps; + if (getattrlist (filename, &alist, &vcaps, sizeof vcaps, 0) == 0) + { + if (vcaps.valid[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_CASE_SENSITIVE) + return ! (vcaps.capabilities[VOL_CAPABILITIES_FORMAT] + & VOL_CAP_FMT_CASE_SENSITIVE); + } + else if (errno != EINVAL) + return false; + } + else if (DARWIN_OS_CASE_SENSITIVE_FIXME == 2) + { + /* 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); + } +#endif + +#if defined CYGWIN || defined DOS_NT + return true; #else - return 0; + return false; #endif } @@ -2349,7 +2378,7 @@ This is what happens in interactive use with M-x. */) /* 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))) + 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", -- 2.39.5