Subject: bug#22300: 25.1.50; Dired -- renaming folders/files to CamelCase/UPPERCASE/lowercase.

From: Eli Zaretskii <[email-masked]>
Date: Wed, 06 Jan 2016 17:40:45 +0200

> Date:  Tue, 05 Jan 2016 19:56:35 -0800
> From:  Keith David Bershatsky <[email-masked]>
> Cc:  [email-masked],Drew Adams <[email-masked]>,John Wiegley <[email-masked]>
> 
> Perhaps my misunderstanding stems from a belief that 
> "/Users/HOME/Desktop/foo/FOO" is a bad thing (to have as a result) when 
> `dired-create-files` runs `(setq to (funcall name-constructor from))`.  I was 
> looking at this as a black and white situation -- i.e., `from` is 
> "/Users/HOME/Desktop/FOO"; and, `to` should be "/Users/HOME/Desktop/foo".

We agree about that, I think.  However, what you actually wrote was
that you thought the result of expand-file-name in this case was
incorrect.  But expand-file-name and file-name-nondirectory are
general-purpose functions, they do nothing specific to Dired, and
their result in this case is correct and expected.  It's the code that
calls them that needs to be fixed, see below.

> Because I do not understand the usefulness of "/Users/HOME/Desktop/foo/FOO" 
> (when the user had explicitly entered a new name of "/Users/HOME/Desktop/foo"), 
> I was expecting `(setq to (funcall name-constructor from))` to return 
> "/Users/HOME/Desktop/foo" in this particular situation.

The logic of dired-do-rename in this case is very simple, and it
mimics the logic of the 'mv' command when its argument is a
directory.  When given the command "mv foo bar", it does this:

  . if the target 'bar' is an existing directory, it moves 'foo' to be
    a subdirectory of 'bar', i.e. 'foo' will become 'bar/foo'

  . otherwise, 'foo' is renamed to the new name 'bar'

What happens on case-insensitive filesystems is that when 'foo'
exists, so does 'FOO' and 'Foo' and 'fOO'.  So the above logic decides
that the target is an existing directory, and attempts to move 'foo'
into itself, which fails.  So it's that logic which needs to be
augmented for case-insensitive filesystems.  But the place to augment
it is not where we compute 'foo/bar' (or in this case 'foo/FOO'), the
place is where the logic decides which of the above two alternatives
to take.

This same logic is implemented both in Dired and in rename-file, so it
should be augmented in both of these places.  As we've done for
MS-Windows and MS-DOS.

> If I am understanding you correctly, you believe that 
> "/Users/HOME/Desktop/foo/FOO" is a good thing at this point in the `elisp` code 
> -- to be dealt with further on down when `dired-create-files` does its thing 
> (with the assistance of some C-source code stuff under the hood).

No, the logic that should be augmented is in dired-do-create-files,
and it happens _before_ dired-create-files is called.  By the time
dired-create-files is called it's too late, because
dired-do-create-files already decided which of the above 2
alternatives to use.  Look at the source of dired-do-create-files, and
you will see code in dired-do-create-files that special-cases
MS-Windows to augment the logic.

> From my layman's perspective (i.e., not a programmer by trade), I was thinking 
> that `(setq to (funcall name-constructor from))` should yield the absolute path 
> of what the user explicitly entered as the new name for the folder or file.  I 
> had assumed, perhaps erroneously, that things would go awry rather quickly if 
> the value of "to" was incorrect at the outset of `dired-create-files`.

The logic to which I alluded above takes care of that: it calls
dired-create-files with a different name-constructor function, which
does what you want:

      (dired-create-files
       file-creator operation fn-list
       (if into-dir			; target is a directory
	   ;; This function uses fluid variable target when called
	   ;; inside dired-create-files:
	   (function
	    (lambda (from)
	      (expand-file-name (file-name-nondirectory from) target)))
	 (function (lambda (_from) target)))
       marker-char)

Which name-constructor function is passed to dired-create-files
depends on the value of into-dir.  The special-case code for
MS-Windows takes care of making that value nil, so that the
constructor which produces "incorrect" file name is not called by
dired-create-files.

To summarize: there is already code in dired-do-create-files that will
DTRT in this case for case-insensitive filesystems.  It just needs to
be invoked on OS X when the underlying filesystem is case-insensitive,
and the only problem preventing us from fixing this is that we don't
yet have a way to tell that on OS X as we do on other systems.

IOW, all it takes to fix this is to write an OS X specific predicate
function that would accept a file name and returns and indication
whether that file lives on a case-insensitive filesystem.  Then we
should just use that function in Dired and in rename-file.