Subject: bug#60722: 30.0.50; [PATCH] Using Tramp to sudo in Eshell doesn't change prompt sigil

From: Jim Porter <[email-masked]>
Date: Tue, 10 Jan 2023 15:50:03 -0800

X-Debbugs-Cc: [email-masked]

Starting with "sudo emacs -Q -f eshell", notice that the prompt looks like:

   /home/user #

Now start "emacs -Q -f eshell":

   ~ $ cd /sudo::
   /sudo:root@huginn:~ $

Notice that the prompt sigil is still "$" instead of "#". I think it 
would be better to indicate that you have a root shell by using "#" here.

Attached is a patch to do this. It adds a new function, 
'user-uid-for-file', which is aware of file name handlers. Then, Tramp 
adds the appropriate handler. Now, Eshell can use that function and we 
get the prompt sigil we expect.

From 2a9f33fa87d0825ef2f3a95f602e4f17bfb19b76 Mon Sep 17 00:00:00 2001
From: Jim Porter <[email-masked]>
Date: Tue, 10 Jan 2023 15:35:18 -0800
Subject: [PATCH] Add 'user-uid-for-file' to get the effective UID for remote
 files

In particular, this lets Eshell show a "#" root prompt sigil when the
user has sudo'ed via "cd /sudo::".

* lisp/simple.el (user-uid-for-file): New function.

* lisp/net/tramp.el (tramp-file-name-for-operation): Add
'user-uid-for-file'.
(tramp-handle-user-uid-for-file): New function.

* lisp/net/tramp-adb.el (tramp-adb-file-name-handler-alist):
* lisp/net/tramp-gvfs.el (tramp-gvfs-file-name-handler-alist):
* lisp/net/tramp-sh.el (tramp-sh-file-name-handler-alist):
* lisp/net/tramp-sudoedit.el (tramp-sudoedit-file-name-handler-alist):
Add 'user-uid-for-file'.

* lisp/net/tramp-archive.el (tramp-archive-file-name-handler-alist):
* lisp/net/tramp-crypt.el (tramp-crypt-file-name-handler-alist):
* lisp/net/tramp-rclone.el (tramp-rclone-file-name-handler-alist):
* lisp/net/tramp-smb.el (tramp-smb-file-name-handler-alist):
* lisp/net/tramp-sshfs.el (tramp-sshfs-file-name-handler-alist):
Add comment about 'user-uid-for-file'.

* lisp/eshell/em-prompt.el (eshell-prompt-function): Use
'user-uid-for-file'.

* doc/lispref/files.texi (Magic File Names): Mention
'user-uid-for-file'.

* etc/NEWS: Announce 'user-uid-for-file'.
---
 doc/lispref/files.texi     | 2 ++
 etc/NEWS                   | 4 ++++
 lisp/eshell/em-prompt.el   | 5 +++--
 lisp/net/tramp-adb.el      | 1 +
 lisp/net/tramp-archive.el  | 1 +
 lisp/net/tramp-crypt.el    | 1 +
 lisp/net/tramp-gvfs.el     | 1 +
 lisp/net/tramp-rclone.el   | 1 +
 lisp/net/tramp-sh.el       | 1 +
 lisp/net/tramp-smb.el      | 1 +
 lisp/net/tramp-sshfs.el    | 1 +
 lisp/net/tramp-sudoedit.el | 1 +
 lisp/net/tramp.el          | 6 ++++++
 lisp/simple.el             | 9 +++++++++
 14 files changed, 33 insertions(+), 2 deletions(-)

diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi
index 91643530f7f..6e42086ac66 100644
--- a/doc/lispref/files.texi
+++ b/doc/lispref/files.texi
@@ -3407,6 +3407,7 @@ Magic File Names
 @code{temporary-file-directory},
 @code{unhandled-file-name-directory},
 @code{unlock-file},
+@code{user-uid-for-file},
 @code{vc-registered},
 @code{verify-visited-file-modtime},@*
 @code{write-region}.
@@ -3468,6 +3469,7 @@ Magic File Names
 @code{temporary-file-directory},
 @code{unhandled-file-name-directory},
 @code{unlock-file},
+@code{user-uid-for-file},
 @code{vc-regis@discretionary{}{}{}tered},
 @code{verify-visited-file-modtime},
 @code{write-region}.
diff --git a/etc/NEWS b/etc/NEWS
index 690e9c3faa9..4d903d1faae 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -190,6 +190,10 @@ compared reliably at all.
 This warning can be suppressed using 'with-suppressed-warnings' with
 the warning name 'suspicious'.
 
+** New function 'user-uid-for-file'.
+This function is like 'user-uid', but is aware of file name handlers,
+so it will return the remote UID for remote files.
+
 
 * Changes in Emacs 30.1 on Non-Free Operating Systems
 
