5

Right now, I have the following in my init

(defun my-edit-file-as-root ()
  "Find file as root"
  (interactive)
  (find-alternate-file (concat "/su:root@localhost:" buffer-file-name)))

(defun my-edit-file-as-root-maybe ()
  "Find file as root if necessary."
  (unless (and buffer-file-name (file-writable-p buffer-file-name))
    (when (y-or-n-p "File is not writable. Open with root? ")
      (my-edit-file-as-root))))

(add-hook 'find-file-hook #'my-edit-file-as-root-maybe)

(a distant child of this code)

Unfortunately, this obviously doesn't work if I'm already using TRAMP (because it blindly concatenates the /su... at the beginning).

I don't know TRAMP internals well enough to determine how to flexibly parse TRAMP filenames and determine what to add to get root access.

How can I modify this to work when already using TRAMP (possibly with multi-hops already in use)?

PythonNut
  • 10,243
  • 2
  • 29
  • 75

2 Answers2

1

Based on the advice of politza and wvxvw:

(defun my-edit-file-as-root ()
  "Find file as root"
  (interactive)
  (let*
    ((sudo (/= (call-process "sudo" nil nil "-n true") 0))
      (file-name
        (if (tramp-tramp-file-p buffer-file-name)
          (with-parsed-tramp-file-name buffer-file-name parsed
            (tramp-make-tramp-file-name
              (if sudo "sudo" "su")
              "root"
              parsed-host
              parsed-localname
              (let ((tramp-postfix-host-format "|")
                     (tramp-prefix-format))
                (tramp-make-tramp-file-name
                  parsed-method
                  parsed-user
                  parsed-host
                  ""
                  parsed-hop))))
          (concat (if sudo
                    "/sudo::"
                    "/su::")
            buffer-file-name))))
    (find-alternate-file file-name)))

This seems to work perfectly in all the cases I tested.

PythonNut
  • 10,243
  • 2
  • 29
  • 75
0

Tramp can jump over multiple hops separated by pipe, here's the relevant documentation: http://www.gnu.org/software/tramp/#Ad_002dhoc-multi_002dhops

wvxvw
  • 11,222
  • 2
  • 30
  • 55
  • 1
    Yes, and I could potentially implement my function as a bunch of regex driven transforms, but that strikes me as being pretty hacky. I'll need to skip back to the last pipe, if there is a pipe, and if that pipe is not escaped. Then I'll need to skip forward to the last colon of that block (might be the first or second colon, depending on the format) if that colon is not escaped etc. If that's the only way, then I'll dive in, but I'm hoping there's some way to get TRAMP to parse its own filenames, and simplify things. – PythonNut May 07 '15 at 19:36
  • `with-parsed-tramp-file-name` ? – politza May 07 '15 at 20:46
  • @PythonNut either as politza said, or you could use `tramp-dissect-file-name` and a bunch of `tramp-file-name-*` functions which will work on the parsed file name. The macro mentioned above uses this function internally. – wvxvw May 07 '15 at 21:57