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
#include <sys/stat.h>
#include <unistd.h>
+#ifdef DARWIN_OS
+#include <sys/attr.h>
+#endif
+
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif
return NILP (tem);
}
\f
+/* 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.
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));
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)
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");
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);