diff --git a/lisp/eshell/em-prompt.el b/lisp/eshell/em-prompt.el
index 575b5a595f1..bc6beaf3cde 100644
--- a/lisp/eshell/em-prompt.el
+++ b/lisp/eshell/em-prompt.el
@@ -49,8 +49,9 @@ eshell-prompt-load-hook
 
 (defcustom eshell-prompt-function
   (lambda ()
-    (concat (abbreviate-file-name (eshell/pwd))
-            (if (= (user-uid) 0) " # " " $ ")))
+    (let ((cwd (eshell/pwd)))
+      (concat (abbreviate-file-name cwd)
+              (if (= (user-uid-for-file cwd) 0) " # " " $ "))))
   "A function that returns the Eshell prompt string.
 Make sure to update `eshell-prompt-regexp' so that it will match your
 prompt."
diff --git a/lisp/net/tramp-adb.el b/lisp/net/tramp-adb.el
index 619d29bb4d6..8de47045b5e 100644
--- a/lisp/net/tramp-adb.el
+++ b/lisp/net/tramp-adb.el
@@ -188,6 +188,7 @@ tramp-adb-file-name-handler-alist
     (tramp-set-file-uid-gid . ignore)
     (unhandled-file-name-directory . ignore)
     (unlock-file . tramp-handle-unlock-file)
+    (user-uid-for-file . tramp-handle-user-uid-for-file)
     (vc-registered . ignore)
     (verify-visited-file-modtime . tramp-handle-verify-visited-file-modtime)
     (write-region . tramp-adb-handle-write-region))
diff --git a/lisp/net/tramp-archive.el b/lisp/net/tramp-archive.el
index a2add1ed73a..08e4a776cc3 100644
--- a/lisp/net/tramp-archive.el
+++ b/lisp/net/tramp-archive.el
@@ -300,6 +300,7 @@ tramp-archive-file-name-handler-alist
     (tramp-set-file-uid-gid . ignore)
     (unhandled-file-name-directory . ignore)
     (unlock-file . ignore)
+    ;; `user-uid-for-file' performed by default handler.
     (vc-registered . ignore)
     (verify-visited-file-modtime . tramp-handle-verify-visited-file-modtime)
     (write-region . tramp-archive-handle-not-implemented))
diff --git a/lisp/net/tramp-crypt.el b/lisp/net/tramp-crypt.el
index 61d1c529619..1192bc75762 100644
--- a/lisp/net/tramp-crypt.el
+++ b/lisp/net/tramp-crypt.el
@@ -239,6 +239,7 @@ tramp-crypt-file-name-handler-alist
     (tramp-set-file-uid-gid . tramp-crypt-handle-set-file-uid-gid)
     (unhandled-file-name-directory . ignore)
     (unlock-file . tramp-crypt-handle-unlock-file)
+    ;; `user-uid-for-file' performed by default handler.
     (vc-registered . ignore)
     (verify-visited-file-modtime . tramp-handle-verify-visited-file-modtime)
     (write-region . tramp-handle-write-region))
diff --git a/lisp/net/tramp-gvfs.el b/lisp/net/tramp-gvfs.el
index bb81b3eb66c..177a3fb7ebe 100644
--- a/lisp/net/tramp-gvfs.el
+++ b/lisp/net/tramp-gvfs.el
@@ -833,6 +833,7 @@ tramp-gvfs-file-name-handler-alist
     (tramp-set-file-uid-gid . tramp-gvfs-handle-set-file-uid-gid)
     (unhandled-file-name-directory . ignore)
     (unlock-file . tramp-handle-unlock-file)
+    (user-uid-for-file . tramp-handle-user-uid-for-file)
     (vc-registered . ignore)
     (verify-visited-file-modtime . tramp-handle-verify-visited-file-modtime)
     (write-region . tramp-handle-write-region))
diff --git a/lisp/net/tramp-rclone.el b/lisp/net/tramp-rclone.el
index 4018fa3aa29..3ac18a00278 100644
--- a/lisp/net/tramp-rclone.el
+++ b/lisp/net/tramp-rclone.el
@@ -153,6 +153,7 @@ tramp-rclone-file-name-handler-alist
     (tramp-set-file-uid-gid . ignore)
     (unhandled-file-name-directory . ignore)
     (unlock-file . tramp-handle-unlock-file)
+    ;; `user-uid-for-file' performed by default handler.
     (vc-registered . ignore)
     (verify-visited-file-modtime . tramp-handle-verify-visited-file-modtime)
     (write-region . tramp-handle-write-region))
diff --git a/lisp/net/tramp-sh.el b/lisp/net/tramp-sh.el
index fbdd40dd1d2..00d7324a4d0 100644
--- a/lisp/net/tramp-sh.el
+++ b/lisp/net/tramp-sh.el
@@ -1121,6 +1121,7 @@ tramp-sh-file-name-handler-alist
     (tramp-set-file-uid-gid . tramp-sh-handle-set-file-uid-gid)
     (unhandled-file-name-directory . ignore)
     (unlock-file . tramp-handle-unlock-file)
