Subject: bug#65685: 29.1; Inconsistent behavior of quoted file name "/:~" across platforms

From: Michael Albinus <[email-masked]>
Date: Tue, 03 Oct 2023 13:22:10 +0200

Jim Porter <[email-masked]> writes:

Hi Jim,

> To see this in action, run 'emacs -Q "/:~"' on both GNU/Linux and
> MS-Windows.[1] On GNU/Linux, this opens a dired buffer for the user's
> home directory. On MS-Windows, it opens a buffer for a new file named
> tilde.
>
> According to the Emacs manual:
>
>> ‘/:’ can also prevent ‘~’ from being treated as a special character
>> for a user’s home directory. For example, /:/tmp/~hack refers to a
>> file whose name is ~hack in directory /tmp.
> I'd interpret this to mean that the MS-Windows behavior is
> correct. However, the example doesn't specifically say what should
> happen when the tilde comes immediately after the "/:". On the very
> rare occasions you might need it, you can always spell "a file named
> tilde in the current directory" like "/:./~".
>
> This is relevant to some future Eshell changes I'm considering[2],
> where (I think) I'd like "/:~" to mean "the user's local home
> directory, even when default-directory is remote". In light of that,
> my selfish preference is that we keep the GNU/Linux behavior and
> standardize it across all systems. However, we could standardize the
> MS-Windows behavior instead; I'd then just have to call out the
> different Eshell semantics in the Eshell manual.
>
> [1] You can see this inconsistency with other commands too, like "M-x
> cd RET /:~ RET".
>
> [2] https://lists.gnu.org/archive/html/emacs-devel/2023-08/msg01244.html

I've played a little bit with file-name-non-special. The result is the
appended patch, which expands always "/:~"  to the local home
directory. Could you pls check whether it works for you?

Best regards, Michael.

diff --git a/lisp/files.el b/lisp/files.el
index ddae097f1d1..53314752604 100644
--- a/lisp/files.el
+++ b/lisp/files.el
@@ -8317,13 +8317,12 @@ file-name-non-special
 	;; Get a list of the indices of the args that are file names.
 	(file-arg-indices
 	 (cdr (or (assq operation
-			'(;; The first eight are special because they
+			'(;; The first seven are special because they
 			  ;; return a file name.  We want to include
 			  ;; the /: in the return value.  So just
 			  ;; avoid stripping it in the first place.
                           (abbreviate-file-name)
                           (directory-file-name)
-                          (expand-file-name)
                           (file-name-as-directory)
                           (file-name-directory)
                           (file-name-sans-versions)
@@ -8332,6 +8331,10 @@ file-name-non-special
 	                  ;; `identity' means just return the first
 			  ;; arg not stripped of its quoting.
 			  (substitute-in-file-name identity)
+                          ;; `expand-file-name' shall do special case
+                          ;; for the first argument starting with
+                          ;; "/:~".  (Bug#65685)
+                          (expand-file-name expand-file-name)
 			  ;; `add' means add "/:" to the result.
 			  (file-truename add 0)
                           ;;`insert-file-contents' needs special handling.
@@ -8387,6 +8390,10 @@ file-name-non-special
     (let ((tramp-mode (and tramp-mode (eq method 'local-copy))))
       (pcase method
         ('identity (car arguments))
+        ('expand-file-name
+         (when (string-prefix-p "/:~" (car arguments))
+           (setcar arguments (file-name-unquote (car arguments) t)))
+         (apply operation arguments))
         ('add (file-name-quote (apply operation arguments) t))
         ('buffer-file-name
          (let ((buffer-file-name (file-name-unquote buffer-file-name t)))