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 /lisp | |
| parent | d591a83451f2a117a2f6bdc95fc0b401c6b69cd0 (diff) | |
| download | emacs-fb7933a38932ce8832a40507e8e10bd61e27eaee.tar.gz emacs-fb7933a38932ce8832a40507e8e10bd61e27eaee.zip | |
*** empty log message ***
Diffstat (limited to 'lisp')
| -rw-r--r-- | lisp/net/tramp-vc.el | 480 | ||||
| -rw-r--r-- | lisp/net/tramp.el | 5152 |
2 files changed, 5632 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 | ||