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