diff options
| author | Kai Großjohann | 2002-06-17 11:47:23 +0000 |
|---|---|---|
| committer | Kai Großjohann | 2002-06-17 11:47:23 +0000 |
| commit | fb7933a38932ce8832a40507e8e10bd61e27eaee (patch) | |
| tree | 5d089864c0a49b6ad43844ad3879e74d20f42801 | |
| parent | d591a83451f2a117a2f6bdc95fc0b401c6b69cd0 (diff) | |
| download | emacs-fb7933a38932ce8832a40507e8e10bd61e27eaee.tar.gz emacs-fb7933a38932ce8832a40507e8e10bd61e27eaee.zip | |
*** empty log message ***
| -rw-r--r-- | lisp/net/tramp-vc.el | 480 | ||||
| -rw-r--r-- | lisp/net/tramp.el | 5152 | ||||
| -rw-r--r-- | man/tramp.texi | 1626 |
3 files changed, 7258 insertions, 0 deletions
diff --git a/lisp/net/tramp-vc.el b/lisp/net/tramp-vc.el new file mode 100644 index 00000000000..b8b0a1eb019 --- /dev/null +++ b/lisp/net/tramp-vc.el | |||
| @@ -0,0 +1,480 @@ | |||
| 1 | ;;; tramp-vc.el --- Version control integration for TRAMP.el | ||
| 2 | |||
| 3 | ;; Copyright (C) 2000 by Free Software Foundation, Inc. | ||
| 4 | |||
| 5 | ;; Author: Daniel Pittman <daniel@danann.net> | ||
| 6 | ;; Keywords: comm, processes | ||
| 7 | |||
| 8 | ;; This file is part of GNU Emacs. | ||
| 9 | |||
| 10 | ;; GNU Emacs is free software; you can redistribute it and/or modify | ||
| 11 | ;; it under the terms of the GNU General Public License as published by | ||
| 12 | ;; the Free Software Foundation; either version 2, or (at your option) | ||
| 13 | ;; any later version. | ||
| 14 | |||
| 15 | ;; GNU Emacs is distributed in the hope that it will be useful, | ||
| 16 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 17 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 18 | ;; GNU General Public License for more details. | ||
| 19 | |||
| 20 | ;; You should have received a copy of the GNU General Public License | ||
| 21 | ;; along with GNU Emacs; see the file COPYING. If not, write to the | ||
| 22 | ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||
| 23 | ;; Boston, MA 02111-1307, USA. | ||
| 24 | |||
| 25 | ;;; Commentary: | ||
| 26 | |||
| 27 | ;; See the main module, 'tramp.el' for discussion of the purpose of TRAMP. | ||
| 28 | ;; This module provides integration between remote files accessed by TRAMP and | ||
| 29 | ;; the Emacs version control system. | ||
| 30 | |||
| 31 | ;;; Code: | ||
| 32 | |||
| 33 | (eval-when-compile | ||
| 34 | (require 'cl)) | ||
| 35 | (require 'vc) | ||
| 36 | ;; Old VC defines vc-rcs-release in vc.el, new VC requires extra module. | ||
| 37 | (unless (boundp 'vc-rcs-release) | ||
| 38 | (require 'vc-rcs)) | ||
| 39 | (require 'tramp) | ||
| 40 | |||
| 41 | ;; -- vc -- | ||
| 42 | |||
| 43 | ;; This used to blow away the file-name-handler-alist and reinstall | ||
| 44 | ;; TRAMP into it. This was intended to let VC work remotely. It didn't, | ||
| 45 | ;; at least not in my XEmacs 21.2 install. | ||
| 46 | ;; | ||
| 47 | ;; In any case, tramp-run-real-handler now deals correctly with disabling | ||
| 48 | ;; the things that should be, making this a no-op. | ||
| 49 | ;; | ||
| 50 | ;; I have removed it from the tramp-file-name-handler-alist because the | ||
| 51 | ;; shortened version does nothing. This is for reference only now. | ||
| 52 | ;; | ||
| 53 | ;; Daniel Pittman <daniel@danann.net> | ||
| 54 | ;; | ||
| 55 | ;; (defun tramp-handle-vc-registered (file) | ||
| 56 | ;; "Like `vc-registered' for tramp files." | ||
| 57 | ;; (tramp-run-real-handler 'vc-registered (list file))) | ||
| 58 | |||
| 59 | ;; `vc-do-command' | ||
| 60 | ;; This function does not deal well with remote files, so we define | ||
| 61 | ;; our own version and make a backup of the original function and | ||
| 62 | ;; call our version for tramp files and the original version for | ||
| 63 | ;; normal files. | ||
| 64 | |||
| 65 | ;; The following function is pretty much copied from vc.el, but | ||
| 66 | ;; the part that actually executes a command is changed. | ||
| 67 | ;; CCC: this probably works for Emacs 21, too. | ||
| 68 | (defun tramp-vc-do-command (buffer okstatus command file last &rest flags) | ||
| 69 | "Like `vc-do-command' but invoked for tramp files. | ||
| 70 | See `vc-do-command' for more information." | ||
| 71 | (save-match-data | ||
| 72 | (and file (setq file (tramp-handle-expand-file-name file))) | ||
| 73 | (if (not buffer) (setq buffer "*vc*")) | ||
| 74 | (if vc-command-messages | ||
| 75 | (message "Running `%s' on `%s'..." command file)) | ||
| 76 | (let ((obuf (current-buffer)) (camefrom (current-buffer)) | ||
| 77 | (squeezed nil) | ||
| 78 | (olddir default-directory) | ||
| 79 | vc-file status) | ||
| 80 | (let* ((v (tramp-dissect-file-name (tramp-handle-expand-file-name file))) | ||
| 81 | (multi-method (tramp-file-name-multi-method v)) | ||
| 82 | (method (tramp-file-name-method v)) | ||
| 83 | (user (tramp-file-name-user v)) | ||
| 84 | (host (tramp-file-name-host v)) | ||
| 85 | (path (tramp-file-name-path v))) | ||
| 86 | (set-buffer (get-buffer-create buffer)) | ||
| 87 | (set (make-local-variable 'vc-parent-buffer) camefrom) | ||
| 88 | (set (make-local-variable 'vc-parent-buffer-name) | ||
| 89 | (concat " from " (buffer-name camefrom))) | ||
| 90 | (setq default-directory olddir) | ||
| 91 | |||
| 92 | (erase-buffer) | ||
| 93 | |||
| 94 | (mapcar | ||
| 95 | (function | ||
| 96 | (lambda (s) (and s (setq squeezed (append squeezed (list s)))))) | ||
| 97 | flags) | ||
| 98 | (if (and (eq last 'MASTER) file | ||
| 99 | (setq vc-file (vc-name file))) | ||
| 100 | (setq squeezed | ||
| 101 | (append squeezed | ||
| 102 | (list (tramp-file-name-path | ||
| 103 | (tramp-dissect-file-name vc-file)))))) | ||
| 104 | (if (and file (eq last 'WORKFILE)) | ||
| 105 | (progn | ||
| 106 | (let* ((pwd (expand-file-name default-directory)) | ||
| 107 | (preflen (length pwd))) | ||
| 108 | (if (string= (substring file 0 preflen) pwd) | ||
| 109 | (setq file (substring file preflen)))) | ||
| 110 | (setq squeezed (append squeezed (list file))))) | ||
| 111 | ;; Unless we (save-window-excursion) the layout of windows in | ||
| 112 | ;; the current frame changes. This is painful, at best. | ||
| 113 | ;; | ||
| 114 | ;; As a point of note, (save-excursion) is still here only because | ||
| 115 | ;; it preserves (point) in the current buffer. (save-window-excursion) | ||
| 116 | ;; does not, at least under XEmacs 21.2. | ||
| 117 | ;; | ||
| 118 | ;; I trust that the FSF support this as well. I can't find useful | ||
| 119 | ;; documentation to check :( | ||
| 120 | ;; | ||
| 121 | ;; Daniel Pittman <daniel@danann.net> | ||
| 122 | (save-excursion | ||
| 123 | (save-window-excursion | ||
| 124 | ;; Actually execute remote command | ||
| 125 | (tramp-handle-shell-command | ||
| 126 | (mapconcat 'tramp-shell-quote-argument | ||
| 127 | (cons command squeezed) " ") t) | ||
| 128 | ;;(tramp-wait-for-output) | ||
| 129 | ;; Get status from command | ||
| 130 | (tramp-send-command multi-method method user host "echo $?") | ||
| 131 | (tramp-wait-for-output) | ||
| 132 | ;; Make sure to get status from last line of output. | ||
| 133 | (goto-char (point-max)) (forward-line -1) | ||
| 134 | (setq status (read (current-buffer))) | ||
| 135 | (message "Command %s returned status %d." command status))) | ||
| 136 | (goto-char (point-max)) | ||
| 137 | (set-buffer-modified-p nil) | ||
| 138 | (forward-line -1) | ||
| 139 | (if (or (not (integerp status)) (and okstatus (< okstatus status))) | ||
| 140 | (progn | ||
| 141 | (pop-to-buffer buffer) | ||
| 142 | (goto-char (point-min)) | ||
| 143 | (shrink-window-if-larger-than-buffer) | ||
| 144 | (error "Running `%s'...FAILED (%s)" command | ||
| 145 | (if (integerp status) | ||
| 146 | (format "status %d" status) | ||
| 147 | status)) | ||
| 148 | ) | ||
| 149 | (if vc-command-messages | ||
| 150 | (message "Running %s...OK" command)) | ||
| 151 | ) | ||
| 152 | (set-buffer obuf) | ||
| 153 | status)) | ||
| 154 | )) | ||
| 155 | |||
| 156 | ;; Following code snarfed from Emacs 21 vc.el and slightly tweaked. | ||
| 157 | (defun tramp-vc-do-command-new (buffer okstatus command file &rest flags) | ||
| 158 | "Like `vc-do-command' but for TRAMP files. | ||
| 159 | This function is for the new VC which comes with Emacs 21. | ||
| 160 | Since TRAMP doesn't do async commands yet, this function doesn't, either." | ||
| 161 | (and file (setq file (expand-file-name file))) | ||
| 162 | (if vc-command-messages | ||
| 163 | (message "Running %s on %s..." command file)) | ||
| 164 | (save-current-buffer | ||
| 165 | (unless (eq buffer t) (vc-setup-buffer buffer)) | ||
| 166 | (let ((squeezed nil) | ||
| 167 | (inhibit-read-only t) | ||
| 168 | (status 0)) | ||
| 169 | (let* ((v (when file (tramp-dissect-file-name file))) | ||
| 170 | (multi-method (when file (tramp-file-name-multi-method v))) | ||
| 171 | (method (when file (tramp-file-name-method v))) | ||
| 172 | (user (when file (tramp-file-name-user v))) | ||
| 173 | (host (when file (tramp-file-name-host v))) | ||
| 174 | (path (when file (tramp-file-name-path v)))) | ||
| 175 | (setq squeezed (delq nil (copy-sequence flags))) | ||
| 176 | (when file | ||
| 177 | (setq squeezed (append squeezed (list path)))) | ||
| 178 | (let ((w32-quote-process-args t)) | ||
| 179 | (when (eq okstatus 'async) | ||
| 180 | (message "Tramp doesn't do async commands, running synchronously.")) | ||
| 181 | (setq status (tramp-handle-shell-command | ||
| 182 | (mapconcat 'tramp-shell-quote-argument | ||
| 183 | (cons command squeezed) " ") t)) | ||
| 184 | (when (or (not (integerp status)) (and okstatus (< okstatus status))) | ||
| 185 | (pop-to-buffer (current-buffer)) | ||
| 186 | (goto-char (point-min)) | ||
| 187 | (shrink-window-if-larger-than-buffer) | ||
| 188 | (error "Running %s...FAILED (%s)" command | ||
| 189 | (if (integerp status) (format "status %d" status) status)))) | ||
| 190 | (if vc-command-messages | ||
| 191 | (message "Running %s...OK" command)) | ||
| 192 | (vc-exec-after | ||
| 193 | `(run-hook-with-args | ||
| 194 | 'vc-post-command-functions ',command ',path ',flags)) | ||
| 195 | status)))) | ||
| 196 | |||
| 197 | |||
| 198 | ;; The context for a VC command is the current buffer. | ||
| 199 | ;; That makes a test on the buffers file more reliable than a test on the | ||
| 200 | ;; arguments. | ||
| 201 | ;; This is needed to handle remote VC correctly - else we test against the | ||
| 202 | ;; local VC system and get things wrong... | ||
| 203 | ;; Daniel Pittman <daniel@danann.net> | ||
| 204 | ;;-(if (fboundp 'vc-call-backend) | ||
| 205 | ;;- () ;; This is the new VC for which we don't have an appropriate advice yet | ||
| 206 | (if (fboundp 'vc-call-backend) | ||
| 207 | (defadvice vc-do-command | ||
| 208 | (around tramp-advice-vc-do-command | ||
| 209 | (buffer okstatus command file &rest flags) | ||
| 210 | activate) | ||
| 211 | "Invoke tramp-vc-do-command for tramp files." | ||
| 212 | (let ((file (symbol-value 'file))) ;pacify byte-compiler | ||
| 213 | (if (or (and (stringp file) (tramp-tramp-file-p file)) | ||
| 214 | (and (buffer-file-name) (tramp-tramp-file-p (buffer-file-name)))) | ||
| 215 | (setq ad-return-value | ||
| 216 | (apply 'tramp-vc-do-command-new buffer okstatus command | ||
| 217 | file ;(or file (buffer-file-name)) | ||
| 218 | flags)) | ||
| 219 | ad-do-it))) | ||
| 220 | (defadvice vc-do-command | ||
| 221 | (around tramp-advice-vc-do-command | ||
| 222 | (buffer okstatus command file last &rest flags) | ||
| 223 | activate) | ||
| 224 | "Invoke tramp-vc-do-command for tramp files." | ||
| 225 | (let ((file (symbol-value 'file))) ;pacify byte-compiler | ||
| 226 | (if (or (and (stringp file) (tramp-tramp-file-p file)) | ||
| 227 | (and (buffer-file-name) (tramp-tramp-file-p (buffer-file-name)))) | ||
| 228 | (setq ad-return-value | ||
| 229 | (apply 'tramp-vc-do-command buffer okstatus command | ||
| 230 | (or file (buffer-file-name)) last flags)) | ||
| 231 | ad-do-it)))) | ||
| 232 | ;;-) | ||
| 233 | |||
| 234 | |||
| 235 | ;; XEmacs uses this to do some of its work. Like vc-do-command, we | ||
| 236 | ;; need to enhance it to make VC work via TRAMP-mode. | ||
| 237 | ;; | ||
| 238 | ;; Like the previous function, this is a cut-and-paste job from the VC | ||
| 239 | ;; file. It's based on the vc-do-command code. | ||
| 240 | ;; CCC: this isn't used in Emacs 21, so do as before. | ||
| 241 | (defun tramp-vc-simple-command (okstatus command file &rest args) | ||
| 242 | ;; Simple version of vc-do-command, for use in vc-hooks only. | ||
| 243 | ;; Don't switch to the *vc-info* buffer before running the | ||
| 244 | ;; command, because that would change its default directory | ||
| 245 | (save-match-data | ||
| 246 | (let* ((v (tramp-dissect-file-name (tramp-handle-expand-file-name file))) | ||
| 247 | (multi-method (tramp-file-name-multi-method v)) | ||
| 248 | (method (tramp-file-name-method v)) | ||
| 249 | (user (tramp-file-name-user v)) | ||
| 250 | (host (tramp-file-name-host v)) | ||
| 251 | (path (tramp-file-name-path v))) | ||
| 252 | (save-excursion (set-buffer (get-buffer-create "*vc-info*")) | ||
| 253 | (erase-buffer)) | ||
| 254 | (let ((exec-path (append vc-path exec-path)) exec-status | ||
| 255 | ;; Add vc-path to PATH for the execution of this command. | ||
| 256 | (process-environment | ||
| 257 | (cons (concat "PATH=" (getenv "PATH") | ||
| 258 | path-separator | ||
| 259 | (mapconcat 'identity vc-path path-separator)) | ||
| 260 | process-environment))) | ||
| 261 | ;; Call the actual process. See tramp-vc-do-command for discussion of | ||
| 262 | ;; why this does both (save-window-excursion) and (save-excursion). | ||
| 263 | ;; | ||
| 264 | ;; As a note, I don't think that the process-environment stuff above | ||
| 265 | ;; has any effect on the remote system. This is a hard one though as | ||
| 266 | ;; there is no real reason to expect local and remote paths to be | ||
| 267 | ;; identical... | ||
| 268 | ;; | ||
| 269 | ;; Daniel Pittman <daniel@danann.net> | ||
| 270 | (save-excursion | ||
| 271 | (save-window-excursion | ||
| 272 | ;; Actually execute remote command | ||
| 273 | (tramp-handle-shell-command | ||
| 274 | (mapconcat 'tramp-shell-quote-argument | ||
| 275 | (append (list command) args (list path)) " ") | ||
| 276 | (get-buffer-create"*vc-info*")) | ||
| 277 | ;(tramp-wait-for-output) | ||
| 278 | ;; Get status from command | ||
| 279 | (tramp-send-command multi-method method user host "echo $?") | ||
| 280 | (tramp-wait-for-output) | ||
| 281 | (setq exec-status (read (current-buffer))) | ||
| 282 | (message "Command %s returned status %d." command exec-status))) | ||
| 283 | |||
| 284 | (cond ((> exec-status okstatus) | ||
| 285 | (switch-to-buffer (get-file-buffer file)) | ||
| 286 | (shrink-window-if-larger-than-buffer | ||
| 287 | (display-buffer "*vc-info*")) | ||
| 288 | (error "Couldn't find version control information"))) | ||
| 289 | exec-status)))) | ||
| 290 | |||
| 291 | ;; This function does not exist any more in Emacs-21's VC | ||
| 292 | (defadvice vc-simple-command | ||
| 293 | (around tramp-advice-vc-simple-command | ||
| 294 | (okstatus command file &rest args) | ||
| 295 | activate) | ||
| 296 | "Invoke tramp-vc-simple-command for tramp files." | ||
| 297 | (let ((file (symbol-value 'file))) ;pacify byte-compiler | ||
| 298 | (if (or (and (stringp file) (tramp-tramp-file-p file)) | ||
| 299 | (and (buffer-file-name) (tramp-tramp-file-p (buffer-file-name)))) | ||
| 300 | (setq ad-return-value | ||
| 301 | (apply 'tramp-vc-simple-command okstatus command | ||
| 302 | (or file (buffer-file-name)) args)) | ||
| 303 | ad-do-it))) | ||
| 304 | |||
| 305 | |||
| 306 | ;; `vc-workfile-unchanged-p' | ||
| 307 | ;; This function does not deal well with remote files, so we do the | ||
| 308 | ;; same as for `vc-do-command'. | ||
| 309 | |||
| 310 | ;; `vc-workfile-unchanged-p' checks the modification time, we cannot | ||
| 311 | ;; do that for remote files, so here's a version which relies on diff. | ||
| 312 | ;; CCC: this one probably works for Emacs 21, too. | ||
| 313 | (defun tramp-vc-workfile-unchanged-p | ||
| 314 | (filename &optional want-differences-if-changed) | ||
| 315 | (if (fboundp 'vc-backend-diff) | ||
| 316 | ;; Old VC. Call `vc-backend-diff'. | ||
| 317 | (let ((status (funcall (symbol-function 'vc-backend-diff) | ||
| 318 | filename nil nil | ||
| 319 | (not want-differences-if-changed)))) | ||
| 320 | (zerop status)) | ||
| 321 | ;; New VC. Call `vc-default-workfile-unchanged-p'. | ||
| 322 | (vc-default-workfile-unchanged-p (vc-backend file) filename))) | ||
| 323 | |||
| 324 | (defadvice vc-workfile-unchanged-p | ||
| 325 | (around tramp-advice-vc-workfile-unchanged-p | ||
| 326 | (filename &optional want-differences-if-changed) | ||
| 327 | activate) | ||
| 328 | "Invoke tramp-vc-workfile-unchanged-p for tramp files." | ||
| 329 | (if (and (stringp filename) | ||
| 330 | (tramp-tramp-file-p filename) | ||
| 331 | (not | ||
| 332 | (let ((v (tramp-dissect-file-name filename))) | ||
| 333 | (tramp-get-remote-perl (tramp-file-name-multi-method v) | ||
| 334 | (tramp-file-name-method v) | ||
| 335 | (tramp-file-name-user v) | ||
| 336 | (tramp-file-name-host v))))) | ||
| 337 | (setq ad-return-value | ||
| 338 | (tramp-vc-workfile-unchanged-p filename want-differences-if-changed)) | ||
| 339 | ad-do-it)) | ||
| 340 | |||
| 341 | |||
| 342 | ;; Redefine a function from vc.el -- allow tramp files. | ||
| 343 | ;; `save-match-data' seems not to be required -- it isn't in | ||
| 344 | ;; the original version, either. | ||
| 345 | ;; CCC: this might need some work -- how does the Emacs 21 version | ||
| 346 | ;; work, anyway? Does it work over ange-ftp? Hm. | ||
| 347 | (if (not (fboundp 'vc-backend-checkout)) | ||
| 348 | () ;; our replacement won't work and is unnecessary anyway | ||
| 349 | (defun vc-checkout (filename &optional writable rev) | ||
| 350 | "Retrieve a copy of the latest version of the given file." | ||
| 351 | ;; If ftp is on this system and the name matches the ange-ftp format | ||
| 352 | ;; for a remote file, the user is trying something that won't work. | ||
| 353 | (funcall (symbol-function 'vc-backend-checkout) filename writable rev) | ||
| 354 | (vc-resynch-buffer filename t t)) | ||
| 355 | ) | ||
| 356 | |||
| 357 | |||
| 358 | ;; Do we need to advise the vc-user-login-name function anyway? | ||
| 359 | ;; This will return the correct login name for the owner of a | ||
| 360 | ;; file. It does not deal with the default remote user name... | ||
| 361 | ;; | ||
| 362 | ;; That is, when vc calls (vc-user-login-name), we return the | ||
| 363 | ;; local login name, something that may be different to the remote | ||
| 364 | ;; default. | ||
| 365 | ;; | ||
| 366 | ;; The remote VC operations will occur as the user that we logged | ||
| 367 | ;; in with however - not always the same as the local user. | ||
| 368 | ;; | ||
| 369 | ;; In the end, I did advise the function. This is because, well, | ||
| 370 | ;; the thing didn't work right otherwise ;) | ||
| 371 | ;; | ||
| 372 | ;; Daniel Pittman <daniel@danann.net> | ||
| 373 | |||
| 374 | (defun tramp-handle-vc-user-login-name (&optional uid) | ||
| 375 | "Return the default user name on the remote machine. | ||
| 376 | Whenever VC calls this function, `file' is bound to the file name | ||
| 377 | in question. If no uid is provided or the uid is equal to the uid | ||
| 378 | owning the file, then we return the user name given in the file name. | ||
| 379 | |||
| 380 | This should only be called when `file' is bound to the | ||
| 381 | filename we are thinking about..." | ||
| 382 | ;; Pacify byte-compiler; this symbol is bound in the calling | ||
| 383 | ;; function. CCC: Maybe it would be better to move the | ||
| 384 | ;; boundness-checking into this function? | ||
| 385 | (let ((file (symbol-value 'file))) | ||
| 386 | (if (and uid (/= uid (nth 2 (file-attributes file)))) | ||
| 387 | (error "tramp-handle-vc-user-login-name cannot map a uid to a name") | ||
| 388 | (let* ((v (tramp-dissect-file-name (tramp-handle-expand-file-name file))) | ||
| 389 | (u (tramp-file-name-user v))) | ||
| 390 | (cond ((stringp u) u) | ||
| 391 | ((vectorp u) (elt u (1- (length u)))) | ||
| 392 | ((null u) (user-login-name)) | ||
| 393 | (t (error "tramp-handle-vc-user-login-name cannot cope!"))))))) | ||
| 394 | |||
| 395 | |||
| 396 | (defadvice vc-user-login-name | ||
| 397 | (around tramp-vc-user-login-name activate) | ||
| 398 | "Support for files on remote machines accessed by TRAMP." | ||
| 399 | ;; We rely on the fact that `file' is bound when this is called. | ||
| 400 | ;; This appears to be the case everywhere in vc.el and vc-hooks.el | ||
| 401 | ;; as of Emacs 20.5. | ||
| 402 | ;; | ||
| 403 | ;; CCC TODO there should be a real solution! Talk to Andre Spiegel | ||
| 404 | ;; about this. | ||
| 405 | (let ((file (when (boundp 'file) | ||
| 406 | (symbol-value 'file)))) ;pacify byte-compiler | ||
| 407 | (or (and (stringp file) | ||
| 408 | (tramp-tramp-file-p file) ; tramp file | ||
| 409 | (setq ad-return-value | ||
| 410 | (save-match-data | ||
| 411 | (tramp-handle-vc-user-login-name uid)))) ; get the owner name | ||
| 412 | ad-do-it))) ; else call the original | ||
| 413 | |||
| 414 | |||
| 415 | ;; Determine the name of the user owning a file. | ||
| 416 | (defun tramp-file-owner (filename) | ||
| 417 | "Return who owns FILE (user name, as a string)." | ||
| 418 | (let ((v (tramp-dissect-file-name | ||
| 419 | (tramp-handle-expand-file-name filename)))) | ||
| 420 | (if (not (tramp-handle-file-exists-p filename)) | ||
| 421 | nil ; file cannot be opened | ||
| 422 | ;; file exists, find out stuff | ||
| 423 | (save-excursion | ||
| 424 | (tramp-send-command | ||
| 425 | (tramp-file-name-multi-method v) (tramp-file-name-method v) | ||
| 426 | (tramp-file-name-user v) (tramp-file-name-host v) | ||
| 427 | (format "%s -Lld %s" | ||
| 428 | (tramp-get-ls-command (tramp-file-name-multi-method v) | ||
| 429 | (tramp-file-name-method v) | ||
| 430 | (tramp-file-name-user v) | ||
| 431 | (tramp-file-name-host v)) | ||
| 432 | (tramp-shell-quote-argument (tramp-file-name-path v)))) | ||
| 433 | (tramp-wait-for-output) | ||
| 434 | ;; parse `ls -l' output ... | ||
| 435 | ;; ... file mode flags | ||
| 436 | (read (current-buffer)) | ||
| 437 | ;; ... number links | ||
| 438 | (read (current-buffer)) | ||
| 439 | ;; ... uid (as a string) | ||
| 440 | (symbol-name (read (current-buffer))))))) | ||
| 441 | |||
| 442 | ;; Wire ourselves into the VC infrastructure... | ||
| 443 | ;; This function does not exist any more in Emacs-21's VC | ||
| 444 | ;; CCC: it appears that no substitute is needed for Emacs 21. | ||
| 445 | (defadvice vc-file-owner | ||
| 446 | (around tramp-vc-file-owner activate) | ||
| 447 | "Support for files on remote machines accessed by TRAMP." | ||
| 448 | (let ((filename (ad-get-arg 0))) | ||
| 449 | (or (and (tramp-file-name-p filename) ; tramp file | ||
| 450 | (setq ad-return-value | ||
| 451 | (save-match-data | ||
| 452 | (tramp-file-owner filename)))) ; get the owner name | ||
| 453 | ad-do-it))) ; else call the original | ||
| 454 | |||
| 455 | |||
| 456 | ;; We need to make the version control software backend version | ||
| 457 | ;; information local to the current buffer. This is because each TRAMP | ||
| 458 | ;; buffer can (theoretically) have a different VC version and I am | ||
| 459 | ;; *way* too lazy to try and push the correct value into each new | ||
| 460 | ;; buffer. | ||
| 461 | ;; | ||
| 462 | ;; Remote VC costs will just have to be paid, at least for the moment. | ||
| 463 | ;; Well, at least, they will right until I feel guilty about doing a | ||
| 464 | ;; botch job here and fix it. :/ | ||
| 465 | ;; | ||
| 466 | ;; Daniel Pittman <daniel@danann.net> | ||
| 467 | ;; CCC: this is probably still needed for Emacs 21. | ||
| 468 | (defun tramp-vc-setup-for-remote () | ||
| 469 | "Make the backend release variables buffer local. | ||
| 470 | This makes remote VC work correctly at the cost of some processing time." | ||
| 471 | (when (and (buffer-file-name) | ||
| 472 | (tramp-tramp-file-p (buffer-file-name))) | ||
| 473 | (make-local-variable 'vc-rcs-release) | ||
| 474 | (setq vc-rcs-release nil))) | ||
| 475 | (add-hook 'find-file-hooks 'tramp-vc-setup-for-remote t) | ||
| 476 | |||
| 477 | ;; No need to load this again if anyone asks. | ||
| 478 | (provide 'tramp-vc) | ||
| 479 | |||
| 480 | ;;; tramp-vc.el ends here | ||
diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el new file mode 100644 index 00000000000..585c5d46986 --- /dev/null +++ b/lisp/net/tramp.el | |||
| @@ -0,0 +1,5152 @@ | |||
| 1 | ;;; tramp.el --- Transparent Remote Access, Multiple Protocol -*- coding: iso-8859-1; -*- | ||
| 2 | |||
| 3 | ;; Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. | ||
| 4 | |||
| 5 | ;; Author: Kai.Grossjohann@CS.Uni-Dortmund.DE | ||
| 6 | ;; Keywords: comm, processes | ||
| 7 | |||
| 8 | ;; This file is part of GNU Emacs. | ||
| 9 | |||
| 10 | ;; GNU Emacs is free software; you can redistribute it and/or modify | ||
| 11 | ;; it under the terms of the GNU General Public License as published by | ||
| 12 | ;; the Free Software Foundation; either version 2, or (at your option) | ||
| 13 | ;; any later version. | ||
| 14 | |||
| 15 | ;; GNU Emacs is distributed in the hope that it will be useful, | ||
| 16 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 17 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 18 | ;; GNU General Public License for more details. | ||
| 19 | |||
| 20 | ;; You should have received a copy of the GNU General Public License | ||
| 21 | ;; along with GNU Emacs; see the file COPYING. If not, write to the | ||
| 22 | ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||
| 23 | ;; Boston, MA 02111-1307, USA. | ||
| 24 | |||
| 25 | ;;; Commentary: | ||
| 26 | |||
| 27 | ;; This package provides remote file editing, similar to ange-ftp. | ||
| 28 | ;; The difference is that ange-ftp uses FTP to transfer files between | ||
| 29 | ;; the local and the remote host, whereas tramp.el uses a combination | ||
| 30 | ;; of rsh and rcp or other work-alike programs, such as ssh/scp. | ||
| 31 | ;; | ||
| 32 | ;; For more detailed instructions, please see the info file, which is | ||
| 33 | ;; included in the file `tramp.tar.gz' mentioned below. | ||
| 34 | ;; | ||
| 35 | ;; Notes: | ||
| 36 | ;; ----- | ||
| 37 | ;; | ||
| 38 | ;; This package only works for Emacs 20 and higher, and for XEmacs 21 | ||
| 39 | ;; and higher. (XEmacs 20 is missing the `with-timeout' macro. Emacs | ||
| 40 | ;; 19 is reported to have other problems. For XEmacs 21, you need the | ||
| 41 | ;; package `fsf-compat' for the `with-timeout' macro.) | ||
| 42 | ;; | ||
| 43 | ;; This version might not work with pre-Emacs 21 VC unless VC is | ||
| 44 | ;; loaded before tramp.el. Could you please test this and tell me about | ||
| 45 | ;; the result? Thanks. | ||
| 46 | ;; | ||
| 47 | ;; Also see the todo list at the bottom of this file. | ||
| 48 | ;; | ||
| 49 | ;; The current version of tramp.el can be retrieved from the following | ||
| 50 | ;; URL: ftp://ls6-ftp.cs.uni-dortmund.de/pub/src/emacs/tramp.tar.gz | ||
| 51 | ;; For your convenience, the *.el file is available separately from | ||
| 52 | ;; the same directory. | ||
| 53 | ;; | ||
| 54 | ;; There's a mailing list for this, as well. Its name is: | ||
| 55 | ;; tramp-devel@lists.sourceforge.net | ||
| 56 | ;; Send a mail with `help' in the subject (!) to the administration | ||
| 57 | ;; address for instructions on joining the list. The administration | ||
| 58 | ;; address is: | ||
| 59 | ;; tramp-devel-request@lists.sourceforge.net | ||
| 60 | ;; You can also use the Web to subscribe, under the following URL: | ||
| 61 | ;; http://lists.sourceforge.net/lists/listinfo/tramp-devel | ||
| 62 | ;; | ||
| 63 | ;; For the adventurous, the current development sources are available | ||
| 64 | ;; via CVS. You can find instructions about this at the following URL: | ||
| 65 | ;; http://sourceforge.net/projects/tramp/ | ||
| 66 | ;; Click on "CVS" in the navigation bar near the top. | ||
| 67 | ;; | ||
| 68 | ;; Don't forget to put on your asbestos longjohns, first! | ||
| 69 | |||
| 70 | ;;; Code: | ||
| 71 | |||
| 72 | (defconst tramp-version "2.0.0" | ||
| 73 | "This version of tramp.") | ||
| 74 | (defconst tramp-bug-report-address "tramp-devel@mail.freesoftware.fsf.org" | ||
| 75 | "Email address to send bug reports to.") | ||
| 76 | |||
| 77 | (require 'timer) | ||
| 78 | (require 'format-spec) ;from Gnus 5.8, also in tar ball | ||
| 79 | (require 'base64) ;for the mimencode methods | ||
| 80 | (require 'shell) | ||
| 81 | (require 'advice) | ||
| 82 | |||
| 83 | ;; ;; It does not work to load EFS after loading TRAMP. | ||
| 84 | ;; (when (fboundp 'efs-file-handler-function) | ||
| 85 | ;; (require 'efs)) | ||
| 86 | |||
| 87 | (eval-when-compile | ||
| 88 | (require 'cl) | ||
| 89 | (require 'custom) | ||
| 90 | ;; Emacs 19.34 compatibility hack -- is this needed? | ||
| 91 | (or (>= emacs-major-version 20) | ||
| 92 | (load "cl-seq"))) | ||
| 93 | |||
| 94 | (unless (boundp 'custom-print-functions) | ||
| 95 | (defvar custom-print-functions nil)) ; not autoloaded before Emacs 20.4 | ||
| 96 | |||
| 97 | ;;; User Customizable Internal Variables: | ||
| 98 | |||
| 99 | (defgroup tramp nil | ||
| 100 | "Edit remote files with a combination of rsh and rcp or similar programs." | ||
| 101 | :group 'files) | ||
| 102 | |||
| 103 | (defcustom tramp-verbose 10 | ||
| 104 | "*Verbosity level for tramp.el. 0 means be silent, 10 is most verbose." | ||
| 105 | :group 'tramp | ||
| 106 | :type 'integer) | ||
| 107 | |||
| 108 | (defcustom tramp-debug-buffer nil | ||
| 109 | "*Whether to send all commands and responses to a debug buffer." | ||
| 110 | :group 'tramp | ||
| 111 | :type 'boolean) | ||
| 112 | |||
| 113 | (defcustom tramp-auto-save-directory nil | ||
| 114 | "*Put auto-save files in this directory, if set. | ||
| 115 | The idea is to use a local directory so that auto-saving is faster." | ||
| 116 | :group 'tramp | ||
| 117 | :type '(choice (const nil) | ||
| 118 | string)) | ||
| 119 | |||
| 120 | (defcustom tramp-sh-program "/bin/sh" | ||
| 121 | "*Use this program for shell commands on the local host. | ||
| 122 | This MUST be a Bourne-like shell. This shell is used to execute | ||
| 123 | the encoding and decoding command on the local host, so if you | ||
| 124 | want to use `~' in those commands, you should choose a shell here | ||
| 125 | which groks tilde expansion. `/bin/sh' normally does not | ||
| 126 | understand tilde expansion. | ||
| 127 | |||
| 128 | Note that this variable is not used for remote commands. There are | ||
| 129 | mechanisms in tramp.el which automatically determine the right shell to | ||
| 130 | use for the remote host." | ||
| 131 | :group 'tramp | ||
| 132 | :type '(file :must-match t)) | ||
| 133 | |||
| 134 | ;; CCC I have changed all occurrences of comint-quote-filename with | ||
| 135 | ;; tramp-shell-quote-argument, except in tramp-handle-expand-many-files. | ||
| 136 | ;; There, comint-quote-filename was removed altogether. If it turns | ||
| 137 | ;; out to be necessary there, something will need to be done. | ||
| 138 | ;;-(defcustom tramp-file-name-quote-list | ||
| 139 | ;;- '(?] ?[ ?\| ?& ?< ?> ?\( ?\) ?\; ?\ ?\* ?\? ?\! ?\" ?\' ?\` ?# ?\@ ?\+ ) | ||
| 140 | ;;- "*Protect these characters from the remote shell. | ||
| 141 | ;;-Any character in this list is quoted (preceded with a backslash) | ||
| 142 | ;;-because it means something special to the shell. This takes effect | ||
| 143 | ;;-when sending file and directory names to the remote shell. | ||
| 144 | ;;- | ||
| 145 | ;;-See `comint-file-name-quote-list' for details." | ||
| 146 | ;;- :group 'tramp | ||
| 147 | ;;- :type '(repeat character)) | ||
| 148 | |||
| 149 | (defcustom tramp-methods | ||
| 150 | '( ("rcp" (tramp-connection-function tramp-open-connection-rsh) | ||
| 151 | (tramp-rsh-program "rsh") | ||
| 152 | (tramp-rcp-program "rcp") | ||
| 153 | (tramp-remote-sh "/bin/sh") | ||
| 154 | (tramp-rsh-args nil) | ||
| 155 | (tramp-rcp-args nil) | ||
| 156 | (tramp-rcp-keep-date-arg "-p") | ||
| 157 | (tramp-su-program nil) | ||
| 158 | (tramp-su-args nil) | ||
| 159 | (tramp-encoding-command nil) | ||
| 160 | (tramp-decoding-command nil) | ||
| 161 | (tramp-encoding-function nil) | ||
| 162 | (tramp-decoding-function nil) | ||
| 163 | (tramp-telnet-program nil) | ||
| 164 | (tramp-telnet-args nil)) | ||
| 165 | ("scp" (tramp-connection-function tramp-open-connection-rsh) | ||
| 166 | (tramp-rsh-program "ssh") | ||
| 167 | (tramp-rcp-program "scp") | ||
| 168 | (tramp-remote-sh "/bin/sh") | ||
| 169 | (tramp-rsh-args ("-e" "none")) | ||
| 170 | (tramp-rcp-args nil) | ||
| 171 | (tramp-rcp-keep-date-arg "-p") | ||
| 172 | (tramp-su-program nil) | ||
| 173 | (tramp-su-args nil) | ||
| 174 | (tramp-encoding-command nil) | ||
| 175 | (tramp-decoding-command nil) | ||
| 176 | (tramp-encoding-function nil) | ||
| 177 | (tramp-decoding-function nil) | ||
| 178 | (tramp-telnet-program nil) | ||
| 179 | (tramp-telnet-args nil)) | ||
| 180 | ("scp1" (tramp-connection-function tramp-open-connection-rsh) | ||
| 181 | (tramp-rsh-program "ssh1") | ||
| 182 | (tramp-rcp-program "scp1") | ||
| 183 | (tramp-remote-sh "/bin/sh") | ||
| 184 | (tramp-rsh-args ("-e" "none")) | ||
| 185 | (tramp-rcp-args nil) | ||
| 186 | (tramp-rcp-keep-date-arg "-p") | ||
| 187 | (tramp-su-program nil) | ||
| 188 | (tramp-su-args nil) | ||
| 189 | (tramp-encoding-command nil) | ||
| 190 | (tramp-decoding-command nil) | ||
| 191 | (tramp-encoding-function nil) | ||
| 192 | (tramp-decoding-function nil) | ||
| 193 | (tramp-telnet-program nil) | ||
| 194 | (tramp-telnet-args nil)) | ||
| 195 | ("scp2" (tramp-connection-function tramp-open-connection-rsh) | ||
| 196 | (tramp-rsh-program "ssh2") | ||
| 197 | (tramp-rcp-program "scp2") | ||
| 198 | (tramp-remote-sh "/bin/sh") | ||
| 199 | (tramp-rsh-args ("-e" "none")) | ||
| 200 | (tramp-rcp-args nil) | ||
| 201 | (tramp-rcp-keep-date-arg "-p") | ||
| 202 | (tramp-su-program nil) | ||
| 203 | (tramp-su-args nil) | ||
| 204 | (tramp-encoding-command nil) | ||
| 205 | (tramp-decoding-command nil) | ||
| 206 | (tramp-encoding-function nil) | ||
| 207 | (tramp-decoding-function nil) | ||
| 208 | (tramp-telnet-program nil) | ||
| 209 | (tramp-telnet-args nil)) | ||
| 210 | ("rsync" (tramp-connection-function tramp-open-connection-rsh) | ||
| 211 | (tramp-rsh-program "ssh") | ||
| 212 | (tramp-rcp-program "rsync") | ||
| 213 | (tramp-remote-sh "/bin/sh") | ||
| 214 | (tramp-rsh-args ("-e" "none")) | ||
| 215 | (tramp-rcp-args ("-e" "ssh")) | ||
| 216 | (tramp-rcp-keep-date-arg "-t") | ||
| 217 | (tramp-su-program nil) | ||
| 218 | (tramp-su-args nil) | ||
| 219 | (tramp-encoding-command nil) | ||
| 220 | (tramp-decoding-command nil) | ||
| 221 | (tramp-encoding-function nil) | ||
| 222 | (tramp-decoding-function nil) | ||
| 223 | (tramp-telnet-program nil) | ||
| 224 | (tramp-telnet-args nil)) | ||
| 225 | ("ru" (tramp-connection-function tramp-open-connection-rsh) | ||
| 226 | (tramp-rsh-program "rsh") | ||
| 227 | (tramp-rcp-program nil) | ||
| 228 | (tramp-remote-sh "/bin/sh") | ||
| 229 | (tramp-rsh-args nil) | ||
| 230 | (tramp-rcp-args nil) | ||
| 231 | (tramp-rcp-keep-date-arg nil) | ||
| 232 | (tramp-su-program nil) | ||
| 233 | (tramp-su-args nil) | ||
| 234 | (tramp-encoding-command "uuencode xxx") | ||
| 235 | (tramp-decoding-command | ||
| 236 | "( uudecode -o - 2>/dev/null || uudecode -p 2>/dev/null )") | ||
| 237 | (tramp-encoding-function nil) | ||
| 238 | (tramp-decoding-function uudecode-decode-region) | ||
| 239 | (tramp-telnet-program nil) | ||
| 240 | (tramp-telnet-args nil)) | ||
| 241 | ("su" (tramp-connection-function tramp-open-connection-rsh) | ||
| 242 | (tramp-rsh-program "ssh") | ||
| 243 | (tramp-rcp-program nil) | ||
| 244 | (tramp-remote-sh "/bin/sh") | ||
| 245 | (tramp-rsh-args ("-e" "none")) | ||
| 246 | (tramp-rcp-args nil) | ||
| 247 | (tramp-rcp-keep-date-arg nil) | ||
| 248 | (tramp-su-program nil) | ||
| 249 | (tramp-su-args nil) | ||
| 250 | (tramp-encoding-command "uuencode xxx") | ||
| 251 | (tramp-decoding-command | ||
| 252 | "( uudecode -o - 2>/dev/null || uudecode -p 2>/dev/null )") | ||
| 253 | (tramp-encoding-function nil) | ||
| 254 | (tramp-decoding-function uudecode-decode-region) | ||
| 255 | (tramp-telnet-program nil) | ||
| 256 | (tramp-telnet-args nil)) | ||
| 257 | ("su1" (tramp-connection-function tramp-open-connection-rsh) | ||
| 258 | (tramp-rsh-program "ssh1") | ||
| 259 | (tramp-rcp-program nil) | ||
| 260 | (tramp-remote-sh "/bin/sh") | ||
| 261 | (tramp-rsh-args ("-e" "none")) | ||
| 262 | (tramp-rcp-args nil) | ||
| 263 | (tramp-rcp-keep-date-arg nil) | ||
| 264 | (tramp-su-program nil) | ||
| 265 | (tramp-su-args nil) | ||
| 266 | (tramp-encoding-command "uuencode xxx") | ||
| 267 | (tramp-decoding-command | ||
| 268 | "( uudecode -o - 2>/dev/null || uudecode -p 2>/dev/null )") | ||
| 269 | (tramp-encoding-function nil) | ||
| 270 | (tramp-decoding-function uudecode-decode-region) | ||
| 271 | (tramp-telnet-program nil) | ||
| 272 | (tramp-telnet-args nil)) | ||
| 273 | ("su2" (tramp-connection-function tramp-open-connection-rsh) | ||
| 274 | (tramp-rsh-program "ssh2") | ||
| 275 | (tramp-rcp-program nil) | ||
| 276 | (tramp-remote-sh "/bin/sh") | ||
| 277 | (tramp-rsh-args ("-e" "none")) | ||
| 278 | (tramp-rcp-args nil) | ||
| 279 | (tramp-rcp-keep-date-arg nil) | ||
| 280 | (tramp-su-program nil) | ||
| 281 | (tramp-su-args nil) | ||
| 282 | (tramp-encoding-command "uuencode xxx") | ||
| 283 | (tramp-decoding-command | ||
| 284 | "( uudecode -o - 2>/dev/null || uudecode -p 2>/dev/null )") | ||
| 285 | (tramp-encoding-function nil) | ||
| 286 | (tramp-decoding-function uudecode-decode-region) | ||
| 287 | (tramp-telnet-program nil) | ||
| 288 | (tramp-telnet-args nil)) | ||
| 289 | ("rm" (tramp-connection-function tramp-open-connection-rsh) | ||
| 290 | (tramp-rsh-program "rsh") | ||
| 291 | (tramp-rcp-program nil) | ||
| 292 | (tramp-remote-sh "/bin/sh") | ||
| 293 | (tramp-rsh-args nil) | ||
| 294 | (tramp-rcp-args nil) | ||
| 295 | (tramp-rcp-keep-date-arg nil) | ||
| 296 | (tramp-su-program nil) | ||
| 297 | (tramp-su-args nil) | ||
| 298 | (tramp-encoding-command "mimencode -b") | ||
| 299 | (tramp-decoding-command "mimencode -u -b") | ||
| 300 | (tramp-encoding-function base64-encode-region) | ||
| 301 | (tramp-decoding-function base64-decode-region) | ||
| 302 | (tramp-telnet-program nil) | ||
| 303 | (tramp-telnet-args nil)) | ||
| 304 | ("sm" (tramp-connection-function tramp-open-connection-rsh) | ||
| 305 | (tramp-rsh-program "ssh") | ||
| 306 | (tramp-rcp-program nil) | ||
| 307 | (tramp-remote-sh "/bin/sh") | ||
| 308 | (tramp-rsh-args ("-e" "none")) | ||
| 309 | (tramp-rcp-args nil) | ||
| 310 | (tramp-rcp-keep-date-arg nil) | ||
| 311 | (tramp-su-program nil) | ||
| 312 | (tramp-su-args nil) | ||
| 313 | (tramp-encoding-command "mimencode -b") | ||
| 314 | (tramp-decoding-command "mimencode -u -b") | ||
| 315 | (tramp-encoding-function base64-encode-region) | ||
| 316 | (tramp-decoding-function base64-decode-region) | ||
| 317 | (tramp-telnet-program nil) | ||
| 318 | (tramp-telnet-args nil)) | ||
| 319 | ("smp" (tramp-connection-function tramp-open-connection-rsh) | ||
| 320 | (tramp-rsh-program "ssh") | ||
| 321 | (tramp-rcp-program nil) | ||
| 322 | (tramp-remote-sh "/bin/sh") | ||
| 323 | (tramp-rsh-args ("-e" "none")) | ||
| 324 | (tramp-rcp-args nil) | ||
| 325 | (tramp-rcp-keep-date-arg nil) | ||
| 326 | (tramp-su-program nil) | ||
| 327 | (tramp-su-args nil) | ||
| 328 | (tramp-encoding-command "tramp_mimencode") | ||
| 329 | (tramp-decoding-command "tramp_mimedecode") | ||
| 330 | (tramp-encoding-function base64-encode-region) | ||
| 331 | (tramp-decoding-function base64-decode-region) | ||
| 332 | (tramp-telnet-program nil)) | ||
| 333 | ("sm1" (tramp-connection-function tramp-open-connection-rsh) | ||
| 334 | (tramp-rsh-program "ssh1") | ||
| 335 | (tramp-rcp-program nil) | ||
| 336 | (tramp-remote-sh "/bin/sh") | ||
| 337 | (tramp-rsh-args ("-e" "none")) | ||
| 338 | (tramp-rcp-args nil) | ||
| 339 | (tramp-rcp-keep-date-arg nil) | ||
| 340 | (tramp-su-program nil) | ||
| 341 | (tramp-su-args nil) | ||
| 342 | (tramp-encoding-command "mimencode -b") | ||
| 343 | (tramp-decoding-command "mimencode -u -b") | ||
| 344 | (tramp-encoding-function base64-encode-region) | ||
| 345 | (tramp-decoding-function base64-decode-region) | ||
| 346 | (tramp-telnet-program nil) | ||
| 347 | (tramp-telnet-args nil)) | ||
| 348 | ("sm2" (tramp-connection-function tramp-open-connection-rsh) | ||
| 349 | (tramp-rsh-program "ssh2") | ||
| 350 | (tramp-rcp-program nil) | ||
| 351 | (tramp-remote-sh "/bin/sh") | ||
| 352 | (tramp-rsh-args ("-e" "none")) | ||
| 353 | (tramp-rcp-args nil) | ||
| 354 | (tramp-rcp-keep-date-arg nil) | ||
| 355 | (tramp-su-program nil) | ||
| 356 | (tramp-su-args nil) | ||
| 357 | (tramp-encoding-command "mimencode -b") | ||
| 358 | (tramp-decoding-command "mimencode -u -b") | ||
| 359 | (tramp-encoding-function base64-encode-region) | ||
| 360 | (tramp-decoding-function base64-decode-region) | ||
| 361 | (tramp-telnet-program nil) | ||
| 362 | (tramp-telnet-args nil)) | ||
| 363 | ("tm" (tramp-connection-function tramp-open-connection-telnet) | ||
| 364 | (tramp-rsh-program nil) | ||
| 365 | (tramp-rcp-program nil) | ||
| 366 | (tramp-remote-sh "/bin/sh") | ||
| 367 | (tramp-rsh-args nil) | ||
| 368 | (tramp-rcp-args nil) | ||
| 369 | (tramp-rcp-keep-date-arg nil) | ||
| 370 | (tramp-su-program nil) | ||
| 371 | (tramp-su-args nil) | ||
| 372 | (tramp-encoding-command "mimencode -b") | ||
| 373 | (tramp-decoding-command "mimencode -u -b") | ||
| 374 | (tramp-encoding-function base64-encode-region) | ||
| 375 | (tramp-decoding-function base64-decode-region) | ||
| 376 | (tramp-telnet-program "telnet") | ||
| 377 | (tramp-telnet-args nil)) | ||
| 378 | ("tu" (tramp-connection-function tramp-open-connection-telnet) | ||
| 379 | (tramp-rsh-program nil) | ||
| 380 | (tramp-rcp-program nil) | ||
| 381 | (tramp-remote-sh "/bin/sh") | ||
| 382 | (tramp-rsh-args nil) | ||
| 383 | (tramp-rcp-args nil) | ||
| 384 | (tramp-rcp-keep-date-arg nil) | ||
| 385 | (tramp-su-program nil) | ||
| 386 | (tramp-su-args nil) | ||
| 387 | (tramp-encoding-command "uuencode xxx") | ||
| 388 | (tramp-decoding-command | ||
| 389 | "( uudecode -o - 2>/dev/null || uudecode -p 2>/dev/null )") | ||
| 390 | (tramp-encoding-function nil) | ||
| 391 | (tramp-decoding-function uudecode-decode-region) | ||
| 392 | (tramp-telnet-program "telnet") | ||
| 393 | (tramp-telnet-args nil)) | ||
| 394 | ("sum" (tramp-connection-function tramp-open-connection-su) | ||
| 395 | (tramp-rsh-program nil) | ||
| 396 | (tramp-rcp-program nil) | ||
| 397 | (tramp-remote-sh "/bin/sh") | ||
| 398 | (tramp-rsh-args nil) | ||
| 399 | (tramp-rcp-args nil) | ||
| 400 | (tramp-rcp-keep-date-arg nil) | ||
| 401 | (tramp-su-program "su") | ||
| 402 | (tramp-su-args ("-" "%u")) | ||
| 403 | (tramp-encoding-command "mimencode -b") | ||
| 404 | (tramp-decoding-command "mimencode -u -b") | ||
| 405 | (tramp-encoding-function base64-encode-region) | ||
| 406 | (tramp-decoding-function base64-decode-region) | ||
| 407 | (tramp-telnet-program nil) | ||
| 408 | (tramp-telnet-args nil)) | ||
| 409 | ("suu" (tramp-connection-function tramp-open-connection-su) | ||
| 410 | (tramp-rsh-program nil) | ||
| 411 | (tramp-rcp-program nil) | ||
| 412 | (tramp-remote-sh "/bin/sh") | ||
| 413 | (tramp-rsh-args nil) | ||
| 414 | (tramp-rcp-args nil) | ||
| 415 | (tramp-rcp-keep-date-arg nil) | ||
| 416 | (tramp-su-program "su") | ||
| 417 | (tramp-su-args ("-" "%u")) | ||
| 418 | (tramp-encoding-command "uuencode xxx") | ||
| 419 | (tramp-decoding-command | ||
| 420 | "( uudecode -o - 2>/dev/null || uudecode -p 2>/dev/null )") | ||
| 421 | (tramp-encoding-function nil) | ||
| 422 | (tramp-decoding-function uudecode-decode-region) | ||
| 423 | (tramp-telnet-program nil) | ||
| 424 | (tramp-telnet-args nil)) | ||
| 425 | ("sudm" (tramp-connection-function tramp-open-connection-su) | ||
| 426 | (tramp-rsh-program nil) | ||
| 427 | (tramp-rcp-program nil) | ||
| 428 | (tramp-remote-sh "/bin/sh") | ||
| 429 | (tramp-rsh-args nil) | ||
| 430 | (tramp-rcp-args nil) | ||
| 431 | (tramp-rcp-keep-date-arg nil) | ||
| 432 | (tramp-su-program "sudo") | ||
| 433 | (tramp-su-args ("-u" "%u" "-s")) | ||
| 434 | (tramp-encoding-command "mimencode -b") | ||
| 435 | (tramp-decoding-command "mimencode -u -b") | ||
| 436 | (tramp-encoding-function base64-encode-region) | ||
| 437 | (tramp-decoding-function base64-decode-region) | ||
| 438 | (tramp-telnet-program nil) | ||
| 439 | (tramp-telnet-args nil)) | ||
| 440 | ("sudu" (tramp-connection-function tramp-open-connection-su) | ||
| 441 | (tramp-rsh-program nil) | ||
| 442 | (tramp-rcp-program nil) | ||
| 443 | (tramp-remote-sh "/bin/sh") | ||
| 444 | (tramp-rsh-args nil) | ||
| 445 | (tramp-rcp-args nil) | ||
| 446 | (tramp-rcp-keep-date-arg nil) | ||
| 447 | (tramp-su-program "sudo") | ||
| 448 | (tramp-su-args ("-u" "%u" "-s")) | ||
| 449 | (tramp-encoding-command "uuencode xxx") | ||
| 450 | (tramp-decoding-command | ||
| 451 | "( uudecode -o - 2>/dev/null || uudecode -p 2>/dev/null )") | ||
| 452 | (tramp-encoding-function nil) | ||
| 453 | (tramp-decoding-function uudecode-decode-region) | ||
| 454 | (tramp-telnet-program nil) | ||
| 455 | (tramp-telnet-args nil)) | ||
| 456 | ("multi" (tramp-connection-function tramp-open-connection-multi) | ||
| 457 | (tramp-rsh-program nil) | ||
| 458 | (tramp-rcp-program nil) | ||
| 459 | (tramp-remote-sh "/bin/sh") | ||
| 460 | (tramp-rsh-args nil) | ||
| 461 | (tramp-rcp-args nil) | ||
| 462 | (tramp-rcp-keep-date-arg nil) | ||
| 463 | (tramp-su-program nil) | ||
| 464 | (tramp-su-args nil) | ||
| 465 | (tramp-encoding-command "mimencode -b") | ||
| 466 | (tramp-decoding-command "mimencode -u -b") | ||
| 467 | (tramp-encoding-function base64-encode-region) | ||
| 468 | (tramp-decoding-function base64-decode-region) | ||
| 469 | (tramp-telnet-program nil) | ||
| 470 | (tramp-telnet-args nil)) | ||
| 471 | ("multiu" (tramp-connection-function tramp-open-connection-multi) | ||
| 472 | (tramp-rsh-program nil) | ||
| 473 | (tramp-rcp-program nil) | ||
| 474 | (tramp-remote-sh "/bin/sh") | ||
| 475 | (tramp-rsh-args nil) | ||
| 476 | (tramp-rcp-args nil) | ||
| 477 | (tramp-rcp-keep-date-arg nil) | ||
| 478 | (tramp-su-program nil) | ||
| 479 | (tramp-su-args nil) | ||
| 480 | (tramp-encoding-command "uuencode xxx") | ||
| 481 | (tramp-decoding-command | ||
| 482 | "( uudecode -o - 2>/dev/null || uudecode -p 2>/dev/null )") | ||
| 483 | (tramp-encoding-function nil) | ||
| 484 | (tramp-decoding-function uudecode-decode-region) | ||
| 485 | (tramp-telnet-program nil) | ||
| 486 | (tramp-telnet-args nil)) | ||
| 487 | ("scpx" (tramp-connection-function tramp-open-connection-rsh) | ||
| 488 | (tramp-rsh-program "ssh") | ||
| 489 | (tramp-rcp-program "scp") | ||
| 490 | (tramp-remote-sh "/bin/sh") | ||
| 491 | (tramp-rsh-args ("-e" "none" "-t" "-t" "/bin/sh")) | ||
| 492 | (tramp-rcp-args nil) | ||
| 493 | (tramp-rcp-keep-date-arg "-p") | ||
| 494 | (tramp-encoding-command nil) | ||
| 495 | (tramp-decoding-command nil) | ||
| 496 | (tramp-encoding-function nil) | ||
| 497 | (tramp-decoding-function nil) | ||
| 498 | (tramp-telnet-program nil) | ||
| 499 | (tramp-telnet-args nil)) | ||
| 500 | ("smx" (tramp-connection-function tramp-open-connection-rsh) | ||
| 501 | (tramp-rsh-program "ssh") | ||
| 502 | (tramp-rcp-program nil) | ||
| 503 | (tramp-remote-sh "/bin/sh") | ||
| 504 | (tramp-rsh-args ("-e" "none" "-t" "-t" "/bin/sh")) | ||
| 505 | (tramp-rcp-args nil) | ||
| 506 | (tramp-rcp-keep-date-arg nil) | ||
| 507 | (tramp-su-program nil) | ||
| 508 | (tramp-su-args nil) | ||
| 509 | (tramp-encoding-command "mimencode -b") | ||
| 510 | (tramp-decoding-command "mimencode -u -b") | ||
| 511 | (tramp-encoding-function base64-encode-region) | ||
| 512 | (tramp-decoding-function base64-decode-region) | ||
| 513 | (tramp-telnet-program nil) | ||
| 514 | (tramp-telnet-args nil)) | ||
| 515 | ("km" | ||
| 516 | (tramp-connection-function tramp-open-connection-rsh) | ||
| 517 | (tramp-rsh-program "krlogin") | ||
| 518 | (tramp-rcp-program nil) | ||
| 519 | (tramp-remote-sh "/bin/sh") | ||
| 520 | (tramp-rsh-args ("-x")) | ||
| 521 | (tramp-rcp-args nil) | ||
| 522 | (tramp-rcp-keep-date-arg nil) | ||
| 523 | (tramp-su-program nil) | ||
| 524 | (tramp-su-args nil) | ||
| 525 | (tramp-encoding-command "mimencode -b") | ||
| 526 | (tramp-decoding-command "mimencode -u -b") | ||
| 527 | (tramp-encoding-function base64-encode-region) | ||
| 528 | (tramp-decoding-function base64-decode-region) | ||
| 529 | (tramp-telnet-program nil) | ||
| 530 | (tramp-telnet-args nil)) | ||
| 531 | ("plinku" | ||
| 532 | (tramp-connection-function tramp-open-connection-rsh) | ||
| 533 | (tramp-rsh-program "plink") | ||
| 534 | (tramp-rcp-program nil) | ||
| 535 | (tramp-remote-sh "/bin/sh") | ||
| 536 | (tramp-rsh-args ("-ssh")) ;optionally add "-v" | ||
| 537 | (tramp-rcp-args nil) | ||
| 538 | (tramp-rcp-keep-date-arg nil) | ||
| 539 | (tramp-su-program nil) | ||
| 540 | (tramp-su-args nil) | ||
| 541 | (tramp-encoding-command "uuencode xxx") | ||
| 542 | (tramp-decoding-command | ||
| 543 | "( uudecode -o - 2>/dev/null || uudecode -p 2>/dev/null )") | ||
| 544 | (tramp-encoding-function nil) | ||
| 545 | (tramp-decoding-function uudecode-decode-region) | ||
| 546 | (tramp-telnet-program nil) | ||
| 547 | (tramp-telnet-args nil)) | ||
| 548 | ("plinkm" | ||
| 549 | (tramp-connection-function tramp-open-connection-rsh) | ||
| 550 | (tramp-rsh-program "plink") | ||
| 551 | (tramp-rcp-program nil) | ||
| 552 | (tramp-remote-sh "/bin/sh") | ||
| 553 | (tramp-rsh-args ("-ssh")) ;optionally add "-v" | ||
| 554 | (tramp-rcp-args nil) | ||
| 555 | (tramp-rcp-keep-date-arg nil) | ||
| 556 | (tramp-su-program nil) | ||
| 557 | (tramp-su-args nil) | ||
| 558 | (tramp-encoding-command "mimencode -b") | ||
| 559 | (tramp-decoding-command "mimencode -u -b") | ||
| 560 | (tramp-encoding-function base64-encode-region) | ||
| 561 | (tramp-decoding-function base64-decode-region) | ||
| 562 | (tramp-telnet-program nil) | ||
| 563 | (tramp-telnet-args nil)) | ||
| 564 | ("pscp" | ||
| 565 | (tramp-connection-function tramp-open-connection-rsh) | ||
| 566 | (tramp-rsh-program "plink") | ||
| 567 | (tramp-rcp-program "pscp") | ||
| 568 | (tramp-remote-sh "/bin/sh") | ||
| 569 | (tramp-rsh-args ("-ssh")) | ||
| 570 | (tramp-rcp-args nil) | ||
| 571 | (tramp-rcp-keep-date-arg "-p") | ||
| 572 | (tramp-su-program nil) | ||
| 573 | (tramp-su-args nil) | ||
| 574 | (tramp-encoding-command nil) | ||
| 575 | (tramp-decoding-command nil) | ||
| 576 | (tramp-encoding-function nil) | ||
| 577 | (tramp-decoding-function nil) | ||
| 578 | (tramp-telnet-program nil) | ||
| 579 | (tramp-telnet-args nil)) | ||
| 580 | ("fcp" | ||
| 581 | (tramp-connection-function tramp-open-connection-rsh) | ||
| 582 | (tramp-rsh-program "fsh") | ||
| 583 | (tramp-rcp-program "fcp") | ||
| 584 | (tramp-remote-sh "/bin/sh -i") | ||
| 585 | (tramp-rsh-args ("sh" "-i")) | ||
| 586 | (tramp-rcp-args nil) | ||
| 587 | (tramp-rcp-keep-date-arg "-p") | ||
| 588 | (tramp-su-program nil) | ||
| 589 | (tramp-su-args nil) | ||
| 590 | (tramp-encoding-command nil) | ||
| 591 | (tramp-decoding-command nil) | ||
| 592 | (tramp-encoding-function nil) | ||
| 593 | (tramp-decoding-function nil) | ||
| 594 | (tramp-telnet-program nil) | ||
| 595 | (tramp-telnet-args nil)) | ||
| 596 | ) | ||
| 597 | "*Alist of methods for remote files. | ||
| 598 | This is a list of entries of the form (NAME PARAM1 PARAM2 ...). | ||
| 599 | Each NAME stands for a remote access method. Each PARAM is a | ||
| 600 | pair of the form (KEY VALUE). The following KEYs are defined: | ||
| 601 | * `tramp-connection-function' | ||
| 602 | This specifies the function to use to connect to the remote host. | ||
| 603 | Currently, `tramp-open-connection-rsh', `tramp-open-connection-telnet' | ||
| 604 | and `tramp-open-connection-su' are defined. See the documentation | ||
| 605 | of these functions for more details. | ||
| 606 | * `tramp-remote-sh' | ||
| 607 | This specifies the Bourne shell to use on the remote host. This | ||
| 608 | MUST be a Bourne-like shell. It is normally not necessary to set | ||
| 609 | this to any value other than \"/bin/sh\": tramp wants to use a shell | ||
| 610 | which groks tilde expansion, but it can search for it. Also note | ||
| 611 | that \"/bin/sh\" exists on all Unixen, this might not be true for | ||
| 612 | the value that you decide to use. You Have Been Warned. | ||
| 613 | * `tramp-rsh-program' | ||
| 614 | This specifies the name of the program to use for rsh; this might be | ||
| 615 | the full path to rsh or the name of a workalike program. | ||
| 616 | * `tramp-rsh-args' | ||
| 617 | This specifies the list of arguments to pass to the above | ||
| 618 | mentioned program. Please note that this is a list of arguments, | ||
| 619 | that is, normally you don't want to put \"-a -b\" or \"-f foo\" | ||
| 620 | here. Instead, you want two list elements, one for \"-a\" and one | ||
| 621 | for \"-b\", or one for \"-f\" and one for \"foo\". | ||
| 622 | * `tramp-rcp-program' | ||
| 623 | This specifies the name of the program to use for rcp; this might be | ||
| 624 | the full path to rcp or the name of a workalike program. | ||
| 625 | * `tramp-rcp-args' | ||
| 626 | This specifies the list of parameters to pass to the above mentioned | ||
| 627 | program, the hints for `tramp-rsh-args' also apply here. | ||
| 628 | * `tramp-rcp-keep-date-arg' | ||
| 629 | This specifies the parameter to use for `rcp' when the timestamp | ||
| 630 | of the original file should be kept. For `rcp', use `-p', for | ||
| 631 | `rsync', use `-t'. | ||
| 632 | * `tramp-su-program' | ||
| 633 | This specifies the name of the program to use for `su'. | ||
| 634 | * `tramp-su-args' | ||
| 635 | This specifies the list of arguments to pass to `su'. | ||
| 636 | \"%u\" is replaced by the user name, use \"%%\" for a literal | ||
| 637 | percent character. | ||
| 638 | * `tramp-encoding-command' | ||
| 639 | This specifies a command to use to encode the file contents for | ||
| 640 | transfer. The command should read the raw file contents from | ||
| 641 | standard input and write the encoded file contents to standard | ||
| 642 | output. In this string, the percent escape \"%f\" should be used | ||
| 643 | to indicate the file to convert. Use \"%%\" if you need a literal | ||
| 644 | percent character in your command. | ||
| 645 | * `tramp-decoding-command' | ||
| 646 | This specifies a command to use to decode file contents encoded | ||
| 647 | with `tramp-encoding-command'. The command should read from standard | ||
| 648 | input and write to standard output. | ||
| 649 | * `tramp-encoding-function' | ||
| 650 | This specifies a function to be called to encode the file contents | ||
| 651 | on the local side. This function should accept two arguments | ||
| 652 | START and END, the beginning and end of the region to encode. The | ||
| 653 | region should be replaced with the encoded contents. | ||
| 654 | * `tramp-decoding-function' | ||
| 655 | Same for decoding on the local side. | ||
| 656 | * `tramp-telnet-program' | ||
| 657 | Specifies the telnet program to use when using | ||
| 658 | `tramp-open-connection-telnet' to log in. | ||
| 659 | * `tramp-telnet-args' | ||
| 660 | Specifies list of arguments to pass to `telnet'. The hints for | ||
| 661 | `tramp-rsh-args' also apply here. | ||
| 662 | |||
| 663 | What does all this mean? Well, you should specify `tramp-rsh-program', | ||
| 664 | `tramp-telnet-program' or `tramp-su-program' for all methods; this program | ||
| 665 | is used to log in to the remote site. Then, there are two ways to | ||
| 666 | actually transfer the files between the local and the remote side. | ||
| 667 | One way is using an additional rcp-like program. If you want to do | ||
| 668 | this, set `tramp-rcp-program' in the method. | ||
| 669 | |||
| 670 | Another possibility for file transfer is inline transfer, i.e. the | ||
| 671 | file is passed through the same buffer used by `tramp-rsh-program'. In | ||
| 672 | this case, the file contents need to be protected since the | ||
| 673 | `tramp-rsh-program' might use escape codes or the connection might not | ||
| 674 | be eight-bit clean. Therefore, file contents are encoded for transit. | ||
| 675 | |||
| 676 | Two possibilities for encoding are uuencode/uudecode and mimencode. | ||
| 677 | For uuencode/uudecode you want to set `tramp-encoding-command' to | ||
| 678 | something like \"uuencode\" and `tramp-decoding-command' to \"uudecode | ||
| 679 | -p\". For mimencode you want to set `tramp-encoding-command' to | ||
| 680 | something like \"mimencode -b\" and `tramp-decoding-command' to | ||
| 681 | \"mimencode -b -u\". | ||
| 682 | |||
| 683 | When using inline transfer, you can use a program or a Lisp function | ||
| 684 | on the local side to encode or decode the file contents. Set the | ||
| 685 | `tramp-encoding-function' and `tramp-decoding-function' parameters to nil | ||
| 686 | in order to use the commands or to the function to use. It is | ||
| 687 | possible to specify one function and the other parameter as nil. | ||
| 688 | |||
| 689 | So, to summarize: if the method is an inline method, you must specify | ||
| 690 | `tramp-encoding-command' and `tramp-decoding-command', and | ||
| 691 | `tramp-rcp-program' must be nil. If the method is out of band, then | ||
| 692 | you must specify `tramp-rcp-program' and `tramp-rcp-args' and | ||
| 693 | `tramp-encoding-command' and `tramp-decoding-command' must be nil. | ||
| 694 | Every method, inline or out of band, must specify | ||
| 695 | `tramp-connection-function' plus the associated arguments (for | ||
| 696 | example, the telnet program if you chose | ||
| 697 | `tramp-open-connection-telnet'). | ||
| 698 | |||
| 699 | Notes: | ||
| 700 | |||
| 701 | When using `tramp-open-connection-su' the phrase `open connection to a | ||
| 702 | remote host' sounds strange, but it is used nevertheless, for | ||
| 703 | consistency. No connection is opened to a remote host, but `su' is | ||
| 704 | started on the local host. You are not allowed to specify a remote | ||
| 705 | host other than `localhost' or the name of the local host. | ||
| 706 | |||
| 707 | Using a uuencode/uudecode inline method is discouraged, please use one | ||
| 708 | of the base64 methods instead since base64 encoding is much more | ||
| 709 | reliable and the commands are more standardized between the different | ||
| 710 | Unix versions. But if you can't use base64 for some reason, please | ||
| 711 | note that the default uudecode command does not work well for some | ||
| 712 | Unices, in particular AIX and Irix. For AIX, you might want to use | ||
| 713 | the following command for uudecode: | ||
| 714 | |||
| 715 | sed '/^begin/d;/^[` ]$/d;/^end/d' | iconv -f uucode -t ISO8859-1 | ||
| 716 | |||
| 717 | For Irix, no solution is known yet." | ||
| 718 | :group 'tramp | ||
| 719 | :type '(repeat | ||
| 720 | (cons string | ||
| 721 | (set (list (const tramp-connection-function) function) | ||
| 722 | (list (const tramp-rsh-program) | ||
| 723 | (choice (const nil) string)) | ||
| 724 | (list (const tramp-rcp-program) | ||
| 725 | (choice (const nil) string)) | ||
| 726 | (list (const tramp-remote-sh) | ||
| 727 | (choice (const nil) string)) | ||
| 728 | (list (const tramp-rsh-args) (repeat string)) | ||
| 729 | (list (const tramp-rcp-args) (repeat string)) | ||
| 730 | (list (const tramp-rcp-keep-date-arg) | ||
| 731 | (choice (const nil) string)) | ||
| 732 | (list (const tramp-su-program) | ||
| 733 | (choice (const nil) string)) | ||
| 734 | (list (const tramp-su-args) (repeat string)) | ||
| 735 | (list (const tramp-encoding-command) | ||
| 736 | (choice (const nil) string)) | ||
| 737 | (list (const tramp-decoding-command) | ||
| 738 | (choice (const nil) string)) | ||
| 739 | (list (const tramp-encoding-function) | ||
| 740 | (choice (const nil) function)) | ||
| 741 | (list (const tramp-decoding-function) | ||
| 742 | (choice (const nil) function)) | ||
| 743 | (list (const tramp-telnet-program) | ||
| 744 | (choice (const nil) string)) | ||
| 745 | (list (const tramp-telnet-args) (repeat string)))))) | ||
| 746 | |||
| 747 | (defcustom tramp-multi-methods '("multi" "multiu") | ||
| 748 | "*List of multi-hop methods. | ||
| 749 | Each entry in this list should be a method name as mentioned in the | ||
| 750 | variable `tramp-methods'." | ||
| 751 | :group 'tramp | ||
| 752 | :type '(repeat string)) | ||
| 753 | |||
| 754 | (defcustom tramp-multi-connection-function-alist | ||
| 755 | '(("telnet" tramp-multi-connect-telnet "telnet %h%n") | ||
| 756 | ("rsh" tramp-multi-connect-rlogin "rsh %h -l %u%n") | ||
| 757 | ("ssh" tramp-multi-connect-rlogin "ssh %h -l %u%n") | ||
| 758 | ("su" tramp-multi-connect-su "su - %u%n") | ||
| 759 | ("sudo" tramp-multi-connect-su "sudo -u %u -s%n")) | ||
| 760 | "*List of connection functions for multi-hop methods. | ||
| 761 | Each list item is a list of three items (METHOD FUNCTION COMMAND), | ||
| 762 | where METHOD is the name as used in the file name, FUNCTION is the | ||
| 763 | function to be executed, and COMMAND is the shell command used for | ||
| 764 | connecting. | ||
| 765 | |||
| 766 | COMMAND may contain percent escapes. `%u' will be replaced with the | ||
| 767 | user name, `%h' will be replaced with the host name, and `%n' will be | ||
| 768 | replaced with an end-of-line character, as specified in the variable | ||
| 769 | `tramp-rsh-end-of-line'. Use `%%' for a literal percent character. | ||
| 770 | Note that the interpretation of the percent escapes also depends on | ||
| 771 | the FUNCTION. For example, the `%u' escape is forbidden with the | ||
| 772 | function `tramp-multi-connect-telnet'. See the documentation of the | ||
| 773 | various functions for details." | ||
| 774 | :group 'tramp | ||
| 775 | :type '(repeat (list string function string))) | ||
| 776 | |||
| 777 | (defcustom tramp-default-method "rcp" | ||
| 778 | "*Default method to use for transferring files. | ||
| 779 | See `tramp-methods' for possibilities." | ||
| 780 | :group 'tramp | ||
| 781 | :type 'string) | ||
| 782 | |||
| 783 | (defcustom tramp-rsh-end-of-line "\n" | ||
| 784 | "*String used for end of line in rsh connections. | ||
| 785 | I don't think this ever needs to be changed, so please tell me about it | ||
| 786 | if you need to change this." | ||
| 787 | :group 'tramp | ||
| 788 | :type 'string) | ||
| 789 | |||
| 790 | (defcustom tramp-remote-path | ||
| 791 | '("/bin" "/usr/bin" "/usr/sbin" "/usr/local/bin" "/usr/ccs/bin" | ||
| 792 | "/local/bin" "/local/freeware/bin" "/local/gnu/bin" | ||
| 793 | "/usr/freeware/bin" "/usr/pkg/bin" "/usr/contrib/bin") | ||
| 794 | "*List of directories to search for executables on remote host. | ||
| 795 | Please notify me about other semi-standard directories to include here. | ||
| 796 | |||
| 797 | You can use `~' in this list, but when searching for a shell which groks | ||
| 798 | tilde expansion, all directory names starting with `~' will be ignored." | ||
| 799 | :group 'tramp | ||
| 800 | :type '(repeat string)) | ||
| 801 | |||
| 802 | (defcustom tramp-login-prompt-regexp | ||
| 803 | ".*ogin: *$" | ||
| 804 | "*Regexp matching login-like prompts. | ||
| 805 | The regexp should match the whole line." | ||
| 806 | :group 'tramp | ||
| 807 | :type 'regexp) | ||
| 808 | |||
| 809 | (defcustom tramp-password-prompt-regexp | ||
| 810 | "^.*\\([pP]assword\\|passphrase.*\\):\^@? *$" | ||
| 811 | "*Regexp matching password-like prompts. | ||
| 812 | The regexp should match the whole line. | ||
| 813 | |||
| 814 | The `sudo' program appears to insert a `^@' character into the prompt." | ||
| 815 | :group 'tramp | ||
| 816 | :type 'regexp) | ||
| 817 | |||
| 818 | (defcustom tramp-wrong-passwd-regexp | ||
| 819 | (concat "^.*\\(Permission denied.\\|Login [Ii]ncorrect\\|" | ||
| 820 | "Received signal [0-9]+\\|Connection \\(refused\\|closed\\)\\|" | ||
| 821 | "Sorry, try again.\\|Name or service not known\\).*$") | ||
| 822 | "*Regexp matching a `login failed' message. | ||
| 823 | The regexp should match the whole line." | ||
| 824 | :group 'tramp | ||
| 825 | :type 'regexp) | ||
| 826 | |||
| 827 | (defcustom tramp-temp-name-prefix "tramp." | ||
| 828 | "*Prefix to use for temporary files. | ||
| 829 | If this is a relative file name (such as \"tramp.\"), it is considered | ||
| 830 | relative to the directory name returned by the function | ||
| 831 | `tramp-temporary-file-directory' (which see). It may also be an | ||
| 832 | absolute file name; don't forget to include a prefix for the filename | ||
| 833 | part, though." | ||
| 834 | :group 'tramp | ||
| 835 | :type 'string) | ||
| 836 | |||
| 837 | (defcustom tramp-discard-garbage nil | ||
| 838 | "*If non-nil, try to discard garbage sent by remote shell. | ||
| 839 | Some shells send such garbage upon connection setup." | ||
| 840 | :group 'tramp | ||
| 841 | :type 'boolean) | ||
| 842 | |||
| 843 | ;; File name format. | ||
| 844 | |||
| 845 | (defcustom tramp-file-name-structure | ||
| 846 | (list "\\`/\\[\\(\\([a-zA-Z0-9]+\\)/\\)?\\(\\([-a-zA-Z0-9_#/:]+\\)@\\)?\\([-a-zA-Z0-9_#/:@.]+\\)\\]\\(.*\\)\\'" | ||
| 847 | 2 4 5 6) | ||
| 848 | "*List of five elements (REGEXP METHOD USER HOST FILE), detailing \ | ||
| 849 | the tramp file name structure. | ||
| 850 | |||
| 851 | The first element REGEXP is a regular expression matching a tramp file | ||
| 852 | name. The regex should contain parentheses around the method name, | ||
| 853 | the user name, the host name, and the file name parts. | ||
| 854 | |||
| 855 | The second element METHOD is a number, saying which pair of | ||
| 856 | parentheses matches the method name. The third element USER is | ||
| 857 | similar, but for the user name. The fourth element HOST is similar, | ||
| 858 | but for the host name. The fifth element FILE is for the file name. | ||
| 859 | These numbers are passed directly to `match-string', which see. That | ||
| 860 | means the opening parentheses are counted to identify the pair. | ||
| 861 | |||
| 862 | See also `tramp-file-name-regexp' and `tramp-make-tramp-file-format'." | ||
| 863 | :group 'tramp | ||
| 864 | :type '(list (regexp :tag "File name regexp") | ||
| 865 | (integer :tag "Paren pair for method name") | ||
| 866 | (integer :tag "Paren pair for user name ") | ||
| 867 | (integer :tag "Paren pair for host name ") | ||
| 868 | (integer :tag "Paren pair for file name "))) | ||
| 869 | |||
| 870 | ;;;###autoload | ||
| 871 | (defcustom tramp-file-name-regexp "\\`/\\[.*\\]" | ||
| 872 | "*Regular expression matching file names handled by tramp. | ||
| 873 | This regexp should match tramp file names but no other file names. | ||
| 874 | \(When tramp.el is loaded, this regular expression is prepended to | ||
| 875 | `file-name-handler-alist', and that is searched sequentially. Thus, | ||
| 876 | if the tramp entry appears rather early in the `file-name-handler-alist' | ||
| 877 | and is a bit too general, then some files might be considered tramp | ||
| 878 | files which are not really tramp files. | ||
| 879 | |||
| 880 | Please note that the entry in `file-name-handler-alist' is made when | ||
| 881 | this file (tramp.el) is loaded. This means that this variable must be set | ||
| 882 | before loading tramp.el. Alternatively, `file-name-handler-alist' can be | ||
| 883 | updated after changing this variable. | ||
| 884 | |||
| 885 | Also see `tramp-file-name-structure' and `tramp-make-tramp-file-format'." | ||
| 886 | :group 'tramp | ||
| 887 | :type 'regexp) | ||
| 888 | |||
| 889 | (defcustom tramp-make-tramp-file-format "/[%m/%u@%h]%p" | ||
| 890 | "*Format string saying how to construct tramp file name. | ||
| 891 | `%m' is replaced by the method name. | ||
| 892 | `%u' is replaced by the user name. | ||
| 893 | `%h' is replaced by the host name. | ||
| 894 | `%p' is replaced by the file name. | ||
| 895 | `%%' is replaced by %. | ||
| 896 | |||
| 897 | Also see `tramp-file-name-structure' and `tramp-file-name-regexp'." | ||
| 898 | :group 'tramp | ||
| 899 | :type 'string) | ||
| 900 | |||
| 901 | ;; HHH: New. This format spec is made to handle the cases where the | ||
| 902 | ;; user does not provide a user name for the connection. | ||
| 903 | (defcustom tramp-make-tramp-file-user-nil-format "/[%m/%h]%p" | ||
| 904 | "*Format string saying how to construct tramp file name when the user name is not known. | ||
| 905 | `%m' is replaced by the method name. | ||
| 906 | `%h' is replaced by the host name. | ||
| 907 | `%p' is replaced by the file name. | ||
| 908 | `%%' is replaced by %. | ||
| 909 | |||
| 910 | Also see `tramp-make-tramp-file-format', `tramp-file-name-structure', and `tramp-file-name-regexp'." | ||
| 911 | :group 'tramp | ||
| 912 | :type 'string) | ||
| 913 | |||
| 914 | (defcustom tramp-multi-file-name-structure | ||
| 915 | (list (concat | ||
| 916 | ;; prefix | ||
| 917 | "\\`/\\[\\(\\([a-z0-9]+\\)\\)?" | ||
| 918 | ;; regexp specifying a hop | ||
| 919 | "\\(\\(%s\\)+\\)" | ||
| 920 | ;; path name | ||
| 921 | "\\]\\(.*\\)\\'") | ||
| 922 | 2 ;number of pair to match method | ||
| 923 | 3 ;number of pair to match hops | ||
| 924 | -1) ;number of pair to match path | ||
| 925 | |||
| 926 | "*Describes the file name structure of `multi' files. | ||
| 927 | Multi files allow you to contact a remote host in several hops. | ||
| 928 | This is a list of four elements (REGEXP METHOD HOP PATH). | ||
| 929 | |||
| 930 | The first element, REGEXP, gives a regular expression to match against | ||
| 931 | the file name. In this regular expression, `%s' is replaced with the | ||
| 932 | value of `tramp-multi-file-name-hop-structure'. (Note: in order to | ||
| 933 | allow multiple hops, you normally want to use something like | ||
| 934 | \"\\\\(\\\\(%s\\\\)+\\\\)\" in the regular expression. The outer pair | ||
| 935 | of parentheses is used for the HOP element, see below.) | ||
| 936 | |||
| 937 | All remaining elements are numbers. METHOD gives the number of the | ||
| 938 | paren pair which matches the method name. HOP gives the number of the | ||
| 939 | paren pair which matches the hop sequence. PATH gives the number of | ||
| 940 | the paren pair which matches the path name on the remote host. | ||
| 941 | |||
| 942 | PATH can also be negative, which means to count from the end. Ie, a | ||
| 943 | value of -1 means the last paren pair. | ||
| 944 | |||
| 945 | I think it would be good if the regexp matches the whole of the | ||
| 946 | string, but I haven't actually tried what happens if it doesn't..." | ||
| 947 | :group 'tramp | ||
| 948 | :type '(list (regexp :tag "File name regexp") | ||
| 949 | (integer :tag "Paren pair for method name") | ||
| 950 | (integer :tag "Paren pair for hops") | ||
| 951 | (integer :tag "Paren pair to match path"))) | ||
| 952 | |||
| 953 | (defcustom tramp-multi-file-name-hop-structure | ||
| 954 | (list "/\\([a-z0-9_]+\\):\\([a-z0-9_]+\\)@\\([a-z0-9.-]+\\)" | ||
| 955 | 1 2 3) | ||
| 956 | "*Describes the structure of a hop in multi files. | ||
| 957 | This is a list of four elements (REGEXP METHOD USER HOST). First | ||
| 958 | element REGEXP is used to match against the hop. Pair number METHOD | ||
| 959 | matches the method of one hop, pair number USER matches the user of | ||
| 960 | one hop, pair number HOST matches the host of one hop. | ||
| 961 | |||
| 962 | This regular expression should match exactly all of one hop." | ||
| 963 | :group 'tramp | ||
| 964 | :type '(list (regexp :tag "Hop regexp") | ||
| 965 | (integer :tag "Paren pair for method name") | ||
| 966 | (integer :tag "Paren pair for user name") | ||
| 967 | (integer :tag "Paren pair for host name"))) | ||
| 968 | |||
| 969 | (defcustom tramp-make-multi-tramp-file-format | ||
| 970 | (list "/[%m" "/%m:%u@%h" "]%p") | ||
| 971 | "*Describes how to construct a `multi' file name. | ||
| 972 | This is a list of three elements PREFIX, HOP and PATH. | ||
| 973 | |||
| 974 | The first element PREFIX says how to construct the prefix, the second | ||
| 975 | element HOP specifies what each hop looks like, and the final element | ||
| 976 | PATH says how to construct the path name. | ||
| 977 | |||
| 978 | In PREFIX, `%%' means `%' and `%m' means the method name. | ||
| 979 | |||
| 980 | In HOP, `%%' means `%' and `%m', `%u', `%h' mean the hop method, hop | ||
| 981 | user and hop host, respectively. | ||
| 982 | |||
| 983 | In PATH, `%%' means `%' and `%p' means the path name. | ||
| 984 | |||
| 985 | The resulting file name always contains one copy of PREFIX and one | ||
| 986 | copy of PATH, but there is one copy of HOP for each hop in the file | ||
| 987 | name. | ||
| 988 | |||
| 989 | Note: the current implementation requires the prefix to contain the | ||
| 990 | method name, followed by all the hops, and the path name must come | ||
| 991 | last." | ||
| 992 | :group 'tramp | ||
| 993 | :type '(list string string string)) | ||
| 994 | |||
| 995 | (defcustom tramp-terminal-type "dumb" | ||
| 996 | "*Value of TERM environment variable for logging in to remote host. | ||
| 997 | Because Tramp wants to parse the output of the remote shell, it is easily | ||
| 998 | confused by ANSI color escape sequences and suchlike. Often, shell init | ||
| 999 | files conditionalize this setup based on the TERM environment variable." | ||
| 1000 | :group 'tramp | ||
| 1001 | :type 'string) | ||
| 1002 | |||
| 1003 | (defcustom tramp-completion-without-shell-p nil | ||
| 1004 | "*If nil, use shell wildcards for completion, else rely on Lisp only. | ||
| 1005 | Using shell wildcards for completions has the advantage that it can be | ||
| 1006 | fast even in large directories, but completion is always | ||
| 1007 | case-sensitive. Relying on Lisp only means that case-insensitive | ||
| 1008 | completion is possible (subject to the variable `completion-ignore-case'), | ||
| 1009 | but it might be slow on large directories." | ||
| 1010 | :group 'tramp | ||
| 1011 | :type 'boolean) | ||
| 1012 | |||
| 1013 | ;;; Internal Variables: | ||
| 1014 | |||
| 1015 | (defvar tramp-buffer-file-attributes nil | ||
| 1016 | "Holds the `ls -ild' output for the current buffer. | ||
| 1017 | This variable is local to each buffer. It is not used if the remote | ||
| 1018 | machine groks Perl. If it is used, it's used as an emulation for | ||
| 1019 | the visited file modtime.") | ||
| 1020 | (make-variable-buffer-local 'tramp-buffer-file-attributes) | ||
| 1021 | |||
| 1022 | (defvar tramp-end-of-output "/////" | ||
| 1023 | "String used to recognize end of output.") | ||
| 1024 | |||
| 1025 | (defvar tramp-connection-function nil | ||
| 1026 | "This internal variable holds a parameter for `tramp-methods'. | ||
| 1027 | In the connection buffer, this variable has the value of the like-named | ||
| 1028 | method parameter, as specified in `tramp-methods' (which see).") | ||
| 1029 | |||
| 1030 | (defvar tramp-remote-sh nil | ||
| 1031 | "This internal variable holds a parameter for `tramp-methods'. | ||
| 1032 | In the connection buffer, this variable has the value of the like-named | ||
| 1033 | method parameter, as specified in `tramp-methods' (which see).") | ||
| 1034 | |||
| 1035 | (defvar tramp-rsh-program nil | ||
| 1036 | "This internal variable holds a parameter for `tramp-methods'. | ||
| 1037 | In the connection buffer, this variable has the value of the like-named | ||
| 1038 | method parameter, as specified in `tramp-methods' (which see).") | ||
| 1039 | |||
| 1040 | (defvar tramp-rsh-args nil | ||
| 1041 | "This internal variable holds a parameter for `tramp-methods'. | ||
| 1042 | In the connection buffer, this variable has the value of the like-named | ||
| 1043 | method parameter, as specified in `tramp-methods' (which see).") | ||
| 1044 | |||
| 1045 | (defvar tramp-rcp-program nil | ||
| 1046 | "This internal variable holds a parameter for `tramp-methods'. | ||
| 1047 | In the connection buffer, this variable has the value of the like-named | ||
| 1048 | method parameter, as specified in `tramp-methods' (which see).") | ||
| 1049 | |||
| 1050 | (defvar tramp-rcp-args nil | ||
| 1051 | "This internal variable holds a parameter for `tramp-methods'. | ||
| 1052 | In the connection buffer, this variable has the value of the like-named | ||
| 1053 | method parameter, as specified in `tramp-methods' (which see).") | ||
| 1054 | |||
| 1055 | (defvar tramp-rcp-keep-date-arg nil | ||
| 1056 | "This internal variable holds a parameter for `tramp-methods'. | ||
| 1057 | In the connection buffer, this variable has the value of the like-named | ||
| 1058 | method parameter, as specified in `tramp-methods' (which see).") | ||
| 1059 | |||
| 1060 | (defvar tramp-encoding-command nil | ||
| 1061 | "This internal variable holds a parameter for `tramp-methods'. | ||
| 1062 | In the connection buffer, this variable has the value of the like-named | ||
| 1063 | method parameter, as specified in `tramp-methods' (which see).") | ||
| 1064 | |||
| 1065 | (defvar tramp-decoding-command nil | ||
| 1066 | "This internal variable holds a parameter for `tramp-methods'. | ||
| 1067 | In the connection buffer, this variable has the value of the like-named | ||
| 1068 | method parameter, as specified in `tramp-methods' (which see).") | ||
| 1069 | |||
| 1070 | (defvar tramp-encoding-function nil | ||
| 1071 | "This internal variable holds a parameter for `tramp-methods'. | ||
| 1072 | In the connection buffer, this variable has the value of the like-named | ||
| 1073 | method parameter, as specified in `tramp-methods' (which see).") | ||
| 1074 | |||
| 1075 | (defvar tramp-decoding-function nil | ||
| 1076 | "This internal variable holds a parameter for `tramp-methods'. | ||
| 1077 | In the connection buffer, this variable has the value of the like-named | ||
| 1078 | method parameter, as specified in `tramp-methods' (which see).") | ||
| 1079 | |||
| 1080 | (defvar tramp-telnet-program nil | ||
| 1081 | "This internal variable holds a parameter for `tramp-methods'. | ||
| 1082 | In the connection buffer, this variable has the value of the like-named | ||
| 1083 | method parameter, as specified in `tramp-methods' (which see).") | ||
| 1084 | |||
| 1085 | (defvar tramp-telnet-args nil | ||
| 1086 | "This internal variable holds a parameter for `tramp-methods'. | ||
| 1087 | In the connection buffer, this variable has the value of the like-named | ||
| 1088 | method parameter, as specified in `tramp-methods' (which see).") | ||
| 1089 | |||
| 1090 | ;; CCC `local in each buffer'? | ||
| 1091 | (defvar tramp-ls-command nil | ||
| 1092 | "This command is used to get a long listing with numeric user and group ids. | ||
| 1093 | This variable is automatically made buffer-local to each rsh process buffer | ||
| 1094 | upon opening the connection.") | ||
| 1095 | |||
| 1096 | (defvar tramp-current-multi-method nil | ||
| 1097 | "Name of `multi' connection method for this *tramp* buffer, or nil if not multi. | ||
| 1098 | This variable is automatically made buffer-local to each rsh process buffer | ||
| 1099 | upon opening the connection.") | ||
| 1100 | |||
| 1101 | (defvar tramp-current-method nil | ||
| 1102 | "Connection method for this *tramp* buffer. | ||
| 1103 | This variable is automatically made buffer-local to each rsh process buffer | ||
| 1104 | upon opening the connection.") | ||
| 1105 | |||
| 1106 | (defvar tramp-current-user nil | ||
| 1107 | "Remote login name for this *tramp* buffer. | ||
| 1108 | This variable is automatically made buffer-local to each rsh process buffer | ||
| 1109 | upon opening the connection.") | ||
| 1110 | |||
| 1111 | (defvar tramp-current-host nil | ||
| 1112 | "Remote host for this *tramp* buffer. | ||
| 1113 | This variable is automatically made buffer-local to each rsh process buffer | ||
| 1114 | upon opening the connection.") | ||
| 1115 | |||
| 1116 | (defvar tramp-test-groks-nt nil | ||
| 1117 | "Whether the `test' command groks the `-nt' switch. | ||
| 1118 | \(`test A -nt B' tests if file A is newer than file B.) | ||
| 1119 | This variable is automatically made buffer-local to each rsh process buffer | ||
| 1120 | upon opening the connection.") | ||
| 1121 | |||
| 1122 | (defvar tramp-file-exists-command nil | ||
| 1123 | "Command to use for checking if a file exists. | ||
| 1124 | This variable is automatically made buffer-local to each rsh process buffer | ||
| 1125 | upon opening the connection.") | ||
| 1126 | |||
| 1127 | ;; Perl script to implement `file-attributes' in a Lisp `read'able output. | ||
| 1128 | ;; If you are hacking on this, note that you get *no* output unless this | ||
| 1129 | ;; spits out a complete line, including the '\n' at the end. | ||
| 1130 | (defconst tramp-perl-file-attributes (concat | ||
| 1131 | "$f = $ARGV[0]; | ||
| 1132 | @s = lstat($f); | ||
| 1133 | if (($s[2] & 0170000) == 0120000) { $l = readlink($f); $l = \"\\\"$l\\\"\"; } | ||
| 1134 | elsif (($s[2] & 0170000) == 040000) { $l = \"t\"; } | ||
| 1135 | else { $l = \"nil\" }; | ||
| 1136 | printf(\"(%s %u %u %u (%u %u) (%u %u) (%u %u) %u %u t (%u . %u) (%u %u))\\n\", | ||
| 1137 | $l, $s[3], $s[4], $s[5], $s[8] >> 16 & 0xffff, $s[8] & 0xffff, | ||
| 1138 | $s[9] >> 16 & 0xffff, $s[9] & 0xffff, $s[10] >> 16 & 0xffff, $s[10] & 0xffff, | ||
| 1139 | $s[7], $s[2], $s[1] >> 16 & 0xffff, $s[1] & 0xffff, $s[0] >> 16 & 0xffff, $s[0] & 0xffff);" | ||
| 1140 | ) | ||
| 1141 | "Perl script to produce output suitable for use with `file-attributes' | ||
| 1142 | on the remote file system.") | ||
| 1143 | |||
| 1144 | ;; Perl script to implement `mime-encode' | ||
| 1145 | (defvar tramp-perl-mime-encode (concat | ||
| 1146 | "sub encode_base64 ($); | ||
| 1147 | my $buf; | ||
| 1148 | while(read(STDIN, $buf, 60*57)) { print encode_base64($buf) } | ||
| 1149 | sub encode_base64 ($) { | ||
| 1150 | my $res = \"\"; | ||
| 1151 | my $eol = \"\n\"; | ||
| 1152 | pos($_[0]) = 0; # ensure start at the beginning | ||
| 1153 | while ($_[0] =~ /(.{1,45})/gs) { | ||
| 1154 | $res .= substr(pack(\"u\", $1), 1); | ||
| 1155 | chop($res); | ||
| 1156 | } | ||
| 1157 | $res =~ tr|` -_|AA-Za-z0-9+/|; # `# help emacs | ||
| 1158 | # fix padding at the end | ||
| 1159 | my $padding = (3 - length($_[0]) % 3) % 3; | ||
| 1160 | $res =~ s/.{$padding}$/\"=\" x $padding/e if $padding; | ||
| 1161 | # break encoded string into lines of no more than 76 characters each | ||
| 1162 | if (length $eol) { | ||
| 1163 | $res =~ s/(.{1,76})/$1$eol/g; | ||
| 1164 | } | ||
| 1165 | $res;}")) | ||
| 1166 | |||
| 1167 | ;; Perl script to implement `mime-decode' | ||
| 1168 | (defvar tramp-perl-mime-decode (concat | ||
| 1169 | "sub decode_base64 ($); | ||
| 1170 | my $buf; | ||
| 1171 | while(read(STDIN, $buf, 60*57)) { print decode_base64($buf) } | ||
| 1172 | sub decode_base64 ($) { | ||
| 1173 | local($^W) = 0; # unpack(\"u\",...) gives bogus warning in 5.00[123] | ||
| 1174 | |||
| 1175 | my $str = shift; | ||
| 1176 | my $res = \"\"; | ||
| 1177 | |||
| 1178 | $str =~ tr|A-Za-z0-9+=/||cd; # remove non-base64 chars | ||
| 1179 | if (length($str) % 4) { | ||
| 1180 | warn(\"Length of base64 data not a multiple of 4\") | ||
| 1181 | } | ||
| 1182 | $str =~ s/=+$//; # remove padding | ||
| 1183 | $str =~ tr|A-Za-z0-9+/| -_|; # convert to uuencoded format | ||
| 1184 | while ($str =~ /(.{1,60})/gs) { | ||
| 1185 | my $len = chr(32 + length($1)*3/4); # compute length byte | ||
| 1186 | $res .= unpack(\"u\", $len . $1 ); # uudecode | ||
| 1187 | } | ||
| 1188 | $res;}")) | ||
| 1189 | |||
| 1190 | ; These values conform to `file-attributes' from XEmacs 21.2. | ||
| 1191 | ; GNU Emacs and other tools not checked. | ||
| 1192 | (defconst tramp-file-mode-type-map '((0 . "-") ; Normal file (SVID-v2 and XPG2) | ||
| 1193 | (1 . "p") ; fifo | ||
| 1194 | (2 . "c") ; character device | ||
| 1195 | (3 . "m") ; multiplexed character device (v7) | ||
| 1196 | (4 . "d") ; directory | ||
| 1197 | (5 . "?") ; Named special file (XENIX) | ||
| 1198 | (6 . "b") ; block device | ||
| 1199 | (7 . "?") ; multiplexed block device (v7) | ||
| 1200 | (8 . "-") ; regular file | ||
| 1201 | (9 . "n") ; network special file (HP-UX) | ||
| 1202 | (10 . "l") ; symlink | ||
| 1203 | (11 . "?") ; ACL shadow inode (Solaris, not userspace) | ||
| 1204 | (12 . "s") ; socket | ||
| 1205 | (13 . "D") ; door special (Solaris) | ||
| 1206 | (14 . "w")) ; whiteout (BSD) | ||
| 1207 | "A list of file types returned from the `stat' system call. | ||
| 1208 | This is used to map a mode number to a permission string.") | ||
| 1209 | |||
| 1210 | (defvar tramp-dos-coding-system | ||
| 1211 | (if (and (fboundp 'coding-system-p) | ||
| 1212 | (funcall 'coding-system-p '(dos))) | ||
| 1213 | 'dos | ||
| 1214 | 'undecided-dos) | ||
| 1215 | "Some Emacsen know the `dos' coding system, others need `undecided-dos'.") | ||
| 1216 | |||
| 1217 | |||
| 1218 | ;; New handlers should be added here. The following operations can be | ||
| 1219 | ;; handled using the normal primitives: file-name-as-directory, | ||
| 1220 | ;; file-name-directory, file-name-nondirectory, | ||
| 1221 | ;; file-name-sans-versions, get-file-buffer. | ||
| 1222 | (defconst tramp-file-name-handler-alist | ||
| 1223 | '( | ||
| 1224 | (load . tramp-handle-load) | ||
| 1225 | (make-symbolic-link . tramp-handle-make-symbolic-link) | ||
| 1226 | (file-name-directory . tramp-handle-file-name-directory) | ||
| 1227 | (file-name-nondirectory . tramp-handle-file-name-nondirectory) | ||
| 1228 | (file-truename . tramp-handle-file-truename) | ||
| 1229 | (file-exists-p . tramp-handle-file-exists-p) | ||
| 1230 | (file-directory-p . tramp-handle-file-directory-p) | ||
| 1231 | (file-executable-p . tramp-handle-file-executable-p) | ||
| 1232 | (file-accessible-directory-p . tramp-handle-file-accessible-directory-p) | ||
| 1233 | (file-readable-p . tramp-handle-file-readable-p) | ||
| 1234 | (file-regular-p . tramp-handle-file-regular-p) | ||
| 1235 | (file-symlink-p . tramp-handle-file-symlink-p) | ||
| 1236 | (file-writable-p . tramp-handle-file-writable-p) | ||
| 1237 | (file-ownership-preserved-p . tramp-handle-file-ownership-preserved-p) | ||
| 1238 | (file-newer-than-file-p . tramp-handle-file-newer-than-file-p) | ||
| 1239 | (file-attributes . tramp-handle-file-attributes) | ||
| 1240 | (file-modes . tramp-handle-file-modes) | ||
| 1241 | (file-directory-files . tramp-handle-file-directory-files) | ||
| 1242 | (directory-files . tramp-handle-directory-files) | ||
| 1243 | (file-name-all-completions . tramp-handle-file-name-all-completions) | ||
| 1244 | (file-name-completion . tramp-handle-file-name-completion) | ||
| 1245 | (add-name-to-file . tramp-handle-add-name-to-file) | ||
| 1246 | (copy-file . tramp-handle-copy-file) | ||
| 1247 | (rename-file . tramp-handle-rename-file) | ||
| 1248 | (set-file-modes . tramp-handle-set-file-modes) | ||
| 1249 | (make-directory . tramp-handle-make-directory) | ||
| 1250 | (delete-directory . tramp-handle-delete-directory) | ||
| 1251 | (delete-file . tramp-handle-delete-file) | ||
| 1252 | (directory-file-name . tramp-handle-directory-file-name) | ||
| 1253 | (shell-command . tramp-handle-shell-command) | ||
| 1254 | (insert-directory . tramp-handle-insert-directory) | ||
| 1255 | (expand-file-name . tramp-handle-expand-file-name) | ||
| 1256 | (file-local-copy . tramp-handle-file-local-copy) | ||
| 1257 | (insert-file-contents . tramp-handle-insert-file-contents) | ||
| 1258 | (write-region . tramp-handle-write-region) | ||
| 1259 | (unhandled-file-name-directory . tramp-handle-unhandled-file-name-directory) | ||
| 1260 | (dired-call-process . tramp-handle-dired-call-process) | ||
| 1261 | (dired-recursive-delete-directory | ||
| 1262 | . tramp-handle-dired-recursive-delete-directory) | ||
| 1263 | (set-visited-file-modtime . tramp-handle-set-visited-file-modtime) | ||
| 1264 | (verify-visited-file-modtime . tramp-handle-verify-visited-file-modtime)) | ||
| 1265 | "Alist of handler functions. | ||
| 1266 | Operations not mentioned here will be handled by the normal Emacs functions.") | ||
| 1267 | |||
| 1268 | ;;; For better error reporting. | ||
| 1269 | |||
| 1270 | (defun tramp-version (arg) | ||
| 1271 | "Print version number of tramp.el in minibuffer or current buffer." | ||
| 1272 | (interactive "P") | ||
| 1273 | (if arg (insert tramp-version) (message tramp-version))) | ||
| 1274 | |||
| 1275 | ;;; Internal functions which must come first. | ||
| 1276 | |||
| 1277 | (defsubst tramp-message (level fmt-string &rest args) | ||
| 1278 | "Emit a message depending on verbosity level. | ||
| 1279 | First arg LEVEL says to be quiet if `tramp-verbose' is less than LEVEL. The | ||
| 1280 | message is emitted only if `tramp-verbose' is greater than or equal to LEVEL. | ||
| 1281 | Calls function `message' with FMT-STRING as control string and the remaining | ||
| 1282 | ARGS to actually emit the message (if applicable). | ||
| 1283 | |||
| 1284 | This function expects to be called from the tramp buffer only!" | ||
| 1285 | (when (<= level tramp-verbose) | ||
| 1286 | (apply #'message (concat "tramp: " fmt-string) args) | ||
| 1287 | (when tramp-debug-buffer | ||
| 1288 | (save-excursion | ||
| 1289 | (set-buffer | ||
| 1290 | (tramp-get-debug-buffer | ||
| 1291 | tramp-current-multi-method tramp-current-method | ||
| 1292 | tramp-current-user tramp-current-host)) | ||
| 1293 | (goto-char (point-max)) | ||
| 1294 | (tramp-insert-with-face | ||
| 1295 | 'italic | ||
| 1296 | (concat "# " (apply #'format fmt-string args) "\n")))))) | ||
| 1297 | |||
| 1298 | (defun tramp-message-for-buffer | ||
| 1299 | (multi-method method user host level fmt-string &rest args) | ||
| 1300 | "Like `tramp-message' but temporarily switches to the tramp buffer. | ||
| 1301 | First three args METHOD, USER, and HOST identify the tramp buffer to use, | ||
| 1302 | remaining args passed to `tramp-message'." | ||
| 1303 | (save-excursion | ||
| 1304 | (set-buffer (tramp-get-buffer multi-method method user host)) | ||
| 1305 | (apply 'tramp-message level fmt-string args))) | ||
| 1306 | |||
| 1307 | (defsubst tramp-line-end-position nil | ||
| 1308 | "Return point at end of line. | ||
| 1309 | Calls `line-end-position' or `point-at-eol' if defined, else | ||
| 1310 | own implementation." | ||
| 1311 | (cond | ||
| 1312 | ((fboundp 'line-end-position) (funcall 'line-end-position)) | ||
| 1313 | ((fboundp 'point-at-eol) (funcall 'point-at-eol)) | ||
| 1314 | (t (save-excursion (end-of-line) (point))))) | ||
| 1315 | |||
| 1316 | ;;; File Name Handler Functions: | ||
| 1317 | |||
| 1318 | ;; The following file name handler ops are not implemented (yet?). | ||
| 1319 | |||
| 1320 | (defun tramp-handle-make-symbolic-link | ||
| 1321 | (filename linkname &optional ok-if-already-exists) | ||
| 1322 | "Like `make-symbolic-link' for tramp files. | ||
| 1323 | This function will raise an error if FILENAME and LINKNAME are not | ||
| 1324 | on the same remote host." | ||
| 1325 | (unless (or (tramp-tramp-file-p filename) | ||
| 1326 | (tramp-tramp-file-p linkname)) | ||
| 1327 | (tramp-run-real-handler 'make-symbolic-link | ||
| 1328 | (list filename linkname ok-if-already-exists))) | ||
| 1329 | (let* ((file (tramp-dissect-file-name filename)) | ||
| 1330 | (link (tramp-dissect-file-name linkname)) | ||
| 1331 | (multi (tramp-file-name-multi-method file)) | ||
| 1332 | (method (tramp-file-name-method file)) | ||
| 1333 | (user (tramp-file-name-user file)) | ||
| 1334 | (host (tramp-file-name-host file)) | ||
| 1335 | (l-multi (tramp-file-name-multi-method link)) | ||
| 1336 | (l-meth (tramp-file-name-method link)) | ||
| 1337 | (l-user (tramp-file-name-user link)) | ||
| 1338 | (l-host (tramp-file-name-host link)) | ||
| 1339 | (ln (tramp-get-remote-ln multi method user host)) | ||
| 1340 | (cwd (file-name-directory (tramp-file-name-path file)))) | ||
| 1341 | (unless ln | ||
| 1342 | (signal 'file-error (list "Making a symbolic link." | ||
| 1343 | "ln(1) does not exist on the remote host."))) | ||
| 1344 | |||
| 1345 | ;; Check that method, user, host are the same. | ||
| 1346 | (unless (equal host l-host) | ||
| 1347 | (signal 'file-error (list "Can't make symlink across hosts" host l-host))) | ||
| 1348 | (unless (equal user l-user) | ||
| 1349 | (signal 'file-error (list "Can't make symlink for different users" | ||
| 1350 | user l-user))) | ||
| 1351 | (unless (and (equal multi l-multi) | ||
| 1352 | (equal method l-meth)) | ||
| 1353 | (signal 'file-error (list "Method must be the same for making symlinks" | ||
| 1354 | multi l-multi method l-meth))) | ||
| 1355 | |||
| 1356 | ;; Do the 'confirm if exists' thing. | ||
| 1357 | (when (file-exists-p (tramp-file-name-path link)) | ||
| 1358 | ;; What to do? | ||
| 1359 | (if (or (null ok-if-already-exists) ; not allowed to exist | ||
| 1360 | (and (numberp ok-if-already-exists) | ||
| 1361 | (not (yes-or-no-p | ||
| 1362 | (format "File %s already exists; make it a link anyway? " | ||
| 1363 | (tramp-file-name-path link)))))) | ||
| 1364 | (signal 'file-already-exists (list "File already exists" | ||
| 1365 | (tramp-file-name-path link))))) | ||
| 1366 | |||
| 1367 | ;; Right, they are on the same host, regardless of user, method, etc. | ||
| 1368 | ;; We now make the link on the remote machine. This will occur as the user | ||
| 1369 | ;; that FILENAME belongs to. | ||
| 1370 | (zerop | ||
| 1371 | (tramp-send-command-and-check | ||
| 1372 | multi method user host | ||
| 1373 | (format "cd %s && %s -sf %s %s" | ||
| 1374 | cwd ln | ||
| 1375 | (tramp-file-name-path file) ; target | ||
| 1376 | (tramp-file-name-path link)) ; link name | ||
| 1377 | t)))) | ||
| 1378 | |||
| 1379 | |||
| 1380 | (defun tramp-handle-load (file &optional noerror nomessage nosuffix must-suffix) | ||
| 1381 | "Like `load' for tramp files. Not implemented!" | ||
| 1382 | (unless (file-name-absolute-p file) | ||
| 1383 | (error "Tramp cannot `load' files without absolute path name")) | ||
| 1384 | (unless nosuffix | ||
| 1385 | (cond ((file-exists-p (concat file ".elc")) | ||
| 1386 | (setq file (concat file ".elc"))) | ||
| 1387 | ((file-exists-p (concat file ".el")) | ||
| 1388 | (setq file (concat file ".el"))))) | ||
| 1389 | (when must-suffix | ||
| 1390 | ;; The first condition is always true for absolute file names. | ||
| 1391 | ;; Included for safety's sake. | ||
| 1392 | (unless (or (file-name-directory file) | ||
| 1393 | (string-match "\\.elc?\\'" file)) | ||
| 1394 | (error "File `%s' does not include a `.el' or `.elc' suffix" | ||
| 1395 | file))) | ||
| 1396 | (unless noerror | ||
| 1397 | (when (not (file-exists-p file)) | ||
| 1398 | (error "Cannot load nonexistant file `%s'" file))) | ||
| 1399 | (if (not (file-exists-p file)) | ||
| 1400 | nil | ||
| 1401 | (unless nomessage | ||
| 1402 | (message "Loading %s..." file)) | ||
| 1403 | (let ((local-copy (file-local-copy file))) | ||
| 1404 | ;; MUST-SUFFIX doesn't exist on XEmacs, so let it default to nil. | ||
| 1405 | (load local-copy noerror t t) | ||
| 1406 | (delete-file local-copy)) | ||
| 1407 | (unless nomessage | ||
| 1408 | (message "Loading %s...done" file)) | ||
| 1409 | t)) | ||
| 1410 | |||
| 1411 | ;; Path manipulation functions that grok TRAMP paths... | ||
| 1412 | (defun tramp-handle-file-name-directory (file) | ||
| 1413 | "Like `file-name-directory' but aware of TRAMP files." | ||
| 1414 | ;; everything except the last filename thing is the directory | ||
| 1415 | (let* ((v (tramp-dissect-file-name file)) | ||
| 1416 | (multi-method (tramp-file-name-multi-method v)) | ||
| 1417 | (method (tramp-file-name-method v)) | ||
| 1418 | (user (tramp-file-name-user v)) | ||
| 1419 | (host (tramp-file-name-host v)) | ||
| 1420 | (path (tramp-file-name-path v))) | ||
| 1421 | (if (or (string= path "") (string= path "/")) | ||
| 1422 | ;; For a filename like "/[foo]", we return "/". The `else' | ||
| 1423 | ;; case would return "/[foo]" unchanged. But if we do that, | ||
| 1424 | ;; then `file-expand-wildcards' ceases to work. It's not | ||
| 1425 | ;; quite clear to me what's the intuition that tells that this | ||
| 1426 | ;; behavior is the right behavior, but oh, well. | ||
| 1427 | "/" | ||
| 1428 | ;; run the command on the path portion only | ||
| 1429 | ;; CCC: This should take into account the remote machine type, no? | ||
| 1430 | ;; --daniel <daniel@danann.net> | ||
| 1431 | (tramp-make-tramp-file-name multi-method method user host | ||
| 1432 | ;; This will not recurse... | ||
| 1433 | (or (file-name-directory path) ""))))) | ||
| 1434 | |||
| 1435 | (defun tramp-handle-file-name-nondirectory (file) | ||
| 1436 | "Like `file-name-nondirectory' but aware of TRAMP files." | ||
| 1437 | (let ((v (tramp-dissect-file-name file))) | ||
| 1438 | (file-name-nondirectory (tramp-file-name-path v)))) | ||
| 1439 | |||
| 1440 | (defun tramp-handle-file-truename (filename &optional counter prev-dirs) | ||
| 1441 | "Like `file-truename' for tramp files." | ||
| 1442 | (let* ((v (tramp-dissect-file-name (tramp-handle-expand-file-name filename))) | ||
| 1443 | (multi-method (tramp-file-name-multi-method v)) | ||
| 1444 | (method (tramp-file-name-method v)) | ||
| 1445 | (user (tramp-file-name-user v)) | ||
| 1446 | (host (tramp-file-name-host v)) | ||
| 1447 | (path (tramp-file-name-path v)) | ||
| 1448 | (steps (tramp-split-string path "/")) | ||
| 1449 | (pathdir (let ((directory-sep-char ?/)) | ||
| 1450 | (file-name-as-directory path))) | ||
| 1451 | (is-dir (string= path pathdir)) | ||
| 1452 | (thisstep nil) | ||
| 1453 | (numchase 0) | ||
| 1454 | ;; Don't make the following value larger than necessary. | ||
| 1455 | ;; People expect an error message in a timely fashion when | ||
| 1456 | ;; something is wrong; otherwise they might think that Emacs | ||
| 1457 | ;; is hung. Of course, correctness has to come first. | ||
| 1458 | (numchase-limit 20) | ||
| 1459 | (result nil) ;result steps in reverse order | ||
| 1460 | (curstri "") | ||
| 1461 | symlink-target) | ||
| 1462 | (tramp-message-for-buffer | ||
| 1463 | multi-method method user host | ||
| 1464 | 10 "Finding true name for `%s'" filename) | ||
| 1465 | (while (and steps (< numchase numchase-limit)) | ||
| 1466 | (setq thisstep (pop steps)) | ||
| 1467 | (tramp-message-for-buffer | ||
| 1468 | multi-method method user host | ||
| 1469 | 10 "Check %s" | ||
| 1470 | (mapconcat 'identity | ||
| 1471 | (append '("") (reverse result) (list thisstep)) | ||
| 1472 | "/")) | ||
| 1473 | (setq symlink-target | ||
| 1474 | (nth 0 (tramp-handle-file-attributes | ||
| 1475 | (tramp-make-tramp-file-name | ||
| 1476 | multi-method method user host | ||
| 1477 | (mapconcat 'identity | ||
| 1478 | (append '("") (reverse result) (list thisstep)) | ||
| 1479 | "/"))))) | ||
| 1480 | (cond ((string= "." thisstep) | ||
| 1481 | (tramp-message-for-buffer multi-method method user host | ||
| 1482 | 10 "Ignoring step `.'")) | ||
| 1483 | ((string= ".." thisstep) | ||
| 1484 | (tramp-message-for-buffer multi-method method user host | ||
| 1485 | 10 "Processing step `..'") | ||
| 1486 | (pop result)) | ||
| 1487 | ((stringp symlink-target) | ||
| 1488 | ;; It's a symlink, follow it. | ||
| 1489 | (tramp-message-for-buffer | ||
| 1490 | multi-method method user host | ||
| 1491 | 10 "Follow symlink to %s" symlink-target) | ||
| 1492 | (setq numchase (1+ numchase)) | ||
| 1493 | (when (file-name-absolute-p symlink-target) | ||
| 1494 | (setq result nil)) | ||
| 1495 | (setq steps | ||
| 1496 | (append (tramp-split-string symlink-target "/") steps))) | ||
| 1497 | (t | ||
| 1498 | ;; It's a file. | ||
| 1499 | (setq result (cons thisstep result))))) | ||
| 1500 | (when (>= numchase numchase-limit) | ||
| 1501 | (error "Maximum number (%d) of symlinks exceeded" numchase-limit)) | ||
| 1502 | (setq result (reverse result)) | ||
| 1503 | (tramp-message-for-buffer | ||
| 1504 | multi-method method user host | ||
| 1505 | 10 "True name of `%s' is `%s'" | ||
| 1506 | filename (mapconcat 'identity (cons "" result) "/")) | ||
| 1507 | (tramp-make-tramp-file-name | ||
| 1508 | multi-method method user host | ||
| 1509 | (concat (mapconcat 'identity (cons "" result) "/") | ||
| 1510 | (if is-dir "/" ""))))) | ||
| 1511 | |||
| 1512 | ;; Basic functions. | ||
| 1513 | |||
| 1514 | (defun tramp-handle-file-exists-p (filename) | ||
| 1515 | "Like `file-exists-p' for tramp files." | ||
| 1516 | (let ((v (tramp-dissect-file-name (tramp-handle-expand-file-name filename))) | ||
| 1517 | multi-method method user host path) | ||
| 1518 | (setq multi-method (tramp-file-name-multi-method v)) | ||
| 1519 | (setq method (tramp-file-name-method v)) | ||
| 1520 | (setq user (tramp-file-name-user v)) | ||
| 1521 | (setq host (tramp-file-name-host v)) | ||
| 1522 | (setq path (tramp-file-name-path v)) | ||
| 1523 | (save-excursion | ||
| 1524 | (zerop (tramp-send-command-and-check | ||
| 1525 | multi-method method user host | ||
| 1526 | (format | ||
| 1527 | (tramp-get-file-exists-command multi-method method user host) | ||
| 1528 | (tramp-shell-quote-argument path))))))) | ||
| 1529 | |||
| 1530 | ;; CCC: This should check for an error condition and signal failure | ||
| 1531 | ;; when something goes wrong. | ||
| 1532 | ;; Daniel Pittman <daniel@danann.net> | ||
| 1533 | (defun tramp-handle-file-attributes (filename &optional nonnumeric) | ||
| 1534 | "Like `file-attributes' for tramp files. | ||
| 1535 | Optional argument NONNUMERIC means return user and group name | ||
| 1536 | rather than as numbers." | ||
| 1537 | (if (tramp-handle-file-exists-p filename) | ||
| 1538 | ;; file exists, find out stuff | ||
| 1539 | (save-excursion | ||
| 1540 | (let* ((v (tramp-dissect-file-name (tramp-handle-expand-file-name filename))) | ||
| 1541 | (multi-method (tramp-file-name-multi-method v)) | ||
| 1542 | (method (tramp-file-name-method v)) | ||
| 1543 | (user (tramp-file-name-user v)) | ||
| 1544 | (host (tramp-file-name-host v)) | ||
| 1545 | (path (tramp-file-name-path v))) | ||
| 1546 | (if (tramp-get-remote-perl multi-method method user host) | ||
| 1547 | (tramp-handle-file-attributes-with-perl multi-method method user host path nonnumeric) | ||
| 1548 | (tramp-handle-file-attributes-with-ls multi-method method user host path nonnumeric)))) | ||
| 1549 | nil)) ; no file | ||
| 1550 | |||
| 1551 | |||
| 1552 | (defun tramp-handle-file-attributes-with-ls | ||
| 1553 | (multi-method method user host path &optional nonnumeric) | ||
| 1554 | "Implement `file-attributes' for tramp files using the ls(1) command." | ||
| 1555 | (let (symlinkp dirp | ||
| 1556 | res-inode res-filemodes res-numlinks | ||
| 1557 | res-uid res-gid res-size res-symlink-target) | ||
| 1558 | (tramp-send-command | ||
| 1559 | multi-method method user host | ||
| 1560 | (format "%s %s %s" | ||
| 1561 | (tramp-get-ls-command multi-method method user host) | ||
| 1562 | (if nonnumeric "-ild" "-ildn") | ||
| 1563 | (tramp-shell-quote-argument path))) | ||
| 1564 | (tramp-wait-for-output) | ||
| 1565 | ;; parse `ls -l' output ... | ||
| 1566 | ;; ... inode | ||
| 1567 | (setq res-inode | ||
| 1568 | (condition-case err | ||
| 1569 | (read (current-buffer)) | ||
| 1570 | (invalid-read-syntax | ||
| 1571 | (when (and (equal (cadr err) | ||
| 1572 | "Integer constant overflow in reader") | ||
| 1573 | (string-match | ||
| 1574 | "^[0-9]+\\([0-9][0-9][0-9][0-9][0-9]\\)\\'" | ||
| 1575 | (caddr err))) | ||
| 1576 | (let* ((big (read (substring (caddr err) 0 | ||
| 1577 | (match-beginning 1)))) | ||
| 1578 | (small (read (match-string 1 (caddr err)))) | ||
| 1579 | (twiddle (/ small 65536))) | ||
| 1580 | (cons (+ big twiddle) | ||
| 1581 | (- small (* twiddle 65536)))))))) | ||
| 1582 | ;; ... file mode flags | ||
| 1583 | (setq res-filemodes (symbol-name (read (current-buffer)))) | ||
| 1584 | ;; ... number links | ||
| 1585 | (setq res-numlinks (read (current-buffer))) | ||
| 1586 | ;; ... uid and gid | ||
| 1587 | (setq res-uid (read (current-buffer))) | ||
| 1588 | (setq res-gid (read (current-buffer))) | ||
| 1589 | (unless nonnumeric | ||
| 1590 | (unless (numberp res-uid) (setq res-uid -1)) | ||
| 1591 | (unless (numberp res-gid) (setq res-gid -1))) | ||
| 1592 | ;; ... size | ||
| 1593 | (setq res-size (read (current-buffer))) | ||
| 1594 | ;; From the file modes, figure out other stuff. | ||
| 1595 | (setq symlinkp (eq ?l (aref res-filemodes 0))) | ||
| 1596 | (setq dirp (eq ?d (aref res-filemodes 0))) | ||
| 1597 | ;; if symlink, find out file name pointed to | ||
| 1598 | (when symlinkp | ||
| 1599 | (search-forward "-> ") | ||
| 1600 | (setq res-symlink-target | ||
| 1601 | (buffer-substring (point) | ||
| 1602 | (tramp-line-end-position)))) | ||
| 1603 | ;; return data gathered | ||
| 1604 | (list | ||
| 1605 | ;; 0. t for directory, string (name linked to) for symbolic | ||
| 1606 | ;; link, or nil. | ||
| 1607 | (or dirp res-symlink-target nil) | ||
| 1608 | ;; 1. Number of links to file. | ||
| 1609 | res-numlinks | ||
| 1610 | ;; 2. File uid. | ||
| 1611 | res-uid | ||
| 1612 | ;; 3. File gid. | ||
| 1613 | res-gid | ||
| 1614 | ;; 4. Last access time, as a list of two integers. First | ||
| 1615 | ;; integer has high-order 16 bits of time, second has low 16 | ||
| 1616 | ;; bits. | ||
| 1617 | ;; 5. Last modification time, likewise. | ||
| 1618 | ;; 6. Last status change time, likewise. | ||
| 1619 | '(0 0) '(0 0) '(0 0) ;CCC how to find out? | ||
| 1620 | ;; 7. Size in bytes (-1, if number is out of range). | ||
| 1621 | res-size | ||
| 1622 | ;; 8. File modes, as a string of ten letters or dashes as in ls -l. | ||
| 1623 | res-filemodes | ||
| 1624 | ;; 9. t iff file's gid would change if file were deleted and | ||
| 1625 | ;; recreated. | ||
| 1626 | nil ;hm? | ||
| 1627 | ;; 10. inode number. | ||
| 1628 | res-inode | ||
| 1629 | ;; 11. Device number. | ||
| 1630 | -1 ;hm? | ||
| 1631 | ))) | ||
| 1632 | |||
| 1633 | (defun tramp-handle-file-attributes-with-perl | ||
| 1634 | (multi-method method user host path &optional nonnumeric) | ||
| 1635 | "Implement `file-attributes' for tramp files using a Perl script. | ||
| 1636 | |||
| 1637 | The Perl command is sent to the remote machine when the connection | ||
| 1638 | is initially created and is kept cached by the remote shell." | ||
| 1639 | (tramp-send-command | ||
| 1640 | multi-method method user host | ||
| 1641 | (format "tramp_file_attributes %s" | ||
| 1642 | (tramp-shell-quote-argument path))) | ||
| 1643 | (tramp-wait-for-output) | ||
| 1644 | (let ((result (read (current-buffer)))) | ||
| 1645 | (setcar (nthcdr 8 result) | ||
| 1646 | (tramp-file-mode-from-int (nth 8 result))) | ||
| 1647 | result)) | ||
| 1648 | |||
| 1649 | (defun tramp-handle-set-visited-file-modtime (&optional time-list) | ||
| 1650 | "Like `set-visited-file-modtime' for tramp files." | ||
| 1651 | (unless (buffer-file-name) | ||
| 1652 | (error "Can't set-visited-file-modtime: buffer `%s' not visiting a file" | ||
| 1653 | (buffer-name))) | ||
| 1654 | (when time-list | ||
| 1655 | (tramp-run-real-handler 'set-visited-file-modtime (list time-list))) | ||
| 1656 | (let* ((coding-system-used nil) | ||
| 1657 | (f (buffer-file-name)) | ||
| 1658 | (v (tramp-dissect-file-name f)) | ||
| 1659 | (multi-method (tramp-file-name-multi-method v)) | ||
| 1660 | (method (tramp-file-name-method v)) | ||
| 1661 | (user (tramp-file-name-user v)) | ||
| 1662 | (host (tramp-file-name-host v)) | ||
| 1663 | (path (tramp-file-name-path v)) | ||
| 1664 | (attr (file-attributes f)) | ||
| 1665 | (modtime (nth 5 attr))) | ||
| 1666 | ;; We use '(0 0) as a don't-know value. See also | ||
| 1667 | ;; `tramp-handle-file-attributes-with-ls'. | ||
| 1668 | (when (boundp 'last-coding-system-used) | ||
| 1669 | (setq coding-system-used last-coding-system-used)) | ||
| 1670 | (if (not (equal modtime '(0 0))) | ||
| 1671 | (tramp-run-real-handler 'set-visited-file-modtime (list modtime)) | ||
| 1672 | (save-excursion | ||
| 1673 | (tramp-send-command | ||
| 1674 | multi-method method user host | ||
| 1675 | (format "%s -ild %s" | ||
| 1676 | (tramp-get-ls-command multi-method method user host) | ||
| 1677 | (tramp-shell-quote-argument path))) | ||
| 1678 | (tramp-wait-for-output) | ||
| 1679 | (setq attr (buffer-substring (point) | ||
| 1680 | (progn (end-of-line) (point))))) | ||
| 1681 | (setq tramp-buffer-file-attributes attr)) | ||
| 1682 | (when (boundp 'last-coding-system-used) | ||
| 1683 | (setq last-coding-system-used coding-system-used)) | ||
| 1684 | nil)) | ||
| 1685 | |||
| 1686 | ;; This function makes the same assumption as | ||
| 1687 | ;; `tramp-handle-set-visited-file-modtime'. | ||
| 1688 | (defun tramp-handle-verify-visited-file-modtime (buf) | ||
| 1689 | "Like `verify-visited-file-modtime' for tramp files." | ||
| 1690 | (with-current-buffer buf | ||
| 1691 | (let* ((f (buffer-file-name)) | ||
| 1692 | (v (tramp-dissect-file-name f)) | ||
| 1693 | (multi-method (tramp-file-name-multi-method v)) | ||
| 1694 | (method (tramp-file-name-method v)) | ||
| 1695 | (user (tramp-file-name-user v)) | ||
| 1696 | (host (tramp-file-name-host v)) | ||
| 1697 | (path (tramp-file-name-path v)) | ||
| 1698 | (attr (file-attributes f)) | ||
| 1699 | (modtime (nth 5 attr))) | ||
| 1700 | (if attr | ||
| 1701 | (if (not (equal modtime '(0 0))) | ||
| 1702 | ;; Why does `file-attributes' return a list (HIGH LOW), but | ||
| 1703 | ;; `visited-file-modtime' returns a cons (HIGH . LOW)? | ||
| 1704 | (let ((mt (visited-file-modtime))) | ||
| 1705 | (< (abs (tramp-time-diff modtime (list (car mt) (cdr mt)))) 2)) | ||
| 1706 | (save-excursion | ||
| 1707 | (tramp-send-command | ||
| 1708 | multi-method method user host | ||
| 1709 | (format "%s -ild %s" | ||
| 1710 | (tramp-get-ls-command multi-method method user host) | ||
| 1711 | (tramp-shell-quote-argument path))) | ||
| 1712 | (tramp-wait-for-output) | ||
| 1713 | (setq attr (buffer-substring (point) | ||
| 1714 | (progn (end-of-line) (point))))) | ||
| 1715 | (equal tramp-buffer-file-attributes attr)) | ||
| 1716 | ;; If file does not exist, say it is not modified. | ||
| 1717 | nil)))) | ||
| 1718 | |||
| 1719 | (defadvice clear-visited-file-modtime (after tramp activate) | ||
| 1720 | "Set `tramp-buffer-file-attributes' back to nil. | ||
| 1721 | Tramp uses this variable as an emulation for the actual modtime of the file, | ||
| 1722 | if the remote host can't provide the modtime." | ||
| 1723 | (setq tramp-buffer-file-attributes nil)) | ||
| 1724 | |||
| 1725 | (defun tramp-handle-set-file-modes (filename mode) | ||
| 1726 | "Like `set-file-modes' for tramp files." | ||
| 1727 | (let ((v (tramp-dissect-file-name filename))) | ||
| 1728 | (save-excursion | ||
| 1729 | (unless (zerop (tramp-send-command-and-check | ||
| 1730 | (tramp-file-name-multi-method v) | ||
| 1731 | (tramp-file-name-method v) | ||
| 1732 | (tramp-file-name-user v) | ||
| 1733 | (tramp-file-name-host v) | ||
| 1734 | (format "chmod %s %s" | ||
| 1735 | (tramp-decimal-to-octal mode) | ||
| 1736 | (tramp-shell-quote-argument | ||
| 1737 | (tramp-file-name-path v))))) | ||
| 1738 | (signal 'file-error | ||
| 1739 | (list "Doing chmod" | ||
| 1740 | ;; FIXME: extract the proper text from chmod's stderr. | ||
| 1741 | "error while changing file's mode" | ||
| 1742 | filename)))))) | ||
| 1743 | |||
| 1744 | ;; Simple functions using the `test' command. | ||
| 1745 | |||
| 1746 | (defun tramp-handle-file-executable-p (filename) | ||
| 1747 | "Like `file-executable-p' for tramp files." | ||
| 1748 | (zerop (tramp-run-test "-x" filename))) | ||
| 1749 | |||
| 1750 | (defun tramp-handle-file-readable-p (filename) | ||
| 1751 | "Like `file-readable-p' for tramp files." | ||
| 1752 | (zerop (tramp-run-test "-r" filename))) | ||
| 1753 | |||
| 1754 | (defun tramp-handle-file-accessible-directory-p (filename) | ||
| 1755 | "Like `file-accessible-directory-p' for tramp files." | ||
| 1756 | (and (zerop (tramp-run-test "-d" filename)) | ||
| 1757 | (zerop (tramp-run-test "-r" filename)) | ||
| 1758 | (zerop (tramp-run-test "-x" filename)))) | ||
| 1759 | |||
| 1760 | ;; When the remote shell is started, it looks for a shell which groks | ||
| 1761 | ;; tilde expansion. Here, we assume that all shells which grok tilde | ||
| 1762 | ;; expansion will also provide a `test' command which groks `-nt' (for | ||
| 1763 | ;; newer than). If this breaks, tell me about it and I'll try to do | ||
| 1764 | ;; something smarter about it. | ||
| 1765 | (defun tramp-handle-file-newer-than-file-p (file1 file2) | ||
| 1766 | "Like `file-newer-than-file-p' for tramp files." | ||
| 1767 | (cond ((not (file-exists-p file1)) | ||
| 1768 | nil) | ||
| 1769 | ((not (file-exists-p file2)) | ||
| 1770 | t) | ||
| 1771 | ;; We are sure both files exist at this point. | ||
| 1772 | (t | ||
| 1773 | (save-excursion | ||
| 1774 | (let* ((v1 (tramp-dissect-file-name file1)) | ||
| 1775 | (mm1 (tramp-file-name-multi-method v1)) | ||
| 1776 | (m1 (tramp-file-name-method v1)) | ||
| 1777 | (u1 (tramp-file-name-user v1)) | ||
| 1778 | (h1 (tramp-file-name-host v1)) | ||
| 1779 | (v2 (tramp-dissect-file-name file2)) | ||
| 1780 | (mm2 (tramp-file-name-multi-method v2)) | ||
| 1781 | (m2 (tramp-file-name-method v2)) | ||
| 1782 | (u2 (tramp-file-name-user v2)) | ||
| 1783 | (h2 (tramp-file-name-host v2))) | ||
| 1784 | (unless (and (equal mm1 mm2) | ||
| 1785 | (equal m1 m2) | ||
| 1786 | (equal u1 u2) | ||
| 1787 | (equal h1 h2)) | ||
| 1788 | (signal 'file-error | ||
| 1789 | (list "Files must have same method, user, host" | ||
| 1790 | file1 file2))) | ||
| 1791 | (unless (and (tramp-tramp-file-p file1) | ||
| 1792 | (tramp-tramp-file-p file2)) | ||
| 1793 | (signal 'file-error | ||
| 1794 | (list "Files must be tramp files on same host" | ||
| 1795 | file1 file2))) | ||
| 1796 | (if (tramp-get-test-groks-nt mm1 m1 u1 h1) | ||
| 1797 | (zerop (tramp-run-test2 "test" file1 file2 "-nt")) | ||
| 1798 | (zerop (tramp-run-test2 "tramp_test_nt" file1 file2)))))))) | ||
| 1799 | |||
| 1800 | ;; Functions implemented using the basic functions above. | ||
| 1801 | |||
| 1802 | (defun tramp-handle-file-modes (filename) | ||
| 1803 | "Like `file-modes' for tramp files." | ||
| 1804 | (when (file-exists-p filename) | ||
| 1805 | (tramp-mode-string-to-int | ||
| 1806 | (nth 8 (tramp-handle-file-attributes filename))))) | ||
| 1807 | |||
| 1808 | (defun tramp-handle-file-directory-p (filename) | ||
| 1809 | "Like `file-directory-p' for tramp files." | ||
| 1810 | ;; Care must be taken that this function returns `t' for symlinks | ||
| 1811 | ;; pointing to directories. Surely the most obvious implementation | ||
| 1812 | ;; would be `test -d', but that returns false for such symlinks. | ||
| 1813 | ;; CCC: Stefan Monnier says that `test -d' follows symlinks. And | ||
| 1814 | ;; I now think he's right. So we could be using `test -d', couldn't | ||
| 1815 | ;; we? | ||
| 1816 | ;; | ||
| 1817 | ;; Alternatives: `cd %s', `test -d %s' | ||
| 1818 | (save-excursion | ||
| 1819 | (let ((v (tramp-dissect-file-name filename))) | ||
| 1820 | (zerop | ||
| 1821 | (tramp-send-command-and-check | ||
| 1822 | (tramp-file-name-multi-method v) (tramp-file-name-method v) | ||
| 1823 | (tramp-file-name-user v) (tramp-file-name-host v) | ||
| 1824 | (format "test -d %s" | ||
| 1825 | (tramp-shell-quote-argument (tramp-file-name-path v))) | ||
| 1826 | t))))) ;run command in subshell | ||
| 1827 | |||
| 1828 | (defun tramp-handle-file-regular-p (filename) | ||
| 1829 | "Like `file-regular-p' for tramp files." | ||
| 1830 | (and (tramp-handle-file-exists-p filename) | ||
| 1831 | (eq ?- (aref (nth 8 (tramp-handle-file-attributes filename)) 0)))) | ||
| 1832 | |||
| 1833 | (defun tramp-handle-file-symlink-p (filename) | ||
| 1834 | "Like `file-symlink-p' for tramp files." | ||
| 1835 | (let ((x (car (tramp-handle-file-attributes filename)))) | ||
| 1836 | (when (stringp x) x))) | ||
| 1837 | |||
| 1838 | (defun tramp-handle-file-writable-p (filename) | ||
| 1839 | "Like `file-writable-p' for tramp files." | ||
| 1840 | (if (tramp-handle-file-exists-p filename) | ||
| 1841 | ;; Existing files must be writable. | ||
| 1842 | (zerop (tramp-run-test "-w" filename)) | ||
| 1843 | ;; If file doesn't exist, check if directory is writable. | ||
| 1844 | (and (zerop (tramp-run-test "-d" (tramp-handle-file-name-directory filename))) | ||
| 1845 | (zerop (tramp-run-test "-w" (tramp-handle-file-name-directory filename)))))) | ||
| 1846 | |||
| 1847 | (defun tramp-handle-file-ownership-preserved-p (filename) | ||
| 1848 | "Like `file-ownership-preserved-p' for tramp files." | ||
| 1849 | (or (not (tramp-handle-file-exists-p filename)) | ||
| 1850 | ;; Existing files must be writable. | ||
| 1851 | (zerop (tramp-run-test "-O" filename)))) | ||
| 1852 | |||
| 1853 | ;; Other file name ops. | ||
| 1854 | |||
| 1855 | ;; ;; Matthias Köppe <mkoeppe@mail.math.uni-magdeburg.de> | ||
| 1856 | ;; (defun tramp-handle-directory-file-name (directory) | ||
| 1857 | ;; "Like `directory-file-name' for tramp files." | ||
| 1858 | ;; (if (and (eq (aref directory (- (length directory) 1)) ?/) | ||
| 1859 | ;; (not (eq (aref directory (- (length directory) 2)) ?:))) | ||
| 1860 | ;; (substring directory 0 (- (length directory) 1)) | ||
| 1861 | ;; directory)) | ||
| 1862 | |||
| 1863 | ;; Philippe Troin <phil@fifi.org> | ||
| 1864 | (defun tramp-handle-directory-file-name (directory) | ||
| 1865 | "Like `directory-file-name' for tramp files." | ||
| 1866 | (let ((directory-length-1 (1- (length directory)))) | ||
| 1867 | (save-match-data | ||
| 1868 | (if (and (eq (aref directory directory-length-1) ?/) | ||
| 1869 | (eq (string-match tramp-file-name-regexp directory) 0) | ||
| 1870 | (/= (match-end 0) directory-length-1)) | ||
| 1871 | (substring directory 0 directory-length-1) | ||
| 1872 | directory)))) | ||
| 1873 | |||
| 1874 | ;; Directory listings. | ||
| 1875 | |||
| 1876 | (defun tramp-handle-directory-files (directory &optional full match nosort) | ||
| 1877 | "Like `directory-files' for tramp files." | ||
| 1878 | (let ((v (tramp-dissect-file-name (tramp-handle-expand-file-name directory))) | ||
| 1879 | multi-method method user host path result x) | ||
| 1880 | (setq multi-method (tramp-file-name-multi-method v)) | ||
| 1881 | (setq method (tramp-file-name-method v)) | ||
| 1882 | (setq user (tramp-file-name-user v)) | ||
| 1883 | (setq host (tramp-file-name-host v)) | ||
| 1884 | (setq path (tramp-file-name-path v)) | ||
| 1885 | (save-excursion | ||
| 1886 | (tramp-barf-unless-okay multi-method method user host | ||
| 1887 | (concat "cd " (tramp-shell-quote-argument path)) | ||
| 1888 | nil | ||
| 1889 | 'file-error | ||
| 1890 | "tramp-handle-directory-files: couldn't `cd %s'" | ||
| 1891 | (tramp-shell-quote-argument path)) | ||
| 1892 | (tramp-send-command | ||
| 1893 | multi-method method user host | ||
| 1894 | (concat (tramp-get-ls-command multi-method method user host) | ||
| 1895 | " -a | cat")) | ||
| 1896 | (tramp-wait-for-output) | ||
| 1897 | (goto-char (point-max)) | ||
| 1898 | (while (zerop (forward-line -1)) | ||
| 1899 | (setq x (buffer-substring (point) | ||
| 1900 | (tramp-line-end-position))) | ||
| 1901 | (when (or (not match) (string-match match x)) | ||
| 1902 | (if full | ||
| 1903 | (push (concat (file-name-as-directory directory) | ||
| 1904 | x) | ||
| 1905 | result) | ||
| 1906 | (push x result)))) | ||
| 1907 | (tramp-send-command multi-method method user host "cd") | ||
| 1908 | (tramp-wait-for-output)) | ||
| 1909 | result)) | ||
| 1910 | |||
| 1911 | ;; This function should return "foo/" for directories and "bar" for | ||
| 1912 | ;; files. We use `ls -ad' to get a list of files (including | ||
| 1913 | ;; directories), and `find . -type d \! -name . -prune' to get a list | ||
| 1914 | ;; of directories. | ||
| 1915 | (defun tramp-handle-file-name-all-completions (filename directory) | ||
| 1916 | "Like `file-name-all-completions' for tramp files." | ||
| 1917 | (unless (save-match-data (string-match "/" filename)) | ||
| 1918 | (let* ((v (tramp-dissect-file-name directory)) | ||
| 1919 | (multi-method (tramp-file-name-multi-method v)) | ||
| 1920 | (method (tramp-file-name-method v)) | ||
| 1921 | (user (tramp-file-name-user v)) | ||
| 1922 | (host (tramp-file-name-host v)) | ||
| 1923 | (path (tramp-file-name-path v)) | ||
| 1924 | (nowild tramp-completion-without-shell-p) | ||
| 1925 | result) | ||
| 1926 | (save-excursion | ||
| 1927 | (tramp-barf-unless-okay | ||
| 1928 | multi-method method user host | ||
| 1929 | (format "cd %s" (tramp-shell-quote-argument path)) | ||
| 1930 | nil 'file-error | ||
| 1931 | "tramp-handle-file-name-all-completions: Couldn't `cd %s'" | ||
| 1932 | (tramp-shell-quote-argument path)) | ||
| 1933 | |||
| 1934 | ;; Get a list of directories and files, including reliably | ||
| 1935 | ;; tagging the directories with a trailing '/'. Because I | ||
| 1936 | ;; rock. --daniel@danann.net | ||
| 1937 | (tramp-send-command | ||
| 1938 | multi-method method user host | ||
| 1939 | (format (concat "%s -a %s 2>/dev/null | while read f; do " | ||
| 1940 | "if test -d \"$f\" 2>/dev/null; " | ||
| 1941 | "then echo \"$f/\"; else echo \"$f\"; fi; done") | ||
| 1942 | (tramp-get-ls-command multi-method method user host) | ||
| 1943 | (if (or nowild (zerop (length filename))) | ||
| 1944 | "" | ||
| 1945 | (format "-d %s*" (tramp-shell-quote-argument filename))))) | ||
| 1946 | |||
| 1947 | ;; Now grab the output. | ||
| 1948 | (tramp-wait-for-output) | ||
| 1949 | (goto-char (point-max)) | ||
| 1950 | (while (zerop (forward-line -1)) | ||
| 1951 | (push (buffer-substring (point) | ||
| 1952 | (tramp-line-end-position)) | ||
| 1953 | result)) | ||
| 1954 | |||
| 1955 | (tramp-send-command multi-method method user host "cd") | ||
| 1956 | (tramp-wait-for-output) | ||
| 1957 | |||
| 1958 | ;; Return the list. | ||
| 1959 | (if nowild | ||
| 1960 | (all-completions filename (mapcar 'list result)) | ||
| 1961 | result))))) | ||
| 1962 | |||
| 1963 | |||
| 1964 | ;; The following isn't needed for Emacs 20 but for 19.34? | ||
| 1965 | (defun tramp-handle-file-name-completion (filename directory) | ||
| 1966 | "Like `file-name-completion' for tramp files." | ||
| 1967 | (unless (tramp-tramp-file-p directory) | ||
| 1968 | (error | ||
| 1969 | "tramp-handle-file-name-completion invoked on non-tramp directory `%s'" | ||
| 1970 | directory)) | ||
| 1971 | ;(setq directory (tramp-handle-expand-file-name directory)) | ||
| 1972 | (try-completion | ||
| 1973 | filename | ||
| 1974 | (mapcar (lambda (x) (cons x nil)) | ||
| 1975 | (tramp-handle-file-name-all-completions filename directory)))) | ||
| 1976 | |||
| 1977 | ;; cp, mv and ln | ||
| 1978 | |||
| 1979 | (defun tramp-handle-add-name-to-file | ||
| 1980 | (filename newname &optional ok-if-already-exists) | ||
| 1981 | "Like `add-name-to-file' for tramp files." | ||
| 1982 | (let* ((v1 (when (tramp-tramp-file-p filename) | ||
| 1983 | (tramp-dissect-file-name (tramp-handle-expand-file-name filename)))) | ||
| 1984 | (v2 (when (tramp-tramp-file-p newname) | ||
| 1985 | (tramp-dissect-file-name (tramp-handle-expand-file-name newname)))) | ||
| 1986 | (mmeth1 (when v1 (tramp-file-name-multi-method v1))) | ||
| 1987 | (mmeth2 (when v2 (tramp-file-name-multi-method v2))) | ||
| 1988 | (meth1 (when v1 (tramp-file-name-method v1))) | ||
| 1989 | (meth2 (when v2 (tramp-file-name-method v2))) | ||
| 1990 | (user1 (when v1 (tramp-file-name-user v1))) | ||
| 1991 | (user2 (when v2 (tramp-file-name-user v2))) | ||
| 1992 | (host1 (when v1 (tramp-file-name-host v1))) | ||
| 1993 | (host2 (when v2 (tramp-file-name-host v2))) | ||
| 1994 | (path1 (when v1 (tramp-file-name-path v1))) | ||
| 1995 | (path2 (when v2 (tramp-file-name-path v2))) | ||
| 1996 | (ln (when v1 (tramp-get-remote-ln mmeth1 meth1 user1 host1)))) | ||
| 1997 | (unless (and meth1 meth2 user1 user2 host1 host2 | ||
| 1998 | (equal mmeth1 mmeth2) | ||
| 1999 | (equal meth1 meth2) | ||
| 2000 | (equal user1 user2) | ||
| 2001 | (equal host1 host2)) | ||
| 2002 | (error "add-name-to-file: %s" | ||
| 2003 | "only implemented for same method, same user, same host")) | ||
| 2004 | (when (and (not ok-if-already-exists) | ||
| 2005 | (file-exists-p newname) | ||
| 2006 | (not (numberp ok-if-already-exists)) | ||
| 2007 | (y-or-n-p | ||
| 2008 | (format | ||
| 2009 | "File %s already exists; make it a new name anyway? " | ||
| 2010 | newname))) | ||
| 2011 | (error "add-name-to-file: file %s already exists" newname)) | ||
| 2012 | (tramp-barf-unless-okay | ||
| 2013 | mmeth1 meth1 user1 host1 | ||
| 2014 | (format "%s %s %s" ln (tramp-shell-quote-argument path1) | ||
| 2015 | (tramp-shell-quote-argument path2)) | ||
| 2016 | nil 'file-error | ||
| 2017 | "error with add-name-to-file, see buffer `%s' for details" | ||
| 2018 | (buffer-name)))) | ||
| 2019 | |||
| 2020 | (defun tramp-handle-copy-file | ||
| 2021 | (filename newname &optional ok-if-already-exists keep-date) | ||
| 2022 | "Like `copy-file' for tramp files." | ||
| 2023 | ;; Check if both files are local -- invoke normal copy-file. | ||
| 2024 | ;; Otherwise, use tramp from local system. | ||
| 2025 | (setq filename (expand-file-name filename)) | ||
| 2026 | (setq newname (expand-file-name newname)) | ||
| 2027 | ;; At least one file a tramp file? | ||
| 2028 | (if (or (tramp-tramp-file-p filename) | ||
| 2029 | (tramp-tramp-file-p newname)) | ||
| 2030 | (tramp-do-copy-or-rename-file | ||
| 2031 | 'copy filename newname ok-if-already-exists keep-date) | ||
| 2032 | (tramp-run-real-handler | ||
| 2033 | 'copy-file | ||
| 2034 | (list filename newname ok-if-already-exists keep-date)))) | ||
| 2035 | |||
| 2036 | (defun tramp-handle-rename-file | ||
| 2037 | (filename newname &optional ok-if-already-exists) | ||
| 2038 | "Like `rename-file' for tramp files." | ||
| 2039 | ;; Check if both files are local -- invoke normal rename-file. | ||
| 2040 | ;; Otherwise, use tramp from local system. | ||
| 2041 | (setq filename (expand-file-name filename)) | ||
| 2042 | (setq newname (expand-file-name newname)) | ||
| 2043 | ;; At least one file a tramp file? | ||
| 2044 | (if (or (tramp-tramp-file-p filename) | ||
| 2045 | (tramp-tramp-file-p newname)) | ||
| 2046 | (tramp-do-copy-or-rename-file | ||
| 2047 | 'rename filename newname ok-if-already-exists) | ||
| 2048 | (tramp-run-real-handler 'rename-file | ||
| 2049 | (list filename newname ok-if-already-exists)))) | ||
| 2050 | |||
| 2051 | (defun tramp-do-copy-or-rename-file | ||
| 2052 | (op filename newname &optional ok-if-already-exists keep-date) | ||
| 2053 | "Copy or rename a remote file. | ||
| 2054 | OP must be `copy' or `rename' and indicates the operation to perform. | ||
| 2055 | FILENAME specifies the file to copy or rename, NEWNAME is the name of | ||
| 2056 | the new file (for copy) or the new name of the file (for rename). | ||
| 2057 | OK-IF-ALREADY-EXISTS means don't barf if NEWNAME exists already. | ||
| 2058 | KEEP-DATE means to make sure that NEWNAME has the same timestamp | ||
| 2059 | as FILENAME. | ||
| 2060 | |||
| 2061 | This function is invoked by `tramp-handle-copy-file' and | ||
| 2062 | `tramp-handle-rename-file'. It is an error if OP is neither of `copy' | ||
| 2063 | and `rename'. FILENAME and NEWNAME must be absolute file names." | ||
| 2064 | (unless (memq op '(copy rename)) | ||
| 2065 | (error "Unknown operation `%s', must be `copy' or `rename'" op)) | ||
| 2066 | (unless ok-if-already-exists | ||
| 2067 | (when (file-exists-p newname) | ||
| 2068 | (signal 'file-already-exists | ||
| 2069 | (list newname)))) | ||
| 2070 | (let* ((v1 (when (tramp-tramp-file-p filename) | ||
| 2071 | (tramp-dissect-file-name (tramp-handle-expand-file-name filename)))) | ||
| 2072 | (v2 (when (tramp-tramp-file-p newname) | ||
| 2073 | (tramp-dissect-file-name (tramp-handle-expand-file-name newname)))) | ||
| 2074 | (mmeth1 (when v1 (tramp-file-name-multi-method v1))) | ||
| 2075 | (mmeth2 (when v2 (tramp-file-name-multi-method v2))) | ||
| 2076 | (meth1 (when v1 (tramp-file-name-method v1))) | ||
| 2077 | (meth2 (when v2 (tramp-file-name-method v2))) | ||
| 2078 | (mmeth (tramp-file-name-multi-method (or v1 v2))) | ||
| 2079 | (meth (tramp-file-name-method (or v1 v2))) | ||
| 2080 | (rcp-program (tramp-get-rcp-program mmeth meth)) | ||
| 2081 | (rcp-args (tramp-get-rcp-args mmeth meth)) | ||
| 2082 | (trampbuf (get-buffer-create "*tramp output*"))) | ||
| 2083 | ;; Check if we can use a shortcut. | ||
| 2084 | (if (and meth1 meth2 (equal mmeth1 mmeth2) (equal meth1 meth2) | ||
| 2085 | (equal (tramp-file-name-host v1) | ||
| 2086 | (tramp-file-name-host v2)) | ||
| 2087 | (equal (tramp-file-name-user v1) | ||
| 2088 | (tramp-file-name-user v2))) | ||
| 2089 | ;; Shortcut: if method, host, user are the same for both | ||
| 2090 | ;; files, we invoke `cp' or `mv' on the remote host directly. | ||
| 2091 | (tramp-do-copy-or-rename-file-directly | ||
| 2092 | op | ||
| 2093 | (tramp-file-name-multi-method v1) | ||
| 2094 | (tramp-file-name-method v1) | ||
| 2095 | (tramp-file-name-user v1) | ||
| 2096 | (tramp-file-name-host v1) | ||
| 2097 | (tramp-file-name-path v1) (tramp-file-name-path v2) | ||
| 2098 | keep-date) | ||
| 2099 | ;; New algorithm: copy file first. Then, if operation is | ||
| 2100 | ;; `rename', go back and delete the original file if the copy | ||
| 2101 | ;; was successful. | ||
| 2102 | (if rcp-program | ||
| 2103 | ;; The following code uses a tramp program to copy the file. | ||
| 2104 | (let ((f1 (if (not v1) | ||
| 2105 | filename | ||
| 2106 | (tramp-make-rcp-program-file-name | ||
| 2107 | (tramp-file-name-user v1) | ||
| 2108 | (tramp-file-name-host v1) | ||
| 2109 | (tramp-shell-quote-argument (tramp-file-name-path v1))))) | ||
| 2110 | (f2 (if (not v2) | ||
| 2111 | newname | ||
| 2112 | (tramp-make-rcp-program-file-name | ||
| 2113 | (tramp-file-name-user v2) | ||
| 2114 | (tramp-file-name-host v2) | ||
| 2115 | (tramp-shell-quote-argument (tramp-file-name-path v2))))) | ||
| 2116 | (default-directory | ||
| 2117 | (if (tramp-tramp-file-p default-directory) | ||
| 2118 | (tramp-temporary-file-directory) | ||
| 2119 | default-directory))) | ||
| 2120 | (when keep-date | ||
| 2121 | (add-to-list 'rcp-args (tramp-get-rcp-keep-date-arg mmeth meth))) | ||
| 2122 | (save-excursion (set-buffer trampbuf) (erase-buffer)) | ||
| 2123 | (unless | ||
| 2124 | (equal 0 (apply #'call-process (tramp-get-rcp-program mmeth meth) | ||
| 2125 | nil trampbuf nil (append rcp-args (list f1 f2)))) | ||
| 2126 | (pop-to-buffer trampbuf) | ||
| 2127 | (error (concat "tramp-do-copy-or-rename-file: %s" | ||
| 2128 | " didn't work, see buffer `%s' for details") | ||
| 2129 | (tramp-get-rcp-program mmeth meth) trampbuf))) | ||
| 2130 | ;; The following code uses an inline method for copying. | ||
| 2131 | ;; Let's start with a simple-minded approach: we create a new | ||
| 2132 | ;; buffer, insert the contents of the source file into it, | ||
| 2133 | ;; then write out the buffer. This should work fine, whether | ||
| 2134 | ;; the source or the target files are tramp files. | ||
| 2135 | ;; CCC TODO: error checking | ||
| 2136 | (when keep-date | ||
| 2137 | (tramp-message 1 (concat "Warning: cannot preserve file time stamp" | ||
| 2138 | " with inline copying across machines"))) | ||
| 2139 | (save-excursion | ||
| 2140 | (set-buffer trampbuf) (erase-buffer) | ||
| 2141 | (insert-file-contents-literally filename) | ||
| 2142 | (let ((coding-system-for-write 'no-conversion)) | ||
| 2143 | (write-region (point-min) (point-max) newname)))) | ||
| 2144 | |||
| 2145 | ;; If the operation was `rename', delete the original file. | ||
| 2146 | (unless (eq op 'copy) | ||
| 2147 | (delete-file filename))))) | ||
| 2148 | |||
| 2149 | (defun tramp-do-copy-or-rename-file-directly | ||
| 2150 | (op multi-method method user host path1 path2 keep-date) | ||
| 2151 | "Invokes `cp' or `mv' on the remote system. | ||
| 2152 | OP must be one of `copy' or `rename', indicating `cp' or `mv', | ||
| 2153 | respectively. METHOD, USER, and HOST specify the connection. | ||
| 2154 | PATH1 and PATH2 specify the two arguments of `cp' or `mv'. | ||
| 2155 | If KEEP-DATE is non-nil, preserve the time stamp when copying." | ||
| 2156 | ;; CCC: What happens to the timestamp when renaming? | ||
| 2157 | (let ((cmd (cond ((and (eq op 'copy) keep-date) "cp -f -p") | ||
| 2158 | ((eq op 'copy) "cp -f") | ||
| 2159 | ((eq op 'rename) "mv -f") | ||
| 2160 | (t (error | ||
| 2161 | "Unknown operation `%s', must be `copy' or `rename'" | ||
| 2162 | op))))) | ||
| 2163 | (save-excursion | ||
| 2164 | (tramp-barf-unless-okay | ||
| 2165 | multi-method method user host | ||
| 2166 | (format "%s %s %s" | ||
| 2167 | cmd | ||
| 2168 | (tramp-shell-quote-argument path1) | ||
| 2169 | (tramp-shell-quote-argument path2)) | ||
| 2170 | nil 'file-error | ||
| 2171 | "Copying directly failed, see buffer `%s' for details." | ||
| 2172 | (buffer-name))))) | ||
| 2173 | |||
| 2174 | ;; mkdir | ||
| 2175 | (defun tramp-handle-make-directory (dir &optional parents) | ||
| 2176 | "Like `make-directory' for tramp files." | ||
| 2177 | (let ((v (tramp-dissect-file-name (tramp-handle-expand-file-name dir)))) | ||
| 2178 | (tramp-barf-unless-okay | ||
| 2179 | (tramp-file-name-multi-method v) (tramp-file-name-method v) | ||
| 2180 | (tramp-file-name-user v) (tramp-file-name-host v) | ||
| 2181 | (format " %s %s" | ||
| 2182 | (if parents "mkdir -p" "mkdir") | ||
| 2183 | (tramp-shell-quote-argument (tramp-file-name-path v))) | ||
| 2184 | nil 'file-error | ||
| 2185 | "Couldn't make directory %s" dir))) | ||
| 2186 | |||
| 2187 | ;; CCC error checking? | ||
| 2188 | (defun tramp-handle-delete-directory (directory) | ||
| 2189 | "Like `delete-directory' for tramp files." | ||
| 2190 | (let ((v (tramp-dissect-file-name (tramp-handle-expand-file-name directory)))) | ||
| 2191 | (save-excursion | ||
| 2192 | (tramp-send-command | ||
| 2193 | (tramp-file-name-multi-method v) (tramp-file-name-method v) | ||
| 2194 | (tramp-file-name-user v) (tramp-file-name-host v) | ||
| 2195 | (format "rmdir %s ; echo ok" | ||
| 2196 | (tramp-shell-quote-argument (tramp-file-name-path v)))) | ||
| 2197 | (tramp-wait-for-output)))) | ||
| 2198 | |||
| 2199 | (defun tramp-handle-delete-file (filename) | ||
| 2200 | "Like `delete-file' for tramp files." | ||
| 2201 | (let ((v (tramp-dissect-file-name (tramp-handle-expand-file-name filename)))) | ||
| 2202 | (save-excursion | ||
| 2203 | (unless (zerop (tramp-send-command-and-check | ||
| 2204 | (tramp-file-name-multi-method v) | ||
| 2205 | (tramp-file-name-method v) | ||
| 2206 | (tramp-file-name-user v) | ||
| 2207 | (tramp-file-name-host v) | ||
| 2208 | (format "rm -f %s" | ||
| 2209 | (tramp-shell-quote-argument | ||
| 2210 | (tramp-file-name-path v))))) | ||
| 2211 | (signal 'file-error "Couldn't delete Tramp file"))))) | ||
| 2212 | |||
| 2213 | ;; Dired. | ||
| 2214 | |||
| 2215 | ;; CCC: This does not seem to be enough. Something dies when | ||
| 2216 | ;; we try and delete two directories under TRAMP :/ | ||
| 2217 | (defun tramp-handle-dired-recursive-delete-directory (filename) | ||
| 2218 | "Recursively delete the directory given. | ||
| 2219 | This is like `dired-recursive-delete-directory' for tramp files." | ||
| 2220 | (let* ((v (tramp-dissect-file-name (tramp-handle-expand-file-name filename))) | ||
| 2221 | (multi-method (tramp-file-name-multi-method v)) | ||
| 2222 | (method (tramp-file-name-method v)) | ||
| 2223 | (user (tramp-file-name-user v)) | ||
| 2224 | (host (tramp-file-name-host v)) | ||
| 2225 | (path (tramp-file-name-path v))) | ||
| 2226 | ;; run a shell command 'rm -r <path>' | ||
| 2227 | ;; Code shamelessly stolen for the dired implementation and, um, hacked :) | ||
| 2228 | (or (tramp-handle-file-exists-p filename) | ||
| 2229 | (signal | ||
| 2230 | 'file-error | ||
| 2231 | (list "Removing old file name" "no such directory" filename))) | ||
| 2232 | ;; Which is better, -r or -R? (-r works for me <daniel@danann.net>) | ||
| 2233 | (tramp-send-command multi-method method user host | ||
| 2234 | (format "rm -r %s" (tramp-shell-quote-argument path))) | ||
| 2235 | ;; Wait for the remote system to return to us... | ||
| 2236 | ;; This might take a while, allow it plenty of time. | ||
| 2237 | (tramp-wait-for-output 120) | ||
| 2238 | ;; Make sure that it worked... | ||
| 2239 | (and (tramp-handle-file-exists-p filename) | ||
| 2240 | (error "Failed to recusively delete %s" filename)))) | ||
| 2241 | |||
| 2242 | |||
| 2243 | (defun tramp-handle-dired-call-process (program discard &rest arguments) | ||
| 2244 | "Like `dired-call-process' for tramp files." | ||
| 2245 | (let ((v (tramp-dissect-file-name | ||
| 2246 | (tramp-handle-expand-file-name default-directory))) | ||
| 2247 | multi-method method user host path) | ||
| 2248 | (setq multi-method (tramp-file-name-multi-method v)) | ||
| 2249 | (setq method (tramp-file-name-method v)) | ||
| 2250 | (setq user (tramp-file-name-user v)) | ||
| 2251 | (setq host (tramp-file-name-host v)) | ||
| 2252 | (setq path (tramp-file-name-path v)) | ||
| 2253 | (save-excursion | ||
| 2254 | (tramp-barf-unless-okay | ||
| 2255 | multi-method method user host | ||
| 2256 | (format "cd %s" (tramp-shell-quote-argument path)) | ||
| 2257 | nil 'file-error | ||
| 2258 | "tramp-handle-dired-call-process: Couldn't `cd %s'" | ||
| 2259 | (tramp-shell-quote-argument path)) | ||
| 2260 | (tramp-send-command | ||
| 2261 | multi-method method user host | ||
| 2262 | (mapconcat #'tramp-shell-quote-argument (cons program arguments) " ")) | ||
| 2263 | (tramp-wait-for-output)) | ||
| 2264 | (unless discard | ||
| 2265 | (insert-buffer (tramp-get-buffer multi-method method user host))) | ||
| 2266 | (save-excursion | ||
| 2267 | (prog1 | ||
| 2268 | (tramp-send-command-and-check multi-method method user host nil) | ||
| 2269 | (tramp-send-command multi-method method user host "cd") | ||
| 2270 | (tramp-wait-for-output))))) | ||
| 2271 | |||
| 2272 | ;; Pacify byte-compiler. The function is needed on XEmacs only. I'm | ||
| 2273 | ;; not sure at all that this is the right way to do it, but let's hope | ||
| 2274 | ;; it works for now, and wait for a guru to point out the Right Way to | ||
| 2275 | ;; achieve this. | ||
| 2276 | ;;(eval-when-compile | ||
| 2277 | ;; (unless (fboundp 'dired-insert-set-properties) | ||
| 2278 | ;; (fset 'dired-insert-set-properties 'ignore))) | ||
| 2279 | ;; Gerd suggests this: | ||
| 2280 | (eval-when-compile (require 'dired)) | ||
| 2281 | ;; Note that dired is required at run-time, too, when it is needed. | ||
| 2282 | ;; It is only needed on XEmacs for the function | ||
| 2283 | ;; `dired-insert-set-properties'. | ||
| 2284 | |||
| 2285 | (defun tramp-handle-insert-directory | ||
| 2286 | (filename switches &optional wildcard full-directory-p) | ||
| 2287 | "Like `insert-directory' for tramp files." | ||
| 2288 | (let ((v (tramp-dissect-file-name (tramp-handle-expand-file-name filename))) | ||
| 2289 | multi-method method user host path) | ||
| 2290 | (setq multi-method (tramp-file-name-multi-method v)) | ||
| 2291 | (setq method (tramp-file-name-method v)) | ||
| 2292 | (setq user (tramp-file-name-user v)) | ||
| 2293 | (setq host (tramp-file-name-host v)) | ||
| 2294 | (setq path (tramp-file-name-path v)) | ||
| 2295 | (tramp-message-for-buffer | ||
| 2296 | multi-method method user host 10 | ||
| 2297 | "Inserting directory `ls %s %s', wildcard %s, fulldir %s" | ||
| 2298 | switches filename (if wildcard "yes" "no") | ||
| 2299 | (if full-directory-p "yes" "no")) | ||
| 2300 | (when wildcard | ||
| 2301 | (setq wildcard (file-name-nondirectory path)) | ||
| 2302 | (setq path (file-name-directory path))) | ||
| 2303 | (when (listp switches) | ||
| 2304 | (setq switches (mapconcat 'identity switches " "))) | ||
| 2305 | (unless full-directory-p | ||
| 2306 | (setq switches (concat "-d " switches))) | ||
| 2307 | (when wildcard | ||
| 2308 | (setq switches (concat switches " " wildcard))) | ||
| 2309 | (save-excursion | ||
| 2310 | ;; If `full-directory-p', we just say `ls -l FILENAME'. | ||
| 2311 | ;; Else we chdir to the parent directory, then say `ls -ld BASENAME'. | ||
| 2312 | (if full-directory-p | ||
| 2313 | (tramp-send-command | ||
| 2314 | multi-method method user host | ||
| 2315 | (format "%s %s %s" | ||
| 2316 | (tramp-get-ls-command multi-method method user host) | ||
| 2317 | switches | ||
| 2318 | (if wildcard | ||
| 2319 | path | ||
| 2320 | (tramp-shell-quote-argument (concat path "."))))) | ||
| 2321 | (tramp-barf-unless-okay | ||
| 2322 | multi-method method user host | ||
| 2323 | (format "cd %s" (tramp-shell-quote-argument | ||
| 2324 | (file-name-directory path))) | ||
| 2325 | nil 'file-error | ||
| 2326 | "Couldn't `cd %s'" | ||
| 2327 | (tramp-shell-quote-argument (file-name-directory path))) | ||
| 2328 | (tramp-send-command | ||
| 2329 | multi-method method user host | ||
| 2330 | (format "%s %s %s" | ||
| 2331 | (tramp-get-ls-command multi-method method user host) | ||
| 2332 | switches | ||
| 2333 | (if full-directory-p | ||
| 2334 | ;; Add "/." to make sure we got complete dir | ||
| 2335 | ;; listing for symlinks, too. | ||
| 2336 | (concat (file-name-as-directory | ||
| 2337 | (file-name-nondirectory path)) ".") | ||
| 2338 | (file-name-nondirectory path))))) | ||
| 2339 | (sit-for 1) ;needed for rsh but not ssh? | ||
| 2340 | (tramp-wait-for-output)) | ||
| 2341 | (insert-buffer (tramp-get-buffer multi-method method user host)) | ||
| 2342 | ;; On XEmacs, we want to call (exchange-point-and-mark t), but | ||
| 2343 | ;; that doesn't exist on Emacs, so we use this workaround instead. | ||
| 2344 | ;; Since zmacs-region-stays doesn't exist in Emacs, this ought to | ||
| 2345 | ;; be safe. Thanks to Daniel Pittman <daniel@danann.net>. | ||
| 2346 | (let ((zmacs-region-stays t)) | ||
| 2347 | (exchange-point-and-mark)) | ||
| 2348 | (save-excursion | ||
| 2349 | (tramp-send-command multi-method method user host "cd") | ||
| 2350 | (tramp-wait-for-output)) | ||
| 2351 | ;; Another XEmacs specialty follows. What's the right way to do | ||
| 2352 | ;; it? | ||
| 2353 | (when (and (featurep 'xemacs) | ||
| 2354 | (eq major-mode 'dired-mode)) | ||
| 2355 | (save-excursion | ||
| 2356 | (require 'dired) | ||
| 2357 | (dired-insert-set-properties (point) (mark t)))))) | ||
| 2358 | |||
| 2359 | ;; Continuation of kluge to pacify byte-compiler. | ||
| 2360 | ;;(eval-when-compile | ||
| 2361 | ;; (when (eq (symbol-function 'dired-insert-set-properties) 'ignore) | ||
| 2362 | ;; (fmakunbound 'dired-insert-set-properties))) | ||
| 2363 | |||
| 2364 | ;; CCC is this the right thing to do? | ||
| 2365 | (defun tramp-handle-unhandled-file-name-directory (filename) | ||
| 2366 | "Like `unhandled-file-name-directory' for tramp files." | ||
| 2367 | (expand-file-name "~/")) | ||
| 2368 | |||
| 2369 | ;; Canonicalization of file names. | ||
| 2370 | |||
| 2371 | (defun tramp-drop-volume-letter (name) | ||
| 2372 | "Cut off unnecessary drive letter from file NAME. | ||
| 2373 | The function `tramp-handle-expand-file-name' calls `expand-file-name' | ||
| 2374 | locally on a remote file name. When the local system is a W32 system | ||
| 2375 | but the remote system is Unix, this introduces a superfluous drive | ||
| 2376 | letter into the file name. This function removes it. | ||
| 2377 | |||
| 2378 | Doesn't do anything if the NAME does not start with a drive letter." | ||
| 2379 | (if (and (> (length name) 1) | ||
| 2380 | (char-equal (aref name 1) ?:) | ||
| 2381 | (let ((c1 (aref name 0))) | ||
| 2382 | (or (and (>= c1 ?A) (<= c1 ?Z)) | ||
| 2383 | (and (>= c1 ?a) (<= c1 ?z))))) | ||
| 2384 | (substring name 2) | ||
| 2385 | name)) | ||
| 2386 | |||
| 2387 | (defun tramp-handle-expand-file-name (name &optional dir) | ||
| 2388 | "Like `expand-file-name' for tramp files." | ||
| 2389 | ;; If DIR is not given, use DEFAULT-DIRECTORY or "/". | ||
| 2390 | (setq dir (or dir default-directory "/")) | ||
| 2391 | ;; Unless NAME is absolute, concat DIR and NAME. | ||
| 2392 | (unless (file-name-absolute-p name) | ||
| 2393 | (setq name (concat (file-name-as-directory dir) name))) | ||
| 2394 | ;; If NAME is not a tramp file, run the real handler | ||
| 2395 | (if (not (tramp-tramp-file-p name)) | ||
| 2396 | (tramp-run-real-handler 'expand-file-name | ||
| 2397 | (list name nil)) | ||
| 2398 | ;; Dissect NAME. | ||
| 2399 | (let* ((v (tramp-dissect-file-name name)) | ||
| 2400 | (multi-method (tramp-file-name-multi-method v)) | ||
| 2401 | (method (tramp-file-name-method v)) | ||
| 2402 | (user (tramp-file-name-user v)) | ||
| 2403 | (host (tramp-file-name-host v)) | ||
| 2404 | (path (tramp-file-name-path v))) | ||
| 2405 | (unless (file-name-absolute-p path) | ||
| 2406 | (setq path (concat "~/" path))) | ||
| 2407 | (save-excursion | ||
| 2408 | ;; Tilde expansion if necessary. This needs a shell which | ||
| 2409 | ;; groks tilde expansion! The function `tramp-find-shell' is | ||
| 2410 | ;; supposed to find such a shell on the remote host. Please | ||
| 2411 | ;; tell me about it when this doesn't work on your system. | ||
| 2412 | (when (string-match "\\`\\(~[^/]*\\)\\(.*\\)\\'" path) | ||
| 2413 | (let ((uname (match-string 1 path)) | ||
| 2414 | (fname (match-string 2 path))) | ||
| 2415 | ;; CCC fanatic error checking? | ||
| 2416 | (set-buffer (tramp-get-buffer multi-method method user host)) | ||
| 2417 | (erase-buffer) | ||
| 2418 | (tramp-send-command | ||
| 2419 | multi-method method user host | ||
| 2420 | (format "cd %s; pwd" uname) | ||
| 2421 | t) | ||
| 2422 | (tramp-wait-for-output) | ||
| 2423 | (goto-char (point-min)) | ||
| 2424 | (setq uname (buffer-substring (point) (tramp-line-end-position))) | ||
| 2425 | (setq path (concat uname fname)) | ||
| 2426 | (erase-buffer))) | ||
| 2427 | ;; No tilde characters in file name, do normal | ||
| 2428 | ;; expand-file-name (this does "/./" and "/../"). We bind | ||
| 2429 | ;; directory-sep-char here for XEmacs on Windows, which would | ||
| 2430 | ;; otherwise use backslash. | ||
| 2431 | (let ((directory-sep-char ?/)) | ||
| 2432 | (tramp-make-tramp-file-name | ||
| 2433 | multi-method method user host | ||
| 2434 | (tramp-drop-volume-letter | ||
| 2435 | (tramp-run-real-handler 'expand-file-name (list path))))))))) | ||
| 2436 | |||
| 2437 | ;; Remote commands. | ||
| 2438 | |||
| 2439 | (defun tramp-handle-shell-command (command &optional output-buffer error-buffer) | ||
| 2440 | "Like `shell-command' for tramp files. | ||
| 2441 | This will break if COMMAND prints a newline, followed by the value of | ||
| 2442 | `tramp-end-of-output', followed by another newline." | ||
| 2443 | (if (tramp-tramp-file-p default-directory) | ||
| 2444 | (let* ((v (tramp-dissect-file-name | ||
| 2445 | (tramp-handle-expand-file-name default-directory))) | ||
| 2446 | (multi-method (tramp-file-name-multi-method v)) | ||
| 2447 | (method (tramp-file-name-method v)) | ||
| 2448 | (user (tramp-file-name-user v)) | ||
| 2449 | (host (tramp-file-name-host v)) | ||
| 2450 | (path (tramp-file-name-path v)) | ||
| 2451 | status) | ||
| 2452 | (when (string-match "&[ \t]*\\'" command) | ||
| 2453 | (error "Tramp doesn't grok asynchronous shell commands, yet")) | ||
| 2454 | (when error-buffer | ||
| 2455 | (error "Tramp doesn't grok optional third arg ERROR-BUFFER, yet")) | ||
| 2456 | (save-excursion | ||
| 2457 | (tramp-barf-unless-okay | ||
| 2458 | multi-method method user host | ||
| 2459 | (format "cd %s" (tramp-shell-quote-argument path)) | ||
| 2460 | nil 'file-error | ||
| 2461 | "tramp-handle-shell-command: Couldn't `cd %s'" | ||
| 2462 | (tramp-shell-quote-argument path)) | ||
| 2463 | (tramp-send-command multi-method method user host | ||
| 2464 | (concat command "; tramp_old_status=$?")) | ||
| 2465 | ;; This will break if the shell command prints "/////" | ||
| 2466 | ;; somewhere. Let's just hope for the best... | ||
| 2467 | (tramp-wait-for-output)) | ||
| 2468 | (unless output-buffer | ||
| 2469 | (setq output-buffer (get-buffer-create "*Shell Command Output*")) | ||
| 2470 | (set-buffer output-buffer) | ||
| 2471 | (erase-buffer)) | ||
| 2472 | (unless (bufferp output-buffer) | ||
| 2473 | (setq output-buffer (current-buffer))) | ||
| 2474 | (set-buffer output-buffer) | ||
| 2475 | (insert-buffer (tramp-get-buffer multi-method method user host)) | ||
| 2476 | (save-excursion | ||
| 2477 | (tramp-send-command multi-method method user host "cd") | ||
| 2478 | (tramp-wait-for-output) | ||
| 2479 | (tramp-send-command | ||
| 2480 | multi-method method user host | ||
| 2481 | "tramp_set_exit_status $tramp_old_status; echo tramp_exit_status $?") | ||
| 2482 | (tramp-wait-for-output) | ||
| 2483 | (goto-char (point-max)) | ||
| 2484 | (unless (search-backward "tramp_exit_status " nil t) | ||
| 2485 | (error "Couldn't find exit status of `%s'" command)) | ||
| 2486 | (skip-chars-forward "^ ") | ||
| 2487 | (setq status (read (current-buffer)))) | ||
| 2488 | (unless (zerop (buffer-size)) | ||
| 2489 | (pop-to-buffer output-buffer)) | ||
| 2490 | status) | ||
| 2491 | ;; The following is only executed if something strange was | ||
| 2492 | ;; happening. Emit a helpful message and do it anyway. | ||
| 2493 | (message "tramp-handle-shell-command called with non-tramp directory: `%s'" | ||
| 2494 | default-directory) | ||
| 2495 | (tramp-run-real-handler 'shell-command | ||
| 2496 | (list command output-buffer error-buffer)))) | ||
| 2497 | |||
| 2498 | ;; File Editing. | ||
| 2499 | |||
| 2500 | (defsubst tramp-make-temp-file () | ||
| 2501 | (funcall (if (fboundp 'make-temp-file) 'make-temp-file 'make-temp-name) | ||
| 2502 | (expand-file-name tramp-temp-name-prefix | ||
| 2503 | (tramp-temporary-file-directory)))) | ||
| 2504 | |||
| 2505 | (defun tramp-handle-file-local-copy (filename) | ||
| 2506 | "Like `file-local-copy' for tramp files." | ||
| 2507 | (let* ((v (tramp-dissect-file-name (tramp-handle-expand-file-name filename))) | ||
| 2508 | (multi-method (tramp-file-name-multi-method v)) | ||
| 2509 | (method (tramp-file-name-method v)) | ||
| 2510 | (user (tramp-file-name-user v)) | ||
| 2511 | (host (tramp-file-name-host v)) | ||
| 2512 | (path (tramp-file-name-path v)) | ||
| 2513 | (trampbuf (get-buffer-create "*tramp output*")) | ||
| 2514 | tmpfil) | ||
| 2515 | (unless (file-exists-p filename) | ||
| 2516 | (error "Cannot make local copy of non-existing file `%s'" | ||
| 2517 | filename)) | ||
| 2518 | (setq tmpfil (tramp-make-temp-file)) | ||
| 2519 | (cond ((tramp-get-rcp-program multi-method method) | ||
| 2520 | ;; Use tramp-like program for file transfer. | ||
| 2521 | (tramp-message-for-buffer | ||
| 2522 | multi-method method user host | ||
| 2523 | 5 "Fetching %s to tmp file %s..." filename tmpfil) | ||
| 2524 | (save-excursion (set-buffer trampbuf) (erase-buffer)) | ||
| 2525 | (unless (equal 0 | ||
| 2526 | (apply #'call-process | ||
| 2527 | (tramp-get-rcp-program multi-method method) | ||
| 2528 | nil trampbuf nil | ||
| 2529 | (append (tramp-get-rcp-args multi-method method) | ||
| 2530 | (list | ||
| 2531 | (tramp-make-rcp-program-file-name | ||
| 2532 | user host | ||
| 2533 | (tramp-shell-quote-argument path)) | ||
| 2534 | tmpfil)))) | ||
| 2535 | (pop-to-buffer trampbuf) | ||
| 2536 | (error (concat "tramp-handle-file-local-copy: `%s' didn't work, " | ||
| 2537 | "see buffer `%s' for details") | ||
| 2538 | (tramp-get-rcp-program multi-method method) trampbuf)) | ||
| 2539 | (tramp-message-for-buffer | ||
| 2540 | multi-method method user host | ||
| 2541 | 5 "Fetching %s to tmp file %s...done" filename tmpfil)) | ||
| 2542 | ((and (tramp-get-encoding-command multi-method method) | ||
| 2543 | (tramp-get-decoding-command multi-method method)) | ||
| 2544 | ;; Use inline encoding for file transfer. | ||
| 2545 | (save-excursion | ||
| 2546 | ;; Following line for setting tramp-current-method, | ||
| 2547 | ;; tramp-current-user, tramp-current-host. | ||
| 2548 | (set-buffer (tramp-get-buffer multi-method method user host)) | ||
| 2549 | (tramp-message 5 "Encoding remote file %s..." filename) | ||
| 2550 | (tramp-barf-unless-okay | ||
| 2551 | multi-method method user host | ||
| 2552 | (concat (tramp-get-encoding-command multi-method method) | ||
| 2553 | " < " (tramp-shell-quote-argument path)) | ||
| 2554 | nil 'file-error | ||
| 2555 | "Encoding remote file failed, see buffer `%s' for details" | ||
| 2556 | (tramp-get-buffer multi-method method user host)) | ||
| 2557 | ;; Remove trailing status code | ||
| 2558 | (goto-char (point-max)) | ||
| 2559 | (delete-region (point) (progn (forward-line -1) (point))) | ||
| 2560 | |||
| 2561 | (tramp-message 5 "Decoding remote file %s..." filename) | ||
| 2562 | (if (and (tramp-get-decoding-function multi-method method) | ||
| 2563 | (fboundp (tramp-get-decoding-function multi-method method))) | ||
| 2564 | ;; If tramp-decoding-function is defined for this | ||
| 2565 | ;; method, we call it. | ||
| 2566 | (let ((tmpbuf (get-buffer-create " *tramp tmp*"))) | ||
| 2567 | (set-buffer tmpbuf) | ||
| 2568 | (erase-buffer) | ||
| 2569 | (insert-buffer (tramp-get-buffer multi-method method | ||
| 2570 | user host)) | ||
| 2571 | (tramp-message-for-buffer | ||
| 2572 | multi-method method user host | ||
| 2573 | 6 "Decoding remote file %s with function %s..." | ||
| 2574 | filename | ||
| 2575 | (tramp-get-decoding-function multi-method method)) | ||
| 2576 | (set-buffer tmpbuf) | ||
| 2577 | (let ((coding-system-for-write 'no-conversion)) | ||
| 2578 | (funcall (tramp-get-decoding-function multi-method method) | ||
| 2579 | (point-min) | ||
| 2580 | (point-max)) | ||
| 2581 | (write-region (point-min) (point-max) tmpfil)) | ||
| 2582 | (kill-buffer tmpbuf)) | ||
| 2583 | ;; If tramp-decoding-function is not defined for this | ||
| 2584 | ;; method, we invoke tramp-decoding-command instead. | ||
| 2585 | (let ((tmpfil2 (tramp-make-temp-file))) | ||
| 2586 | (write-region (point-min) (point-max) tmpfil2) | ||
| 2587 | (tramp-message | ||
| 2588 | 6 "Decoding remote file %s with command %s..." | ||
| 2589 | filename | ||
| 2590 | (tramp-get-decoding-command multi-method method)) | ||
| 2591 | (call-process | ||
| 2592 | tramp-sh-program | ||
| 2593 | tmpfil2 ;input | ||
| 2594 | nil ;output | ||
| 2595 | nil ;display | ||
| 2596 | "-c" (concat (tramp-get-decoding-command multi-method method) | ||
| 2597 | " > " tmpfil)) | ||
| 2598 | (delete-file tmpfil2))) | ||
| 2599 | (tramp-message-for-buffer | ||
| 2600 | multi-method method user host | ||
| 2601 | 5 "Decoding remote file %s...done" filename))) | ||
| 2602 | |||
| 2603 | (t (error "Wrong method specification for `%s'" method))) | ||
| 2604 | tmpfil)) | ||
| 2605 | |||
| 2606 | |||
| 2607 | (defun tramp-handle-insert-file-contents | ||
| 2608 | (filename &optional visit beg end replace) | ||
| 2609 | "Like `insert-file-contents' for tramp files." | ||
| 2610 | (barf-if-buffer-read-only) | ||
| 2611 | (setq filename (expand-file-name filename)) | ||
| 2612 | (let* ((v (tramp-dissect-file-name (tramp-handle-expand-file-name filename))) | ||
| 2613 | (multi-method (tramp-file-name-multi-method v)) | ||
| 2614 | (method (tramp-file-name-method v)) | ||
| 2615 | (user (tramp-file-name-user v)) | ||
| 2616 | (host (tramp-file-name-host v)) | ||
| 2617 | (path (tramp-file-name-path v))) | ||
| 2618 | (if (not (tramp-handle-file-exists-p filename)) | ||
| 2619 | (progn | ||
| 2620 | (when visit | ||
| 2621 | (setq buffer-file-name filename) | ||
| 2622 | (set-visited-file-modtime) | ||
| 2623 | (set-buffer-modified-p nil)) | ||
| 2624 | (signal 'file-error | ||
| 2625 | (format "File `%s' not found on remote host" filename)) | ||
| 2626 | (list (tramp-handle-expand-file-name filename) 0)) | ||
| 2627 | (let ((local-copy (tramp-handle-file-local-copy filename)) | ||
| 2628 | (coding-system-used nil) | ||
| 2629 | (result nil)) | ||
| 2630 | (when visit | ||
| 2631 | (setq buffer-file-name filename) | ||
| 2632 | (set-visited-file-modtime) | ||
| 2633 | (set-buffer-modified-p nil)) | ||
| 2634 | (tramp-message-for-buffer | ||
| 2635 | multi-method method user host | ||
| 2636 | 9 "Inserting local temp file `%s'..." local-copy) | ||
| 2637 | (setq result | ||
| 2638 | (tramp-run-real-handler 'insert-file-contents | ||
| 2639 | (list local-copy nil beg end replace))) | ||
| 2640 | ;; Now `last-coding-system-used' has right value. Remember it. | ||
| 2641 | (when (boundp 'last-coding-system-used) | ||
| 2642 | (setq coding-system-used last-coding-system-used)) | ||
| 2643 | (tramp-message 9 "Inserting local temp file `%s'...done" local-copy) | ||
| 2644 | (delete-file local-copy) | ||
| 2645 | (when (boundp 'last-coding-system-used) | ||
| 2646 | (setq last-coding-system-used coding-system-used)) | ||
| 2647 | (list (expand-file-name filename) | ||
| 2648 | (second result)))))) | ||
| 2649 | |||
| 2650 | ;; CCC grok APPEND, LOCKNAME, CONFIRM | ||
| 2651 | (defun tramp-handle-write-region | ||
| 2652 | (start end filename &optional append visit lockname confirm) | ||
| 2653 | "Like `write-region' for tramp files." | ||
| 2654 | (unless (eq append nil) | ||
| 2655 | (error "Cannot append to file using tramp (`%s')" filename)) | ||
| 2656 | (setq filename (expand-file-name filename)) | ||
| 2657 | ;; Following part commented out because we don't know what to do about | ||
| 2658 | ;; file locking, and it does not appear to be a problem to ignore it. | ||
| 2659 | ;; Ange-ftp ignores it, too. | ||
| 2660 | ; (when (and lockname (stringp lockname)) | ||
| 2661 | ; (setq lockname (expand-file-name lockname))) | ||
| 2662 | ; (unless (or (eq lockname nil) | ||
| 2663 | ; (string= lockname filename)) | ||
| 2664 | ; (error "tramp-handle-write-region: LOCKNAME must be nil or equal FILENAME")) | ||
| 2665 | ;; XEmacs takes a coding system as the sevent argument, not `confirm' | ||
| 2666 | (when (and (not (featurep 'xemacs)) | ||
| 2667 | confirm (file-exists-p filename)) | ||
| 2668 | (unless (y-or-n-p (format "File %s exists; overwrite anyway? " | ||
| 2669 | filename)) | ||
| 2670 | (error "File not overwritten"))) | ||
| 2671 | (let* ((curbuf (current-buffer)) | ||
| 2672 | (v (tramp-dissect-file-name filename)) | ||
| 2673 | (multi-method (tramp-file-name-multi-method v)) | ||
| 2674 | (method (tramp-file-name-method v)) | ||
| 2675 | (user (tramp-file-name-user v)) | ||
| 2676 | (host (tramp-file-name-host v)) | ||
| 2677 | (path (tramp-file-name-path v)) | ||
| 2678 | (rcp-program (tramp-get-rcp-program multi-method method)) | ||
| 2679 | (rcp-args (tramp-get-rcp-args multi-method method)) | ||
| 2680 | (encoding-command (tramp-get-encoding-command multi-method method)) | ||
| 2681 | (encoding-function (tramp-get-encoding-function multi-method method)) | ||
| 2682 | (decoding-command (tramp-get-decoding-command multi-method method)) | ||
| 2683 | (trampbuf (get-buffer-create "*tramp output*")) | ||
| 2684 | ;; We use this to save the value of `last-coding-system-used' | ||
| 2685 | ;; after writing the tmp file. At the end of the function, | ||
| 2686 | ;; we set `last-coding-system-used' to this saved value. | ||
| 2687 | ;; This way, any intermediary coding systems used while | ||
| 2688 | ;; talking to the remote shell or suchlike won't hose this | ||
| 2689 | ;; variable. This approach was snarfed from ange-ftp.el. | ||
| 2690 | coding-system-used | ||
| 2691 | tmpfil) | ||
| 2692 | ;; Write region into a tmp file. This isn't really needed if we | ||
| 2693 | ;; use an encoding function, but currently we use it always | ||
| 2694 | ;; because this makes the logic simpler. | ||
| 2695 | (setq tmpfil (tramp-make-temp-file)) | ||
| 2696 | ;; We say `no-message' here because we don't want the visited file | ||
| 2697 | ;; modtime data to be clobbered from the temp file. We call | ||
| 2698 | ;; `set-visited-file-modtime' ourselves later on. | ||
| 2699 | (tramp-run-real-handler | ||
| 2700 | 'write-region | ||
| 2701 | (if confirm ; don't pass this arg unless defined for backward compat. | ||
| 2702 | (list start end tmpfil append 'no-message lockname confirm) | ||
| 2703 | (list start end tmpfil append 'no-message lockname))) | ||
| 2704 | ;; Now, `last-coding-system-used' has the right value. Remember it. | ||
| 2705 | (when (boundp 'last-coding-system-used) | ||
| 2706 | (setq coding-system-used last-coding-system-used)) | ||
| 2707 | ;; This is a bit lengthy due to the different methods possible for | ||
| 2708 | ;; file transfer. First, we check whether the method uses an rcp | ||
| 2709 | ;; program. If so, we call it. Otherwise, both encoding and | ||
| 2710 | ;; decoding command must be specified. However, if the method | ||
| 2711 | ;; _also_ specifies an encoding function, then that is used for | ||
| 2712 | ;; encoding the contents of the tmp file. | ||
| 2713 | (cond (rcp-program | ||
| 2714 | ;; use rcp-like program for file transfer | ||
| 2715 | (let ((argl (append rcp-args | ||
| 2716 | (list | ||
| 2717 | tmpfil | ||
| 2718 | (tramp-make-rcp-program-file-name | ||
| 2719 | user host | ||
| 2720 | (tramp-shell-quote-argument path)))))) | ||
| 2721 | (tramp-message-for-buffer | ||
| 2722 | multi-method method user host | ||
| 2723 | 6 "Writing tmp file using `%s'..." rcp-program) | ||
| 2724 | (save-excursion (set-buffer trampbuf) (erase-buffer)) | ||
| 2725 | (when tramp-debug-buffer | ||
| 2726 | (save-excursion | ||
| 2727 | (set-buffer (tramp-get-debug-buffer multi-method | ||
| 2728 | method user host)) | ||
| 2729 | (goto-char (point-max)) | ||
| 2730 | (tramp-insert-with-face | ||
| 2731 | 'bold (format "$ %s %s\n" rcp-program | ||
| 2732 | (mapconcat 'identity argl " "))))) | ||
| 2733 | (unless (equal 0 | ||
| 2734 | (apply #'call-process | ||
| 2735 | rcp-program nil trampbuf nil argl)) | ||
| 2736 | (pop-to-buffer trampbuf) | ||
| 2737 | (error "Cannot write region to file `%s', command `%s' failed" | ||
| 2738 | filename rcp-program)) | ||
| 2739 | (tramp-message-for-buffer multi-method method user host | ||
| 2740 | 6 "Transferring file using `%s'...done" | ||
| 2741 | rcp-program))) | ||
| 2742 | ((and encoding-command decoding-command) | ||
| 2743 | ;; Use inline file transfer | ||
| 2744 | (let ((tmpbuf (get-buffer-create " *tramp file transfer*"))) | ||
| 2745 | (save-excursion | ||
| 2746 | ;; Encode tmpfil into tmpbuf | ||
| 2747 | (tramp-message-for-buffer multi-method method user host | ||
| 2748 | 5 "Encoding region...") | ||
| 2749 | (set-buffer tmpbuf) | ||
| 2750 | (erase-buffer) | ||
| 2751 | ;; Use encoding function or command. | ||
| 2752 | (if (and encoding-function | ||
| 2753 | (fboundp encoding-function)) | ||
| 2754 | (progn | ||
| 2755 | (tramp-message-for-buffer | ||
| 2756 | multi-method method user host | ||
| 2757 | 6 "Encoding region using function...") | ||
| 2758 | (insert-file-contents-literally tmpfil) | ||
| 2759 | ;; CCC. The following `let' is a workaround for | ||
| 2760 | ;; the base64.el that comes with pgnus-0.84. If | ||
| 2761 | ;; both of the following conditions are | ||
| 2762 | ;; satisfied, it tries to write to a local file | ||
| 2763 | ;; in default-directory, but at this point, | ||
| 2764 | ;; default-directory is remote. | ||
| 2765 | ;; (CALL-PROCESS-REGION can't write to remote | ||
| 2766 | ;; files, it seems.) The file in question is a | ||
| 2767 | ;; tmp file anyway. | ||
| 2768 | (let ((default-directory (tramp-temporary-file-directory))) | ||
| 2769 | (funcall encoding-function (point-min) (point-max))) | ||
| 2770 | (goto-char (point-max)) | ||
| 2771 | (unless (bolp) | ||
| 2772 | (newline))) | ||
| 2773 | (tramp-message-for-buffer multi-method method user host | ||
| 2774 | 6 "Encoding region using command...") | ||
| 2775 | (unless (equal 0 | ||
| 2776 | (call-process | ||
| 2777 | tramp-sh-program | ||
| 2778 | tmpfil ;input = local tmp file | ||
| 2779 | t ;output is current buffer | ||
| 2780 | nil ;don't redisplay | ||
| 2781 | "-c" | ||
| 2782 | encoding-command)) | ||
| 2783 | (pop-to-buffer trampbuf) | ||
| 2784 | (error (concat "Cannot write to `%s', local encoding" | ||
| 2785 | " command `%s' failed") | ||
| 2786 | filename encoding-command))) | ||
| 2787 | ;; Send tmpbuf into remote decoding command which | ||
| 2788 | ;; writes to remote file. Because this happens on the | ||
| 2789 | ;; remote host, we cannot use the function. | ||
| 2790 | (tramp-message-for-buffer | ||
| 2791 | multi-method method user host | ||
| 2792 | 5 "Decoding region into remote file %s..." filename) | ||
| 2793 | (tramp-send-command | ||
| 2794 | multi-method method user host | ||
| 2795 | (format "%s >%s <<'EOF'" | ||
| 2796 | decoding-command | ||
| 2797 | (tramp-shell-quote-argument path))) | ||
| 2798 | (set-buffer tmpbuf) | ||
| 2799 | (tramp-message-for-buffer | ||
| 2800 | multi-method method user host | ||
| 2801 | 6 "Sending data to remote host...") | ||
| 2802 | (tramp-send-region multi-method method user host | ||
| 2803 | (point-min) (point-max)) | ||
| 2804 | ;; wait for remote decoding to complete | ||
| 2805 | (tramp-message-for-buffer | ||
| 2806 | multi-method method user host 6 "Sending end of data token...") | ||
| 2807 | (tramp-send-command | ||
| 2808 | multi-method method user host "EOF") | ||
| 2809 | (tramp-message-for-buffer | ||
| 2810 | multi-method method user host 6 | ||
| 2811 | "Waiting for remote host to process data...") | ||
| 2812 | (set-buffer (tramp-get-buffer multi-method method user host)) | ||
| 2813 | (tramp-wait-for-output) | ||
| 2814 | (tramp-barf-unless-okay | ||
| 2815 | multi-method method user host nil nil 'file-error | ||
| 2816 | (concat "Couldn't write region to `%s'," | ||
| 2817 | " decode using `%s' failed") | ||
| 2818 | filename decoding-command) | ||
| 2819 | (tramp-message 5 "Decoding region into remote file %s...done" | ||
| 2820 | filename) | ||
| 2821 | (kill-buffer tmpbuf)))) | ||
| 2822 | (t | ||
| 2823 | (error | ||
| 2824 | (concat "Method `%s' should specify both encoding and " | ||
| 2825 | "decoding command or an rcp program") | ||
| 2826 | method))) | ||
| 2827 | (delete-file tmpfil) | ||
| 2828 | (unless (equal curbuf (current-buffer)) | ||
| 2829 | (error "Buffer has changed from `%s' to `%s'" | ||
| 2830 | curbuf (current-buffer))) | ||
| 2831 | (when (eq visit t) | ||
| 2832 | (set-visited-file-modtime)) | ||
| 2833 | ;; Make `last-coding-system-used' have the right value. | ||
| 2834 | (when (boundp 'last-coding-system-used) | ||
| 2835 | (setq last-coding-system-used coding-system-used)) | ||
| 2836 | (when (or (eq visit t) | ||
| 2837 | (eq visit nil) | ||
| 2838 | (stringp visit)) | ||
| 2839 | (message "Wrote %s" filename)))) | ||
| 2840 | |||
| 2841 | ;; Call down to the real handler. | ||
| 2842 | ;; Because EFS does not play nicely with TRAMP (both systems match an | ||
| 2843 | ;; TRAMP path) it is needed to disable efs as well as tramp for the | ||
| 2844 | ;; operation. | ||
| 2845 | ;; | ||
| 2846 | ;; Other than that, this is the canon file-handler code that the doco | ||
| 2847 | ;; says should be used here. Which is nice. | ||
| 2848 | ;; | ||
| 2849 | ;; Under XEmacs current, EFS also hooks in as | ||
| 2850 | ;; efs-sifn-handler-function to handle any path with environment | ||
| 2851 | ;; variables. This has two implications: | ||
| 2852 | ;; 1) That EFS may not be completely dead (yet) for TRAMP paths | ||
| 2853 | ;; 2) That TRAMP might want to do the same thing. | ||
| 2854 | ;; Details as they come in. | ||
| 2855 | ;; | ||
| 2856 | ;; Daniel Pittman <daniel@danann.net> | ||
| 2857 | |||
| 2858 | ;; (defun tramp-run-real-handler (operation args) | ||
| 2859 | ;; "Invoke normal file name handler for OPERATION. | ||
| 2860 | ;; This inhibits EFS and Ange-FTP, too, because they conflict with tramp. | ||
| 2861 | ;; First arg specifies the OPERATION, remaining ARGS are passed to the | ||
| 2862 | ;; OPERATION." | ||
| 2863 | ;; (let ((inhibit-file-name-handlers | ||
| 2864 | ;; (list 'tramp-file-name-handler | ||
| 2865 | ;; 'efs-file-handler-function | ||
| 2866 | ;; 'ange-ftp-hook-function | ||
| 2867 | ;; (and (eq inhibit-file-name-operation operation) | ||
| 2868 | ;; inhibit-file-name-handlers))) | ||
| 2869 | ;; (inhibit-file-name-operation operation)) | ||
| 2870 | ;; (apply operation args))) | ||
| 2871 | |||
| 2872 | (defun tramp-run-real-handler (operation args) | ||
| 2873 | "Invoke normal file name handler for OPERATION. | ||
| 2874 | First arg specifies the OPERATION, remaining ARGS are passed to the | ||
| 2875 | OPERATION." | ||
| 2876 | (let ((inhibit-file-name-handlers | ||
| 2877 | (list 'tramp-file-name-handler | ||
| 2878 | (and (eq inhibit-file-name-operation operation) | ||
| 2879 | inhibit-file-name-handlers))) | ||
| 2880 | (inhibit-file-name-operation operation)) | ||
| 2881 | (apply operation args))) | ||
| 2882 | |||
| 2883 | |||
| 2884 | ;; Main function. | ||
| 2885 | ;;;###autoload | ||
| 2886 | (defun tramp-file-name-handler (operation &rest args) | ||
| 2887 | "Invoke tramp file name handler. | ||
| 2888 | Falls back to normal file name handler if no tramp file name handler exists." | ||
| 2889 | (let ((fn (assoc operation tramp-file-name-handler-alist))) | ||
| 2890 | ;(message "Handling %s using %s" operation fn) | ||
| 2891 | (if fn | ||
| 2892 | (save-match-data | ||
| 2893 | (apply (cdr fn) args)) | ||
| 2894 | (tramp-run-real-handler operation args)))) | ||
| 2895 | |||
| 2896 | ;; Register in file name handler alist | ||
| 2897 | ;;;###autoload | ||
| 2898 | (add-to-list 'file-name-handler-alist | ||
| 2899 | (cons tramp-file-name-regexp 'tramp-file-name-handler)) | ||
| 2900 | |||
| 2901 | ;; If jka-compr is already loaded, move it to the front of | ||
| 2902 | ;; `file-name-handler-alist'. On Emacs 21.3 or so this will not be | ||
| 2903 | ;; necessary anymore. | ||
| 2904 | (let ((jka (rassoc 'jka-compr-handler file-name-handler-alist))) | ||
| 2905 | (when jka | ||
| 2906 | (setq file-name-handler-alist | ||
| 2907 | (cons jka (delete jka file-name-handler-alist))))) | ||
| 2908 | |||
| 2909 | ;;; Interactions with other packages: | ||
| 2910 | |||
| 2911 | ;; -- complete.el -- | ||
| 2912 | |||
| 2913 | ;; This function contributed by Ed Sabol | ||
| 2914 | (defun tramp-handle-expand-many-files (name) | ||
| 2915 | "Like `PC-expand-many-files' for tramp files." | ||
| 2916 | (save-match-data | ||
| 2917 | (if (or (string-match "\\*" name) | ||
| 2918 | (string-match "\\?" name) | ||
| 2919 | (string-match "\\[.*\\]" name)) | ||
| 2920 | (save-excursion | ||
| 2921 | ;; Dissect NAME. | ||
| 2922 | (let* ((v (tramp-dissect-file-name name)) | ||
| 2923 | (multi-method (tramp-file-name-multi-method v)) | ||
| 2924 | (method (tramp-file-name-method v)) | ||
| 2925 | (user (tramp-file-name-user v)) | ||
| 2926 | (host (tramp-file-name-host v)) | ||
| 2927 | (path (tramp-file-name-path v)) | ||
| 2928 | bufstr) | ||
| 2929 | ;; CCC: To do it right, we should quote certain characters | ||
| 2930 | ;; in the file name, but since the echo command is going to | ||
| 2931 | ;; break anyway when there are spaces in the file names, we | ||
| 2932 | ;; don't bother. | ||
| 2933 | ;;-(let ((comint-file-name-quote-list | ||
| 2934 | ;;- (set-difference tramp-file-name-quote-list | ||
| 2935 | ;;- '(?\* ?\? ?[ ?])))) | ||
| 2936 | ;;- (tramp-send-command | ||
| 2937 | ;;- multi-method method user host | ||
| 2938 | ;;- (format "echo %s" (comint-quote-filename path))) | ||
| 2939 | ;;- (tramp-wait-for-output)) | ||
| 2940 | (tramp-send-command multi-method method user host | ||
| 2941 | (format "echo %s" path)) | ||
| 2942 | (tramp-wait-for-output) | ||
| 2943 | (setq bufstr (buffer-substring (point-min) | ||
| 2944 | (tramp-line-end-position))) | ||
| 2945 | (goto-char (point-min)) | ||
| 2946 | (if (string-equal path bufstr) | ||
| 2947 | nil | ||
| 2948 | (insert "(\"") | ||
| 2949 | (while (search-forward " " nil t) | ||
| 2950 | (delete-backward-char 1) | ||
| 2951 | (insert "\" \"")) | ||
| 2952 | (goto-char (point-max)) | ||
| 2953 | (delete-backward-char 1) | ||
| 2954 | (insert "\")") | ||
| 2955 | (goto-char (point-min)) | ||
| 2956 | (mapcar | ||
| 2957 | (function (lambda (x) | ||
| 2958 | (tramp-make-tramp-file-name multi-method method | ||
| 2959 | user host x))) | ||
| 2960 | (read (current-buffer)))))) | ||
| 2961 | (list (tramp-handle-expand-file-name name))))) | ||
| 2962 | |||
| 2963 | ;; Check for complete.el and override PC-expand-many-files if appropriate. | ||
| 2964 | (eval-when-compile | ||
| 2965 | (defun tramp-save-PC-expand-many-files (name))); avoid compiler warning | ||
| 2966 | |||
| 2967 | (defun tramp-setup-complete () | ||
| 2968 | (fset 'tramp-save-PC-expand-many-files | ||
| 2969 | (symbol-function 'PC-expand-many-files)) | ||
| 2970 | (defun PC-expand-many-files (name) | ||
| 2971 | (if (tramp-tramp-file-p name) | ||
| 2972 | (tramp-handle-expand-many-files name) | ||
| 2973 | (tramp-save-PC-expand-many-files name)))) | ||
| 2974 | |||
| 2975 | ;; Why isn't eval-after-load sufficient? | ||
| 2976 | (if (fboundp 'PC-expand-many-files) | ||
| 2977 | (tramp-setup-complete) | ||
| 2978 | (eval-after-load "complete" '(tramp-setup-complete))) | ||
| 2979 | |||
| 2980 | |||
| 2981 | |||
| 2982 | |||
| 2983 | ;;; Internal Functions: | ||
| 2984 | |||
| 2985 | (defun tramp-set-auto-save () | ||
| 2986 | (when (and (buffer-file-name) | ||
| 2987 | (tramp-tramp-file-p (buffer-file-name)) | ||
| 2988 | auto-save-default) | ||
| 2989 | (auto-save-mode 1))) | ||
| 2990 | (add-hook 'find-file-hooks 'tramp-set-auto-save t) | ||
| 2991 | |||
| 2992 | (defun tramp-run-test (switch filename) | ||
| 2993 | "Run `test' on the remote system, given a SWITCH and a FILENAME. | ||
| 2994 | Returns the exit code of the `test' program." | ||
| 2995 | (let ((v (tramp-dissect-file-name filename))) | ||
| 2996 | (save-excursion | ||
| 2997 | (tramp-send-command-and-check | ||
| 2998 | (tramp-file-name-multi-method v) (tramp-file-name-method v) | ||
| 2999 | (tramp-file-name-user v) (tramp-file-name-host v) | ||
| 3000 | (format "test %s %s" switch | ||
| 3001 | (tramp-shell-quote-argument (tramp-file-name-path v))))))) | ||
| 3002 | |||
| 3003 | (defun tramp-run-test2 (program file1 file2 &optional switch) | ||
| 3004 | "Run `test'-like PROGRAM on the remote system, given FILE1, FILE2. | ||
| 3005 | The optional SWITCH is inserted between the two files. | ||
| 3006 | Returns the exit code of the `test' PROGRAM. Barfs if the methods, | ||
| 3007 | hosts, or files, disagree." | ||
| 3008 | (let* ((v1 (tramp-dissect-file-name file1)) | ||
| 3009 | (v2 (tramp-dissect-file-name file2)) | ||
| 3010 | (mmethod1 (tramp-file-name-multi-method v1)) | ||
| 3011 | (mmethod2 (tramp-file-name-multi-method v2)) | ||
| 3012 | (method1 (tramp-file-name-method v1)) | ||
| 3013 | (method2 (tramp-file-name-method v2)) | ||
| 3014 | (user1 (tramp-file-name-user v1)) | ||
| 3015 | (user2 (tramp-file-name-user v2)) | ||
| 3016 | (host1 (tramp-file-name-host v1)) | ||
| 3017 | (host2 (tramp-file-name-host v2)) | ||
| 3018 | (path1 (tramp-file-name-path v1)) | ||
| 3019 | (path2 (tramp-file-name-path v2))) | ||
| 3020 | (unless (and method1 method2 host1 host2 | ||
| 3021 | (equal mmethod1 mmethod2) | ||
| 3022 | (equal method1 method2) | ||
| 3023 | (equal user1 user2) | ||
| 3024 | (equal host1 host2)) | ||
| 3025 | (error "tramp-run-test2: %s" | ||
| 3026 | "only implemented for same method, same user, same host")) | ||
| 3027 | (save-excursion | ||
| 3028 | (tramp-send-command-and-check | ||
| 3029 | mmethod1 method1 user1 host1 | ||
| 3030 | (format "%s %s %s %s" | ||
| 3031 | program | ||
| 3032 | (tramp-shell-quote-argument path1) | ||
| 3033 | (or switch "") | ||
| 3034 | (tramp-shell-quote-argument path2)))))) | ||
| 3035 | |||
| 3036 | (defun tramp-buffer-name (multi-method method user host) | ||
| 3037 | "A name for the connection buffer for USER at HOST using METHOD." | ||
| 3038 | (cond (multi-method | ||
| 3039 | (tramp-buffer-name-multi-method "tramp" multi-method method user host)) | ||
| 3040 | (user | ||
| 3041 | (format "*tramp/%s %s@%s*" method user host)) | ||
| 3042 | (t | ||
| 3043 | (format "*tramp/%s %s*" method host)))) | ||
| 3044 | |||
| 3045 | (defun tramp-buffer-name-multi-method (prefix multi-method method user host) | ||
| 3046 | "A name for the multi method connection buffer. | ||
| 3047 | MULTI-METHOD gives the multi method, METHOD the array of methods, | ||
| 3048 | USER the array of user names, HOST the array of host names." | ||
| 3049 | (unless (and (= (length method) (length user)) | ||
| 3050 | (= (length method) (length host))) | ||
| 3051 | (error "Syntax error in multi method (implementation error)")) | ||
| 3052 | (let ((len (length method)) | ||
| 3053 | (i 0) | ||
| 3054 | string-list) | ||
| 3055 | (while (< i len) | ||
| 3056 | (setq string-list | ||
| 3057 | (cons (if (aref user i) | ||
| 3058 | (format "%s#%s@%s:" (aref method i) | ||
| 3059 | (aref user i) (aref host i)) | ||
| 3060 | (format "%s@%s:" (aref method i) (aref host i))) | ||
| 3061 | string-list)) | ||
| 3062 | (incf i)) | ||
| 3063 | (format "*%s/%s %s*" | ||
| 3064 | prefix multi-method | ||
| 3065 | (apply 'concat (reverse string-list))))) | ||
| 3066 | |||
| 3067 | (defun tramp-get-buffer (multi-method method user host) | ||
| 3068 | "Get the connection buffer to be used for USER at HOST using METHOD." | ||
| 3069 | (get-buffer-create (tramp-buffer-name multi-method method user host))) | ||
| 3070 | |||
| 3071 | (defun tramp-debug-buffer-name (multi-method method user host) | ||
| 3072 | "A name for the debug buffer for USER at HOST using METHOD." | ||
| 3073 | (cond (multi-method | ||
| 3074 | (tramp-buffer-name-multi-method "debug tramp" | ||
| 3075 | multi-method method user host)) | ||
| 3076 | (user | ||
| 3077 | (format "*debug tramp/%s %s@%s*" method user host)) | ||
| 3078 | (t | ||
| 3079 | (format "*debug tramp/%s %s*" method host)))) | ||
| 3080 | |||
| 3081 | (defun tramp-get-debug-buffer (multi-method method user host) | ||
| 3082 | "Get the debug buffer for USER at HOST using METHOD." | ||
| 3083 | (get-buffer-create (tramp-debug-buffer-name multi-method method user host))) | ||
| 3084 | |||
| 3085 | (defun tramp-find-executable (multi-method method user host | ||
| 3086 | progname dirlist ignore-tilde) | ||
| 3087 | "Searches for PROGNAME in all directories mentioned in DIRLIST. | ||
| 3088 | First args METHOD, USER and HOST specify the connection, PROGNAME | ||
| 3089 | is the program to search for, and DIRLIST gives the list of directories | ||
| 3090 | to search. If IGNORE-TILDE is non-nil, directory names starting | ||
| 3091 | with `~' will be ignored. | ||
| 3092 | |||
| 3093 | Returns the full path name of PROGNAME, if found, and nil otherwise. | ||
| 3094 | |||
| 3095 | This function expects to be in the right *tramp* buffer." | ||
| 3096 | (let (result) | ||
| 3097 | (when ignore-tilde | ||
| 3098 | ;; Remove all ~/foo directories from dirlist. In Emacs 20, | ||
| 3099 | ;; `remove' is in CL, and we want to avoid CL dependencies. | ||
| 3100 | (let (newdl d) | ||
| 3101 | (while dirlist | ||
| 3102 | (setq d (car dirlist)) | ||
| 3103 | (setq dirlist (cdr dirlist)) | ||
| 3104 | (unless (char-equal ?~ (aref d 0)) | ||
| 3105 | (setq newdl (cons d newdl)))) | ||
| 3106 | (setq dirlist (nreverse newdl)))) | ||
| 3107 | (tramp-send-command | ||
| 3108 | multi-method method user host | ||
| 3109 | (format (concat "while read d; " | ||
| 3110 | "do if test -x $d/%s -a -f $d/%s; " | ||
| 3111 | "then echo tramp_executable $d/%s; " | ||
| 3112 | "break; fi; done <<'EOF'") | ||
| 3113 | progname progname progname)) | ||
| 3114 | (mapcar (lambda (d) | ||
| 3115 | (tramp-send-command multi-method method user host d)) | ||
| 3116 | dirlist) | ||
| 3117 | (tramp-send-command multi-method method user host "EOF") | ||
| 3118 | (tramp-wait-for-output) | ||
| 3119 | (goto-char (point-max)) | ||
| 3120 | (when (search-backward "tramp_executable " nil t) | ||
| 3121 | (skip-chars-forward "^ ") | ||
| 3122 | (skip-chars-forward " ") | ||
| 3123 | (buffer-substring (point) (tramp-line-end-position))))) | ||
| 3124 | |||
| 3125 | (defun tramp-set-remote-path (multi-method method user host var dirlist) | ||
| 3126 | "Sets the remote environment VAR to existing directories from DIRLIST. | ||
| 3127 | I.e., for each directory in DIRLIST, it is tested whether it exists and if | ||
| 3128 | so, it is added to the environment variable VAR." | ||
| 3129 | (let ((existing-dirs | ||
| 3130 | (mapcar | ||
| 3131 | (lambda (x) | ||
| 3132 | (when (and | ||
| 3133 | (file-exists-p | ||
| 3134 | (tramp-make-tramp-file-name multi-method method user host x)) | ||
| 3135 | (file-directory-p | ||
| 3136 | (tramp-make-tramp-file-name multi-method method user host x))) | ||
| 3137 | x)) | ||
| 3138 | dirlist))) | ||
| 3139 | (tramp-send-command | ||
| 3140 | multi-method method user host | ||
| 3141 | (concat var "=" | ||
| 3142 | (mapconcat 'identity (delq nil existing-dirs) ":") | ||
| 3143 | "; export " var)) | ||
| 3144 | (tramp-wait-for-output))) | ||
| 3145 | |||
| 3146 | ;; -- communication with external shell -- | ||
| 3147 | |||
| 3148 | (defun tramp-find-file-exists-command (multi-method method user host) | ||
| 3149 | "Find a command on the remote host for checking if a file exists. | ||
| 3150 | Here, we are looking for a command which has zero exit status if the | ||
| 3151 | file exists and nonzero exit status otherwise." | ||
| 3152 | (make-local-variable 'tramp-file-exists-command) | ||
| 3153 | (tramp-message 10 "Finding command to check if file exists") | ||
| 3154 | (let ((existing | ||
| 3155 | (tramp-make-tramp-file-name | ||
| 3156 | multi-method method user host | ||
| 3157 | "/")) ;assume this file always exists | ||
| 3158 | (nonexisting | ||
| 3159 | (tramp-make-tramp-file-name | ||
| 3160 | multi-method method user host | ||
| 3161 | "/ this file does not exist "))) ;assume this never exists | ||
| 3162 | ;; The algorithm is as follows: we try a list of several commands. | ||
| 3163 | ;; For each command, we first run `$cmd /' -- this should return | ||
| 3164 | ;; true, as the root directory always exists. And then we run | ||
| 3165 | ;; `$cmd /this\ file\ does\ not\ exist', hoping that the file indeed | ||
| 3166 | ;; does not exist. This should return false. We use the first | ||
| 3167 | ;; command we find that seems to work. | ||
| 3168 | ;; The list of commands to try is as follows: | ||
| 3169 | ;; `ls -d' This works on most systems, but NetBSD 1.4 | ||
| 3170 | ;; has a bug: `ls' always returns zero exit | ||
| 3171 | ;; status, even for files which don't exist. | ||
| 3172 | ;; `test -e' Some Bourne shells have a `test' builtin | ||
| 3173 | ;; which does not know the `-e' option. | ||
| 3174 | ;; `/bin/test -e' For those, the `test' binary on disk normally | ||
| 3175 | ;; provides the option. Alas, the binary | ||
| 3176 | ;; is sometimes `/bin/test' and sometimes it's | ||
| 3177 | ;; `/usr/bin/test'. | ||
| 3178 | ;; `/usr/bin/test -e' In case `/bin/test' does not exist. | ||
| 3179 | (unless (or | ||
| 3180 | (and (setq tramp-file-exists-command "ls -d %s") | ||
| 3181 | (tramp-handle-file-exists-p existing) | ||
| 3182 | (not (tramp-handle-file-exists-p nonexisting))) | ||
| 3183 | (and (setq tramp-file-exists-command "test -e %s") | ||
| 3184 | (tramp-handle-file-exists-p existing) | ||
| 3185 | (not (tramp-handle-file-exists-p nonexisting))) | ||
| 3186 | (and (setq tramp-file-exists-command "/bin/test -e %s") | ||
| 3187 | (tramp-handle-file-exists-p existing) | ||
| 3188 | (not (tramp-handle-file-exists-p nonexisting))) | ||
| 3189 | (and (setq tramp-file-exists-command "/usr/bin/test -e %s") | ||
| 3190 | (tramp-handle-file-exists-p existing) | ||
| 3191 | (not (tramp-handle-file-exists-p nonexisting)))) | ||
| 3192 | (error "Couldn't find command to check if file exists.")))) | ||
| 3193 | |||
| 3194 | |||
| 3195 | ;; CCC test ksh or bash found for tilde expansion? | ||
| 3196 | (defun tramp-find-shell (multi-method method user host) | ||
| 3197 | "Find a shell on the remote host which groks tilde expansion." | ||
| 3198 | (let ((shell nil)) | ||
| 3199 | (tramp-send-command multi-method method user host "echo ~root") | ||
| 3200 | (tramp-wait-for-output) | ||
| 3201 | (cond | ||
| 3202 | ((string-match "^~root$" (buffer-string)) | ||
| 3203 | (setq shell | ||
| 3204 | (or (tramp-find-executable multi-method method user host | ||
| 3205 | "bash" tramp-remote-path t) | ||
| 3206 | (tramp-find-executable multi-method method user host | ||
| 3207 | "ksh" tramp-remote-path t))) | ||
| 3208 | (unless shell | ||
| 3209 | (error "Couldn't find a shell which groks tilde expansion")) | ||
| 3210 | ;; Hack: avoid reading of ~/.bashrc. What we should do is have an | ||
| 3211 | ;; alist for extra args to give to each shell... | ||
| 3212 | (when (string-match "/bash\\'" shell) | ||
| 3213 | (setq shell (concat shell " --norc"))) | ||
| 3214 | (tramp-message | ||
| 3215 | 5 "Starting remote shell `%s' for tilde expansion..." shell) | ||
| 3216 | (tramp-send-command | ||
| 3217 | multi-method method user host | ||
| 3218 | (concat "PS1='$ ' ; exec " shell)) | ||
| 3219 | (unless (tramp-wait-for-regexp | ||
| 3220 | (get-buffer-process (current-buffer)) | ||
| 3221 | 60 (format "\\(\\$ *\\|\\(%s\\)\\'\\)" shell-prompt-pattern)) | ||
| 3222 | (pop-to-buffer (buffer-name)) | ||
| 3223 | (error "Couldn't find remote `%s' prompt." shell)) | ||
| 3224 | (process-send-string nil (format "PS1='%s%s%s'; PS2=''; PS3=''%s" | ||
| 3225 | tramp-rsh-end-of-line | ||
| 3226 | tramp-end-of-output | ||
| 3227 | tramp-rsh-end-of-line | ||
| 3228 | tramp-rsh-end-of-line)) | ||
| 3229 | (tramp-wait-for-output) | ||
| 3230 | (tramp-send-command multi-method method user host "echo hello") | ||
| 3231 | (tramp-message 5 "Waiting for remote `%s' to start up..." shell) | ||
| 3232 | (unless (tramp-wait-for-output 5) | ||
| 3233 | (unless (tramp-wait-for-output 5) | ||
| 3234 | (pop-to-buffer (buffer-name)) | ||
| 3235 | (error "Couldn't start remote `%s', see buffer `%s' for details" | ||
| 3236 | shell (buffer-name)))) | ||
| 3237 | (tramp-message 5 "Waiting for remote `%s' to start up...done" shell)) | ||
| 3238 | (t (tramp-message 5 "Remote `%s' groks tilde expansion, good" | ||
| 3239 | (tramp-get-remote-sh multi-method method)))))) | ||
| 3240 | |||
| 3241 | (defun tramp-check-ls-command (multi-method method user host cmd) | ||
| 3242 | "Checks whether the given `ls' executable groks `-n'. | ||
| 3243 | METHOD, USER and HOST specify the connection, CMD (the full path name of) | ||
| 3244 | the `ls' executable. Returns t if CMD supports the `-n' option, nil | ||
| 3245 | otherwise." | ||
| 3246 | (tramp-message 9 "Checking remote `%s' command for `-n' option" | ||
| 3247 | cmd) | ||
| 3248 | (when (tramp-handle-file-executable-p | ||
| 3249 | (tramp-make-tramp-file-name multi-method method user host cmd)) | ||
| 3250 | (let ((result nil)) | ||
| 3251 | (tramp-message 7 "Testing remote command `%s' for -n..." cmd) | ||
| 3252 | (setq result | ||
| 3253 | (tramp-send-command-and-check | ||
| 3254 | multi-method method user host | ||
| 3255 | (format "%s -lnd / >/dev/null" | ||
| 3256 | cmd))) | ||
| 3257 | (tramp-message 7 "Testing remote command `%s' for -n...%s" | ||
| 3258 | cmd | ||
| 3259 | (if (zerop result) "okay" "failed")) | ||
| 3260 | (zerop result)))) | ||
| 3261 | |||
| 3262 | (defun tramp-check-ls-commands (multi-method method user host cmd dirlist) | ||
| 3263 | "Checks whether the given `ls' executable in one of the dirs groks `-n'. | ||
| 3264 | Returns nil if none was found, else the command is returned." | ||
| 3265 | (let ((dl dirlist) | ||
| 3266 | (result nil)) | ||
| 3267 | ;; It would be better to use the CL function `find', but | ||
| 3268 | ;; we don't want run-time dependencies on CL. | ||
| 3269 | (while (and dl (not result)) | ||
| 3270 | (let ((x (concat (file-name-as-directory (car dl)) cmd))) | ||
| 3271 | (when (tramp-check-ls-command multi-method method user host x) | ||
| 3272 | (setq result x))) | ||
| 3273 | (setq dl (cdr dl))) | ||
| 3274 | result)) | ||
| 3275 | |||
| 3276 | (defun tramp-find-ls-command (multi-method method user host) | ||
| 3277 | "Finds an `ls' command which groks the `-n' option, returning nil if failed. | ||
| 3278 | \(This option prints numeric user and group ids in a long listing.)" | ||
| 3279 | (tramp-message 9 "Finding a suitable `ls' command") | ||
| 3280 | (or | ||
| 3281 | (tramp-check-ls-commands multi-method method user host "ls" tramp-remote-path) | ||
| 3282 | (tramp-check-ls-commands multi-method method user host "gnuls" tramp-remote-path) | ||
| 3283 | (tramp-check-ls-commands multi-method method user host "gls" tramp-remote-path))) | ||
| 3284 | |||
| 3285 | ;; ------------------------------------------------------------ | ||
| 3286 | ;; -- Functions for establishing connection -- | ||
| 3287 | ;; ------------------------------------------------------------ | ||
| 3288 | |||
| 3289 | (defun tramp-process-actions | ||
| 3290 | (multi-method method user host actions &optional timeout) | ||
| 3291 | "Process given ACTIONS for login specified via first four args. | ||
| 3292 | ACTIONS is a list of items (REGEXP FUN), where REGEXP specifies what | ||
| 3293 | output from the remote end to look for, and FUN specifies the action | ||
| 3294 | to take when the regexp matches." | ||
| 3295 | nil) | ||
| 3296 | |||
| 3297 | (defun tramp-open-connection-telnet (multi-method method user host) | ||
| 3298 | "Open a connection using a telnet METHOD. | ||
| 3299 | This starts the command `telnet HOST ARGS'[*], then waits for a remote | ||
| 3300 | login prompt, then sends the user name USER, then waits for a remote | ||
| 3301 | password prompt. It queries the user for the password, then sends the | ||
| 3302 | password to the remote host. | ||
| 3303 | |||
| 3304 | If USER is nil, uses value returned by `(user-login-name)' instead. | ||
| 3305 | |||
| 3306 | Recognition of the remote shell prompt is based on the variable | ||
| 3307 | `shell-prompt-pattern' which must be set up correctly. | ||
| 3308 | |||
| 3309 | Please note that it is NOT possible to use this connection method | ||
| 3310 | together with an out-of-band transfer method! You must use an inline | ||
| 3311 | transfer method. | ||
| 3312 | |||
| 3313 | Maybe the different regular expressions need to be tuned. | ||
| 3314 | |||
| 3315 | * Actually, the telnet program as well as the args to be used can be | ||
| 3316 | specified in the method parameters, see the variable `tramp-methods'." | ||
| 3317 | (save-match-data | ||
| 3318 | (when (tramp-method-out-of-band-p multi-method method) | ||
| 3319 | (error "Cannot use out-of-band method `%s' with telnet connection method" | ||
| 3320 | method)) | ||
| 3321 | (when multi-method | ||
| 3322 | (error "Cannot multi-connect using telnet connection method")) | ||
| 3323 | (tramp-pre-connection multi-method method user host) | ||
| 3324 | (tramp-message 7 "Opening connection for %s@%s using %s..." | ||
| 3325 | (or user (user-login-name)) host method) | ||
| 3326 | (let ((process-environment (copy-sequence process-environment))) | ||
| 3327 | (setenv "TERM" tramp-terminal-type) | ||
| 3328 | (let* ((default-directory (tramp-temporary-file-directory)) | ||
| 3329 | (coding-system-for-read (unless (and (not (featurep 'xemacs)) | ||
| 3330 | (> emacs-major-version 20)) | ||
| 3331 | tramp-dos-coding-system)) | ||
| 3332 | (p (apply 'start-process | ||
| 3333 | (tramp-buffer-name multi-method method user host) | ||
| 3334 | (tramp-get-buffer multi-method method user host) | ||
| 3335 | (tramp-get-telnet-program multi-method method) | ||
| 3336 | host | ||
| 3337 | (tramp-get-telnet-args multi-method method))) | ||
| 3338 | (found nil) | ||
| 3339 | (pw nil)) | ||
| 3340 | (process-kill-without-query p) | ||
| 3341 | (tramp-message 9 "Waiting for login prompt...") | ||
| 3342 | (unless (tramp-wait-for-regexp p nil tramp-login-prompt-regexp) | ||
| 3343 | (pop-to-buffer (buffer-name)) | ||
| 3344 | (kill-process p) | ||
| 3345 | (error "Couldn't find remote login prompt")) | ||
| 3346 | (erase-buffer) | ||
| 3347 | ;; Remote login defaults to local one. | ||
| 3348 | (tramp-message 9 "Sending login name %s" (or user (user-login-name))) | ||
| 3349 | (process-send-string p (concat (or user (user-login-name)) | ||
| 3350 | tramp-rsh-end-of-line)) | ||
| 3351 | (tramp-message 9 "Waiting for password prompt...") | ||
| 3352 | (unless (setq found (tramp-wait-for-regexp | ||
| 3353 | p nil tramp-password-prompt-regexp)) | ||
| 3354 | (pop-to-buffer (buffer-name)) | ||
| 3355 | (kill-process p) | ||
| 3356 | (error "Couldn't find remote password prompt")) | ||
| 3357 | (erase-buffer) | ||
| 3358 | (setq pw (tramp-read-passwd (car found))) | ||
| 3359 | (tramp-message 9 "Sending password") | ||
| 3360 | (process-send-string p (concat pw tramp-rsh-end-of-line)) | ||
| 3361 | (tramp-message 9 "Waiting 30s for remote shell to come up...") | ||
| 3362 | (unless (setq found | ||
| 3363 | (tramp-wait-for-regexp | ||
| 3364 | p 30 (format "\\(%s\\)\\|\\(%s\\)\\'" | ||
| 3365 | tramp-wrong-passwd-regexp | ||
| 3366 | shell-prompt-pattern))) | ||
| 3367 | (pop-to-buffer (buffer-name)) | ||
| 3368 | (kill-process p) | ||
| 3369 | (error "Couldn't find remote shell prompt")) | ||
| 3370 | (when (nth 1 found) | ||
| 3371 | (pop-to-buffer (buffer-name)) | ||
| 3372 | (kill-process p) | ||
| 3373 | (error "Login failed: %s" (nth 1 found))) | ||
| 3374 | (tramp-open-connection-setup-interactive-shell | ||
| 3375 | p multi-method method user host) | ||
| 3376 | (tramp-post-connection multi-method method user host))))) | ||
| 3377 | |||
| 3378 | ;; HHH: Changed to handle the case when USER is nil. | ||
| 3379 | (defun tramp-open-connection-rsh (multi-method method user host) | ||
| 3380 | "Open a connection using an rsh METHOD. | ||
| 3381 | This starts the command `rsh HOST -l USER'[*], then waits for a remote | ||
| 3382 | password or shell prompt. If a password prompt is seen, the user is | ||
| 3383 | queried for a password, this function sends the password to the remote | ||
| 3384 | host and waits for a shell prompt. | ||
| 3385 | |||
| 3386 | If USER is nil, start the command `rsh HOST'[*] instead | ||
| 3387 | |||
| 3388 | Recognition of the remote shell prompt is based on the variable | ||
| 3389 | `shell-prompt-pattern' which must be set up correctly. | ||
| 3390 | |||
| 3391 | Please note that it is NOT possible to use this connection method with | ||
| 3392 | an out-of-band transfer method if this function asks the user for a | ||
| 3393 | password! You must use an inline transfer method in this case. | ||
| 3394 | Sadly, the transfer method cannot be switched on the fly, instead you | ||
| 3395 | must specify the right method in the file name. | ||
| 3396 | |||
| 3397 | * Actually, the rsh program to be used can be specified in the | ||
| 3398 | method parameters, see the variable `tramp-methods'." | ||
| 3399 | (save-match-data | ||
| 3400 | (when multi-method | ||
| 3401 | (error "Cannot multi-connect using rsh connection method")) | ||
| 3402 | (tramp-pre-connection multi-method method user host) | ||
| 3403 | (if user | ||
| 3404 | (tramp-message 7 "Opening connection for %s@%s using %s..." | ||
| 3405 | user host method) | ||
| 3406 | (tramp-message 7 "Opening connection at %s using %s..." host method)) | ||
| 3407 | (let ((process-environment (copy-sequence process-environment))) | ||
| 3408 | (setenv "TERM" tramp-terminal-type) | ||
| 3409 | (let* ((default-directory (tramp-temporary-file-directory)) | ||
| 3410 | (coding-system-for-read (unless (and (not (featurep 'xemacs)) | ||
| 3411 | (> emacs-major-version 20)) | ||
| 3412 | tramp-dos-coding-system)) | ||
| 3413 | (p (if user | ||
| 3414 | (apply #'start-process | ||
| 3415 | (tramp-buffer-name multi-method method user host) | ||
| 3416 | (tramp-get-buffer multi-method method user host) | ||
| 3417 | (tramp-get-rsh-program multi-method method) | ||
| 3418 | host "-l" user | ||
| 3419 | (tramp-get-rsh-args multi-method method)) | ||
| 3420 | (apply #'start-process | ||
| 3421 | (tramp-buffer-name multi-method method user host) | ||
| 3422 | (tramp-get-buffer multi-method method user host) | ||
| 3423 | (tramp-get-rsh-program multi-method method) | ||
| 3424 | host | ||
| 3425 | (tramp-get-rsh-args multi-method method)))) | ||
| 3426 | (found nil)) | ||
| 3427 | (process-kill-without-query p) | ||
| 3428 | (tramp-message 9 "Waiting 60s for shell or passwd prompt from %s" host) | ||
| 3429 | (setq found | ||
| 3430 | (tramp-wait-for-regexp | ||
| 3431 | p 60 | ||
| 3432 | (format | ||
| 3433 | "\\(%s\\)\\|\\(%s\\)\\'" | ||
| 3434 | tramp-password-prompt-regexp | ||
| 3435 | shell-prompt-pattern))) | ||
| 3436 | (unless found | ||
| 3437 | (pop-to-buffer (buffer-name)) | ||
| 3438 | (kill-process p) | ||
| 3439 | (error "Couldn't find remote shell or passwd prompt")) | ||
| 3440 | (when (nth 1 found) | ||
| 3441 | (when (tramp-method-out-of-band-p multi-method method) | ||
| 3442 | (pop-to-buffer (buffer-name)) | ||
| 3443 | (kill-process p) | ||
| 3444 | (error (concat "Out of band method `%s' not applicable" | ||
| 3445 | " for remote shell asking for a password") | ||
| 3446 | method)) | ||
| 3447 | (erase-buffer) | ||
| 3448 | (tramp-message 9 "Sending password...") | ||
| 3449 | (tramp-enter-password p (nth 1 found)) | ||
| 3450 | (tramp-message 9 "Sent password, waiting 60s for remote shell prompt") | ||
| 3451 | (setq found (tramp-wait-for-regexp p 60 | ||
| 3452 | (format "\\(%s\\)\\|\\(%s\\)\\'" | ||
| 3453 | tramp-wrong-passwd-regexp | ||
| 3454 | shell-prompt-pattern)))) | ||
| 3455 | (unless found | ||
| 3456 | (pop-to-buffer (buffer-name)) | ||
| 3457 | (kill-process p) | ||
| 3458 | (error "Couldn't find remote shell prompt")) | ||
| 3459 | (when (nth 1 found) | ||
| 3460 | (pop-to-buffer (buffer-name)) | ||
| 3461 | (kill-process p) | ||
| 3462 | (error "Login failed: %s" (nth 1 found))) | ||
| 3463 | (tramp-message 7 "Initializing remote shell") | ||
| 3464 | (tramp-open-connection-setup-interactive-shell | ||
| 3465 | p multi-method method user host) | ||
| 3466 | (tramp-post-connection multi-method method user host))))) | ||
| 3467 | |||
| 3468 | ;; HHH: Changed. Now utilizes (or user (user-login-name)) instead of USER. | ||
| 3469 | (defun tramp-open-connection-su (multi-method method user host) | ||
| 3470 | "Open a connection using the `su' program with METHOD. | ||
| 3471 | This starts `su - USER', then waits for a password prompt. The HOST | ||
| 3472 | name must be equal to the local host name or to `localhost'. | ||
| 3473 | |||
| 3474 | If USER is nil, uses value returned by user-login-name instead. | ||
| 3475 | |||
| 3476 | Recognition of the remote shell prompt is based on the variable | ||
| 3477 | `shell-prompt-pattern' which must be set up correctly. Note that the | ||
| 3478 | other user may have a different shell prompt than you do, so it is not | ||
| 3479 | at all unlikely that this variable is set up wrongly!" | ||
| 3480 | (save-match-data | ||
| 3481 | (when (tramp-method-out-of-band-p multi-method method) | ||
| 3482 | (error "Cannot use out-of-band method `%s' with `su' connection method" | ||
| 3483 | method)) | ||
| 3484 | (unless (or (string-match (concat "^" (regexp-quote host)) | ||
| 3485 | (system-name)) | ||
| 3486 | (string= "localhost" host)) | ||
| 3487 | (error | ||
| 3488 | "Cannot connect to different host `%s' with `su' connection method" | ||
| 3489 | host)) | ||
| 3490 | (when (not user) | ||
| 3491 | (error "Must give user name for `su' connection method")) | ||
| 3492 | (tramp-pre-connection multi-method method user host) | ||
| 3493 | (tramp-message 7 "Opening connection for `%s' using `%s'..." | ||
| 3494 | (or user (user-login-name)) method) | ||
| 3495 | (let ((process-environment (copy-sequence process-environment))) | ||
| 3496 | (setenv "TERM" tramp-terminal-type) | ||
| 3497 | (let* ((default-directory (tramp-temporary-file-directory)) | ||
| 3498 | (coding-system-for-read (unless (and (not (featurep 'xemacs)) | ||
| 3499 | (> emacs-major-version 20)) | ||
| 3500 | tramp-dos-coding-system)) | ||
| 3501 | (p (apply 'start-process | ||
| 3502 | (tramp-buffer-name multi-method method | ||
| 3503 | user host) | ||
| 3504 | (tramp-get-buffer multi-method method | ||
| 3505 | user host) | ||
| 3506 | (tramp-get-su-program multi-method method) | ||
| 3507 | (mapcar | ||
| 3508 | '(lambda (x) | ||
| 3509 | (format-spec | ||
| 3510 | x (list (cons ?u user)))) | ||
| 3511 | (tramp-get-su-args multi-method method)))) | ||
| 3512 | (found nil) | ||
| 3513 | (pw nil)) | ||
| 3514 | (process-kill-without-query p) | ||
| 3515 | (tramp-message 9 "Waiting 30s for shell or password prompt...") | ||
| 3516 | (unless (setq found (tramp-wait-for-regexp | ||
| 3517 | p 30 | ||
| 3518 | (format "\\(%s\\)\\|\\(%s\\)\\'" | ||
| 3519 | tramp-password-prompt-regexp | ||
| 3520 | shell-prompt-pattern))) | ||
| 3521 | (pop-to-buffer (buffer-name)) | ||
| 3522 | (kill-process p) | ||
| 3523 | (error "Couldn't find shell or password prompt")) | ||
| 3524 | (when (nth 1 found) | ||
| 3525 | (erase-buffer) | ||
| 3526 | (setq pw (tramp-read-passwd (car found))) | ||
| 3527 | (tramp-message 9 "Sending password") | ||
| 3528 | (process-send-string p (concat pw tramp-rsh-end-of-line)) | ||
| 3529 | (tramp-message 9 "Waiting 30s for remote shell to come up...") | ||
| 3530 | (unless (setq found | ||
| 3531 | (tramp-wait-for-regexp | ||
| 3532 | p 30 (format "\\(%s\\)\\|\\(%s\\)\\'" | ||
| 3533 | tramp-wrong-passwd-regexp | ||
| 3534 | shell-prompt-pattern))) | ||
| 3535 | (pop-to-buffer (buffer-name)) | ||
| 3536 | (kill-process p) | ||
| 3537 | (error "Couldn't find remote shell prompt")) | ||
| 3538 | (when (nth 1 found) | ||
| 3539 | (pop-to-buffer (buffer-name)) | ||
| 3540 | (kill-process p) | ||
| 3541 | (error "`su' failed: %s" (nth 1 found)))) | ||
| 3542 | (tramp-open-connection-setup-interactive-shell | ||
| 3543 | p multi-method method user host) | ||
| 3544 | (tramp-post-connection multi-method method | ||
| 3545 | user host))))) | ||
| 3546 | |||
| 3547 | ;; HHH: Not Changed. Multi method. It is not clear to me how this can | ||
| 3548 | ;; handle not giving a user name in the "file name". | ||
| 3549 | ;; | ||
| 3550 | ;; This is more difficult than for the single-hop method. In the | ||
| 3551 | ;; multi-hop-method, the desired behaviour should be that the | ||
| 3552 | ;; user must specify names for the telnet hops of which the user | ||
| 3553 | ;; name is different than the "original" name (or different from | ||
| 3554 | ;; the previous hop. | ||
| 3555 | (defun tramp-open-connection-multi (multi-method method user host) | ||
| 3556 | "Open a multi-hop connection using METHOD. | ||
| 3557 | This uses a slightly changed file name syntax. The idea is to say | ||
| 3558 | [multi/telnet:u1@h1/rsh:u2@h2]/path/to/file | ||
| 3559 | This will use telnet to log in as u1 to h1, then use rsh from there to | ||
| 3560 | log in as u2 to h2." | ||
| 3561 | (save-match-data | ||
| 3562 | (unless multi-method | ||
| 3563 | (error "Multi-hop open connection function called on non-multi method")) | ||
| 3564 | (when (tramp-method-out-of-band-p multi-method method) | ||
| 3565 | (error "No out of band multi-hop connections")) | ||
| 3566 | (unless (and (arrayp method) (not (stringp method))) | ||
| 3567 | (error "METHOD must be an array of strings for multi methods")) | ||
| 3568 | (unless (and (arrayp user) (not (stringp user))) | ||
| 3569 | (error "USER must be an array of strings for multi methods")) | ||
| 3570 | (unless (and (arrayp host) (not (stringp host))) | ||
| 3571 | (error "HOST must be an array of strings for multi methods")) | ||
| 3572 | (unless (and (= (length method) (length user)) | ||
| 3573 | (= (length method) (length host))) | ||
| 3574 | (error "Arrays METHOD, USER, HOST must have equal length")) | ||
| 3575 | (tramp-pre-connection multi-method method user host) | ||
| 3576 | (tramp-message 7 "Opening `%s' connection..." multi-method) | ||
| 3577 | (let ((process-environment (copy-sequence process-environment))) | ||
| 3578 | (setenv "TERM" tramp-terminal-type) | ||
| 3579 | (let* ((default-directory (tramp-temporary-file-directory)) | ||
| 3580 | (coding-system-for-read (unless (and (not (featurep 'xemacs)) | ||
| 3581 | (> emacs-major-version 20)) | ||
| 3582 | tramp-dos-coding-system)) | ||
| 3583 | (p (start-process (tramp-buffer-name multi-method method user host) | ||
| 3584 | (tramp-get-buffer multi-method method user host) | ||
| 3585 | tramp-sh-program)) | ||
| 3586 | (num-hops (length method)) | ||
| 3587 | (i 0)) | ||
| 3588 | (process-kill-without-query p) | ||
| 3589 | (tramp-message 9 "Waiting 60s for local shell to come up...") | ||
| 3590 | (unless (tramp-wait-for-regexp | ||
| 3591 | p 60 (format "%s\\'" shell-prompt-pattern)) | ||
| 3592 | (pop-to-buffer (buffer-name)) | ||
| 3593 | (kill-process p) | ||
| 3594 | (error "Couldn't find local shell prompt")) | ||
| 3595 | ;; Now do all the connections as specified. | ||
| 3596 | (while (< i num-hops) | ||
| 3597 | (let* ((m (aref method i)) | ||
| 3598 | (u (aref user i)) | ||
| 3599 | (h (aref host i)) | ||
| 3600 | (entry (assoc m tramp-multi-connection-function-alist)) | ||
| 3601 | (multi-func (nth 1 entry)) | ||
| 3602 | (command (nth 2 entry))) | ||
| 3603 | ;; The multi-funcs don't need to do save-match-data, as that | ||
| 3604 | ;; is done here. | ||
| 3605 | (funcall multi-func p m u h command) | ||
| 3606 | (erase-buffer) | ||
| 3607 | (incf i))) | ||
| 3608 | (tramp-open-connection-setup-interactive-shell | ||
| 3609 | p multi-method method user host) | ||
| 3610 | (tramp-post-connection multi-method method user host))))) | ||
| 3611 | |||
| 3612 | ;; HHH: Changed. Multi method. Don't know how to handle this in the case | ||
| 3613 | ;; of no user name provided. Hack to make it work as it did before: | ||
| 3614 | ;; changed `user' to `(or user (user-login-name))' in the places where | ||
| 3615 | ;; the value is actually used. | ||
| 3616 | (defun tramp-multi-connect-telnet (p method user host command) | ||
| 3617 | "Issue `telnet' command. | ||
| 3618 | Uses shell COMMAND to issue a `telnet' command to log in as USER to | ||
| 3619 | HOST. You can use percent escapes in COMMAND: `%h' is replaced with | ||
| 3620 | the host name, and `%n' is replaced with an end of line character, as | ||
| 3621 | set in `tramp-rsh-end-of-line'. Use `%%' if you want a literal percent | ||
| 3622 | character. | ||
| 3623 | |||
| 3624 | If USER is nil, uses the return value of (user-login-name) instead." | ||
| 3625 | (let ((cmd (format-spec command (list (cons ?h host) | ||
| 3626 | (cons ?n tramp-rsh-end-of-line)))) | ||
| 3627 | (cmd1 (format-spec command (list (cons ?h host) | ||
| 3628 | (cons ?n "")))) | ||
| 3629 | found pw) | ||
| 3630 | (erase-buffer) | ||
| 3631 | (tramp-message 9 "Sending telnet command `%s'" cmd1) | ||
| 3632 | (process-send-string p cmd) | ||
| 3633 | (tramp-message 9 "Waiting 30s for login prompt from %s" host) | ||
| 3634 | (unless (tramp-wait-for-regexp p 30 tramp-login-prompt-regexp) | ||
| 3635 | (pop-to-buffer (buffer-name)) | ||
| 3636 | (kill-process p) | ||
| 3637 | (error "Couldn't find login prompt from host %s" host)) | ||
| 3638 | (erase-buffer) | ||
| 3639 | (tramp-message 9 "Sending login name %s" (or user (user-login-name))) | ||
| 3640 | (process-send-string p (concat (or user (user-login-name)) tramp-rsh-end-of-line)) | ||
| 3641 | (tramp-message 9 "Waiting for password prompt") | ||
| 3642 | (unless (setq found (tramp-wait-for-regexp p nil tramp-password-prompt-regexp)) | ||
| 3643 | (pop-to-buffer (buffer-name)) | ||
| 3644 | (kill-process p) | ||
| 3645 | (error "Couldn't find password prompt from host %s" host)) | ||
| 3646 | (erase-buffer) | ||
| 3647 | (setq pw (tramp-read-passwd | ||
| 3648 | (format "Password for %s@%s, %s" (or user (user-login-name)) host found))) | ||
| 3649 | (tramp-message 9 "Sending password") | ||
| 3650 | (process-send-string p (concat pw tramp-rsh-end-of-line)) | ||
| 3651 | (tramp-message 9 "Waiting 60s for remote shell to come up...") | ||
| 3652 | (unless (setq found (tramp-wait-for-regexp | ||
| 3653 | p 60 (format "\\(%s\\)\\|\\(%s\\)\\'" | ||
| 3654 | tramp-wrong-passwd-regexp | ||
| 3655 | shell-prompt-pattern))) | ||
| 3656 | (pop-to-buffer (buffer-name)) | ||
| 3657 | (kill-process p) | ||
| 3658 | (error "Couldn't find shell prompt from host %s" host)) | ||
| 3659 | (when (nth 1 found) | ||
| 3660 | (pop-to-buffer (buffer-name)) | ||
| 3661 | (kill-process p) | ||
| 3662 | (error "Login to %s failed: %s" (nth 2 found))))) | ||
| 3663 | |||
| 3664 | ;; HHH: Changed. Multi method. Don't know how to handle this in the case | ||
| 3665 | ;; of no user name provided. Hack to make it work as it did before: | ||
| 3666 | ;; changed `user' to `(or user (user-login-name))' in the places where | ||
| 3667 | ;; the value is actually used. | ||
| 3668 | (defun tramp-multi-connect-rlogin (p method user host command) | ||
| 3669 | "Issue `rlogin' command. | ||
| 3670 | Uses shell COMMAND to issue an `rlogin' command to log in as USER to | ||
| 3671 | HOST. You can use percent escapes in COMMAND. `%u' will be replaced | ||
| 3672 | with the user name, `%h' will be replaced with the host name, and `%n' | ||
| 3673 | will be replaced with the value of `tramp-rsh-end-of-line'. You can use | ||
| 3674 | `%%' if you want to use a literal percent character. | ||
| 3675 | |||
| 3676 | If USER is nil, uses the return value of (user-login-name) instead." | ||
| 3677 | (let ((cmd (format-spec command (list (cons ?h host) | ||
| 3678 | (cons ?u (or user (user-login-name))) | ||
| 3679 | (cons ?n tramp-rsh-end-of-line)))) | ||
| 3680 | (cmd1 (format-spec command (list (cons ?h host) | ||
| 3681 | (cons ?u (or user (user-login-name))) | ||
| 3682 | (cons ?n "")))) | ||
| 3683 | found) | ||
| 3684 | (erase-buffer) | ||
| 3685 | (tramp-message 9 "Sending rlogin command `%s'" cmd1) | ||
| 3686 | (process-send-string p cmd) | ||
| 3687 | (tramp-message 9 "Waiting 60s for shell or passwd prompt from %s" host) | ||
| 3688 | (unless (setq found | ||
| 3689 | (tramp-wait-for-regexp p 60 | ||
| 3690 | (format "\\(%s\\)\\|\\(%s\\)\\'" | ||
| 3691 | tramp-password-prompt-regexp | ||
| 3692 | shell-prompt-pattern))) | ||
| 3693 | (pop-to-buffer (buffer-name)) | ||
| 3694 | (kill-process p) | ||
| 3695 | (error "Couldn't find remote shell or passwd prompt")) | ||
| 3696 | (when (nth 1 found) | ||
| 3697 | (erase-buffer) | ||
| 3698 | (tramp-message 9 "Sending password...") | ||
| 3699 | (tramp-enter-password p (nth 1 found)) | ||
| 3700 | (tramp-message 9 "Sent password, waiting 60s for remote shell prompt") | ||
| 3701 | (setq found (tramp-wait-for-regexp p 60 | ||
| 3702 | (format "\\(%s\\)\\|\\(%s\\)\\'" | ||
| 3703 | tramp-wrong-passwd-regexp | ||
| 3704 | shell-prompt-pattern)))) | ||
| 3705 | (unless found | ||
| 3706 | (pop-to-buffer (buffer-name)) | ||
| 3707 | (kill-process p) | ||
| 3708 | (error "Couldn't find remote shell prompt")) | ||
| 3709 | (when (nth 1 found) | ||
| 3710 | (pop-to-buffer (buffer-name)) | ||
| 3711 | (kill-process p) | ||
| 3712 | (error "Login failed: %s" (nth 1 found))))) | ||
| 3713 | |||
| 3714 | ;; HHH: Changed. Multi method. Don't know how to handle this in the case | ||
| 3715 | ;; of no user name provided. Hack to make it work as it did before: | ||
| 3716 | ;; changed `user' to `(or user (user-login-name))' in the places where | ||
| 3717 | ;; the value is actually used. | ||
| 3718 | (defun tramp-multi-connect-su (p method user host command) | ||
| 3719 | "Issue `su' command. | ||
| 3720 | Uses shell COMMAND to issue a `su' command to log in as USER on | ||
| 3721 | HOST. The HOST name is ignored, this just changes the user id on the | ||
| 3722 | host currently logged in to. | ||
| 3723 | |||
| 3724 | If USER is nil, uses the return value of (user-login-name) instead. | ||
| 3725 | |||
| 3726 | You can use percent escapes in the COMMAND. `%u' is replaced with the | ||
| 3727 | user name, and `%n' is replaced with the value of | ||
| 3728 | `tramp-rsh-end-of-line'. Use `%%' if you want a literal percent | ||
| 3729 | character." | ||
| 3730 | (let ((cmd (format-spec command (list (cons ?u (or user (user-login-name))) | ||
| 3731 | (cons ?n tramp-rsh-end-of-line)))) | ||
| 3732 | (cmd1 (format-spec command (list (cons ?u (or user (user-login-name))) | ||
| 3733 | (cons ?n "")))) | ||
| 3734 | found) | ||
| 3735 | (erase-buffer) | ||
| 3736 | (tramp-message 9 "Sending su command `%s'" cmd1) | ||
| 3737 | (process-send-string p cmd) | ||
| 3738 | (tramp-message 9 "Waiting 60s for shell or passwd prompt for %s" (or user (user-login-name))) | ||
| 3739 | (unless (setq found (tramp-wait-for-regexp | ||
| 3740 | p 60 (format "\\(%s\\)\\|\\(%s\\)\\'" | ||
| 3741 | tramp-password-prompt-regexp | ||
| 3742 | shell-prompt-pattern))) | ||
| 3743 | (pop-to-buffer (buffer-name)) | ||
| 3744 | (kill-process p) | ||
| 3745 | (error "Couldn't find shell or passwd prompt for %s" | ||
| 3746 | (or user (user-login-name)))) | ||
| 3747 | (when (nth 1 found) | ||
| 3748 | (tramp-message 9 "Sending password...") | ||
| 3749 | (tramp-enter-password p (nth 1 found)) | ||
| 3750 | (erase-buffer) | ||
| 3751 | (tramp-message 9 "Sent password, waiting 60s for remote shell prompt") | ||
| 3752 | (setq found (tramp-wait-for-regexp p 60 | ||
| 3753 | (format "\\(%s\\)\\|\\(%s\\)\\'" | ||
| 3754 | tramp-wrong-passwd-regexp | ||
| 3755 | shell-prompt-pattern)))) | ||
| 3756 | (unless found | ||
| 3757 | (pop-to-buffer (buffer-name)) | ||
| 3758 | (kill-process p) | ||
| 3759 | (error "Couldn't find remote shell prompt")) | ||
| 3760 | (when (nth 1 found) | ||
| 3761 | (pop-to-buffer (buffer-name)) | ||
| 3762 | (kill-process p) | ||
| 3763 | (error "Login failed: %s" (nth 1 found))))) | ||
| 3764 | |||
| 3765 | ;; Utility functions. | ||
| 3766 | |||
| 3767 | (defun tramp-wait-for-regexp (proc timeout regexp) | ||
| 3768 | "Wait for a REGEXP to appear from process PROC within TIMEOUT seconds. | ||
| 3769 | Expects the output of PROC to be sent to the current buffer. Returns | ||
| 3770 | the string that matched, or nil. Waits indefinitely if TIMEOUT is | ||
| 3771 | nil." | ||
| 3772 | (let ((found nil) | ||
| 3773 | (start-time (current-time))) | ||
| 3774 | (cond (timeout | ||
| 3775 | ;; Work around a bug in XEmacs 21, where the timeout | ||
| 3776 | ;; expires faster than it should. This degenerates | ||
| 3777 | ;; to polling for buggy XEmacsen, but oh, well. | ||
| 3778 | (while (and (not found) | ||
| 3779 | (< (tramp-time-diff (current-time) start-time) | ||
| 3780 | timeout)) | ||
| 3781 | (with-timeout (timeout) | ||
| 3782 | (while (not found) | ||
| 3783 | (accept-process-output proc 1) | ||
| 3784 | (goto-char (point-min)) | ||
| 3785 | (setq found (when (re-search-forward regexp nil t) | ||
| 3786 | (tramp-match-string-list))))))) | ||
| 3787 | (t | ||
| 3788 | (while (not found) | ||
| 3789 | (accept-process-output proc 1) | ||
| 3790 | (goto-char (point-min)) | ||
| 3791 | (setq found (when (re-search-forward regexp nil t) | ||
| 3792 | (tramp-match-string-list)))))) | ||
| 3793 | (when tramp-debug-buffer | ||
| 3794 | (append-to-buffer | ||
| 3795 | (tramp-get-debug-buffer tramp-current-multi-method tramp-current-method | ||
| 3796 | tramp-current-user tramp-current-host) | ||
| 3797 | (point-min) (point-max)) | ||
| 3798 | (when (not found) | ||
| 3799 | (save-excursion | ||
| 3800 | (set-buffer | ||
| 3801 | (tramp-get-debug-buffer tramp-current-multi-method tramp-current-method | ||
| 3802 | tramp-current-user tramp-current-host)) | ||
| 3803 | (goto-char (point-max)) | ||
| 3804 | (insert "[[Regexp `" regexp "' not found" | ||
| 3805 | (if timeout (concat " in " timeout " secs") "") | ||
| 3806 | "]]")))) | ||
| 3807 | found)) | ||
| 3808 | |||
| 3809 | (defun tramp-enter-password (p prompt) | ||
| 3810 | "Prompt for a password and send it to the remote end. | ||
| 3811 | Uses PROMPT as a prompt and sends the password to process P." | ||
| 3812 | (let ((pw (tramp-read-passwd prompt))) | ||
| 3813 | (process-send-string p (concat pw tramp-rsh-end-of-line)))) | ||
| 3814 | |||
| 3815 | ;; HHH: Not Changed. This might handle the case where USER is not | ||
| 3816 | ;; given in the "File name" very poorly. Then, the local | ||
| 3817 | ;; variable tramp-current user will be set to nil. | ||
| 3818 | (defun tramp-pre-connection (multi-method method user host) | ||
| 3819 | "Do some setup before actually logging in. | ||
| 3820 | METHOD, USER and HOST specify the connection." | ||
| 3821 | (set-buffer (tramp-get-buffer multi-method method user host)) | ||
| 3822 | (set (make-local-variable 'tramp-current-multi-method) multi-method) | ||
| 3823 | (set (make-local-variable 'tramp-current-method) method) | ||
| 3824 | (set (make-local-variable 'tramp-current-user) user) | ||
| 3825 | (set (make-local-variable 'tramp-current-host) host) | ||
| 3826 | (set (make-local-variable 'inhibit-eol-conversion) nil) | ||
| 3827 | (erase-buffer)) | ||
| 3828 | |||
| 3829 | (defun tramp-open-connection-setup-interactive-shell | ||
| 3830 | (p multi-method method user host) | ||
| 3831 | "Set up an interactive shell. | ||
| 3832 | Mainly sets the prompt and the echo correctly. P is the shell process | ||
| 3833 | to set up. METHOD, USER and HOST specify the connection." | ||
| 3834 | ;; Wait a bit in case the remote end feels like sending a little | ||
| 3835 | ;; junk first. It seems that fencepost.gnu.org does this when doing | ||
| 3836 | ;; a Kerberos login. | ||
| 3837 | (sit-for 1) | ||
| 3838 | (tramp-discard-garbage-erase-buffer p multi-method method user host) | ||
| 3839 | (process-send-string nil (format "exec %s%s" | ||
| 3840 | (tramp-get-remote-sh multi-method method) | ||
| 3841 | tramp-rsh-end-of-line)) | ||
| 3842 | (when tramp-debug-buffer | ||
| 3843 | (save-excursion | ||
| 3844 | (set-buffer (tramp-get-debug-buffer multi-method method user host)) | ||
| 3845 | (goto-char (point-max)) | ||
| 3846 | (tramp-insert-with-face | ||
| 3847 | 'bold (format "$ exec %s\n" (tramp-get-remote-sh multi-method method))))) | ||
| 3848 | (tramp-message 9 "Waiting 30s for remote `%s' to come up..." | ||
| 3849 | (tramp-get-remote-sh multi-method method)) | ||
| 3850 | (unless (tramp-wait-for-regexp | ||
| 3851 | p 30 (format "\\(\\$ *\\|%s\\)\\'" shell-prompt-pattern)) | ||
| 3852 | (pop-to-buffer (buffer-name)) | ||
| 3853 | (error "Remote `%s' didn't come up. See buffer `%s' for details" | ||
| 3854 | (tramp-get-remote-sh multi-method method) (buffer-name))) | ||
| 3855 | (tramp-message 9 "Setting up remote shell environment") | ||
| 3856 | (tramp-discard-garbage-erase-buffer p multi-method method user host) | ||
| 3857 | (process-send-string | ||
| 3858 | nil (format "stty -inlcr -echo kill '^U'%s" tramp-rsh-end-of-line)) | ||
| 3859 | (unless (tramp-wait-for-regexp | ||
| 3860 | p 30 (format "\\(\\$ *\\|%s\\)\\'" shell-prompt-pattern)) | ||
| 3861 | (pop-to-buffer (buffer-name)) | ||
| 3862 | (error "Couldn't `stty -echo', see buffer `%s'" (buffer-name))) | ||
| 3863 | (erase-buffer) | ||
| 3864 | (process-send-string nil (format "TERM=dumb; export TERM%s" | ||
| 3865 | tramp-rsh-end-of-line)) | ||
| 3866 | (unless (tramp-wait-for-regexp | ||
| 3867 | p 30 (format "\\(\\$ *\\|%s\\)\\'" shell-prompt-pattern)) | ||
| 3868 | (pop-to-buffer (buffer-name)) | ||
| 3869 | (error "Couldn't `TERM=dumb; export TERM', see buffer `%s'" (buffer-name))) | ||
| 3870 | ;; Try to set up the coding system correctly. | ||
| 3871 | ;; CCC this can't be the right way to do it. Hm. | ||
| 3872 | (save-excursion | ||
| 3873 | (erase-buffer) | ||
| 3874 | (tramp-message 9 "Determining coding system") | ||
| 3875 | (process-send-string nil (format "echo foo ; echo bar %s" | ||
| 3876 | tramp-rsh-end-of-line)) | ||
| 3877 | (unless (tramp-wait-for-regexp | ||
| 3878 | p 30 (format "\\(\\$ *\\|%s\\)\\'" shell-prompt-pattern)) | ||
| 3879 | (pop-to-buffer (buffer-name)) | ||
| 3880 | (error "Couldn't `echo foo; echo bar' to determine line endings'")) | ||
| 3881 | (goto-char (point-min)) | ||
| 3882 | (if (featurep 'mule) | ||
| 3883 | ;; Use MULE to select the right EOL convention for communicating | ||
| 3884 | ;; with the process. | ||
| 3885 | (let* ((cs (or (process-coding-system p) (cons 'undecided 'undecided))) | ||
| 3886 | cs-decode cs-encode) | ||
| 3887 | (when (symbolp cs) (setq cs (cons cs cs))) | ||
| 3888 | (setq cs-decode (car cs)) | ||
| 3889 | (setq cs-encode (cdr cs)) | ||
| 3890 | (unless cs-decode (setq cs-decode 'undecided)) | ||
| 3891 | (unless cs-encode (setq cs-encode 'undecided)) | ||
| 3892 | (setq cs-encode (tramp-coding-system-change-eol-conversion | ||
| 3893 | cs-encode 'unix)) | ||
| 3894 | (when (search-forward "\r" nil t) | ||
| 3895 | (setq cs-decode (tramp-coding-system-change-eol-conversion | ||
| 3896 | cs-decode 'dos))) | ||
| 3897 | (set-buffer-process-coding-system cs-decode cs-encode)) | ||
| 3898 | ;; Look for ^M and do something useful if found. | ||
| 3899 | (when (search-forward "\r" nil t) | ||
| 3900 | ;; We have found a ^M but cannot frob the process coding system | ||
| 3901 | ;; because we're running on a non-MULE Emacs. Let's try | ||
| 3902 | ;; stty, instead. | ||
| 3903 | (tramp-message 9 "Trying `stty -onlcr'") | ||
| 3904 | (process-send-string nil (format "stty -onlcr%s" tramp-rsh-end-of-line)) | ||
| 3905 | (unless (tramp-wait-for-regexp | ||
| 3906 | p 30 (format "\\(\\$ *\\|%s\\)\\'" shell-prompt-pattern)) | ||
| 3907 | (pop-to-buffer (buffer-name)) | ||
| 3908 | (error "Couldn't `stty -onlcr', see buffer `%s'" (buffer-name)))))) | ||
| 3909 | (erase-buffer) | ||
| 3910 | (tramp-message | ||
| 3911 | 9 "Waiting 30s for `HISTFILE=$HOME/.tramp_history; HISTSIZE=1'") | ||
| 3912 | (process-send-string | ||
| 3913 | nil (format "HISTFILE=$HOME/.tramp_history; HISTSIZE=1%s" | ||
| 3914 | tramp-rsh-end-of-line)) | ||
| 3915 | (unless (tramp-wait-for-regexp | ||
| 3916 | p 30 (format "\\(\\$ *\\|%s\\)\\'" shell-prompt-pattern)) | ||
| 3917 | (pop-to-buffer (buffer-name)) | ||
| 3918 | (error (concat "Couldn't `HISTFILE=$HOME/.tramp_history; " | ||
| 3919 | "HISTSIZE=1', see buffer `%s'") | ||
| 3920 | (buffer-name))) | ||
| 3921 | (erase-buffer) | ||
| 3922 | (tramp-message 9 "Waiting 30s for `set +o vi +o emacs'") | ||
| 3923 | (process-send-string | ||
| 3924 | nil (format "set +o vi +o emacs%s" ;mustn't `>/dev/null' with AIX? | ||
| 3925 | tramp-rsh-end-of-line)) | ||
| 3926 | (unless (tramp-wait-for-regexp | ||
| 3927 | p 30 (format "\\(\\$ *\\|%s\\)\\'" shell-prompt-pattern)) | ||
| 3928 | (pop-to-buffer (buffer-name)) | ||
| 3929 | (error "Couldn't `set +o vi +o emacs', see buffer `%s'" | ||
| 3930 | (buffer-name))) | ||
| 3931 | (erase-buffer) | ||
| 3932 | (tramp-message 9 "Waiting 30s for `unset MAIL MAILCHECK MAILPATH'") | ||
| 3933 | (process-send-string | ||
| 3934 | nil (format "unset MAIL MAILCHECK MAILPATH 1>/dev/null 2>/dev/null%s" | ||
| 3935 | tramp-rsh-end-of-line)) | ||
| 3936 | (unless (tramp-wait-for-regexp | ||
| 3937 | p 30 (format "\\(\\$ *\\|%s\\)\\'" shell-prompt-pattern)) | ||
| 3938 | (pop-to-buffer (buffer-name)) | ||
| 3939 | (error "Couldn't `unset MAIL MAILCHECK MAILPATH', see buffer `%s'" | ||
| 3940 | (buffer-name))) | ||
| 3941 | (erase-buffer) | ||
| 3942 | (tramp-message 9 "Waiting 30s for `unset CDPATH'") | ||
| 3943 | (process-send-string | ||
| 3944 | nil (format "unset CDPATH%s" tramp-rsh-end-of-line)) | ||
| 3945 | (unless (tramp-wait-for-regexp | ||
| 3946 | p 30 (format "\\(\\$ *\\|%s\\)\\'" shell-prompt-pattern)) | ||
| 3947 | (pop-to-buffer (buffer-name)) | ||
| 3948 | (error "Couldn't `unset CDPATH', see buffer `%s'" | ||
| 3949 | (buffer-name))) | ||
| 3950 | (erase-buffer) | ||
| 3951 | (tramp-message 9 "Setting shell prompt") | ||
| 3952 | (tramp-send-command | ||
| 3953 | multi-method method user host | ||
| 3954 | (format "PS1='%s%s%s'; PS2=''; PS3=''" | ||
| 3955 | tramp-rsh-end-of-line | ||
| 3956 | tramp-end-of-output | ||
| 3957 | tramp-rsh-end-of-line)) | ||
| 3958 | (tramp-wait-for-output) | ||
| 3959 | (tramp-send-command multi-method method user host "echo hello") | ||
| 3960 | (tramp-message 9 "Waiting for remote `%s' to come up..." | ||
| 3961 | (tramp-get-remote-sh multi-method method)) | ||
| 3962 | (unless (tramp-wait-for-output 5) | ||
| 3963 | (unless (tramp-wait-for-output 5) | ||
| 3964 | (pop-to-buffer (buffer-name)) | ||
| 3965 | (error "Couldn't set remote shell prompt. See buffer `%s' for details" | ||
| 3966 | (buffer-name)))) | ||
| 3967 | (tramp-message 7 "Waiting for remote `%s' to come up...done" | ||
| 3968 | (tramp-get-remote-sh multi-method method))) | ||
| 3969 | |||
| 3970 | (defun tramp-post-connection (multi-method method user host) | ||
| 3971 | "Prepare a remote shell before being able to work on it. | ||
| 3972 | METHOD, USER and HOST specify the connection. | ||
| 3973 | Among other things, this finds a shell which groks tilde expansion, | ||
| 3974 | tries to find an `ls' command which groks the `-n' option, sets the | ||
| 3975 | locale to C and sets up the remote shell search path." | ||
| 3976 | ;; Search for a good shell before searching for a command which | ||
| 3977 | ;; checks if a file exists. This is done because Tramp wants to use | ||
| 3978 | ;; "test foo; echo $?" to check if various conditions hold, and | ||
| 3979 | ;; there are buggy /bin/sh implementations which don't execute the | ||
| 3980 | ;; "echo $?" part if the "test" part has an error. In particular, | ||
| 3981 | ;; the Solaris /bin/sh is a problem. I'm betting that all systems | ||
| 3982 | ;; with buggy /bin/sh implementations will have a working bash or | ||
| 3983 | ;; ksh. Whee... | ||
| 3984 | (tramp-find-shell multi-method method user host) | ||
| 3985 | (tramp-find-file-exists-command multi-method method user host) | ||
| 3986 | (sit-for 1) | ||
| 3987 | ;; Without (sit-for 0.1) at least, my machine will almost always blow | ||
| 3988 | ;; up on 'not numberp /root' - a race that causes the 'echo ~root' | ||
| 3989 | ;; output of (tramp-find-shell) to show up along with the output of | ||
| 3990 | ;; (tramp-find-ls-command) testing. | ||
| 3991 | ;; | ||
| 3992 | ;; I can't work out why this is a problem though. The (tramp-wait-for-output) | ||
| 3993 | ;; call in (tramp-find-shell) *should* make this not happen, I thought. | ||
| 3994 | ;; | ||
| 3995 | ;; After much debugging I couldn't find any problem with the implementation | ||
| 3996 | ;; of that function though. The workaround stays for me at least. :/ | ||
| 3997 | ;; | ||
| 3998 | ;; Daniel Pittman <daniel@danann.net> | ||
| 3999 | (make-local-variable 'tramp-ls-command) | ||
| 4000 | (setq tramp-ls-command (tramp-find-ls-command multi-method method user host)) | ||
| 4001 | (unless tramp-ls-command | ||
| 4002 | (tramp-message | ||
| 4003 | 1 | ||
| 4004 | "Danger! Couldn't find ls which groks -n. Muddling through anyway") | ||
| 4005 | (setq tramp-ls-command | ||
| 4006 | (tramp-find-executable multi-method method user host | ||
| 4007 | "ls" tramp-remote-path nil))) | ||
| 4008 | (unless tramp-ls-command | ||
| 4009 | (error "Fatal error: Couldn't find remote executable `ls'")) | ||
| 4010 | (tramp-message 5 "Using remote command `%s' for getting directory listings" | ||
| 4011 | tramp-ls-command) | ||
| 4012 | (tramp-send-command multi-method method user host | ||
| 4013 | (concat "tramp_set_exit_status () {" tramp-rsh-end-of-line | ||
| 4014 | "return $1" tramp-rsh-end-of-line | ||
| 4015 | "}")) | ||
| 4016 | (tramp-wait-for-output) | ||
| 4017 | ;; Set remote PATH variable. | ||
| 4018 | (tramp-set-remote-path multi-method method user host "PATH" tramp-remote-path) | ||
| 4019 | ;; Tell remote shell to use standard time format, needed for | ||
| 4020 | ;; parsing `ls -l' output. | ||
| 4021 | (tramp-send-command multi-method method user host | ||
| 4022 | "LC_TIME=C; export LC_TIME; echo huhu") | ||
| 4023 | (tramp-wait-for-output) | ||
| 4024 | (tramp-send-command multi-method method user host | ||
| 4025 | "mesg n; echo huhu") | ||
| 4026 | (tramp-wait-for-output) | ||
| 4027 | (tramp-send-command multi-method method user host | ||
| 4028 | "biff n ; echo huhu") | ||
| 4029 | (tramp-wait-for-output) | ||
| 4030 | ;; Unalias ls(1) to work around issues with those silly people who make it | ||
| 4031 | ;; spit out ANSI escapes or whatever. | ||
| 4032 | (tramp-send-command multi-method method user host | ||
| 4033 | "unalias ls; echo huhu") | ||
| 4034 | (tramp-wait-for-output) | ||
| 4035 | ;; Does `test A -nt B' work? Use abominable `find' construct if it | ||
| 4036 | ;; doesn't. BSD/OS 4.0 wants the parentheses around the command, | ||
| 4037 | ;; for otherwise the shell crashes. | ||
| 4038 | (erase-buffer) | ||
| 4039 | (make-local-variable 'tramp-test-groks-nt) | ||
| 4040 | (tramp-send-command multi-method method user host | ||
| 4041 | "( test / -nt / )") | ||
| 4042 | (tramp-wait-for-output) | ||
| 4043 | (goto-char (point-min)) | ||
| 4044 | (setq tramp-test-groks-nt | ||
| 4045 | (looking-at (format "\n%s\n" (regexp-quote tramp-end-of-output)))) | ||
| 4046 | (unless tramp-test-groks-nt | ||
| 4047 | (tramp-send-command | ||
| 4048 | multi-method method user host | ||
| 4049 | (concat "tramp_test_nt () {" tramp-rsh-end-of-line | ||
| 4050 | "test -n \"`find $1 -prune -newer $2 -print`\"" tramp-rsh-end-of-line | ||
| 4051 | "}"))) | ||
| 4052 | (tramp-wait-for-output) | ||
| 4053 | ;; Find a `perl'. | ||
| 4054 | (erase-buffer) | ||
| 4055 | (let ((tramp-remote-perl | ||
| 4056 | (or (tramp-find-executable multi-method method user host | ||
| 4057 | "perl5" tramp-remote-path nil) | ||
| 4058 | (tramp-find-executable multi-method method user host | ||
| 4059 | "perl" tramp-remote-path nil)))) | ||
| 4060 | (when tramp-remote-perl | ||
| 4061 | (tramp-set-connection-property "perl" tramp-remote-perl multi-method method user host) | ||
| 4062 | ;; Set up stat in Perl if we can. | ||
| 4063 | (when tramp-remote-perl | ||
| 4064 | (tramp-message 5 "Sending the Perl `file-attributes' implementation.") | ||
| 4065 | (tramp-send-linewise | ||
| 4066 | multi-method method user host | ||
| 4067 | (concat "tramp_file_attributes () {\n" | ||
| 4068 | tramp-remote-perl | ||
| 4069 | " -e '" tramp-perl-file-attributes "' $1 2>/dev/null\n" | ||
| 4070 | "}")) | ||
| 4071 | (tramp-wait-for-output) | ||
| 4072 | (when (string= (tramp-get-encoding-command multi-method method) | ||
| 4073 | "tramp_mimencode") | ||
| 4074 | (tramp-message 5 "Sending the Perl `mime-encode' implementation.") | ||
| 4075 | (tramp-send-linewise | ||
| 4076 | multi-method method user host | ||
| 4077 | (concat "tramp_mimencode () {\n" | ||
| 4078 | (if (tramp-find-executable multi-method method user host | ||
| 4079 | "mimencode" tramp-remote-path t) | ||
| 4080 | "mimencode -b $1" | ||
| 4081 | (concat tramp-remote-perl | ||
| 4082 | " -e '" tramp-perl-mime-encode "' $1 2>/dev/null")) | ||
| 4083 | "\n}")) | ||
| 4084 | (tramp-wait-for-output)) | ||
| 4085 | (when (string= (tramp-get-decoding-command multi-method method) | ||
| 4086 | "tramp_mimedecode") | ||
| 4087 | (tramp-message 5 "Sending the Perl `mime-decode' implementation.") | ||
| 4088 | (tramp-send-linewise | ||
| 4089 | multi-method method user host | ||
| 4090 | (concat "tramp_mimedecode () {\n" | ||
| 4091 | (if (tramp-find-executable multi-method method user host | ||
| 4092 | "mimencode" tramp-remote-path t) | ||
| 4093 | "mimencode -u -b $1" | ||
| 4094 | (concat tramp-remote-perl | ||
| 4095 | " -e '" tramp-perl-mime-decode "' $1 2>/dev/null")) | ||
| 4096 | "\n}")) | ||
| 4097 | (tramp-wait-for-output))))) | ||
| 4098 | ;; Find ln(1) | ||
| 4099 | (erase-buffer) | ||
| 4100 | (let ((ln (tramp-find-executable multi-method method user host | ||
| 4101 | "ln" tramp-remote-path nil))) | ||
| 4102 | (when ln | ||
| 4103 | (tramp-set-connection-property "ln" ln multi-method method user host))) | ||
| 4104 | (erase-buffer) | ||
| 4105 | ;; If encoding/decoding command are given, test to see if they work. | ||
| 4106 | ;; CCC: Maybe it would be useful to run the encoder both locally and | ||
| 4107 | ;; remotely to see if they produce the same result. | ||
| 4108 | (let ((decoding (tramp-get-decoding-command multi-method method)) | ||
| 4109 | (encoding (tramp-get-encoding-command multi-method method)) | ||
| 4110 | (magic-string "xyzzy")) | ||
| 4111 | (when (and (or decoding encoding) (not (and decoding encoding))) | ||
| 4112 | (tramp-kill-process multi-method method user host) | ||
| 4113 | (error | ||
| 4114 | "Must give both decoding and encoding command in method definition")) | ||
| 4115 | (when (and decoding encoding) | ||
| 4116 | (tramp-message | ||
| 4117 | 5 | ||
| 4118 | "Checking to see if encoding/decoding commands work on remote host...") | ||
| 4119 | (tramp-send-command | ||
| 4120 | multi-method method user host | ||
| 4121 | (format "echo %s | %s | %s" | ||
| 4122 | (tramp-shell-quote-argument magic-string) encoding decoding)) | ||
| 4123 | (tramp-wait-for-output) | ||
| 4124 | (unless (looking-at (regexp-quote magic-string)) | ||
| 4125 | (tramp-kill-process multi-method method user host) | ||
| 4126 | (error "Remote host cannot execute de/encoding commands. See buffer `%s' for details" | ||
| 4127 | (buffer-name))) | ||
| 4128 | (erase-buffer) | ||
| 4129 | (tramp-message | ||
| 4130 | 5 "Checking to see if encoding/decoding commands work on remote host...done")))) | ||
| 4131 | |||
| 4132 | |||
| 4133 | (defun tramp-maybe-open-connection (multi-method method user host) | ||
| 4134 | "Maybe open a connection to HOST, logging in as USER, using METHOD. | ||
| 4135 | Does not do anything if a connection is already open, but re-opens the | ||
| 4136 | connection if a previous connection has died for some reason." | ||
| 4137 | (let ((p (get-buffer-process (tramp-get-buffer multi-method method user host)))) | ||
| 4138 | (unless (and p | ||
| 4139 | (processp p) | ||
| 4140 | (memq (process-status p) '(run open))) | ||
| 4141 | (when (and p (processp p)) | ||
| 4142 | (delete-process p)) | ||
| 4143 | (funcall (tramp-get-connection-function multi-method method) | ||
| 4144 | multi-method method user host)))) | ||
| 4145 | |||
| 4146 | (defun tramp-send-command | ||
| 4147 | (multi-method method user host command &optional noerase) | ||
| 4148 | "Send the COMMAND to USER at HOST (logged in using METHOD). | ||
| 4149 | Erases temporary buffer before sending the command (unless NOERASE | ||
| 4150 | is true)." | ||
| 4151 | (tramp-maybe-open-connection multi-method method user host) | ||
| 4152 | (when tramp-debug-buffer | ||
| 4153 | (save-excursion | ||
| 4154 | (set-buffer (tramp-get-debug-buffer multi-method method user host)) | ||
| 4155 | (goto-char (point-max)) | ||
| 4156 | (tramp-insert-with-face 'bold (format "$ %s\n" command)))) | ||
| 4157 | (let ((proc nil)) | ||
| 4158 | (set-buffer (tramp-get-buffer multi-method method user host)) | ||
| 4159 | (unless noerase (erase-buffer)) | ||
| 4160 | (setq proc (get-buffer-process (current-buffer))) | ||
| 4161 | (process-send-string proc | ||
| 4162 | (concat command tramp-rsh-end-of-line)))) | ||
| 4163 | |||
| 4164 | ;; It seems that Tru64 Unix does not like it if long strings are sent | ||
| 4165 | ;; to it in one go. (This happens when sending the Perl | ||
| 4166 | ;; `file-attributes' implementation, for instance.) Therefore, we | ||
| 4167 | ;; have this function which waits a bit at each line. | ||
| 4168 | (defun tramp-send-linewise | ||
| 4169 | (multi-method method user host string &optional noerase) | ||
| 4170 | "Send the STRING to USER at HOST linewise. | ||
| 4171 | Erases temporary buffer before sending the STRING (unless NOERASE | ||
| 4172 | is true). | ||
| 4173 | |||
| 4174 | The STRING is expected to use Unix line-endings, but the lines sent to | ||
| 4175 | the remote host use line-endings as defined in the variable | ||
| 4176 | `tramp-rsh-end-of-line'." | ||
| 4177 | (tramp-maybe-open-connection multi-method method user host) | ||
| 4178 | (when tramp-debug-buffer | ||
| 4179 | (save-excursion | ||
| 4180 | (set-buffer (tramp-get-debug-buffer multi-method method user host)) | ||
| 4181 | (goto-char (point-max)) | ||
| 4182 | (tramp-insert-with-face 'bold (format "$ %s\n" string)))) | ||
| 4183 | (let ((proc nil) | ||
| 4184 | (lines (split-string string "\n"))) | ||
| 4185 | (set-buffer (tramp-get-buffer multi-method method user host)) | ||
| 4186 | (unless noerase (erase-buffer)) | ||
| 4187 | (setq proc (get-buffer-process (current-buffer))) | ||
| 4188 | (mapcar (lambda (x) | ||
| 4189 | (sleep-for 0.1) | ||
| 4190 | (process-send-string proc | ||
| 4191 | (concat x tramp-rsh-end-of-line))) | ||
| 4192 | lines))) | ||
| 4193 | |||
| 4194 | (defun tramp-wait-for-output (&optional timeout) | ||
| 4195 | "Wait for output from remote rsh command." | ||
| 4196 | (let ((proc (get-buffer-process (current-buffer))) | ||
| 4197 | (found nil) | ||
| 4198 | (start-time (current-time)) | ||
| 4199 | (end-of-output (concat "^" | ||
| 4200 | (regexp-quote tramp-end-of-output) | ||
| 4201 | "$"))) | ||
| 4202 | ;; Algorithm: get waiting output. See if last line contains | ||
| 4203 | ;; end-of-output sentinel. If not, wait a bit and again get | ||
| 4204 | ;; waiting output. Repeat until timeout expires or end-of-output | ||
| 4205 | ;; sentinel is seen. Will hang if timeout is nil and | ||
| 4206 | ;; end-of-output sentinel never appears. | ||
| 4207 | (save-match-data | ||
| 4208 | (cond (timeout | ||
| 4209 | ;; Work around an XEmacs bug, where the timeout expires | ||
| 4210 | ;; faster than it should. This degenerates into polling | ||
| 4211 | ;; for buggy XEmacsen, but oh, well. | ||
| 4212 | (while (and (not found) | ||
| 4213 | (< (tramp-time-diff (current-time) start-time) | ||
| 4214 | timeout)) | ||
| 4215 | (with-timeout (timeout) | ||
| 4216 | (while (not found) | ||
| 4217 | (accept-process-output proc 1) | ||
| 4218 | (goto-char (point-max)) | ||
| 4219 | (forward-line -1) | ||
| 4220 | (setq found (looking-at end-of-output)))))) | ||
| 4221 | (t | ||
| 4222 | (while (not found) | ||
| 4223 | (accept-process-output proc 1) | ||
| 4224 | (goto-char (point-max)) | ||
| 4225 | (forward-line -1) | ||
| 4226 | (setq found (looking-at end-of-output)))))) | ||
| 4227 | ;; At this point, either the timeout has expired or we have found | ||
| 4228 | ;; the end-of-output sentinel. | ||
| 4229 | (when found | ||
| 4230 | (goto-char (point-max)) | ||
| 4231 | (forward-line -2) | ||
| 4232 | (delete-region (point) (point-max))) | ||
| 4233 | ;; Add output to debug buffer if appropriate. | ||
| 4234 | (when tramp-debug-buffer | ||
| 4235 | (append-to-buffer | ||
| 4236 | (tramp-get-debug-buffer tramp-current-multi-method tramp-current-method | ||
| 4237 | tramp-current-user tramp-current-host) | ||
| 4238 | (point-min) (point-max)) | ||
| 4239 | (when (not found) | ||
| 4240 | (save-excursion | ||
| 4241 | (set-buffer | ||
| 4242 | (tramp-get-debug-buffer tramp-current-multi-method tramp-current-method | ||
| 4243 | tramp-current-user tramp-current-host)) | ||
| 4244 | (goto-char (point-max)) | ||
| 4245 | (insert "[[Remote prompt `" end-of-output "' not found" | ||
| 4246 | (if timeout (concat " in " timeout " secs") "") | ||
| 4247 | "]]")))) | ||
| 4248 | (goto-char (point-min)) | ||
| 4249 | ;; Return value is whether end-of-output sentinel was found. | ||
| 4250 | found)) | ||
| 4251 | |||
| 4252 | (defun tramp-match-string-list (&optional string) | ||
| 4253 | "Returns list of all match strings. | ||
| 4254 | That is, (list (match-string 0) (match-string 1) ...), according to the | ||
| 4255 | number of matches." | ||
| 4256 | (let* ((nmatches (/ (length (match-data)) 2)) | ||
| 4257 | (i (- nmatches 1)) | ||
| 4258 | (res nil)) | ||
| 4259 | (while (>= i 0) | ||
| 4260 | (setq res (cons (match-string i string) res)) | ||
| 4261 | (setq i (- i 1))) | ||
| 4262 | res)) | ||
| 4263 | |||
| 4264 | (defun tramp-send-command-and-check (multi-method method user host command | ||
| 4265 | &optional subshell) | ||
| 4266 | "Run COMMAND and check its exit status. | ||
| 4267 | MULTI-METHOD and METHOD specify how to log in (as USER) to the remote HOST. | ||
| 4268 | Sends `echo $?' along with the COMMAND for checking the exit status. If | ||
| 4269 | COMMAND is nil, just sends `echo $?'. Returns the exit status found. | ||
| 4270 | |||
| 4271 | If the optional argument SUBSHELL is non-nil, the command is executed in | ||
| 4272 | a subshell, ie surrounded by parentheses." | ||
| 4273 | (tramp-send-command multi-method method user host | ||
| 4274 | (concat (if subshell "( " "") | ||
| 4275 | command | ||
| 4276 | (if command " 2>/dev/null; " "") | ||
| 4277 | "echo tramp_exit_status $?" | ||
| 4278 | (if subshell " )" " "))) | ||
| 4279 | (tramp-wait-for-output) | ||
| 4280 | (goto-char (point-max)) | ||
| 4281 | (unless (search-backward "tramp_exit_status " nil t) | ||
| 4282 | (error "Couldn't find exit status of `%s'" command)) | ||
| 4283 | (skip-chars-forward "^ ") | ||
| 4284 | (read (current-buffer))) | ||
| 4285 | |||
| 4286 | (defun tramp-barf-unless-okay (multi-method method user host command subshell | ||
| 4287 | signal fmt &rest args) | ||
| 4288 | "Run COMMAND, check exit status, throw error if exit status not okay. | ||
| 4289 | Similar to `tramp-send-command-and-check' but accepts two more arguments | ||
| 4290 | FMT and ARGS which are passed to `error'." | ||
| 4291 | (unless (zerop (tramp-send-command-and-check | ||
| 4292 | multi-method method user host command subshell)) | ||
| 4293 | ;; CCC: really pop-to-buffer? Maybe it's appropriate to be more | ||
| 4294 | ;; silent. | ||
| 4295 | (pop-to-buffer (current-buffer)) | ||
| 4296 | (funcall 'signal signal (apply 'format fmt args)))) | ||
| 4297 | |||
| 4298 | (defun tramp-send-region (multi-method method user host start end) | ||
| 4299 | "Send the region from START to END to remote command | ||
| 4300 | running as USER on HOST using METHOD." | ||
| 4301 | (let ((proc (get-buffer-process | ||
| 4302 | (tramp-get-buffer multi-method method user host)))) | ||
| 4303 | (unless proc | ||
| 4304 | (error "Can't send region to remote host -- not logged in")) | ||
| 4305 | (process-send-region proc start end) | ||
| 4306 | (when tramp-debug-buffer | ||
| 4307 | (append-to-buffer | ||
| 4308 | (tramp-get-debug-buffer multi-method method user host) | ||
| 4309 | start end)))) | ||
| 4310 | |||
| 4311 | (defun tramp-send-eof (multi-method method user host) | ||
| 4312 | "Send EOF to the remote end. | ||
| 4313 | METHOD, HOST and USER specify the the connection." | ||
| 4314 | (let ((proc (get-buffer-process | ||
| 4315 | (tramp-get-buffer multi-method method user host)))) | ||
| 4316 | (unless proc | ||
| 4317 | (error "Can't send EOF to remote host -- not logged in")) | ||
| 4318 | (process-send-eof proc))) | ||
| 4319 | ; (process-send-string proc "\^D"))) | ||
| 4320 | |||
| 4321 | (defun tramp-kill-process (multi-method method user host) | ||
| 4322 | "Kill the connection process used by Tramp. | ||
| 4323 | MULTI-METHOD, METHOD, USER, and HOST, specify the connection." | ||
| 4324 | (let ((proc (get-buffer-process | ||
| 4325 | (tramp-get-buffer multi-method method user host)))) | ||
| 4326 | (kill-process proc))) | ||
| 4327 | |||
| 4328 | (defun tramp-discard-garbage-erase-buffer (p multi-method method user host) | ||
| 4329 | "Erase buffer, then discard subsequent garbage. | ||
| 4330 | If `tramp-discard-garbage' is nil, just erase buffer." | ||
| 4331 | (if (not tramp-discard-garbage) | ||
| 4332 | (erase-buffer) | ||
| 4333 | (while (prog1 (erase-buffer) (accept-process-output p 0.25)) | ||
| 4334 | (when tramp-debug-buffer | ||
| 4335 | (save-excursion | ||
| 4336 | (set-buffer (tramp-get-debug-buffer multi-method method user host)) | ||
| 4337 | (goto-char (point-max)) | ||
| 4338 | (tramp-insert-with-face | ||
| 4339 | 'bold (format "Additional characters detected\n"))))))) | ||
| 4340 | |||
| 4341 | (defun tramp-mode-string-to-int (mode-string) | ||
| 4342 | "Converts a ten-letter `drwxrwxrwx'-style mode string into mode bits." | ||
| 4343 | (let* ((mode-chars (string-to-vector mode-string)) | ||
| 4344 | (owner-read (aref mode-chars 1)) | ||
| 4345 | (owner-write (aref mode-chars 2)) | ||
| 4346 | (owner-execute-or-setid (aref mode-chars 3)) | ||
| 4347 | (group-read (aref mode-chars 4)) | ||
| 4348 | (group-write (aref mode-chars 5)) | ||
| 4349 | (group-execute-or-setid (aref mode-chars 6)) | ||
| 4350 | (other-read (aref mode-chars 7)) | ||
| 4351 | (other-write (aref mode-chars 8)) | ||
| 4352 | (other-execute-or-sticky (aref mode-chars 9))) | ||
| 4353 | (save-match-data | ||
| 4354 | (logior | ||
| 4355 | (case owner-read | ||
| 4356 | (?r (tramp-octal-to-decimal "00400")) (?- 0) | ||
| 4357 | (t (error "Second char `%c' must be one of `r-'" owner-read))) | ||
| 4358 | (case owner-write | ||
| 4359 | (?w (tramp-octal-to-decimal "00200")) (?- 0) | ||
| 4360 | (t (error "Third char `%c' must be one of `w-'" owner-write))) | ||
| 4361 | (case owner-execute-or-setid | ||
| 4362 | (?x (tramp-octal-to-decimal "00100")) | ||
| 4363 | (?S (tramp-octal-to-decimal "04000")) | ||
| 4364 | (?s (tramp-octal-to-decimal "04100")) | ||
| 4365 | (?- 0) | ||
| 4366 | (t (error "Fourth char `%c' must be one of `xsS-'" | ||
| 4367 | owner-execute-or-setid))) | ||
| 4368 | (case group-read | ||
| 4369 | (?r (tramp-octal-to-decimal "00040")) (?- 0) | ||
| 4370 | (t (error "Fifth char `%c' must be one of `r-'" group-read))) | ||
| 4371 | (case group-write | ||
| 4372 | (?w (tramp-octal-to-decimal "00020")) (?- 0) | ||
| 4373 | (t (error "Sixth char `%c' must be one of `w-'" group-write))) | ||
| 4374 | (case group-execute-or-setid | ||
| 4375 | (?x (tramp-octal-to-decimal "00010")) | ||
| 4376 | (?S (tramp-octal-to-decimal "02000")) | ||
| 4377 | (?s (tramp-octal-to-decimal "02010")) | ||
| 4378 | (?- 0) | ||
| 4379 | (t (error "Seventh char `%c' must be one of `xsS-'" | ||
| 4380 | group-execute-or-setid))) | ||
| 4381 | (case other-read | ||
| 4382 | (?r (tramp-octal-to-decimal "00004")) (?- 0) | ||
| 4383 | (t (error "Eighth char `%c' must be one of `r-'" other-read))) | ||
| 4384 | (case other-write | ||
| 4385 | (?w (tramp-octal-to-decimal "00002")) (?- 0) | ||
| 4386 | (t (error "Nineth char `%c' must be one of `w-'" other-write))) | ||
| 4387 | (case other-execute-or-sticky | ||
| 4388 | (?x (tramp-octal-to-decimal "00001")) | ||
| 4389 | (?T (tramp-octal-to-decimal "01000")) | ||
| 4390 | (?t (tramp-octal-to-decimal "01001")) | ||
| 4391 | (?- 0) | ||
| 4392 | (t (error "Tenth char `%c' must be one of `xtT-'" | ||
| 4393 | other-execute-or-sticky))))))) | ||
| 4394 | |||
| 4395 | |||
| 4396 | (defun tramp-file-mode-from-int (mode) | ||
| 4397 | "Turn an integer representing a file mode into an ls(1)-like string." | ||
| 4398 | (let ((type (cdr (assoc (logand (lsh mode -12) 15) tramp-file-mode-type-map))) | ||
| 4399 | (user (logand (lsh mode -6) 7)) | ||
| 4400 | (group (logand (lsh mode -3) 7)) | ||
| 4401 | (other (logand (lsh mode -0) 7)) | ||
| 4402 | (suid (> (logand (lsh mode -9) 4) 0)) | ||
| 4403 | (sgid (> (logand (lsh mode -9) 2) 0)) | ||
| 4404 | (sticky (> (logand (lsh mode -9) 1) 0))) | ||
| 4405 | (setq user (tramp-file-mode-permissions user suid "s")) | ||
| 4406 | (setq group (tramp-file-mode-permissions group sgid "s")) | ||
| 4407 | (setq other (tramp-file-mode-permissions other sticky "t")) | ||
| 4408 | (concat type user group other))) | ||
| 4409 | |||
| 4410 | |||
| 4411 | (defun tramp-file-mode-permissions (perm suid suid-text) | ||
| 4412 | "Convert a permission bitset into a string. | ||
| 4413 | This is used internally by `tramp-file-mode-from-int'." | ||
| 4414 | (let ((r (> (logand perm 4) 0)) | ||
| 4415 | (w (> (logand perm 2) 0)) | ||
| 4416 | (x (> (logand perm 1) 0))) | ||
| 4417 | (concat (or (and r "r") "-") | ||
| 4418 | (or (and w "w") "-") | ||
| 4419 | (or (and suid x suid-text) ; suid, execute | ||
| 4420 | (and suid (upcase suid-text)) ; suid, !execute | ||
| 4421 | (and x "x") "-")))) ; !suid | ||
| 4422 | |||
| 4423 | |||
| 4424 | (defun tramp-decimal-to-octal (i) | ||
| 4425 | "Return a string consisting of the octal digits of I. | ||
| 4426 | Not actually used. Use `(format \"%o\" i)' instead?" | ||
| 4427 | (cond ((< i 0) (error "Cannot convert negative number to octal")) | ||
| 4428 | ((not (integerp i)) (error "Cannot convert non-integer to octal")) | ||
| 4429 | ((zerop i) "0") | ||
| 4430 | (t (concat (tramp-decimal-to-octal (/ i 8)) | ||
| 4431 | (number-to-string (% i 8)))))) | ||
| 4432 | |||
| 4433 | |||
| 4434 | ;;(defun tramp-octal-to-decimal (ostr) | ||
| 4435 | ;; "Given a string of octal digits, return a decimal number." | ||
| 4436 | ;; (cond ((null ostr) 0) | ||
| 4437 | ;; ((string= "" ostr) 0) | ||
| 4438 | ;; (t (let ((last (aref ostr (1- (length ostr)))) | ||
| 4439 | ;; (rest (substring ostr 0 (1- (length ostr))))) | ||
| 4440 | ;; (unless (and (>= last ?0) | ||
| 4441 | ;; (<= last ?7)) | ||
| 4442 | ;; (error "Not an octal digit: %c" last)) | ||
| 4443 | ;; (+ (- last ?0) (* 8 (tramp-octal-to-decimal rest))))))) | ||
| 4444 | ;; Kudos to Gerd Moellmann for this suggestion. | ||
| 4445 | (defun tramp-octal-to-decimal (ostr) | ||
| 4446 | "Given a string of octal digits, return a decimal number." | ||
| 4447 | (let ((x (or ostr ""))) | ||
| 4448 | ;; `save-match' is in `tramp-mode-string-to-int' which calls this. | ||
| 4449 | (unless (string-match "\\`[0-7]*\\'" x) | ||
| 4450 | (error "Non-octal junk in string `%s'" x)) | ||
| 4451 | (string-to-number ostr 8))) | ||
| 4452 | |||
| 4453 | (defun tramp-shell-case-fold (string) | ||
| 4454 | "Converts STRING to shell glob pattern which ignores case." | ||
| 4455 | (mapconcat | ||
| 4456 | (lambda (c) | ||
| 4457 | (if (equal (downcase c) (upcase c)) | ||
| 4458 | (vector c) | ||
| 4459 | (format "[%c%c]" (downcase c) (upcase c)))) | ||
| 4460 | string | ||
| 4461 | "")) | ||
| 4462 | |||
| 4463 | |||
| 4464 | ;; ------------------------------------------------------------ | ||
| 4465 | ;; -- TRAMP file names -- | ||
| 4466 | ;; ------------------------------------------------------------ | ||
| 4467 | ;; Conversion functions between external representation and | ||
| 4468 | ;; internal data structure. Convenience functions for internal | ||
| 4469 | ;; data structure. | ||
| 4470 | |||
| 4471 | (defstruct tramp-file-name multi-method method user host path) | ||
| 4472 | |||
| 4473 | (defun tramp-tramp-file-p (name) | ||
| 4474 | "Return t iff NAME is a tramp file." | ||
| 4475 | (save-match-data | ||
| 4476 | (string-match tramp-file-name-regexp name))) | ||
| 4477 | |||
| 4478 | ;; HHH: Changed. Used to assign the return value of (user-login-name) | ||
| 4479 | ;; to the `user' part of the structure if a user name was not | ||
| 4480 | ;; provided, now it assigns nil. | ||
| 4481 | (defun tramp-dissect-file-name (name) | ||
| 4482 | "Return an `tramp-file-name' structure. | ||
| 4483 | The structure consists of remote method, remote user, remote host and | ||
| 4484 | remote path name." | ||
| 4485 | (let (method) | ||
| 4486 | (save-match-data | ||
| 4487 | (unless (string-match (nth 0 tramp-file-name-structure) name) | ||
| 4488 | (error "Not a tramp file name: %s" name)) | ||
| 4489 | (setq method (or (match-string (nth 1 tramp-file-name-structure) name) | ||
| 4490 | tramp-default-method)) | ||
| 4491 | (if (member method tramp-multi-methods) | ||
| 4492 | ;; If it's a multi method, the file name structure contains | ||
| 4493 | ;; arrays of method, user and host. | ||
| 4494 | (tramp-dissect-multi-file-name name) | ||
| 4495 | ;; Normal method. | ||
| 4496 | (make-tramp-file-name | ||
| 4497 | :multi-method nil | ||
| 4498 | :method method | ||
| 4499 | :user (or (match-string (nth 2 tramp-file-name-structure) name) | ||
| 4500 | nil) | ||
| 4501 | :host (match-string (nth 3 tramp-file-name-structure) name) | ||
| 4502 | :path (match-string (nth 4 tramp-file-name-structure) name)))))) | ||
| 4503 | |||
| 4504 | ;; HHH: Not Changed. Multi method. Will probably not handle the case where | ||
| 4505 | ;; a user name is not provided in the "file name" very well. | ||
| 4506 | (defun tramp-dissect-multi-file-name (name) | ||
| 4507 | "Not implemented yet." | ||
| 4508 | (let ((regexp (nth 0 tramp-multi-file-name-structure)) | ||
| 4509 | (method-index (nth 1 tramp-multi-file-name-structure)) | ||
| 4510 | (hops-index (nth 2 tramp-multi-file-name-structure)) | ||
| 4511 | (path-index (nth 3 tramp-multi-file-name-structure)) | ||
| 4512 | (hop-regexp (nth 0 tramp-multi-file-name-hop-structure)) | ||
| 4513 | (hop-method-index (nth 1 tramp-multi-file-name-hop-structure)) | ||
| 4514 | (hop-user-index (nth 2 tramp-multi-file-name-hop-structure)) | ||
| 4515 | (hop-host-index (nth 3 tramp-multi-file-name-hop-structure)) | ||
| 4516 | method hops len hop-methods hop-users hop-hosts path) | ||
| 4517 | (unless (string-match (format regexp hop-regexp) name) | ||
| 4518 | (error "Not a multi tramp file name: %s" name)) | ||
| 4519 | (setq method (match-string method-index name)) | ||
| 4520 | (setq hops (match-string hops-index name)) | ||
| 4521 | (setq len (/ (length (match-data t)) 2)) | ||
| 4522 | (when (< path-index 0) (incf path-index len)) | ||
| 4523 | (setq path (match-string path-index name)) | ||
| 4524 | (let ((index 0)) | ||
| 4525 | (while (string-match hop-regexp hops index) | ||
| 4526 | (setq index (match-end 0)) | ||
| 4527 | (setq hop-methods | ||
| 4528 | (cons (match-string hop-method-index hops) hop-methods)) | ||
| 4529 | (setq hop-users | ||
| 4530 | (cons (match-string hop-user-index hops) hop-users)) | ||
| 4531 | (setq hop-hosts | ||
| 4532 | (cons (match-string hop-host-index hops) hop-hosts)))) | ||
| 4533 | (make-tramp-file-name | ||
| 4534 | :multi-method method | ||
| 4535 | :method (apply 'vector (reverse hop-methods)) | ||
| 4536 | :user (apply 'vector (reverse hop-users)) | ||
| 4537 | :host (apply 'vector (reverse hop-hosts)) | ||
| 4538 | :path path))) | ||
| 4539 | |||
| 4540 | (defun tramp-make-tramp-file-name (multi-method method user host path) | ||
| 4541 | "Constructs a tramp file name from METHOD, USER, HOST and PATH." | ||
| 4542 | (unless tramp-make-tramp-file-format | ||
| 4543 | (error "`tramp-make-tramp-file-format' is nil")) | ||
| 4544 | (if multi-method | ||
| 4545 | (tramp-make-tramp-multi-file-name multi-method method user host path) | ||
| 4546 | (if user | ||
| 4547 | (format-spec tramp-make-tramp-file-format | ||
| 4548 | (list (cons ?m method) | ||
| 4549 | (cons ?u user) | ||
| 4550 | (cons ?h host) | ||
| 4551 | (cons ?p path))) | ||
| 4552 | (format-spec tramp-make-tramp-file-user-nil-format | ||
| 4553 | (list (cons ?m method) | ||
| 4554 | (cons ?h host) | ||
| 4555 | (cons ?p path)))))) | ||
| 4556 | |||
| 4557 | ;; CCC: Henrik Holm: Not Changed. Multi Method. What should be done | ||
| 4558 | ;; with this when USER is nil? | ||
| 4559 | (defun tramp-make-tramp-multi-file-name (multi-method method user host path) | ||
| 4560 | "Constructs a tramp file name for a multi-hop method." | ||
| 4561 | (unless tramp-make-multi-tramp-file-format | ||
| 4562 | (error "`tramp-make-multi-tramp-file-format' is nil")) | ||
| 4563 | (let* ((prefix-format (nth 0 tramp-make-multi-tramp-file-format)) | ||
| 4564 | (hop-format (nth 1 tramp-make-multi-tramp-file-format)) | ||
| 4565 | (path-format (nth 2 tramp-make-multi-tramp-file-format)) | ||
| 4566 | (prefix (format-spec prefix-format (list (cons ?m multi-method)))) | ||
| 4567 | (hops "") | ||
| 4568 | (path (format-spec path-format (list (cons ?p path)))) | ||
| 4569 | (i 0) | ||
| 4570 | (len (length method))) | ||
| 4571 | (while (< i len) | ||
| 4572 | (let ((m (aref method i)) | ||
| 4573 | (u (aref user i)) | ||
| 4574 | (h (aref host i))) | ||
| 4575 | (setq hops (concat hops | ||
| 4576 | (format-spec | ||
| 4577 | hop-format | ||
| 4578 | (list (cons ?m m) | ||
| 4579 | (cons ?u u) | ||
| 4580 | (cons ?h h))))) | ||
| 4581 | (incf i))) | ||
| 4582 | (concat prefix hops path))) | ||
| 4583 | |||
| 4584 | ;; HHH: Changed. Handles the case where no user name is given in the | ||
| 4585 | ;; file name. | ||
| 4586 | (defun tramp-make-rcp-program-file-name (user host path) | ||
| 4587 | "Create a file name suitable to be passed to `rcp'." | ||
| 4588 | (if user | ||
| 4589 | (format "%s@%s:%s" user host path) | ||
| 4590 | (format "%s:%s" host path))) | ||
| 4591 | |||
| 4592 | (defun tramp-method-out-of-band-p (multi-method method) | ||
| 4593 | "Return t if this is an out-of-band method, nil otherwise. | ||
| 4594 | It is important to check for this condition, since it is not possible | ||
| 4595 | to enter a password for the `tramp-rcp-program'." | ||
| 4596 | (tramp-get-rcp-program multi-method method)) | ||
| 4597 | |||
| 4598 | ;; Variables local to connection. | ||
| 4599 | |||
| 4600 | (defun tramp-get-ls-command (multi-method method user host) | ||
| 4601 | (save-excursion | ||
| 4602 | (tramp-maybe-open-connection multi-method method user host) | ||
| 4603 | (set-buffer (tramp-get-buffer multi-method method user host)) | ||
| 4604 | tramp-ls-command)) | ||
| 4605 | |||
| 4606 | (defun tramp-get-test-groks-nt (multi-method method user host) | ||
| 4607 | (save-excursion | ||
| 4608 | (tramp-maybe-open-connection multi-method method user host) | ||
| 4609 | (set-buffer (tramp-get-buffer multi-method method user host)) | ||
| 4610 | tramp-test-groks-nt)) | ||
| 4611 | |||
| 4612 | (defun tramp-get-file-exists-command (multi-method method user host) | ||
| 4613 | (save-excursion | ||
| 4614 | (tramp-maybe-open-connection multi-method method user host) | ||
| 4615 | (set-buffer (tramp-get-buffer multi-method method user host)) | ||
| 4616 | tramp-file-exists-command)) | ||
| 4617 | |||
| 4618 | (defun tramp-get-remote-perl (multi-method method user host) | ||
| 4619 | (tramp-get-connection-property "perl" nil multi-method method user host)) | ||
| 4620 | |||
| 4621 | (defun tramp-get-remote-ln (multi-method method user host) | ||
| 4622 | (tramp-get-connection-property "ln" nil multi-method method user host)) | ||
| 4623 | |||
| 4624 | ;; Get a property of a TRAMP connection. | ||
| 4625 | (defun tramp-get-connection-property (property default multi-method method user host) | ||
| 4626 | "Get the named property for the connection. | ||
| 4627 | If the value is not set for the connection, return `default'" | ||
| 4628 | (tramp-maybe-open-connection multi-method method user host) | ||
| 4629 | (with-current-buffer (tramp-get-buffer multi-method method user host) | ||
| 4630 | (let (error) | ||
| 4631 | (condition-case nil | ||
| 4632 | (symbol-value (intern (concat "tramp-connection-property-" property))) | ||
| 4633 | (error default))))) | ||
| 4634 | |||
| 4635 | ;; Set a property of a TRAMP connection. | ||
| 4636 | (defun tramp-set-connection-property (property value multi-method method user host) | ||
| 4637 | "Set the named property of a TRAMP connection." | ||
| 4638 | (tramp-maybe-open-connection multi-method method user host) | ||
| 4639 | (with-current-buffer (tramp-get-buffer multi-method method user host) | ||
| 4640 | (set (make-local-variable | ||
| 4641 | (intern (concat "tramp-connection-property-" property))) | ||
| 4642 | value))) | ||
| 4643 | |||
| 4644 | |||
| 4645 | |||
| 4646 | (defun tramp-get-connection-function (multi-method method) | ||
| 4647 | (second (or (assoc 'tramp-connection-function | ||
| 4648 | (assoc (or multi-method method tramp-default-method) | ||
| 4649 | tramp-methods)) | ||
| 4650 | (error "Method `%s' didn't specify a connection function" | ||
| 4651 | (or multi-method method))))) | ||
| 4652 | |||
| 4653 | (defun tramp-get-remote-sh (multi-method method) | ||
| 4654 | (second (or (assoc 'tramp-remote-sh | ||
| 4655 | (assoc (or multi-method method tramp-default-method) | ||
| 4656 | tramp-methods)) | ||
| 4657 | (error "Method `%s' didn't specify a remote shell" | ||
| 4658 | (or multi-method method))))) | ||
| 4659 | |||
| 4660 | (defun tramp-get-rsh-program (multi-method method) | ||
| 4661 | (second (or (assoc 'tramp-rsh-program | ||
| 4662 | (assoc (or multi-method method tramp-default-method) | ||
| 4663 | tramp-methods)) | ||
| 4664 | (error "Method `%s' didn't specify an rsh program" | ||
| 4665 | (or multi-method method))))) | ||
| 4666 | |||
| 4667 | (defun tramp-get-rsh-args (multi-method method) | ||
| 4668 | (second (or (assoc 'tramp-rsh-args | ||
| 4669 | (assoc (or multi-method method tramp-default-method) | ||
| 4670 | tramp-methods)) | ||
| 4671 | (error "Method `%s' didn't specify rsh args" | ||
| 4672 | (or multi-method method))))) | ||
| 4673 | |||
| 4674 | (defun tramp-get-rcp-program (multi-method method) | ||
| 4675 | (second (or (assoc 'tramp-rcp-program | ||
| 4676 | (assoc (or multi-method method tramp-default-method) | ||
| 4677 | tramp-methods)) | ||
| 4678 | (error "Method `%s' didn't specify an rcp program" | ||
| 4679 | (or multi-method method))))) | ||
| 4680 | |||
| 4681 | (defun tramp-get-rcp-args (multi-method method) | ||
| 4682 | (second (or (assoc 'tramp-rcp-args | ||
| 4683 | (assoc (or multi-method method tramp-default-method) | ||
| 4684 | tramp-methods)) | ||
| 4685 | (error "Method `%s' didn't specify rcp args" | ||
| 4686 | (or multi-method method))))) | ||
| 4687 | |||
| 4688 | (defun tramp-get-rcp-keep-date-arg (multi-method method) | ||
| 4689 | (second (or (assoc 'tramp-rcp-keep-date-arg | ||
| 4690 | (assoc (or multi-method method tramp-default-method) | ||
| 4691 | tramp-methods)) | ||
| 4692 | (error "Method `%s' didn't specify `keep-date' arg for tramp" | ||
| 4693 | (or multi-method method))))) | ||
| 4694 | |||
| 4695 | (defun tramp-get-su-program (multi-method method) | ||
| 4696 | (second (or (assoc 'tramp-su-program | ||
| 4697 | (assoc (or multi-method method tramp-default-method) | ||
| 4698 | tramp-methods)) | ||
| 4699 | (error "Method `%s' didn't specify a su program" | ||
| 4700 | (or multi-method method))))) | ||
| 4701 | |||
| 4702 | (defun tramp-get-su-args (multi-method method) | ||
| 4703 | (second (or (assoc 'tramp-su-args | ||
| 4704 | (assoc (or multi-method method tramp-default-method) | ||
| 4705 | tramp-methods)) | ||
| 4706 | (error "Method `%s' didn't specify su args" | ||
| 4707 | (or multi-method method))))) | ||
| 4708 | |||
| 4709 | (defun tramp-get-encoding-command (multi-method method) | ||
| 4710 | (second (or (assoc 'tramp-encoding-command | ||
| 4711 | (assoc (or multi-method method tramp-default-method) | ||
| 4712 | tramp-methods)) | ||
| 4713 | (error "Method `%s' didn't specify an encoding command" | ||
| 4714 | (or multi-method method))))) | ||
| 4715 | |||
| 4716 | (defun tramp-get-decoding-command (multi-method method) | ||
| 4717 | (second (or (assoc 'tramp-decoding-command | ||
| 4718 | (assoc (or multi-method method tramp-default-method) | ||
| 4719 | tramp-methods)) | ||
| 4720 | (error "Method `%s' didn't specify a decoding command" | ||
| 4721 | (or multi-method method))))) | ||
| 4722 | |||
| 4723 | (defun tramp-get-encoding-function (multi-method method) | ||
| 4724 | (second (or (assoc 'tramp-encoding-function | ||
| 4725 | (assoc (or multi-method method tramp-default-method) | ||
| 4726 | tramp-methods)) | ||
| 4727 | (error "Method `%s' didn't specify an encoding function" | ||
| 4728 | (or multi-method method))))) | ||
| 4729 | |||
| 4730 | (defun tramp-get-decoding-function (multi-method method) | ||
| 4731 | (second (or (assoc 'tramp-decoding-function | ||
| 4732 | (assoc (or multi-method method tramp-default-method) | ||
| 4733 | tramp-methods)) | ||
| 4734 | (error "Method `%s' didn't specify a decoding function" | ||
| 4735 | (or multi-method method))))) | ||
| 4736 | |||
| 4737 | (defun tramp-get-telnet-program (multi-method method) | ||
| 4738 | (second (or (assoc 'tramp-telnet-program | ||
| 4739 | (assoc (or multi-method method tramp-default-method) | ||
| 4740 | tramp-methods)) | ||
| 4741 | (error "Method `%s' didn't specify a telnet program" | ||
| 4742 | (or multi-method method))))) | ||
| 4743 | |||
| 4744 | (defun tramp-get-telnet-args (multi-method method) | ||
| 4745 | (second (or (assoc 'tramp-telnet-args | ||
| 4746 | (assoc (or multi-method method tramp-default-method) | ||
| 4747 | tramp-methods)) | ||
| 4748 | (error "Method `%s' didn't specify telnet args" | ||
| 4749 | (or multi-method method))))) | ||
| 4750 | |||
| 4751 | ;; Auto saving to a special directory. | ||
| 4752 | |||
| 4753 | (defun tramp-make-auto-save-file-name (fn) | ||
| 4754 | "Returns a file name in `tramp-auto-save-directory' for autosaving this file." | ||
| 4755 | (when tramp-auto-save-directory | ||
| 4756 | (unless (file-exists-p tramp-auto-save-directory) | ||
| 4757 | (make-directory tramp-auto-save-directory t))) | ||
| 4758 | ;; jka-compr doesn't like auto-saving, so by appending "~" to the | ||
| 4759 | ;; file name we make sure that jka-compr isn't used for the | ||
| 4760 | ;; auto-save file. | ||
| 4761 | (let ((buffer-file-name (expand-file-name | ||
| 4762 | (tramp-subst-strs-in-string '(("_" . "|") | ||
| 4763 | ("/" . "_a") | ||
| 4764 | (":" . "_b") | ||
| 4765 | ("|" . "__") | ||
| 4766 | ("[" . "_l") | ||
| 4767 | ("]" . "_r")) | ||
| 4768 | fn) | ||
| 4769 | tramp-auto-save-directory))) | ||
| 4770 | (make-auto-save-file-name))) | ||
| 4771 | |||
| 4772 | (defadvice make-auto-save-file-name | ||
| 4773 | (around tramp-advice-make-auto-save-file-name () activate) | ||
| 4774 | "Invoke `tramp-make-auto-save-file-name' for tramp files." | ||
| 4775 | (if (and (buffer-file-name) (tramp-tramp-file-p (buffer-file-name)) | ||
| 4776 | tramp-auto-save-directory) | ||
| 4777 | (setq ad-return-value | ||
| 4778 | (tramp-make-auto-save-file-name (buffer-file-name))) | ||
| 4779 | ad-do-it)) | ||
| 4780 | |||
| 4781 | (defun tramp-subst-strs-in-string (alist string) | ||
| 4782 | "Replace all occurrences of the string FROM with TO in STRING. | ||
| 4783 | ALIST is of the form ((FROM . TO) ...)." | ||
| 4784 | (save-match-data | ||
| 4785 | (while alist | ||
| 4786 | (let* ((pr (car alist)) | ||
| 4787 | (from (car pr)) | ||
| 4788 | (to (cdr pr))) | ||
| 4789 | (while (string-match (regexp-quote from) string) | ||
| 4790 | (setq string (replace-match to t t string))) | ||
| 4791 | (setq alist (cdr alist)))) | ||
| 4792 | string)) | ||
| 4793 | |||
| 4794 | (defun tramp-insert-with-face (face string) | ||
| 4795 | "Insert text with a specific face." | ||
| 4796 | (let ((start (point))) | ||
| 4797 | (insert string) | ||
| 4798 | (add-text-properties start (point) (list 'face face)))) | ||
| 4799 | |||
| 4800 | ;; ------------------------------------------------------------ | ||
| 4801 | ;; -- Compatibility functions section -- | ||
| 4802 | ;; ------------------------------------------------------------ | ||
| 4803 | |||
| 4804 | (defun tramp-temporary-file-directory () | ||
| 4805 | "Return name of directory for temporary files (compat function). | ||
| 4806 | For Emacs, this is the variable `temporary-file-directory', for XEmacs | ||
| 4807 | this is the function `temp-directory'." | ||
| 4808 | (cond ((boundp 'temporary-file-directory) | ||
| 4809 | (symbol-value 'temporary-file-directory)) | ||
| 4810 | ((fboundp 'temp-directory) | ||
| 4811 | (funcall (symbol-function 'temp-directory))) ;pacify byte-compiler | ||
| 4812 | ((let ((d (getenv "TEMP"))) (and d (file-directory-p d))) | ||
| 4813 | (file-name-as-directory (getenv "TEMP"))) | ||
| 4814 | ((let ((d (getenv "TMP"))) (and d (file-directory-p d))) | ||
| 4815 | (file-name-as-directory (getenv "TMP"))) | ||
| 4816 | ((let ((d (getenv "TMPDIR"))) (and d (file-directory-p d))) | ||
| 4817 | (file-name-as-directory (getenv "TMPDIR"))) | ||
| 4818 | ((file-exists-p "c:/temp") (file-name-as-directory "c:/temp")) | ||
| 4819 | (t (message (concat "Neither `temporary-file-directory' nor " | ||
| 4820 | "`temp-directory' is defined -- using /tmp.")) | ||
| 4821 | (file-name-as-directory "/tmp")))) | ||
| 4822 | |||
| 4823 | (defun tramp-read-passwd (prompt) | ||
| 4824 | "Read a password from user (compat function). | ||
| 4825 | Invokes `read-passwd' if that is defined, else `ange-ftp-read-passwd'." | ||
| 4826 | (apply | ||
| 4827 | (if (fboundp 'read-passwd) #'read-passwd #'ange-ftp-read-passwd) | ||
| 4828 | (list prompt))) | ||
| 4829 | |||
| 4830 | (defun tramp-time-diff (t1 t2) | ||
| 4831 | "Return the difference between the two times, in seconds. | ||
| 4832 | T1 and T2 are time values (as returned by `current-time' for example). | ||
| 4833 | |||
| 4834 | NOTE: This function will fail if the time difference is too large to | ||
| 4835 | fit in an integer." | ||
| 4836 | ;; Pacify byte-compiler with `symbol-function'. | ||
| 4837 | (cond ((fboundp 'subtract-time) | ||
| 4838 | (cadr (funcall (symbol-function 'subtract-time) t1 t2))) | ||
| 4839 | ((fboundp 'itimer-time-difference) | ||
| 4840 | (floor (funcall | ||
| 4841 | (symbol-function 'itimer-time-difference) | ||
| 4842 | (if (< (length t1) 3) (append t1 '(0)) t1) | ||
| 4843 | (if (< (length t2) 3) (append t2 '(0)) t2)))) | ||
| 4844 | (t | ||
| 4845 | ;; snarfed from Emacs 21 time-date.el | ||
| 4846 | (cadr (let ((borrow (< (cadr t1) (cadr t2)))) | ||
| 4847 | (list (- (car t1) (car t2) (if borrow 1 0)) | ||
| 4848 | (- (+ (if borrow 65536 0) (cadr t1)) (cadr t2)))))))) | ||
| 4849 | |||
| 4850 | (defun tramp-coding-system-change-eol-conversion (coding-system eol-type) | ||
| 4851 | "Return a coding system like CODING-SYSTEM but with given EOL-TYPE. | ||
| 4852 | EOL-TYPE can be one of `dos', `unix', or `mac'." | ||
| 4853 | (cond ((fboundp 'coding-system-change-eol-conversion) | ||
| 4854 | (apply #'coding-system-change-eol-conversion | ||
| 4855 | (list coding-system eol-type))) | ||
| 4856 | ((fboundp 'subsidiary-coding-system) | ||
| 4857 | (apply | ||
| 4858 | #'subsidiary-coding-system | ||
| 4859 | (list coding-system | ||
| 4860 | (cond ((eq eol-type 'dos) 'crlf) | ||
| 4861 | ((eq eol-type 'unix) 'lf) | ||
| 4862 | ((eq eol-type 'mac) 'cr) | ||
| 4863 | (t | ||
| 4864 | (error "Unknown EOL-TYPE `%s', must be %s" | ||
| 4865 | eol-type | ||
| 4866 | "`dos', `unix', or `mac'")))))) | ||
| 4867 | (t (error "Can't change EOL conversion -- is MULE missing?")))) | ||
| 4868 | |||
| 4869 | (defun tramp-split-string (string pattern) | ||
| 4870 | "Like `split-string' but omit empty strings. | ||
| 4871 | In Emacs, (split-string \"/foo/bar\" \"/\") returns (\"foo\" \"bar\"). | ||
| 4872 | This is, the first, empty, element is omitted. In XEmacs, the first | ||
| 4873 | element is not omitted. | ||
| 4874 | |||
| 4875 | Note: this function has been written for `tramp-handle-file-truename'. | ||
| 4876 | If you want to use it for something else, you'll have to check whether | ||
| 4877 | it does the right thing." | ||
| 4878 | (delete "" (split-string string pattern))) | ||
| 4879 | |||
| 4880 | ;; ------------------------------------------------------------ | ||
| 4881 | ;; -- Kludges section -- | ||
| 4882 | ;; ------------------------------------------------------------ | ||
| 4883 | |||
| 4884 | ;; Currently (as of Emacs 20.5), the function `shell-quote-argument' | ||
| 4885 | ;; does not deal well with newline characters. Newline is replaced by | ||
| 4886 | ;; backslash newline. But if, say, the string `a backslash newline b' | ||
| 4887 | ;; is passed to a shell, the shell will expand this into "ab", | ||
| 4888 | ;; completely omitting the newline. This is not what was intended. | ||
| 4889 | ;; It does not appear to be possible to make the function | ||
| 4890 | ;; `shell-quote-argument' work with newlines without making it | ||
| 4891 | ;; dependent on the shell used. But within this package, we know that | ||
| 4892 | ;; we will always use a Bourne-like shell, so we use an approach which | ||
| 4893 | ;; groks newlines. | ||
| 4894 | ;; | ||
| 4895 | ;; The approach is simple: we call `shell-quote-argument', then | ||
| 4896 | ;; massage the newline part of the result. | ||
| 4897 | ;; | ||
| 4898 | ;; This function should produce a string which is grokked by a Unix | ||
| 4899 | ;; shell, even if the Emacs is running on Windows. Since this is the | ||
| 4900 | ;; kludges section, we bind `system-type' in such a way that | ||
| 4901 | ;; `shell-quote-arguments' behaves as if on Unix. | ||
| 4902 | ;; | ||
| 4903 | ;; Thanks to Mario DeWeerd for the hint that it is sufficient for this | ||
| 4904 | ;; function to work with Bourne-like shells. | ||
| 4905 | ;; | ||
| 4906 | ;; CCC: This function should be rewritten so that | ||
| 4907 | ;; `shell-quote-argument' is not used. This way, we are safe from | ||
| 4908 | ;; changes in `shell-quote-argument'. | ||
| 4909 | (defun tramp-shell-quote-argument (s) | ||
| 4910 | "Similar to `shell-quote-argument', but groks newlines. | ||
| 4911 | Only works for Bourne-like shells." | ||
| 4912 | (let ((system-type 'not-windows)) | ||
| 4913 | (save-match-data | ||
| 4914 | (let ((result (shell-quote-argument s)) | ||
| 4915 | (nl (regexp-quote (format "\\%s" tramp-rsh-end-of-line)))) | ||
| 4916 | (when (and (>= (length result) 2) | ||
| 4917 | (string= (substring result 0 2) "\\~")) | ||
| 4918 | (setq result (substring result 1))) | ||
| 4919 | (while (string-match nl result) | ||
| 4920 | (setq result (replace-match (format "'%s'" tramp-rsh-end-of-line) | ||
| 4921 | t t result))) | ||
| 4922 | result)))) | ||
| 4923 | |||
| 4924 | ;; ;; EFS hooks itself into the file name handling stuff in more places | ||
| 4925 | ;; ;; than just `file-name-handler-alist'. The following tells EFS to stay | ||
| 4926 | ;; ;; away from tramp.el paths. | ||
| 4927 | ;; ;; | ||
| 4928 | ;; ;; This is needed because EFS installs (efs-dired-before-readin) into | ||
| 4929 | ;; ;; 'dired-before-readin-hook'. This prevents EFS from opening an FTP | ||
| 4930 | ;; ;; connection to help it's dired process. Not that I have any real | ||
| 4931 | ;; ;; idea *why* this is helpful to dired. | ||
| 4932 | ;; ;; | ||
| 4933 | ;; ;; Anyway, this advice fixes the problem (with a sledgehammer :) | ||
| 4934 | ;; ;; | ||
| 4935 | ;; ;; Daniel Pittman <daniel@danann.net> | ||
| 4936 | ;; ;; | ||
| 4937 | ;; ;; CCC: when the other defadvice calls have disappeared, make sure | ||
| 4938 | ;; ;; not to call defadvice unless it's necessary. How do we find out whether | ||
| 4939 | ;; ;; it is necessary? (featurep 'efs) is surely the wrong way -- | ||
| 4940 | ;; ;; EFS might nicht be loaded yet. | ||
| 4941 | ;; (defadvice efs-ftp-path (around dont-match-tramp-path activate protect) | ||
| 4942 | ;; "Cause efs-ftp-path to fail when the path is a TRAMP path." | ||
| 4943 | ;; (if (tramp-tramp-file-p (ad-get-arg 0)) | ||
| 4944 | ;; nil | ||
| 4945 | ;; ad-do-it)) | ||
| 4946 | |||
| 4947 | ;; We currently use "[" and "]" in the filename format. In Emacs | ||
| 4948 | ;; 20.x, this means that Emacs wants to expand wildcards if | ||
| 4949 | ;; `find-file-wildcards' is non-nil, and then barfs because no | ||
| 4950 | ;; expansion could be found. We detect this situation and do | ||
| 4951 | ;; something really awful: we have `file-expand-wildcards' return the | ||
| 4952 | ;; original filename if it can't expand anything. Let's just hope | ||
| 4953 | ;; that this doesn't break anything else. | ||
| 4954 | ;; | ||
| 4955 | ;; Another problem is that the check is done by Emacs version, which | ||
| 4956 | ;; is really not what we want to do. Oh, well. | ||
| 4957 | |||
| 4958 | ;;(when (and (not (featurep 'xemacs)) | ||
| 4959 | ;; (= emacs-major-version 20)) | ||
| 4960 | ;; It seems that this advice is needed in Emacs 21, too. | ||
| 4961 | (defadvice file-expand-wildcards (around tramp-fix activate) | ||
| 4962 | (let ((name (ad-get-arg 0))) | ||
| 4963 | (if (tramp-tramp-file-p name) | ||
| 4964 | ;; If it's a Tramp file, dissect it and look if wildcards | ||
| 4965 | ;; need to be expanded at all. | ||
| 4966 | (let ((v (tramp-dissect-file-name name))) | ||
| 4967 | (if (string-match "[[*?]" (tramp-file-name-path v)) | ||
| 4968 | (let ((res ad-do-it)) | ||
| 4969 | (setq ad-return-value (or res (list name)))) | ||
| 4970 | (setq ad-return-value (list name)))) | ||
| 4971 | ;; If it is not a Tramp file, just run the original function. | ||
| 4972 | (let ((res ad-do-it)) | ||
| 4973 | (setq ad-return-value (or res (list name))))))) | ||
| 4974 | ;; ) | ||
| 4975 | |||
| 4976 | ;; Make the `reporter` functionality available for making bug reports about | ||
| 4977 | ;; the package. A most useful piece of code. | ||
| 4978 | |||
| 4979 | (unless (fboundp 'reporter-submit-bug-report) | ||
| 4980 | (autoload 'reporter-submit-bug-report "reporter")) | ||
| 4981 | |||
| 4982 | (defun tramp-bug () | ||
| 4983 | "Submit a bug report to the TRAMP developers." | ||
| 4984 | (interactive) | ||
| 4985 | (require 'reporter) | ||
| 4986 | (let ((reporter-prompt-for-summary-p t)) | ||
| 4987 | (reporter-submit-bug-report | ||
| 4988 | tramp-bug-report-address ; to-address | ||
| 4989 | (format "tramp (%s)" tramp-version) ; package name and version | ||
| 4990 | `(;; Current state | ||
| 4991 | tramp-ls-command | ||
| 4992 | tramp-test-groks-nt | ||
| 4993 | tramp-file-exists-command | ||
| 4994 | tramp-current-multi-method | ||
| 4995 | tramp-current-method | ||
| 4996 | tramp-current-user | ||
| 4997 | tramp-current-host | ||
| 4998 | |||
| 4999 | ;; System defaults | ||
| 5000 | tramp-auto-save-directory ; vars to dump | ||
| 5001 | tramp-default-method | ||
| 5002 | tramp-rsh-end-of-line | ||
| 5003 | tramp-remote-path | ||
| 5004 | tramp-login-prompt-regexp | ||
| 5005 | tramp-password-prompt-regexp | ||
| 5006 | tramp-wrong-passwd-regexp | ||
| 5007 | tramp-temp-name-prefix | ||
| 5008 | tramp-file-name-structure | ||
| 5009 | tramp-file-name-regexp | ||
| 5010 | tramp-multi-file-name-structure | ||
| 5011 | tramp-multi-file-name-hop-structure | ||
| 5012 | tramp-multi-methods | ||
| 5013 | tramp-multi-connection-function-alist | ||
| 5014 | tramp-make-tramp-file-format | ||
| 5015 | tramp-end-of-output | ||
| 5016 | |||
| 5017 | ;; Non-tramp variables of interest | ||
| 5018 | shell-prompt-pattern | ||
| 5019 | backup-by-copying | ||
| 5020 | backup-by-copying-when-linked | ||
| 5021 | backup-by-copying-when-mismatch | ||
| 5022 | ,(when (boundp 'backup-by-copying-when-privileged-mismatch) | ||
| 5023 | 'backup-by-copying-when-privileged-mismatch) | ||
| 5024 | file-name-handler-alist) | ||
| 5025 | nil ; pre-hook | ||
| 5026 | nil ; post-hook | ||
| 5027 | "\ | ||
| 5028 | Enter your bug report in this message, including as much detail as you | ||
| 5029 | possibly can about the problem, what you did to cause it and what the | ||
| 5030 | local and remote machines are. | ||
| 5031 | |||
| 5032 | If you can give a simple set of instructions to make this bug happen | ||
| 5033 | reliably, please include those. Thank you for helping kill bugs in | ||
| 5034 | TRAMP. | ||
| 5035 | --bug report follows this line--"))) | ||
| 5036 | |||
| 5037 | (defalias 'tramp-submit-bug 'tramp-bug) | ||
| 5038 | |||
| 5039 | (provide 'tramp) | ||
| 5040 | |||
| 5041 | ;; Make sure that we get integration with the VC package. | ||
| 5042 | ;; When it is loaded, we need to pull in the integration module. | ||
| 5043 | ;; This must come after (provide 'tramp) because tramp-vc.el | ||
| 5044 | ;; requires tramp. | ||
| 5045 | (eval-after-load "vc" | ||
| 5046 | '(require 'tramp-vc)) | ||
| 5047 | |||
| 5048 | ;;; TODO: | ||
| 5049 | |||
| 5050 | ;; * Cooperate with PCL-CVS. It uses start-process, which doesn't | ||
| 5051 | ;; work for remote files. | ||
| 5052 | ;; * Allow /[method/user@host:port] syntax for the ssh "-p" argument. | ||
| 5053 | ;; * Rewrite `tramp-shell-quote-argument' to abstain from using | ||
| 5054 | ;; `shell-quote-argument'. | ||
| 5055 | ;; * Completion gets confused when you leave out the method name. | ||
| 5056 | ;; * Support `dired-compress-file' filename handler. | ||
| 5057 | ;; * In Emacs 21, `insert-directory' shows total number of bytes used | ||
| 5058 | ;; by the files in that directory. Add this here. | ||
| 5059 | ;; * Avoid screen blanking when hitting `g' in dired. (Eli Tziperman) | ||
| 5060 | ;; * Make ffap.el grok Tramp filenames. (Eli Tziperman) | ||
| 5061 | ;; * When logging in, keep looking for questions according to an alist | ||
| 5062 | ;; and then invoke the right function. | ||
| 5063 | ;; * Case-insensitive filename completion. (Norbert Goevert.) | ||
| 5064 | ;; * Running CVS remotely doesn't appear to work right. It thinks | ||
| 5065 | ;; files are locked by somebody else even if I'm the locking user. | ||
| 5066 | ;; Sometimes, one gets `No CVSROOT specified' errors from CVS. | ||
| 5067 | ;; (Skip Montanaro) | ||
| 5068 | ;; * Don't use globbing for directories with many files, as this is | ||
| 5069 | ;; likely to produce long command lines, and some shells choke on | ||
| 5070 | ;; long command lines. | ||
| 5071 | ;; * Implement `load' operation. | ||
| 5072 | ;; * Find out about the new auto-save mechanism in Emacs 21 and | ||
| 5073 | ;; do the right thing. | ||
| 5074 | ;; * `vc-directory' does not work. It never displays any files, even | ||
| 5075 | ;; if it does show files when run locally. | ||
| 5076 | ;; * Allow correction of passwords, if the remote end allows this. | ||
| 5077 | ;; (Mark Hershberger) | ||
| 5078 | ;; * Make sure permissions of tmp file are good. | ||
| 5079 | ;; (Nelson Minar <nelson@media.mit.edu>) | ||
| 5080 | ;; * Grok passwd prompts with scp? (David Winter | ||
| 5081 | ;; <winter@nevis1.nevis.columbia.edu>). Maybe just do `ssh -l user | ||
| 5082 | ;; host', then wait a while for the passwd or passphrase prompt. If | ||
| 5083 | ;; there is one, remember the passwd/phrase. | ||
| 5084 | ;; * How to deal with MULE in `insert-file-contents' and `write-region'? | ||
| 5085 | ;; * Do asynchronous `shell-command's. | ||
| 5086 | ;; * Grok `append' parameter for `write-region'. | ||
| 5087 | ;; * Test remote ksh or bash for tilde expansion in `tramp-find-shell'? | ||
| 5088 | ;; * abbreviate-file-name | ||
| 5089 | ;; * grok ~ in tramp-remote-path (Henrik Holm <henrikh@tele.ntnu.no>) | ||
| 5090 | ;; * `C' in dired gives error `not tramp file name'. | ||
| 5091 | ;; * Also allow to omit user names when doing multi-hop. Not sure yet | ||
| 5092 | ;; what the user names should default to, though. | ||
| 5093 | ;; * better error checking. At least whenever we see something | ||
| 5094 | ;; strange when doing zerop, we should kill the process and start | ||
| 5095 | ;; again. (Greg Stark) | ||
| 5096 | ;; * Add caching for filename completion. (Greg Stark) | ||
| 5097 | ;; Of course, this has issues with usability (stale cache bites) | ||
| 5098 | ;; -- <daniel@danann.net> | ||
| 5099 | ;; * Provide a local cache of old versions of remote files for the rsync | ||
| 5100 | ;; transfer method to use. (Greg Stark) | ||
| 5101 | ;; * Remove unneeded parameters from methods. | ||
| 5102 | ;; * Invoke rsync once for copying a whole directory hierarchy. | ||
| 5103 | ;; (Francesco Potortì) | ||
| 5104 | ;; * Should we set PATH ourselves or should we rely on the remote end | ||
| 5105 | ;; to do it? | ||
| 5106 | ;; * Do the autoconf thing. | ||
| 5107 | ;; * Make it work for XEmacs 20, which is missing `with-timeout'. | ||
| 5108 | ;; * Allow non-Unix remote systems. (More a long-term thing.) | ||
| 5109 | ;; * Make it work for different encodings, and for different file name | ||
| 5110 | ;; encodings, too. (Daniel Pittman) | ||
| 5111 | ;; * Change applicable functions to pass a struct tramp-file-name rather | ||
| 5112 | ;; than the individual items MULTI-METHOD, METHOD, USER, HOST, PATH. | ||
| 5113 | ;; * Implement asynchronous shell commands. | ||
| 5114 | ;; * Clean up unused *tramp/foo* buffers after a while. (Pete Forman) | ||
| 5115 | ;; * Progress reports while copying files. (Michael Kifer) | ||
| 5116 | ;; * `Smart' connection method that uses inline for small and out of | ||
| 5117 | ;; band for large files. (Michael Kifer) | ||
| 5118 | ;; * Don't search for perl5 and perl. Instead, only search for perl and | ||
| 5119 | ;; then look if it's the right version (with `perl -v'). | ||
| 5120 | ;; * When editing a remote CVS controlled file as a different user, VC | ||
| 5121 | ;; gets confused about the file locking status. Try to find out why | ||
| 5122 | ;; the workaround doesn't work. | ||
| 5123 | ;; * When user is running ssh-agent, it would be useful to add the | ||
| 5124 | ;; passwords typed by the user to that agent. This way, the next time | ||
| 5125 | ;; round, the users don't have to type all this in again. | ||
| 5126 | ;; This would be especially useful for start-process, I think. | ||
| 5127 | ;; An easy way to implement start-process is to open a second shell | ||
| 5128 | ;; connection which is inconvenient if the user has to reenter | ||
| 5129 | ;; passwords. | ||
| 5130 | ;; * Change `copy-file' to grok the case where the filename handler | ||
| 5131 | ;; for the source and the target file are different. Right now, | ||
| 5132 | ;; it looks at the source file and then calls that handler, if | ||
| 5133 | ;; there is one. But since ange-ftp, for instance, does not know | ||
| 5134 | ;; about Tramp, it does not do the right thing if the target file | ||
| 5135 | ;; name is a Tramp name. | ||
| 5136 | |||
| 5137 | ;; Functions for file-name-handler-alist: | ||
| 5138 | ;; diff-latest-backup-file -- in diff.el | ||
| 5139 | ;; dired-compress-file | ||
| 5140 | ;; dired-uncache -- this will be needed when we do insert-directory caching | ||
| 5141 | ;; file-name-as-directory -- use primitive? | ||
| 5142 | ;; file-name-directory -- use primitive? | ||
| 5143 | ;; file-name-nondirectory -- use primitive? | ||
| 5144 | ;; file-name-sans-versions -- use primitive? | ||
| 5145 | ;; file-newer-than-file-p | ||
| 5146 | ;; find-backup-file-name | ||
| 5147 | ;; get-file-buffer -- use primitive | ||
| 5148 | ;; load | ||
| 5149 | ;; unhandled-file-name-directory | ||
| 5150 | ;; vc-registered | ||
| 5151 | |||
| 5152 | ;;; tramp.el ends here | ||
diff --git a/man/tramp.texi b/man/tramp.texi new file mode 100644 index 00000000000..5123f6c7ffb --- /dev/null +++ b/man/tramp.texi | |||
| @@ -0,0 +1,1626 @@ | |||
| 1 | \input texinfo @c -*-texinfo-*- | ||
| 2 | @c %**start of header | ||
| 3 | @setfilename tramp.info | ||
| 4 | @settitle TRAMP User Manual | ||
| 5 | @setchapternewpage odd | ||
| 6 | @c %**end of header | ||
| 7 | |||
| 8 | @c This is *so* much nicer :) | ||
| 9 | @footnotestyle end | ||
| 10 | |||
| 11 | @c Version values, for easy modification | ||
| 12 | @c NOTE: The 'UPDATED' value is updated by the 'time-stamp' function. | ||
| 13 | @c If you change it by hand, the modifications will not stay. | ||
| 14 | @set VERSION $Revision: 2.20 $ | ||
| 15 | @set UPDATED Friday, 14 June, 2002 | ||
| 16 | |||
| 17 | |||
| 18 | @c Entries for @command{install-info} to use | ||
| 19 | @direntry | ||
| 20 | * TRAMP: (tramp). Transparent Remote Access, Multiple Protocol | ||
| 21 | Emacs remote file access via rsh and rcp. | ||
| 22 | @end direntry | ||
| 23 | |||
| 24 | @c Macro to make formatting of the tramp program name consistent. | ||
| 25 | @macro tramp | ||
| 26 | @sc{tramp} | ||
| 27 | @end macro | ||
| 28 | |||
| 29 | @c Copying permissions, et al | ||
| 30 | @ifinfo | ||
| 31 | This file documents @tramp{}, a remote file editing package for Emacs and | ||
| 32 | XEmacs. | ||
| 33 | |||
| 34 | Copyright @copyright{} 1999, 2000 Free Software Foundation, Inc. | ||
| 35 | |||
| 36 | Permission is granted to make and distribute verbatim copies of this | ||
| 37 | manual provided the copyright notice and this permission notice are | ||
| 38 | preserved on all copies. | ||
| 39 | |||
| 40 | @ignore | ||
| 41 | Permission is granted to process this file through TeX and print the | ||
| 42 | results, provided the printed document carries a copying permission | ||
| 43 | notice identical to this one except for the removal of this paragraph | ||
| 44 | (this paragraph not being relevant to the printed manual). | ||
| 45 | |||
| 46 | @end ignore | ||
| 47 | Permission is granted to copy and distribute modified versions of this | ||
| 48 | manual under the conditions for verbatim copying, provided also that the | ||
| 49 | sections entitled ``Copying'' and ``GNU General Public License'' are | ||
| 50 | included exactly as in the original, and provided that the entire | ||
| 51 | resulting derived work is distributed under the terms of a permission | ||
| 52 | notice identical to this one. | ||
| 53 | |||
| 54 | Permission is granted to copy and distribute translations of this manual | ||
| 55 | into another language, under the above conditions for modified versions, | ||
| 56 | except that this permission notice may be stated in a translation | ||
| 57 | approved by the Free Software Foundation. | ||
| 58 | @end ifinfo | ||
| 59 | |||
| 60 | @tex | ||
| 61 | |||
| 62 | @titlepage | ||
| 63 | @title @tramp{} User Manual | ||
| 64 | @subtitle Last updated @value{UPDATED} | ||
| 65 | |||
| 66 | @author by Daniel Pittman | ||
| 67 | @author based on documentation by Kai Gro@ss{}johann | ||
| 68 | @page | ||
| 69 | |||
| 70 | @vskip 0pt plus 1filll | ||
| 71 | Permission is granted to make and distribute verbatim copies of this | ||
| 72 | manual provided the copyright notice and this permission notice are | ||
| 73 | preserved on all copies. | ||
| 74 | |||
| 75 | Permission is granted to copy and distribute modified versions of this | ||
| 76 | manual under the conditions for verbatim copying, provided also that the | ||
| 77 | sections entitled ``Copying'' and ``GNU General Public License'' are | ||
| 78 | included exactly as in the original, and provided that the entire | ||
| 79 | resulting derived work is distributed under the terms of a permission | ||
| 80 | notice identical to this one. | ||
| 81 | |||
| 82 | Permission is granted to copy and distribute translations of this manual | ||
| 83 | into another language, under the above conditions for modified versions, | ||
| 84 | except that this permission notice may be stated in a translation | ||
| 85 | approved by the Free Software Foundation. | ||
| 86 | |||
| 87 | @end titlepage | ||
| 88 | @page | ||
| 89 | |||
| 90 | @end tex | ||
| 91 | |||
| 92 | @ifnottex | ||
| 93 | @node Top, Copying, (dir), (dir) | ||
| 94 | @top @tramp{} User Manual | ||
| 95 | |||
| 96 | @tramp{} stands for `Transparent Remote (file) Access, Multiple | ||
| 97 | Protocol'. This package provides remote file editing, similar to | ||
| 98 | @cite{ange-ftp} and @cite{EFS}. | ||
| 99 | |||
| 100 | The difference is that ange-ftp uses FTP to transfer files between the | ||
| 101 | local and the remote host, whereas @tramp{} uses a combination of | ||
| 102 | @command{rsh} and @command{rcp} or other work-alike programs, such as | ||
| 103 | @command{ssh}/@command{scp}. | ||
| 104 | |||
| 105 | This is version @value{VERSION} of the @tramp{} manual, last updated on | ||
| 106 | @value{UPDATED}. | ||
| 107 | |||
| 108 | You can find the latest version of this document on the web at | ||
| 109 | @uref{http://www.freesoftware.fsf.org/tramp/}. | ||
| 110 | |||
| 111 | @ifhtml | ||
| 112 | This manual is also available as a @uref{tramp_ja.html, Japanese | ||
| 113 | translation}. | ||
| 114 | |||
| 115 | The latest release of @tramp{} is available for | ||
| 116 | @uref{http://savannah.gnu.org/download/tramp/, | ||
| 117 | download}, or you may see @ref{Obtaining @tramp{}} for more details, | ||
| 118 | including the CVS server details. | ||
| 119 | |||
| 120 | @tramp{} also has a @uref{https://savannah.gnu.org/projects/tramp/, | ||
| 121 | Savannah Project Page}. | ||
| 122 | @end ifhtml | ||
| 123 | |||
| 124 | There is a mailing list for @tramp{}, available at | ||
| 125 | @email{tramp-devel@@mail.freesoftware.fsf.org}, and archived at | ||
| 126 | @uref{http://www.mail-archive.com/emacs-rcp@@ls6.cs.uni-dortmund.de/} as | ||
| 127 | well as the usual Savannah archives. | ||
| 128 | |||
| 129 | @end ifnottex | ||
| 130 | |||
| 131 | @menu | ||
| 132 | * Copying:: @tramp{} Copying conditions. | ||
| 133 | * Overview:: What @tramp{} can and cannot do. | ||
| 134 | |||
| 135 | For the end user: | ||
| 136 | * Obtaining @tramp{}:: How to obtain @tramp{}. | ||
| 137 | * History:: History of @tramp{} | ||
| 138 | * Installation:: Installing @tramp{} with your (X)Emacs. | ||
| 139 | * Configuration:: Configuring @tramp{} for use. | ||
| 140 | * Usage:: An overview of the operation of @tramp{}. | ||
| 141 | * Bug Reports:: Reporting Bugs and Problems | ||
| 142 | * Frequently Asked Questions:: Questions and answers from the mailing list. | ||
| 143 | |||
| 144 | For the developer: | ||
| 145 | * Version Control:: The inner workings of remote version control. | ||
| 146 | * Files directories and paths:: How file names, directories and paths are mangled and managed. | ||
| 147 | * Issues:: | ||
| 148 | |||
| 149 | @detailmenu | ||
| 150 | --- The Detailed Node Listing --- | ||
| 151 | |||
| 152 | Configuring @tramp{} for use | ||
| 153 | |||
| 154 | * Connection types:: Types of connections made to remote machines. | ||
| 155 | * Inline methods:: Inline methods. | ||
| 156 | * External transfer methods:: External transfer methods. | ||
| 157 | * Multi-hop Methods:: Connecting to a remote host using multiple hops. | ||
| 158 | * Default Method:: Selecting a default method. | ||
| 159 | * Customizing Methods:: Using Non-Standard Methods. | ||
| 160 | * Remote Programs:: How @tramp{} finds and uses programs on the remote machine. | ||
| 161 | * Remote shell setup:: | ||
| 162 | |||
| 163 | Using @tramp | ||
| 164 | |||
| 165 | * Filename Syntax:: @tramp{} filename conventions. | ||
| 166 | * Multi-hop filename syntax:: Multi-hop filename conventions | ||
| 167 | * Dired:: Dired and filename completion. | ||
| 168 | |||
| 169 | The inner workings of remote version control | ||
| 170 | |||
| 171 | * Version Controlled Files:: Determining if a file is under version control. | ||
| 172 | * Remote Commands:: Executing the version control commands on the remote machine. | ||
| 173 | * Changed workfiles:: Detecting if the working file has changed. | ||
| 174 | * Checking out files:: Bringing the workfile out of the repository. | ||
| 175 | * Miscellaneous Version Control:: Things related to Version Control that don't fit elsewhere | ||
| 176 | |||
| 177 | Things related to Version Control that don't fit elsewhere | ||
| 178 | |||
| 179 | * Remote File Ownership:: How VC determines who owns a workfile. | ||
| 180 | * Back-end Versions:: How VC determines what release your RCS is. | ||
| 181 | |||
| 182 | How file names, directories and paths are mangled and managed. | ||
| 183 | |||
| 184 | * Path deconstruction:: Breaking a path into its components. | ||
| 185 | |||
| 186 | @end detailmenu | ||
| 187 | @end menu | ||
| 188 | |||
| 189 | @node Copying | ||
| 190 | @chapter @tramp{} Copying conditions | ||
| 191 | |||
| 192 | Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. | ||
| 193 | |||
| 194 | tramp.el is free software; you can redistribute it and/or modify it under | ||
| 195 | the terms of the GNU General Public License as published by the Free | ||
| 196 | Software Foundation; either version 2, or (at your option) any later | ||
| 197 | version. | ||
| 198 | |||
| 199 | tramp.el is distributed in the hope that it will be useful, but WITHOUT | ||
| 200 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 201 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 202 | more details. | ||
| 203 | |||
| 204 | You should have received a copy of the GNU General Public License along | ||
| 205 | with GNU Emacs; see the file COPYING. If not, write to the Free Software | ||
| 206 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | ||
| 207 | USA. | ||
| 208 | |||
| 209 | |||
| 210 | @node Overview | ||
| 211 | @chapter An overview of @tramp | ||
| 212 | |||
| 213 | After the installation of @tramp{} into your Emacs, you will be able to | ||
| 214 | access files on remote machines as though they were local. Access to the | ||
| 215 | remote file system for editing files, version control, and | ||
| 216 | @command{dired} are transparently enabled. | ||
| 217 | |||
| 218 | Your access to the remote machine can be with the @command{rsh}, | ||
| 219 | @command{rlogin}, @command{telnet} programs or with any similar | ||
| 220 | connection method. This connection must pass ASCII successfully to be | ||
| 221 | usable but need not be 8-bit clean. | ||
| 222 | |||
| 223 | The package provides support for @command{ssh} connections out of the | ||
| 224 | box, one of the more common uses of the package. This allows relatively | ||
| 225 | secure access to machines, especially if @command{ftp} access is | ||
| 226 | disabled. | ||
| 227 | |||
| 228 | The majority of activity carried out by @tramp{} requires only that the | ||
| 229 | remote login is possible and is carried out at the terminal. In order to | ||
| 230 | access remote files @tramp{} needs to transfer their content to the local | ||
| 231 | machine temporarily. | ||
| 232 | |||
| 233 | @tramp{} can transfer files between the machines in a variety of ways. The | ||
| 234 | details are easy to select, depending on your needs and the machines in | ||
| 235 | question. | ||
| 236 | |||
| 237 | The fastest transfer methods rely on a remote file transfer package such | ||
| 238 | as @command{rcp}, @command{scp} or @command{rsync}. The use of these | ||
| 239 | methods is only possible if the file copy command does not ask for a | ||
| 240 | password for the remote machine. | ||
| 241 | |||
| 242 | If the remote copy methods are not suitable for you, @tramp{} also | ||
| 243 | supports the use of encoded transfers directly through the shell. This | ||
| 244 | requires that the @command{mimencode} or @command{uuencode} tools are | ||
| 245 | available on the remote machine. | ||
| 246 | |||
| 247 | Within these limitations, @tramp{} is quite powerful. It is worth noting | ||
| 248 | that, as of the time of writing, it is far from a polished end-user | ||
| 249 | product. For a while yet you should expect to run into rough edges and | ||
| 250 | problems with the code now and then. | ||
| 251 | |||
| 252 | It is finished enough that the developers use it for day to day work but | ||
| 253 | the installation and setup can be a little difficult to master, as can | ||
| 254 | the terminology. | ||
| 255 | |||
| 256 | @tramp{} is still under active development and any problems you encounter, | ||
| 257 | trivial or major, should be reported to the @tramp{} developers. | ||
| 258 | @xref{Bug Reports}. | ||
| 259 | |||
| 260 | |||
| 261 | @subsubheading Behind the scenes | ||
| 262 | |||
| 263 | This section tries to explain what goes on behind the scenes when you | ||
| 264 | access a remote file through @tramp{}. | ||
| 265 | |||
| 266 | Suppose you type @kbd{C-x C-f} and enter part of an @tramp{} file name, | ||
| 267 | then hit @kbd{@key{TAB}} for completion. Suppose further that this is | ||
| 268 | the first time that @tramp{} is invoked for the host in question. Here's | ||
| 269 | what happens: | ||
| 270 | |||
| 271 | @itemize | ||
| 272 | @item | ||
| 273 | @tramp{} discovers that it needs a connection to the host. So it invokes | ||
| 274 | @command{telnet HOST} or @command{rsh HOST -l USER} or a similar tool to | ||
| 275 | connect to the remote host. Communication with this process happens | ||
| 276 | through an Emacs buffer, that is, the output from the remote end goes | ||
| 277 | into a buffer. | ||
| 278 | |||
| 279 | @item | ||
| 280 | The remote host may prompt for a login name (for @command{telnet}). The | ||
| 281 | login name is given in the file name, so @tramp{} sends the login name and | ||
| 282 | a newline. | ||
| 283 | |||
| 284 | @item | ||
| 285 | The remote host may prompt for a password or pass phrase (for | ||
| 286 | @command{rsh} or for @command{telnet} after sending the login name). | ||
| 287 | @tramp{} displays the prompt in the minibuffer, asking you for the | ||
| 288 | password or pass phrase. | ||
| 289 | |||
| 290 | You enter the password or pass phrase. @tramp{} sends it to the remote | ||
| 291 | host, followed by a newline. | ||
| 292 | |||
| 293 | @item | ||
| 294 | @tramp{} now waits for the shell prompt or for a message that the login | ||
| 295 | failed. | ||
| 296 | |||
| 297 | If @tramp{} sees neither of them after a certain period of time (a minute, | ||
| 298 | say), then it issues an error message saying that it couldn't find the | ||
| 299 | remote shell prompt and shows you what the remote host has sent. | ||
| 300 | |||
| 301 | If @tramp{} sees a `login failed' message, it tells you so, aborts the | ||
| 302 | login attempt and allows you to try again. | ||
| 303 | |||
| 304 | @item | ||
| 305 | Suppose that the login was successful and @tramp{} sees the shell prompt | ||
| 306 | from the remote host. Now @tramp{} invokes @command{/bin/sh} because | ||
| 307 | Bourne shells and C shells have different command | ||
| 308 | syntaxes.@footnote{Invoking @command{/bin/sh} will fail if your login | ||
| 309 | shell doesn't recognize @command{exec /bin/sh} as a valid command. | ||
| 310 | Maybe you use the Scheme shell @command{scsh}@dots{}} | ||
| 311 | |||
| 312 | After the Bourne shell has come up, @tramp{} sends a few commands to | ||
| 313 | ensure a good working environment. It turns off echoing, it sets the | ||
| 314 | shell prompt, and a few other things. | ||
| 315 | |||
| 316 | @item | ||
| 317 | Now the remote shell is up and it good working order. Remember, what | ||
| 318 | was supposed to happen is that @tramp{} tries to find out what files exist | ||
| 319 | on the remote host so that it can do filename completion. | ||
| 320 | |||
| 321 | So, @tramp{} basically issues @command{cd} and @command{ls} commands and | ||
| 322 | also sometimes @command{echo} with globbing. Another command that is | ||
| 323 | often used is @command{test} to find out whether a file is writable or a | ||
| 324 | directory or the like. The output of each command is parsed for the | ||
| 325 | necessary operation. | ||
| 326 | |||
| 327 | @item | ||
| 328 | Suppose you are finished with filename completion, have entered @kbd{C-x | ||
| 329 | C-f}, a full file name and hit @kbd{@key{RET}}. Now comes the time to | ||
| 330 | transfer the file contents from the remote host to the local host so | ||
| 331 | that you can edit them. | ||
| 332 | |||
| 333 | See above for an explanation of how @tramp{} transfers the file contents. | ||
| 334 | |||
| 335 | For inline transfers, @tramp{} issues a command like @command{mimencode -b | ||
| 336 | /path/to/remote/file}, waits until the output has accumulated in the | ||
| 337 | buffer that's used for communication, then decodes that output to | ||
| 338 | produce the file contents. | ||
| 339 | |||
| 340 | For out-of-band transfers, @tramp{} issues a command like @command{rcp | ||
| 341 | user@@host:/path/to/remote/file /tmp/tramp.4711} and then reads the local | ||
| 342 | temporary file @file{/tmp/tramp.4711} into a buffer and deletes the | ||
| 343 | temporary file. | ||
| 344 | |||
| 345 | @item | ||
| 346 | You now edit the buffer contents, blithely unaware of what has happened | ||
| 347 | behind the scenes. (Unless you have read this section, that is.) When | ||
| 348 | you are finished, you type @kbd{C-x C-s} to save the buffer. | ||
| 349 | |||
| 350 | @item | ||
| 351 | Again, @tramp{} transfers the file contents to the remote host either | ||
| 352 | inline or out-of-band. This is the reverse of what happens when reading | ||
| 353 | the file. | ||
| 354 | |||
| 355 | @end itemize | ||
| 356 | |||
| 357 | I hope this has provided you with a basic overview of what happens | ||
| 358 | behind the scenes when you open a file with @tramp{}. | ||
| 359 | |||
| 360 | |||
| 361 | @c For the end user | ||
| 362 | @node Obtaining @tramp{} | ||
| 363 | @chapter Obtaining @tramp{}. | ||
| 364 | |||
| 365 | @tramp{} is freely available on the Internet and the latest release may be | ||
| 366 | downloaded from | ||
| 367 | @uref{ftp://ls6-ftp.cs.uni-dortmund.de/pub/src/emacs/tramp.tar.gz}. This | ||
| 368 | release includes the full documentation and code for @tramp{}, suitable | ||
| 369 | for installation. | ||
| 370 | |||
| 371 | For the especially brave, @tramp{} is available from CVS. The CVS version | ||
| 372 | is the latest version of the code and may contain incomplete features or | ||
| 373 | new issues. Use these versions at your own risk. | ||
| 374 | |||
| 375 | Instructions for obtaining the latest development version of @tramp{} | ||
| 376 | from CVS can be found by going to the Savannah project page at | ||
| 377 | @uref{http://savannah.gnu.org/projects/tramp/} and then clicking on the | ||
| 378 | CVS link in the navigation bar at the top. Or follow the example | ||
| 379 | session below: | ||
| 380 | |||
| 381 | @example | ||
| 382 | ] @strong{cd ~/lisp} | ||
| 383 | ] @strong{cvs -d:pserver:anoncvs@@subversions.gnu.org:/cvsroot/tramp login} | ||
| 384 | |||
| 385 | (Logging in to anoncvs@@subversions.gnu.org) | ||
| 386 | CVS password: @strong{(just hit RET here)} | ||
| 387 | @dots{} | ||
| 388 | |||
| 389 | ] @strong{cvs -z3 -d:pserver:anoncvs@@subversions.gnu.org:/cvsroot/tramp co tramp} | ||
| 390 | @end example | ||
| 391 | |||
| 392 | You should now have a directory @file{~/lisp/tramp} containing the latest | ||
| 393 | version of @tramp{}. You can fetch the latest updates from the repository | ||
| 394 | by issuing the command: | ||
| 395 | |||
| 396 | @example | ||
| 397 | ] @strong{cd ~/lisp/tramp} | ||
| 398 | ] @strong{cvs update -d} | ||
| 399 | @end example | ||
| 400 | |||
| 401 | |||
| 402 | @node History | ||
| 403 | @chapter History of @tramp{} | ||
| 404 | |||
| 405 | Development was started end of November 1998. The package was called | ||
| 406 | `rssh.el', back then. It only provided one method to access a file, | ||
| 407 | using @command{ssh} to log in to a remote host and using @command{scp} | ||
| 408 | to transfer the file contents. After a while, the name was changed to | ||
| 409 | `rcp.el', and now it's @tramp{}. Along the way, many more methods for | ||
| 410 | getting a remote shell and for transferring the file contents were | ||
| 411 | added. Support for VC was added. | ||
| 412 | |||
| 413 | The most recent addition of a major feature was the multi-hop methods | ||
| 414 | added in April 2000. | ||
| 415 | |||
| 416 | |||
| 417 | @node Installation | ||
| 418 | @chapter Installing @tramp{} into Emacs or XEmacs | ||
| 419 | |||
| 420 | Installing @tramp{} into your Emacs or XEmacs is a relatively easy | ||
| 421 | process, at least compared to rebuilding your machine from scratch. ;) | ||
| 422 | |||
| 423 | Seriously though, the installation should be a fairly simple matter. | ||
| 424 | |||
| 425 | The easiest way to proceed is as follows: | ||
| 426 | |||
| 427 | @itemize | ||
| 428 | @item | ||
| 429 | Choose a directory, say @file{~/emacs/}. Change into that directory and | ||
| 430 | unpack the tarball. This will give you a directory | ||
| 431 | @file{~/emacs/tramp/} which contains subdirectories @file{lisp} for the | ||
| 432 | Lisp code and @file{texi} for the documentation. | ||
| 433 | |||
| 434 | @item | ||
| 435 | Optionally byte-compile all files in the Lisp directory, | ||
| 436 | @file{~/emacs/tramp/lisp/}, by issuing a command like the following from | ||
| 437 | the top level directory @file{~/emacs/tramp/}: | ||
| 438 | @example | ||
| 439 | make EMACS=emacs all # for Emacs users | ||
| 440 | make EMACS=xemacs all # for XEmacs users | ||
| 441 | @end example | ||
| 442 | |||
| 443 | @item | ||
| 444 | NOTE: | ||
| 445 | @example | ||
| 446 | If you run into problems running the example @command{make} | ||
| 447 | commands, don't dispare. You can still byte compile the | ||
| 448 | @file{*.el} files by opening emacs in @command{dired} | ||
| 449 | (@command{C-x d}) mode, at @file{~/tramp/lisp}. Mark the lisp | ||
| 450 | files with @command{m}, then press @command{B} to byte compile | ||
| 451 | your selections. | ||
| 452 | |||
| 453 | Something similar can be done to create the info manual. | ||
| 454 | Just cd to @file{~/emacs/tramp/texi} and load the @file{tramp.texi} | ||
| 455 | file in emacs. Then press @command{M-x makeinfo-buffer <RET>} | ||
| 456 | to generate @file{tramp.info}. | ||
| 457 | @end example | ||
| 458 | |||
| 459 | @item | ||
| 460 | Tell Emacs about the new Lisp directory and the @tramp{} package | ||
| 461 | with the following lines in @file{~/.emacs}: | ||
| 462 | @lisp | ||
| 463 | (add-to-list 'load-path "~/emacs/tramp/lisp/") | ||
| 464 | (require 'tramp) | ||
| 465 | @end lisp | ||
| 466 | |||
| 467 | @item | ||
| 468 | To be able to read the Info documentation, create a file | ||
| 469 | @file{~/emacs/tramp/texi/dir} using for example the | ||
| 470 | @command{install-info} command, and add the directory to the search | ||
| 471 | path for Info. | ||
| 472 | |||
| 473 | @item | ||
| 474 | NOTE: | ||
| 475 | @example | ||
| 476 | On systems using `gnu' @command{install-info}, the | ||
| 477 | @command{install-info} syntax is very direct and simple. One can | ||
| 478 | cd to @file{~/emacs/tramp/texi} and type: | ||
| 479 | @command{install-info tramp.info dir} | ||
| 480 | and a @file{dir} file will be created with the @tramp{} | ||
| 481 | entry. The info reader will know how to interpret it, but must | ||
| 482 | be told where to find it (see below). If you want anything fancier | ||
| 483 | you'll need to look through @command{man install-info}. | ||
| 484 | |||
| 485 | Debian gnu/linux doesn't default to `gnu' @command{install-info} and | ||
| 486 | uses its own version. This version does not create a @file{dir} file | ||
| 487 | for you from scratch. You must provide a skeleton dir file it | ||
| 488 | recognizes. One can be found in a default install at | ||
| 489 | @file{/usr/info/dir}. Copy the top of this file down to the first | ||
| 490 | occurrence of `* Menu' including that line plus one more blank line, | ||
| 491 | to your working directory @file{texi/dir}, or use the sample provided | ||
| 492 | in the @file{texi} directroy of this distribution. See | ||
| 493 | @file{texi/dir_sample} | ||
| 494 | |||
| 495 | Once a @file{dir} file is in place, this command will make the entry. | ||
| 496 | install-info --infodir=. tramp.info | ||
| 497 | If you want it in a specific category | ||
| 498 | (see @command{man install-info} for further details) | ||
| 499 | @end example | ||
| 500 | |||
| 501 | If the environment variable @env{INFOPATH} is set, add the directory | ||
| 502 | @file{~/emacs/tramp/texi/} to it. Else, add the directory to | ||
| 503 | @code{Info-default-directory-list}, as follows: | ||
| 504 | @lisp | ||
| 505 | (add-to-list 'Info-default-directory-list "~/emacs/tramp/texi/") | ||
| 506 | @end lisp | ||
| 507 | XEmacs 21 users should use @code{Info-directory-list} rather than | ||
| 508 | @code{Info-default-directory-list}. | ||
| 509 | |||
| 510 | @end itemize | ||
| 511 | |||
| 512 | |||
| 513 | For XEmacs users, the package @command{fsf-compat} must be installed. | ||
| 514 | For details on package installation, see @ref{Packages, , ,xemacs}. | ||
| 515 | @ifhtml | ||
| 516 | (If the previous link doesn't work, try the XEmacs documentation at | ||
| 517 | @uref{http://www.xemacs.org/Documentation/packageGuide.html,the XEmacs | ||
| 518 | site}.) | ||
| 519 | @end ifhtml | ||
| 520 | |||
| 521 | @node Configuration | ||
| 522 | @chapter Configuring @tramp{} for use | ||
| 523 | |||
| 524 | @tramp{} is (normally) fully functional when it is initially | ||
| 525 | installed. It is initially configured to use the @command{rsh} and | ||
| 526 | @command{rcp} programs to connect to the remote host. | ||
| 527 | |||
| 528 | On some hosts, there are problems with opening a connection. These are | ||
| 529 | related to the behavior of the remote shell. See @xref{Remote shell | ||
| 530 | setup}, for details on this. | ||
| 531 | |||
| 532 | If you do not wish to use these commands to connect to the remote host, | ||
| 533 | you should change the default connection and transfer method that @tramp | ||
| 534 | uses. There are several different methods that @tramp{} can use to | ||
| 535 | connect to remote machines and transfer files (@pxref{Connection types}). | ||
| 536 | |||
| 537 | |||
| 538 | @menu | ||
| 539 | * Connection types:: Types of connections made to remote machines. | ||
| 540 | * Inline methods:: Inline methods. | ||
| 541 | * External transfer methods:: External transfer methods. | ||
| 542 | * Multi-hop Methods:: Connecting to a remote host using multiple hops. | ||
| 543 | * Default Method:: Selecting a default method. | ||
| 544 | * Customizing Methods:: Using Non-Standard Methods. | ||
| 545 | * Remote Programs:: How @tramp{} finds and uses programs on the remote machine. | ||
| 546 | * Remote shell setup:: Remote shell setup hints. | ||
| 547 | * Windows setup hints:: Issues with Cygwin ssh. | ||
| 548 | @end menu | ||
| 549 | |||
| 550 | |||
| 551 | @node Connection types | ||
| 552 | @section Types of connections made to remote machines. | ||
| 553 | |||
| 554 | There are two basic types of transfer methods, each with its own | ||
| 555 | advantages and limitations. Both types of connection make use of a | ||
| 556 | remote shell access program such as @command{rsh}, @command{ssh} or | ||
| 557 | @command{telnet} to connect to the remote machine. | ||
| 558 | |||
| 559 | This connection is used to perform many of the operations that @tramp | ||
| 560 | requires to make the remote file system transparently accessible from | ||
| 561 | the local machine. It is only when visiting files that the methods | ||
| 562 | differ. | ||
| 563 | |||
| 564 | Loading or saving a remote file requires that the content of the file be | ||
| 565 | transfered between the two machines. The content of the file can be | ||
| 566 | transfered over the same connection used to log in to the remote machine | ||
| 567 | or the file can be transfered through another connection using a remote | ||
| 568 | copy program such as @command{rcp}, @command{scp} or @command{rsync}. | ||
| 569 | The former are called @dfn{inline methods}, the latter are called | ||
| 570 | @dfn{external transfer methods}. | ||
| 571 | |||
| 572 | The performance of the external transfer methods is generally better | ||
| 573 | than that of the inline methods. This is caused by the need to encode | ||
| 574 | and decode the data when transferring inline. | ||
| 575 | |||
| 576 | The one exception to this rule are the @command{scp} based transfer | ||
| 577 | methods. While these methods do see better performance when actually | ||
| 578 | transferring files, the overhead of the cryptographic negotiation at | ||
| 579 | startup may drown out the improvement in file transfer times. | ||
| 580 | |||
| 581 | External transfer methods do require that the remote copy command is not | ||
| 582 | interactive --- that is, the command does not prompt you for a password. | ||
| 583 | If you cannot perform remote copies without a password, you will need to | ||
| 584 | use an inline transfer method to work with @tramp{}. | ||
| 585 | |||
| 586 | A variant of the inline methods are the @dfn{multi-hop methods}. | ||
| 587 | These methods allow you to connect a remote host using a number `hops', | ||
| 588 | each of which connects to a different host. This is useful if you are | ||
| 589 | in a secured network where you need to go through a bastion host to | ||
| 590 | connect to the outside world. | ||
| 591 | |||
| 592 | |||
| 593 | @node Inline methods | ||
| 594 | @section Inline methods | ||
| 595 | |||
| 596 | The inline methods in @tramp{} are quite powerful and can work in | ||
| 597 | situations where you cannot use an external transfer program to connect. | ||
| 598 | Inline methods are the only methods that work when connecting to the | ||
| 599 | remote machine via telnet. (There are also strange inline methods which | ||
| 600 | allow you to transfer files between @emph{user identities} rather than | ||
| 601 | hosts, see below.) | ||
| 602 | |||
| 603 | These methods depend on the existence of a suitable encoding and | ||
| 604 | decoding command on remote machine. Locally, @tramp{} may be able to use | ||
| 605 | features of Emacs to decode and encode the files or it may require | ||
| 606 | access to external commands to perform that task. | ||
| 607 | |||
| 608 | @tramp{} supports the use of @command{uuencode} to transfer files. This is | ||
| 609 | @emph{not} recommended. The @command{uuencode} and @command{uudecode} | ||
| 610 | commands are not well standardized and may not function correctly or at | ||
| 611 | all on some machines, notably AIX and IRIX. These systems do not work | ||
| 612 | with @command{uuencode} at all. (But do see the note about AIX in the | ||
| 613 | documentation for @var{tramp-methods}.) | ||
| 614 | |||
| 615 | In summary, if possible use the @command{mimencode} methods to transfer | ||
| 616 | the data base64 encoded. This has the advantage of using a built-in | ||
| 617 | command in every modern Emacs, improving performance. | ||
| 618 | |||
| 619 | @itemize | ||
| 620 | @item @option{rm} --- @command{rsh} with @command{mimencode} | ||
| 621 | |||
| 622 | Connect to the remote host with @command{rsh} and use base64 encoding to | ||
| 623 | transfer files between the machines. | ||
| 624 | |||
| 625 | This requires the @command{mimencode} command that is part of the | ||
| 626 | @command{metamail} packages. This may not be installed on all remote | ||
| 627 | machines. | ||
| 628 | |||
| 629 | |||
| 630 | @item @option{sm} --- @command{ssh} with @command{mimencode} | ||
| 631 | |||
| 632 | Connect to the remote host with @command{ssh} and use base64 encoding to | ||
| 633 | transfer files between the machines. | ||
| 634 | |||
| 635 | This is identical to the previous option except that the @command{ssh} | ||
| 636 | package is used, making the connection more secure. | ||
| 637 | |||
| 638 | There are also two variants, @option{sm1} and @option{sm2} that use the | ||
| 639 | @command{ssh1} and @command{ssh2} commands explicitly. If you don't know | ||
| 640 | what these are, you do not need these options. | ||
| 641 | |||
| 642 | |||
| 643 | @item @option{tm} --- @command{telnet} with @command{mimencode} | ||
| 644 | |||
| 645 | Connect to the remote host with @command{telnet} and use base64 encoding | ||
| 646 | to transfer files between the machines. | ||
| 647 | |||
| 648 | This requires the @command{mimencode} command that is part of the | ||
| 649 | @command{metamail} packages. | ||
| 650 | |||
| 651 | |||
| 652 | @item @option{ru} --- @command{rsh} with @command{uuencode} | ||
| 653 | |||
| 654 | Connect to the remote host with @command{rsh} and use the | ||
| 655 | @command{uuencode} and @command{uudecode} commands to transfer files | ||
| 656 | between the machines. | ||
| 657 | |||
| 658 | |||
| 659 | @item @option{su} --- @command{ssh} with @command{uuencode} | ||
| 660 | |||
| 661 | Connect to the remote host with @command{ssh} and use the | ||
| 662 | @command{uuencode} and @command{uudecode} commands to transfer files | ||
| 663 | between the machines. | ||
| 664 | |||
| 665 | As with the @command{ssh} and base64 option above, this provides the | ||
| 666 | @option{su1} and @option{su2} methods to explicitly select an ssh | ||
| 667 | version. | ||
| 668 | |||
| 669 | Note that this method does not invoke the @command{su} program, see | ||
| 670 | below for methods which use that. | ||
| 671 | |||
| 672 | |||
| 673 | @item @option{tu} --- @command{telnet} with @command{uuencode} | ||
| 674 | |||
| 675 | Connect to the remote host with @command{telnet} and use the | ||
| 676 | @command{uuencode} and @command{uudecode} commands to transfer files | ||
| 677 | between the machines. | ||
| 678 | |||
| 679 | |||
| 680 | @item @option{sum} --- @command{su} with @command{mimencode} | ||
| 681 | |||
| 682 | This method does not connect to a remote host at all, rather it uses the | ||
| 683 | @command{su} program to allow you to edit files as another user. Uses | ||
| 684 | base64 encoding to transfer the file contents. | ||
| 685 | |||
| 686 | |||
| 687 | @item @option{suu} --- @command{su} with @command{uuencode} | ||
| 688 | |||
| 689 | Like @option{sum}, this uses the @command{su} program to allow you to | ||
| 690 | edit files on the local host as another user. Uses @command{uuencode} | ||
| 691 | and @command{uudecode} to transfer the file contents. | ||
| 692 | |||
| 693 | |||
| 694 | @item @option{sudm} --- @command{sudo} with @command{mimencode} | ||
| 695 | |||
| 696 | This is similar to the @option{sum} method, but it uses @command{sudo} | ||
| 697 | rather than @command{su} to become a different user. | ||
| 698 | |||
| 699 | Note that @command{sudo} must be configured to allow you to start a | ||
| 700 | shell as the user. It would be nice if it was sufficient if | ||
| 701 | @command{ls} and @command{mimencode} were allowed, but that is not easy | ||
| 702 | to implement, so I haven't got around to it, yet. | ||
| 703 | |||
| 704 | |||
| 705 | @item @option{sudu} --- @command{sudo} with @command{uuencode} | ||
| 706 | |||
| 707 | This is similar to the @option{suu} method, but it uses @command{sudo} | ||
| 708 | rather than @command{su} to become a different user. | ||
| 709 | |||
| 710 | |||
| 711 | @item @option{smx} --- @command{ssh} with @command{mimencode} | ||
| 712 | |||
| 713 | As you expect, this is similar to @option{sm}, only a little | ||
| 714 | different. Whereas @option{sm} opens a normal interactive shell on | ||
| 715 | the remote host, this option uses @command{ssh -t -t HOST -l USER | ||
| 716 | /bin/sh} tp open a connection. This is useful for users where the | ||
| 717 | normal login shell is set up to ask them a number of questions when | ||
| 718 | logging in. This procedure avoids these questions, and just gives | ||
| 719 | @tramp{} a more-or-less `standard' login shell to work with. | ||
| 720 | |||
| 721 | This is also useful for Windows users where @command{ssh}, when | ||
| 722 | invoked from an Emacs buffer, tells them that it is not allocating a | ||
| 723 | pseudo tty. When this happens, the login shell is wont to not print | ||
| 724 | any shell prompt, which confuses @tramp{} mightily. | ||
| 725 | |||
| 726 | |||
| 727 | @item @option{km} --- @command{krlogin} with @command{mimencode} | ||
| 728 | |||
| 729 | This method is also similar to @option{sm}. It only uses the | ||
| 730 | @command{krlogin -x} command to log in to the remote host. | ||
| 731 | |||
| 732 | |||
| 733 | @item @option{plinku} --- @command{plink} with @command{uuencode} | ||
| 734 | |||
| 735 | This method is mostly interesting for Windows users using the PuTTY | ||
| 736 | implementation of SSH. It uses @command{plink -ssh} to log in to the | ||
| 737 | remote host. | ||
| 738 | |||
| 739 | CCC: Do we have to connect to the remote host once from the command | ||
| 740 | line to accept the SSH key? Maybe this can be made automatic? | ||
| 741 | |||
| 742 | @item @option{plinkm} --- @command{plink} with @command{mimencode} | ||
| 743 | |||
| 744 | Like @option{plinku}, but uses base64 encoding instead of uu encoding. | ||
| 745 | |||
| 746 | @end itemize | ||
| 747 | |||
| 748 | |||
| 749 | |||
| 750 | @node External transfer methods | ||
| 751 | @section External transfer methods | ||
| 752 | |||
| 753 | The external transfer methods operate through multiple channels, using | ||
| 754 | the remote shell connection for many actions while delegating file | ||
| 755 | transfers to an external transfer utility. | ||
| 756 | |||
| 757 | This saves the overhead of encoding and decoding that multiplexing the | ||
| 758 | transfer through the one connection has with the inline methods. | ||
| 759 | |||
| 760 | If you want to use an external transfer method you @emph{must} be able | ||
| 761 | to execute the transfer utility to copy files to and from the remote | ||
| 762 | machine without any interaction. | ||
| 763 | |||
| 764 | This means that you will need to use @command{ssh-agent} if you use the | ||
| 765 | @command{scp} program for transfers, or maybe your version of | ||
| 766 | @command{scp} accepts a password on the command line.@footnote{PuTTY's | ||
| 767 | @command{pscp} allows you to specify the password on the command line.} | ||
| 768 | If you use @command{rsync} via @command{ssh} then the same rule must | ||
| 769 | apply to that connection. | ||
| 770 | |||
| 771 | If you cannot get @command{scp} to run without asking for a password but | ||
| 772 | would still like to use @command{ssh} to secure your connection, have a | ||
| 773 | look at the @command{ssh} based inline methods. | ||
| 774 | |||
| 775 | |||
| 776 | @itemize | ||
| 777 | @item @option{rcp} --- @command{rsh} and @command{rcp} | ||
| 778 | |||
| 779 | This method uses the @command{rsh} and @command{rcp} commands to connect | ||
| 780 | to the remote machine and transfer files. This is probably the fastest | ||
| 781 | connection method available. | ||
| 782 | |||
| 783 | |||
| 784 | @item @option{scp} --- @command{ssh} and @command{scp} | ||
| 785 | |||
| 786 | Using @command{ssh} to connect to the remote host and @command{scp} to | ||
| 787 | transfer files between the machines is the best method for securely | ||
| 788 | connecting to a remote machine and accessing files. | ||
| 789 | |||
| 790 | The performance of this option is also quite good. It may be slower than | ||
| 791 | the inline methods when you often open and close small files however. | ||
| 792 | The cost of the cryptographic handshake at the start of an @command{scp} | ||
| 793 | session can begin to absorb the advantage that the lack of encoding and | ||
| 794 | decoding presents. | ||
| 795 | |||
| 796 | |||
| 797 | @item @option{rsync} --- @command{ssh} and @command{rsync} | ||
| 798 | |||
| 799 | Using the @command{ssh} command to connect securely to the remote | ||
| 800 | machine and the @command{rsync} command to transfer files is almost | ||
| 801 | identical to the @option{scp} method. | ||
| 802 | |||
| 803 | While @command{rsync} performs much better than @command{scp} when | ||
| 804 | transferring files that exist on both hosts, this advantage is lost if | ||
| 805 | the file exists only on one side of the connection. | ||
| 806 | |||
| 807 | The @command{rsync} based method may be considerably faster than the | ||
| 808 | @command{rcp} based methods when writing to the remote system. Reading | ||
| 809 | files to the local machine is no faster than with a direct copy. | ||
| 810 | |||
| 811 | |||
| 812 | @item @option{scpx} --- @command{ssh} and @command{scp} | ||
| 813 | |||
| 814 | As you expect, this is similar to @option{scp}, only a little | ||
| 815 | different. Whereas @option{scp} opens a normal interactive shell on the | ||
| 816 | remote host, this option uses @command{ssh -t -t HOST -l USER /bin/sh} to | ||
| 817 | open a connection. This is useful for users where the normal login | ||
| 818 | shell is set up to ask them a number of questions when logging in. This | ||
| 819 | procedure avoids these questions, and just gives @tramp{} a more-or-less | ||
| 820 | `standard' login shell to work with. | ||
| 821 | |||
| 822 | This is also useful for Windows users where @command{ssh}, when | ||
| 823 | invoked from an Emacs buffer, tells them that it is not allocating a | ||
| 824 | pseudo tty. When this happens, the login shell is wont to not print | ||
| 825 | any shell prompt, which confuses @tramp{} mightily. | ||
| 826 | |||
| 827 | |||
| 828 | @item @option{pscp} --- @command{plink} and @command{pscp} | ||
| 829 | |||
| 830 | This method is similar to @option{scp}, but it uses the | ||
| 831 | @command{plink} command to connect to the remote host, and it uses | ||
| 832 | @command{pscp} for transferring the files. These programs are part | ||
| 833 | of PuTTY, an SSH implementation for Windows. | ||
| 834 | |||
| 835 | |||
| 836 | @item @option{fcp} --- @command{fsh} and @command{fcp} | ||
| 837 | |||
| 838 | This method is similar to @option{scp}, but it uses the @command{fsh} | ||
| 839 | command to connect to the remote host, and it uses @command{fcp} for | ||
| 840 | transferring the files. @command{fsh/fcp} are a front-end for | ||
| 841 | @command{ssh} which allow for reusing the same @command{ssh} session | ||
| 842 | for submitting several commands. This avoids the startup overhead of | ||
| 843 | @command{scp} (which has to establish a secure connection whenever it | ||
| 844 | is called). Note, however, that you can also use one of the inline | ||
| 845 | methods to achieve a similar effect. | ||
| 846 | |||
| 847 | This method uses the command @command{fsh HOST -l USER /bin/sh -i} to | ||
| 848 | establish the connection, it does not work to just say @command{fsh | ||
| 849 | HOST -l USER}. | ||
| 850 | |||
| 851 | @end itemize | ||
| 852 | |||
| 853 | @node Multi-hop Methods | ||
| 854 | @section Connecting to a remote host using multiple hops | ||
| 855 | |||
| 856 | Sometimes, the methods described before are not sufficient. Sometimes, | ||
| 857 | it is not possible to connect to a remote host using a simple command. | ||
| 858 | For example, if you are in a secured network, you might have to log in | ||
| 859 | to a `bastion host' first before you can connect to the outside world. | ||
| 860 | Of course, the target host may also require a bastion host. The format | ||
| 861 | of multi-hop filenames is slightly different than the format of normal | ||
| 862 | @tramp{} methods. | ||
| 863 | |||
| 864 | A multi-hop file name specifies a method, a number of hops, and a path | ||
| 865 | name on the remote system. The method specifies how the file is | ||
| 866 | transferred through the inline connection. The following two multi-hop | ||
| 867 | methods are available: | ||
| 868 | |||
| 869 | @itemize | ||
| 870 | @item @option{multi} --- base64 encoding with @command{mimencode} | ||
| 871 | |||
| 872 | The file is transferred through the connection in base64 encoding. Uses | ||
| 873 | the @command{mimencode} program for doing encoding and decoding, but | ||
| 874 | uses an Emacs internal implementation on the local host if available. | ||
| 875 | |||
| 876 | @item @option{multiu} --- use commands @command{uuencode} and @command{uudecode} | ||
| 877 | |||
| 878 | The file is transferred through the connection in `uu' encoding. Uses | ||
| 879 | the @command{uuencode} and @command{uudecode} programs for encoding and | ||
| 880 | decoding, but uses a Lisp implementation for decoding on the local host | ||
| 881 | if available. | ||
| 882 | |||
| 883 | @end itemize | ||
| 884 | |||
| 885 | Each hop consists of a @dfn{hop method} specification, a user name and a | ||
| 886 | host name. The following hop methods are (currently) available: | ||
| 887 | |||
| 888 | @itemize | ||
| 889 | @item @option{telnet} | ||
| 890 | |||
| 891 | Uses the well-known @command{telnet} program to connect to the host. | ||
| 892 | Whereas user name and host name are supplied in the file name, the | ||
| 893 | user is queried for the password. | ||
| 894 | |||
| 895 | @item @option{rsh} | ||
| 896 | |||
| 897 | This uses @command{rsh} to connect to the host. You do not need to | ||
| 898 | enter a password unless @command{rsh} explicitly asks for it. | ||
| 899 | |||
| 900 | @item @option{ssh} | ||
| 901 | |||
| 902 | This uses @command{ssh} to connect to the host. You might have to enter | ||
| 903 | a password or a pass phrase. | ||
| 904 | |||
| 905 | @item @option{su} | ||
| 906 | |||
| 907 | This method does not actually contact a different host, but it allows | ||
| 908 | you to become a different user on the host you're currently on. This | ||
| 909 | might be useful if you want to edit files as root, but the remote host | ||
| 910 | does not allow remote root logins. In this case you can use | ||
| 911 | @option{telnet}, @option{rsh} or @option{ssh} to connect to the | ||
| 912 | remote host as a non-root user, then use an @option{su} hop to become | ||
| 913 | root. But @option{su} need not be the last hop in a sequence, you could | ||
| 914 | also use it somewhere in the middle, if the need arises. | ||
| 915 | |||
| 916 | Even though you @emph{must} specify both user and host with a | ||
| 917 | @option{su} hop, the host name is ignored and only the user name is | ||
| 918 | used. | ||
| 919 | |||
| 920 | @item @option{sudo} | ||
| 921 | |||
| 922 | This is similar to the @option{su} hop, except that it uses | ||
| 923 | @command{sudo} rather than @command{su} to become a different user. | ||
| 924 | |||
| 925 | @end itemize | ||
| 926 | |||
| 927 | Some people might wish to use port forwarding with @code{ssh} or maybe | ||
| 928 | they have to use a nonstandard port. This can be accomplished by | ||
| 929 | putting a stanza in @file{~/.ssh/config} for the account which specifies | ||
| 930 | a different port number for a certain host name. But it can also be | ||
| 931 | accomplished within Tramp, by adding a multi-hop method. For example: | ||
| 932 | |||
| 933 | @lisp | ||
| 934 | (add-to-list 'tramp-multi-connection-function-alist | ||
| 935 | '("sshf" tramp-multi-connect-rlogin "ssh %h -l %u -p 4400%n")) | ||
| 936 | @end lisp | ||
| 937 | |||
| 938 | Now you can use a @code{sshf} hop which connects to port 4400 instead of | ||
| 939 | the standard port. | ||
| 940 | |||
| 941 | |||
| 942 | @node Default Method | ||
| 943 | @section Selecting a default method | ||
| 944 | |||
| 945 | When you select an appropriate transfer method for your typical usage | ||
| 946 | you should set the variable @var{tramp-default-method} to reflect that | ||
| 947 | choice. This variable controls which method will be used when a method | ||
| 948 | is not specified in the @tramp{} file path. For example: | ||
| 949 | |||
| 950 | @lisp | ||
| 951 | (setq tramp-default-method "scp") | ||
| 952 | @end lisp | ||
| 953 | |||
| 954 | External transfer methods are normally preferable to inline transfer | ||
| 955 | methods, giving better performance. They may not be useful if you use | ||
| 956 | many remote machines where you cannot log in without a password. | ||
| 957 | |||
| 958 | @xref{Inline methods}. | ||
| 959 | @xref{External transfer methods}. | ||
| 960 | @xref{Multi-hop Methods}. | ||
| 961 | |||
| 962 | Another consideration with the selection of transfer methods is the | ||
| 963 | environment you will use them in and, especially when used over the | ||
| 964 | Internet, the security implications of your preferred method. | ||
| 965 | |||
| 966 | The @command{rsh} and @command{telnet} methods send your password as | ||
| 967 | plain text as you log in to the remote machine, as well as transferring | ||
| 968 | the files in such a way that the content can easily be read from other | ||
| 969 | machines. | ||
| 970 | |||
| 971 | If you need to connect to remote systems that are accessible from the | ||
| 972 | Internet, you should give serious thought to using @command{ssh} based | ||
| 973 | methods to connect. These provide a much higher level of security, | ||
| 974 | making it a non-trivial exercise for someone to obtain your password or | ||
| 975 | read the content of the files you are editing. | ||
| 976 | |||
| 977 | @node Customizing Methods | ||
| 978 | @section Using Non-Standard Methods | ||
| 979 | |||
| 980 | There is a variable @code{tramp-methods} which you can change if the | ||
| 981 | predefined methods don't seem right. | ||
| 982 | |||
| 983 | For the time being, I'll refer you to the Lisp documentation of that | ||
| 984 | variable, accessible with @kbd{C-h v tramp-methods @key{RET}}. | ||
| 985 | |||
| 986 | |||
| 987 | @node Remote Programs | ||
| 988 | @section How @tramp{} finds and uses programs on the remote machine. | ||
| 989 | |||
| 990 | @tramp{} depends on a number of programs on the remote host in order to | ||
| 991 | function, including @command{ls}, @command{test}, @command{find} and | ||
| 992 | @command{cat}. | ||
| 993 | |||
| 994 | In addition to these required tools, there are various tools that may be | ||
| 995 | required based on the connection method. See @ref{Inline methods} and | ||
| 996 | @ref{External transfer methods} for details on these. | ||
| 997 | |||
| 998 | Certain other tools, such as @command{perl} (or @command{perl5}) and | ||
| 999 | @command{grep} will be used if they can be found. When they are | ||
| 1000 | available, they are used to improve the performance and accuracy of | ||
| 1001 | remote file access. | ||
| 1002 | |||
| 1003 | When @tramp{} connects to the remote machine, it searches for the | ||
| 1004 | programs that it can use. The variable @var{tramp-remote-path} controls | ||
| 1005 | the directories searched on the remote machine. | ||
| 1006 | |||
| 1007 | By default, this is set to a reasonable set of defaults for most | ||
| 1008 | machines. It is possible, however, that your local (or remote ;) system | ||
| 1009 | administrator has put the tools you want in some obscure local | ||
| 1010 | directory. | ||
| 1011 | |||
| 1012 | In this case, you can still use them with @tramp{}. You simply need to | ||
| 1013 | add code to your @file{.emacs} to add the directory to the remote path. | ||
| 1014 | This will then be searched by @tramp{} when you connect and the software | ||
| 1015 | found. | ||
| 1016 | |||
| 1017 | To add a directory to the remote search path, you could use code such | ||
| 1018 | as: | ||
| 1019 | |||
| 1020 | @example | ||
| 1021 | (require 'tramp) @i{; @tramp{} must be loaded before this} | ||
| 1022 | @i{; happens.} | ||
| 1023 | |||
| 1024 | @i{; We have @command{perl} in "/usr/local/perl"} | ||
| 1025 | (add-to-list 'tramp-remote-path "/usr/local/perl") | ||
| 1026 | @end example | ||
| 1027 | |||
| 1028 | @node Remote shell setup | ||
| 1029 | @comment node-name, next, previous, up | ||
| 1030 | @section Remote shell setup hints | ||
| 1031 | |||
| 1032 | As explained in the @ref{Overview} section, @tramp{} connects to the | ||
| 1033 | remote host and talks to the shell it finds there. Of course, when you | ||
| 1034 | log in, the shell executes its init files. Suppose your init file | ||
| 1035 | requires you to enter the birthdate of your mother; clearly @tramp{} | ||
| 1036 | does not know this and hence fails to log you in to that host. | ||
| 1037 | |||
| 1038 | There are different possible strategies for pursuing this problem. One | ||
| 1039 | strategy is to enable @tramp{} to deal with all possible situations. | ||
| 1040 | This is a losing battle, since it is not possible to deal with | ||
| 1041 | @emph{all} situations. The other strategy is to require you to set up | ||
| 1042 | the remote host such that it behaves like @tramp{} expect. This might | ||
| 1043 | be inconvenient because you have to invest a lot of effort into shell | ||
| 1044 | setup before you can begin to use @tramp{}. | ||
| 1045 | |||
| 1046 | The package, therefore, pursues a combined approach. It tries to figure | ||
| 1047 | out some of the more common setups, and only requires you to avoid | ||
| 1048 | really exotic stuff. For example, it looks through a list of | ||
| 1049 | directories to find some programs on the remote host. And also, it | ||
| 1050 | knows that it is not obvious how to check whether a file exist, and | ||
| 1051 | therefore it tries different possibilities. (On some hosts and shells, | ||
| 1052 | the command @code{test -e} does the trick, on some hosts the shell | ||
| 1053 | builtin doesn't work but the program @code{/usr/bin/test -e} or | ||
| 1054 | @code{/bin/test -e} works. And on still other hosts, @code{ls -d} is | ||
| 1055 | the right way to do this.) | ||
| 1056 | |||
| 1057 | Below you find a discussion of a few things that @tramp{} does not deal | ||
| 1058 | with, and that you therefore have to set up correctly. | ||
| 1059 | |||
| 1060 | @itemize | ||
| 1061 | @item @code{shell-prompt-pattern} | ||
| 1062 | |||
| 1063 | @vindex shell-prompt-pattern | ||
| 1064 | After logging in to the remote host, @tramp{} has to wait for the remote | ||
| 1065 | shell startup to finish before it can send commands to the remote | ||
| 1066 | shell. The strategy here is to wait for the shell prompt. In order to | ||
| 1067 | recognize the shell prompt, the variable @code{shell-prompt-pattern} has | ||
| 1068 | to be set correctly to recognize the shell prompt on the remote host. | ||
| 1069 | |||
| 1070 | @item @code{tset} and other questions | ||
| 1071 | |||
| 1072 | Some people invoke the @code{tset} program from their shell startup | ||
| 1073 | scripts which asks the user about the terminal type of the shell. Maybe | ||
| 1074 | some shells ask other questions when they are started. @tramp{} does | ||
| 1075 | not know how to answer these questions. (A facility for enabling | ||
| 1076 | @tramp{} to answer these questions is planned for some future version, | ||
| 1077 | but don't hold your breath.) | ||
| 1078 | |||
| 1079 | Therefore, you should take care that the shell does not ask any | ||
| 1080 | questions when invoked from @tramp{}. You can do this by checking the | ||
| 1081 | @code{TERM} environment variable, it will be set to @code{dumb} when | ||
| 1082 | connecting. | ||
| 1083 | |||
| 1084 | @vindex tramp-terminal-type | ||
| 1085 | The variable @code{tramp-terminal-type} can be used to change this value | ||
| 1086 | @code{dumb}. | ||
| 1087 | |||
| 1088 | @end itemize | ||
| 1089 | |||
| 1090 | |||
| 1091 | @node Windows setup hints | ||
| 1092 | @section Issues with Cygwin ssh | ||
| 1093 | |||
| 1094 | This section needs a lot of work! Please help. | ||
| 1095 | |||
| 1096 | If you use the Cygwin installation of ssh (you have to explicitly select | ||
| 1097 | it in the installer), then it should work out of the box to just select | ||
| 1098 | @code{smx} as the connection method. You can find information about | ||
| 1099 | setting up Cygwin in their FAQ at @uref{http://cygwin.com/faq/}. | ||
| 1100 | |||
| 1101 | |||
| 1102 | @node Usage | ||
| 1103 | @chapter Using @tramp | ||
| 1104 | |||
| 1105 | Once you have installed @tramp{} it will operate fairly transparently. You | ||
| 1106 | will be able to access files on any remote machine that you can log in | ||
| 1107 | to as though they were local. | ||
| 1108 | |||
| 1109 | Files are specified to @tramp{} using a formalized syntax specifying the | ||
| 1110 | details of the system to connect to. This is similar to the syntax used | ||
| 1111 | by the @command{EFS} and @command{ange-ftp} packages. | ||
| 1112 | |||
| 1113 | |||
| 1114 | @menu | ||
| 1115 | * Filename Syntax:: @tramp{} filename conventions. | ||
| 1116 | * Multi-hop filename syntax:: Multi-hop filename conventions | ||
| 1117 | * Dired:: Dired and filename completion. | ||
| 1118 | @end menu | ||
| 1119 | |||
| 1120 | |||
| 1121 | @node Filename Syntax | ||
| 1122 | @section @tramp{} filename conventions | ||
| 1123 | |||
| 1124 | To access the file <path> on the remote machine <machine> you would | ||
| 1125 | specify the filename @file{/[<machine>]<path>}. (The square brackets | ||
| 1126 | are part of the file name.) This will connect to <machine> and transfer | ||
| 1127 | the file using the default method. @xref{Default Method}. | ||
| 1128 | |||
| 1129 | Some examples of @tramp{} filenames are: | ||
| 1130 | |||
| 1131 | @table @file | ||
| 1132 | @item /[melancholia].emacs | ||
| 1133 | Edit the file @file{.emacs} in your home directory on the machine | ||
| 1134 | @code{melancholia}. | ||
| 1135 | |||
| 1136 | @item /[melancholia.danann.net].emacs | ||
| 1137 | This edits the same file, using the fully qualified domain name of | ||
| 1138 | the machine. | ||
| 1139 | |||
| 1140 | @item /[melancholia]~/.emacs | ||
| 1141 | This also edits the same file --- the @file{~} is expanded to your | ||
| 1142 | home directory on the remote machine, just like it is locally. | ||
| 1143 | |||
| 1144 | @item /[melancholia]~daniel/.emacs | ||
| 1145 | This edits the file @file{.emacs} in the home directory of the user | ||
| 1146 | @code{daniel} on the machine @code{melancholia}. The @file{~<user>} | ||
| 1147 | construct is expanded to the home directory of that user on the remote | ||
| 1148 | machine. | ||
| 1149 | |||
| 1150 | @item /[melancholia]/etc/squid.conf | ||
| 1151 | This edits the file @file{/etc/squid.conf} on the machine | ||
| 1152 | @code{melancholia}. | ||
| 1153 | |||
| 1154 | @end table | ||
| 1155 | |||
| 1156 | |||
| 1157 | Unless you specify a different name to use, @tramp{} will use the current | ||
| 1158 | local user name as the remote user name to log in with. If you need to | ||
| 1159 | log in as a different user, you can specify the user name as part of the | ||
| 1160 | filename. | ||
| 1161 | |||
| 1162 | To log in to the remote machine as a specific user, you use the syntax | ||
| 1163 | @file{/[<user>@@<machine>]/path/to.file}. That means that connecting to | ||
| 1164 | @code{melancholia} as @code{daniel} and editing @file{.emacs} in your | ||
| 1165 | home directory you would specify @file{/[daniel@@melancholia].emacs}. | ||
| 1166 | |||
| 1167 | |||
| 1168 | It is also possible to specify other file transfer methods | ||
| 1169 | (@pxref{Default Method}) as part of the filename. This is done by | ||
| 1170 | replacing the initial @file{/[} with @file{/[<method>/}. (Note the | ||
| 1171 | trailing slash!) The user, machine and file specification remain the | ||
| 1172 | same. | ||
| 1173 | |||
| 1174 | So, to connect to the machine @code{melancholia} as @code{daniel}, using | ||
| 1175 | the @option{su} method to transfer files, and edit @file{.emacs} in my | ||
| 1176 | home directory I would specify the filename | ||
| 1177 | @file{/[su/daniel@@melancholia].emacs}. | ||
| 1178 | |||
| 1179 | |||
| 1180 | @node Multi-hop filename syntax | ||
| 1181 | @section Multi-hop filename conventions | ||
| 1182 | |||
| 1183 | The syntax of multi-hop file names is necessarily slightly different | ||
| 1184 | than the syntax of other @tramp{} file names. Here's an example multi-hop | ||
| 1185 | file name: | ||
| 1186 | |||
| 1187 | @file{/[multi/rsh:out@@gate/telnet:kai@@real.host]/path/to.file} | ||
| 1188 | |||
| 1189 | This is quite a mouthful. So let's go through it step by step. The | ||
| 1190 | file name consists of three parts, separated by slashes and square | ||
| 1191 | brackets. The first part is @file{/[multi}, the method specification. | ||
| 1192 | The second part is @file{rsh:out@@gate/telnet:kai@@real.host} and | ||
| 1193 | specifies the hops. (Yes, the second part may contain even more | ||
| 1194 | slashes, so that's why this file name has more than two colons in it.) | ||
| 1195 | The final part is @file{/path/to.file} and specifies the file name on | ||
| 1196 | the remote host. | ||
| 1197 | |||
| 1198 | The first part and the final part should be clear. @ref{Multi-hop | ||
| 1199 | Methods}, for a list of alternatives for the method specification. | ||
| 1200 | |||
| 1201 | The second part can be subdivided again into components, so-called hops. | ||
| 1202 | In the above file name, there are two hops, @file{rsh:out@@gate} and | ||
| 1203 | @file{telnet:kai@@real.host}. | ||
| 1204 | |||
| 1205 | Each hop can @emph{again} be subdivided into (three) components, the | ||
| 1206 | @dfn{hop method}, the @dfn{user name} and the @dfn{host name}. The | ||
| 1207 | meaning of the second and third component should be clear, and the hop | ||
| 1208 | method says what program to use to perform that hop. | ||
| 1209 | |||
| 1210 | The first hop, @file{rsh:out@@gate}, says to use @command{rsh} to log in | ||
| 1211 | as user @code{out} to the host @code{gate}. Starting at that host, the | ||
| 1212 | second hop, @file{telnet:kai@@real.host}, says to use @command{telnet} | ||
| 1213 | to log in as user @code{kai} to host @code{real.host}. | ||
| 1214 | |||
| 1215 | @xref{Multi-hop Methods}, for a list of possible hop method values. The | ||
| 1216 | variable @var{tramp-multi-connection-function-alist} contains the list of | ||
| 1217 | possible hop methods and information on how to execute them, should you | ||
| 1218 | want to add your own. | ||
| 1219 | |||
| 1220 | |||
| 1221 | @node Dired | ||
| 1222 | @section Dired and filename completion | ||
| 1223 | |||
| 1224 | @tramp{} works transparently with dired, enabling you to use this powerful | ||
| 1225 | file management tool to manage files on any machine you have access to | ||
| 1226 | over the Internet. | ||
| 1227 | |||
| 1228 | Filename completion also works with @tramp{} for files on remote machines | ||
| 1229 | although there is no completion for user names or machine names at this | ||
| 1230 | stage. | ||
| 1231 | |||
| 1232 | As filename completion needs to fetch the listing of files from the | ||
| 1233 | remote machine, this feature is sometimes fairly slow. As @tramp{} does not | ||
| 1234 | yet cache the results of directory listing, there is no gain in | ||
| 1235 | performance the second time you complete filenames. | ||
| 1236 | |||
| 1237 | If you need to browse a directory tree, Dired is a better choice, at | ||
| 1238 | present, than filename completion. Dired has its own cache mechanism | ||
| 1239 | and will only fetch the directory listing once. | ||
| 1240 | |||
| 1241 | |||
| 1242 | @node Bug Reports | ||
| 1243 | @chapter Reporting Bugs and Problems | ||
| 1244 | |||
| 1245 | Bugs and problems with @tramp{} are actively worked on by the development | ||
| 1246 | team. Feature requests and suggestions are also more than welcome. | ||
| 1247 | |||
| 1248 | The @tramp{} mailing list is a great place to get information on working | ||
| 1249 | with @tramp{}, solving problems and general discussion and advice on topics | ||
| 1250 | relating to the package. | ||
| 1251 | |||
| 1252 | The mailing list is at @email{tramp-devel@@mail.freesoftware.fsf.org}. | ||
| 1253 | Messages sent to this address go to all the subscribers. This is | ||
| 1254 | @emph{not} the address to send subscription requests to. | ||
| 1255 | |||
| 1256 | For help on subscribing to the list, send mail to the administrative | ||
| 1257 | address, @email{tramp-devel-request@@mail.freesoftware.fsf.org}, with the | ||
| 1258 | subject @samp{help}. | ||
| 1259 | |||
| 1260 | To report a bug in @tramp{}, you should execute @kbd{M-x tramp-bug}. This | ||
| 1261 | will automatically generate a buffer with the details of your system and | ||
| 1262 | @tramp{} version. | ||
| 1263 | |||
| 1264 | When submitting a bug report, please try to describe in excruciating | ||
| 1265 | detail the steps required to reproduce the problem, the setup of the | ||
| 1266 | remote machine and any special conditions that exist. | ||
| 1267 | |||
| 1268 | If you can identify a minimal test case that reproduces the problem, | ||
| 1269 | include that with your bug report. This will make it much easier for the | ||
| 1270 | development team to analyze and correct the problem. | ||
| 1271 | |||
| 1272 | @node Frequently Asked Questions | ||
| 1273 | @chapter Frequently Asked Questions | ||
| 1274 | |||
| 1275 | @itemize @bullet | ||
| 1276 | @item Where can I get the latest @tramp{}? | ||
| 1277 | |||
| 1278 | @tramp{} is available at | ||
| 1279 | @uref{ftp://ls6-ftp.cs.uni-dortmund.de/pub/src/emacs/tramp.tar.gz}. | ||
| 1280 | There is also a Savannah project page, at | ||
| 1281 | @uref{https://savannah.gnu.org/projects/tramp/}. | ||
| 1282 | |||
| 1283 | |||
| 1284 | @item Which systems does it work on? | ||
| 1285 | |||
| 1286 | The package has been used successfully on Emacs 20 and Emacs 21, as well | ||
| 1287 | as XEmacs 21. XEmacs 20 is more problematic, see the notes in | ||
| 1288 | @file{tramp.el}. I don't think anybody has really tried it on Emacs 19. | ||
| 1289 | |||
| 1290 | The package was intended to work on Unix, and it really expects a | ||
| 1291 | Unix-like system on the remote end, but some people seemed to have some | ||
| 1292 | success getting it to work on NT Emacs. | ||
| 1293 | |||
| 1294 | There are some informations on Tramp on NT at the following URL; many | ||
| 1295 | thanks to Joe Stoy for providing the information: | ||
| 1296 | @uref{ftp://ftp.comlab.ox.ac.uk/tmp/Joe.Stoy/} | ||
| 1297 | |||
| 1298 | The above mostly contains patches to old ssh versions; Tom Roche has a | ||
| 1299 | Web page with instructions: | ||
| 1300 | @uref{http://www4.ncsu.edu/~tlroche/plinkTramp.html} | ||
| 1301 | |||
| 1302 | ??? Is the XEmacs info correct? | ||
| 1303 | |||
| 1304 | ??? Can somebody provide some information for getting it to work on NT | ||
| 1305 | Emacs? I think there was some issue with @command{ssh}? | ||
| 1306 | |||
| 1307 | |||
| 1308 | @item I can't stop EFS starting with XEmacs | ||
| 1309 | |||
| 1310 | Not all the older versions of @tramp{} supported XEmacs correctly. The | ||
| 1311 | first thing to do is to make sure that you have the latest version of | ||
| 1312 | @tramp{} installed. | ||
| 1313 | |||
| 1314 | If you do, please try and find out exactly the conditions required for | ||
| 1315 | the @code{EFS} handlers to fire. If you can, putting a breakpoint on | ||
| 1316 | @code{efs-ftp-path} and sending in the stack trace along with your bug | ||
| 1317 | report would make it easier for the developers to work out what is going | ||
| 1318 | wrong. | ||
| 1319 | |||
| 1320 | |||
| 1321 | @item File name completion does not work with @tramp{} | ||
| 1322 | |||
| 1323 | When you log in to the remote machine, do you see the output of | ||
| 1324 | @command{ls} in color? If so, this may be the cause of your problems. | ||
| 1325 | |||
| 1326 | @command{ls} outputs @acronym{ANSI} escape sequences that your terminal | ||
| 1327 | emulator interprets to set the colors. These escape sequences will | ||
| 1328 | confuse @tramp{} however. | ||
| 1329 | |||
| 1330 | In your @file{.bashrc}, @file{.profile} or equivalent on the remote | ||
| 1331 | machine you probably have an alias configured that adds the option | ||
| 1332 | @option{--color=yes} or @option{--color=auto}. | ||
| 1333 | |||
| 1334 | You should remove that alias and ensure that a new login @emph{does not} | ||
| 1335 | display the output of @command{ls} in color. If you still cannot use | ||
| 1336 | filename completion, report a bug to the @tramp{} developers. | ||
| 1337 | |||
| 1338 | |||
| 1339 | @item File name completion does not work in large directories | ||
| 1340 | |||
| 1341 | @tramp{} uses globbing for some operations. (Globbing means to use the | ||
| 1342 | shell to expand wildcards such as `*.c'.) This might create long | ||
| 1343 | command lines, especially in directories with many files. Some shell | ||
| 1344 | choke on long command lines, or don't cope well with the globbing | ||
| 1345 | itself. | ||
| 1346 | |||
| 1347 | If you have a large directory on the remote end, you may wish to execute | ||
| 1348 | a command like @command{ls -d * ..?* > /dev/null} and see if it hangs. | ||
| 1349 | Note that you must first start the right shell, which might be | ||
| 1350 | @command{/bin/sh}, @command{ksh} or @command{bash}, depending on which | ||
| 1351 | of those supports tilde expansion. | ||
| 1352 | |||
| 1353 | |||
| 1354 | @item What kinds of systems does @tramp{} work on | ||
| 1355 | |||
| 1356 | @tramp{} really expects the remote system to be a Unix-like system. The | ||
| 1357 | local system should preferably be Unix-like, as well, but @tramp{} might | ||
| 1358 | work on NT with some tweaking. | ||
| 1359 | |||
| 1360 | |||
| 1361 | @item How can I get notified when @tramp{} file transfers are complete? | ||
| 1362 | |||
| 1363 | The following snippet can be put in your @file{~/.emacs} file. It makes | ||
| 1364 | Emacs beep after reading from or writing to the remote host. | ||
| 1365 | |||
| 1366 | @lisp | ||
| 1367 | (defadvice tramp-handle-write-region | ||
| 1368 | (after tramp-write-beep-advice activate) | ||
| 1369 | " make tramp beep after writing a file." | ||
| 1370 | (interactive) | ||
| 1371 | (beep)) | ||
| 1372 | (defadvice tramp-handle-do-copy-or-rename-file | ||
| 1373 | (after tramp-copy-beep-advice activate) | ||
| 1374 | " make tramp beep after copying a file." | ||
| 1375 | (interactive) | ||
| 1376 | (beep)) | ||
| 1377 | (defadvice tramp-handle-insert-file-contents | ||
| 1378 | (after tramp-copy-beep-advice activate) | ||
| 1379 | " make tramp beep after copying a file." | ||
| 1380 | (interactive) | ||
| 1381 | (beep)) | ||
| 1382 | @end lisp | ||
| 1383 | |||
| 1384 | |||
| 1385 | @item There's this @file{~/.sh_history} file on the remote host which | ||
| 1386 | keeps growing and growing. What's that? | ||
| 1387 | |||
| 1388 | Sometimes, @tramp{} starts @code{ksh} on the remote host for tilde | ||
| 1389 | expansion. Maybe @code{ksh} saves the history by default. @tramp{} | ||
| 1390 | tries to turn off saving the history, but maybe you have to help. For | ||
| 1391 | example, you could put this in your @file{.kshrc}: | ||
| 1392 | |||
| 1393 | @example | ||
| 1394 | if [ -f $HOME/.sh_history ] ; then | ||
| 1395 | /bin/rm $HOME/.sh_history | ||
| 1396 | fi | ||
| 1397 | if [ "$@{HISTFILE-unset@}" != "unset" ] ; then | ||
| 1398 | unset HISTFILE | ||
| 1399 | fi | ||
| 1400 | if [ "$@{HISTSIZE-unset@}" != "unset" ] ; then | ||
| 1401 | unset HISTSIZE | ||
| 1402 | fi | ||
| 1403 | @end example | ||
| 1404 | |||
| 1405 | @end itemize | ||
| 1406 | |||
| 1407 | |||
| 1408 | @c For the developer | ||
| 1409 | @node Version Control | ||
| 1410 | @chapter The inner workings of remote version control | ||
| 1411 | |||
| 1412 | Unlike EFS and ange-ftp, @tramp{} has full shell access to the remote | ||
| 1413 | machine. This makes it possible to provide version control for files | ||
| 1414 | accessed under @tramp{}. | ||
| 1415 | |||
| 1416 | The actual version control binaries must be installed on the remote | ||
| 1417 | machine, accessible in the directories specified in | ||
| 1418 | @var{tramp-remote-path}. | ||
| 1419 | |||
| 1420 | This transparent integration with the version control systems is one of | ||
| 1421 | the most valuable features provided by @tramp{}, but it is far from perfect. | ||
| 1422 | Work is ongoing to improve the transparency of the system. | ||
| 1423 | |||
| 1424 | @menu | ||
| 1425 | * Version Controlled Files:: Determining if a file is under version control. | ||
| 1426 | * Remote Commands:: Executing the version control commands on the remote machine. | ||
| 1427 | * Changed workfiles:: Detecting if the working file has changed. | ||
| 1428 | * Checking out files:: Bringing the workfile out of the repository. | ||
| 1429 | * Miscellaneous Version Control:: Things related to Version Control that don't fit elsewhere | ||
| 1430 | @end menu | ||
| 1431 | |||
| 1432 | |||
| 1433 | @node Version Controlled Files | ||
| 1434 | @section Determining if a file is under version control | ||
| 1435 | |||
| 1436 | The VC package uses the existence of on-disk revision control master | ||
| 1437 | files to determine if a given file is under revision control. These file | ||
| 1438 | tests happen on the remote machine through the standard @tramp{} mechanisms. | ||
| 1439 | |||
| 1440 | |||
| 1441 | @node Remote Commands | ||
| 1442 | @section Executing the version control commands on the remote machine | ||
| 1443 | |||
| 1444 | There are no hooks provided by VC to allow intercepting of the version | ||
| 1445 | control command execution. The calls occur through the | ||
| 1446 | @code{call-process} mechanism, a function that is somewhat more | ||
| 1447 | efficient than the @code{shell-command} function but that does not | ||
| 1448 | provide hooks for remote execution of commands. | ||
| 1449 | |||
| 1450 | To work around this, the functions @code{vc-do-command} and | ||
| 1451 | @code{vc-simple-command} have been advised to intercept requests for | ||
| 1452 | operations on files accessed via @tramp{}. | ||
| 1453 | |||
| 1454 | In the case of a remote file, the @code{shell-command} interface is | ||
| 1455 | used, with some wrapper code, to provide the same functionality on the | ||
| 1456 | remote machine as would be seen on the local machine. | ||
| 1457 | |||
| 1458 | |||
| 1459 | @node Changed workfiles | ||
| 1460 | @section Detecting if the working file has changed | ||
| 1461 | |||
| 1462 | As there is currently no way to get access to the mtime of a file on a | ||
| 1463 | remote machine in a portable way, the @code{vc-workfile-unchanged-p} | ||
| 1464 | function is advised to call an @tramp{} specific function for remote files. | ||
| 1465 | |||
| 1466 | The @code{tramp-vc-workfile-unchanged-p} function uses the functioning VC | ||
| 1467 | diff functionality to determine if any changes have occurred between the | ||
| 1468 | workfile and the version control master. | ||
| 1469 | |||
| 1470 | This requires that a shell command be executed remotely, a process that | ||
| 1471 | is notably heavier-weight than the mtime comparison used for local | ||
| 1472 | files. Unfortunately, unless a portable solution to the issue is found, | ||
| 1473 | this will remain the cost of remote version control. | ||
| 1474 | |||
| 1475 | |||
| 1476 | @node Checking out files | ||
| 1477 | @section Bringing the workfile out of the repository | ||
| 1478 | |||
| 1479 | VC will, by default, check for remote files and refuse to act on them | ||
| 1480 | when checking out files from the repository. To work around this | ||
| 1481 | problem, the function @code{vc-checkout} knows about @tramp{} files and | ||
| 1482 | allows version control to occur. | ||
| 1483 | |||
| 1484 | |||
| 1485 | @node Miscellaneous Version Control | ||
| 1486 | @section Things related to Version Control that don't fit elsewhere | ||
| 1487 | |||
| 1488 | Minor implementation details, &c. | ||
| 1489 | |||
| 1490 | @menu | ||
| 1491 | * Remote File Ownership:: How VC determines who owns a workfile. | ||
| 1492 | * Back-end Versions:: How VC determines what release your RCS is. | ||
| 1493 | @end menu | ||
| 1494 | |||
| 1495 | |||
| 1496 | @node Remote File Ownership | ||
| 1497 | @subsection How VC determines who owns a workfile | ||
| 1498 | |||
| 1499 | Emacs provides the @code{user-full-name} function to return the login name | ||
| 1500 | of the current user as well as mapping from arbitrary user id values | ||
| 1501 | back to login names. The VC code uses this functionality to map from the | ||
| 1502 | uid of the owner of a workfile to the login name in some circumstances. | ||
| 1503 | |||
| 1504 | This will not, for obvious reasons, work if the remote system has a | ||
| 1505 | different set of logins. As such, it is necessary to delegate to the | ||
| 1506 | remote machine the job of determining the login name associated with a | ||
| 1507 | uid. | ||
| 1508 | |||
| 1509 | Unfortunately, with the profusion of distributed management systems such | ||
| 1510 | as @code{NIS}, @code{NIS+} and @code{NetInfo}, there is no simple, | ||
| 1511 | reliable and portable method for performing this mapping. | ||
| 1512 | |||
| 1513 | Thankfully, the only place in the VC code that depends on the mapping of | ||
| 1514 | a uid to a login name is the @code{vc-file-owner} function. This returns | ||
| 1515 | the login of the owner of the file as a string. | ||
| 1516 | |||
| 1517 | This function has been advised to use the output of @command{ls} on the | ||
| 1518 | remote machine to determine the login name, delegating the problem of | ||
| 1519 | mapping the uid to the login to the remote system which should know more | ||
| 1520 | about it than I do. | ||
| 1521 | |||
| 1522 | |||
| 1523 | @node Back-end Versions | ||
| 1524 | @subsection How VC determines what release your RCS is | ||
| 1525 | |||
| 1526 | VC needs to know what release your revision control binaries you are | ||
| 1527 | running as not all features VC supports are available with older | ||
| 1528 | versions of @command{rcs(1)}, @command{cvs(1)} or @command{sccs(1)}. | ||
| 1529 | |||
| 1530 | The default implementation of VC determines this value the first time it | ||
| 1531 | is needed and then stores the value globally to avoid the overhead of | ||
| 1532 | executing a process and parsing its output each time the information is | ||
| 1533 | needed. | ||
| 1534 | |||
| 1535 | Unfortunately, life is not quite so easy when remote version control | ||
| 1536 | comes into the picture. Each remote machine may have a different version | ||
| 1537 | of the version control tools and, while this is painful, we need to | ||
| 1538 | ensure that unavailable features are not used remotely. | ||
| 1539 | |||
| 1540 | To resolve this issue, @tramp{} currently takes the sledgehammer | ||
| 1541 | approach of making the release values of the revision control tools | ||
| 1542 | local to each @tramp{} buffer, forcing VC to determine these values | ||
| 1543 | again each time a new file is visited. | ||
| 1544 | |||
| 1545 | This has, quite obviously, some performance implications. Thankfully, | ||
| 1546 | most of the common operations performed by VC do not actually require | ||
| 1547 | that the remote version be known. This makes the problem far less | ||
| 1548 | apparent. | ||
| 1549 | |||
| 1550 | Eventually these values will be captured by @tramp{} on a system by | ||
| 1551 | system basis and the results cached to improve performance. | ||
| 1552 | |||
| 1553 | |||
| 1554 | @node Files directories and paths | ||
| 1555 | @chapter How file names, directories and paths are mangled and managed. | ||
| 1556 | |||
| 1557 | @menu | ||
| 1558 | * Path deconstruction:: Breaking a path into its components. | ||
| 1559 | @end menu | ||
| 1560 | |||
| 1561 | |||
| 1562 | @node Path deconstruction | ||
| 1563 | @section Breaking a path into its components. | ||
| 1564 | |||
| 1565 | @tramp{} filenames are somewhat different, obviously, to ordinary path | ||
| 1566 | names. As such, the lisp functions @code{file-name-directory} and | ||
| 1567 | @code{file-name-nondirectory} are overridden within the @tramp{} package. | ||
| 1568 | |||
| 1569 | Their replacements are reasonably simplistic in their approach. They | ||
| 1570 | dissect the filename, call the original handler on the remote path and | ||
| 1571 | then rebuild the @tramp{} path with the result. | ||
| 1572 | |||
| 1573 | This allows the platform specific hacks in the original handlers to take | ||
| 1574 | effect while preserving the @tramp{} path information. | ||
| 1575 | |||
| 1576 | |||
| 1577 | @node Issues | ||
| 1578 | @chapter Debatable Issues and What Was Decided | ||
| 1579 | |||
| 1580 | @itemize @bullet | ||
| 1581 | @item The uuencode method does not always work. | ||
| 1582 | |||
| 1583 | Due to the design of @tramp{}, the encoding and decoding programs need to | ||
| 1584 | read from stdin and write to stdout. On some systems, @code{uudecode -o | ||
| 1585 | -} will read stdin and write the decoded file to stdout, on other | ||
| 1586 | systems @code{uudecode -p} does the same thing. But some systems have | ||
| 1587 | uudecode implementations which cannot do this at all---it is not | ||
| 1588 | possible to call these uudecode implementations with suitable parameters | ||
| 1589 | so that they write to stdout. | ||
| 1590 | |||
| 1591 | Of course, this could be circumvented: the @code{begin foo 644} line | ||
| 1592 | could be rewritten to put in some temporary file name, then | ||
| 1593 | @code{uudecode} could be called, then the temp file could be printed and | ||
| 1594 | deleted. | ||
| 1595 | |||
| 1596 | But I have decided that this is too fragile to reliably work, so on some | ||
| 1597 | systems you'll have to do without the uuencode methods. | ||
| 1598 | |||
| 1599 | @item @tramp{} does not work on XEmacs 20. | ||
| 1600 | |||
| 1601 | This is because it requires the macro @code{with-timeout} which does not | ||
| 1602 | appear to exist in XEmacs 20. I'm somewhat reluctant to add an | ||
| 1603 | emulation macro to @tramp{}, but if somebody who uses XEmacs 20 steps | ||
| 1604 | forward and wishes to implement and test it, please contact me or the | ||
| 1605 | mailing list. | ||
| 1606 | |||
| 1607 | @end itemize | ||
| 1608 | |||
| 1609 | |||
| 1610 | @c End of tramp.texi - the TRAMP User Manual | ||
| 1611 | @bye | ||
| 1612 | |||
| 1613 | @c TODO | ||
| 1614 | @c | ||
| 1615 | @c * Say something about the .login and .profile files of the remote | ||
| 1616 | @c shells. | ||
| 1617 | @c * Explain how tramp.el works in principle: open a shell on a remote | ||
| 1618 | @c host and then send commands to it. | ||
| 1619 | |||
| 1620 | @c Local Variables: | ||
| 1621 | @c eval: (add-hook 'write-file-hooks 'time-stamp) | ||
| 1622 | @c time-stamp-start: "@set UPDATED " | ||
| 1623 | @c time-stamp-format: "%:a, %:d %:b, %:y" | ||
| 1624 | @c time-stamp-end: "$" | ||
| 1625 | @c time-stamp-line-limit: 50 | ||
| 1626 | @c End: | ||