+    (user-uid-for-file . tramp-handle-user-uid-for-file)
     (vc-registered . tramp-sh-handle-vc-registered)
     (verify-visited-file-modtime . tramp-sh-handle-verify-visited-file-modtime)
     (write-region . tramp-sh-handle-write-region))
diff --git a/lisp/net/tramp-smb.el b/lisp/net/tramp-smb.el
index f31865d498d..10532c739c6 100644
--- a/lisp/net/tramp-smb.el
+++ b/lisp/net/tramp-smb.el
@@ -304,6 +304,7 @@ tramp-smb-file-name-handler-alist
     (tramp-set-file-uid-gid . ignore)
     (unhandled-file-name-directory . ignore)
     (unlock-file . tramp-handle-unlock-file)
+    ;; `user-uid-for-file' performed by default handler.
     (vc-registered . ignore)
     (verify-visited-file-modtime . tramp-handle-verify-visited-file-modtime)
     (write-region . tramp-smb-handle-write-region))
diff --git a/lisp/net/tramp-sshfs.el b/lisp/net/tramp-sshfs.el
index 27b2854e451..8118a8cfa25 100644
--- a/lisp/net/tramp-sshfs.el
+++ b/lisp/net/tramp-sshfs.el
@@ -159,6 +159,7 @@ tramp-sshfs-file-name-handler-alist
     (tramp-set-file-uid-gid . ignore)
     (unhandled-file-name-directory . ignore)
     (unlock-file . tramp-handle-unlock-file)
+    ;; `user-uid-for-file' performed by default handler.
     (vc-registered . ignore)
     (verify-visited-file-modtime . tramp-handle-verify-visited-file-modtime)
     (write-region . tramp-sshfs-handle-write-region))
diff --git a/lisp/net/tramp-sudoedit.el b/lisp/net/tramp-sudoedit.el
index c4e1d32f525..af7d5ba09ca 100644
--- a/lisp/net/tramp-sudoedit.el
+++ b/lisp/net/tramp-sudoedit.el
@@ -149,6 +149,7 @@ tramp-sudoedit-file-name-handler-alist
     (tramp-set-file-uid-gid . tramp-sudoedit-handle-set-file-uid-gid)
     (unhandled-file-name-directory . ignore)
     (unlock-file . tramp-handle-unlock-file)
+    (user-uid-for-file . tramp-handle-user-uid-for-file)
     (vc-registered . ignore)
     (verify-visited-file-modtime . tramp-handle-verify-visited-file-modtime)
     (write-region . tramp-handle-write-region))
diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el
index 4bf0fdefc0b..a93f4bda605 100644
--- a/lisp/net/tramp.el
+++ b/lisp/net/tramp.el
@@ -2589,6 +2589,8 @@ tramp-file-name-for-operation
 	      file-locked-p lock-file make-lock-file-name unlock-file
 	      ;; Emacs 29+ only.
 	      abbreviate-file-name
+	      ;; Emacs 30+ only.
+	      user-uid-for-file
 	      ;; Tramp internal magic file name function.
 	      tramp-set-file-uid-gid))
     (if (file-name-absolute-p (nth 0 args))
@@ -3690,6 +3692,10 @@ tramp-handle-abbreviate-file-name
 	 vec (concat "~" (substring filename (match-beginning 1))))
       (tramp-make-tramp-file-name (tramp-dissect-file-name filename)))))
 
+(defun tramp-handle-user-uid-for-file (filename)
+  "Like `user-uid' for Tramp files."
+  (tramp-get-remote-uid (tramp-dissect-file-name filename) 'integer))
+
 (defun tramp-handle-access-file (filename string)
   "Like `access-file' for Tramp files."
   (setq filename (file-truename filename))
diff --git a/lisp/simple.el b/lisp/simple.el
index 690968ca938..07dd3196a3e 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -4664,6 +4664,15 @@ shell-command--same-buffer-confirm
                      action))
       (user-error "Shell command in progress"))))
 
+(defun user-uid-for-file (filename)
+  "Return the effective uid for FILENAME.
+For local files, this is equivalent to `user-uid' (which see),
+but for remote files, this returns the effective uid for that
+remote connection."
+  (if-let ((handler (find-file-name-handler filename 'user-uid-for-file)))
+      (funcall handler 'user-uid-for-file filename)
+    (user-uid)))
+
 (defun max-mini-window-lines (&optional frame)
   "Compute maximum number of lines for echo area in FRAME.
 As defined by `max-mini-window-height'.  FRAME defaults to the
-- 
2.25.1