Subject: RE: bug#10489: 24.0.92; dired-do-copy may create infinite directory hierarchy

From: "Drew Adams" <[email-masked]>
Date: Fri, 13 Jan 2012 08:56:48 -0800

> > FWIW, this is what Bookmark+ uses, based on some input from Michael:
> > 
> > (defun bmkp-same-file-p (file1 file2)
> >   "Return non-nil if FILE1 and FILE2 name the same file.
> > If either name is not absolute, then it is expanded relative to
> > `default-directory' for the test."
> >   (and (equal (file-remote-p file1) (file-remote-p file2))
> >        (string= (file-truename (expand-file-name file1))
> >                 (file-truename (expand-file-name file2)))))
> 
> Which is obviously wrong on case-insensitive filesystems.

Yes.

If both files are local then we can check whether to ignore case using what
Michael or Stefan suggested (the standard value of
`read-file-name-completion-ignore-case' or, better, some to-be-created constant
for this purpose).

But if both files are remote then the remote system setting(s) would need to be
checked.

Here is a modified version of the above code, which I expect (untested) DTRT for
local files and just punts for remote file names (assumes that they are
case-sensitive).  Perhaps Michael has a good suggestion wrt treating that case.

(defun bmkp-same-file-p (file1 file2)
  "Return non-nil if FILE1 and FILE2 name the same file.
If either name is not absolute, then it is expanded relative to
`default-directory' for the test."
  (let* ((remote1  (bmkp-file-remote-p file1))
         (remote2  (bmkp-file-remote-p file2))
         (ignore-case-p
          (and (not remote1) (not remote2)
               (eval (car (get 'read-file-name-completion-ignore-case
                               'standard-value))))))
    (and (equal remote1 remote2)
         (compare-strings (file-truename (expand-file-name file1))
                          (file-truename (expand-file-name file2))
                          ignore-case-p))))

----

BTW, I notice in `abbreviate-file-name' this code:

;; To fix this right, we need a `file-name-case-sensitive-p'
;; function, but we don't have that yet, so just guess.
(let ((case-fold-search
       (memq system-type '(ms-dos windows-nt darwin cygwin))))

That same value is used to define the standard value of
`read-file-name-completion-ignore-case'.  Presumably it would be better for code
to check that standard value (or some TBD constant, as Stefan suggested) than to
rely on an explicit list of systems here and there in the code.  IOW,
define/maintain the value in a single place.

There are other places in the code where we try to determine case-sensitivity of
file names based on `system-type'.  And we don't always use the same system list
for this. E.g., in `files.el' alone:

We use this in `set-auto-mode':

(memq system-type '(windows-nt cygwin))

And this in `dir-locals-find-file', `make-backup-file-name-1',
`make-auto-save-file-name', `file-relative-name',
`shell-quote-wildcard-pattern', and `file-name-invalid-regexp':

(memq system-type '(windows-nt cygwin ms-dos))

And this in `insert-directory':

(memq system-type '(ms-dos windows-nt))

And this in `buffer-file-numbers-unique':

(memq system-type '(windows-nt))

And again, in `abbreviate-file-name' we use:

(memq system-type '(ms-dos windows-nt darwin cygwin))

Now maybe some of those tests are not for case-sensitivity (I didn't take the
time to look closely).  Or maybe, even when they are, there are good reasons for
such differences.

But perhaps one or more of them should in fact have the same value as
`read-file-name-completion-ignore-case' (or said TBD constant), and would
benefit from being replaced by that?