Line data Source code
1 : ;;; tramp-sh.el --- Tramp access functions for (s)sh-like connections -*- lexical-binding:t -*-
2 :
3 : ;; Copyright (C) 1998-2017 Free Software Foundation, Inc.
4 :
5 : ;; (copyright statements below in code to be updated with the above notice)
6 :
7 : ;; Author: Kai Großjohann <kai.grossjohann@gmx.net>
8 : ;; Michael Albinus <michael.albinus@gmx.de>
9 : ;; Maintainer: Michael Albinus <michael.albinus@gmx.de>
10 : ;; Keywords: comm, processes
11 : ;; Package: tramp
12 :
13 : ;; This file is part of GNU Emacs.
14 :
15 : ;; GNU Emacs is free software: you can redistribute it and/or modify
16 : ;; it under the terms of the GNU General Public License as published by
17 : ;; the Free Software Foundation, either version 3 of the License, or
18 : ;; (at your option) any later version.
19 :
20 : ;; GNU Emacs is distributed in the hope that it will be useful,
21 : ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
22 : ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 : ;; GNU General Public License for more details.
24 :
25 : ;; You should have received a copy of the GNU General Public License
26 : ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
27 :
28 : ;;; Code:
29 :
30 : (require 'tramp)
31 :
32 : ;; Pacify byte-compiler.
33 : (eval-when-compile
34 : (require 'dired))
35 :
36 : (declare-function dired-remove-file "dired-aux")
37 : (defvar dired-compress-file-suffixes)
38 : (defvar vc-handled-backends)
39 : (defvar vc-bzr-program)
40 : (defvar vc-git-program)
41 : (defvar vc-hg-program)
42 :
43 : ;;;###tramp-autoload
44 : (defcustom tramp-inline-compress-start-size 4096
45 : "The minimum size of compressing where inline transfer.
46 : When inline transfer, compress transferred data of file
47 : whose size is this value or above (up to `tramp-copy-size-limit').
48 : If it is nil, no compression at all will be applied."
49 : :group 'tramp
50 : :type '(choice (const nil) integer)
51 : :require 'tramp)
52 :
53 : ;;;###tramp-autoload
54 : (defcustom tramp-copy-size-limit 10240
55 : "The maximum file size where inline copying is preferred over an \
56 : out-of-the-band copy.
57 : If it is nil, out-of-the-band copy will be used without a check."
58 : :group 'tramp
59 : :type '(choice (const nil) integer)
60 : :require 'tramp)
61 :
62 : ;;;###tramp-autoload
63 : (defcustom tramp-terminal-type "dumb"
64 : "Value of TERM environment variable for logging in to remote host.
65 : Because Tramp wants to parse the output of the remote shell, it is easily
66 : confused by ANSI color escape sequences and suchlike. Often, shell init
67 : files conditionalize this setup based on the TERM environment variable."
68 : :group 'tramp
69 : :type 'string
70 : :require 'tramp)
71 :
72 : ;;;###tramp-autoload
73 : (defcustom tramp-histfile-override "~/.tramp_history"
74 : "When invoking a shell, override the HISTFILE with this value.
75 : When setting to a string, it redirects the shell history to that
76 : file. Be careful when setting to \"/dev/null\"; this might
77 : result in undesired results when using \"bash\" as shell.
78 :
79 : The value t unsets any setting of HISTFILE, and sets both
80 : HISTFILESIZE and HISTSIZE to 0. If you set this variable to nil,
81 : however, the *override* is disabled, so the history will go to
82 : the default storage location, e.g. \"$HOME/.sh_history\"."
83 : :group 'tramp
84 : :version "25.2"
85 : :type '(choice (const :tag "Do not override HISTFILE" nil)
86 : (const :tag "Unset HISTFILE" t)
87 : (string :tag "Redirect to a file"))
88 : :require 'tramp)
89 :
90 : ;;;###tramp-autoload
91 : (defconst tramp-display-escape-sequence-regexp "\e[[;0-9]+m"
92 : "Terminal control escape sequences for display attributes.")
93 :
94 : ;;;###tramp-autoload
95 : (defconst tramp-device-escape-sequence-regexp "\e[[0-9]+n"
96 : "Terminal control escape sequences for device status.")
97 :
98 : ;; ksh on OpenBSD 4.5 requires that $PS1 contains a `#' character for
99 : ;; root users. It uses the `$' character for other users. In order
100 : ;; to guarantee a proper prompt, we use "#$ " for the prompt.
101 :
102 : (defvar tramp-end-of-output
103 : (format
104 : "///%s#$"
105 : (md5 (concat (prin1-to-string process-environment) (current-time-string))))
106 : "String used to recognize end of output.
107 : The `$' character at the end is quoted; the string cannot be
108 : detected as prompt when being sent on echoing hosts, therefore.")
109 :
110 : ;;;###tramp-autoload
111 : (defconst tramp-initial-end-of-output "#$ "
112 : "Prompt when establishing a connection.")
113 :
114 : (defconst tramp-end-of-heredoc (md5 tramp-end-of-output)
115 : "String used to recognize end of heredoc strings.")
116 :
117 : ;;;###tramp-autoload
118 : (defcustom tramp-use-ssh-controlmaster-options t
119 : "Whether to use `tramp-ssh-controlmaster-options'."
120 : :group 'tramp
121 : :version "24.4"
122 : :type 'boolean
123 : :require 'tramp)
124 :
125 : (defvar tramp-ssh-controlmaster-options nil
126 : "Which ssh Control* arguments to use.
127 :
128 : If it is a string, it should have the form
129 : \"-o ControlMaster=auto -o ControlPath=\\='tramp.%%r@%%h:%%p\\='
130 : -o ControlPersist=no\". Percent characters in the ControlPath
131 : spec must be doubled, because the string is used as format string.
132 :
133 : Otherwise, it will be auto-detected by Tramp, if
134 : `tramp-use-ssh-controlmaster-options' is non-nil. The value
135 : depends on the installed local ssh version.
136 :
137 : The string is used in `tramp-methods'.")
138 :
139 : ;; Initialize `tramp-methods' with the supported methods.
140 : ;;;###tramp-autoload
141 : (add-to-list 'tramp-methods
142 : '("rcp"
143 : (tramp-login-program "rsh")
144 : (tramp-login-args (("%h") ("-l" "%u")))
145 : (tramp-remote-shell "/bin/sh")
146 : (tramp-remote-shell-login ("-l"))
147 : (tramp-remote-shell-args ("-c"))
148 : (tramp-copy-program "rcp")
149 : (tramp-copy-args (("-p" "%k") ("-r")))
150 : (tramp-copy-keep-date t)
151 : (tramp-copy-recursive t)))
152 : ;;;###tramp-autoload
153 : (add-to-list 'tramp-methods
154 : '("remcp"
155 : (tramp-login-program "remsh")
156 : (tramp-login-args (("%h") ("-l" "%u")))
157 : (tramp-remote-shell "/bin/sh")
158 : (tramp-remote-shell-login ("-l"))
159 : (tramp-remote-shell-args ("-c"))
160 : (tramp-copy-program "rcp")
161 : (tramp-copy-args (("-p" "%k")))
162 : (tramp-copy-keep-date t)))
163 : ;;;###tramp-autoload
164 : (add-to-list 'tramp-methods
165 : '("scp"
166 : (tramp-login-program "ssh")
167 : (tramp-login-args (("-l" "%u") ("-p" "%p") ("%c")
168 : ("-e" "none") ("%h")))
169 : (tramp-async-args (("-q")))
170 : (tramp-remote-shell "/bin/sh")
171 : (tramp-remote-shell-login ("-l"))
172 : (tramp-remote-shell-args ("-c"))
173 : (tramp-copy-program "scp")
174 : (tramp-copy-args (("-P" "%p") ("-p" "%k") ("-q") ("-r") ("%c")))
175 : (tramp-copy-keep-date t)
176 : (tramp-copy-recursive t)))
177 : ;;;###tramp-autoload
178 : (add-to-list 'tramp-methods
179 : '("scpx"
180 : (tramp-login-program "ssh")
181 : (tramp-login-args (("-l" "%u") ("-p" "%p") ("%c")
182 : ("-e" "none") ("-t" "-t") ("%h") ("/bin/sh")))
183 : (tramp-async-args (("-q")))
184 : (tramp-remote-shell "/bin/sh")
185 : (tramp-remote-shell-login ("-l"))
186 : (tramp-remote-shell-args ("-c"))
187 : (tramp-copy-program "scp")
188 : (tramp-copy-args (("-P" "%p") ("-p" "%k")
189 : ("-q") ("-r") ("%c")))
190 : (tramp-copy-keep-date t)
191 : (tramp-copy-recursive t)))
192 : ;;;###tramp-autoload
193 : (add-to-list 'tramp-methods
194 : '("rsync"
195 : (tramp-login-program "ssh")
196 : (tramp-login-args (("-l" "%u") ("-p" "%p") ("%c")
197 : ("-e" "none") ("%h")))
198 : (tramp-async-args (("-q")))
199 : (tramp-remote-shell "/bin/sh")
200 : (tramp-remote-shell-login ("-l"))
201 : (tramp-remote-shell-args ("-c"))
202 : (tramp-copy-program "rsync")
203 : (tramp-copy-args (("-t" "%k") ("-p") ("-r") ("-s") ("-c")))
204 : (tramp-copy-env (("RSYNC_RSH") ("ssh" "%c")))
205 : (tramp-copy-keep-date t)
206 : (tramp-copy-keep-tmpfile t)
207 : (tramp-copy-recursive t)))
208 : ;;;###tramp-autoload
209 : (add-to-list 'tramp-methods
210 : '("rsh"
211 : (tramp-login-program "rsh")
212 : (tramp-login-args (("%h") ("-l" "%u")))
213 : (tramp-remote-shell "/bin/sh")
214 : (tramp-remote-shell-login ("-l"))
215 : (tramp-remote-shell-args ("-c"))))
216 : ;;;###tramp-autoload
217 : (add-to-list 'tramp-methods
218 : '("remsh"
219 : (tramp-login-program "remsh")
220 : (tramp-login-args (("%h") ("-l" "%u")))
221 : (tramp-remote-shell "/bin/sh")
222 : (tramp-remote-shell-login ("-l"))
223 : (tramp-remote-shell-args ("-c"))))
224 : ;;;###tramp-autoload
225 : (add-to-list 'tramp-methods
226 : '("ssh"
227 : (tramp-login-program "ssh")
228 : (tramp-login-args (("-l" "%u") ("-p" "%p") ("%c")
229 : ("-e" "none") ("%h")))
230 : (tramp-async-args (("-q")))
231 : (tramp-remote-shell "/bin/sh")
232 : (tramp-remote-shell-login ("-l"))
233 : (tramp-remote-shell-args ("-c"))))
234 : ;;;###tramp-autoload
235 : (add-to-list 'tramp-methods
236 : '("sshx"
237 : (tramp-login-program "ssh")
238 : (tramp-login-args (("-l" "%u") ("-p" "%p") ("%c")
239 : ("-e" "none") ("-t" "-t") ("%h") ("/bin/sh")))
240 : (tramp-async-args (("-q")))
241 : (tramp-remote-shell "/bin/sh")
242 : (tramp-remote-shell-login ("-l"))
243 : (tramp-remote-shell-args ("-c"))))
244 : ;;;###tramp-autoload
245 : (add-to-list 'tramp-methods
246 : '("telnet"
247 : (tramp-login-program "telnet")
248 : (tramp-login-args (("%h") ("%p") ("2>/dev/null")))
249 : (tramp-remote-shell "/bin/sh")
250 : (tramp-remote-shell-login ("-l"))
251 : (tramp-remote-shell-args ("-c"))))
252 : ;;;###tramp-autoload
253 : (add-to-list 'tramp-methods
254 : '("nc"
255 : (tramp-login-program "telnet")
256 : (tramp-login-args (("%h") ("%p") ("2>/dev/null")))
257 : (tramp-remote-shell "/bin/sh")
258 : (tramp-remote-shell-login ("-l"))
259 : (tramp-remote-shell-args ("-c"))
260 : (tramp-copy-program "nc")
261 : ;; We use "-v" for better error tracking.
262 : (tramp-copy-args (("-w" "1") ("-v") ("%h") ("%r")))
263 : (tramp-remote-copy-program "nc")
264 : ;; We use "-p" as required for newer busyboxes. For older
265 : ;; busybox/nc versions, the value must be (("-l") ("%r")). This
266 : ;; can be achieved by tweaking `tramp-connection-properties'.
267 : (tramp-remote-copy-args (("-l") ("-p" "%r") ("2>/dev/null")))))
268 : ;;;###tramp-autoload
269 : (add-to-list 'tramp-methods
270 : '("su"
271 : (tramp-login-program "su")
272 : (tramp-login-args (("-") ("%u")))
273 : (tramp-remote-shell "/bin/sh")
274 : (tramp-remote-shell-login ("-l"))
275 : (tramp-remote-shell-args ("-c"))
276 : (tramp-connection-timeout 10)))
277 : ;;;###tramp-autoload
278 : (add-to-list
279 : 'tramp-methods
280 : '("sg"
281 : (tramp-login-program "sg")
282 : (tramp-login-args (("-") ("%u")))
283 : (tramp-remote-shell "/bin/sh")
284 : (tramp-remote-shell-args ("-c"))
285 : (tramp-connection-timeout 10)))
286 : ;;;###tramp-autoload
287 : (add-to-list 'tramp-methods
288 : '("sudo"
289 : (tramp-login-program "sudo")
290 : ;; The password template must be masked. Otherwise, it could be
291 : ;; interpreted as password prompt if the remote host echoes the command.
292 : (tramp-login-args (("-u" "%u") ("-s") ("-H")
293 : ("-p" "P\"\"a\"\"s\"\"s\"\"w\"\"o\"\"r\"\"d\"\":")))
294 : ;; Local $SHELL could be a nasty one, like zsh or fish. Let's override it.
295 : (tramp-login-env (("SHELL") ("/bin/sh")))
296 : (tramp-remote-shell "/bin/sh")
297 : (tramp-remote-shell-login ("-l"))
298 : (tramp-remote-shell-args ("-c"))
299 : (tramp-connection-timeout 10)))
300 : ;;;###tramp-autoload
301 : (add-to-list 'tramp-methods
302 : '("doas"
303 : (tramp-login-program "doas")
304 : (tramp-login-args (("-u" "%u") ("-s")))
305 : (tramp-remote-shell "/bin/sh")
306 : (tramp-remote-shell-args ("-c"))
307 : (tramp-connection-timeout 10)))
308 : ;;;###tramp-autoload
309 : (add-to-list 'tramp-methods
310 : '("ksu"
311 : (tramp-login-program "ksu")
312 : (tramp-login-args (("%u") ("-q")))
313 : (tramp-remote-shell "/bin/sh")
314 : (tramp-remote-shell-login ("-l"))
315 : (tramp-remote-shell-args ("-c"))
316 : (tramp-connection-timeout 10)))
317 : ;;;###tramp-autoload
318 : (add-to-list 'tramp-methods
319 : '("krlogin"
320 : (tramp-login-program "krlogin")
321 : (tramp-login-args (("%h") ("-l" "%u") ("-x")))
322 : (tramp-remote-shell "/bin/sh")
323 : (tramp-remote-shell-login ("-l"))
324 : (tramp-remote-shell-args ("-c"))))
325 : ;;;###tramp-autoload
326 : (add-to-list 'tramp-methods
327 : `("plink"
328 : (tramp-login-program "plink")
329 : ;; ("%h") must be a single element, see `tramp-compute-multi-hops'.
330 : (tramp-login-args (("-l" "%u") ("-P" "%p") ("-ssh") ("-t")
331 : ("%h") ("\"")
332 : (,(format
333 : "env 'TERM=%s' 'PROMPT_COMMAND=' 'PS1=%s'"
334 : tramp-terminal-type
335 : tramp-initial-end-of-output))
336 : ("/bin/sh") ("\"")))
337 : (tramp-remote-shell "/bin/sh")
338 : (tramp-remote-shell-login ("-l"))
339 : (tramp-remote-shell-args ("-c"))))
340 : ;;;###tramp-autoload
341 : (add-to-list 'tramp-methods
342 : `("plinkx"
343 : (tramp-login-program "plink")
344 : (tramp-login-args (("-load") ("%h") ("-t") ("\"")
345 : (,(format
346 : "env 'TERM=%s' 'PROMPT_COMMAND=' 'PS1=%s'"
347 : tramp-terminal-type
348 : tramp-initial-end-of-output))
349 : ("/bin/sh") ("\"")))
350 : (tramp-remote-shell "/bin/sh")
351 : (tramp-remote-shell-login ("-l"))
352 : (tramp-remote-shell-args ("-c"))))
353 : ;;;###tramp-autoload
354 : (add-to-list 'tramp-methods
355 : `("pscp"
356 : (tramp-login-program "plink")
357 : (tramp-login-args (("-l" "%u") ("-P" "%p") ("-ssh") ("-t")
358 : ("%h") ("\"")
359 : (,(format
360 : "env 'TERM=%s' 'PROMPT_COMMAND=' 'PS1=%s'"
361 : tramp-terminal-type
362 : tramp-initial-end-of-output))
363 : ("/bin/sh") ("\"")))
364 : (tramp-remote-shell "/bin/sh")
365 : (tramp-remote-shell-login ("-l"))
366 : (tramp-remote-shell-args ("-c"))
367 : (tramp-copy-program "pscp")
368 : (tramp-copy-args (("-l" "%u") ("-P" "%p") ("-scp") ("-p" "%k")
369 : ("-q") ("-r")))
370 : (tramp-copy-keep-date t)
371 : (tramp-copy-recursive t)))
372 : ;;;###tramp-autoload
373 : (add-to-list 'tramp-methods
374 : `("psftp"
375 : (tramp-login-program "plink")
376 : (tramp-login-args (("-l" "%u") ("-P" "%p") ("-ssh") ("-t")
377 : ("%h") ("\"")
378 : (,(format
379 : "env 'TERM=%s' 'PROMPT_COMMAND=' 'PS1=%s'"
380 : tramp-terminal-type
381 : tramp-initial-end-of-output))
382 : ("/bin/sh") ("\"")))
383 : (tramp-remote-shell "/bin/sh")
384 : (tramp-remote-shell-login ("-l"))
385 : (tramp-remote-shell-args ("-c"))
386 : (tramp-copy-program "pscp")
387 : (tramp-copy-args (("-l" "%u") ("-P" "%p") ("-sftp") ("-p" "%k")
388 : ("-q")))
389 : (tramp-copy-keep-date t)))
390 : ;;;###tramp-autoload
391 : (add-to-list 'tramp-methods
392 : '("fcp"
393 : (tramp-login-program "fsh")
394 : (tramp-login-args (("%h") ("-l" "%u") ("sh" "-i")))
395 : (tramp-remote-shell "/bin/sh")
396 : (tramp-remote-shell-login ("-l"))
397 : (tramp-remote-shell-args ("-i") ("-c"))
398 : (tramp-copy-program "fcp")
399 : (tramp-copy-args (("-p" "%k")))
400 : (tramp-copy-keep-date t)))
401 :
402 : ;;;###tramp-autoload
403 : (add-to-list 'tramp-default-method-alist
404 : `(,tramp-local-host-regexp "\\`root\\'" "su"))
405 :
406 : ;;;###tramp-autoload
407 : (add-to-list 'tramp-default-user-alist
408 : `(,(concat "\\`" (regexp-opt '("su" "sudo" "doas" "ksu")) "\\'")
409 : nil "root"))
410 : ;; Do not add "ssh" based methods, otherwise ~/.ssh/config would be ignored.
411 : ;; Do not add "plink" based methods, they ask interactively for the user.
412 : ;;;###tramp-autoload
413 : (add-to-list 'tramp-default-user-alist
414 : `(,(concat
415 : "\\`"
416 : (regexp-opt
417 : '("rcp" "remcp" "rsh" "telnet" "nc" "krlogin" "fcp"))
418 : "\\'")
419 : nil ,(user-login-name)))
420 :
421 : ;;;###tramp-autoload
422 : (defconst tramp-completion-function-alist-rsh
423 : '((tramp-parse-rhosts "/etc/hosts.equiv")
424 : (tramp-parse-rhosts "~/.rhosts"))
425 : "Default list of (FUNCTION FILE) pairs to be examined for rsh methods.")
426 :
427 : ;;;###tramp-autoload
428 : (defconst tramp-completion-function-alist-ssh
429 : '((tramp-parse-rhosts "/etc/hosts.equiv")
430 : (tramp-parse-rhosts "/etc/shosts.equiv")
431 : (tramp-parse-shosts "/etc/ssh_known_hosts")
432 : (tramp-parse-sconfig "/etc/ssh_config")
433 : (tramp-parse-shostkeys "/etc/ssh2/hostkeys")
434 : (tramp-parse-sknownhosts "/etc/ssh2/knownhosts")
435 : (tramp-parse-rhosts "~/.rhosts")
436 : (tramp-parse-rhosts "~/.shosts")
437 : (tramp-parse-shosts "~/.ssh/known_hosts")
438 : (tramp-parse-sconfig "~/.ssh/config")
439 : (tramp-parse-shostkeys "~/.ssh2/hostkeys")
440 : (tramp-parse-sknownhosts "~/.ssh2/knownhosts"))
441 : "Default list of (FUNCTION FILE) pairs to be examined for ssh methods.")
442 :
443 : ;;;###tramp-autoload
444 : (defconst tramp-completion-function-alist-telnet
445 : '((tramp-parse-hosts "/etc/hosts"))
446 : "Default list of (FUNCTION FILE) pairs to be examined for telnet methods.")
447 :
448 : ;;;###tramp-autoload
449 : (defconst tramp-completion-function-alist-su
450 : '((tramp-parse-passwd "/etc/passwd"))
451 : "Default list of (FUNCTION FILE) pairs to be examined for su methods.")
452 :
453 : ;;;###tramp-autoload
454 : (defconst tramp-completion-function-alist-sg
455 : '((tramp-parse-etc-group "/etc/group"))
456 : "Default list of (FUNCTION FILE) pairs to be examined for sg methods.")
457 :
458 : ;;;###tramp-autoload
459 : (defconst tramp-completion-function-alist-putty
460 : `((tramp-parse-putty
461 : ,(if (memq system-type '(windows-nt))
462 : "HKEY_CURRENT_USER\\Software\\SimonTatham\\PuTTY\\Sessions"
463 : "~/.putty/sessions")))
464 : "Default list of (FUNCTION REGISTRY) pairs to be examined for putty sessions.")
465 :
466 : ;;;###tramp-autoload
467 : (eval-after-load 'tramp
468 : '(progn
469 : (tramp-set-completion-function "rcp" tramp-completion-function-alist-rsh)
470 : (tramp-set-completion-function "remcp" tramp-completion-function-alist-rsh)
471 : (tramp-set-completion-function "scp" tramp-completion-function-alist-ssh)
472 : (tramp-set-completion-function "scpx" tramp-completion-function-alist-ssh)
473 : (tramp-set-completion-function "rsync" tramp-completion-function-alist-ssh)
474 : (tramp-set-completion-function "rsh" tramp-completion-function-alist-rsh)
475 : (tramp-set-completion-function "remsh" tramp-completion-function-alist-rsh)
476 : (tramp-set-completion-function "ssh" tramp-completion-function-alist-ssh)
477 : (tramp-set-completion-function "sshx" tramp-completion-function-alist-ssh)
478 : (tramp-set-completion-function
479 : "telnet" tramp-completion-function-alist-telnet)
480 : (tramp-set-completion-function "nc" tramp-completion-function-alist-telnet)
481 : (tramp-set-completion-function "su" tramp-completion-function-alist-su)
482 : (tramp-set-completion-function "sudo" tramp-completion-function-alist-su)
483 : (tramp-set-completion-function "doas" tramp-completion-function-alist-su)
484 : (tramp-set-completion-function "ksu" tramp-completion-function-alist-su)
485 : (tramp-set-completion-function "sg" tramp-completion-function-alist-sg)
486 : (tramp-set-completion-function
487 : "krlogin" tramp-completion-function-alist-rsh)
488 : (tramp-set-completion-function "plink" tramp-completion-function-alist-ssh)
489 : (tramp-set-completion-function
490 : "plinkx" tramp-completion-function-alist-putty)
491 : (tramp-set-completion-function "pscp" tramp-completion-function-alist-ssh)
492 : (tramp-set-completion-function "psftp" tramp-completion-function-alist-ssh)
493 : (tramp-set-completion-function "fcp" tramp-completion-function-alist-ssh)))
494 :
495 : ;; "getconf PATH" yields:
496 : ;; HP-UX: /usr/bin:/usr/ccs/bin:/opt/ansic/bin:/opt/langtools/bin:/opt/fortran/bin
497 : ;; Solaris: /usr/xpg4/bin:/usr/ccs/bin:/usr/bin:/opt/SUNWspro/bin
498 : ;; GNU/Linux (Debian, Suse, RHEL): /bin:/usr/bin
499 : ;; FreeBSD: /usr/bin:/bin:/usr/sbin:/sbin: - beware trailing ":"!
500 : ;; Darwin: /usr/bin:/bin:/usr/sbin:/sbin
501 : ;; IRIX64: /usr/bin
502 : ;; QNAP QTS: ---
503 : ;;;###tramp-autoload
504 : (defcustom tramp-remote-path
505 : '(tramp-default-remote-path "/bin" "/usr/bin" "/sbin" "/usr/sbin"
506 : "/usr/local/bin" "/usr/local/sbin" "/local/bin" "/local/freeware/bin"
507 : "/local/gnu/bin" "/usr/freeware/bin" "/usr/pkg/bin" "/usr/contrib/bin"
508 : "/opt/bin" "/opt/sbin" "/opt/local/bin")
509 : "List of directories to search for executables on remote host.
510 : For every remote host, this variable will be set buffer local,
511 : keeping the list of existing directories on that host.
512 :
513 : You can use `~' in this list, but when searching for a shell which groks
514 : tilde expansion, all directory names starting with `~' will be ignored.
515 :
516 : `Default Directories' represent the list of directories given by
517 : the command \"getconf PATH\". It is recommended to use this
518 : entry on head of this list, because these are the default
519 : directories for POSIX compatible commands. On remote hosts which
520 : do not offer the getconf command (like cygwin), the value
521 : \"/bin:/usr/bin\" is used instead. This entry is represented in
522 : the list by the special value `tramp-default-remote-path'.
523 :
524 : `Private Directories' are the settings of the $PATH environment,
525 : as given in your `~/.profile'. This entry is represented in
526 : the list by the special value `tramp-own-remote-path'."
527 : :group 'tramp
528 : :type '(repeat (choice
529 : (const :tag "Default Directories" tramp-default-remote-path)
530 : (const :tag "Private Directories" tramp-own-remote-path)
531 : (string :tag "Directory")))
532 : :require 'tramp)
533 :
534 : ;;;###tramp-autoload
535 : (defcustom tramp-remote-process-environment
536 : `("ENV=''" "TMOUT=0" "LC_CTYPE=''"
537 : ,(format "TERM=%s" tramp-terminal-type)
538 : ,(format "INSIDE_EMACS='%s,tramp:%s'" emacs-version tramp-version)
539 : "CDPATH=" "HISTORY=" "MAIL=" "MAILCHECK=" "MAILPATH=" "PAGER=cat"
540 : "autocorrect=" "correct=")
541 : "List of environment variables to be set on the remote host.
542 :
543 : Each element should be a string of the form ENVVARNAME=VALUE. An
544 : entry ENVVARNAME= disables the corresponding environment variable,
545 : which might have been set in the init files like ~/.profile.
546 :
547 : Special handling is applied to the PATH environment, which should
548 : not be set here. Instead, it should be set via `tramp-remote-path'."
549 : :group 'tramp
550 : :version "26.1"
551 : :type '(repeat string)
552 : :require 'tramp)
553 :
554 : ;;;###tramp-autoload
555 : (defcustom tramp-sh-extra-args '(("/bash\\'" . "-norc -noprofile"))
556 : "Alist specifying extra arguments to pass to the remote shell.
557 : Entries are (REGEXP . ARGS) where REGEXP is a regular expression
558 : matching the shell file name and ARGS is a string specifying the
559 : arguments.
560 :
561 : This variable is only used when Tramp needs to start up another shell
562 : for tilde expansion. The extra arguments should typically prevent the
563 : shell from reading its init file."
564 : :group 'tramp
565 : ;; This might be the wrong way to test whether the widget type
566 : ;; `alist' is available. Who knows the right way to test it?
567 : :type (if (get 'alist 'widget-type)
568 : '(alist :key-type string :value-type string)
569 : '(repeat (cons string string)))
570 : :require 'tramp)
571 :
572 : (defconst tramp-actions-before-shell
573 : '((tramp-login-prompt-regexp tramp-action-login)
574 : (tramp-password-prompt-regexp tramp-action-password)
575 : (tramp-wrong-passwd-regexp tramp-action-permission-denied)
576 : (shell-prompt-pattern tramp-action-succeed)
577 : (tramp-shell-prompt-pattern tramp-action-succeed)
578 : (tramp-yesno-prompt-regexp tramp-action-yesno)
579 : (tramp-yn-prompt-regexp tramp-action-yn)
580 : (tramp-terminal-prompt-regexp tramp-action-terminal)
581 : (tramp-process-alive-regexp tramp-action-process-alive))
582 : "List of pattern/action pairs.
583 : Whenever a pattern matches, the corresponding action is performed.
584 : Each item looks like (PATTERN ACTION).
585 :
586 : The PATTERN should be a symbol, a variable. The value of this
587 : variable gives the regular expression to search for. Note that the
588 : regexp must match at the end of the buffer, \"\\'\" is implicitly
589 : appended to it.
590 :
591 : The ACTION should also be a symbol, but a function. When the
592 : corresponding PATTERN matches, the ACTION function is called.")
593 :
594 : (defconst tramp-actions-copy-out-of-band
595 : '((tramp-password-prompt-regexp tramp-action-password)
596 : (tramp-wrong-passwd-regexp tramp-action-permission-denied)
597 : (tramp-copy-failed-regexp tramp-action-permission-denied)
598 : (tramp-process-alive-regexp tramp-action-out-of-band))
599 : "List of pattern/action pairs.
600 : This list is used for copying/renaming with out-of-band methods.
601 :
602 : See `tramp-actions-before-shell' for more info.")
603 :
604 : (defconst tramp-uudecode
605 : "(echo begin 600 %t; tail -n +2) | uudecode
606 : cat %t
607 : rm -f %t"
608 : "Shell function to implement `uudecode' to standard output.
609 : Many systems support `uudecode -o /dev/stdout' or `uudecode -o -'
610 : for this or `uudecode -p', but some systems don't, and for them
611 : we have this shell function.")
612 :
613 : (defconst tramp-perl-file-truename
614 : "%s -e '
615 : use File::Spec;
616 : use Cwd \"realpath\";
617 :
618 : sub myrealpath {
619 : my ($file) = @_;
620 : return realpath($file) if -e $file;
621 : }
622 :
623 : sub recursive {
624 : my ($volume, @dirs) = @_;
625 : my $real = myrealpath(File::Spec->catpath(
626 : $volume, File::Spec->catdir(@dirs), \"\"));
627 : if ($real) {
628 : my ($vol, $dir) = File::Spec->splitpath($real, 1);
629 : return ($vol, File::Spec->splitdir($dir));
630 : }
631 : else {
632 : my $last = pop(@dirs);
633 : ($volume, @dirs) = recursive($volume, @dirs);
634 : push(@dirs, $last);
635 : return ($volume, @dirs);
636 : }
637 : }
638 :
639 : $result = myrealpath($ARGV[0]);
640 : if (!$result) {
641 : my ($vol, $dir) = File::Spec->splitpath($ARGV[0], 1);
642 : ($vol, @dirs) = recursive($vol, File::Spec->splitdir($dir));
643 :
644 : $result = File::Spec->catpath($vol, File::Spec->catdir(@dirs), \"\");
645 : }
646 :
647 : $result =~ s/\"/\\\\\"/g;
648 : print \"\\\"$result\\\"\\n\";
649 : ' \"$1\" 2>/dev/null"
650 : "Perl script to produce output suitable for use with `file-truename'
651 : on the remote file system.
652 : Escape sequence %s is replaced with name of Perl binary.
653 : This string is passed to `format', so percent characters need to be doubled.")
654 :
655 : (defconst tramp-perl-file-name-all-completions
656 : "%s -e '
657 : opendir(d, $ARGV[0]) || die(\"$ARGV[0]: $!\\nfail\\n\");
658 : @files = readdir(d); closedir(d);
659 : foreach $f (@files) {
660 : if (-d \"$ARGV[0]/$f\") {
661 : print \"$f/\\n\";
662 : }
663 : else {
664 : print \"$f\\n\";
665 : }
666 : }
667 : print \"ok\\n\"
668 : ' \"$1\" 2>/dev/null"
669 : "Perl script to produce output suitable for use with
670 : `file-name-all-completions' on the remote file system. Escape
671 : sequence %s is replaced with name of Perl binary. This string is
672 : passed to `format', so percent characters need to be doubled.")
673 :
674 : ;; Perl script to implement `file-attributes' in a Lisp `read'able
675 : ;; output. If you are hacking on this, note that you get *no* output
676 : ;; unless this spits out a complete line, including the '\n' at the
677 : ;; end.
678 : ;; The device number is returned as "-1", because there will be a virtual
679 : ;; device number set in `tramp-sh-handle-file-attributes'.
680 : (defconst tramp-perl-file-attributes
681 : "%s -e '
682 : @stat = lstat($ARGV[0]);
683 : if (!@stat) {
684 : print \"nil\\n\";
685 : exit 0;
686 : }
687 : if (($stat[2] & 0170000) == 0120000)
688 : {
689 : $type = readlink($ARGV[0]);
690 : $type =~ s/\"/\\\\\"/g;
691 : $type = \"\\\"$type\\\"\";
692 : }
693 : elsif (($stat[2] & 0170000) == 040000)
694 : {
695 : $type = \"t\";
696 : }
697 : else
698 : {
699 : $type = \"nil\"
700 : };
701 : $uid = ($ARGV[1] eq \"integer\") ? $stat[4] : \"\\\"\" . getpwuid($stat[4]) . \"\\\"\";
702 : $gid = ($ARGV[1] eq \"integer\") ? $stat[5] : \"\\\"\" . getgrgid($stat[5]) . \"\\\"\";
703 : printf(
704 : \"(%%s %%u %%s %%s (%%u %%u) (%%u %%u) (%%u %%u) %%u.0 %%u t (%%u . %%u) -1)\\n\",
705 : $type,
706 : $stat[3],
707 : $uid,
708 : $gid,
709 : $stat[8] >> 16 & 0xffff,
710 : $stat[8] & 0xffff,
711 : $stat[9] >> 16 & 0xffff,
712 : $stat[9] & 0xffff,
713 : $stat[10] >> 16 & 0xffff,
714 : $stat[10] & 0xffff,
715 : $stat[7],
716 : $stat[2],
717 : $stat[1] >> 16 & 0xffff,
718 : $stat[1] & 0xffff
719 : );' \"$1\" \"$2\" 2>/dev/null"
720 : "Perl script to produce output suitable for use with `file-attributes'
721 : on the remote file system.
722 : Escape sequence %s is replaced with name of Perl binary.
723 : This string is passed to `format', so percent characters need to be doubled.")
724 :
725 : (defconst tramp-perl-directory-files-and-attributes
726 : "%s -e '
727 : chdir($ARGV[0]) or printf(\"\\\"Cannot change to $ARGV[0]: $''!''\\\"\\n\"), exit();
728 : opendir(DIR,\".\") or printf(\"\\\"Cannot open directory $ARGV[0]: $''!''\\\"\\n\"), exit();
729 : @list = readdir(DIR);
730 : closedir(DIR);
731 : $n = scalar(@list);
732 : printf(\"(\\n\");
733 : for($i = 0; $i < $n; $i++)
734 : {
735 : $filename = $list[$i];
736 : @stat = lstat($filename);
737 : if (($stat[2] & 0170000) == 0120000)
738 : {
739 : $type = readlink($filename);
740 : $type =~ s/\"/\\\\\"/g;
741 : $type = \"\\\"$type\\\"\";
742 : }
743 : elsif (($stat[2] & 0170000) == 040000)
744 : {
745 : $type = \"t\";
746 : }
747 : else
748 : {
749 : $type = \"nil\"
750 : };
751 : $uid = ($ARGV[1] eq \"integer\") ? $stat[4] : \"\\\"\" . getpwuid($stat[4]) . \"\\\"\";
752 : $gid = ($ARGV[1] eq \"integer\") ? $stat[5] : \"\\\"\" . getgrgid($stat[5]) . \"\\\"\";
753 : $filename =~ s/\"/\\\\\"/g;
754 : printf(
755 : \"(\\\"%%s\\\" %%s %%u %%s %%s (%%u %%u) (%%u %%u) (%%u %%u) %%u.0 %%u t (%%u . %%u) (%%u . %%u))\\n\",
756 : $filename,
757 : $type,
758 : $stat[3],
759 : $uid,
760 : $gid,
761 : $stat[8] >> 16 & 0xffff,
762 : $stat[8] & 0xffff,
763 : $stat[9] >> 16 & 0xffff,
764 : $stat[9] & 0xffff,
765 : $stat[10] >> 16 & 0xffff,
766 : $stat[10] & 0xffff,
767 : $stat[7],
768 : $stat[2],
769 : $stat[1] >> 16 & 0xffff,
770 : $stat[1] & 0xffff,
771 : $stat[0] >> 16 & 0xffff,
772 : $stat[0] & 0xffff);
773 : }
774 : printf(\")\\n\");' \"$1\" \"$2\" 2>/dev/null"
775 : "Perl script implementing `directory-files-attributes' as Lisp `read'able
776 : output.
777 : Escape sequence %s is replaced with name of Perl binary.
778 : This string is passed to `format', so percent characters need to be doubled.")
779 :
780 : ;; These two use base64 encoding.
781 : (defconst tramp-perl-encode-with-module
782 : "%s -MMIME::Base64 -0777 -ne 'print encode_base64($_)' 2>/dev/null"
783 : "Perl program to use for encoding a file.
784 : Escape sequence %s is replaced with name of Perl binary.
785 : This string is passed to `format', so percent characters need to be doubled.
786 : This implementation requires the MIME::Base64 Perl module to be installed
787 : on the remote host.")
788 :
789 : (defconst tramp-perl-decode-with-module
790 : "%s -MMIME::Base64 -0777 -ne 'print decode_base64($_)' 2>/dev/null"
791 : "Perl program to use for decoding a file.
792 : Escape sequence %s is replaced with name of Perl binary.
793 : This string is passed to `format', so percent characters need to be doubled.
794 : This implementation requires the MIME::Base64 Perl module to be installed
795 : on the remote host.")
796 :
797 : (defconst tramp-perl-encode
798 : "%s -e '
799 : # This script contributed by Juanma Barranquero <lektu@terra.es>.
800 : # Copyright (C) 2002-2017 Free Software Foundation, Inc.
801 : use strict;
802 :
803 : my %%trans = do {
804 : my $i = 0;
805 : map {(substr(unpack(q(B8), chr $i++), 2, 6), $_)}
806 : split //, q(ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/);
807 : };
808 : my $data;
809 :
810 : # We read in chunks of 54 bytes, to generate output lines
811 : # of 72 chars (plus end of line)
812 : while (read STDIN, $data, 54) {
813 : my $pad = q();
814 :
815 : # Only for the last chunk, and only if did not fill the last three-byte packet
816 : if (eof) {
817 : my $mod = length($data) %% 3;
818 : $pad = q(=) x (3 - $mod) if $mod;
819 : }
820 :
821 : # Not the fastest method, but it is simple: unpack to binary string, split
822 : # by groups of 6 bits and convert back from binary to byte; then map into
823 : # the translation table
824 : print
825 : join q(),
826 : map($trans{$_},
827 : (substr(unpack(q(B*), $data) . q(00000), 0, 432) =~ /....../g)),
828 : $pad,
829 : qq(\\n);
830 : }' 2>/dev/null"
831 : "Perl program to use for encoding a file.
832 : Escape sequence %s is replaced with name of Perl binary.
833 : This string is passed to `format', so percent characters need to be doubled.")
834 :
835 : (defconst tramp-perl-decode
836 : "%s -e '
837 : # This script contributed by Juanma Barranquero <lektu@terra.es>.
838 : # Copyright (C) 2002-2017 Free Software Foundation, Inc.
839 : use strict;
840 :
841 : my %%trans = do {
842 : my $i = 0;
843 : map {($_, substr(unpack(q(B8), chr $i++), 2, 6))}
844 : split //, q(ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/)
845 : };
846 :
847 : my %%bytes = map {(unpack(q(B8), chr $_), chr $_)} 0 .. 255;
848 :
849 : binmode(\\*STDOUT);
850 :
851 : # We are going to accumulate into $pending to accept any line length
852 : # (we do not check they are <= 76 chars as the RFC says)
853 : my $pending = q();
854 :
855 : while (my $data = <STDIN>) {
856 : chomp $data;
857 :
858 : # If we find one or two =, we have reached the end and
859 : # any following data is to be discarded
860 : my $finished = $data =~ s/(==?).*/$1/;
861 : $pending .= $data;
862 :
863 : my $len = length($pending);
864 : my $chunk = substr($pending, 0, $len & ~3);
865 : $pending = substr($pending, $len & ~3 + 1);
866 :
867 : # Easy method: translate from chars to (pregenerated) six-bit packets, join,
868 : # split in 8-bit chunks and convert back to char.
869 : print join q(),
870 : map $bytes{$_},
871 : ((join q(), map {$trans{$_} || q()} split //, $chunk) =~ /......../g);
872 :
873 : last if $finished;
874 : }' 2>/dev/null"
875 : "Perl program to use for decoding a file.
876 : Escape sequence %s is replaced with name of Perl binary.
877 : This string is passed to `format', so percent characters need to be doubled.")
878 :
879 : (defconst tramp-perl-pack
880 : "%s -e 'binmode STDIN; binmode STDOUT; print pack(q{u*}, join q{}, <>)'"
881 : "Perl program to use for encoding a file.
882 : Escape sequence %s is replaced with name of Perl binary.")
883 :
884 : (defconst tramp-perl-unpack
885 : "%s -e 'binmode STDIN; binmode STDOUT; print unpack(q{u*}, join q{}, <>)'"
886 : "Perl program to use for decoding a file.
887 : Escape sequence %s is replaced with name of Perl binary.")
888 :
889 : (defconst tramp-awk-encode
890 : "od -v -t x1 -A n | busybox awk '\\
891 : BEGIN {
892 : b64 = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\"
893 : b16 = \"0123456789abcdef\"
894 : }
895 : {
896 : for (c=1; c<=length($0); c++) {
897 : d=index(b16, substr($0,c,1))
898 : if (d--) {
899 : for (b=1; b<=4; b++) {
900 : o=o*2+int(d/8); d=(d*2)%%16
901 : if (++obc==6) {
902 : printf substr(b64,o+1,1)
903 : if (++rc>75) { printf \"\\n\"; rc=0 }
904 : obc=0; o=0
905 : }
906 : }
907 : }
908 : }
909 : }
910 : END {
911 : if (obc) {
912 : tail=(obc==2) ? \"==\\n\" : \"=\\n\"
913 : while (obc++<6) { o=o*2 }
914 : printf \"%%c\", substr(b64,o+1,1)
915 : } else {
916 : tail=\"\\n\"
917 : }
918 : printf tail
919 : }'"
920 : "Awk program to use for encoding a file.
921 : This string is passed to `format', so percent characters need to be doubled.")
922 :
923 : (defconst tramp-awk-decode
924 : "busybox awk '\\
925 : BEGIN {
926 : b64 = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\"
927 : }
928 : {
929 : for (i=1; i<=length($0); i++) {
930 : c=index(b64, substr($0,i,1))
931 : if(c--) {
932 : for(b=0; b<6; b++) {
933 : o=o*2+int(c/32); c=(c*2)%%64
934 : if(++obc==8) {
935 : if (o) {
936 : printf \"%%c\", o
937 : } else {
938 : system(\"dd if=/dev/zero bs=1 count=1 2>/dev/null\")
939 : }
940 : obc=0; o=0
941 : }
942 : }
943 : }
944 : }
945 : }'"
946 : "Awk program to use for decoding a file.
947 : This string is passed to `format', so percent characters need to be doubled.")
948 :
949 : (defconst tramp-awk-coding-test
950 : "test -c /dev/zero && \
951 : od -v -t x1 -A n </dev/null && \
952 : busybox awk '{}' </dev/null"
953 : "Test command for checking `tramp-awk-encode' and `tramp-awk-decode'.")
954 :
955 : (defconst tramp-stat-marker "/////"
956 : "Marker in stat commands for file attributes.")
957 :
958 : (defconst tramp-stat-quoted-marker "\\/\\/\\/\\/\\/"
959 : "Quoted marker in stat commands for file attributes.")
960 :
961 : (defconst tramp-vc-registered-read-file-names
962 : "echo \"(\"
963 : while read file; do
964 : if %s \"$file\"; then
965 : echo \"(\\\"$file\\\" \\\"file-exists-p\\\" t)\"
966 : else
967 : echo \"(\\\"$file\\\" \\\"file-exists-p\\\" nil)\"
968 : fi
969 : if %s \"$file\"; then
970 : echo \"(\\\"$file\\\" \\\"file-readable-p\\\" t)\"
971 : else
972 : echo \"(\\\"$file\\\" \\\"file-readable-p\\\" nil)\"
973 : fi
974 : done
975 : echo \")\""
976 : "Script to check existence of VC related files.
977 : It must be send formatted with two strings; the tests for file
978 : existence, and file readability. Input shall be read via
979 : here-document, otherwise the command could exceed maximum length
980 : of command line.")
981 :
982 : ;; New handlers should be added here.
983 : ;;;###tramp-autoload
984 : (defconst tramp-sh-file-name-handler-alist
985 : '(;; `access-file' performed by default handler.
986 : (add-name-to-file . tramp-sh-handle-add-name-to-file)
987 : ;; `byte-compiler-base-file-name' performed by default handler.
988 : (copy-directory . tramp-sh-handle-copy-directory)
989 : (copy-file . tramp-sh-handle-copy-file)
990 : (delete-directory . tramp-sh-handle-delete-directory)
991 : (delete-file . tramp-sh-handle-delete-file)
992 : ;; `diff-latest-backup-file' performed by default handler.
993 : (directory-file-name . tramp-handle-directory-file-name)
994 : (directory-files . tramp-handle-directory-files)
995 : (directory-files-and-attributes
996 : . tramp-sh-handle-directory-files-and-attributes)
997 : (dired-compress-file . tramp-sh-handle-dired-compress-file)
998 : (dired-uncache . tramp-handle-dired-uncache)
999 : (expand-file-name . tramp-sh-handle-expand-file-name)
1000 : (file-accessible-directory-p . tramp-handle-file-accessible-directory-p)
1001 : (file-acl . tramp-sh-handle-file-acl)
1002 : (file-attributes . tramp-sh-handle-file-attributes)
1003 : (file-directory-p . tramp-sh-handle-file-directory-p)
1004 : (file-equal-p . tramp-handle-file-equal-p)
1005 : (file-executable-p . tramp-sh-handle-file-executable-p)
1006 : (file-exists-p . tramp-sh-handle-file-exists-p)
1007 : (file-in-directory-p . tramp-handle-file-in-directory-p)
1008 : (file-local-copy . tramp-sh-handle-file-local-copy)
1009 : (file-modes . tramp-handle-file-modes)
1010 : (file-name-all-completions . tramp-sh-handle-file-name-all-completions)
1011 : (file-name-as-directory . tramp-handle-file-name-as-directory)
1012 : (file-name-case-insensitive-p . tramp-handle-file-name-case-insensitive-p)
1013 : (file-name-completion . tramp-handle-file-name-completion)
1014 : (file-name-directory . tramp-handle-file-name-directory)
1015 : (file-name-nondirectory . tramp-handle-file-name-nondirectory)
1016 : ;; `file-name-sans-versions' performed by default handler.
1017 : (file-newer-than-file-p . tramp-sh-handle-file-newer-than-file-p)
1018 : (file-notify-add-watch . tramp-sh-handle-file-notify-add-watch)
1019 : (file-notify-rm-watch . tramp-handle-file-notify-rm-watch)
1020 : (file-notify-valid-p . tramp-handle-file-notify-valid-p)
1021 : (file-ownership-preserved-p . tramp-sh-handle-file-ownership-preserved-p)
1022 : (file-readable-p . tramp-sh-handle-file-readable-p)
1023 : (file-regular-p . tramp-handle-file-regular-p)
1024 : (file-remote-p . tramp-handle-file-remote-p)
1025 : (file-selinux-context . tramp-sh-handle-file-selinux-context)
1026 : (file-symlink-p . tramp-handle-file-symlink-p)
1027 : (file-truename . tramp-sh-handle-file-truename)
1028 : (file-writable-p . tramp-sh-handle-file-writable-p)
1029 : (find-backup-file-name . tramp-handle-find-backup-file-name)
1030 : ;; `find-file-noselect' performed by default handler.
1031 : ;; `get-file-buffer' performed by default handler.
1032 : (insert-directory . tramp-sh-handle-insert-directory)
1033 : (insert-file-contents . tramp-handle-insert-file-contents)
1034 : (load . tramp-handle-load)
1035 : (make-auto-save-file-name . tramp-handle-make-auto-save-file-name)
1036 : (make-directory . tramp-sh-handle-make-directory)
1037 : (make-nearby-temp-file . tramp-handle-make-nearby-temp-file)
1038 : (make-symbolic-link . tramp-sh-handle-make-symbolic-link)
1039 : (process-file . tramp-sh-handle-process-file)
1040 : (rename-file . tramp-sh-handle-rename-file)
1041 : (set-file-acl . tramp-sh-handle-set-file-acl)
1042 : (set-file-modes . tramp-sh-handle-set-file-modes)
1043 : (set-file-selinux-context . tramp-sh-handle-set-file-selinux-context)
1044 : (set-file-times . tramp-sh-handle-set-file-times)
1045 : (set-visited-file-modtime . tramp-sh-handle-set-visited-file-modtime)
1046 : (shell-command . tramp-handle-shell-command)
1047 : (start-file-process . tramp-sh-handle-start-file-process)
1048 : (substitute-in-file-name . tramp-handle-substitute-in-file-name)
1049 : (temporary-file-directory . tramp-handle-temporary-file-directory)
1050 : (unhandled-file-name-directory . ignore)
1051 : (vc-registered . tramp-sh-handle-vc-registered)
1052 : (verify-visited-file-modtime . tramp-sh-handle-verify-visited-file-modtime)
1053 : (write-region . tramp-sh-handle-write-region))
1054 : "Alist of handler functions.
1055 : Operations not mentioned here will be handled by the normal Emacs functions.")
1056 :
1057 : ;;; File Name Handler Functions:
1058 :
1059 : (defun tramp-sh-handle-make-symbolic-link
1060 : (filename linkname &optional ok-if-already-exists)
1061 : "Like `make-symbolic-link' for Tramp files.
1062 : If LINKNAME is a non-Tramp file, it is used verbatim as the target of
1063 : the symlink. If LINKNAME is a Tramp file, only the localname component is
1064 : used as the target of the symlink.
1065 :
1066 : If LINKNAME is a Tramp file and the localname component is relative, then
1067 : it is expanded first, before the localname component is taken. Note that
1068 : this can give surprising results if the user/host for the source and
1069 : target of the symlink differ."
1070 164 : (with-parsed-tramp-file-name linkname l
1071 162 : (let ((ln (tramp-get-remote-ln l))
1072 162 : (cwd (tramp-run-real-handler
1073 162 : 'file-name-directory (list l-localname))))
1074 162 : (unless ln
1075 0 : (tramp-error
1076 0 : l 'file-error
1077 162 : "Making a symbolic link. ln(1) does not exist on the remote host."))
1078 :
1079 : ;; Do the 'confirm if exists' thing.
1080 162 : (when (file-exists-p linkname)
1081 : ;; What to do?
1082 4 : (if (or (null ok-if-already-exists) ; not allowed to exist
1083 2 : (and (numberp ok-if-already-exists)
1084 0 : (not (yes-or-no-p
1085 0 : (format
1086 : "File %s already exists; make it a link anyway? "
1087 4 : l-localname)))))
1088 2 : (tramp-error l 'file-already-exists l-localname)
1089 160 : (delete-file linkname)))
1090 :
1091 : ;; If FILENAME is a Tramp name, use just the localname component.
1092 160 : (when (tramp-tramp-file-p filename)
1093 160 : (setq filename
1094 160 : (tramp-file-name-localname
1095 160 : (tramp-dissect-file-name (expand-file-name filename)))))
1096 :
1097 160 : (tramp-flush-file-property l (file-name-directory l-localname))
1098 160 : (tramp-flush-file-property l l-localname)
1099 :
1100 : ;; Right, they are on the same host, regardless of user, method,
1101 : ;; etc. We now make the link on the remote machine. This will
1102 : ;; occur as the user that FILENAME belongs to.
1103 160 : (and (tramp-send-command-and-check
1104 160 : l (format "cd %s" (tramp-shell-quote-argument cwd)))
1105 160 : (tramp-send-command-and-check
1106 160 : l (format
1107 : "%s -sf %s %s"
1108 160 : ln
1109 160 : (tramp-shell-quote-argument filename)
1110 : ;; The command could exceed PATH_MAX, so we use
1111 : ;; relative file names. However, relative file names
1112 : ;; could start with "-". `tramp-shell-quote-argument'
1113 : ;; does not handle this, we must do it ourselves.
1114 160 : (tramp-shell-quote-argument
1115 160 : (concat "./" (file-name-nondirectory l-localname)))))))))
1116 :
1117 : (defun tramp-sh-handle-file-truename (filename)
1118 : "Like `file-truename' for Tramp files."
1119 1543 : (format
1120 : "%s%s"
1121 1543 : (with-parsed-tramp-file-name (expand-file-name filename) nil
1122 1543 : (tramp-make-tramp-file-name
1123 1543 : method user domain host port
1124 3861 : (with-tramp-file-property v localname "file-truename"
1125 775 : (let ((result nil) ; result steps in reverse order
1126 775 : (quoted (tramp-compat-file-name-quoted-p localname))
1127 775 : (localname (tramp-compat-file-name-unquote localname)))
1128 775 : (tramp-message v 4 "Finding true name for `%s'" filename)
1129 775 : (cond
1130 : ;; Use GNU readlink --canonicalize-missing where available.
1131 775 : ((tramp-get-remote-readlink v)
1132 467 : (tramp-send-command-and-check
1133 467 : v
1134 467 : (format "%s --canonicalize-missing %s"
1135 467 : (tramp-get-remote-readlink v)
1136 467 : (tramp-shell-quote-argument localname)))
1137 467 : (with-current-buffer (tramp-get-connection-buffer v)
1138 467 : (goto-char (point-min))
1139 467 : (setq result (buffer-substring (point-min) (point-at-eol)))))
1140 :
1141 : ;; Use Perl implementation.
1142 308 : ((and (tramp-get-remote-perl v)
1143 154 : (tramp-get-connection-property v "perl-file-spec" nil)
1144 308 : (tramp-get-connection-property v "perl-cwd-realpath" nil))
1145 154 : (tramp-maybe-send-script
1146 154 : v tramp-perl-file-truename "tramp_perl_file_truename")
1147 154 : (setq result
1148 154 : (tramp-send-command-and-read
1149 154 : v
1150 154 : (format "tramp_perl_file_truename %s"
1151 154 : (tramp-shell-quote-argument localname)))))
1152 :
1153 : ;; Do it yourself.
1154 154 : (t (let ((steps (split-string localname "/" 'omit))
1155 : (thisstep nil)
1156 : (numchase 0)
1157 : ;; Don't make the following value larger than
1158 : ;; necessary. People expect an error message in a
1159 : ;; timely fashion when something is wrong;
1160 : ;; otherwise they might think that Emacs is hung.
1161 : ;; Of course, correctness has to come first.
1162 : (numchase-limit 20)
1163 : symlink-target)
1164 1091 : (while (and steps (< numchase numchase-limit))
1165 1874 : (setq thisstep (pop steps))
1166 937 : (tramp-message
1167 937 : v 5 "Check %s"
1168 937 : (mapconcat 'identity
1169 937 : (append '("") (reverse result) (list thisstep))
1170 937 : "/"))
1171 937 : (setq symlink-target
1172 937 : (tramp-compat-file-attribute-type
1173 937 : (file-attributes
1174 937 : (tramp-make-tramp-file-name
1175 937 : method user domain host port
1176 937 : (mapconcat 'identity
1177 937 : (append '("")
1178 937 : (reverse result)
1179 937 : (list thisstep))
1180 937 : "/")))))
1181 937 : (cond ((string= "." thisstep)
1182 0 : (tramp-message v 5 "Ignoring step `.'"))
1183 937 : ((string= ".." thisstep)
1184 0 : (tramp-message v 5 "Processing step `..'")
1185 0 : (pop result))
1186 937 : ((stringp symlink-target)
1187 : ;; It's a symlink, follow it.
1188 19 : (tramp-message
1189 19 : v 5 "Follow symlink to %s" symlink-target)
1190 19 : (setq numchase (1+ numchase))
1191 19 : (when (file-name-absolute-p symlink-target)
1192 19 : (setq result nil))
1193 : ;; If the symlink was absolute, we'll get a
1194 : ;; string like "/user@host:/some/target";
1195 : ;; extract the "/some/target" part from it.
1196 19 : (when (tramp-tramp-file-p symlink-target)
1197 0 : (unless (tramp-equal-remote filename symlink-target)
1198 0 : (tramp-error
1199 0 : v 'file-error
1200 : "Symlink target `%s' on wrong host"
1201 0 : symlink-target))
1202 19 : (setq symlink-target localname))
1203 19 : (setq steps
1204 19 : (append
1205 19 : (split-string symlink-target "/" 'omit) steps)))
1206 : (t
1207 : ;; It's a file.
1208 937 : (setq result (cons thisstep result)))))
1209 154 : (when (>= numchase numchase-limit)
1210 0 : (tramp-error
1211 0 : v 'file-error
1212 154 : "Maximum number (%d) of symlinks exceeded" numchase-limit))
1213 154 : (setq result (reverse result))
1214 : ;; Combine list to form string.
1215 154 : (setq result
1216 154 : (if result
1217 154 : (mapconcat 'identity (cons "" result) "/")
1218 154 : "/"))
1219 154 : (when (string= "" result)
1220 775 : (setq result "/")))))
1221 :
1222 775 : (when quoted (setq result (tramp-compat-file-name-quote result)))
1223 775 : (tramp-message v 4 "True name of `%s' is `%s'" localname result)
1224 1543 : result))))
1225 :
1226 : ;; Preserve trailing "/".
1227 1543 : (if (string-equal (file-name-nondirectory filename) "") "/" "")))
1228 :
1229 : ;; Basic functions.
1230 :
1231 : (defun tramp-sh-handle-file-exists-p (filename)
1232 : "Like `file-exists-p' for Tramp files."
1233 3085 : (with-parsed-tramp-file-name filename nil
1234 8225 : (with-tramp-file-property v localname "file-exists-p"
1235 2055 : (or (not (null (tramp-get-file-property
1236 2055 : v localname "file-attributes-integer" nil)))
1237 1886 : (not (null (tramp-get-file-property
1238 1886 : v localname "file-attributes-string" nil)))
1239 1884 : (tramp-send-command-and-check
1240 1884 : v
1241 1884 : (format
1242 : "%s %s"
1243 1884 : (tramp-get-file-exists-command v)
1244 3085 : (tramp-shell-quote-argument localname)))))))
1245 :
1246 : (defun tramp-sh-handle-file-attributes (filename &optional id-format)
1247 : "Like `file-attributes' for Tramp files."
1248 3497 : (unless id-format (setq id-format 'integer))
1249 3497 : (ignore-errors
1250 : ;; Don't modify `last-coding-system-used' by accident.
1251 3497 : (let ((last-coding-system-used last-coding-system-used))
1252 3497 : (with-parsed-tramp-file-name (expand-file-name filename) nil
1253 3497 : (with-tramp-file-property
1254 8380 : v localname (format "file-attributes-%s" id-format)
1255 1386 : (save-excursion
1256 1386 : (tramp-convert-file-attributes
1257 1386 : v
1258 1386 : (or
1259 1386 : (cond
1260 1386 : ((tramp-get-remote-stat v)
1261 871 : (tramp-do-file-attributes-with-stat v localname id-format))
1262 515 : ((tramp-get-remote-perl v)
1263 162 : (tramp-do-file-attributes-with-perl v localname id-format))
1264 1386 : (t nil))
1265 : ;; The scripts could fail, for example with huge file size.
1266 3497 : (tramp-do-file-attributes-with-ls v localname id-format)))))))))
1267 :
1268 : (defun tramp-do-file-attributes-with-ls (vec localname &optional id-format)
1269 : "Implement `file-attributes' for Tramp files using the ls(1) command."
1270 717 : (let (symlinkp dirp
1271 : res-inode res-filemodes res-numlinks
1272 : res-uid res-gid res-size res-symlink-target)
1273 717 : (tramp-message vec 5 "file attributes with ls: %s" localname)
1274 : ;; We cannot send all three commands combined, it could exceed
1275 : ;; NAME_MAX or PATH_MAX. Happened on macOS, for example.
1276 717 : (when (or (tramp-send-command-and-check
1277 717 : vec
1278 717 : (format "%s %s"
1279 717 : (tramp-get-file-exists-command vec)
1280 717 : (tramp-shell-quote-argument localname)))
1281 422 : (tramp-send-command-and-check
1282 422 : vec
1283 422 : (format "%s -h %s"
1284 422 : (tramp-get-test-command vec)
1285 717 : (tramp-shell-quote-argument localname))))
1286 295 : (tramp-send-command
1287 295 : vec
1288 295 : (format "%s %s %s %s"
1289 295 : (tramp-get-ls-command vec)
1290 295 : (if (eq id-format 'integer) "-ildn" "-ild")
1291 : ;; On systems which have no quoting style, file names
1292 : ;; with special characters could fail.
1293 295 : (cond
1294 295 : ((tramp-get-ls-command-with-quoting-style vec)
1295 : "--quoting-style=c")
1296 0 : ((tramp-get-ls-command-with-w-option vec)
1297 : "-w")
1298 295 : (t ""))
1299 295 : (tramp-shell-quote-argument localname)))
1300 : ;; Parse `ls -l' output ...
1301 295 : (with-current-buffer (tramp-get-buffer vec)
1302 295 : (when (> (buffer-size) 0)
1303 295 : (goto-char (point-min))
1304 : ;; ... inode
1305 295 : (setq res-inode (read (current-buffer)))
1306 : ;; ... file mode flags
1307 295 : (setq res-filemodes (symbol-name (read (current-buffer))))
1308 : ;; ... number links
1309 295 : (setq res-numlinks (read (current-buffer)))
1310 : ;; ... uid and gid
1311 295 : (setq res-uid (read (current-buffer)))
1312 295 : (setq res-gid (read (current-buffer)))
1313 295 : (if (eq id-format 'integer)
1314 289 : (progn
1315 289 : (unless (numberp res-uid)
1316 289 : (setq res-uid tramp-unknown-id-integer))
1317 289 : (unless (numberp res-gid)
1318 289 : (setq res-gid tramp-unknown-id-integer)))
1319 6 : (progn
1320 6 : (unless (stringp res-uid) (setq res-uid (symbol-name res-uid)))
1321 295 : (unless (stringp res-gid) (setq res-gid (symbol-name res-gid)))))
1322 : ;; ... size
1323 295 : (setq res-size (read (current-buffer)))
1324 : ;; From the file modes, figure out other stuff.
1325 295 : (setq symlinkp (eq ?l (aref res-filemodes 0)))
1326 295 : (setq dirp (eq ?d (aref res-filemodes 0)))
1327 : ;; If symlink, find out file name pointed to.
1328 295 : (when symlinkp
1329 38 : (search-forward "-> ")
1330 38 : (setq res-symlink-target
1331 38 : (if (tramp-get-ls-command-with-quoting-style vec)
1332 38 : (read (current-buffer))
1333 295 : (buffer-substring (point) (point-at-eol)))))
1334 : ;; Return data gathered.
1335 295 : (list
1336 : ;; 0. t for directory, string (name linked to) for symbolic
1337 : ;; link, or nil.
1338 295 : (or dirp res-symlink-target)
1339 : ;; 1. Number of links to file.
1340 295 : res-numlinks
1341 : ;; 2. File uid.
1342 295 : res-uid
1343 : ;; 3. File gid.
1344 295 : res-gid
1345 : ;; 4. Last access time, as a list of integers. Normally
1346 : ;; this would be in the same format as `current-time', but
1347 : ;; the subseconds part is not currently implemented, and
1348 : ;; (0 0) denotes an unknown time.
1349 : ;; 5. Last modification time, likewise.
1350 : ;; 6. Last status change time, likewise.
1351 : '(0 0) '(0 0) '(0 0) ;CCC how to find out?
1352 : ;; 7. Size in bytes (-1, if number is out of range).
1353 295 : res-size
1354 : ;; 8. File modes, as a string of ten letters or dashes as in ls -l.
1355 295 : res-filemodes
1356 : ;; 9. t if file's gid would change if file were deleted and
1357 : ;; recreated. Will be set in `tramp-convert-file-attributes'.
1358 : t
1359 : ;; 10. Inode number.
1360 295 : res-inode
1361 : ;; 11. Device number. Will be replaced by a virtual device number.
1362 717 : -1))))))
1363 :
1364 : (defun tramp-do-file-attributes-with-perl
1365 : (vec localname &optional id-format)
1366 : "Implement `file-attributes' for Tramp files using a Perl script."
1367 162 : (tramp-message vec 5 "file attributes with perl: %s" localname)
1368 162 : (tramp-maybe-send-script
1369 162 : vec tramp-perl-file-attributes "tramp_perl_file_attributes")
1370 162 : (tramp-send-command-and-read
1371 162 : vec
1372 162 : (format "tramp_perl_file_attributes %s %s"
1373 162 : (tramp-shell-quote-argument localname) id-format)))
1374 :
1375 : (defun tramp-do-file-attributes-with-stat
1376 : (vec localname &optional id-format)
1377 : "Implement `file-attributes' for Tramp files using stat(1) command."
1378 871 : (tramp-message vec 5 "file attributes with stat: %s" localname)
1379 871 : (tramp-send-command-and-read
1380 871 : vec
1381 871 : (format
1382 871 : (concat
1383 : ;; On Opsware, pdksh (which is the true name of ksh there)
1384 : ;; doesn't parse correctly the sequence "((". Therefore, we add
1385 : ;; a space. Apostrophes in the stat output are masked as
1386 : ;; `tramp-stat-marker', in order to make a proper shell escape of
1387 : ;; them in file names.
1388 : "( (%s %s || %s -h %s) && (%s -c "
1389 : "'((%s%%N%s) %%h %s %s %%Xe0 %%Ye0 %%Ze0 %%se0 %s%%A%s t %%ie0 -1)' "
1390 871 : "%s | sed -e 's/\"/\\\\\"/g' -e 's/%s/\"/g') || echo nil)")
1391 871 : (tramp-get-file-exists-command vec)
1392 871 : (tramp-shell-quote-argument localname)
1393 871 : (tramp-get-test-command vec)
1394 871 : (tramp-shell-quote-argument localname)
1395 871 : (tramp-get-remote-stat vec)
1396 871 : tramp-stat-marker tramp-stat-marker
1397 871 : (if (eq id-format 'integer)
1398 871 : "%ue0" (concat tramp-stat-marker "%U" tramp-stat-marker))
1399 871 : (if (eq id-format 'integer)
1400 871 : "%ge0" (concat tramp-stat-marker "%G" tramp-stat-marker))
1401 871 : tramp-stat-marker tramp-stat-marker
1402 871 : (tramp-shell-quote-argument localname)
1403 871 : tramp-stat-quoted-marker)))
1404 :
1405 : (defun tramp-sh-handle-set-visited-file-modtime (&optional time-list)
1406 : "Like `set-visited-file-modtime' for Tramp files."
1407 0 : (unless (buffer-file-name)
1408 0 : (error "Can't set-visited-file-modtime: buffer `%s' not visiting a file"
1409 0 : (buffer-name)))
1410 0 : (if time-list
1411 0 : (tramp-run-real-handler 'set-visited-file-modtime (list time-list))
1412 0 : (let ((f (buffer-file-name))
1413 : coding-system-used)
1414 0 : (with-parsed-tramp-file-name f nil
1415 0 : (let* ((remote-file-name-inhibit-cache t)
1416 0 : (attr (file-attributes f))
1417 : ;; '(-1 65535) means file doesn't exists yet.
1418 0 : (modtime (or (tramp-compat-file-attribute-modification-time attr)
1419 0 : '(-1 65535))))
1420 0 : (setq coding-system-used last-coding-system-used)
1421 : ;; We use '(0 0) as a don't-know value. See also
1422 : ;; `tramp-do-file-attributes-with-ls'.
1423 0 : (if (not (equal modtime '(0 0)))
1424 0 : (tramp-run-real-handler 'set-visited-file-modtime (list modtime))
1425 0 : (progn
1426 0 : (tramp-send-command
1427 0 : v
1428 0 : (format "%s -ild %s"
1429 0 : (tramp-get-ls-command v)
1430 0 : (tramp-shell-quote-argument localname)))
1431 0 : (setq attr (buffer-substring (point) (point-at-eol))))
1432 0 : (tramp-set-file-property
1433 0 : v localname "visited-file-modtime-ild" attr))
1434 0 : (setq last-coding-system-used coding-system-used)
1435 0 : nil)))))
1436 :
1437 : ;; This function makes the same assumption as
1438 : ;; `tramp-sh-handle-set-visited-file-modtime'.
1439 : (defun tramp-sh-handle-verify-visited-file-modtime (&optional buf)
1440 : "Like `verify-visited-file-modtime' for Tramp files.
1441 : At the time `verify-visited-file-modtime' calls this function, we
1442 : already know that the buffer is visiting a file and that
1443 : `visited-file-modtime' does not return 0. Do not call this
1444 : function directly, unless those two cases are already taken care
1445 : of."
1446 0 : (with-current-buffer (or buf (current-buffer))
1447 0 : (let ((f (buffer-file-name)))
1448 : ;; There is no file visiting the buffer, or the buffer has no
1449 : ;; recorded last modification time, or there is no established
1450 : ;; connection.
1451 0 : (if (or (not f)
1452 0 : (eq (visited-file-modtime) 0)
1453 0 : (not (file-remote-p f nil 'connected)))
1454 : t
1455 0 : (with-parsed-tramp-file-name f nil
1456 0 : (let* ((remote-file-name-inhibit-cache t)
1457 0 : (attr (file-attributes f))
1458 0 : (modtime (tramp-compat-file-attribute-modification-time attr))
1459 0 : (mt (visited-file-modtime)))
1460 :
1461 0 : (cond
1462 : ;; File exists, and has a known modtime.
1463 0 : ((and attr (not (equal modtime '(0 0))))
1464 0 : (< (abs (tramp-time-diff
1465 0 : modtime
1466 : ;; For compatibility, deal with both the old
1467 : ;; (HIGH . LOW) and the new (HIGH LOW) return
1468 : ;; values of `visited-file-modtime'.
1469 0 : (if (atom (cdr mt))
1470 0 : (list (car mt) (cdr mt))
1471 0 : mt)))
1472 0 : 2))
1473 : ;; Modtime has the don't know value.
1474 0 : (attr
1475 0 : (tramp-send-command
1476 0 : v
1477 0 : (format "%s -ild %s"
1478 0 : (tramp-get-ls-command v)
1479 0 : (tramp-shell-quote-argument localname)))
1480 0 : (with-current-buffer (tramp-get-buffer v)
1481 0 : (setq attr (buffer-substring (point) (point-at-eol))))
1482 0 : (equal
1483 0 : attr
1484 0 : (tramp-get-file-property
1485 0 : v localname "visited-file-modtime-ild" "")))
1486 : ;; If file does not exist, say it is not modified if and
1487 : ;; only if that agrees with the buffer's record.
1488 0 : (t (equal mt '(-1 65535))))))))))
1489 :
1490 : (defun tramp-sh-handle-set-file-modes (filename mode)
1491 : "Like `set-file-modes' for Tramp files."
1492 99 : (with-parsed-tramp-file-name filename nil
1493 99 : (tramp-flush-file-property v (file-name-directory localname))
1494 99 : (tramp-flush-file-property v localname)
1495 : ;; FIXME: extract the proper text from chmod's stderr.
1496 99 : (tramp-barf-unless-okay
1497 99 : v
1498 99 : (format "chmod %o %s" mode (tramp-shell-quote-argument localname))
1499 99 : "Error while changing file's mode %s" filename)))
1500 :
1501 : (defun tramp-sh-handle-set-file-times (filename &optional time)
1502 : "Like `set-file-times' for Tramp files."
1503 10 : (with-parsed-tramp-file-name filename nil
1504 10 : (when (tramp-get-remote-touch v)
1505 10 : (tramp-flush-file-property v (file-name-directory localname))
1506 10 : (tramp-flush-file-property v localname)
1507 10 : (let ((time (if (or (null time) (equal time '(0 0)))
1508 2 : (current-time)
1509 10 : time)))
1510 10 : (tramp-send-command-and-check
1511 10 : v (format
1512 : "env TZ=UTC %s %s %s"
1513 10 : (tramp-get-remote-touch v)
1514 10 : (if (tramp-get-connection-property v "touch-t" nil)
1515 10 : (format "-t %s" (format-time-string "%Y%m%d%H%M.%S" time t))
1516 10 : "")
1517 10 : (tramp-shell-quote-argument localname)))))))
1518 :
1519 : (defun tramp-set-file-uid-gid (filename &optional uid gid)
1520 : "Set the ownership for FILENAME.
1521 : If UID and GID are provided, these values are used; otherwise uid
1522 : and gid of the corresponding user is taken. Both parameters must
1523 : be non-negative integers."
1524 : ;; Modern Unices allow chown only for root. So we might need
1525 : ;; another implementation, see `dired-do-chown'. OTOH, it is mostly
1526 : ;; working with su(do)? when it is needed, so it shall succeed in
1527 : ;; the majority of cases.
1528 : ;; Don't modify `last-coding-system-used' by accident.
1529 675 : (let ((last-coding-system-used last-coding-system-used))
1530 675 : (if (tramp-tramp-file-p filename)
1531 405 : (with-parsed-tramp-file-name filename nil
1532 405 : (if (and (zerop (user-uid)) (tramp-local-host-p v))
1533 : ;; If we are root on the local host, we can do it directly.
1534 0 : (tramp-set-file-uid-gid localname uid gid)
1535 405 : (let ((uid (or (and (natnump uid) uid)
1536 405 : (tramp-get-remote-uid v 'integer)))
1537 405 : (gid (or (and (natnump gid) gid)
1538 405 : (tramp-get-remote-gid v 'integer))))
1539 405 : (tramp-send-command
1540 405 : v (format
1541 405 : "chown %d:%d %s" uid gid
1542 405 : (tramp-shell-quote-argument localname))))))
1543 :
1544 : ;; We handle also the local part, because there doesn't exist
1545 : ;; `set-file-uid-gid'. On W32 "chown" does not work.
1546 270 : (unless (memq system-type '(ms-dos windows-nt))
1547 270 : (let ((uid (or (and (natnump uid) uid) (tramp-get-local-uid 'integer)))
1548 270 : (gid (or (and (natnump gid) gid) (tramp-get-local-gid 'integer))))
1549 270 : (tramp-call-process
1550 : nil "chown" nil nil nil
1551 675 : (format "%d:%d" uid gid) (shell-quote-argument filename)))))))
1552 :
1553 : (defun tramp-remote-selinux-p (vec)
1554 : "Check, whether SELINUX is enabled on the remote host."
1555 0 : (with-tramp-connection-property (tramp-get-connection-process vec) "selinux-p"
1556 0 : (tramp-send-command-and-check vec "selinuxenabled")))
1557 :
1558 : (defun tramp-sh-handle-file-selinux-context (filename)
1559 : "Like `file-selinux-context' for Tramp files."
1560 0 : (with-parsed-tramp-file-name filename nil
1561 0 : (with-tramp-file-property v localname "file-selinux-context"
1562 0 : (let ((context '(nil nil nil nil))
1563 0 : (regexp (concat "\\([a-z0-9_]+\\):" "\\([a-z0-9_]+\\):"
1564 0 : "\\([a-z0-9_]+\\):" "\\([a-z0-9_]+\\)")))
1565 0 : (when (and (tramp-remote-selinux-p v)
1566 0 : (tramp-send-command-and-check
1567 0 : v (format
1568 : "%s -d -Z %s"
1569 0 : (tramp-get-ls-command v)
1570 0 : (tramp-shell-quote-argument localname))))
1571 0 : (with-current-buffer (tramp-get-connection-buffer v)
1572 0 : (goto-char (point-min))
1573 0 : (when (re-search-forward regexp (point-at-eol) t)
1574 0 : (setq context (list (match-string 1) (match-string 2)
1575 0 : (match-string 3) (match-string 4))))))
1576 : ;; Return the context.
1577 0 : context))))
1578 :
1579 : (defun tramp-sh-handle-set-file-selinux-context (filename context)
1580 : "Like `set-file-selinux-context' for Tramp files."
1581 0 : (with-parsed-tramp-file-name filename nil
1582 0 : (when (and (consp context)
1583 0 : (tramp-remote-selinux-p v))
1584 0 : (let ((user (and (stringp (nth 0 context)) (nth 0 context)))
1585 0 : (role (and (stringp (nth 1 context)) (nth 1 context)))
1586 0 : (type (and (stringp (nth 2 context)) (nth 2 context)))
1587 0 : (range (and (stringp (nth 3 context)) (nth 3 context))))
1588 0 : (when (tramp-send-command-and-check
1589 0 : v (format "chcon %s %s %s %s %s"
1590 0 : (if user (format "--user=%s" user) "")
1591 0 : (if role (format "--role=%s" role) "")
1592 0 : (if type (format "--type=%s" type) "")
1593 0 : (if range (format "--range=%s" range) "")
1594 0 : (tramp-shell-quote-argument localname)))
1595 0 : (if (and user role type range)
1596 0 : (tramp-set-file-property
1597 0 : v localname "file-selinux-context" context)
1598 0 : (tramp-set-file-property
1599 0 : v localname "file-selinux-context" 'undef))
1600 0 : t)))))
1601 :
1602 : (defun tramp-remote-acl-p (vec)
1603 : "Check, whether ACL is enabled on the remote host."
1604 0 : (with-tramp-connection-property (tramp-get-connection-process vec) "acl-p"
1605 0 : (tramp-send-command-and-check vec "getfacl /")))
1606 :
1607 : (defun tramp-sh-handle-file-acl (filename)
1608 : "Like `file-acl' for Tramp files."
1609 0 : (with-parsed-tramp-file-name filename nil
1610 0 : (with-tramp-file-property v localname "file-acl"
1611 0 : (when (and (tramp-remote-acl-p v)
1612 0 : (tramp-send-command-and-check
1613 0 : v (format
1614 : "getfacl -ac %s"
1615 0 : (tramp-shell-quote-argument localname))))
1616 0 : (with-current-buffer (tramp-get-connection-buffer v)
1617 0 : (goto-char (point-max))
1618 0 : (delete-blank-lines)
1619 0 : (when (> (point-max) (point-min))
1620 0 : (substring-no-properties (buffer-string))))))))
1621 :
1622 : (defun tramp-sh-handle-set-file-acl (filename acl-string)
1623 : "Like `set-file-acl' for Tramp files."
1624 0 : (with-parsed-tramp-file-name (expand-file-name filename) nil
1625 0 : (if (and (stringp acl-string) (tramp-remote-acl-p v)
1626 0 : (progn
1627 0 : (tramp-send-command
1628 0 : v (format "setfacl --set-file=- %s <<'%s'\n%s\n%s\n"
1629 0 : (tramp-shell-quote-argument localname)
1630 0 : tramp-end-of-heredoc
1631 0 : acl-string
1632 0 : tramp-end-of-heredoc))
1633 0 : (tramp-send-command-and-check v nil)))
1634 : ;; Success.
1635 0 : (progn
1636 0 : (tramp-set-file-property v localname "file-acl" acl-string)
1637 0 : t)
1638 : ;; In case of errors, we return nil.
1639 0 : (tramp-set-file-property v localname "file-acl-string" 'undef)
1640 0 : nil)))
1641 :
1642 : ;; Simple functions using the `test' command.
1643 :
1644 : (defun tramp-sh-handle-file-executable-p (filename)
1645 : "Like `file-executable-p' for Tramp files."
1646 4 : (with-parsed-tramp-file-name filename nil
1647 12 : (with-tramp-file-property v localname "file-executable-p"
1648 : ;; Examine `file-attributes' cache to see if request can be
1649 : ;; satisfied without remote operation.
1650 4 : (or (tramp-check-cached-permissions v ?x)
1651 4 : (tramp-run-test "-x" filename)))))
1652 :
1653 : (defun tramp-sh-handle-file-readable-p (filename)
1654 : "Like `file-readable-p' for Tramp files."
1655 46 : (with-parsed-tramp-file-name filename nil
1656 138 : (with-tramp-file-property v localname "file-readable-p"
1657 : ;; Examine `file-attributes' cache to see if request can be
1658 : ;; satisfied without remote operation.
1659 46 : (or (tramp-check-cached-permissions v ?r)
1660 46 : (tramp-run-test "-r" filename)))))
1661 :
1662 : ;; When the remote shell is started, it looks for a shell which groks
1663 : ;; tilde expansion. Here, we assume that all shells which grok tilde
1664 : ;; expansion will also provide a `test' command which groks `-nt' (for
1665 : ;; newer than). If this breaks, tell me about it and I'll try to do
1666 : ;; something smarter about it.
1667 : (defun tramp-sh-handle-file-newer-than-file-p (file1 file2)
1668 : "Like `file-newer-than-file-p' for Tramp files."
1669 6 : (cond ((not (file-exists-p file1))
1670 : nil)
1671 4 : ((not (file-exists-p file2))
1672 : t)
1673 : ;; We are sure both files exist at this point.
1674 : (t
1675 2 : (save-excursion
1676 : ;; We try to get the mtime of both files. If they are not
1677 : ;; equal to the "dont-know" value, then we subtract the times
1678 : ;; and obtain the result.
1679 2 : (let ((fa1 (file-attributes file1))
1680 2 : (fa2 (file-attributes file2)))
1681 2 : (if (and
1682 2 : (not
1683 2 : (equal (tramp-compat-file-attribute-modification-time fa1)
1684 2 : '(0 0)))
1685 2 : (not
1686 2 : (equal (tramp-compat-file-attribute-modification-time fa2)
1687 2 : '(0 0))))
1688 2 : (> 0 (tramp-time-diff
1689 2 : (tramp-compat-file-attribute-modification-time fa2)
1690 2 : (tramp-compat-file-attribute-modification-time fa1)))
1691 : ;; If one of them is the dont-know value, then we can
1692 : ;; still try to run a shell command on the remote host.
1693 : ;; However, this only works if both files are Tramp
1694 : ;; files and both have the same method, same user, same
1695 : ;; host.
1696 0 : (unless (tramp-equal-remote file1 file2)
1697 0 : (with-parsed-tramp-file-name
1698 0 : (if (tramp-tramp-file-p file1) file1 file2) nil
1699 0 : (tramp-error
1700 0 : v 'file-error
1701 : "Files %s and %s must have same method, user, host"
1702 0 : file1 file2)))
1703 0 : (with-parsed-tramp-file-name file1 nil
1704 0 : (tramp-run-test2
1705 6 : (tramp-get-test-nt-command v) file1 file2))))))))
1706 :
1707 : ;; Functions implemented using the basic functions above.
1708 :
1709 : (defun tramp-sh-handle-file-directory-p (filename)
1710 : "Like `file-directory-p' for Tramp files."
1711 2230 : (with-parsed-tramp-file-name filename nil
1712 : ;; `file-directory-p' is used as predicate for file name completion.
1713 : ;; Sometimes, when a connection is not established yet, it is
1714 : ;; desirable to return t immediately for "/method:foo:". It can
1715 : ;; be expected that this is always a directory.
1716 2230 : (or (zerop (length localname))
1717 6122 : (with-tramp-file-property v localname "file-directory-p"
1718 2230 : (tramp-run-test "-d" filename)))))
1719 :
1720 : (defun tramp-sh-handle-file-writable-p (filename)
1721 : "Like `file-writable-p' for Tramp files."
1722 1526 : (with-parsed-tramp-file-name filename nil
1723 3182 : (with-tramp-file-property v localname "file-writable-p"
1724 130 : (if (file-exists-p filename)
1725 : ;; Examine `file-attributes' cache to see if request can be
1726 : ;; satisfied without remote operation.
1727 130 : (or (tramp-check-cached-permissions v ?w)
1728 130 : (tramp-run-test "-w" filename))
1729 : ;; If file doesn't exist, check if directory is writable.
1730 0 : (and (tramp-run-test "-d" (file-name-directory filename))
1731 1526 : (tramp-run-test "-w" (file-name-directory filename)))))))
1732 :
1733 : (defun tramp-sh-handle-file-ownership-preserved-p (filename &optional group)
1734 : "Like `file-ownership-preserved-p' for Tramp files."
1735 12 : (with-parsed-tramp-file-name filename nil
1736 34 : (with-tramp-file-property v localname "file-ownership-preserved-p"
1737 10 : (let ((attributes (file-attributes filename)))
1738 : ;; Return t if the file doesn't exist, since it's true that no
1739 : ;; information would be lost by an (attempted) delete and create.
1740 10 : (or (null attributes)
1741 6 : (and
1742 6 : (= (tramp-compat-file-attribute-user-id attributes)
1743 6 : (tramp-get-remote-uid v 'integer))
1744 6 : (or (not group)
1745 6 : (= (tramp-compat-file-attribute-group-id attributes)
1746 12 : (tramp-get-remote-gid v 'integer)))))))))
1747 :
1748 : ;; Directory listings.
1749 :
1750 : (defun tramp-sh-handle-directory-files-and-attributes
1751 : (directory &optional full match nosort id-format)
1752 : "Like `directory-files-and-attributes' for Tramp files."
1753 234 : (unless id-format (setq id-format 'integer))
1754 234 : (when (file-directory-p directory)
1755 234 : (setq directory (expand-file-name directory))
1756 234 : (let* ((temp
1757 234 : (copy-tree
1758 234 : (with-parsed-tramp-file-name directory nil
1759 234 : (with-tramp-file-property
1760 622 : v localname
1761 388 : (format "directory-files-and-attributes-%s" id-format)
1762 154 : (save-excursion
1763 154 : (mapcar
1764 : (lambda (x)
1765 409 : (cons (car x)
1766 409 : (tramp-convert-file-attributes v (cdr x))))
1767 154 : (or
1768 154 : (cond
1769 154 : ((tramp-get-remote-stat v)
1770 78 : (tramp-do-directory-files-and-attributes-with-stat
1771 78 : v localname id-format))
1772 76 : ((tramp-get-remote-perl v)
1773 38 : (tramp-do-directory-files-and-attributes-with-perl
1774 38 : v localname id-format))
1775 234 : (t nil)))))))))
1776 : result item)
1777 :
1778 891 : (while temp
1779 1314 : (setq item (pop temp))
1780 657 : (when (or (null match) (string-match match (car item)))
1781 195 : (when full
1782 195 : (setcar item (expand-file-name (car item) directory)))
1783 657 : (push item result)))
1784 :
1785 234 : (or (if nosort
1786 0 : result
1787 264 : (sort result (lambda (x y) (string< (car x) (car y)))))
1788 : ;; The scripts could fail, for example with huge file size.
1789 57 : (tramp-handle-directory-files-and-attributes
1790 234 : directory full match nosort id-format)))))
1791 :
1792 : (defun tramp-do-directory-files-and-attributes-with-perl
1793 : (vec localname &optional id-format)
1794 : "Implement `directory-files-and-attributes' for Tramp files using a Perl script."
1795 38 : (tramp-message vec 5 "directory-files-and-attributes with perl: %s" localname)
1796 38 : (tramp-maybe-send-script
1797 38 : vec tramp-perl-directory-files-and-attributes
1798 38 : "tramp_perl_directory_files_and_attributes")
1799 38 : (let ((object
1800 38 : (tramp-send-command-and-read
1801 38 : vec
1802 38 : (format "tramp_perl_directory_files_and_attributes %s %s"
1803 38 : (tramp-shell-quote-argument localname) id-format))))
1804 38 : (when (stringp object) (tramp-error vec 'file-error object))
1805 38 : object))
1806 :
1807 : (defun tramp-do-directory-files-and-attributes-with-stat
1808 : (vec localname &optional id-format)
1809 : "Implement `directory-files-and-attributes' for Tramp files using stat(1) command."
1810 78 : (tramp-message vec 5 "directory-files-and-attributes with stat: %s" localname)
1811 78 : (tramp-send-command-and-read
1812 78 : vec
1813 78 : (format
1814 78 : (concat
1815 : ;; We must care about file names with spaces, or starting with
1816 : ;; "-"; this would confuse xargs. "ls -aQ" might be a solution,
1817 : ;; but it does not work on all remote systems. Apostrophes in
1818 : ;; the stat output are masked as `tramp-stat-marker', in order to
1819 : ;; make a proper shell escape of them in file names.
1820 : "cd %s && echo \"(\"; (%s %s -a | "
1821 : "xargs %s -c "
1822 : "'(%s%%n%s (%s%%N%s) %%h %s %s %%Xe0 %%Ye0 %%Ze0 %%se0 %s%%A%s t %%ie0 -1)' "
1823 78 : "-- 2>/dev/null | sed -e 's/\"/\\\\\"/g' -e 's/%s/\"/g'); echo \")\"")
1824 78 : (tramp-shell-quote-argument localname)
1825 78 : (tramp-get-ls-command vec)
1826 : ;; On systems which have no quoting style, file names with special
1827 : ;; characters could fail.
1828 78 : (cond
1829 78 : ((tramp-get-ls-command-with-quoting-style vec)
1830 : "--quoting-style=shell")
1831 0 : ((tramp-get-ls-command-with-w-option vec)
1832 : "-w")
1833 78 : (t ""))
1834 78 : (tramp-get-remote-stat vec)
1835 78 : tramp-stat-marker tramp-stat-marker
1836 78 : tramp-stat-marker tramp-stat-marker
1837 78 : (if (eq id-format 'integer)
1838 78 : "%ue0" (concat tramp-stat-marker "%U" tramp-stat-marker))
1839 78 : (if (eq id-format 'integer)
1840 78 : "%ge0" (concat tramp-stat-marker "%G" tramp-stat-marker))
1841 78 : tramp-stat-marker tramp-stat-marker
1842 78 : tramp-stat-quoted-marker)))
1843 :
1844 : ;; This function should return "foo/" for directories and "bar" for
1845 : ;; files.
1846 : (defun tramp-sh-handle-file-name-all-completions (filename directory)
1847 : "Like `file-name-all-completions' for Tramp files."
1848 226 : (unless (save-match-data (string-match "/" filename))
1849 226 : (all-completions
1850 226 : filename
1851 226 : (with-parsed-tramp-file-name (expand-file-name directory) nil
1852 578 : (with-tramp-file-property v localname "file-name-all-completions"
1853 126 : (let (result)
1854 : ;; Get a list of directories and files, including reliably
1855 : ;; tagging the directories with a trailing "/". Because I
1856 : ;; rock. --daniel@danann.net
1857 126 : (tramp-send-command
1858 126 : v
1859 126 : (if (tramp-get-remote-perl v)
1860 61 : (progn
1861 61 : (tramp-maybe-send-script
1862 61 : v tramp-perl-file-name-all-completions
1863 61 : "tramp_perl_file_name_all_completions")
1864 61 : (format "tramp_perl_file_name_all_completions %s"
1865 61 : (tramp-shell-quote-argument localname)))
1866 :
1867 65 : (format (concat
1868 : "(cd %s 2>&1 && %s -a 2>/dev/null"
1869 : " | while IFS= read f; do"
1870 : " if %s -d \"$f\" 2>/dev/null;"
1871 : " then \\echo \"$f/\"; else \\echo \"$f\"; fi; done"
1872 65 : " && \\echo ok) || \\echo fail")
1873 65 : (tramp-shell-quote-argument localname)
1874 65 : (tramp-get-ls-command v)
1875 126 : (tramp-get-test-command v))))
1876 :
1877 : ;; Now grab the output.
1878 126 : (with-current-buffer (tramp-get-buffer v)
1879 126 : (goto-char (point-max))
1880 :
1881 : ;; Check result code, found in last line of output.
1882 126 : (forward-line -1)
1883 126 : (if (looking-at "^fail$")
1884 0 : (progn
1885 : ;; Grab error message from line before last line
1886 : ;; (it was put there by `cd 2>&1').
1887 0 : (forward-line -1)
1888 0 : (tramp-error
1889 0 : v 'file-error
1890 : "tramp-sh-handle-file-name-all-completions: %s"
1891 0 : (buffer-substring (point) (point-at-eol))))
1892 : ;; For peace of mind, if buffer doesn't end in `fail'
1893 : ;; then it should end in `ok'. If neither are in the
1894 : ;; buffer something went seriously wrong on the remote
1895 : ;; side.
1896 126 : (unless (looking-at "^ok$")
1897 0 : (tramp-error
1898 0 : v 'file-error "\
1899 : tramp-sh-handle-file-name-all-completions: internal error accessing `%s': `%s'"
1900 126 : (tramp-shell-quote-argument localname) (buffer-string))))
1901 :
1902 684 : (while (zerop (forward-line -1))
1903 1116 : (push (buffer-substring (point) (point-at-eol)) result)))
1904 226 : result))))))
1905 :
1906 : ;; cp, mv and ln
1907 :
1908 : (defun tramp-sh-handle-add-name-to-file
1909 : (filename newname &optional ok-if-already-exists)
1910 : "Like `add-name-to-file' for Tramp files."
1911 8 : (unless (tramp-equal-remote filename newname)
1912 2 : (with-parsed-tramp-file-name
1913 2 : (if (tramp-tramp-file-p filename) filename newname) nil
1914 2 : (tramp-error
1915 2 : v 'file-error
1916 : "add-name-to-file: %s"
1917 6 : "only implemented for same method, same user, same host")))
1918 6 : (with-parsed-tramp-file-name filename v1
1919 6 : (with-parsed-tramp-file-name newname v2
1920 6 : (let ((ln (when v1 (tramp-get-remote-ln v1))))
1921 6 : (when (and (numberp ok-if-already-exists)
1922 0 : (file-exists-p newname)
1923 0 : (yes-or-no-p
1924 0 : (format
1925 : "File %s already exists; make it a new name anyway? "
1926 6 : newname)))
1927 6 : (tramp-error v2 'file-already-exists newname))
1928 6 : (when ok-if-already-exists (setq ln (concat ln " -f")))
1929 6 : (tramp-flush-file-property v2 (file-name-directory v2-localname))
1930 6 : (tramp-flush-file-property v2 v2-localname)
1931 6 : (tramp-barf-unless-okay
1932 6 : v1
1933 6 : (format "%s %s %s" ln
1934 6 : (tramp-shell-quote-argument v1-localname)
1935 6 : (tramp-shell-quote-argument v2-localname))
1936 : "error with add-name-to-file, see buffer `%s' for details"
1937 6 : (buffer-name))))))
1938 :
1939 : (defun tramp-sh-handle-copy-file
1940 : (filename newname &optional ok-if-already-exists keep-date
1941 : preserve-uid-gid preserve-extended-attributes)
1942 : "Like `copy-file' for Tramp files."
1943 176 : (setq filename (expand-file-name filename)
1944 176 : newname (expand-file-name newname))
1945 176 : (if (or (tramp-tramp-file-p filename)
1946 176 : (tramp-tramp-file-p newname))
1947 176 : (tramp-do-copy-or-rename-file
1948 176 : 'copy filename newname ok-if-already-exists keep-date
1949 176 : preserve-uid-gid preserve-extended-attributes)
1950 0 : (tramp-run-real-handler
1951 : 'copy-file
1952 0 : (list filename newname ok-if-already-exists keep-date
1953 173 : preserve-uid-gid preserve-extended-attributes))))
1954 :
1955 : (defun tramp-sh-handle-copy-directory
1956 : (dirname newname &optional keep-date parents copy-contents)
1957 : "Like `copy-directory' for Tramp files."
1958 8 : (let ((t1 (tramp-tramp-file-p dirname))
1959 8 : (t2 (tramp-tramp-file-p newname)))
1960 8 : (with-parsed-tramp-file-name (if t1 dirname newname) nil
1961 8 : (if (and (not copy-contents)
1962 4 : (tramp-get-method-parameter v 'tramp-copy-recursive)
1963 : ;; When DIRNAME and NEWNAME are remote, they must have
1964 : ;; the same method.
1965 0 : (or (null t1) (null t2)
1966 0 : (string-equal
1967 0 : (tramp-file-name-method (tramp-dissect-file-name dirname))
1968 0 : (tramp-file-name-method
1969 8 : (tramp-dissect-file-name newname)))))
1970 : ;; scp or rsync DTRT.
1971 0 : (progn
1972 0 : (setq dirname (directory-file-name (expand-file-name dirname))
1973 0 : newname (directory-file-name (expand-file-name newname)))
1974 0 : (if (and (file-directory-p newname)
1975 0 : (not (string-equal (file-name-nondirectory dirname)
1976 0 : (file-name-nondirectory newname))))
1977 0 : (setq newname
1978 0 : (expand-file-name
1979 0 : (file-name-nondirectory dirname) newname)))
1980 0 : (if (not (file-directory-p (file-name-directory newname)))
1981 0 : (make-directory (file-name-directory newname) parents))
1982 0 : (tramp-do-copy-or-rename-file-out-of-band
1983 0 : 'copy dirname newname keep-date))
1984 : ;; We must do it file-wise.
1985 8 : (tramp-run-real-handler
1986 : 'copy-directory
1987 8 : (if copy-contents
1988 4 : (list dirname newname keep-date parents copy-contents)
1989 8 : (list dirname newname keep-date parents))))
1990 :
1991 : ;; When newname did exist, we have wrong cached values.
1992 8 : (when t2
1993 8 : (with-parsed-tramp-file-name newname nil
1994 8 : (tramp-flush-file-property v (file-name-directory localname))
1995 8 : (tramp-flush-file-property v localname))))))
1996 :
1997 : (defun tramp-sh-handle-rename-file
1998 : (filename newname &optional ok-if-already-exists)
1999 : "Like `rename-file' for Tramp files."
2000 : ;; Check if both files are local -- invoke normal rename-file.
2001 : ;; Otherwise, use Tramp from local system.
2002 12 : (setq filename (expand-file-name filename))
2003 12 : (setq newname (expand-file-name newname))
2004 : ;; At least one file a Tramp file?
2005 12 : (if (or (tramp-tramp-file-p filename)
2006 12 : (tramp-tramp-file-p newname))
2007 12 : (tramp-do-copy-or-rename-file
2008 12 : 'rename filename newname ok-if-already-exists
2009 9 : 'keep-time 'preserve-uid-gid)
2010 0 : (tramp-run-real-handler
2011 9 : 'rename-file (list filename newname ok-if-already-exists))))
2012 :
2013 : (defun tramp-do-copy-or-rename-file
2014 : (op filename newname &optional ok-if-already-exists keep-date
2015 : preserve-uid-gid preserve-extended-attributes)
2016 : "Copy or rename a remote file.
2017 : OP must be `copy' or `rename' and indicates the operation to perform.
2018 : FILENAME specifies the file to copy or rename, NEWNAME is the name of
2019 : the new file (for copy) or the new name of the file (for rename).
2020 : OK-IF-ALREADY-EXISTS means don't barf if NEWNAME exists already.
2021 : KEEP-DATE means to make sure that NEWNAME has the same timestamp
2022 : as FILENAME. PRESERVE-UID-GID, when non-nil, instructs to keep
2023 : the uid and gid if both files are on the same host.
2024 : PRESERVE-EXTENDED-ATTRIBUTES activates selinux and acl commands.
2025 :
2026 : This function is invoked by `tramp-sh-handle-copy-file' and
2027 : `tramp-sh-handle-rename-file'. It is an error if OP is neither
2028 : of `copy' and `rename'. FILENAME and NEWNAME must be absolute
2029 : file names."
2030 188 : (unless (memq op '(copy rename))
2031 188 : (error "Unknown operation `%s', must be `copy' or `rename'" op))
2032 188 : (let ((t1 (tramp-tramp-file-p filename))
2033 188 : (t2 (tramp-tramp-file-p newname))
2034 188 : (length (tramp-compat-file-attribute-size
2035 188 : (file-attributes (file-truename filename))))
2036 188 : (attributes (and preserve-extended-attributes
2037 188 : (apply 'file-extended-attributes (list filename)))))
2038 :
2039 188 : (with-parsed-tramp-file-name (if t1 filename newname) nil
2040 188 : (when (and (not ok-if-already-exists) (file-exists-p newname))
2041 182 : (tramp-error v 'file-already-exists newname))
2042 :
2043 182 : (with-tramp-progress-reporter
2044 364 : v 0 (format "%s %s to %s"
2045 364 : (if (eq op 'copy) "Copying" "Renaming")
2046 364 : filename newname)
2047 :
2048 182 : (cond
2049 : ;; Both are Tramp files.
2050 182 : ((and t1 t2)
2051 14 : (with-parsed-tramp-file-name filename v1
2052 14 : (with-parsed-tramp-file-name newname v2
2053 14 : (cond
2054 : ;; Shortcut: if method, host, user are the same for
2055 : ;; both files, we invoke `cp' or `mv' on the remote
2056 : ;; host directly.
2057 14 : ((tramp-equal-remote filename newname)
2058 14 : (tramp-do-copy-or-rename-file-directly
2059 14 : op filename newname
2060 14 : ok-if-already-exists keep-date preserve-uid-gid))
2061 :
2062 : ;; Try out-of-band operation.
2063 0 : ((and
2064 0 : (tramp-method-out-of-band-p v1 length)
2065 0 : (tramp-method-out-of-band-p v2 length))
2066 0 : (tramp-do-copy-or-rename-file-out-of-band
2067 0 : op filename newname keep-date))
2068 :
2069 : ;; No shortcut was possible. So we copy the file
2070 : ;; first. If the operation was `rename', we go back
2071 : ;; and delete the original file (if the copy was
2072 : ;; successful). The approach is simple-minded: we
2073 : ;; create a new buffer, insert the contents of the
2074 : ;; source file into it, then write out the buffer to
2075 : ;; the target file. The advantage is that it doesn't
2076 : ;; matter which file name handlers are used for the
2077 : ;; source and target file.
2078 : (t
2079 0 : (tramp-do-copy-or-rename-file-via-buffer
2080 14 : op filename newname keep-date))))))
2081 :
2082 : ;; One file is a Tramp file, the other one is local.
2083 168 : ((or t1 t2)
2084 168 : (cond
2085 : ;; Fast track on local machine.
2086 168 : ((tramp-local-host-p v)
2087 0 : (tramp-do-copy-or-rename-file-directly
2088 0 : op filename newname
2089 0 : ok-if-already-exists keep-date preserve-uid-gid))
2090 :
2091 : ;; If the Tramp file has an out-of-band method, the
2092 : ;; corresponding copy-program can be invoked.
2093 168 : ((tramp-method-out-of-band-p v length)
2094 0 : (tramp-do-copy-or-rename-file-out-of-band
2095 0 : op filename newname keep-date))
2096 :
2097 : ;; Use the inline method via a Tramp buffer.
2098 168 : (t (tramp-do-copy-or-rename-file-via-buffer
2099 168 : op filename newname keep-date))))
2100 :
2101 : (t
2102 : ;; One of them must be a Tramp file.
2103 182 : (error "Tramp implementation says this cannot happen")))
2104 :
2105 : ;; Handle `preserve-extended-attributes'. We ignore possible
2106 : ;; errors, because ACL strings could be incompatible.
2107 182 : (when attributes
2108 0 : (ignore-errors
2109 182 : (apply 'set-file-extended-attributes (list newname attributes))))
2110 :
2111 : ;; In case of `rename', we must flush the cache of the source file.
2112 182 : (when (and t1 (eq op 'rename))
2113 6 : (with-parsed-tramp-file-name filename v1
2114 6 : (tramp-flush-file-property v1 (file-name-directory v1-localname))
2115 182 : (tramp-flush-file-property v1 v1-localname)))
2116 :
2117 : ;; When newname did exist, we have wrong cached values.
2118 182 : (when t2
2119 96 : (with-parsed-tramp-file-name newname v2
2120 96 : (tramp-flush-file-property v2 (file-name-directory v2-localname))
2121 182 : (tramp-flush-file-property v2 v2-localname)))))))
2122 :
2123 : (defun tramp-do-copy-or-rename-file-via-buffer (op filename newname keep-date)
2124 : "Use an Emacs buffer to copy or rename a file.
2125 : First arg OP is either `copy' or `rename' and indicates the operation.
2126 : FILENAME is the source file, NEWNAME the target file.
2127 : KEEP-DATE is non-nil if NEWNAME should have the same timestamp as FILENAME."
2128 : ;; Check, whether file is too large. Emacs checks in `insert-file-1'
2129 : ;; and `find-file-noselect', but that's not called here.
2130 168 : (abort-if-file-too-large
2131 168 : (tramp-compat-file-attribute-size (file-attributes (file-truename filename)))
2132 168 : (symbol-name op) filename)
2133 : ;; We must disable multibyte, because binary data shall not be
2134 : ;; converted. We don't want the target file to be compressed, so we
2135 : ;; let-bind `jka-compr-inhibit' to t. `epa-file-handler' shall not
2136 : ;; be called either. We remove `tramp-file-name-handler' from
2137 : ;; `inhibit-file-name-handlers'; otherwise the file name handler for
2138 : ;; `insert-file-contents' might be deactivated in some corner cases.
2139 168 : (let ((coding-system-for-read 'binary)
2140 : (coding-system-for-write 'binary)
2141 : (jka-compr-inhibit t)
2142 : (inhibit-file-name-operation 'write-region)
2143 : (inhibit-file-name-handlers
2144 168 : (cons 'epa-file-handler
2145 168 : (remq 'tramp-file-name-handler inhibit-file-name-handlers))))
2146 168 : (with-temp-file newname
2147 168 : (set-buffer-multibyte nil)
2148 168 : (insert-file-contents-literally filename)))
2149 : ;; KEEP-DATE handling.
2150 168 : (when keep-date
2151 6 : (set-file-times
2152 6 : newname
2153 6 : (tramp-compat-file-attribute-modification-time
2154 168 : (file-attributes filename))))
2155 : ;; Set the mode.
2156 168 : (set-file-modes newname (tramp-default-file-modes filename))
2157 : ;; If the operation was `rename', delete the original file.
2158 168 : (unless (eq op 'copy) (delete-file filename)))
2159 :
2160 : (defun tramp-do-copy-or-rename-file-directly
2161 : (op filename newname ok-if-already-exists keep-date preserve-uid-gid)
2162 : "Invokes `cp' or `mv' on the remote system.
2163 : OP must be one of `copy' or `rename', indicating `cp' or `mv',
2164 : respectively. FILENAME specifies the file to copy or rename,
2165 : NEWNAME is the name of the new file (for copy) or the new name of
2166 : the file (for rename). Both files must reside on the same host.
2167 : KEEP-DATE means to make sure that NEWNAME has the same timestamp
2168 : as FILENAME. PRESERVE-UID-GID, when non-nil, instructs to keep
2169 : the uid and gid from FILENAME."
2170 14 : (let ((t1 (tramp-tramp-file-p filename))
2171 14 : (t2 (tramp-tramp-file-p newname))
2172 14 : (file-times (tramp-compat-file-attribute-modification-time
2173 14 : (file-attributes filename)))
2174 14 : (file-modes (tramp-default-file-modes filename)))
2175 14 : (with-parsed-tramp-file-name (if t1 filename newname) nil
2176 14 : (let* ((cmd (cond ((and (eq op 'copy) preserve-uid-gid) "cp -f -p")
2177 14 : ((eq op 'copy) "cp -f")
2178 3 : ((eq op 'rename) "mv -f")
2179 0 : (t (tramp-error
2180 0 : v 'file-error
2181 : "Unknown operation `%s', must be `copy' or `rename'"
2182 14 : op))))
2183 14 : (localname1 (if t1 (file-remote-p filename 'localname) filename))
2184 14 : (localname2 (if t2 (file-remote-p newname 'localname) newname))
2185 14 : (prefix (file-remote-p (if t1 filename newname)))
2186 : cmd-result)
2187 :
2188 14 : (cond
2189 : ;; Both files are on a remote host, with same user.
2190 14 : ((and t1 t2)
2191 14 : (setq cmd-result
2192 14 : (tramp-send-command-and-check
2193 14 : v (format "%s %s %s" cmd
2194 14 : (tramp-shell-quote-argument localname1)
2195 14 : (tramp-shell-quote-argument localname2))))
2196 14 : (with-current-buffer (tramp-get-buffer v)
2197 14 : (goto-char (point-min))
2198 14 : (unless
2199 14 : (or
2200 14 : (and keep-date
2201 : ;; Mask cp -f error.
2202 3 : (re-search-forward
2203 14 : tramp-operation-not-permitted-regexp nil t))
2204 14 : cmd-result)
2205 0 : (tramp-error-with-buffer
2206 0 : nil v 'file-error
2207 : "Copying directly failed, see buffer `%s' for details."
2208 14 : (buffer-name)))))
2209 :
2210 : ;; We are on the local host.
2211 0 : ((or t1 t2)
2212 0 : (cond
2213 : ;; We can do it directly.
2214 0 : ((let (file-name-handler-alist)
2215 0 : (and (file-readable-p localname1)
2216 : ;; No sticky bit when renaming.
2217 0 : (or (eq op 'copy)
2218 0 : (zerop
2219 0 : (logand
2220 0 : (file-modes (file-name-directory localname1))
2221 0 : (string-to-number "1000" 8))))
2222 0 : (file-writable-p (file-name-directory localname2))
2223 0 : (or (file-directory-p localname2)
2224 0 : (file-writable-p localname2))))
2225 0 : (if (eq op 'copy)
2226 0 : (copy-file
2227 0 : localname1 localname2 ok-if-already-exists
2228 0 : keep-date preserve-uid-gid)
2229 0 : (tramp-run-real-handler
2230 0 : 'rename-file (list localname1 localname2 ok-if-already-exists))))
2231 :
2232 : ;; We can do it directly with `tramp-send-command'
2233 0 : ((and (file-readable-p (concat prefix localname1))
2234 0 : (file-writable-p
2235 0 : (file-name-directory (concat prefix localname2)))
2236 0 : (or (file-directory-p (concat prefix localname2))
2237 0 : (file-writable-p (concat prefix localname2))))
2238 0 : (tramp-do-copy-or-rename-file-directly
2239 0 : op (concat prefix localname1) (concat prefix localname2)
2240 0 : ok-if-already-exists keep-date t)
2241 : ;; We must change the ownership to the local user.
2242 0 : (tramp-set-file-uid-gid
2243 0 : (concat prefix localname2)
2244 0 : (tramp-get-local-uid 'integer)
2245 0 : (tramp-get-local-gid 'integer)))
2246 :
2247 : ;; We need a temporary file in between.
2248 : (t
2249 : ;; Create the temporary file.
2250 0 : (let ((tmpfile (tramp-compat-make-temp-file localname1)))
2251 0 : (unwind-protect
2252 0 : (progn
2253 0 : (cond
2254 0 : (t1
2255 0 : (tramp-barf-unless-okay
2256 0 : v (format
2257 0 : "%s %s %s" cmd
2258 0 : (tramp-shell-quote-argument localname1)
2259 0 : (tramp-shell-quote-argument tmpfile))
2260 : "Copying directly failed, see buffer `%s' for details."
2261 0 : (tramp-get-buffer v))
2262 : ;; We must change the ownership as remote user.
2263 : ;; Since this does not work reliable, we also
2264 : ;; give read permissions.
2265 0 : (set-file-modes
2266 0 : (concat prefix tmpfile) (string-to-number "0777" 8))
2267 0 : (tramp-set-file-uid-gid
2268 0 : (concat prefix tmpfile)
2269 0 : (tramp-get-local-uid 'integer)
2270 0 : (tramp-get-local-gid 'integer)))
2271 0 : (t2
2272 0 : (if (eq op 'copy)
2273 0 : (copy-file
2274 0 : localname1 tmpfile t keep-date preserve-uid-gid)
2275 0 : (tramp-run-real-handler
2276 0 : 'rename-file (list localname1 tmpfile t)))
2277 : ;; We must change the ownership as local user.
2278 : ;; Since this does not work reliable, we also
2279 : ;; give read permissions.
2280 0 : (set-file-modes tmpfile (string-to-number "0777" 8))
2281 0 : (tramp-set-file-uid-gid
2282 0 : tmpfile
2283 0 : (tramp-get-remote-uid v 'integer)
2284 0 : (tramp-get-remote-gid v 'integer))))
2285 :
2286 : ;; Move the temporary file to its destination.
2287 0 : (cond
2288 0 : (t2
2289 0 : (tramp-barf-unless-okay
2290 0 : v (format
2291 : "cp -f -p %s %s"
2292 0 : (tramp-shell-quote-argument tmpfile)
2293 0 : (tramp-shell-quote-argument localname2))
2294 : "Copying directly failed, see buffer `%s' for details."
2295 0 : (tramp-get-buffer v)))
2296 0 : (t1
2297 0 : (tramp-run-real-handler
2298 : 'rename-file
2299 0 : (list tmpfile localname2 ok-if-already-exists)))))
2300 :
2301 : ;; Save exit.
2302 14 : (ignore-errors (delete-file tmpfile)))))))))
2303 :
2304 : ;; Set the time and mode. Mask possible errors.
2305 14 : (ignore-errors
2306 14 : (when keep-date
2307 3 : (set-file-times newname file-times)
2308 14 : (set-file-modes newname file-modes))))))
2309 :
2310 : (defun tramp-do-copy-or-rename-file-out-of-band (op filename newname keep-date)
2311 : "Invoke `scp' program to copy.
2312 : The method used must be an out-of-band method."
2313 0 : (let* ((t1 (tramp-tramp-file-p filename))
2314 0 : (t2 (tramp-tramp-file-p newname))
2315 0 : (orig-vec (tramp-dissect-file-name (if t1 filename newname)))
2316 : copy-program copy-args copy-env copy-keep-date listener spec
2317 : options source target remote-copy-program remote-copy-args)
2318 :
2319 0 : (with-parsed-tramp-file-name (if t1 filename newname) nil
2320 0 : (if (and t1 t2)
2321 :
2322 : ;; Both are Tramp files. We shall optimize it when the
2323 : ;; methods for FILENAME and NEWNAME are the same.
2324 0 : (let* ((dir-flag (file-directory-p filename))
2325 0 : (tmpfile (tramp-compat-make-temp-file localname dir-flag)))
2326 0 : (if dir-flag
2327 0 : (setq tmpfile
2328 0 : (expand-file-name
2329 0 : (file-name-nondirectory newname) tmpfile)))
2330 0 : (unwind-protect
2331 0 : (progn
2332 0 : (tramp-do-copy-or-rename-file-out-of-band
2333 0 : op filename tmpfile keep-date)
2334 0 : (tramp-do-copy-or-rename-file-out-of-band
2335 0 : 'rename tmpfile newname keep-date))
2336 : ;; Save exit.
2337 0 : (ignore-errors
2338 0 : (if dir-flag
2339 0 : (delete-directory
2340 0 : (expand-file-name ".." tmpfile) 'recursive)
2341 0 : (delete-file tmpfile)))))
2342 :
2343 : ;; Set variables for computing the prompt for reading password.
2344 0 : (setq tramp-current-method (tramp-file-name-method v)
2345 0 : tramp-current-user (or (tramp-file-name-user v)
2346 0 : (tramp-get-connection-property
2347 0 : v "login-as" nil))
2348 0 : tramp-current-domain (tramp-file-name-domain v)
2349 0 : tramp-current-host (tramp-file-name-host v)
2350 0 : tramp-current-port (tramp-file-name-port v))
2351 :
2352 : ;; Check which ones of source and target are Tramp files.
2353 0 : (setq source (funcall
2354 0 : (if (and (file-directory-p filename)
2355 0 : (not (file-exists-p newname)))
2356 : 'file-name-as-directory
2357 0 : 'identity)
2358 0 : (if t1
2359 0 : (tramp-make-copy-program-file-name v)
2360 0 : (tramp-unquote-shell-quote-argument filename)))
2361 0 : target (if t2
2362 0 : (tramp-make-copy-program-file-name v)
2363 0 : (tramp-unquote-shell-quote-argument newname)))
2364 :
2365 : ;; Check for user. There might be an interactive setting.
2366 0 : (setq user (or (tramp-file-name-user v)
2367 0 : (tramp-get-connection-property v "login-as" nil)))
2368 :
2369 : ;; Check for listener port.
2370 0 : (when (tramp-get-method-parameter v 'tramp-remote-copy-args)
2371 0 : (setq listener (number-to-string (+ 50000 (random 10000))))
2372 0 : (while
2373 0 : (zerop (tramp-call-process v "nc" nil nil nil "-z" host listener))
2374 0 : (setq listener (number-to-string (+ 50000 (random 10000))))))
2375 :
2376 : ;; Compose copy command.
2377 0 : (setq host (or host "")
2378 0 : user (or user "")
2379 0 : port (or port "")
2380 0 : spec (format-spec-make
2381 0 : ?t (tramp-get-connection-property
2382 0 : (tramp-get-connection-process v) "temp-file" ""))
2383 0 : options (format-spec (tramp-ssh-controlmaster-options v) spec)
2384 0 : spec (format-spec-make
2385 0 : ?h host ?u user ?p port ?r listener ?c options
2386 0 : ?k (if keep-date " " ""))
2387 0 : copy-program (tramp-get-method-parameter v 'tramp-copy-program)
2388 0 : copy-keep-date (tramp-get-method-parameter
2389 0 : v 'tramp-copy-keep-date)
2390 :
2391 : copy-args
2392 0 : (delete
2393 : ;; " " has either been a replacement of "%k" (when
2394 : ;; keep-date argument is non-nil), or a replacement
2395 : ;; for the whole keep-date sublist.
2396 : " "
2397 0 : (dolist
2398 0 : (x (tramp-get-method-parameter v 'tramp-copy-args) copy-args)
2399 0 : (setq copy-args
2400 0 : (append
2401 0 : copy-args
2402 0 : (let ((y (mapcar (lambda (z) (format-spec z spec)) x)))
2403 0 : (if (member "" y) '(" ") y))))))
2404 :
2405 : copy-env
2406 0 : (delq
2407 : nil
2408 0 : (mapcar
2409 : (lambda (x)
2410 0 : (setq x (mapcar (lambda (y) (format-spec y spec)) x))
2411 0 : (unless (member "" x) (mapconcat 'identity x " ")))
2412 0 : (tramp-get-method-parameter v 'tramp-copy-env)))
2413 :
2414 : remote-copy-program
2415 0 : (tramp-get-method-parameter v 'tramp-remote-copy-program))
2416 :
2417 0 : (dolist (x (tramp-get-method-parameter v 'tramp-remote-copy-args))
2418 0 : (setq remote-copy-args
2419 0 : (append
2420 0 : remote-copy-args
2421 0 : (let ((y (mapcar (lambda (z) (format-spec z spec)) x)))
2422 0 : (if (member "" y) '(" ") y)))))
2423 :
2424 : ;; Check for local copy program.
2425 0 : (unless (executable-find copy-program)
2426 0 : (tramp-error
2427 0 : v 'file-error "Cannot find local copy program: %s" copy-program))
2428 :
2429 : ;; Install listener on the remote side. The prompt must be
2430 : ;; consumed later on, when the process does not listen anymore.
2431 0 : (when remote-copy-program
2432 0 : (unless (with-tramp-connection-property
2433 0 : v (concat "remote-copy-program-" remote-copy-program)
2434 0 : (tramp-find-executable
2435 0 : v remote-copy-program (tramp-get-remote-path v)))
2436 0 : (tramp-error
2437 0 : v 'file-error
2438 0 : "Cannot find remote listener: %s" remote-copy-program))
2439 0 : (setq remote-copy-program
2440 0 : (mapconcat
2441 : 'identity
2442 0 : (append
2443 0 : (list remote-copy-program) remote-copy-args
2444 0 : (list (if t1 (concat "<" source) (concat ">" target)) "&"))
2445 0 : " "))
2446 0 : (tramp-send-command v remote-copy-program)
2447 0 : (with-timeout
2448 0 : (60 (tramp-error
2449 0 : v 'file-error
2450 : "Listener process not running on remote host: `%s'"
2451 0 : remote-copy-program))
2452 0 : (tramp-send-command v (format "netstat -l | grep -q :%s" listener))
2453 0 : (while (not (tramp-send-command-and-check v nil))
2454 0 : (tramp-send-command
2455 0 : v (format "netstat -l | grep -q :%s" listener)))))
2456 :
2457 0 : (with-temp-buffer
2458 0 : (unwind-protect
2459 : ;; The default directory must be remote.
2460 0 : (let ((default-directory
2461 0 : (file-name-directory (if t1 filename newname)))
2462 0 : (process-environment (copy-sequence process-environment)))
2463 : ;; Set the transfer process properties.
2464 0 : (tramp-set-connection-property
2465 0 : v "process-name" (buffer-name (current-buffer)))
2466 0 : (tramp-set-connection-property
2467 0 : v "process-buffer" (current-buffer))
2468 0 : (while copy-env
2469 0 : (tramp-message
2470 0 : orig-vec 6 "%s=\"%s\"" (car copy-env) (cadr copy-env))
2471 0 : (setenv (pop copy-env) (pop copy-env)))
2472 0 : (setq
2473 : copy-args
2474 0 : (append
2475 0 : copy-args
2476 0 : (if remote-copy-program
2477 0 : (list (if t1 (concat ">" target) (concat "<" source)))
2478 0 : (list source target))))
2479 :
2480 : ;; Use an asynchronous process. By this, password can
2481 : ;; be handled. We don't set a timeout, because the
2482 : ;; copying of large files can last longer than 60 secs.
2483 0 : (let* ((command
2484 0 : (mapconcat
2485 0 : 'identity (append (list copy-program) copy-args)
2486 0 : " "))
2487 0 : (p (let ((default-directory
2488 0 : (tramp-compat-temporary-file-directory)))
2489 0 : (start-process-shell-command
2490 0 : (tramp-get-connection-name v)
2491 0 : (tramp-get-connection-buffer v)
2492 0 : command))))
2493 0 : (tramp-message orig-vec 6 "%s" command)
2494 0 : (tramp-set-connection-property p "vector" orig-vec)
2495 0 : (process-put p 'adjust-window-size-function 'ignore)
2496 0 : (set-process-query-on-exit-flag p nil)
2497 :
2498 : ;; We must adapt `tramp-local-end-of-line' for
2499 : ;; sending the password.
2500 0 : (let ((tramp-local-end-of-line tramp-rsh-end-of-line))
2501 0 : (tramp-process-actions
2502 0 : p v nil tramp-actions-copy-out-of-band))))
2503 :
2504 : ;; Reset the transfer process properties.
2505 0 : (tramp-set-connection-property v "process-name" nil)
2506 0 : (tramp-set-connection-property v "process-buffer" nil)
2507 : ;; Clear the remote prompt.
2508 0 : (when (and remote-copy-program
2509 0 : (not (tramp-send-command-and-check v nil)))
2510 : ;; Houston, we have a problem! Likely, the listener is
2511 : ;; still running, so let's clear everything (but the
2512 : ;; cached password).
2513 0 : (tramp-cleanup-connection v 'keep-debug 'keep-password))))
2514 :
2515 : ;; Handle KEEP-DATE argument.
2516 0 : (when (and keep-date (not copy-keep-date))
2517 0 : (set-file-times
2518 0 : newname
2519 0 : (tramp-compat-file-attribute-modification-time
2520 0 : (file-attributes filename))))
2521 :
2522 : ;; Set the mode.
2523 0 : (unless (and keep-date copy-keep-date)
2524 0 : (ignore-errors
2525 0 : (set-file-modes newname (tramp-default-file-modes filename)))))
2526 :
2527 : ;; If the operation was `rename', delete the original file.
2528 0 : (unless (eq op 'copy)
2529 0 : (if (file-regular-p filename)
2530 0 : (delete-file filename)
2531 0 : (delete-directory filename 'recursive))))))
2532 :
2533 : (defun tramp-sh-handle-make-directory (dir &optional parents)
2534 : "Like `make-directory' for Tramp files."
2535 132 : (setq dir (expand-file-name dir))
2536 132 : (with-parsed-tramp-file-name dir nil
2537 132 : (tramp-flush-directory-property v (file-name-directory localname))
2538 132 : (save-excursion
2539 132 : (tramp-barf-unless-okay
2540 132 : v (format "%s %s"
2541 132 : (if parents "mkdir -p" "mkdir")
2542 132 : (tramp-shell-quote-argument localname))
2543 132 : "Couldn't make directory %s" dir))))
2544 :
2545 : (defun tramp-sh-handle-delete-directory (directory &optional recursive trash)
2546 : "Like `delete-directory' for Tramp files."
2547 124 : (setq directory (expand-file-name directory))
2548 124 : (with-parsed-tramp-file-name directory nil
2549 124 : (tramp-flush-file-property v (file-name-directory localname))
2550 124 : (tramp-flush-directory-property v localname)
2551 124 : (tramp-barf-unless-okay
2552 124 : v (format "cd / && %s %s"
2553 124 : (or (and trash (tramp-get-remote-trash v))
2554 124 : (if recursive "rm -rf" "rmdir"))
2555 124 : (tramp-shell-quote-argument localname))
2556 124 : "Couldn't delete %s" directory)))
2557 :
2558 : (defun tramp-sh-handle-delete-file (filename &optional trash)
2559 : "Like `delete-file' for Tramp files."
2560 378 : (setq filename (expand-file-name filename))
2561 378 : (with-parsed-tramp-file-name filename nil
2562 378 : (tramp-flush-file-property v (file-name-directory localname))
2563 378 : (tramp-flush-file-property v localname)
2564 378 : (tramp-barf-unless-okay
2565 378 : v (format "%s %s"
2566 378 : (or (and trash (tramp-get-remote-trash v)) "rm -f")
2567 378 : (tramp-shell-quote-argument localname))
2568 378 : "Couldn't delete %s" filename)))
2569 :
2570 : ;; Dired.
2571 :
2572 : (defun tramp-sh-handle-dired-compress-file (file)
2573 : "Like `dired-compress-file' for Tramp files."
2574 : ;; Code stolen mainly from dired-aux.el.
2575 0 : (with-parsed-tramp-file-name file nil
2576 0 : (tramp-flush-file-property v localname)
2577 0 : (save-excursion
2578 0 : (let ((suffixes dired-compress-file-suffixes)
2579 : suffix)
2580 : ;; See if any suffix rule matches this file name.
2581 0 : (while suffixes
2582 0 : (let (case-fold-search)
2583 0 : (if (string-match (car (car suffixes)) localname)
2584 0 : (setq suffix (car suffixes) suffixes nil))
2585 0 : (setq suffixes (cdr suffixes))))
2586 :
2587 0 : (cond ((file-symlink-p file)
2588 : nil)
2589 0 : ((and suffix (nth 2 suffix))
2590 : ;; We found an uncompression rule.
2591 0 : (with-tramp-progress-reporter
2592 0 : v 0 (format "Uncompressing %s" file)
2593 0 : (when (tramp-send-command-and-check
2594 0 : v (concat (nth 2 suffix) " "
2595 0 : (tramp-shell-quote-argument localname)))
2596 0 : (dired-remove-file file)
2597 0 : (string-match (car suffix) file)
2598 0 : (concat (substring file 0 (match-beginning 0))))))
2599 : (t
2600 : ;; We don't recognize the file as compressed, so compress it.
2601 : ;; Try gzip.
2602 0 : (with-tramp-progress-reporter v 0 (format "Compressing %s" file)
2603 0 : (when (tramp-send-command-and-check
2604 0 : v (concat "gzip -f "
2605 0 : (tramp-shell-quote-argument localname)))
2606 0 : (dired-remove-file file)
2607 0 : (cond ((file-exists-p (concat file ".gz"))
2608 0 : (concat file ".gz"))
2609 0 : ((file-exists-p (concat file ".z"))
2610 0 : (concat file ".z"))
2611 0 : (t nil))))))))))
2612 :
2613 : (defun tramp-sh-handle-insert-directory
2614 : (filename switches &optional wildcard full-directory-p)
2615 : "Like `insert-directory' for Tramp files."
2616 10 : (setq filename (expand-file-name filename))
2617 10 : (unless switches (setq switches ""))
2618 10 : (with-parsed-tramp-file-name filename nil
2619 10 : (if (and (featurep 'ls-lisp)
2620 10 : (not (symbol-value 'ls-lisp-use-insert-directory-program)))
2621 0 : (tramp-handle-insert-directory
2622 0 : filename switches wildcard full-directory-p)
2623 10 : (when (stringp switches)
2624 10 : (setq switches (split-string switches)))
2625 10 : (when (tramp-get-ls-command-with-quoting-style v)
2626 10 : (setq switches (append switches '("--quoting-style=literal"))))
2627 10 : (when (and (member "--dired" switches)
2628 10 : (not (tramp-get-ls-command-with-dired v)))
2629 10 : (setq switches (delete "--dired" switches)))
2630 10 : (when wildcard
2631 2 : (setq wildcard (tramp-run-real-handler
2632 2 : 'file-name-nondirectory (list localname)))
2633 2 : (setq localname (tramp-run-real-handler
2634 10 : 'file-name-directory (list localname))))
2635 10 : (unless (or full-directory-p (member "-d" switches))
2636 10 : (setq switches (append switches '("-d"))))
2637 10 : (setq switches (mapconcat 'tramp-shell-quote-argument switches " "))
2638 10 : (when wildcard
2639 10 : (setq switches (concat switches " " wildcard)))
2640 10 : (tramp-message
2641 10 : v 4 "Inserting directory `ls %s %s', wildcard %s, fulldir %s"
2642 10 : switches filename (if wildcard "yes" "no")
2643 10 : (if full-directory-p "yes" "no"))
2644 : ;; If `full-directory-p', we just say `ls -l FILENAME'.
2645 : ;; Else we chdir to the parent directory, then say `ls -ld BASENAME'.
2646 10 : (if full-directory-p
2647 2 : (tramp-send-command
2648 2 : v
2649 2 : (format "%s %s %s 2>/dev/null"
2650 2 : (tramp-get-ls-command v)
2651 2 : switches
2652 2 : (if wildcard
2653 0 : localname
2654 2 : (tramp-shell-quote-argument (concat localname ".")))))
2655 8 : (tramp-barf-unless-okay
2656 8 : v
2657 8 : (format "cd %s" (tramp-shell-quote-argument
2658 8 : (tramp-run-real-handler
2659 8 : 'file-name-directory (list localname))))
2660 : "Couldn't `cd %s'"
2661 8 : (tramp-shell-quote-argument
2662 8 : (tramp-run-real-handler 'file-name-directory (list localname))))
2663 8 : (tramp-send-command
2664 8 : v
2665 8 : (format "%s %s %s 2>/dev/null"
2666 8 : (tramp-get-ls-command v)
2667 8 : switches
2668 8 : (if (or wildcard
2669 6 : (zerop (length
2670 6 : (tramp-run-real-handler
2671 8 : 'file-name-nondirectory (list localname)))))
2672 : ""
2673 4 : (tramp-shell-quote-argument
2674 4 : (tramp-run-real-handler
2675 10 : 'file-name-nondirectory (list localname)))))))
2676 :
2677 10 : (save-restriction
2678 10 : (let ((beg (point)))
2679 10 : (narrow-to-region (point) (point))
2680 : ;; We cannot use `insert-buffer-substring' because the Tramp
2681 : ;; buffer changes its contents before insertion due to calling
2682 : ;; `expand-file-name' and alike.
2683 10 : (insert
2684 10 : (with-current-buffer (tramp-get-buffer v)
2685 10 : (buffer-string)))
2686 :
2687 : ;; Check for "--dired" output.
2688 10 : (forward-line -2)
2689 10 : (when (looking-at "//SUBDIRED//")
2690 10 : (forward-line -1))
2691 10 : (when (looking-at "//DIRED//\\s-+")
2692 2 : (let ((databeg (match-end 0))
2693 2 : (end (point-at-eol)))
2694 : ;; Now read the numeric positions of file names.
2695 2 : (goto-char databeg)
2696 16 : (while (< (point) end)
2697 14 : (let ((start (+ beg (read (current-buffer))))
2698 14 : (end (+ beg (read (current-buffer)))))
2699 14 : (if (memq (char-after end) '(?\n ?\ ))
2700 : ;; End is followed by \n or by " -> ".
2701 14 : (put-text-property start end 'dired-filename t))))))
2702 : ;; Remove trailing lines.
2703 10 : (goto-char (point-at-bol))
2704 14 : (while (looking-at "//")
2705 4 : (forward-line 1)
2706 10 : (delete-region (match-beginning 0) (point)))
2707 :
2708 : ;; Some busyboxes are reluctant to discard colors.
2709 10 : (unless
2710 10 : (string-match "color" (tramp-get-connection-property v "ls" ""))
2711 0 : (goto-char beg)
2712 0 : (while
2713 0 : (re-search-forward tramp-display-escape-sequence-regexp nil t)
2714 10 : (replace-match "")))
2715 :
2716 : ;; Decode the output, it could be multibyte.
2717 10 : (decode-coding-region
2718 10 : beg (point-max)
2719 10 : (or file-name-coding-system default-file-name-coding-system))
2720 :
2721 : ;; The inserted file could be from somewhere else.
2722 10 : (when (and (not wildcard) (not full-directory-p))
2723 6 : (goto-char (point-max))
2724 6 : (when (file-symlink-p filename)
2725 6 : (goto-char (search-backward "->" beg 'noerror)))
2726 6 : (search-backward
2727 6 : (if (zerop (length (file-name-nondirectory filename)))
2728 : "."
2729 6 : (file-name-nondirectory filename))
2730 6 : beg 'noerror)
2731 10 : (replace-match (file-relative-name filename) t))
2732 :
2733 10 : (goto-char (point-max)))))))
2734 :
2735 : ;; Canonicalization of file names.
2736 :
2737 : (defun tramp-sh-handle-expand-file-name (name &optional dir)
2738 : "Like `expand-file-name' for Tramp files.
2739 : If the localname part of the given file name starts with \"/../\" then
2740 : the result will be a local, non-Tramp, file name."
2741 : ;; If DIR is not given, use `default-directory' or "/".
2742 24378 : (setq dir (or dir default-directory "/"))
2743 : ;; Unless NAME is absolute, concat DIR and NAME.
2744 24378 : (unless (file-name-absolute-p name)
2745 24378 : (setq name (concat (file-name-as-directory dir) name)))
2746 : ;; If connection is not established yet, run the real handler.
2747 24378 : (if (not (tramp-connectable-p name))
2748 240 : (tramp-run-real-handler 'expand-file-name (list name nil))
2749 : ;; Dissect NAME.
2750 24138 : (with-parsed-tramp-file-name name nil
2751 24138 : (unless (tramp-run-real-handler 'file-name-absolute-p (list localname))
2752 24138 : (setq localname (concat "~/" localname)))
2753 : ;; Tilde expansion if necessary. This needs a shell which
2754 : ;; groks tilde expansion! The function `tramp-find-shell' is
2755 : ;; supposed to find such a shell on the remote host. Please
2756 : ;; tell me about it when this doesn't work on your system.
2757 24138 : (when (string-match "\\`\\(~[^/]*\\)\\(.*\\)\\'" localname)
2758 1 : (let ((uname (match-string 1 localname))
2759 1 : (fname (match-string 2 localname)))
2760 : ;; We cannot simply apply "~/", because under sudo "~/" is
2761 : ;; expanded to the local user home directory but to the
2762 : ;; root home directory. On the other hand, using always
2763 : ;; the default user name for tilde expansion is not
2764 : ;; appropriate either, because ssh and companions might
2765 : ;; use a user name from the config file.
2766 1 : (when (and (string-equal uname "~")
2767 1 : (string-match "\\`su\\(do\\)?\\'" method))
2768 1 : (setq uname (concat uname user)))
2769 1 : (setq uname
2770 2 : (with-tramp-connection-property v uname
2771 1 : (tramp-send-command
2772 1 : v (format "cd %s && pwd" (tramp-shell-quote-argument uname)))
2773 1 : (with-current-buffer (tramp-get-buffer v)
2774 1 : (goto-char (point-min))
2775 1 : (buffer-substring (point) (point-at-eol)))))
2776 24138 : (setq localname (concat uname fname))))
2777 : ;; There might be a double slash, for example when "~/"
2778 : ;; expands to "/". Remove this.
2779 24138 : (while (string-match "//" localname)
2780 24138 : (setq localname (replace-match "/" t t localname)))
2781 : ;; No tilde characters in file name, do normal
2782 : ;; `expand-file-name' (this does "/./" and "/../").
2783 : ;; `default-directory' is bound, because on Windows there would
2784 : ;; be problems with UNC shares or Cygwin mounts.
2785 24138 : (let ((default-directory (tramp-compat-temporary-file-directory)))
2786 24138 : (tramp-make-tramp-file-name
2787 24138 : method user domain host port
2788 24138 : (tramp-drop-volume-letter
2789 24138 : (tramp-run-real-handler
2790 24138 : 'expand-file-name (list localname)))
2791 24378 : hop)))))
2792 :
2793 : ;;; Remote commands:
2794 :
2795 : (defun tramp-process-sentinel (proc event)
2796 : "Flush file caches."
2797 51 : (unless (process-live-p proc)
2798 51 : (let ((vec (tramp-get-connection-property proc "vector" nil)))
2799 51 : (when vec
2800 0 : (tramp-message vec 5 "Sentinel called: `%S' `%s'" proc event)
2801 0 : (tramp-flush-connection-property proc)
2802 51 : (tramp-flush-directory-property vec "")))))
2803 :
2804 : ;; We use BUFFER also as connection buffer during setup. Because of
2805 : ;; this, its original contents must be saved, and restored once
2806 : ;; connection has been setup.
2807 : (defun tramp-sh-handle-start-file-process (name buffer program &rest args)
2808 : "Like `start-file-process' for Tramp files."
2809 27 : (with-parsed-tramp-file-name (expand-file-name default-directory) nil
2810 27 : (let* ((buffer
2811 27 : (if buffer
2812 27 : (get-buffer-create buffer)
2813 : ;; BUFFER can be nil. We use a temporary buffer.
2814 27 : (generate-new-buffer tramp-temp-buffer-name)))
2815 : ;; When PROGRAM matches "*sh", and the first arg is "-c",
2816 : ;; it might be that the arguments exceed the command line
2817 : ;; length. Therefore, we modify the command.
2818 27 : (heredoc (and (stringp program)
2819 27 : (string-match "sh$" program)
2820 21 : (string-equal "-c" (car args))
2821 27 : (= (length args) 2)))
2822 : ;; When PROGRAM is nil, we just provide a tty.
2823 27 : (args (if (not heredoc) args
2824 20 : (let ((i 250))
2825 20 : (while (and (< i (length (cadr args)))
2826 20 : (string-match " " (cadr args) i))
2827 0 : (setcdr
2828 0 : args
2829 0 : (list (replace-match " \\\\\n" nil nil (cadr args))))
2830 20 : (setq i (+ i 250))))
2831 27 : (cdr args)))
2832 : ;; Use a human-friendly prompt, for example for `shell'.
2833 : ;; We discard hops, if existing, that's why we cannot use
2834 : ;; `file-remote-p'.
2835 27 : (prompt (format "PS1=%s %s"
2836 27 : (tramp-make-tramp-file-name
2837 27 : (tramp-file-name-method v)
2838 27 : (tramp-file-name-user v)
2839 27 : (tramp-file-name-domain v)
2840 27 : (tramp-file-name-host v)
2841 27 : (tramp-file-name-port v)
2842 27 : (tramp-file-name-localname v))
2843 27 : tramp-initial-end-of-output))
2844 : ;; We use as environment the difference to toplevel
2845 : ;; `process-environment'.
2846 : env uenv
2847 27 : (env (dolist (elt (cons prompt process-environment) env)
2848 2871 : (or (member elt (default-toplevel-value 'process-environment))
2849 36 : (if (string-match "=" elt)
2850 34 : (setq env (append env `(,elt)))
2851 2 : (if (tramp-get-env-with-u-option v)
2852 2 : (setq env (append `("-u" ,elt) env))
2853 2871 : (setq uenv (cons elt uenv)))))))
2854 : (command
2855 27 : (when (stringp program)
2856 27 : (format "cd %s && %s exec %s env %s %s"
2857 27 : (tramp-shell-quote-argument localname)
2858 27 : (if uenv
2859 0 : (format
2860 : "unset %s &&"
2861 0 : (mapconcat 'tramp-shell-quote-argument uenv " "))
2862 27 : "")
2863 27 : (if heredoc (format "<<'%s'" tramp-end-of-heredoc) "")
2864 27 : (mapconcat 'tramp-shell-quote-argument env " ")
2865 27 : (if heredoc
2866 20 : (format "%s\n(\n%s\n) </dev/tty\n%s"
2867 20 : program (car args) tramp-end-of-heredoc)
2868 7 : (mapconcat 'tramp-shell-quote-argument
2869 27 : (cons program args) " ")))))
2870 : (tramp-process-connection-type
2871 27 : (or (null program) tramp-process-connection-type))
2872 27 : (bmp (and (buffer-live-p buffer) (buffer-modified-p buffer)))
2873 27 : (name1 name)
2874 : (i 0)
2875 : ;; We do not want to raise an error when
2876 : ;; `start-file-process' has been started several times in
2877 : ;; `eshell' and friends.
2878 : (tramp-current-connection nil))
2879 :
2880 27 : (while (get-process name1)
2881 : ;; NAME must be unique as process name.
2882 0 : (setq i (1+ i)
2883 27 : name1 (format "%s<%d>" name i)))
2884 27 : (setq name name1)
2885 : ;; Set the new process properties.
2886 27 : (tramp-set-connection-property v "process-name" name)
2887 27 : (tramp-set-connection-property v "process-buffer" buffer)
2888 :
2889 27 : (with-current-buffer (tramp-get-connection-buffer v)
2890 27 : (unwind-protect
2891 : ;; We catch this event. Otherwise, `start-process' could
2892 : ;; be called on the local host.
2893 27 : (save-excursion
2894 27 : (save-restriction
2895 : ;; Activate narrowing in order to save BUFFER
2896 : ;; contents. Clear also the modification time;
2897 : ;; otherwise we might be interrupted by
2898 : ;; `verify-visited-file-modtime'.
2899 27 : (let ((buffer-undo-list t)
2900 : (buffer-read-only nil)
2901 27 : (mark (point-max)))
2902 27 : (clear-visited-file-modtime)
2903 27 : (narrow-to-region (point-max) (point-max))
2904 : ;; We call `tramp-maybe-open-connection', in order
2905 : ;; to cleanup the prompt afterwards.
2906 27 : (catch 'suppress
2907 27 : (tramp-maybe-open-connection v)
2908 27 : (widen)
2909 27 : (delete-region mark (point))
2910 27 : (narrow-to-region (point-max) (point-max))
2911 : ;; Now do it.
2912 27 : (if command
2913 : ;; Send the command.
2914 27 : (tramp-send-command v command nil t) ; nooutput
2915 : ;; Check, whether a pty is associated.
2916 0 : (unless (process-get
2917 0 : (tramp-get-connection-process v) 'remote-tty)
2918 0 : (tramp-error
2919 0 : v 'file-error
2920 27 : "pty association is not supported for `%s'" name))))
2921 27 : (let ((p (tramp-get-connection-process v)))
2922 : ;; Set query flag and process marker for this
2923 : ;; process. We ignore errors, because the process
2924 : ;; could have finished already.
2925 27 : (ignore-errors
2926 27 : (set-process-query-on-exit-flag p t)
2927 27 : (set-marker (process-mark p) (point)))
2928 : ;; Return process.
2929 27 : p))))
2930 :
2931 : ;; Save exit.
2932 27 : (if (string-match tramp-temp-buffer-name (buffer-name))
2933 0 : (ignore-errors
2934 0 : (set-process-buffer (tramp-get-connection-process v) nil)
2935 0 : (kill-buffer (current-buffer)))
2936 27 : (set-buffer-modified-p bmp))
2937 27 : (tramp-set-connection-property v "process-name" nil)
2938 27 : (tramp-set-connection-property v "process-buffer" nil))))))
2939 :
2940 : (defun tramp-sh-handle-process-file
2941 : (program &optional infile destination display &rest args)
2942 : "Like `process-file' for Tramp files."
2943 : ;; The implementation is not complete yet.
2944 102 : (when (and (numberp destination) (zerop destination))
2945 102 : (error "Implementation does not handle immediate return"))
2946 :
2947 102 : (with-parsed-tramp-file-name default-directory nil
2948 102 : (let (command env uenv input tmpinput stderr tmpstderr outbuf ret)
2949 : ;; Compute command.
2950 102 : (setq command (mapconcat 'tramp-shell-quote-argument
2951 102 : (cons program args) " "))
2952 : ;; We use as environment the difference to toplevel `process-environment'.
2953 102 : (dolist (elt process-environment)
2954 10867 : (or (member elt (default-toplevel-value 'process-environment))
2955 157 : (if (string-match "=" elt)
2956 79 : (setq env (append env `(,elt)))
2957 78 : (if (tramp-get-env-with-u-option v)
2958 78 : (setq env (append `("-u" ,elt) env))
2959 10867 : (setq uenv (cons elt uenv))))))
2960 102 : (when env
2961 81 : (setq command
2962 81 : (format
2963 : "env %s %s"
2964 102 : (mapconcat 'tramp-shell-quote-argument env " ") command)))
2965 102 : (when uenv
2966 0 : (setq command
2967 0 : (format
2968 : "unset %s && %s"
2969 102 : (mapconcat 'tramp-shell-quote-argument uenv " ") command)))
2970 : ;; Determine input.
2971 102 : (if (null infile)
2972 102 : (setq input "/dev/null")
2973 0 : (setq infile (expand-file-name infile))
2974 0 : (if (tramp-equal-remote default-directory infile)
2975 : ;; INFILE is on the same remote host.
2976 0 : (setq input (with-parsed-tramp-file-name infile nil localname))
2977 : ;; INFILE must be copied to remote host.
2978 0 : (setq input (tramp-make-tramp-temp-file v)
2979 : tmpinput
2980 0 : (tramp-make-tramp-file-name method user domain host port input))
2981 102 : (copy-file infile tmpinput t)))
2982 102 : (when input (setq command (format "%s <%s" command input)))
2983 :
2984 : ;; Determine output.
2985 102 : (cond
2986 : ;; Just a buffer.
2987 102 : ((bufferp destination)
2988 8 : (setq outbuf destination))
2989 : ;; A buffer name.
2990 94 : ((stringp destination)
2991 0 : (setq outbuf (get-buffer-create destination)))
2992 : ;; (REAL-DESTINATION ERROR-DESTINATION)
2993 94 : ((consp destination)
2994 : ;; output.
2995 0 : (cond
2996 0 : ((bufferp (car destination))
2997 0 : (setq outbuf (car destination)))
2998 0 : ((stringp (car destination))
2999 0 : (setq outbuf (get-buffer-create (car destination))))
3000 0 : ((car destination)
3001 0 : (setq outbuf (current-buffer))))
3002 : ;; stderr.
3003 0 : (cond
3004 0 : ((stringp (cadr destination))
3005 0 : (setcar (cdr destination) (expand-file-name (cadr destination)))
3006 0 : (if (tramp-equal-remote default-directory (cadr destination))
3007 : ;; stderr is on the same remote host.
3008 0 : (setq stderr (with-parsed-tramp-file-name
3009 0 : (cadr destination) nil localname))
3010 : ;; stderr must be copied to remote host. The temporary
3011 : ;; file must be deleted after execution.
3012 0 : (setq stderr (tramp-make-tramp-temp-file v)
3013 0 : tmpstderr (tramp-make-tramp-file-name
3014 0 : method user domain host port stderr))))
3015 : ;; stderr to be discarded.
3016 0 : ((null (cadr destination))
3017 0 : (setq stderr "/dev/null"))))
3018 : ;; 't
3019 94 : (destination
3020 102 : (setq outbuf (current-buffer))))
3021 102 : (when stderr (setq command (format "%s 2>%s" command stderr)))
3022 :
3023 : ;; Send the command. It might not return in time, so we protect
3024 : ;; it. Call it in a subshell, in order to preserve working
3025 : ;; directory.
3026 102 : (condition-case nil
3027 102 : (unwind-protect
3028 102 : (setq ret
3029 102 : (if (tramp-send-command-and-check
3030 102 : v (format "cd %s && %s"
3031 102 : (tramp-shell-quote-argument localname)
3032 102 : command)
3033 102 : t t)
3034 102 : 0 1))
3035 : ;; We should add the output anyway.
3036 102 : (when outbuf
3037 96 : (with-current-buffer outbuf
3038 96 : (insert
3039 96 : (with-current-buffer (tramp-get-connection-buffer v)
3040 96 : (buffer-string))))
3041 102 : (when (and display (get-buffer-window outbuf t)) (redisplay))))
3042 : ;; When the user did interrupt, we should do it also. We use
3043 : ;; return code -1 as marker.
3044 : (quit
3045 0 : (kill-buffer (tramp-get-connection-buffer v))
3046 0 : (setq ret -1))
3047 : ;; Handle errors.
3048 : (error
3049 0 : (kill-buffer (tramp-get-connection-buffer v))
3050 102 : (setq ret 1)))
3051 :
3052 : ;; Provide error file.
3053 102 : (when tmpstderr (rename-file tmpstderr (cadr destination) t))
3054 :
3055 : ;; Cleanup. We remove all file cache values for the connection,
3056 : ;; because the remote process could have changed them.
3057 102 : (when tmpinput (delete-file tmpinput))
3058 :
3059 102 : (unless process-file-side-effects
3060 102 : (tramp-flush-directory-property v ""))
3061 :
3062 : ;; Return exit status.
3063 102 : (if (equal ret -1)
3064 0 : (keyboard-quit)
3065 102 : ret))))
3066 :
3067 : (defun tramp-sh-handle-file-local-copy (filename)
3068 : "Like `file-local-copy' for Tramp files."
3069 270 : (with-parsed-tramp-file-name filename nil
3070 270 : (unless (file-exists-p filename)
3071 0 : (tramp-error
3072 0 : v tramp-file-missing
3073 270 : "Cannot make local copy of non-existing file `%s'" filename))
3074 :
3075 270 : (let* ((size (tramp-compat-file-attribute-size
3076 270 : (file-attributes (file-truename filename))))
3077 270 : (rem-enc (tramp-get-inline-coding v "remote-encoding" size))
3078 270 : (loc-dec (tramp-get-inline-coding v "local-decoding" size))
3079 270 : (tmpfile (tramp-compat-make-temp-file filename)))
3080 :
3081 270 : (condition-case err
3082 270 : (cond
3083 : ;; `copy-file' handles direct copy and out-of-band methods.
3084 270 : ((or (tramp-local-host-p v)
3085 270 : (tramp-method-out-of-band-p v size))
3086 0 : (copy-file filename tmpfile 'ok-if-already-exists 'keep-time))
3087 :
3088 : ;; Use inline encoding for file transfer.
3089 270 : (rem-enc
3090 270 : (save-excursion
3091 270 : (with-tramp-progress-reporter
3092 540 : v 3
3093 540 : (format-message "Encoding remote file `%s' with `%s'"
3094 540 : filename rem-enc)
3095 270 : (tramp-barf-unless-okay
3096 270 : v (format rem-enc (tramp-shell-quote-argument localname))
3097 270 : "Encoding remote file failed"))
3098 :
3099 270 : (with-tramp-progress-reporter
3100 540 : v 3 (format-message "Decoding local file `%s' with `%s'"
3101 540 : tmpfile loc-dec)
3102 270 : (if (functionp loc-dec)
3103 : ;; If local decoding is a function, we call it.
3104 : ;; We must disable multibyte, because
3105 : ;; `uudecode-decode-region' doesn't handle it
3106 : ;; correctly. Unset `file-name-handler-alist'.
3107 : ;; Otherwise, epa-file gets confused.
3108 270 : (let (file-name-handler-alist
3109 : (coding-system-for-write 'binary))
3110 270 : (with-temp-file tmpfile
3111 270 : (set-buffer-multibyte nil)
3112 270 : (insert-buffer-substring (tramp-get-buffer v))
3113 270 : (funcall loc-dec (point-min) (point-max))))
3114 :
3115 : ;; If tramp-decoding-function is not defined for this
3116 : ;; method, we invoke tramp-decoding-command instead.
3117 0 : (let ((tmpfile2 (tramp-compat-make-temp-file filename)))
3118 : ;; Unset `file-name-handler-alist'. Otherwise,
3119 : ;; epa-file gets confused.
3120 0 : (let (file-name-handler-alist
3121 : (coding-system-for-write 'binary))
3122 0 : (with-current-buffer (tramp-get-buffer v)
3123 0 : (write-region
3124 0 : (point-min) (point-max) tmpfile2 nil 'no-message)))
3125 0 : (unwind-protect
3126 0 : (tramp-call-local-coding-command
3127 0 : loc-dec tmpfile2 tmpfile)
3128 270 : (delete-file tmpfile2)))))
3129 :
3130 : ;; Set proper permissions.
3131 270 : (set-file-modes tmpfile (tramp-default-file-modes filename))
3132 : ;; Set local user ownership.
3133 270 : (tramp-set-file-uid-gid tmpfile)))
3134 :
3135 : ;; Oops, I don't know what to do.
3136 0 : (t (tramp-error
3137 270 : v 'file-error "Wrong method specification for `%s'" method)))
3138 :
3139 : ;; Error handling.
3140 : ((error quit)
3141 0 : (delete-file tmpfile)
3142 270 : (signal (car err) (cdr err))))
3143 :
3144 270 : (run-hooks 'tramp-handle-file-local-copy-hook)
3145 270 : tmpfile)))
3146 :
3147 : ;; CCC grok LOCKNAME
3148 : (defun tramp-sh-handle-write-region
3149 : (start end filename &optional append visit lockname mustbenew)
3150 : "Like `write-region' for Tramp files."
3151 409 : (setq filename (expand-file-name filename))
3152 409 : (with-parsed-tramp-file-name filename nil
3153 409 : (when (and mustbenew (file-exists-p filename)
3154 6 : (or (eq mustbenew 'excl)
3155 4 : (not
3156 4 : (y-or-n-p
3157 409 : (format "File %s exists; overwrite anyway? " filename)))))
3158 405 : (tramp-error v 'file-already-exists filename))
3159 :
3160 405 : (let ((uid (or (tramp-compat-file-attribute-user-id
3161 405 : (file-attributes filename 'integer))
3162 405 : (tramp-get-remote-uid v 'integer)))
3163 405 : (gid (or (tramp-compat-file-attribute-group-id
3164 405 : (file-attributes filename 'integer))
3165 405 : (tramp-get-remote-gid v 'integer))))
3166 :
3167 405 : (if (and (tramp-local-host-p v)
3168 : ;; `file-writable-p' calls `file-expand-file-name'. We
3169 : ;; cannot use `tramp-run-real-handler' therefore.
3170 0 : (let (file-name-handler-alist)
3171 0 : (and
3172 0 : (file-writable-p (file-name-directory localname))
3173 0 : (or (file-directory-p localname)
3174 405 : (file-writable-p localname)))))
3175 : ;; Short track: if we are on the local host, we can run directly.
3176 0 : (tramp-run-real-handler
3177 0 : 'write-region (list start end localname append 'no-message lockname))
3178 :
3179 405 : (let* ((modes (save-excursion (tramp-default-file-modes filename)))
3180 : ;; We use this to save the value of
3181 : ;; `last-coding-system-used' after writing the tmp
3182 : ;; file. At the end of the function, we set
3183 : ;; `last-coding-system-used' to this saved value. This
3184 : ;; way, any intermediary coding systems used while
3185 : ;; talking to the remote shell or suchlike won't hose
3186 : ;; this variable. This approach was snarfed from
3187 : ;; ange-ftp.el.
3188 : coding-system-used
3189 : ;; Write region into a tmp file. This isn't really
3190 : ;; needed if we use an encoding function, but currently
3191 : ;; we use it always because this makes the logic
3192 : ;; simpler. We must also set `temporary-file-directory',
3193 : ;; because it could point to a remote directory.
3194 : (temporary-file-directory
3195 405 : (tramp-compat-temporary-file-directory))
3196 405 : (tmpfile (or tramp-temp-buffer-file-name
3197 405 : (tramp-compat-make-temp-file filename))))
3198 :
3199 : ;; If `append' is non-nil, we copy the file locally, and let
3200 : ;; the native `write-region' implementation do the job.
3201 405 : (when append (copy-file filename tmpfile 'ok))
3202 :
3203 : ;; We say `no-message' here because we don't want the
3204 : ;; visited file modtime data to be clobbered from the temp
3205 : ;; file. We call `set-visited-file-modtime' ourselves later
3206 : ;; on. We must ensure that `file-coding-system-alist'
3207 : ;; matches `tmpfile'.
3208 405 : (let (file-name-handler-alist
3209 : (file-coding-system-alist
3210 405 : (tramp-find-file-name-coding-system-alist filename tmpfile)))
3211 405 : (condition-case err
3212 405 : (tramp-run-real-handler
3213 : 'write-region
3214 405 : (list start end tmpfile append 'no-message lockname))
3215 : ((error quit)
3216 0 : (setq tramp-temp-buffer-file-name nil)
3217 0 : (delete-file tmpfile)
3218 405 : (signal (car err) (cdr err))))
3219 :
3220 : ;; Now, `last-coding-system-used' has the right value. Remember it.
3221 405 : (setq coding-system-used last-coding-system-used))
3222 :
3223 : ;; The permissions of the temporary file should be set. If
3224 : ;; FILENAME does not exist (eq modes nil) it has been
3225 : ;; renamed to the backup file. This case `save-buffer'
3226 : ;; handles permissions.
3227 : ;; Ensure that it is still readable.
3228 405 : (when modes
3229 405 : (set-file-modes
3230 405 : tmpfile
3231 405 : (logior (or modes 0) (string-to-number "0400" 8))))
3232 :
3233 : ;; This is a bit lengthy due to the different methods
3234 : ;; possible for file transfer. First, we check whether the
3235 : ;; method uses an scp program. If so, we call it.
3236 : ;; Otherwise, both encoding and decoding command must be
3237 : ;; specified. However, if the method _also_ specifies an
3238 : ;; encoding function, then that is used for encoding the
3239 : ;; contents of the tmp file.
3240 405 : (let* ((size (tramp-compat-file-attribute-size
3241 405 : (file-attributes tmpfile)))
3242 405 : (rem-dec (tramp-get-inline-coding v "remote-decoding" size))
3243 405 : (loc-enc (tramp-get-inline-coding v "local-encoding" size)))
3244 405 : (cond
3245 : ;; `copy-file' handles direct copy and out-of-band methods.
3246 405 : ((or (tramp-local-host-p v)
3247 405 : (tramp-method-out-of-band-p v size))
3248 0 : (if (and (not (stringp start))
3249 0 : (= (or end (point-max)) (point-max))
3250 0 : (= (or start (point-min)) (point-min))
3251 0 : (tramp-get-method-parameter v 'tramp-copy-keep-tmpfile))
3252 0 : (progn
3253 0 : (setq tramp-temp-buffer-file-name tmpfile)
3254 0 : (condition-case err
3255 : ;; We keep the local file for performance
3256 : ;; reasons, useful for "rsync".
3257 0 : (copy-file tmpfile filename t)
3258 : ((error quit)
3259 0 : (setq tramp-temp-buffer-file-name nil)
3260 0 : (delete-file tmpfile)
3261 0 : (signal (car err) (cdr err)))))
3262 0 : (setq tramp-temp-buffer-file-name nil)
3263 : ;; Don't rename, in order to keep context in SELinux.
3264 0 : (unwind-protect
3265 0 : (copy-file tmpfile filename t)
3266 0 : (delete-file tmpfile))))
3267 :
3268 : ;; Use inline file transfer.
3269 405 : (rem-dec
3270 : ;; Encode tmpfile.
3271 405 : (unwind-protect
3272 405 : (with-temp-buffer
3273 405 : (set-buffer-multibyte nil)
3274 : ;; Use encoding function or command.
3275 405 : (with-tramp-progress-reporter
3276 810 : v 3 (format-message
3277 : "Encoding local file `%s' using `%s'"
3278 810 : tmpfile loc-enc)
3279 405 : (if (functionp loc-enc)
3280 : ;; The following `let' is a workaround for
3281 : ;; the base64.el that comes with pgnus-0.84.
3282 : ;; If both of the following conditions are
3283 : ;; satisfied, it tries to write to a local
3284 : ;; file in default-directory, but at this
3285 : ;; point, default-directory is remote.
3286 : ;; (`call-process-region' can't write to
3287 : ;; remote files, it seems.) The file in
3288 : ;; question is a tmp file anyway.
3289 405 : (let ((coding-system-for-read 'binary)
3290 : (default-directory
3291 405 : (tramp-compat-temporary-file-directory)))
3292 405 : (insert-file-contents-literally tmpfile)
3293 405 : (funcall loc-enc (point-min) (point-max)))
3294 :
3295 0 : (unless (zerop (tramp-call-local-coding-command
3296 0 : loc-enc tmpfile t))
3297 0 : (tramp-error
3298 0 : v 'file-error
3299 0 : (concat "Cannot write to `%s', "
3300 0 : "local encoding command `%s' failed")
3301 405 : filename loc-enc))))
3302 :
3303 : ;; Send buffer into remote decoding command which
3304 : ;; writes to remote file. Because this happens on
3305 : ;; the remote host, we cannot use the function.
3306 405 : (with-tramp-progress-reporter
3307 810 : v 3 (format-message
3308 : "Decoding remote file `%s' using `%s'"
3309 810 : filename rem-dec)
3310 405 : (goto-char (point-max))
3311 405 : (unless (bolp) (newline))
3312 405 : (tramp-send-command
3313 405 : v
3314 405 : (format
3315 405 : (concat rem-dec " <<'%s'\n%s%s")
3316 405 : (tramp-shell-quote-argument localname)
3317 405 : tramp-end-of-heredoc
3318 405 : (buffer-string)
3319 405 : tramp-end-of-heredoc))
3320 405 : (tramp-barf-unless-okay
3321 405 : v nil
3322 : "Couldn't write region to `%s', decode using `%s' failed"
3323 405 : filename rem-dec)
3324 : ;; When `file-precious-flag' is set, the region is
3325 : ;; written to a temporary file. Check that the
3326 : ;; checksum is equal to that from the local tmpfile.
3327 405 : (when file-precious-flag
3328 0 : (erase-buffer)
3329 0 : (and
3330 : ;; cksum runs locally, if possible.
3331 0 : (zerop (tramp-call-process v "cksum" tmpfile t))
3332 : ;; cksum runs remotely.
3333 0 : (tramp-send-command-and-check
3334 0 : v
3335 0 : (format
3336 0 : "cksum <%s" (tramp-shell-quote-argument localname)))
3337 : ;; ... they are different.
3338 0 : (not
3339 0 : (string-equal
3340 0 : (buffer-string)
3341 0 : (with-current-buffer (tramp-get-buffer v)
3342 0 : (buffer-string))))
3343 0 : (tramp-error
3344 0 : v 'file-error
3345 0 : (concat "Couldn't write region to `%s',"
3346 0 : " decode using `%s' failed")
3347 405 : filename rem-dec)))))
3348 :
3349 : ;; Save exit.
3350 405 : (delete-file tmpfile)))
3351 :
3352 : ;; That's not expected.
3353 : (t
3354 0 : (tramp-error
3355 0 : v 'file-error
3356 0 : (concat "Method `%s' should specify both encoding and "
3357 0 : "decoding command or an scp program")
3358 405 : method))))
3359 :
3360 : ;; Make `last-coding-system-used' have the right value.
3361 405 : (when coding-system-used
3362 405 : (set 'last-coding-system-used coding-system-used))))
3363 :
3364 405 : (tramp-flush-file-property v (file-name-directory localname))
3365 405 : (tramp-flush-file-property v localname)
3366 :
3367 : ;; We must protect `last-coding-system-used', now we have set it
3368 : ;; to its correct value.
3369 405 : (let (last-coding-system-used (need-chown t))
3370 : ;; Set file modification time.
3371 405 : (when (or (eq visit t) (stringp visit))
3372 0 : (let ((file-attr (file-attributes filename 'integer)))
3373 0 : (set-visited-file-modtime
3374 : ;; We must pass modtime explicitly, because FILENAME can
3375 : ;; be different from (buffer-file-name), f.e. if
3376 : ;; `file-precious-flag' is set.
3377 0 : (tramp-compat-file-attribute-modification-time file-attr))
3378 0 : (when (and (= (tramp-compat-file-attribute-user-id file-attr) uid)
3379 0 : (= (tramp-compat-file-attribute-group-id file-attr) gid))
3380 405 : (setq need-chown nil))))
3381 :
3382 : ;; Set the ownership.
3383 405 : (when need-chown
3384 405 : (tramp-set-file-uid-gid filename uid gid))
3385 405 : (when (or (eq visit t) (null visit) (stringp visit))
3386 405 : (tramp-message v 0 "Wrote %s" filename))
3387 405 : (run-hooks 'tramp-handle-write-region-hook)))))
3388 :
3389 : (defvar tramp-vc-registered-file-names nil
3390 : "List used to collect file names, which are checked during `vc-registered'.")
3391 :
3392 : ;; VC backends check for the existence of various different special
3393 : ;; files. This is very time consuming, because every single check
3394 : ;; requires a remote command (the file cache must be invalidated).
3395 : ;; Therefore, we apply a kind of optimization. We install the file
3396 : ;; name handler `tramp-vc-file-name-handler', which does nothing but
3397 : ;; remembers all file names for which `file-exists-p' or
3398 : ;; `file-readable-p' has been applied. A first run of `vc-registered'
3399 : ;; is performed. Afterwards, a script is applied for all collected
3400 : ;; file names, using just one remote command. The result of this
3401 : ;; script is used to fill the file cache with actual values. Now we
3402 : ;; can reset the file name handlers, and we make a second run of
3403 : ;; `vc-registered', which returns the expected result without sending
3404 : ;; any other remote command.
3405 : (defun tramp-sh-handle-vc-registered (file)
3406 : "Like `vc-registered' for Tramp files."
3407 0 : (with-temp-message ""
3408 0 : (with-parsed-tramp-file-name file nil
3409 0 : (with-tramp-progress-reporter
3410 0 : v 3 (format-message "Checking `vc-registered' for %s" file)
3411 :
3412 : ;; There could be new files, created by the vc backend. We
3413 : ;; cannot reuse the old cache entries, therefore. In
3414 : ;; `tramp-get-file-property', `remote-file-name-inhibit-cache'
3415 : ;; could also be a timestamp as `current-time' returns. This
3416 : ;; means invalidate all cache entries with an older timestamp.
3417 0 : (let (tramp-vc-registered-file-names
3418 0 : (remote-file-name-inhibit-cache (current-time))
3419 : (file-name-handler-alist
3420 0 : `((,(tramp-file-name-regexp) . tramp-vc-file-name-handler))))
3421 :
3422 : ;; Here we collect only file names, which need an operation.
3423 0 : (tramp-with-demoted-errors
3424 : v "Error in 1st pass of `vc-registered': %s"
3425 0 : (tramp-run-real-handler 'vc-registered (list file)))
3426 0 : (tramp-message v 10 "\n%s" tramp-vc-registered-file-names)
3427 :
3428 : ;; Send just one command, in order to fill the cache.
3429 0 : (when tramp-vc-registered-file-names
3430 0 : (tramp-maybe-send-script
3431 0 : v
3432 0 : (format tramp-vc-registered-read-file-names
3433 0 : (tramp-get-file-exists-command v)
3434 0 : (format "%s -r" (tramp-get-test-command v)))
3435 0 : "tramp_vc_registered_read_file_names")
3436 :
3437 0 : (dolist
3438 : (elt
3439 0 : (ignore-errors
3440 : ;; We cannot use `tramp-send-command-and-read',
3441 : ;; because this does not cooperate well with
3442 : ;; heredoc documents.
3443 0 : (tramp-send-command
3444 0 : v
3445 0 : (format
3446 : "tramp_vc_registered_read_file_names <<'%s'\n%s\n%s\n"
3447 0 : tramp-end-of-heredoc
3448 0 : (mapconcat 'tramp-shell-quote-argument
3449 0 : tramp-vc-registered-file-names
3450 0 : "\n")
3451 0 : tramp-end-of-heredoc))
3452 0 : (with-current-buffer (tramp-get-connection-buffer v)
3453 : ;; Read the expression.
3454 0 : (goto-char (point-min))
3455 0 : (read (current-buffer)))))
3456 :
3457 0 : (tramp-set-file-property
3458 0 : v (car elt) (cadr elt) (cadr (cdr elt))))))
3459 :
3460 : ;; Second run. Now all `file-exists-p' or `file-readable-p'
3461 : ;; calls shall be answered from the file cache. We unset
3462 : ;; `process-file-side-effects' and `remote-file-name-inhibit-cache'
3463 : ;; in order to keep the cache.
3464 0 : (let ((vc-handled-backends vc-handled-backends)
3465 : remote-file-name-inhibit-cache process-file-side-effects)
3466 : ;; Reduce `vc-handled-backends' in order to minimize process calls.
3467 0 : (when (and (memq 'Bzr vc-handled-backends)
3468 0 : (boundp 'vc-bzr-program)
3469 0 : (not (with-tramp-connection-property v vc-bzr-program
3470 0 : (tramp-find-executable
3471 0 : v vc-bzr-program (tramp-get-remote-path v)))))
3472 0 : (setq vc-handled-backends (remq 'Bzr vc-handled-backends)))
3473 0 : (when (and (memq 'Git vc-handled-backends)
3474 0 : (boundp 'vc-git-program)
3475 0 : (not (with-tramp-connection-property v vc-git-program
3476 0 : (tramp-find-executable
3477 0 : v vc-git-program (tramp-get-remote-path v)))))
3478 0 : (setq vc-handled-backends (remq 'Git vc-handled-backends)))
3479 0 : (when (and (memq 'Hg vc-handled-backends)
3480 0 : (boundp 'vc-hg-program)
3481 0 : (not (with-tramp-connection-property v vc-hg-program
3482 0 : (tramp-find-executable
3483 0 : v vc-hg-program (tramp-get-remote-path v)))))
3484 0 : (setq vc-handled-backends (remq 'Hg vc-handled-backends)))
3485 : ;; Run.
3486 0 : (tramp-with-demoted-errors
3487 : v "Error in 2nd pass of `vc-registered': %s"
3488 0 : (tramp-run-real-handler 'vc-registered (list file))))))))
3489 :
3490 : ;;;###tramp-autoload
3491 : (defun tramp-sh-file-name-handler (operation &rest args)
3492 : "Invoke remote-shell Tramp file name handler.
3493 : Fall back to normal file name handler if no Tramp handler exists."
3494 46539 : (let ((fn (assoc operation tramp-sh-file-name-handler-alist)))
3495 46539 : (if fn
3496 46539 : (save-match-data (apply (cdr fn) args))
3497 46517 : (tramp-run-real-handler operation args))))
3498 :
3499 : ;; This must be the last entry, because `identity' always matches.
3500 : ;;;###tramp-autoload
3501 : (tramp-register-foreign-file-name-handler
3502 : 'identity 'tramp-sh-file-name-handler 'append)
3503 :
3504 : (defun tramp-vc-file-name-handler (operation &rest args)
3505 : "Invoke special file name handler, which collects files to be handled."
3506 0 : (save-match-data
3507 0 : (let ((filename
3508 0 : (tramp-replace-environment-variables
3509 0 : (apply 'tramp-file-name-for-operation operation args)))
3510 0 : (fn (assoc operation tramp-sh-file-name-handler-alist)))
3511 0 : (with-parsed-tramp-file-name filename nil
3512 0 : (cond
3513 : ;; That's what we want: file names, for which checks are
3514 : ;; applied. We assume that VC uses only `file-exists-p' and
3515 : ;; `file-readable-p' checks; otherwise we must extend the
3516 : ;; list. We do not perform any action, but return nil, in
3517 : ;; order to keep `vc-registered' running.
3518 0 : ((and fn (memq operation '(file-exists-p file-readable-p)))
3519 0 : (add-to-list 'tramp-vc-registered-file-names localname 'append)
3520 : nil)
3521 : ;; `process-file' and `start-file-process' shall be ignored.
3522 0 : ((and fn (eq operation 'process-file) 0))
3523 0 : ((and fn (eq operation 'start-file-process) nil))
3524 : ;; Tramp file name handlers like `expand-file-name'. They
3525 : ;; must still work.
3526 0 : (fn (save-match-data (apply (cdr fn) args)))
3527 : ;; Default file name handlers, we don't care.
3528 0 : (t (tramp-run-real-handler operation args)))))))
3529 :
3530 : (defun tramp-sh-handle-file-notify-add-watch (file-name flags _callback)
3531 : "Like `file-notify-add-watch' for Tramp files."
3532 0 : (setq file-name (expand-file-name file-name))
3533 0 : (with-parsed-tramp-file-name file-name nil
3534 0 : (let ((default-directory (file-name-directory file-name))
3535 : command events filter p sequence)
3536 0 : (cond
3537 : ;; gvfs-monitor-dir.
3538 0 : ((setq command (tramp-get-remote-gvfs-monitor-dir v))
3539 0 : (setq filter 'tramp-sh-gvfs-monitor-dir-process-filter
3540 : events
3541 0 : (cond
3542 0 : ((and (memq 'change flags) (memq 'attribute-change flags))
3543 : '(created changed changes-done-hint moved deleted
3544 : attribute-changed))
3545 0 : ((memq 'change flags)
3546 : '(created changed changes-done-hint moved deleted))
3547 0 : ((memq 'attribute-change flags) '(attribute-changed)))
3548 0 : sequence `(,command ,localname)))
3549 : ;; inotifywait.
3550 0 : ((setq command (tramp-get-remote-inotifywait v))
3551 0 : (setq filter 'tramp-sh-inotifywait-process-filter
3552 : events
3553 0 : (cond
3554 0 : ((and (memq 'change flags) (memq 'attribute-change flags))
3555 0 : (concat "create,modify,move,moved_from,moved_to,move_self,"
3556 0 : "delete,delete_self,attrib,ignored"))
3557 0 : ((memq 'change flags)
3558 0 : (concat "create,modify,move,moved_from,moved_to,move_self,"
3559 0 : "delete,delete_self,ignored"))
3560 0 : ((memq 'attribute-change flags) "attrib,ignored"))
3561 0 : sequence `(,command "-mq" "-e" ,events ,localname)
3562 : ;; Make events a list of symbols.
3563 : events
3564 0 : (mapcar
3565 0 : (lambda (x) (intern-soft (replace-regexp-in-string "_" "-" x)))
3566 0 : (split-string events "," 'omit))))
3567 : ;; None.
3568 0 : (t (tramp-error
3569 0 : v 'file-notify-error
3570 : "No file notification program found on %s"
3571 0 : (file-remote-p file-name))))
3572 : ;; Start process.
3573 0 : (setq p (apply
3574 : 'start-file-process
3575 0 : (file-name-nondirectory command)
3576 0 : (generate-new-buffer
3577 0 : (format " *%s*" (file-name-nondirectory command)))
3578 0 : sequence))
3579 : ;; Return the process object as watch-descriptor.
3580 0 : (if (not (processp p))
3581 0 : (tramp-error
3582 0 : v 'file-notify-error
3583 : "`%s' failed to start on remote host"
3584 0 : (mapconcat 'identity sequence " "))
3585 0 : (tramp-message v 6 "Run `%s', %S" (mapconcat 'identity sequence " ") p)
3586 0 : (tramp-set-connection-property p "vector" v)
3587 : ;; Needed for process filter.
3588 0 : (process-put p 'events events)
3589 0 : (process-put p 'watch-name localname)
3590 0 : (set-process-query-on-exit-flag p nil)
3591 0 : (set-process-filter p filter)
3592 : ;; There might be an error if the monitor is not supported.
3593 : ;; Give the filter a chance to read the output.
3594 0 : (tramp-accept-process-output p 1)
3595 0 : (unless (process-live-p p)
3596 0 : (tramp-error
3597 0 : v 'file-notify-error "Monitoring not supported for `%s'" file-name))
3598 0 : p))))
3599 :
3600 : (defun tramp-sh-gvfs-monitor-dir-process-filter (proc string)
3601 : "Read output from \"gvfs-monitor-dir\" and add corresponding \
3602 : file-notify events."
3603 0 : (let ((events (process-get proc 'events))
3604 : (remote-prefix
3605 0 : (with-current-buffer (process-buffer proc)
3606 0 : (file-remote-p default-directory)))
3607 0 : (rest-string (process-get proc 'rest-string)))
3608 0 : (when rest-string
3609 0 : (tramp-message proc 10 "Previous string:\n%s" rest-string))
3610 0 : (tramp-message proc 6 "%S\n%s" proc string)
3611 0 : (setq string (concat rest-string string)
3612 : ;; Attribute change is returned in unused wording.
3613 0 : string (replace-regexp-in-string
3614 0 : "ATTRIB CHANGED" "ATTRIBUTE_CHANGED" string))
3615 0 : (when (string-match "Monitoring not supported" string)
3616 0 : (delete-process proc))
3617 :
3618 0 : (while (string-match
3619 0 : (concat "^[\n\r]*"
3620 : "Directory Monitor Event:[\n\r]+"
3621 : "Child = \\([^\n\r]+\\)[\n\r]+"
3622 : "\\(Other = \\([^\n\r]+\\)[\n\r]+\\)?"
3623 0 : "Event = \\([^[:blank:]]+\\)[\n\r]+")
3624 0 : string)
3625 0 : (let* ((file (match-string 1 string))
3626 0 : (file1 (match-string 3 string))
3627 : (object
3628 0 : (list
3629 0 : proc
3630 0 : (list
3631 0 : (intern-soft
3632 0 : (replace-regexp-in-string
3633 0 : "_" "-" (downcase (match-string 4 string)))))
3634 : ;; File names are returned as absolute paths. We must
3635 : ;; add the remote prefix.
3636 0 : (concat remote-prefix file)
3637 0 : (when file1 (concat remote-prefix file1)))))
3638 0 : (setq string (replace-match "" nil nil string))
3639 : ;; Remove watch when file or directory to be watched is deleted.
3640 0 : (when (and (member (cl-caadr object) '(moved deleted))
3641 0 : (string-equal file (process-get proc 'watch-name)))
3642 0 : (delete-process proc))
3643 : ;; Usually, we would add an Emacs event now. Unfortunately,
3644 : ;; `unread-command-events' does not accept several events at
3645 : ;; once. Therefore, we apply the handler directly.
3646 0 : (when (member (cl-caadr object) events)
3647 0 : (tramp-compat-funcall
3648 : 'file-notify-handle-event
3649 0 : `(file-notify ,object file-notify-callback)))))
3650 :
3651 : ;; Save rest of the string.
3652 0 : (when (zerop (length string)) (setq string nil))
3653 0 : (when string (tramp-message proc 10 "Rest string:\n%s" string))
3654 0 : (process-put proc 'rest-string string)))
3655 :
3656 : (defun tramp-sh-inotifywait-process-filter (proc string)
3657 : "Read output from \"inotifywait\" and add corresponding file-notify events."
3658 0 : (let ((events (process-get proc 'events)))
3659 0 : (tramp-message proc 6 "%S\n%s" proc string)
3660 0 : (dolist (line (split-string string "[\n\r]+" 'omit))
3661 : ;; Check, whether there is a problem.
3662 0 : (unless
3663 0 : (string-match
3664 0 : (concat "^[^[:blank:]]+"
3665 : "[[:blank:]]+\\([^[:blank:]]+\\)+"
3666 0 : "\\([[:blank:]]+\\([^\n\r]+\\)\\)?")
3667 0 : line)
3668 0 : (tramp-error proc 'file-notify-error "%s" line))
3669 :
3670 0 : (let ((object
3671 0 : (list
3672 0 : proc
3673 0 : (mapcar
3674 : (lambda (x)
3675 0 : (intern-soft
3676 0 : (replace-regexp-in-string "_" "-" (downcase x))))
3677 0 : (split-string (match-string 1 line) "," 'omit))
3678 0 : (match-string 3 line))))
3679 : ;; Remove watch when file or directory to be watched is deleted.
3680 0 : (when (member (cl-caadr object) '(move-self delete-self ignored))
3681 0 : (delete-process proc))
3682 : ;; Usually, we would add an Emacs event now. Unfortunately,
3683 : ;; `unread-command-events' does not accept several events at
3684 : ;; once. Therefore, we apply the handler directly.
3685 0 : (when (member (cl-caadr object) events)
3686 0 : (tramp-compat-funcall
3687 : 'file-notify-handle-event
3688 0 : `(file-notify ,object file-notify-callback)))))))
3689 :
3690 : ;;; Internal Functions:
3691 :
3692 : (defun tramp-maybe-send-script (vec script name)
3693 : "Define in remote shell function NAME implemented as SCRIPT.
3694 : Only send the definition if it has not already been done."
3695 : ;; We cannot let-bind (tramp-get-connection-process vec) because it
3696 : ;; might be nil.
3697 415 : (let ((scripts (tramp-get-connection-property
3698 415 : (tramp-get-connection-process vec) "scripts" nil)))
3699 415 : (unless (member name scripts)
3700 14 : (with-tramp-progress-reporter
3701 28 : vec 5 (format-message "Sending script `%s'" name)
3702 : ;; In bash, leading TABs like in `tramp-vc-registered-read-file-names'
3703 : ;; could result in unwanted command expansion. Avoid this.
3704 14 : (setq script (replace-regexp-in-string
3705 14 : (make-string 1 ?\t) (make-string 8 ? ) script))
3706 : ;; The script could contain a call of Perl. This is masked with `%s'.
3707 14 : (when (and (string-match "%s" script)
3708 14 : (not (tramp-get-remote-perl vec)))
3709 14 : (tramp-error vec 'file-error "No Perl available on remote host"))
3710 14 : (tramp-barf-unless-okay
3711 14 : vec
3712 14 : (format "%s () {\n%s\n}"
3713 14 : name (format script (tramp-get-remote-perl vec)))
3714 14 : "Script %s sending failed" name)
3715 14 : (tramp-set-connection-property
3716 415 : (tramp-get-connection-process vec) "scripts" (cons name scripts))))))
3717 :
3718 : (defun tramp-run-test (switch filename)
3719 : "Run `test' on the remote system, given a SWITCH and a FILENAME.
3720 : Returns the exit code of the `test' program."
3721 1702 : (with-parsed-tramp-file-name filename nil
3722 1702 : (tramp-send-command-and-check
3723 1702 : v
3724 1702 : (format
3725 : "%s %s %s"
3726 1702 : (tramp-get-test-command v)
3727 1702 : switch
3728 1702 : (tramp-shell-quote-argument localname)))))
3729 :
3730 : (defun tramp-run-test2 (format-string file1 file2)
3731 : "Run `test'-like program on the remote system, given FILE1, FILE2.
3732 : FORMAT-STRING contains the program name, switches, and place holders.
3733 : Returns the exit code of the `test' program. Barfs if the methods,
3734 : hosts, or files, disagree."
3735 0 : (unless (tramp-equal-remote file1 file2)
3736 0 : (with-parsed-tramp-file-name (if (tramp-tramp-file-p file1) file1 file2) nil
3737 0 : (tramp-error
3738 0 : v 'file-error
3739 0 : "tramp-run-test2 only implemented for same method, user, host")))
3740 0 : (with-parsed-tramp-file-name file1 v1
3741 0 : (with-parsed-tramp-file-name file1 v2
3742 0 : (tramp-send-command-and-check
3743 0 : v1
3744 0 : (format format-string
3745 0 : (tramp-shell-quote-argument v1-localname)
3746 0 : (tramp-shell-quote-argument v2-localname))))))
3747 :
3748 : (defun tramp-find-executable
3749 : (vec progname dirlist &optional ignore-tilde ignore-path)
3750 : "Searches for PROGNAME in $PATH and all directories mentioned in DIRLIST.
3751 : First arg VEC specifies the connection, PROGNAME is the program
3752 : to search for, and DIRLIST gives the list of directories to
3753 : search. If IGNORE-TILDE is non-nil, directory names starting
3754 : with `~' will be ignored. If IGNORE-PATH is non-nil, searches
3755 : only in DIRLIST.
3756 :
3757 : Returns the absolute file name of PROGNAME, if found, and nil otherwise.
3758 :
3759 : This function expects to be in the right *tramp* buffer."
3760 136 : (with-current-buffer (tramp-get-connection-buffer vec)
3761 136 : (let (result)
3762 : ;; Check whether the executable is in $PATH. "which(1)" does not
3763 : ;; report always a correct error code; therefore we check the
3764 : ;; number of words it returns. "SunOS 5.10" (and maybe "SunOS
3765 : ;; 5.11") have problems with this command, we disable the call
3766 : ;; therefore.
3767 136 : (unless (or ignore-path
3768 91 : (string-match
3769 91 : (regexp-opt '("SunOS 5.10" "SunOS 5.11"))
3770 136 : (tramp-get-connection-property vec "uname" "")))
3771 91 : (tramp-send-command vec (format "which \\%s | wc -w" progname))
3772 91 : (goto-char (point-min))
3773 91 : (if (looking-at "^\\s-*1$")
3774 136 : (setq result (concat "\\" progname))))
3775 136 : (unless result
3776 56 : (when ignore-tilde
3777 : ;; Remove all ~/foo directories from dirlist.
3778 45 : (let (newdl d)
3779 180 : (while dirlist
3780 135 : (setq d (car dirlist))
3781 135 : (setq dirlist (cdr dirlist))
3782 135 : (unless (char-equal ?~ (aref d 0))
3783 135 : (setq newdl (cons d newdl))))
3784 56 : (setq dirlist (nreverse newdl))))
3785 56 : (tramp-send-command
3786 56 : vec
3787 56 : (format (concat "while read d; "
3788 : "do if test -x $d/%s && test -f $d/%s; "
3789 : "then echo tramp_executable $d/%s; "
3790 : "break; fi; done <<'%s'\n"
3791 56 : "%s\n%s")
3792 56 : progname progname progname
3793 56 : tramp-end-of-heredoc
3794 56 : (mapconcat 'identity dirlist "\n")
3795 56 : tramp-end-of-heredoc))
3796 56 : (goto-char (point-max))
3797 56 : (when (search-backward "tramp_executable " nil t)
3798 45 : (skip-chars-forward "^ ")
3799 45 : (skip-chars-forward " ")
3800 136 : (setq result (buffer-substring (point) (point-at-eol)))))
3801 136 : result)))
3802 :
3803 : (defun tramp-set-remote-path (vec)
3804 : "Sets the remote environment PATH to existing directories.
3805 : I.e., for each directory in `tramp-remote-path', it is tested
3806 : whether it exists and if so, it is added to the environment
3807 : variable PATH."
3808 71 : (tramp-message vec 5 "Setting $PATH environment variable")
3809 71 : (tramp-send-command
3810 71 : vec (format "PATH=%s; export PATH"
3811 71 : (mapconcat 'identity (tramp-get-remote-path vec) ":"))))
3812 :
3813 : ;; ------------------------------------------------------------
3814 : ;; -- Communication with external shell --
3815 : ;; ------------------------------------------------------------
3816 :
3817 : (defun tramp-find-file-exists-command (vec)
3818 : "Find a command on the remote host for checking if a file exists.
3819 : Here, we are looking for a command which has zero exit status if the
3820 : file exists and nonzero exit status otherwise."
3821 36 : (let ((existing "/")
3822 : (nonexistent
3823 36 : (tramp-shell-quote-argument "/ this file does not exist "))
3824 : result)
3825 : ;; The algorithm is as follows: we try a list of several commands.
3826 : ;; For each command, we first run `$cmd /' -- this should return
3827 : ;; true, as the root directory always exists. And then we run
3828 : ;; `$cmd /this\ file\ does\ not\ exist ', hoping that the file indeed
3829 : ;; does not exist. This should return false. We use the first
3830 : ;; command we find that seems to work.
3831 : ;; The list of commands to try is as follows:
3832 : ;; `ls -d' This works on most systems, but NetBSD 1.4
3833 : ;; has a bug: `ls' always returns zero exit
3834 : ;; status, even for files which don't exist.
3835 : ;; `test -e' Some Bourne shells have a `test' builtin
3836 : ;; which does not know the `-e' option.
3837 : ;; `/bin/test -e' For those, the `test' binary on disk normally
3838 : ;; provides the option. Alas, the binary
3839 : ;; is sometimes `/bin/test' and sometimes it's
3840 : ;; `/usr/bin/test'.
3841 : ;; `/usr/bin/test -e' In case `/bin/test' does not exist.
3842 36 : (unless (or
3843 36 : (ignore-errors
3844 36 : (and (setq result (format "%s -e" (tramp-get-test-command vec)))
3845 36 : (tramp-send-command-and-check
3846 36 : vec (format "%s %s" result existing))
3847 36 : (not (tramp-send-command-and-check
3848 36 : vec (format "%s %s" result nonexistent)))))
3849 0 : (ignore-errors
3850 0 : (and (setq result "/bin/test -e")
3851 0 : (tramp-send-command-and-check
3852 0 : vec (format "%s %s" result existing))
3853 0 : (not (tramp-send-command-and-check
3854 0 : vec (format "%s %s" result nonexistent)))))
3855 0 : (ignore-errors
3856 0 : (and (setq result "/usr/bin/test -e")
3857 0 : (tramp-send-command-and-check
3858 0 : vec (format "%s %s" result existing))
3859 0 : (not (tramp-send-command-and-check
3860 0 : vec (format "%s %s" result nonexistent)))))
3861 0 : (ignore-errors
3862 0 : (and (setq result (format "%s -d" (tramp-get-ls-command vec)))
3863 0 : (tramp-send-command-and-check
3864 0 : vec (format "%s %s" result existing))
3865 0 : (not (tramp-send-command-and-check
3866 36 : vec (format "%s %s" result nonexistent))))))
3867 0 : (tramp-error
3868 36 : vec 'file-error "Couldn't find command to check if file exists"))
3869 36 : result))
3870 :
3871 : (defun tramp-open-shell (vec shell)
3872 : "Opens shell SHELL."
3873 71 : (with-tramp-progress-reporter
3874 142 : vec 5 (format-message "Opening remote shell `%s'" shell)
3875 : ;; Find arguments for this shell.
3876 71 : (let ((alist tramp-sh-extra-args)
3877 : item extra-args)
3878 142 : (while (and alist (null extra-args))
3879 142 : (setq item (pop alist))
3880 71 : (when (string-match (car item) shell)
3881 71 : (setq extra-args (cdr item))))
3882 : ;; It is useful to set the prompt in the following command
3883 : ;; because some people have a setting for $PS1 which /bin/sh
3884 : ;; doesn't know about and thus /bin/sh will display a strange
3885 : ;; prompt. For example, if $PS1 has "${CWD}" in the value, then
3886 : ;; ksh will display the current working directory but /bin/sh
3887 : ;; will display a dollar sign. The following command line sets
3888 : ;; $PS1 to a sane value, and works under Bourne-ish shells as
3889 : ;; well as csh-like shells. We also unset the variable $ENV
3890 : ;; because that is read by some sh implementations (eg, bash
3891 : ;; when called as sh) on startup; this way, we avoid the startup
3892 : ;; file clobbering $PS1. $PROMPT_COMMAND is another way to set
3893 : ;; the prompt in /bin/bash, it must be discarded as well.
3894 : ;; $HISTFILE is set according to `tramp-histfile-override'.
3895 71 : (tramp-send-command
3896 71 : vec (format
3897 : "exec env ENV=%s %s PROMPT_COMMAND='' PS1=%s PS2='' PS3='' %s %s"
3898 71 : (or (getenv-internal "ENV" tramp-remote-process-environment) "")
3899 71 : (if (stringp tramp-histfile-override)
3900 71 : (format "HISTFILE=%s"
3901 71 : (tramp-shell-quote-argument tramp-histfile-override))
3902 0 : (if tramp-histfile-override
3903 : "HISTFILE='' HISTFILESIZE=0 HISTSIZE=0"
3904 71 : ""))
3905 71 : (tramp-shell-quote-argument tramp-end-of-output)
3906 71 : shell (or extra-args ""))
3907 71 : t)
3908 : ;; Check proper HISTFILE setting. We give up when not working.
3909 71 : (when (and (stringp tramp-histfile-override)
3910 71 : (file-name-directory tramp-histfile-override))
3911 71 : (tramp-barf-unless-okay
3912 71 : vec
3913 71 : (format
3914 : "(cd %s)"
3915 71 : (tramp-shell-quote-argument
3916 71 : (file-name-directory tramp-histfile-override)))
3917 : "`tramp-histfile-override' uses invalid file `%s'"
3918 71 : tramp-histfile-override)))
3919 :
3920 71 : (tramp-set-connection-property
3921 71 : (tramp-get-connection-process vec) "remote-shell" shell)))
3922 :
3923 : (defun tramp-find-shell (vec)
3924 : "Opens a shell on the remote host which groks tilde expansion."
3925 71 : (with-current-buffer (tramp-get-buffer vec)
3926 71 : (let ((default-shell (tramp-get-method-parameter vec 'tramp-remote-shell))
3927 : shell)
3928 71 : (setq shell
3929 115 : (with-tramp-connection-property vec "remote-shell"
3930 : ;; CCC: "root" does not exist always, see my QNAP TS-459.
3931 : ;; Which check could we apply instead?
3932 44 : (tramp-send-command vec "echo ~root" t)
3933 44 : (if (or (string-match "^~root$" (buffer-string))
3934 : ;; The default shell (ksh93) of OpenSolaris and
3935 : ;; Solaris is buggy. We've got reports for
3936 : ;; "SunOS 5.10" and "SunOS 5.11" so far.
3937 44 : (string-match (regexp-opt '("SunOS 5.10" "SunOS 5.11"))
3938 44 : (tramp-get-connection-property
3939 44 : vec "uname" "")))
3940 :
3941 0 : (or (tramp-find-executable
3942 0 : vec "bash" (tramp-get-remote-path vec) t t)
3943 0 : (tramp-find-executable
3944 0 : vec "ksh" (tramp-get-remote-path vec) t t)
3945 : ;; Maybe it works at least for some other commands.
3946 0 : (prog1
3947 0 : default-shell
3948 0 : (tramp-message
3949 0 : vec 2
3950 0 : (concat
3951 : "Couldn't find a remote shell which groks tilde "
3952 0 : "expansion, using `%s'")
3953 0 : default-shell)))
3954 :
3955 71 : default-shell)))
3956 :
3957 : ;; Open a new shell if needed.
3958 71 : (unless (string-equal shell default-shell)
3959 0 : (tramp-message
3960 0 : vec 5 "Starting remote shell `%s' for tilde expansion" shell)
3961 71 : (tramp-open-shell vec shell)))))
3962 :
3963 : ;; Utility functions.
3964 :
3965 : (defun tramp-barf-if-no-shell-prompt (proc timeout &rest error-args)
3966 : "Wait for shell prompt and barf if none appears.
3967 : Looks at process PROC to see if a shell prompt appears in TIMEOUT
3968 : seconds. If not, it produces an error message with the given ERROR-ARGS."
3969 71 : (let ((vec (tramp-get-connection-property proc "vector" nil)))
3970 71 : (condition-case nil
3971 71 : (tramp-wait-for-regexp
3972 71 : proc timeout
3973 71 : (format
3974 71 : "\\(%s\\|%s\\)\\'" shell-prompt-pattern tramp-shell-prompt-pattern))
3975 : (error
3976 0 : (delete-process proc)
3977 0 : (apply 'tramp-error-with-buffer
3978 71 : (tramp-get-connection-buffer vec) vec 'file-error error-args)))))
3979 :
3980 : (defun tramp-open-connection-setup-interactive-shell (proc vec)
3981 : "Set up an interactive shell.
3982 : Mainly sets the prompt and the echo correctly. PROC is the shell
3983 : process to set up. VEC specifies the connection."
3984 71 : (let ((tramp-end-of-output tramp-initial-end-of-output)
3985 : (case-fold-search t))
3986 71 : (tramp-open-shell vec (tramp-get-method-parameter vec 'tramp-remote-shell))
3987 :
3988 : ;; Disable echo expansion.
3989 71 : (tramp-message vec 5 "Setting up remote shell environment")
3990 71 : (tramp-send-command
3991 71 : vec "stty -inlcr -onlcr -echo kill '^U' erase '^H'" t)
3992 : ;; Check whether the echo has really been disabled. Some
3993 : ;; implementations, like busybox of embedded GNU/Linux, don't
3994 : ;; support disabling.
3995 71 : (tramp-send-command vec "echo foo" t)
3996 71 : (with-current-buffer (process-buffer proc)
3997 71 : (goto-char (point-min))
3998 71 : (when (looking-at "echo foo")
3999 0 : (tramp-set-connection-property proc "remote-echo" t)
4000 0 : (tramp-message vec 5 "Remote echo still on. Ok.")
4001 : ;; Make sure backspaces and their echo are enabled and no line
4002 : ;; width magic interferes with them.
4003 71 : (tramp-send-command vec "stty icanon erase ^H cols 32767" t))))
4004 :
4005 71 : (tramp-message vec 5 "Setting shell prompt")
4006 71 : (tramp-send-command
4007 71 : vec (format "PS1=%s PS2='' PS3='' PROMPT_COMMAND=''"
4008 71 : (tramp-shell-quote-argument tramp-end-of-output))
4009 71 : t)
4010 :
4011 : ;; Check whether the output of "uname -sr" has been changed. If
4012 : ;; yes, this is a strong indication that we must expire all
4013 : ;; connection properties. We start again with
4014 : ;; `tramp-maybe-open-connection', it will be caught there.
4015 71 : (tramp-message vec 5 "Checking system information")
4016 71 : (let ((old-uname (tramp-get-connection-property vec "uname" nil))
4017 : (uname
4018 71 : (tramp-set-connection-property
4019 71 : vec "uname"
4020 71 : (tramp-send-command-and-read vec "echo \\\"`uname -sr`\\\""))))
4021 71 : (when (and (stringp old-uname) (not (string-equal old-uname uname)))
4022 0 : (tramp-message
4023 0 : vec 3
4024 : "Connection reset, because remote host changed from `%s' to `%s'"
4025 0 : old-uname uname)
4026 : ;; We want to keep the password.
4027 0 : (tramp-cleanup-connection vec t t)
4028 71 : (throw 'uname-changed (tramp-maybe-open-connection vec)))
4029 :
4030 : ;; Try to set up the coding system correctly.
4031 : ;; CCC this can't be the right way to do it. Hm.
4032 71 : (tramp-message vec 5 "Determining coding system")
4033 71 : (with-current-buffer (process-buffer proc)
4034 : ;; Use MULE to select the right EOL convention for communicating
4035 : ;; with the process.
4036 71 : (let ((cs (or (and (memq 'utf-8 (coding-system-list))
4037 71 : (string-match "utf-?8" (tramp-get-remote-locale vec))
4038 71 : (cons 'utf-8 'utf-8))
4039 0 : (process-coding-system proc)
4040 71 : (cons 'undecided 'undecided)))
4041 : cs-decode cs-encode)
4042 71 : (when (symbolp cs) (setq cs (cons cs cs)))
4043 71 : (setq cs-decode (or (car cs) 'undecided)
4044 71 : cs-encode (or (cdr cs) 'undecided)
4045 : cs-encode
4046 71 : (coding-system-change-eol-conversion
4047 71 : cs-encode (if (string-match "^Darwin" uname) 'mac 'unix)))
4048 71 : (tramp-send-command vec "echo foo ; echo bar" t)
4049 71 : (goto-char (point-min))
4050 71 : (when (search-forward "\r" nil t)
4051 71 : (setq cs-decode (coding-system-change-eol-conversion cs-decode 'dos)))
4052 : ;; Special setting for macOS.
4053 71 : (when (and (string-match "^Darwin" uname)
4054 71 : (memq 'utf-8-hfs (coding-system-list)))
4055 0 : (setq cs-decode 'utf-8-hfs
4056 71 : cs-encode 'utf-8-hfs))
4057 71 : (set-buffer-process-coding-system cs-decode cs-encode)
4058 71 : (tramp-message
4059 71 : vec 5 "Setting coding system to `%s' and `%s'" cs-decode cs-encode)))
4060 :
4061 71 : (tramp-send-command vec "set +o vi +o emacs" t)
4062 :
4063 : ;; Check whether the remote host suffers from buggy
4064 : ;; `send-process-string'. This is known for FreeBSD (see comment
4065 : ;; in `send_process', file process.c). I've tested sending 624
4066 : ;; bytes successfully, sending 625 bytes failed. Emacs makes a
4067 : ;; hack when this host type is detected locally. It cannot handle
4068 : ;; remote hosts, though.
4069 142 : (with-tramp-connection-property proc "chunksize"
4070 71 : (cond
4071 71 : ((and (integerp tramp-chunksize) (> tramp-chunksize 0))
4072 0 : tramp-chunksize)
4073 : (t
4074 71 : (tramp-message
4075 71 : vec 5 "Checking remote host type for `send-process-string' bug")
4076 71 : (if (string-match "^FreeBSD" uname) 500 0))))
4077 :
4078 : ;; Set remote PATH variable.
4079 71 : (tramp-set-remote-path vec)
4080 :
4081 : ;; Search for a good shell before searching for a command which
4082 : ;; checks if a file exists. This is done because Tramp wants to
4083 : ;; use "test foo; echo $?" to check if various conditions hold,
4084 : ;; and there are buggy /bin/sh implementations which don't execute
4085 : ;; the "echo $?" part if the "test" part has an error. In
4086 : ;; particular, the OpenSolaris /bin/sh is a problem. There are
4087 : ;; also other problems with /bin/sh of OpenSolaris, like
4088 : ;; redirection of stderr in function declarations, or changing
4089 : ;; HISTFILE in place. Therefore, OpenSolaris' /bin/sh is replaced
4090 : ;; by bash, when detected.
4091 71 : (tramp-find-shell vec)
4092 :
4093 : ;; Disable unexpected output.
4094 71 : (tramp-send-command vec "mesg n 2>/dev/null; biff n 2>/dev/null" t)
4095 :
4096 : ;; IRIX64 bash expands "!" even when in single quotes. This
4097 : ;; destroys our shell functions, we must disable it. See
4098 : ;; <http://stackoverflow.com/questions/3291692/irix-bash-shell-expands-expression-in-single-quotes-yet-shouldnt>.
4099 71 : (when (string-match "^IRIX64" uname)
4100 71 : (tramp-send-command vec "set +H" t))
4101 :
4102 : ;; Disable tab expansion.
4103 71 : (if (string-match "BSD\\|Darwin" uname)
4104 0 : (tramp-send-command vec "stty tabs" t)
4105 71 : (tramp-send-command vec "stty tab0" t))
4106 :
4107 : ;; Set utf8 encoding. Needed for macOS, for example. This is
4108 : ;; non-POSIX, so we must expect errors on some systems.
4109 71 : (tramp-send-command vec "stty iutf8 2>/dev/null" t)
4110 :
4111 : ;; Set `remote-tty' process property.
4112 71 : (let ((tty (tramp-send-command-and-read vec "echo \\\"`tty`\\\"" 'noerror)))
4113 71 : (unless (zerop (length tty))
4114 71 : (process-put proc 'remote-tty tty)))
4115 :
4116 : ;; Dump stty settings in the traces.
4117 71 : (when (>= tramp-verbose 9)
4118 71 : (tramp-send-command vec "stty -a" t))
4119 :
4120 : ;; Set the environment.
4121 71 : (tramp-message vec 5 "Setting default environment")
4122 :
4123 71 : (let (unset vars)
4124 71 : (dolist (item (reverse
4125 71 : (append `(,(tramp-get-remote-locale vec))
4126 71 : (copy-sequence tramp-remote-process-environment))))
4127 1001 : (setq item (split-string item "=" 'omit))
4128 1001 : (setcdr item (mapconcat 'identity (cdr item) "="))
4129 1001 : (if (and (stringp (cdr item)) (not (string-equal (cdr item) "")))
4130 1008 : (push (format "%s %s" (car item) (cdr item)) vars)
4131 1001 : (push (car item) unset)))
4132 71 : (when vars
4133 71 : (tramp-send-command
4134 71 : vec
4135 71 : (format
4136 : "while read var val; do export $var=\"$val\"; done <<'%s'\n%s\n%s"
4137 71 : tramp-end-of-heredoc
4138 71 : (mapconcat 'identity vars "\n")
4139 71 : tramp-end-of-heredoc)
4140 71 : t))
4141 71 : (when unset
4142 71 : (tramp-send-command
4143 71 : vec (format "unset %s" (mapconcat 'identity unset " ")) t)))))
4144 :
4145 : ;; Old text from documentation of tramp-methods:
4146 : ;; Using a uuencode/uudecode inline method is discouraged, please use one
4147 : ;; of the base64 methods instead since base64 encoding is much more
4148 : ;; reliable and the commands are more standardized between the different
4149 : ;; Unix versions. But if you can't use base64 for some reason, please
4150 : ;; note that the default uudecode command does not work well for some
4151 : ;; Unices, in particular AIX and Irix. For AIX, you might want to use
4152 : ;; the following command for uudecode:
4153 : ;;
4154 : ;; sed '/^begin/d;/^[` ]$/d;/^end/d' | iconv -f uucode -t ISO8859-1
4155 : ;;
4156 : ;; For Irix, no solution is known yet.
4157 :
4158 : (autoload 'uudecode-decode-region "uudecode")
4159 :
4160 : (defconst tramp-local-coding-commands
4161 : `((b64 base64-encode-region base64-decode-region)
4162 : (uu tramp-uuencode-region uudecode-decode-region)
4163 : (pack ,(format tramp-perl-pack "perl") ,(format tramp-perl-unpack "perl")))
4164 : "List of local coding commands for inline transfer.
4165 : Each item is a list that looks like this:
4166 :
4167 : \(FORMAT ENCODING DECODING)
4168 :
4169 : FORMAT is symbol describing the encoding/decoding format. It can be
4170 : `b64' for base64 encoding, `uu' for uu encoding, or `pack' for simple packing.
4171 :
4172 : ENCODING and DECODING can be strings, giving commands, or symbols,
4173 : giving functions. If they are strings, then they can contain
4174 : the \"%s\" format specifier. If that specifier is present, the input
4175 : file name will be put into the command line at that spot. If the
4176 : specifier is not present, the input should be read from standard
4177 : input.
4178 :
4179 : If they are functions, they will be called with two arguments, start
4180 : and end of region, and are expected to replace the region contents
4181 : with the encoded or decoded results, respectively.")
4182 :
4183 : (defconst tramp-remote-coding-commands
4184 : `((b64 "base64" "base64 -d -i")
4185 : ;; "-i" is more robust with older base64 from GNU coreutils.
4186 : ;; However, I don't know whether all base64 versions do supports
4187 : ;; this option.
4188 : (b64 "base64" "base64 -d")
4189 : (b64 "openssl enc -base64" "openssl enc -d -base64")
4190 : (b64 "mimencode -b" "mimencode -u -b")
4191 : (b64 "mmencode -b" "mmencode -u -b")
4192 : (b64 "recode data..base64" "recode base64..data")
4193 : (b64 tramp-perl-encode-with-module tramp-perl-decode-with-module)
4194 : (b64 tramp-perl-encode tramp-perl-decode)
4195 : ;; This is painful slow, so we put it on the end.
4196 : (b64 tramp-awk-encode tramp-awk-decode ,tramp-awk-coding-test)
4197 : (uu "uuencode xxx" "uudecode -o /dev/stdout" "test -c /dev/stdout")
4198 : (uu "uuencode xxx" "uudecode -o -")
4199 : (uu "uuencode xxx" "uudecode -p")
4200 : (uu "uuencode xxx" tramp-uudecode)
4201 : (pack tramp-perl-pack tramp-perl-unpack))
4202 : "List of remote coding commands for inline transfer.
4203 : Each item is a list that looks like this:
4204 :
4205 : \(FORMAT ENCODING DECODING [TEST])
4206 :
4207 : FORMAT is a symbol describing the encoding/decoding format. It can be
4208 : `b64' for base64 encoding, `uu' for uu encoding, or `pack' for simple packing.
4209 :
4210 : ENCODING and DECODING can be strings, giving commands, or symbols,
4211 : giving variables. If they are strings, then they can contain
4212 : the \"%s\" format specifier. If that specifier is present, the input
4213 : file name will be put into the command line at that spot. If the
4214 : specifier is not present, the input should be read from standard
4215 : input.
4216 :
4217 : If they are variables, this variable is a string containing a
4218 : Perl or Shell implementation for this functionality. This
4219 : program will be transferred to the remote host, and it is
4220 : available as shell function with the same name. A \"%t\" format
4221 : specifier in the variable value denotes a temporary file.
4222 :
4223 : The optional TEST command can be used for further tests, whether
4224 : ENCODING and DECODING are applicable.")
4225 :
4226 : (defun tramp-find-inline-encoding (vec)
4227 : "Find an inline transfer encoding that works.
4228 : Goes through the list `tramp-local-coding-commands' and
4229 : `tramp-remote-coding-commands'."
4230 31 : (save-excursion
4231 31 : (let ((local-commands tramp-local-coding-commands)
4232 : (magic "xyzzy")
4233 31 : (p (tramp-get-connection-process vec))
4234 : loc-enc loc-dec rem-enc rem-dec rem-test litem ritem found)
4235 62 : (while (and local-commands (not found))
4236 62 : (setq litem (pop local-commands))
4237 31 : (catch 'wont-work-local
4238 31 : (let ((format (nth 0 litem))
4239 31 : (remote-commands tramp-remote-coding-commands))
4240 31 : (setq loc-enc (nth 1 litem))
4241 31 : (setq loc-dec (nth 2 litem))
4242 : ;; If the local encoder or decoder is a string, the
4243 : ;; corresponding command has to work locally.
4244 31 : (if (not (stringp loc-enc))
4245 31 : (tramp-message
4246 31 : vec 5 "Checking local encoding function `%s'" loc-enc)
4247 0 : (tramp-message
4248 0 : vec 5 "Checking local encoding command `%s' for sanity" loc-enc)
4249 0 : (unless (zerop (tramp-call-local-coding-command
4250 0 : loc-enc nil nil))
4251 31 : (throw 'wont-work-local nil)))
4252 31 : (if (not (stringp loc-dec))
4253 31 : (tramp-message
4254 31 : vec 5 "Checking local decoding function `%s'" loc-dec)
4255 0 : (tramp-message
4256 0 : vec 5 "Checking local decoding command `%s' for sanity" loc-dec)
4257 0 : (unless (zerop (tramp-call-local-coding-command
4258 0 : loc-dec nil nil))
4259 31 : (throw 'wont-work-local nil)))
4260 : ;; Search for remote coding commands with the same format
4261 62 : (while (and remote-commands (not found))
4262 62 : (setq ritem (pop remote-commands))
4263 31 : (catch 'wont-work-remote
4264 31 : (when (equal format (nth 0 ritem))
4265 31 : (setq rem-enc (nth 1 ritem))
4266 31 : (setq rem-dec (nth 2 ritem))
4267 31 : (setq rem-test (nth 3 ritem))
4268 : ;; Check the remote test command if exists.
4269 31 : (when (stringp rem-test)
4270 0 : (tramp-message
4271 0 : vec 5 "Checking remote test command `%s'" rem-test)
4272 0 : (unless (tramp-send-command-and-check vec rem-test t)
4273 31 : (throw 'wont-work-remote nil)))
4274 : ;; Check if remote perl exists when necessary.
4275 31 : (when (and (symbolp rem-enc)
4276 0 : (string-match "perl" (symbol-name rem-enc))
4277 31 : (not (tramp-get-remote-perl vec)))
4278 31 : (throw 'wont-work-remote nil))
4279 : ;; Check if remote encoding and decoding commands can be
4280 : ;; called remotely with null input and output. This makes
4281 : ;; sure there are no syntax errors and the command is really
4282 : ;; found. Note that we do not redirect stdout to /dev/null,
4283 : ;; for two reasons: when checking the decoding command, we
4284 : ;; actually check the output it gives. And also, when
4285 : ;; redirecting "mimencode" output to /dev/null, then as root
4286 : ;; it might change the permissions of /dev/null!
4287 31 : (when (not (stringp rem-enc))
4288 0 : (let ((name (symbol-name rem-enc)))
4289 0 : (while (string-match (regexp-quote "-") name)
4290 0 : (setq name (replace-match "_" nil t name)))
4291 0 : (tramp-maybe-send-script vec (symbol-value rem-enc) name)
4292 31 : (setq rem-enc name)))
4293 31 : (tramp-message
4294 31 : vec 5
4295 31 : "Checking remote encoding command `%s' for sanity" rem-enc)
4296 31 : (unless (tramp-send-command-and-check
4297 31 : vec (format "%s </dev/null" rem-enc) t)
4298 31 : (throw 'wont-work-remote nil))
4299 :
4300 31 : (when (not (stringp rem-dec))
4301 0 : (let ((name (symbol-name rem-dec))
4302 0 : (value (symbol-value rem-dec))
4303 : tmpfile)
4304 0 : (while (string-match (regexp-quote "-") name)
4305 0 : (setq name (replace-match "_" nil t name)))
4306 0 : (when (string-match "\\(^\\|[^%]\\)%t" value)
4307 0 : (setq tmpfile
4308 0 : (make-temp-name
4309 0 : (expand-file-name
4310 0 : tramp-temp-name-prefix
4311 0 : (tramp-get-remote-tmpdir vec)))
4312 : value
4313 0 : (format-spec
4314 0 : value
4315 0 : (format-spec-make
4316 : ?t
4317 0 : (file-remote-p tmpfile 'localname)))))
4318 0 : (tramp-maybe-send-script vec value name)
4319 31 : (setq rem-dec name)))
4320 31 : (tramp-message
4321 31 : vec 5
4322 31 : "Checking remote decoding command `%s' for sanity" rem-dec)
4323 31 : (unless (tramp-send-command-and-check
4324 31 : vec
4325 31 : (format "echo %s | %s | %s" magic rem-enc rem-dec)
4326 31 : t)
4327 31 : (throw 'wont-work-remote nil))
4328 :
4329 31 : (with-current-buffer (tramp-get-buffer vec)
4330 31 : (goto-char (point-min))
4331 31 : (unless (looking-at (regexp-quote magic))
4332 31 : (throw 'wont-work-remote nil)))
4333 :
4334 : ;; `rem-enc' and `rem-dec' could be a string meanwhile.
4335 31 : (setq rem-enc (nth 1 ritem))
4336 31 : (setq rem-dec (nth 2 ritem))
4337 31 : (setq found t)))))))
4338 :
4339 31 : (when found
4340 : ;; Set connection properties. Since the commands are risky
4341 : ;; (due to output direction), we cache them in the process cache.
4342 31 : (tramp-message vec 5 "Using local encoding `%s'" loc-enc)
4343 31 : (tramp-set-connection-property p "local-encoding" loc-enc)
4344 31 : (tramp-message vec 5 "Using local decoding `%s'" loc-dec)
4345 31 : (tramp-set-connection-property p "local-decoding" loc-dec)
4346 31 : (tramp-message vec 5 "Using remote encoding `%s'" rem-enc)
4347 31 : (tramp-set-connection-property p "remote-encoding" rem-enc)
4348 31 : (tramp-message vec 5 "Using remote decoding `%s'" rem-dec)
4349 31 : (tramp-set-connection-property p "remote-decoding" rem-dec)))))
4350 :
4351 : (defun tramp-call-local-coding-command (cmd input output)
4352 : "Call the local encoding or decoding command.
4353 : If CMD contains \"%s\", provide input file INPUT there in command.
4354 : Otherwise, INPUT is passed via standard input.
4355 : INPUT can also be nil which means `/dev/null'.
4356 : OUTPUT can be a string (which specifies a file name), or t (which
4357 : means standard output and thus the current buffer), or nil (which
4358 : means discard it)."
4359 1 : (tramp-call-process
4360 1 : nil tramp-encoding-shell
4361 1 : (when (and input (not (string-match "%s" cmd))) input)
4362 1 : (if (eq output t) t nil)
4363 : nil
4364 1 : tramp-encoding-command-switch
4365 1 : (concat
4366 1 : (if (string-match "%s" cmd) (format cmd input) cmd)
4367 1 : (if (stringp output) (concat " >" output) ""))))
4368 :
4369 : (defconst tramp-inline-compress-commands
4370 : '(("gzip" "gzip -d")
4371 : ("bzip2" "bzip2 -d")
4372 : ("xz" "xz -d")
4373 : ("compress" "compress -d"))
4374 : "List of compress and decompress commands for inline transfer.
4375 : Each item is a list that looks like this:
4376 :
4377 : \(COMPRESS DECOMPRESS)
4378 :
4379 : COMPRESS or DECOMPRESS are strings with the respective commands.")
4380 :
4381 : (defun tramp-find-inline-compress (vec)
4382 : "Find an inline transfer compress command that works.
4383 : Goes through the list `tramp-inline-compress-commands'."
4384 1 : (save-excursion
4385 1 : (let ((commands tramp-inline-compress-commands)
4386 : (magic "xyzzy")
4387 1 : (p (tramp-get-connection-process vec))
4388 : item compress decompress found)
4389 2 : (while (and commands (not found))
4390 1 : (catch 'next
4391 2 : (setq item (pop commands)
4392 1 : compress (nth 0 item)
4393 1 : decompress (nth 1 item))
4394 1 : (tramp-message
4395 1 : vec 5
4396 : "Checking local compress commands `%s', `%s' for sanity"
4397 1 : compress decompress)
4398 1 : (unless
4399 1 : (zerop
4400 1 : (tramp-call-local-coding-command
4401 1 : (format
4402 : ;; Windows shells need the program file name after
4403 : ;; the pipe symbol be quoted if they use forward
4404 : ;; slashes as directory separators.
4405 1 : (if (memq system-type '(windows-nt))
4406 : "echo %s | \"%s\" | \"%s\""
4407 1 : "echo %s | %s | %s")
4408 1 : magic compress decompress)
4409 1 : nil nil))
4410 1 : (throw 'next nil))
4411 1 : (tramp-message
4412 1 : vec 5
4413 : "Checking remote compress commands `%s', `%s' for sanity"
4414 1 : compress decompress)
4415 1 : (unless (tramp-send-command-and-check
4416 1 : vec (format "echo %s | %s | %s" magic compress decompress) t)
4417 1 : (throw 'next nil))
4418 1 : (setq found t)))
4419 :
4420 : ;; Did we find something?
4421 1 : (if found
4422 1 : (progn
4423 : ;; Set connection properties. Since the commands are
4424 : ;; risky (due to output direction), we cache them in the
4425 : ;; process cache.
4426 1 : (tramp-message
4427 1 : vec 5 "Using inline transfer compress command `%s'" compress)
4428 1 : (tramp-set-connection-property p "inline-compress" compress)
4429 1 : (tramp-message
4430 1 : vec 5 "Using inline transfer decompress command `%s'" decompress)
4431 1 : (tramp-set-connection-property p "inline-decompress" decompress))
4432 :
4433 0 : (tramp-set-connection-property p "inline-compress" nil)
4434 0 : (tramp-set-connection-property p "inline-decompress" nil)
4435 0 : (tramp-message
4436 1 : vec 2 "Couldn't find an inline transfer compress command")))))
4437 :
4438 : (defun tramp-compute-multi-hops (vec)
4439 : "Expands VEC according to `tramp-default-proxies-alist'."
4440 71 : (let ((target-alist `(,vec))
4441 71 : (hops (or (tramp-file-name-hop vec) ""))
4442 71 : (item vec)
4443 : choices proxy)
4444 :
4445 : ;; Ad-hoc proxy definitions.
4446 71 : (dolist (proxy (reverse (split-string hops tramp-postfix-hop-regexp 'omit)))
4447 0 : (let ((user (tramp-file-name-user item))
4448 0 : (host (tramp-file-name-host item))
4449 0 : (proxy (concat
4450 0 : (tramp-prefix-format) proxy (tramp-postfix-host-format))))
4451 0 : (tramp-message
4452 0 : vec 5 "Add proxy (\"%s\" \"%s\" \"%s\")"
4453 0 : (and (stringp host) (regexp-quote host))
4454 0 : (and (stringp user) (regexp-quote user))
4455 0 : proxy)
4456 : ;; Add the hop.
4457 0 : (add-to-list
4458 : 'tramp-default-proxies-alist
4459 0 : (list (and (stringp host) (regexp-quote host))
4460 0 : (and (stringp user) (regexp-quote user))
4461 0 : proxy))
4462 71 : (setq item (tramp-dissect-file-name proxy))))
4463 : ;; Save the new value.
4464 71 : (when (and hops tramp-save-ad-hoc-proxies)
4465 0 : (customize-save-variable
4466 71 : 'tramp-default-proxies-alist tramp-default-proxies-alist))
4467 :
4468 : ;; Look for proxy hosts to be passed.
4469 71 : (setq choices tramp-default-proxies-alist)
4470 71 : (while choices
4471 0 : (setq item (pop choices)
4472 0 : proxy (eval (nth 2 item)))
4473 0 : (when (and
4474 : ;; Host.
4475 0 : (string-match (or (eval (nth 0 item)) "")
4476 0 : (or (tramp-file-name-host (car target-alist)) ""))
4477 : ;; User.
4478 0 : (string-match (or (eval (nth 1 item)) "")
4479 0 : (or (tramp-file-name-user (car target-alist)) "")))
4480 0 : (if (null proxy)
4481 : ;; No more hops needed.
4482 0 : (setq choices nil)
4483 : ;; Replace placeholders.
4484 0 : (setq proxy
4485 0 : (format-spec
4486 0 : proxy
4487 0 : (format-spec-make
4488 0 : ?u (or (tramp-file-name-user (car target-alist)) "")
4489 0 : ?h (or (tramp-file-name-host (car target-alist)) ""))))
4490 0 : (with-parsed-tramp-file-name proxy l
4491 : ;; Add the hop.
4492 0 : (push l target-alist)
4493 : ;; Start next search.
4494 71 : (setq choices tramp-default-proxies-alist)))))
4495 :
4496 : ;; Foreign and out-of-band methods are not supported for multi-hops.
4497 71 : (when (cdr target-alist)
4498 0 : (setq choices target-alist)
4499 0 : (while (setq item (pop choices))
4500 0 : (when (or (not (tramp-get-method-parameter item 'tramp-login-program))
4501 0 : (tramp-get-method-parameter item 'tramp-copy-program))
4502 0 : (tramp-error
4503 0 : vec 'file-error
4504 : "Method `%s' is not supported for multi-hops."
4505 71 : (tramp-file-name-method item)))))
4506 :
4507 : ;; In case the host name is not used for the remote shell
4508 : ;; command, the user could be misguided by applying a random
4509 : ;; host name.
4510 71 : (let* ((v (car target-alist))
4511 71 : (method (tramp-file-name-method v))
4512 71 : (host (tramp-file-name-host v)))
4513 71 : (unless
4514 71 : (or
4515 : ;; There are multi-hops.
4516 71 : (cdr target-alist)
4517 : ;; The host name is used for the remote shell command.
4518 71 : (member '("%h") (tramp-get-method-parameter v 'tramp-login-args))
4519 : ;; The host is local. We cannot use `tramp-local-host-p'
4520 : ;; here, because it opens a connection as well.
4521 71 : (string-match tramp-local-host-regexp host))
4522 0 : (tramp-error
4523 0 : v 'file-error
4524 : "Host `%s' looks like a remote host, `%s' can only use the local host"
4525 71 : host method)))
4526 :
4527 : ;; Result.
4528 71 : target-alist))
4529 :
4530 : (defun tramp-ssh-controlmaster-options (vec)
4531 : "Return the Control* arguments of the local ssh."
4532 71 : (cond
4533 : ;; No options to be computed.
4534 71 : ((or (null tramp-use-ssh-controlmaster-options)
4535 71 : (null (assoc "%c" (tramp-get-method-parameter vec 'tramp-login-args))))
4536 : "")
4537 :
4538 : ;; There is already a value to be used.
4539 0 : ((stringp tramp-ssh-controlmaster-options) tramp-ssh-controlmaster-options)
4540 :
4541 : ;; Determine the options.
4542 0 : (t (setq tramp-ssh-controlmaster-options "")
4543 0 : (let ((case-fold-search t))
4544 0 : (ignore-errors
4545 0 : (when (executable-find "ssh")
4546 0 : (with-tramp-progress-reporter
4547 0 : vec 4 "Computing ControlMaster options"
4548 0 : (with-temp-buffer
4549 0 : (tramp-call-process vec "ssh" nil t nil "-o" "ControlMaster")
4550 0 : (goto-char (point-min))
4551 0 : (when (search-forward-regexp "missing.+argument" nil t)
4552 0 : (setq tramp-ssh-controlmaster-options
4553 0 : "-o ControlMaster=auto")))
4554 0 : (unless (zerop (length tramp-ssh-controlmaster-options))
4555 0 : (with-temp-buffer
4556 : ;; We use a non-existing IP address, in order to
4557 : ;; avoid useless connections, and DNS timeouts.
4558 : ;; Setting ConnectTimeout is needed since OpenSSH 7.
4559 0 : (tramp-call-process
4560 0 : vec "ssh" nil t nil
4561 0 : "-o" "ConnectTimeout=1" "-o" "ControlPath=%C" "0.0.0.1")
4562 0 : (goto-char (point-min))
4563 0 : (setq tramp-ssh-controlmaster-options
4564 0 : (concat tramp-ssh-controlmaster-options
4565 0 : (if (search-forward-regexp "unknown.+key" nil t)
4566 : " -o ControlPath='tramp.%%r@%%h:%%p'"
4567 0 : " -o ControlPath='tramp.%%C'"))))
4568 0 : (with-temp-buffer
4569 0 : (tramp-call-process vec "ssh" nil t nil "-o" "ControlPersist")
4570 0 : (goto-char (point-min))
4571 0 : (when (search-forward-regexp "missing.+argument" nil t)
4572 0 : (setq tramp-ssh-controlmaster-options
4573 0 : (concat tramp-ssh-controlmaster-options
4574 0 : " -o ControlPersist=no")))))))))
4575 71 : tramp-ssh-controlmaster-options)))
4576 :
4577 : (defun tramp-maybe-open-connection (vec)
4578 : "Maybe open a connection VEC.
4579 : Does not do anything if a connection is already open, but re-opens the
4580 : connection if a previous connection has died for some reason."
4581 10661 : (let ((p (tramp-get-connection-process vec))
4582 10661 : (process-name (tramp-get-connection-property vec "process-name" nil))
4583 10661 : (process-environment (copy-sequence process-environment))
4584 10661 : (pos (with-current-buffer (tramp-get-connection-buffer vec) (point))))
4585 :
4586 : ;; If Tramp opens the same connection within a short time frame,
4587 : ;; there is a problem. We shall signal this.
4588 10661 : (unless (or (process-live-p p)
4589 71 : (not (tramp-file-name-equal-p
4590 71 : vec (car tramp-current-connection)))
4591 0 : (> (tramp-time-diff
4592 0 : (current-time) (cdr tramp-current-connection))
4593 10661 : (or tramp-connection-min-time-diff 0)))
4594 10661 : (throw 'suppress 'suppress))
4595 :
4596 : ;; If too much time has passed since last command was sent, look
4597 : ;; whether process is still alive. If it isn't, kill it. When
4598 : ;; using ssh, it can sometimes happen that the remote end has hung
4599 : ;; up but the local ssh client doesn't recognize this until it
4600 : ;; tries to send some data to the remote end. So that's why we
4601 : ;; try to send a command from time to time, then look again
4602 : ;; whether the process is really alive.
4603 10661 : (condition-case nil
4604 10661 : (when (and (> (tramp-time-diff
4605 10661 : (current-time)
4606 10661 : (tramp-get-connection-property
4607 10661 : p "last-cmd-time" '(0 0 0)))
4608 10661 : 60)
4609 10661 : (process-live-p p))
4610 0 : (tramp-send-command vec "echo are you awake" t t)
4611 0 : (unless (and (process-live-p p)
4612 0 : (tramp-wait-for-output p 10))
4613 : ;; The error will be caught locally.
4614 10661 : (tramp-error vec 'file-error "Awake did fail")))
4615 : (file-error
4616 0 : (tramp-cleanup-connection vec t)
4617 10661 : (setq p nil)))
4618 :
4619 : ;; New connection must be opened.
4620 10661 : (condition-case err
4621 10661 : (unless (process-live-p p)
4622 :
4623 : ;; During completion, don't reopen a new connection. We
4624 : ;; check this for the process related to
4625 : ;; `tramp-buffer-name'; otherwise `start-file-process'
4626 : ;; wouldn't run ever when `non-essential' is non-nil.
4627 71 : (when (and (tramp-completion-mode-p)
4628 71 : (null (get-process (tramp-buffer-name vec))))
4629 71 : (throw 'non-essential 'non-essential))
4630 :
4631 71 : (with-tramp-progress-reporter
4632 142 : vec 3
4633 142 : (if (zerop (length (tramp-file-name-user vec)))
4634 142 : (format "Opening connection for %s using %s"
4635 142 : (tramp-file-name-host vec)
4636 142 : (tramp-file-name-method vec))
4637 0 : (format "Opening connection for %s@%s using %s"
4638 0 : (tramp-file-name-user vec)
4639 0 : (tramp-file-name-host vec)
4640 142 : (tramp-file-name-method vec)))
4641 :
4642 71 : (catch 'uname-changed
4643 : ;; Start new process.
4644 71 : (when (and p (processp p))
4645 71 : (delete-process p))
4646 71 : (setenv "TERM" tramp-terminal-type)
4647 71 : (setenv "LC_ALL" (tramp-get-local-locale vec))
4648 71 : (if (stringp tramp-histfile-override)
4649 71 : (setenv "HISTFILE" tramp-histfile-override)
4650 0 : (if tramp-histfile-override
4651 0 : (progn
4652 0 : (setenv "HISTFILE")
4653 0 : (setenv "HISTFILESIZE" "0")
4654 71 : (setenv "HISTSIZE" "0"))))
4655 71 : (setenv "PROMPT_COMMAND")
4656 71 : (setenv "PS1" tramp-initial-end-of-output)
4657 71 : (unless (stringp tramp-encoding-shell)
4658 71 : (tramp-error vec 'file-error "`tramp-encoding-shell' not set"))
4659 71 : (let* ((target-alist (tramp-compute-multi-hops vec))
4660 : ;; We will apply `tramp-ssh-controlmaster-options'
4661 : ;; only for the first hop.
4662 71 : (options (tramp-ssh-controlmaster-options vec))
4663 71 : (process-connection-type tramp-process-connection-type)
4664 : (process-adaptive-read-buffering nil)
4665 : ;; There are unfortunate settings for "cmdproxy" on
4666 : ;; W32 systems.
4667 : (process-coding-system-alist nil)
4668 : (coding-system-for-read nil)
4669 : ;; This must be done in order to avoid our file
4670 : ;; name handler.
4671 71 : (p (let ((default-directory
4672 71 : (tramp-compat-temporary-file-directory)))
4673 71 : (apply
4674 : 'start-process
4675 71 : (tramp-get-connection-name vec)
4676 71 : (tramp-get-connection-buffer vec)
4677 71 : (if tramp-encoding-command-interactive
4678 71 : (list tramp-encoding-shell
4679 71 : tramp-encoding-command-interactive)
4680 71 : (list tramp-encoding-shell))))))
4681 :
4682 : ;; Set sentinel and query flag.
4683 71 : (tramp-set-connection-property p "vector" vec)
4684 71 : (set-process-sentinel p 'tramp-process-sentinel)
4685 71 : (process-put p 'adjust-window-size-function 'ignore)
4686 71 : (set-process-query-on-exit-flag p nil)
4687 71 : (setq tramp-current-connection (cons vec (current-time))
4688 71 : tramp-current-host (system-name))
4689 :
4690 71 : (tramp-message
4691 71 : vec 6 "%s" (mapconcat 'identity (process-command p) " "))
4692 :
4693 : ;; Check whether process is alive.
4694 71 : (tramp-barf-if-no-shell-prompt
4695 71 : p 10
4696 71 : "Couldn't find local shell prompt for %s" tramp-encoding-shell)
4697 :
4698 : ;; Now do all the connections as specified.
4699 142 : (while target-alist
4700 71 : (let* ((hop (car target-alist))
4701 71 : (l-method (tramp-file-name-method hop))
4702 71 : (l-user (tramp-file-name-user hop))
4703 71 : (l-domain (tramp-file-name-domain hop))
4704 71 : (l-host (tramp-file-name-host hop))
4705 71 : (l-port (tramp-file-name-port hop))
4706 : (login-program
4707 71 : (tramp-get-method-parameter hop 'tramp-login-program))
4708 : (login-args
4709 71 : (tramp-get-method-parameter hop 'tramp-login-args))
4710 : (login-env
4711 71 : (tramp-get-method-parameter hop 'tramp-login-env))
4712 : (async-args
4713 71 : (tramp-get-method-parameter hop 'tramp-async-args))
4714 : (connection-timeout
4715 71 : (tramp-get-method-parameter
4716 71 : hop 'tramp-connection-timeout))
4717 71 : (command login-program)
4718 : ;; We don't create the temporary file. In
4719 : ;; fact, it is just a prefix for the
4720 : ;; ControlPath option of ssh; the real
4721 : ;; temporary file has another name, and it is
4722 : ;; created and protected by ssh. It is also
4723 : ;; removed by ssh when the connection is
4724 : ;; closed. The temporary file name is cached
4725 : ;; in the main connection process, therefore
4726 : ;; we cannot use `tramp-get-connection-process'.
4727 : (tmpfile
4728 71 : (with-tramp-connection-property
4729 116 : (get-process (tramp-buffer-name vec)) "temp-file"
4730 45 : (make-temp-name
4731 45 : (expand-file-name
4732 45 : tramp-temp-name-prefix
4733 71 : (tramp-compat-temporary-file-directory)))))
4734 : spec r-shell)
4735 :
4736 : ;; Add arguments for asynchronous processes.
4737 71 : (when (and process-name async-args)
4738 71 : (setq login-args (append async-args login-args)))
4739 :
4740 : ;; Check, whether there is a restricted shell.
4741 71 : (dolist (elt tramp-restricted-shell-hosts-alist)
4742 0 : (when (string-match elt tramp-current-host)
4743 71 : (setq r-shell t)))
4744 :
4745 : ;; Set variables for computing the prompt for
4746 : ;; reading password.
4747 71 : (setq tramp-current-method l-method
4748 71 : tramp-current-user l-user
4749 71 : tramp-current-domain l-domain
4750 71 : tramp-current-host l-host
4751 71 : tramp-current-port l-port)
4752 :
4753 : ;; Add login environment.
4754 71 : (when login-env
4755 0 : (setq
4756 : login-env
4757 0 : (mapcar
4758 : (lambda (x)
4759 0 : (setq x (mapcar (lambda (y) (format-spec y spec)) x))
4760 0 : (unless (member "" x) (mapconcat 'identity x " ")))
4761 0 : login-env))
4762 0 : (while login-env
4763 0 : (setq command
4764 0 : (format
4765 : "%s=%s %s"
4766 0 : (pop login-env)
4767 0 : (tramp-shell-quote-argument (pop login-env))
4768 0 : command)))
4769 71 : (setq command (concat "env " command)))
4770 :
4771 : ;; Replace `login-args' place holders.
4772 71 : (setq
4773 71 : l-host (or l-host "")
4774 71 : l-user (or l-user "")
4775 71 : l-port (or l-port "")
4776 71 : spec (format-spec-make ?t tmpfile)
4777 71 : options (format-spec options spec)
4778 71 : spec (format-spec-make
4779 71 : ?h l-host ?u l-user ?p l-port ?c options)
4780 : command
4781 71 : (concat
4782 : ;; We do not want to see the trailing local
4783 : ;; prompt in `start-file-process'.
4784 71 : (unless r-shell "exec ")
4785 71 : command " "
4786 71 : (mapconcat
4787 : (lambda (x)
4788 142 : (setq x (mapcar (lambda (y) (format-spec y spec)) x))
4789 71 : (unless (member "" x) (mapconcat 'identity x " ")))
4790 71 : login-args " ")
4791 : ;; Local shell could be a Windows COMSPEC. It
4792 : ;; doesn't know the ";" syntax, but we must exit
4793 : ;; always for `start-file-process'. It could
4794 : ;; also be a restricted shell, which does not
4795 : ;; allow "exec".
4796 71 : (when r-shell " && exit || exit")))
4797 :
4798 : ;; Send the command.
4799 71 : (tramp-message vec 3 "Sending command `%s'" command)
4800 71 : (tramp-send-command vec command t t)
4801 71 : (tramp-process-actions
4802 71 : p vec
4803 71 : (min
4804 71 : pos (with-current-buffer (process-buffer p) (point-max)))
4805 71 : tramp-actions-before-shell
4806 71 : (or connection-timeout tramp-connection-timeout))
4807 71 : (tramp-message
4808 71 : vec 3 "Found remote shell prompt on `%s'" l-host))
4809 : ;; Next hop.
4810 71 : (setq options ""
4811 71 : target-alist (cdr target-alist)))
4812 :
4813 : ;; Set connection-local variables.
4814 71 : (tramp-set-connection-local-variables vec)
4815 :
4816 : ;; Make initial shell settings.
4817 71 : (tramp-open-connection-setup-interactive-shell p vec)
4818 :
4819 : ;; Mark it as connected.
4820 10661 : (tramp-set-connection-property p "connected" t)))))
4821 :
4822 : ;; Cleanup, and propagate the signal.
4823 : ((error quit)
4824 0 : (tramp-cleanup-connection vec t)
4825 10661 : (signal (car err) (cdr err))))))
4826 :
4827 : (defun tramp-send-command (vec command &optional neveropen nooutput)
4828 : "Send the COMMAND to connection VEC.
4829 : Erases temporary buffer before sending the command. If optional
4830 : arg NEVEROPEN is non-nil, never try to open the connection. This
4831 : is meant to be used from `tramp-maybe-open-connection' only. The
4832 : function waits for output unless NOOUTPUT is set."
4833 11530 : (unless neveropen (tramp-maybe-open-connection vec))
4834 11530 : (let ((p (tramp-get-connection-process vec)))
4835 11530 : (when (tramp-get-connection-property p "remote-echo" nil)
4836 : ;; We mark the command string that it can be erased in the output buffer.
4837 0 : (tramp-set-connection-property p "check-remote-echo" t)
4838 : ;; If we put `tramp-echo-mark' after a trailing newline (which
4839 : ;; is assumed to be unquoted) `tramp-send-string' doesn't see
4840 : ;; that newline and adds `tramp-rsh-end-of-line' right after
4841 : ;; `tramp-echo-mark', so the remote shell sees two consecutive
4842 : ;; trailing line endings and sends two prompts after executing
4843 : ;; the command, which confuses `tramp-wait-for-output'.
4844 0 : (when (and (not (string= command ""))
4845 0 : (string-equal (substring command -1) "\n"))
4846 0 : (setq command (substring command 0 -1)))
4847 : ;; No need to restore a trailing newline here since `tramp-send-string'
4848 : ;; makes sure that the string ends in `tramp-rsh-end-of-line', anyway.
4849 11530 : (setq command (format "%s%s%s" tramp-echo-mark command tramp-echo-mark)))
4850 : ;; Send the command.
4851 11530 : (tramp-message vec 6 "%s" command)
4852 11530 : (tramp-send-string vec command)
4853 11530 : (unless nooutput (tramp-wait-for-output p))))
4854 :
4855 : (defun tramp-wait-for-output (proc &optional timeout)
4856 : "Wait for output from remote command."
4857 11432 : (unless (buffer-live-p (process-buffer proc))
4858 0 : (delete-process proc)
4859 11432 : (tramp-error proc 'file-error "Process `%s' not available, try again" proc))
4860 11432 : (with-current-buffer (process-buffer proc)
4861 11432 : (let* (;; Initially, `tramp-end-of-output' is "#$ ". There might
4862 : ;; be leading escape sequences, which must be ignored.
4863 : ;; Busyboxes built with the EDITING_ASK_TERMINAL config
4864 : ;; option send also escape sequences, which must be
4865 : ;; ignored.
4866 11432 : (regexp (format "[^#$\n]*%s\\(%s\\)?\r?$"
4867 11432 : (regexp-quote tramp-end-of-output)
4868 11432 : tramp-device-escape-sequence-regexp))
4869 : ;; Sometimes, the commands do not return a newline but a
4870 : ;; null byte before the shell prompt, for example "git
4871 : ;; ls-files -c -z ...".
4872 11432 : (regexp1 (format "\\(^\\|\000\\)%s" regexp))
4873 11432 : (found (tramp-wait-for-regexp proc timeout regexp1)))
4874 11432 : (if found
4875 11432 : (let (buffer-read-only)
4876 : ;; A simple-minded busybox has sent " ^H" sequences.
4877 : ;; Delete them.
4878 11432 : (goto-char (point-min))
4879 11432 : (when (re-search-forward "^\\(.\b\\)+$" (point-at-eol) t)
4880 0 : (forward-line 1)
4881 11432 : (delete-region (point-min) (point)))
4882 : ;; Delete the prompt.
4883 11432 : (goto-char (point-max))
4884 11432 : (re-search-backward regexp nil t)
4885 11432 : (delete-region (point) (point-max)))
4886 0 : (if timeout
4887 0 : (tramp-error
4888 0 : proc 'file-error
4889 : "[[Remote prompt `%s' not found in %d secs]]"
4890 0 : tramp-end-of-output timeout)
4891 0 : (tramp-error
4892 0 : proc 'file-error
4893 11432 : "[[Remote prompt `%s' not found]]" tramp-end-of-output)))
4894 : ;; Return value is whether end-of-output sentinel was found.
4895 11432 : found)))
4896 :
4897 : (defun tramp-send-command-and-check
4898 : (vec command &optional subshell dont-suppress-err)
4899 : "Run COMMAND and check its exit status.
4900 : Sends `echo $?' along with the COMMAND for checking the exit status.
4901 : If COMMAND is nil, just sends `echo $?'. Returns t if the exit
4902 : status is 0, and nil otherwise.
4903 :
4904 : If the optional argument SUBSHELL is non-nil, the command is
4905 : executed in a subshell, ie surrounded by parentheses. If
4906 : DONT-SUPPRESS-ERR is non-nil, stderr won't be sent to /dev/null."
4907 9101 : (tramp-send-command
4908 9101 : vec
4909 9101 : (concat (if subshell "( " "")
4910 9101 : command
4911 9101 : (if command (if dont-suppress-err "; " " 2>/dev/null; ") "")
4912 : "echo tramp_exit_status $?"
4913 9101 : (if subshell " )" "")))
4914 9101 : (with-current-buffer (tramp-get-connection-buffer vec)
4915 9101 : (goto-char (point-max))
4916 9101 : (unless (re-search-backward "tramp_exit_status [0-9]+" nil t)
4917 0 : (tramp-error
4918 9101 : vec 'file-error "Couldn't find exit status of `%s'" command))
4919 9101 : (skip-chars-forward "^ ")
4920 9101 : (prog1
4921 9101 : (zerop (read (current-buffer)))
4922 9101 : (let (buffer-read-only)
4923 9101 : (delete-region (match-beginning 0) (point-max))))))
4924 :
4925 : (defun tramp-barf-unless-okay (vec command fmt &rest args)
4926 : "Run COMMAND, check exit status, throw error if exit status not okay.
4927 : Similar to `tramp-send-command-and-check' but accepts two more arguments
4928 : FMT and ARGS which are passed to `error'."
4929 2982 : (or (tramp-send-command-and-check vec command)
4930 2976 : (apply 'tramp-error vec 'file-error fmt args)))
4931 :
4932 : (defun tramp-send-command-and-read (vec command &optional noerror marker)
4933 : "Run COMMAND and return the output, which must be a Lisp expression.
4934 : If MARKER is a regexp, read the output after that string.
4935 : In case there is no valid Lisp expression and NOERROR is nil, it
4936 : raises an error."
4937 1641 : (when (if noerror
4938 166 : (tramp-send-command-and-check vec command)
4939 1475 : (tramp-barf-unless-okay
4940 1641 : vec command "`%s' returns with error" command))
4941 1641 : (with-current-buffer (tramp-get-connection-buffer vec)
4942 1641 : (goto-char (point-min))
4943 : ;; Read the marker.
4944 1641 : (when (stringp marker)
4945 0 : (condition-case nil
4946 0 : (re-search-forward marker)
4947 0 : (error (unless noerror
4948 0 : (tramp-error
4949 0 : vec 'file-error
4950 : "`%s' does not return the marker `%s': `%s'"
4951 1641 : command marker (buffer-string))))))
4952 : ;; Read the expression.
4953 1641 : (condition-case nil
4954 1641 : (prog1 (read (current-buffer))
4955 : ;; Error handling.
4956 1641 : (when (re-search-forward "\\S-" (point-at-eol) t)
4957 1641 : (error nil)))
4958 0 : (error (unless noerror
4959 0 : (tramp-error
4960 0 : vec 'file-error
4961 : "`%s' does not return a valid Lisp expression: `%s'"
4962 1641 : command (buffer-string))))))))
4963 :
4964 : (defun tramp-convert-file-attributes (vec attr)
4965 : "Convert `file-attributes' ATTR generated by perl script, stat or ls.
4966 : Convert file mode bits to string and set virtual device number.
4967 : Return ATTR."
4968 1795 : (when attr
4969 : ;; Remove color escape sequences from symlink.
4970 1373 : (when (stringp (car attr))
4971 95 : (while (string-match tramp-display-escape-sequence-regexp (car attr))
4972 1373 : (setcar attr (replace-match "" nil nil (car attr)))))
4973 : ;; Convert uid and gid. Use `tramp-unknown-id-integer' as
4974 : ;; indication of unusable value.
4975 1373 : (when (and (numberp (nth 2 attr)) (< (nth 2 attr) 0))
4976 1373 : (setcar (nthcdr 2 attr) tramp-unknown-id-integer))
4977 1373 : (when (and (floatp (nth 2 attr))
4978 1373 : (<= (nth 2 attr) most-positive-fixnum))
4979 1373 : (setcar (nthcdr 2 attr) (round (nth 2 attr))))
4980 1373 : (when (and (numberp (nth 3 attr)) (< (nth 3 attr) 0))
4981 1373 : (setcar (nthcdr 3 attr) tramp-unknown-id-integer))
4982 1373 : (when (and (floatp (nth 3 attr))
4983 1373 : (<= (nth 3 attr) most-positive-fixnum))
4984 1373 : (setcar (nthcdr 3 attr) (round (nth 3 attr))))
4985 : ;; Convert last access time.
4986 1373 : (unless (listp (nth 4 attr))
4987 841 : (setcar (nthcdr 4 attr)
4988 841 : (list (floor (nth 4 attr) 65536)
4989 1373 : (floor (mod (nth 4 attr) 65536)))))
4990 : ;; Convert last modification time.
4991 1373 : (unless (listp (nth 5 attr))
4992 841 : (setcar (nthcdr 5 attr)
4993 841 : (list (floor (nth 5 attr) 65536)
4994 1373 : (floor (mod (nth 5 attr) 65536)))))
4995 : ;; Convert last status change time.
4996 1373 : (unless (listp (nth 6 attr))
4997 841 : (setcar (nthcdr 6 attr)
4998 841 : (list (floor (nth 6 attr) 65536)
4999 1373 : (floor (mod (nth 6 attr) 65536)))))
5000 : ;; Convert file size.
5001 1373 : (when (< (nth 7 attr) 0)
5002 1373 : (setcar (nthcdr 7 attr) -1))
5003 1373 : (when (and (floatp (nth 7 attr))
5004 1373 : (<= (nth 7 attr) most-positive-fixnum))
5005 1373 : (setcar (nthcdr 7 attr) (round (nth 7 attr))))
5006 : ;; Convert file mode bits to string.
5007 1373 : (unless (stringp (nth 8 attr))
5008 237 : (setcar (nthcdr 8 attr) (tramp-file-mode-from-int (nth 8 attr)))
5009 237 : (when (stringp (car attr))
5010 1373 : (aset (nth 8 attr) 0 ?l)))
5011 : ;; Convert directory indication bit.
5012 1373 : (when (string-match "^d" (nth 8 attr))
5013 1373 : (setcar attr t))
5014 : ;; Convert symlink from `tramp-do-file-attributes-with-stat'.
5015 1373 : (when (consp (car attr))
5016 439 : (if (and (stringp (caar attr))
5017 439 : (string-match ".+ -> .\\(.+\\)." (caar attr)))
5018 123 : (setcar attr (match-string 1 (caar attr)))
5019 1373 : (setcar attr nil)))
5020 : ;; Set file's gid change bit.
5021 1373 : (setcar (nthcdr 9 attr)
5022 1373 : (if (numberp (nth 3 attr))
5023 1232 : (not (= (nth 3 attr)
5024 1232 : (tramp-get-remote-gid vec 'integer)))
5025 141 : (not (string-equal
5026 141 : (nth 3 attr)
5027 1373 : (tramp-get-remote-gid vec 'string)))))
5028 : ;; Convert inode.
5029 1373 : (unless (listp (nth 10 attr))
5030 1136 : (setcar (nthcdr 10 attr)
5031 1136 : (condition-case nil
5032 1136 : (let ((high (nth 10 attr))
5033 : middle low)
5034 1136 : (if (<= high most-positive-fixnum)
5035 1136 : (floor high)
5036 : ;; The low 16 bits.
5037 0 : (setq low (mod high #x10000)
5038 0 : high (/ high #x10000))
5039 0 : (if (<= high most-positive-fixnum)
5040 0 : (cons (floor high) (floor low))
5041 : ;; The middle 24 bits.
5042 0 : (setq middle (mod high #x1000000)
5043 0 : high (/ high #x1000000))
5044 1136 : (cons (floor high) (cons (floor middle) (floor low))))))
5045 : ;; Inodes can be incredible huge. We must hide this.
5046 1373 : (error (tramp-get-inode vec)))))
5047 : ;; Set virtual device number.
5048 1373 : (setcar (nthcdr 11 attr)
5049 1373 : (tramp-get-device vec))
5050 1795 : attr))
5051 :
5052 : (defun tramp-shell-case-fold (string)
5053 : "Converts STRING to shell glob pattern which ignores case."
5054 0 : (mapconcat
5055 : (lambda (c)
5056 0 : (if (equal (downcase c) (upcase c))
5057 0 : (vector c)
5058 0 : (format "[%c%c]" (downcase c) (upcase c))))
5059 0 : string
5060 0 : ""))
5061 :
5062 : (defun tramp-make-copy-program-file-name (vec)
5063 : "Create a file name suitable for `scp', `pscp', or `nc' and workalikes."
5064 0 : (let ((method (tramp-file-name-method vec))
5065 0 : (user (tramp-file-name-user vec))
5066 0 : (host (tramp-file-name-host vec))
5067 : (localname
5068 0 : (directory-file-name (tramp-file-name-unquote-localname vec))))
5069 0 : (when (string-match tramp-ipv6-regexp host)
5070 0 : (setq host (format "[%s]" host)))
5071 0 : (unless (string-match "ftp$" method)
5072 0 : (setq localname (tramp-shell-quote-argument localname)))
5073 0 : (cond
5074 0 : ((tramp-get-method-parameter vec 'tramp-remote-copy-program)
5075 0 : localname)
5076 0 : ((not (zerop (length user)))
5077 0 : (format "%s@%s:%s" user host (shell-quote-argument localname)))
5078 0 : (t (format "%s:%s" host (shell-quote-argument localname))))))
5079 :
5080 : (defun tramp-method-out-of-band-p (vec size)
5081 : "Return t if this is an out-of-band method, nil otherwise."
5082 843 : (and
5083 : ;; It shall be an out-of-band method.
5084 843 : (tramp-get-method-parameter vec 'tramp-copy-program)
5085 : ;; There must be a size, otherwise the file doesn't exist.
5086 0 : (numberp size)
5087 : ;; Either the file size is large enough, or (in rare cases) there
5088 : ;; does not exist a remote encoding.
5089 0 : (or (null tramp-copy-size-limit)
5090 0 : (> size tramp-copy-size-limit)
5091 843 : (null (tramp-get-inline-coding vec "remote-encoding" size)))))
5092 :
5093 : ;; Variables local to connection.
5094 :
5095 : (defun tramp-get-remote-path (vec)
5096 : "Compile list of remote directories for $PATH.
5097 : Nonexistent directories are removed from spec."
5098 207 : (with-tramp-connection-property
5099 : ;; When `tramp-own-remote-path' is in `tramp-remote-path', we
5100 : ;; cache the result for the session only. Otherwise, the result
5101 : ;; is cached persistently.
5102 270 : (if (memq 'tramp-own-remote-path tramp-remote-path)
5103 0 : (tramp-get-connection-process vec)
5104 270 : vec)
5105 : "remote-path"
5106 63 : (let* ((remote-path (copy-tree tramp-remote-path))
5107 63 : (elt1 (memq 'tramp-default-remote-path remote-path))
5108 63 : (elt2 (memq 'tramp-own-remote-path remote-path))
5109 : (default-remote-path
5110 63 : (when elt1
5111 63 : (or
5112 63 : (tramp-send-command-and-read
5113 63 : vec "echo \\\"`getconf PATH 2>/dev/null`\\\"" 'noerror)
5114 : ;; Default if "getconf" is not available.
5115 0 : (progn
5116 0 : (tramp-message
5117 0 : vec 3
5118 : "`getconf PATH' not successful, using default value \"%s\"."
5119 0 : "/bin:/usr/bin")
5120 63 : "/bin:/usr/bin"))))
5121 : (own-remote-path
5122 : ;; The login shell could return more than just the $PATH
5123 : ;; string. So we use `tramp-end-of-heredoc' as marker.
5124 63 : (when elt2
5125 0 : (or
5126 0 : (tramp-send-command-and-read
5127 0 : vec
5128 0 : (format
5129 : "%s %s %s 'echo %s \\\"$PATH\\\"'"
5130 0 : (tramp-get-method-parameter vec 'tramp-remote-shell)
5131 0 : (mapconcat
5132 : 'identity
5133 0 : (tramp-get-method-parameter vec 'tramp-remote-shell-login)
5134 0 : " ")
5135 0 : (mapconcat
5136 : 'identity
5137 0 : (tramp-get-method-parameter vec 'tramp-remote-shell-args)
5138 0 : " ")
5139 0 : (tramp-shell-quote-argument tramp-end-of-heredoc))
5140 0 : 'noerror (regexp-quote tramp-end-of-heredoc))
5141 0 : (progn
5142 0 : (tramp-message
5143 0 : vec 2 "Could not retrieve `tramp-own-remote-path'")
5144 63 : nil)))))
5145 :
5146 : ;; Replace place holder `tramp-default-remote-path'.
5147 63 : (when elt1
5148 63 : (setcdr elt1
5149 63 : (append
5150 63 : (split-string (or default-remote-path "") ":" 'omit)
5151 63 : (cdr elt1)))
5152 63 : (setq remote-path (delq 'tramp-default-remote-path remote-path)))
5153 :
5154 : ;; Replace place holder `tramp-own-remote-path'.
5155 63 : (when elt2
5156 0 : (setcdr elt2
5157 0 : (append
5158 0 : (split-string (or own-remote-path "") ":" 'omit)
5159 0 : (cdr elt2)))
5160 63 : (setq remote-path (delq 'tramp-own-remote-path remote-path)))
5161 :
5162 : ;; Remove double entries.
5163 63 : (setq elt1 remote-path)
5164 1197 : (while (consp elt1)
5165 1260 : (while (and (car elt1) (setq elt2 (member (car elt1) (cdr elt1))))
5166 1134 : (setcar elt2 nil))
5167 1134 : (setq elt1 (cdr elt1)))
5168 :
5169 : ;; Remove non-existing directories.
5170 63 : (delq
5171 : nil
5172 63 : (mapcar
5173 : (lambda (x)
5174 1134 : (and
5175 1134 : (stringp x)
5176 1008 : (file-directory-p
5177 1008 : (tramp-make-tramp-file-name
5178 1008 : (tramp-file-name-method vec)
5179 1008 : (tramp-file-name-user vec)
5180 1008 : (tramp-file-name-domain vec)
5181 1008 : (tramp-file-name-host vec)
5182 1008 : (tramp-file-name-port vec)
5183 1008 : x))
5184 1134 : x))
5185 207 : remote-path)))))
5186 :
5187 : (defun tramp-get-remote-locale (vec)
5188 : "Determine remote locale, supporting UTF8 if possible."
5189 186 : (with-tramp-connection-property vec "locale"
5190 44 : (tramp-send-command vec "locale -a")
5191 44 : (let ((candidates '("en_US.utf8" "C.utf8" "en_US.UTF-8" "C.UTF-8"))
5192 : locale)
5193 44 : (with-current-buffer (tramp-get-connection-buffer vec)
5194 88 : (while candidates
5195 44 : (goto-char (point-min))
5196 44 : (if (string-match (format "^%s\r?$" (regexp-quote (car candidates)))
5197 44 : (buffer-string))
5198 44 : (setq locale (car candidates)
5199 44 : candidates nil)
5200 44 : (setq candidates (cdr candidates)))))
5201 : ;; Return value.
5202 142 : (format "LC_ALL=%s" (or locale "C")))))
5203 :
5204 : (defun tramp-get-ls-command (vec)
5205 : "Determine remote `ls' command."
5206 467 : (with-tramp-connection-property vec "ls"
5207 9 : (tramp-message vec 5 "Finding a suitable `ls' command")
5208 9 : (or
5209 9 : (catch 'ls-found
5210 9 : (dolist (cmd '("ls" "gnuls" "gls"))
5211 9 : (let ((dl (tramp-get-remote-path vec))
5212 : result)
5213 9 : (while (and dl (setq result (tramp-find-executable vec cmd dl t t)))
5214 : ;; Check parameters. On busybox, "ls" output coloring is
5215 : ;; enabled by default sometimes. So we try to disable it
5216 : ;; when possible. $LS_COLORING is not supported there.
5217 : ;; Some "ls" versions are sensible wrt the order of
5218 : ;; arguments, they fail when "-al" is after the
5219 : ;; "--color=never" argument (for example on FreeBSD).
5220 9 : (when (tramp-send-command-and-check
5221 9 : vec (format "%s -lnd /" result))
5222 9 : (when (tramp-send-command-and-check
5223 9 : vec (format
5224 9 : "%s --color=never -al /dev/null" result))
5225 9 : (setq result (concat result " --color=never")))
5226 9 : (throw 'ls-found result))
5227 9 : (setq dl (cdr dl))))))
5228 458 : (tramp-error vec 'file-error "Couldn't find a proper `ls' command"))))
5229 :
5230 : (defun tramp-get-ls-command-with-dired (vec)
5231 : "Check, whether the remote `ls' command supports the --dired option."
5232 2 : (save-match-data
5233 3 : (with-tramp-connection-property vec "ls-dired"
5234 1 : (tramp-message vec 5 "Checking, whether `ls --dired' works")
5235 : ;; Some "ls" versions are sensible wrt the order of arguments,
5236 : ;; they fail when "-al" is after the "--dired" argument (for
5237 : ;; example on FreeBSD).
5238 1 : (tramp-send-command-and-check
5239 2 : vec (format "%s --dired -al /dev/null" (tramp-get-ls-command vec))))))
5240 :
5241 : (defun tramp-get-ls-command-with-quoting-style (vec)
5242 : "Check, whether the remote `ls' command supports the --quoting-style option."
5243 421 : (save-match-data
5244 430 : (with-tramp-connection-property vec "ls-quoting-style"
5245 9 : (tramp-message vec 5 "Checking, whether `ls --quoting-style=shell' works")
5246 9 : (tramp-send-command-and-check
5247 9 : vec (format "%s --quoting-style=shell -al /dev/null"
5248 421 : (tramp-get-ls-command vec))))))
5249 :
5250 : (defun tramp-get-ls-command-with-w-option (vec)
5251 : "Check, whether the remote `ls' command supports the -w option."
5252 0 : (save-match-data
5253 0 : (with-tramp-connection-property vec "ls-w-option"
5254 0 : (tramp-message vec 5 "Checking, whether `ls -w' works")
5255 : ;; Option "-w" is available on BSD systems. No argument is
5256 : ;; given, because this could return wrong results in case "ls"
5257 : ;; supports the "-w NUM" argument, as for busyboxes.
5258 0 : (tramp-send-command-and-check
5259 0 : vec (format "%s -alw" (tramp-get-ls-command vec))))))
5260 :
5261 : (defun tramp-get-test-command (vec)
5262 : "Determine remote `test' command."
5263 3148 : (with-tramp-connection-property vec "test"
5264 52 : (tramp-message vec 5 "Finding a suitable `test' command")
5265 52 : (if (tramp-send-command-and-check vec "test 0")
5266 : "test"
5267 3096 : (tramp-find-executable vec "test" (tramp-get-remote-path vec)))))
5268 :
5269 : (defun tramp-get-test-nt-command (vec)
5270 : "Check, whether the remote `test' command supports the -nt option."
5271 : ;; Does `test A -nt B' work? Use abominable `find' construct if it
5272 : ;; doesn't. BSD/OS 4.0 wants the parentheses around the command,
5273 : ;; for otherwise the shell crashes.
5274 0 : (with-tramp-connection-property vec "test-nt"
5275 0 : (or
5276 0 : (progn
5277 0 : (tramp-send-command
5278 0 : vec (format "( %s / -nt / )" (tramp-get-test-command vec)))
5279 0 : (with-current-buffer (tramp-get-buffer vec)
5280 0 : (goto-char (point-min))
5281 0 : (when (looking-at (regexp-quote tramp-end-of-output))
5282 0 : (format "%s %%s -nt %%s" (tramp-get-test-command vec)))))
5283 0 : (progn
5284 0 : (tramp-send-command
5285 0 : vec
5286 0 : (format
5287 : "tramp_test_nt () {\n%s -n \"`find $1 -prune -newer $2 -print`\"\n}"
5288 0 : (tramp-get-test-command vec)))
5289 0 : "tramp_test_nt %s %s"))))
5290 :
5291 : (defun tramp-get-file-exists-command (vec)
5292 : "Determine remote command for file existing check."
5293 3508 : (with-tramp-connection-property vec "file-exists"
5294 36 : (tramp-message vec 5 "Finding command to check if file exists")
5295 3472 : (tramp-find-file-exists-command vec)))
5296 :
5297 : (defun tramp-get-remote-ln (vec)
5298 : "Determine remote `ln' command."
5299 178 : (with-tramp-connection-property vec "ln"
5300 10 : (tramp-message vec 5 "Finding a suitable `ln' command")
5301 168 : (tramp-find-executable vec "ln" (tramp-get-remote-path vec))))
5302 :
5303 : (defun tramp-get-remote-perl (vec)
5304 : "Determine remote `perl' command."
5305 1063 : (with-tramp-connection-property vec "perl"
5306 8 : (tramp-message vec 5 "Finding a suitable `perl' command")
5307 8 : (let ((result
5308 8 : (or (tramp-find-executable vec "perl5" (tramp-get-remote-path vec))
5309 8 : (tramp-find-executable vec "perl" (tramp-get-remote-path vec)))))
5310 : ;; Perform a basic check.
5311 8 : (and result
5312 8 : (null (tramp-send-command-and-check
5313 8 : vec (format "%s -e 'print \"Hello\n\";'" result)))
5314 8 : (setq result nil))
5315 : ;; We must check also for some Perl modules.
5316 8 : (when result
5317 16 : (with-tramp-connection-property vec "perl-file-spec"
5318 8 : (tramp-send-command-and-check
5319 8 : vec (format "%s -e 'use File::Spec;'" result)))
5320 16 : (with-tramp-connection-property vec "perl-cwd-realpath"
5321 8 : (tramp-send-command-and-check
5322 8 : vec (format "%s -e 'use Cwd \"realpath\";'" result))))
5323 1055 : result)))
5324 :
5325 : (defun tramp-get-remote-stat (vec)
5326 : "Determine remote `stat' command."
5327 2523 : (with-tramp-connection-property vec "stat"
5328 32 : (tramp-message vec 5 "Finding a suitable `stat' command")
5329 32 : (let ((result (tramp-find-executable
5330 32 : vec "stat" (tramp-get-remote-path vec)))
5331 : tmp)
5332 : ;; Check whether stat(1) returns usable syntax. "%s" does not
5333 : ;; work on older AIX systems. Recent GNU stat versions (8.24?)
5334 : ;; use shell quoted format for "%N", we check the boundaries "`"
5335 : ;; and "'", therefore. See Bug#23422 in coreutils.
5336 : ;; Since GNU stat 8.26, environment variable QUOTING_STYLE is
5337 : ;; supported.
5338 32 : (when result
5339 32 : (setq result (concat "env QUOTING_STYLE=locale " result)
5340 32 : tmp (tramp-send-command-and-read
5341 32 : vec (format "%s -c '(\"%%N\" %%s)' /" result) 'noerror))
5342 32 : (unless (and (listp tmp) (stringp (car tmp))
5343 32 : (string-match "^\\(`/'\\|‘/’\\)$" (car tmp))
5344 32 : (integerp (cadr tmp)))
5345 32 : (setq result nil)))
5346 2491 : result)))
5347 :
5348 : (defun tramp-get-remote-readlink (vec)
5349 : "Determine remote `readlink' command."
5350 1269 : (with-tramp-connection-property vec "readlink"
5351 27 : (tramp-message vec 5 "Finding a suitable `readlink' command")
5352 27 : (let ((result (tramp-find-executable
5353 27 : vec "readlink" (tramp-get-remote-path vec))))
5354 27 : (when (and result
5355 27 : (tramp-send-command-and-check
5356 27 : vec (format "%s --canonicalize-missing /" result)))
5357 1242 : result))))
5358 :
5359 : (defun tramp-get-remote-trash (vec)
5360 : "Determine remote `trash' command.
5361 : This command is returned only if `delete-by-moving-to-trash' is non-nil."
5362 0 : (and delete-by-moving-to-trash
5363 0 : (with-tramp-connection-property vec "trash"
5364 0 : (tramp-message vec 5 "Finding a suitable `trash' command")
5365 0 : (tramp-find-executable vec "trash" (tramp-get-remote-path vec)))))
5366 :
5367 : (defun tramp-get-remote-touch (vec)
5368 : "Determine remote `touch' command."
5369 23 : (with-tramp-connection-property vec "touch"
5370 3 : (tramp-message vec 5 "Finding a suitable `touch' command")
5371 3 : (let ((result (tramp-find-executable
5372 3 : vec "touch" (tramp-get-remote-path vec)))
5373 : (tmpfile
5374 3 : (make-temp-name
5375 3 : (expand-file-name
5376 3 : tramp-temp-name-prefix (tramp-get-remote-tmpdir vec)))))
5377 : ;; Busyboxes do support the "-t" option only when they have been
5378 : ;; built with the DESKTOP config option. Let's check it.
5379 3 : (when result
5380 3 : (tramp-set-connection-property
5381 3 : vec "touch-t"
5382 3 : (tramp-send-command-and-check
5383 3 : vec
5384 3 : (format
5385 : "%s -t %s %s"
5386 3 : result
5387 3 : (format-time-string "%Y%m%d%H%M.%S")
5388 3 : (file-remote-p tmpfile 'localname))))
5389 3 : (delete-file tmpfile))
5390 20 : result)))
5391 :
5392 : (defun tramp-get-remote-gvfs-monitor-dir (vec)
5393 : "Determine remote `gvfs-monitor-dir' command."
5394 0 : (with-tramp-connection-property vec "gvfs-monitor-dir"
5395 0 : (tramp-message vec 5 "Finding a suitable `gvfs-monitor-dir' command")
5396 : ;; We distinguish "gvfs-monitor-dir.exe" from cygwin in order to
5397 : ;; establish better timeouts in filenotify-tests.el. Any better
5398 : ;; distinction approach would be welcome!
5399 0 : (or (tramp-find-executable
5400 0 : vec "gvfs-monitor-dir.exe" (tramp-get-remote-path vec) t t)
5401 0 : (tramp-find-executable
5402 0 : vec "gvfs-monitor-dir" (tramp-get-remote-path vec) t t))))
5403 :
5404 : (defun tramp-get-remote-inotifywait (vec)
5405 : "Determine remote `inotifywait' command."
5406 0 : (with-tramp-connection-property vec "inotifywait"
5407 0 : (tramp-message vec 5 "Finding a suitable `inotifywait' command")
5408 0 : (tramp-find-executable vec "inotifywait" (tramp-get-remote-path vec) t t)))
5409 :
5410 : (defun tramp-get-remote-id (vec)
5411 : "Determine remote `id' command."
5412 238 : (with-tramp-connection-property vec "id"
5413 36 : (tramp-message vec 5 "Finding POSIX `id' command")
5414 36 : (catch 'id-found
5415 36 : (dolist (cmd '("id" "gid"))
5416 36 : (let ((dl (tramp-get-remote-path vec))
5417 : result)
5418 36 : (while (and dl (setq result (tramp-find-executable vec cmd dl t t)))
5419 : ;; Check POSIX parameter.
5420 36 : (when (tramp-send-command-and-check vec (format "%s -u" result))
5421 36 : (throw 'id-found result))
5422 202 : (setq dl (cdr dl))))))))
5423 :
5424 : (defun tramp-get-remote-uid-with-id (vec id-format)
5425 : "Implement `tramp-get-remote-uid' for Tramp files using `id'."
5426 31 : (tramp-send-command-and-read
5427 31 : vec
5428 31 : (format "%s -u%s %s"
5429 31 : (tramp-get-remote-id vec)
5430 31 : (if (equal id-format 'integer) "" "n")
5431 31 : (if (equal id-format 'integer)
5432 31 : "" "| sed -e s/^/\\\"/ -e s/\\$/\\\"/"))))
5433 :
5434 : (defun tramp-get-remote-uid-with-perl (vec id-format)
5435 : "Implement `tramp-get-remote-uid' for Tramp files using a Perl script."
5436 0 : (tramp-send-command-and-read
5437 0 : vec
5438 0 : (format "%s -le '%s'"
5439 0 : (tramp-get-remote-perl vec)
5440 0 : (if (equal id-format 'integer)
5441 : "print $>"
5442 0 : "print \"\\\"\", scalar getpwuid($>), \"\\\"\""))))
5443 :
5444 : (defun tramp-get-remote-python (vec)
5445 : "Determine remote `python' command."
5446 0 : (with-tramp-connection-property vec "python"
5447 0 : (tramp-message vec 5 "Finding a suitable `python' command")
5448 0 : (or (tramp-find-executable vec "python" (tramp-get-remote-path vec))
5449 0 : (tramp-find-executable vec "python2" (tramp-get-remote-path vec))
5450 0 : (tramp-find-executable vec "python3" (tramp-get-remote-path vec)))))
5451 :
5452 : (defun tramp-get-remote-uid-with-python (vec id-format)
5453 : "Implement `tramp-get-remote-uid' for Tramp files using `python'."
5454 0 : (tramp-send-command-and-read
5455 0 : vec
5456 0 : (format "%s -c \"%s\""
5457 0 : (tramp-get-remote-python vec)
5458 0 : (if (equal id-format 'integer)
5459 : "import os; print (os.getuid())"
5460 0 : "import os, pwd; print ('\\\"' + pwd.getpwuid(os.getuid())[0] + '\\\"')"))))
5461 :
5462 : (defun tramp-get-remote-uid (vec id-format)
5463 : "The uid of the remote connection VEC, in ID-FORMAT.
5464 : ID-FORMAT valid values are `string' and `integer'."
5465 1870 : (with-tramp-connection-property vec (format "uid-%s" id-format)
5466 31 : (let ((res
5467 31 : (ignore-errors
5468 31 : (cond
5469 31 : ((tramp-get-remote-id vec)
5470 31 : (tramp-get-remote-uid-with-id vec id-format))
5471 0 : ((tramp-get-remote-perl vec)
5472 0 : (tramp-get-remote-uid-with-perl vec id-format))
5473 0 : ((tramp-get-remote-python vec)
5474 31 : (tramp-get-remote-uid-with-python vec id-format))))))
5475 : ;; Ensure there is a valid result.
5476 31 : (cond
5477 31 : ((and (equal id-format 'integer) (not (integerp res)))
5478 0 : tramp-unknown-id-integer)
5479 31 : ((and (equal id-format 'string) (not (stringp res)))
5480 0 : tramp-unknown-id-string)
5481 1839 : (t res)))))
5482 :
5483 : (defun tramp-get-remote-gid-with-id (vec id-format)
5484 : "Implement `tramp-get-remote-gid' for Tramp files using `id'."
5485 70 : (tramp-send-command-and-read
5486 70 : vec
5487 70 : (format "%s -g%s %s"
5488 70 : (tramp-get-remote-id vec)
5489 70 : (if (equal id-format 'integer) "" "n")
5490 70 : (if (equal id-format 'integer)
5491 70 : "" "| sed -e s/^/\\\"/ -e s/\\$/\\\"/"))))
5492 :
5493 : (defun tramp-get-remote-gid-with-perl (vec id-format)
5494 : "Implement `tramp-get-remote-gid' for Tramp files using a Perl script."
5495 0 : (tramp-send-command-and-read
5496 0 : vec
5497 0 : (format "%s -le '%s'"
5498 0 : (tramp-get-remote-perl vec)
5499 0 : (if (equal id-format 'integer)
5500 : "print ($)=~/(\\d+)/)"
5501 0 : "print \"\\\"\", scalar getgrgid($)), \"\\\"\""))))
5502 :
5503 : (defun tramp-get-remote-gid-with-python (vec id-format)
5504 : "Implement `tramp-get-remote-gid' for Tramp files using `python'."
5505 0 : (tramp-send-command-and-read
5506 0 : vec
5507 0 : (format "%s -c \"%s\""
5508 0 : (tramp-get-remote-python vec)
5509 0 : (if (equal id-format 'integer)
5510 : "import os; print (os.getgid())"
5511 0 : "import os, grp; print ('\\\"' + grp.getgrgid(os.getgid())[0] + '\\\"')"))))
5512 :
5513 : (defun tramp-get-remote-gid (vec id-format)
5514 : "The gid of the remote connection VEC, in ID-FORMAT.
5515 : ID-FORMAT valid values are `string' and `integer'."
5516 1770 : (with-tramp-connection-property vec (format "gid-%s" id-format)
5517 70 : (let ((res
5518 70 : (ignore-errors
5519 70 : (cond
5520 70 : ((tramp-get-remote-id vec)
5521 70 : (tramp-get-remote-gid-with-id vec id-format))
5522 0 : ((tramp-get-remote-perl vec)
5523 0 : (tramp-get-remote-gid-with-perl vec id-format))
5524 0 : ((tramp-get-remote-python vec)
5525 70 : (tramp-get-remote-gid-with-python vec id-format))))))
5526 : ;; Ensure there is a valid result.
5527 70 : (cond
5528 70 : ((and (equal id-format 'integer) (not (integerp res)))
5529 0 : tramp-unknown-id-integer)
5530 70 : ((and (equal id-format 'string) (not (stringp res)))
5531 0 : tramp-unknown-id-string)
5532 1700 : (t res)))))
5533 :
5534 : (defun tramp-get-env-with-u-option (vec)
5535 : "Check, whether the remote `env' command supports the -u option."
5536 90 : (with-tramp-connection-property vec "env-u-option"
5537 10 : (tramp-message vec 5 "Checking, whether `env -u' works")
5538 : ;; Option "-u" is a GNU extension.
5539 10 : (tramp-send-command-and-check
5540 80 : vec "env FOO=foo env -u FOO 2>/dev/null | grep -qv FOO" t)))
5541 :
5542 : ;; Some predefined connection properties.
5543 : (defun tramp-get-inline-compress (vec prop size)
5544 : "Return the compress command related to PROP.
5545 : PROP is either `inline-compress' or `inline-decompress'. SIZE is
5546 : the length of the file to be compressed.
5547 :
5548 : If no corresponding command is found, nil is returned."
5549 1350 : (when (and (integerp tramp-inline-compress-start-size)
5550 1350 : (> size tramp-inline-compress-start-size))
5551 5 : (with-tramp-connection-property (tramp-get-connection-process vec) prop
5552 1 : (tramp-find-inline-compress vec)
5553 1 : (tramp-get-connection-property
5554 1350 : (tramp-get-connection-process vec) prop nil))))
5555 :
5556 : (defun tramp-get-inline-coding (vec prop size)
5557 : "Return the coding command related to PROP.
5558 : PROP is either `remote-encoding', `remote-decoding',
5559 : `local-encoding' or `local-decoding'.
5560 :
5561 : SIZE is the length of the file to be coded. Depending on SIZE,
5562 : compression might be applied.
5563 :
5564 : If no corresponding command is found, nil is returned.
5565 : Otherwise, either a string is returned which contains a `%s' mark
5566 : to be used for the respective input or output file; or a Lisp
5567 : function cell is returned to be applied on a buffer."
5568 : ;; We must catch the errors, because we want to return nil, when
5569 : ;; no inline coding is found.
5570 1350 : (ignore-errors
5571 1350 : (let ((coding
5572 1350 : (with-tramp-connection-property
5573 1381 : (tramp-get-connection-process vec) prop
5574 31 : (tramp-find-inline-encoding vec)
5575 31 : (tramp-get-connection-property
5576 1350 : (tramp-get-connection-process vec) prop nil)))
5577 1350 : (prop1 (if (string-match "encoding" prop)
5578 1350 : "inline-compress" "inline-decompress"))
5579 : compress)
5580 : ;; The connection property might have been cached. So we must
5581 : ;; send the script to the remote side - maybe.
5582 1350 : (when (and coding (symbolp coding) (string-match "remote" prop))
5583 0 : (let ((name (symbol-name coding)))
5584 0 : (while (string-match (regexp-quote "-") name)
5585 0 : (setq name (replace-match "_" nil t name)))
5586 0 : (tramp-maybe-send-script vec (symbol-value coding) name)
5587 1350 : (setq coding name)))
5588 1350 : (when coding
5589 : ;; Check for the `compress' command.
5590 1350 : (setq compress (tramp-get-inline-compress vec prop1 size))
5591 : ;; Return the value.
5592 1350 : (cond
5593 1350 : ((and compress (symbolp coding))
5594 2 : (if (string-match "decompress" prop1)
5595 2 : `(lambda (beg end)
5596 2 : (,coding beg end)
5597 : (let ((coding-system-for-write 'binary)
5598 : (coding-system-for-read 'binary))
5599 : (apply
5600 2 : 'tramp-call-process-region ',vec (point-min) (point-max)
5601 2 : (car (split-string ,compress)) t t nil
5602 2 : (cdr (split-string ,compress)))))
5603 0 : `(lambda (beg end)
5604 : (let ((coding-system-for-write 'binary)
5605 : (coding-system-for-read 'binary))
5606 : (apply
5607 0 : 'tramp-call-process-region ',vec beg end
5608 0 : (car (split-string ,compress)) t t nil
5609 0 : (cdr (split-string ,compress))))
5610 2 : (,coding (point-min) (point-max)))))
5611 1348 : ((symbolp coding)
5612 673 : coding)
5613 675 : ((and compress (string-match "decoding" prop))
5614 0 : (format
5615 : ;; Windows shells need the program file name after
5616 : ;; the pipe symbol be quoted if they use forward
5617 : ;; slashes as directory separators.
5618 0 : (cond
5619 0 : ((and (string-match "local" prop)
5620 0 : (memq system-type '(windows-nt)))
5621 : "(%s | \"%s\")")
5622 0 : ((string-match "local" prop) "(%s | %s)")
5623 0 : (t "(%s | %s >%%s)"))
5624 0 : coding compress))
5625 675 : (compress
5626 2 : (format
5627 : ;; Windows shells need the program file name after
5628 : ;; the pipe symbol be quoted if they use forward
5629 : ;; slashes as directory separators.
5630 2 : (if (and (string-match "local" prop)
5631 2 : (memq system-type '(windows-nt)))
5632 : "(%s <%%s | \"%s\")"
5633 2 : "(%s <%%s | %s)")
5634 2 : compress coding))
5635 673 : ((string-match "decoding" prop)
5636 405 : (cond
5637 405 : ((string-match "local" prop) (format "%s" coding))
5638 405 : (t (format "%s >%%s" coding))))
5639 : (t
5640 1350 : (format "%s <%%s" coding)))))))
5641 :
5642 : (add-hook 'tramp-unload-hook
5643 : (lambda ()
5644 : (unload-feature 'tramp-sh 'force)))
5645 :
5646 : (provide 'tramp-sh)
5647 :
5648 : ;;; TODO:
5649 :
5650 : ;; * Don't use globbing for directories with many files, as this is
5651 : ;; likely to produce long command lines, and some shells choke on
5652 : ;; long command lines.
5653 : ;;
5654 : ;; * Don't search for perl5 and perl. Instead, only search for perl and
5655 : ;; then look if it's the right version (with `perl -v').
5656 : ;;
5657 : ;; * When editing a remote CVS controlled file as a different user, VC
5658 : ;; gets confused about the file locking status. Try to find out why
5659 : ;; the workaround doesn't work.
5660 : ;;
5661 : ;; * Allow out-of-band methods as _last_ multi-hop. Open a connection
5662 : ;; until the last but one hop via `start-file-process'. Apply it
5663 : ;; also for ftp and smb.
5664 : ;;
5665 : ;; * WIBNI if we had a command "trampclient"? If I was editing in
5666 : ;; some shell with root privileges, it would be nice if I could
5667 : ;; just call
5668 : ;; trampclient filename.c
5669 : ;; as an editor, and the _current_ shell would connect to an Emacs
5670 : ;; server and would be used in an existing non-privileged Emacs
5671 : ;; session for doing the editing in question.
5672 : ;; That way, I need not tell Emacs my password again and be afraid
5673 : ;; that it makes it into core dumps or other ugly stuff (I had Emacs
5674 : ;; once display a just typed password in the context of a keyboard
5675 : ;; sequence prompt for a question immediately following in a shell
5676 : ;; script run within Emacs -- nasty).
5677 : ;; And if I have some ssh session running to a different computer,
5678 : ;; having the possibility of passing a local file there to a local
5679 : ;; Emacs session (in case I can arrange for a connection back) would
5680 : ;; be nice.
5681 : ;; Likely the corresponding Tramp server should not allow the
5682 : ;; equivalent of the emacsclient -eval option in order to make this
5683 : ;; reasonably unproblematic. And maybe trampclient should have some
5684 : ;; way of passing credentials, like by using an SSL socket or
5685 : ;; something. (David Kastrup)
5686 : ;;
5687 : ;; * Reconnect directly to a compliant shell without first going
5688 : ;; through the user's default shell. (Pete Forman)
5689 : ;;
5690 : ;; * How can I interrupt the remote process with a signal
5691 : ;; (interrupt-process seems not to work)? (Markus Triska)
5692 : ;;
5693 : ;; * Avoid the local shell entirely for starting remote processes. If
5694 : ;; so, I think even a signal, when delivered directly to the local
5695 : ;; SSH instance, would correctly be propagated to the remote process
5696 : ;; automatically; possibly SSH would have to be started with
5697 : ;; "-t". (Markus Triska)
5698 : ;;
5699 : ;; * It makes me wonder if tramp couldn't fall back to ssh when scp
5700 : ;; isn't on the remote host. (Mark A. Hershberger)
5701 : ;;
5702 : ;; * Use lsh instead of ssh. (Alfred M. Szmidt)
5703 : ;;
5704 : ;; * Optimize out-of-band copying when both methods are scp-like (not
5705 : ;; rsync).
5706 : ;;
5707 : ;; * Keep a second connection open for out-of-band methods like scp or
5708 : ;; rsync.
5709 : ;;
5710 : ;; * Implement completion for "/method:user@host:~<abc> TAB".
5711 : ;;
5712 : ;; * I think you could get the best of both worlds by using an
5713 : ;; approach similar to Tramp but running a little tramp-daemon on
5714 : ;; the other end, such that we can use a more efficient
5715 : ;; communication protocol (e.g. when saving a file we could locally
5716 : ;; diff it against the last version (of which the remote daemon
5717 : ;; would also keep a copy), and then only send the diff).
5718 : ;;
5719 : ;; This said, even using such a daemon it might be difficult to get
5720 : ;; good performance: part of the problem is the number of
5721 : ;; round-trips. E.g. when saving a file we have to check if the
5722 : ;; file was modified in the mean time and whether saving into a new
5723 : ;; inode would change the owner (etc...), which each require a
5724 : ;; round-trip. To get rid of these round-trips, we'd have to
5725 : ;; shortcut this code and delegate the higher-level "save file"
5726 : ;; operation to the remote server, which then has to perform those
5727 : ;; tasks but still obeying the locally set customizations about how
5728 : ;; to do each one of those tasks.
5729 : ;;
5730 : ;; We could either put higher-level ops in there (like
5731 : ;; `save-buffer'), which implies replicating the whole `save-buffer'
5732 : ;; behavior, which is a lot of work and likely to be not 100%
5733 : ;; faithful.
5734 : ;;
5735 : ;; Or we could introduce new low-level ops that are asynchronous,
5736 : ;; and then rewrite save-buffer to use them. IOW save-buffer would
5737 : ;; start with a bunch of calls like `start-getting-file-attributes'
5738 : ;; which could immediately be passed on to the remote side, and
5739 : ;; later on checks the return value of those calls as and when
5740 : ;; needed. (Stefan Monnier)
5741 :
5742 : ;;; tramp-sh.el ends here
|