]> code.delx.au - gnu-emacs/blobdiff - lisp/net/tramp-sh.el
Update copyright year to 2015
[gnu-emacs] / lisp / net / tramp-sh.el
index a67ca32bbaa715c34f3a963e95cced7d2e02e6c2..2a046f7473dde160cf2278c551540f5cb0687763 100644 (file)
@@ -1,6 +1,6 @@
 ;;; tramp-sh.el --- Tramp access functions for (s)sh-like connections
 
 ;;; tramp-sh.el --- Tramp access functions for (s)sh-like connections
 
-;; Copyright (C) 1998-2014 Free Software Foundation, Inc.
+;; Copyright (C) 1998-2015 Free Software Foundation, Inc.
 
 ;; (copyright statements below in code to be updated with the above notice)
 
 
 ;; (copyright statements below in code to be updated with the above notice)
 
@@ -64,6 +64,22 @@ files conditionalize this setup based on the TERM environment variable."
   :group 'tramp
   :type 'string)
 
   :group 'tramp
   :type 'string)
 
+;;;###tramp-autoload
+(defcustom tramp-histfile-override "/dev/null"
+  "When invoking a shell, override the HISTFILE with this value.
+By default, the HISTFILE is set to the \"/dev/null\" value, which
+is special on Unix systems and indicates the shell history should
+not be logged (this avoids clutter due to Tramp commands).
+
+If you set this variable to nil, however, the *override* is
+disabled, so the history will go to the default storage
+location, e.g. \"$HOME/.sh_history\"."
+  :group 'tramp
+  :version "25.1"
+  :type '(choice (const :tag "Do not override HISTFILE" nil)
+                 (const :tag "Empty the history (/dev/null)" "/dev/null")
+                 (string :tag "Redirect to a file")))
+
 ;;;###tramp-autoload
 (defconst tramp-color-escape-sequence-regexp "\e[[;0-9]+m"
   "Escape sequences produced by the \"ls\" command.")
 ;;;###tramp-autoload
 (defconst tramp-color-escape-sequence-regexp "\e[[;0-9]+m"
   "Escape sequences produced by the \"ls\" command.")
@@ -1103,23 +1119,24 @@ target of the symlink differ."
 (defun tramp-sh-handle-file-attributes (filename &optional id-format)
   "Like `file-attributes' for Tramp files."
   (unless id-format (setq id-format 'integer))
 (defun tramp-sh-handle-file-attributes (filename &optional id-format)
   "Like `file-attributes' for Tramp files."
   (unless id-format (setq id-format 'integer))
-  ;; Don't modify `last-coding-system-used' by accident.
-  (let ((last-coding-system-used last-coding-system-used))
-    (with-parsed-tramp-file-name (expand-file-name filename) nil
-      (with-tramp-file-property
-         v localname (format "file-attributes-%s" id-format)
-       (save-excursion
-         (tramp-convert-file-attributes
-          v
-          (or
-           (cond
-            ((tramp-get-remote-stat v)
-             (tramp-do-file-attributes-with-stat v localname id-format))
-            ((tramp-get-remote-perl v)
-             (tramp-do-file-attributes-with-perl v localname id-format))
-            (t nil))
-           ;; The scripts could fail, for example with huge file size.
-           (tramp-do-file-attributes-with-ls v localname id-format))))))))
+  (ignore-errors
+    ;; Don't modify `last-coding-system-used' by accident.
+    (let ((last-coding-system-used last-coding-system-used))
+      (with-parsed-tramp-file-name (expand-file-name filename) nil
+       (with-tramp-file-property
+           v localname (format "file-attributes-%s" id-format)
+         (save-excursion
+           (tramp-convert-file-attributes
+            v
+            (or
+             (cond
+              ((tramp-get-remote-stat v)
+               (tramp-do-file-attributes-with-stat v localname id-format))
+              ((tramp-get-remote-perl v)
+               (tramp-do-file-attributes-with-perl v localname id-format))
+              (t nil))
+             ;; The scripts could fail, for example with huge file size.
+             (tramp-do-file-attributes-with-ls v localname id-format)))))))))
 
 (defun tramp-do-file-attributes-with-ls (vec localname &optional id-format)
   "Implement `file-attributes' for Tramp files using the ls(1) command."
 
 (defun tramp-do-file-attributes-with-ls (vec localname &optional id-format)
   "Implement `file-attributes' for Tramp files using the ls(1) command."
@@ -2061,10 +2078,15 @@ FILENAME is the source file, NEWNAME the target file.
 KEEP-DATE is non-nil if NEWNAME should have the same timestamp as FILENAME."
   (with-temp-buffer
     ;; We must disable multibyte, because binary data shall not be
 KEEP-DATE is non-nil if NEWNAME should have the same timestamp as FILENAME."
   (with-temp-buffer
     ;; We must disable multibyte, because binary data shall not be
-    ;; converted.
+    ;; converted.  We remove `tramp-file-name-handler' from
+    ;; `inhibit-file-name-handlers'; otherwise the file name handler
+    ;; for `insert-file-contents' might be deactivated in some corner
+    ;; cases.
     (set-buffer-multibyte nil)
     (let ((coding-system-for-read 'binary)
     (set-buffer-multibyte nil)
     (let ((coding-system-for-read 'binary)
-         (jka-compr-inhibit t))
+         (jka-compr-inhibit t)
+         (inhibit-file-name-handlers
+          (remq 'tramp-file-name-handler inhibit-file-name-handlers)))
       (insert-file-contents-literally filename))
     ;; We don't want the target file to be compressed, so we let-bind
     ;; `jka-compr-inhibit' to t.
       (insert-file-contents-literally filename))
     ;; We don't want the target file to be compressed, so we let-bind
     ;; `jka-compr-inhibit' to t.
@@ -2449,8 +2471,12 @@ The method used must be an out-of-band method."
                   (mapconcat 'identity (process-command p) " "))
                  (tramp-set-connection-property p "vector" orig-vec)
                  (tramp-compat-set-process-query-on-exit-flag p nil)
                   (mapconcat 'identity (process-command p) " "))
                  (tramp-set-connection-property p "vector" orig-vec)
                  (tramp-compat-set-process-query-on-exit-flag p nil)
-                 (tramp-process-actions
-                  p v nil tramp-actions-copy-out-of-band)
+
+                 ;; We must adapt `tramp-local-end-of-line' for
+                 ;; sending the password.
+                 (let ((tramp-local-end-of-line tramp-rsh-end-of-line))
+                   (tramp-process-actions
+                    p v nil tramp-actions-copy-out-of-band))
 
                  ;; Check the return code.
                  (goto-char (point-max))
 
                  ;; Check the return code.
                  (goto-char (point-max))
@@ -2829,16 +2855,27 @@ the result will be a local, non-Tramp, file name."
                        (list (replace-match " \\\\\n" nil nil (cadr args))))
                       (setq i (+ i 250))))
                   (cdr args)))
                        (list (replace-match " \\\\\n" nil nil (cadr args))))
                       (setq i (+ i 250))))
                   (cdr args)))
+          ;; Use a human-friendly prompt, for example for `shell'.
+          (prompt (format "PS1=%s"
+                          (format "%s %s"
+                                  (file-remote-p default-directory)
+                                  tramp-initial-end-of-output)))
+          ;; We use as environment the difference to toplevel
+          ;; `process-environment'.
+          env
+          (env
+           (dolist
+               (elt
+                (cons prompt (nreverse (copy-sequence process-environment)))
+                env)
+             (or (member elt (default-toplevel-value 'process-environment))
+                 (setq env (cons elt env)))))
           (command
            (when (stringp program)
           (command
            (when (stringp program)
-             (format "cd %s && exec %s env PS1=%s %s"
+             (format "cd %s && exec %s env %s %s"
                      (tramp-shell-quote-argument localname)
                      (if heredoc (format "<<'%s'" tramp-end-of-heredoc) "")
                      (tramp-shell-quote-argument localname)
                      (if heredoc (format "<<'%s'" tramp-end-of-heredoc) "")
-                     ;; Use a human-friendly prompt, for example for `shell'.
-                     (tramp-shell-quote-argument
-                      (format "%s %s"
-                              (file-remote-p default-directory)
-                              tramp-initial-end-of-output))
+                     (mapconcat 'tramp-shell-quote-argument env " ")
                      (if heredoc
                          (format "%s\n(\n%s\n) </dev/tty\n%s"
                                  program (car args) tramp-end-of-heredoc)
                      (if heredoc
                          (format "%s\n(\n%s\n) </dev/tty\n%s"
                                  program (car args) tramp-end-of-heredoc)
@@ -2850,7 +2887,7 @@ the result will be a local, non-Tramp, file name."
           (name1 name)
           (i 0)
           ;; We do not want to raise an error when
           (name1 name)
           (i 0)
           ;; We do not want to raise an error when
-          ;; `start-file-process' has been started several time in
+          ;; `start-file-process' has been started several times in
           ;; `eshell' and friends.
           (tramp-current-connection nil))
 
           ;; `eshell' and friends.
           (tramp-current-connection nil))
 
@@ -2925,10 +2962,20 @@ the result will be a local, non-Tramp, file name."
     (error "Implementation does not handle immediate return"))
 
   (with-parsed-tramp-file-name default-directory nil
     (error "Implementation does not handle immediate return"))
 
   (with-parsed-tramp-file-name default-directory nil
-    (let (command input tmpinput stderr tmpstderr outbuf ret)
+    (let (command env input tmpinput stderr tmpstderr outbuf ret)
       ;; Compute command.
       (setq command (mapconcat 'tramp-shell-quote-argument
                               (cons program args) " "))
       ;; Compute command.
       (setq command (mapconcat 'tramp-shell-quote-argument
                               (cons program args) " "))
+      ;; We use as environment the difference to toplevel `process-environment'.
+      (setq env
+           (dolist (elt (nreverse (copy-sequence process-environment)) env)
+             (or (member elt (default-toplevel-value 'process-environment))
+                 (setq env (cons elt env)))))
+      (when env
+       (setq command
+             (format
+              "env %s %s"
+              (mapconcat 'tramp-shell-quote-argument env " ") command)))
       ;; Determine input.
       (if (null infile)
          (setq input "/dev/null")
       ;; Determine input.
       (if (null infile)
          (setq input "/dev/null")
@@ -3841,9 +3888,24 @@ file exists and nonzero exit status otherwise."
        (setq item (pop alist))
        (when (string-match (car item) shell)
          (setq extra-args (cdr item))))
        (setq item (pop alist))
        (when (string-match (car item) shell)
          (setq extra-args (cdr item))))
+      ;; It is useful to set the prompt in the following command
+      ;; because some people have a setting for $PS1 which /bin/sh
+      ;; doesn't know about and thus /bin/sh will display a strange
+      ;; prompt.  For example, if $PS1 has "${CWD}" in the value, then
+      ;; ksh will display the current working directory but /bin/sh
+      ;; will display a dollar sign.  The following command line sets
+      ;; $PS1 to a sane value, and works under Bourne-ish shells as
+      ;; well as csh-like shells.  We also unset the variable $ENV
+      ;; because that is read by some sh implementations (eg, bash
+      ;; when called as sh) on startup; this way, we avoid the startup
+      ;; file clobbering $PS1.  $PROMPT_COMMAND is another way to set
+      ;; the prompt in /bin/bash, it must be discarded as well.
       (tramp-send-command
        vec (format
       (tramp-send-command
        vec (format
-           "exec env ENV='' HISTFILE=/dev/null PROMPT_COMMAND='' PS1=%s PS2='' PS3='' %s %s"
+           "exec env ENV=''%s PROMPT_COMMAND='' PS1=%s PS2='' PS3='' %s %s"
+            (if tramp-histfile-override
+                (concat " HISTFILE=" tramp-histfile-override)
+              "")
            (tramp-shell-quote-argument tramp-end-of-output)
            shell (or extra-args ""))
        t))
            (tramp-shell-quote-argument tramp-end-of-output)
            shell (or extra-args ""))
        t))
@@ -3917,20 +3979,6 @@ seconds.  If not, it produces an error message with the given ERROR-ARGS."
 Mainly sets the prompt and the echo correctly.  PROC is the shell
 process to set up.  VEC specifies the connection."
   (let ((tramp-end-of-output tramp-initial-end-of-output))
 Mainly sets the prompt and the echo correctly.  PROC is the shell
 process to set up.  VEC specifies the connection."
   (let ((tramp-end-of-output tramp-initial-end-of-output))
-    ;; It is useful to set the prompt in the following command because
-    ;; some people have a setting for $PS1 which /bin/sh doesn't know
-    ;; about and thus /bin/sh will display a strange prompt.  For
-    ;; example, if $PS1 has "${CWD}" in the value, then ksh will
-    ;; display the current working directory but /bin/sh will display
-    ;; a dollar sign.  The following command line sets $PS1 to a sane
-    ;; value, and works under Bourne-ish shells as well as csh-like
-    ;; shells.  Daniel Pittman reports that the unusual positioning of
-    ;; the single quotes makes it work under `rc', too.  We also unset
-    ;; the variable $ENV because that is read by some sh
-    ;; implementations (eg, bash when called as sh) on startup; this
-    ;; way, we avoid the startup file clobbering $PS1.  $PROMPT_COMMAND
-    ;; is another way to set the prompt in /bin/bash, it must be
-    ;; discarded as well.
     (tramp-open-shell
      vec
      (or (tramp-get-connection-property vec "remote-shell" nil)
     (tramp-open-shell
      vec
      (or (tramp-get-connection-property vec "remote-shell" nil)
@@ -3955,10 +4003,8 @@ process to set up.  VEC specifies the connection."
 
   (tramp-message vec 5 "Setting shell prompt")
   (tramp-send-command
 
   (tramp-message vec 5 "Setting shell prompt")
   (tramp-send-command
-   vec (format "PS1=%s" (tramp-shell-quote-argument tramp-end-of-output)) t)
-  (tramp-send-command vec "PS2=''" t)
-  (tramp-send-command vec "PS3=''" t)
-  (tramp-send-command vec "PROMPT_COMMAND=''" t)
+   vec (format "PS1=%s PS2='' PS3='' PROMPT_COMMAND=''"
+              (tramp-shell-quote-argument tramp-end-of-output)) t)
 
   ;; Try to set up the coding system correctly.
   ;; CCC this can't be the right way to do it.  Hm.
 
   ;; Try to set up the coding system correctly.
   ;; CCC this can't be the right way to do it.  Hm.
@@ -3967,9 +4013,9 @@ process to set up.  VEC specifies the connection."
     (if (featurep 'mule)
        ;; Use MULE to select the right EOL convention for communicating
        ;; with the process.
     (if (featurep 'mule)
        ;; Use MULE to select the right EOL convention for communicating
        ;; with the process.
-       (let ((cs (or (when (string-match
-                            "utf8" (or (tramp-get-remote-locale vec) ""))
-                       (cons 'utf-8 'utf-8))
+       (let ((cs (or (and (memq 'utf-8 (coding-system-list))
+                          (string-match "utf8" (tramp-get-remote-locale vec))
+                          (cons 'utf-8 'utf-8))
                      (tramp-compat-funcall 'process-coding-system proc)
                      (cons 'undecided 'undecided)))
              cs-decode cs-encode)
                      (tramp-compat-funcall 'process-coding-system proc)
                      (cons 'undecided 'undecided)))
              cs-decode cs-encode)
@@ -4075,18 +4121,24 @@ process to set up.  VEC specifies the connection."
   ;; Set the environment.
   (tramp-message vec 5 "Setting default environment")
 
   ;; Set the environment.
   (tramp-message vec 5 "Setting default environment")
 
-  (let ((env (append (when (tramp-get-remote-locale vec) ; Discard `(nil)'.
-                      `(,(tramp-get-remote-locale vec)))
+  (let ((env (append `(,(tramp-get-remote-locale vec))
                     (copy-sequence tramp-remote-process-environment)))
                     (copy-sequence tramp-remote-process-environment)))
-       unset item)
+       unset vars item)
     (while env
       (setq item (tramp-compat-split-string (car env) "="))
       (setcdr item (mapconcat 'identity (cdr item) "="))
       (if (and (stringp (cdr item)) (not (string-equal (cdr item) "")))
     (while env
       (setq item (tramp-compat-split-string (car env) "="))
       (setcdr item (mapconcat 'identity (cdr item) "="))
       (if (and (stringp (cdr item)) (not (string-equal (cdr item) "")))
-         (tramp-send-command
-          vec (format "%s=%s; export %s" (car item) (cdr item) (car item)) t)
+         (push (format "%s %s" (car item) (cdr item)) vars)
        (push (car item) unset))
       (setq env (cdr env)))
        (push (car item) unset))
       (setq env (cdr env)))
+    (when vars
+      (tramp-send-command
+       vec
+       (format "while read var val; do export $var=$val; done <<'%s'\n%s\n%s"
+              tramp-end-of-heredoc
+              (mapconcat 'identity vars "\n")
+              tramp-end-of-heredoc)
+       t))
     (when unset
       (tramp-send-command
        vec (format "unset %s" (mapconcat 'identity unset " ")) t))))
     (when unset
       (tramp-send-command
        vec (format "unset %s" (mapconcat 'identity unset " ")) t))))
@@ -4462,7 +4514,8 @@ Gateway hops are already opened."
        ;; Therefore, we must remember the gateway vector.  But we
        ;; cannot do it as connection property, because it shouldn't
        ;; be persistent.  And we have no started process yet either.
        ;; Therefore, we must remember the gateway vector.  But we
        ;; cannot do it as connection property, because it shouldn't
        ;; be persistent.  And we have no started process yet either.
-       (tramp-set-file-property (car target-alist) "" "gateway" hop)))
+       (let ((tramp-verbose 0))
+         (tramp-set-file-property (car target-alist) "" "gateway" hop))))
 
     ;; Foreign and out-of-band methods are not supported for multi-hops.
     (when (cdr target-alist)
 
     ;; Foreign and out-of-band methods are not supported for multi-hops.
     (when (cdr target-alist)
@@ -4574,7 +4627,8 @@ connection if a previous connection has died for some reason."
                (delete-process p))
              (setenv "TERM" tramp-terminal-type)
              (setenv "LC_ALL" "en_US.utf8")
                (delete-process p))
              (setenv "TERM" tramp-terminal-type)
              (setenv "LC_ALL" "en_US.utf8")
-             (setenv "HISTFILE" "/dev/null")
+             (when tramp-histfile-override
+                (setenv "HISTFILE" tramp-histfile-override))
              (setenv "PROMPT_COMMAND")
              (setenv "PS1" tramp-initial-end-of-output)
              (let* ((target-alist (tramp-compute-multi-hops vec))
              (setenv "PROMPT_COMMAND")
              (setenv "PS1" tramp-initial-end-of-output)
              (let* ((target-alist (tramp-compute-multi-hops vec))
@@ -4638,7 +4692,8 @@ connection if a previous connection has died for some reason."
                           l-method 'tramp-connection-timeout))
                         (gw-args
                          (tramp-get-method-parameter l-method 'tramp-gw-args))
                           l-method 'tramp-connection-timeout))
                         (gw-args
                          (tramp-get-method-parameter l-method 'tramp-gw-args))
-                        (gw (tramp-get-file-property hop "" "gateway" nil))
+                        (gw (let ((tramp-verbose 0))
+                              (tramp-get-file-property hop "" "gateway" nil)))
                         (g-method (and gw (tramp-file-name-method gw)))
                         (g-user (and gw (tramp-file-name-user gw)))
                         (g-host (and gw (tramp-file-name-real-host gw)))
                         (g-method (and gw (tramp-file-name-method gw)))
                         (g-user (and gw (tramp-file-name-user gw)))
                         (g-host (and gw (tramp-file-name-real-host gw)))
@@ -4666,8 +4721,10 @@ connection if a previous connection has died for some reason."
                      (setq login-args (append async-args login-args)))
 
                    ;; Add gateway arguments if necessary.
                      (setq login-args (append async-args login-args)))
 
                    ;; Add gateway arguments if necessary.
-                   (when (and gw gw-args)
-                     (setq login-args (append gw-args login-args)))
+                   (when gw
+                     (tramp-set-connection-property p "gateway" t)
+                     (when gw-args
+                       (setq login-args (append gw-args login-args))))
 
                    ;; Check for port number.  Until now, there's no
                    ;; need for handling like method, user, host.
 
                    ;; Check for port number.  Until now, there's no
                    ;; need for handling like method, user, host.
@@ -4764,6 +4821,17 @@ function waits for output unless NOOUTPUT is set."
     (when (tramp-get-connection-property p "remote-echo" nil)
       ;; We mark the command string that it can be erased in the output buffer.
       (tramp-set-connection-property p "check-remote-echo" t)
     (when (tramp-get-connection-property p "remote-echo" nil)
       ;; We mark the command string that it can be erased in the output buffer.
       (tramp-set-connection-property p "check-remote-echo" t)
+      ;; If we put `tramp-echo-mark' after a trailing newline (which
+      ;; is assumed to be unquoted) `tramp-send-string' doesn't see
+      ;; that newline and adds `tramp-rsh-end-of-line' right after
+      ;; `tramp-echo-mark', so the remote shell sees two consecutive
+      ;; trailing line endings and sends two prompts after executing
+      ;; the command, which confuses `tramp-wait-for-output'.
+      (when (and (not (string= command ""))
+                (string-equal (substring command -1) "\n"))
+       (setq command (substring command 0 -1)))
+      ;; No need to restore a trailing newline here since `tramp-send-string'
+      ;; makes sure that the string ends in `tramp-rsh-end-of-line', anyway.
       (setq command (format "%s%s%s" tramp-echo-mark command tramp-echo-mark)))
     ;; Send the command.
     (tramp-message vec 6 "%s" command)
       (setq command (format "%s%s%s" tramp-echo-mark command tramp-echo-mark)))
     ;; Send the command.
     (tramp-message vec 6 "%s" command)
@@ -4842,8 +4910,9 @@ FMT and ARGS which are passed to `error'."
   (or (tramp-send-command-and-check vec command)
       (apply 'tramp-error vec 'file-error fmt args)))
 
   (or (tramp-send-command-and-check vec command)
       (apply 'tramp-error vec 'file-error fmt args)))
 
-(defun tramp-send-command-and-read (vec command &optional noerror)
+(defun tramp-send-command-and-read (vec command &optional noerror marker)
   "Run COMMAND and return the output, which must be a Lisp expression.
   "Run COMMAND and return the output, which must be a Lisp expression.
+If MARKER is a regexp, read the output after that string.
 In case there is no valid Lisp expression and NOERROR is nil, it
 raises an error."
   (when (if noerror
 In case there is no valid Lisp expression and NOERROR is nil, it
 raises an error."
   (when (if noerror
@@ -4851,8 +4920,17 @@ raises an error."
          (tramp-barf-unless-okay
           vec command "`%s' returns with error" command))
     (with-current-buffer (tramp-get-connection-buffer vec)
          (tramp-barf-unless-okay
           vec command "`%s' returns with error" command))
     (with-current-buffer (tramp-get-connection-buffer vec)
-      ;; Read the expression.
       (goto-char (point-min))
       (goto-char (point-min))
+      ;; Read the marker.
+      (when (stringp marker)
+       (condition-case nil
+           (re-search-forward marker)
+         (error (unless noerror
+                  (tramp-error
+                   vec 'file-error
+                   "`%s' does not return the marker `%s': `%s'"
+                   command marker (buffer-string))))))
+      ;; Read the expression.
       (condition-case nil
          (prog1 (read (current-buffer))
            ;; Error handling.
       (condition-case nil
          (prog1 (read (current-buffer))
            ;; Error handling.
@@ -5004,13 +5082,22 @@ Return ATTR."
                   "/bin:/usr/bin")
                  "/bin:/usr/bin"))))
           (own-remote-path
                   "/bin:/usr/bin")
                  "/bin:/usr/bin"))))
           (own-remote-path
-            (when elt2
-              (condition-case nil
-                  (tramp-send-command-and-read vec "echo \\\"$PATH\\\"")
-                (error
-                 (tramp-message
-                  vec 3 "$PATH not set, ignoring `tramp-own-remote-path'.")
-                 nil)))))
+           ;; The login shell could return more than just the $PATH
+           ;; string.  So we use `tramp-end-of-heredoc' as marker.
+           (when elt2
+             (tramp-send-command-and-read
+              vec
+              (format
+               "%s -l %s 'echo %s \\\"$PATH\\\"'"
+               (tramp-get-method-parameter
+                (tramp-file-name-method vec) 'tramp-remote-shell)
+               (mapconcat
+                'identity
+                (tramp-get-method-parameter
+                 (tramp-file-name-method vec) 'tramp-remote-shell-args)
+                " ")
+               (tramp-shell-quote-argument tramp-end-of-heredoc))
+              nil (regexp-quote tramp-end-of-heredoc)))))
 
       ;; Replace place holder `tramp-default-remote-path'.
       (when elt1
 
       ;; Replace place holder `tramp-default-remote-path'.
       (when elt1
@@ -5054,7 +5141,7 @@ Return ATTR."
 (defun tramp-get-remote-locale (vec)
   (with-tramp-connection-property vec "locale"
     (tramp-send-command vec "locale -a")
 (defun tramp-get-remote-locale (vec)
   (with-tramp-connection-property vec "locale"
     (tramp-send-command vec "locale -a")
-    (let ((candidates '("en_US.utf8" "C.utf8" "C"))
+    (let ((candidates '("en_US.utf8" "C.utf8"))
          locale)
       (with-current-buffer (tramp-get-connection-buffer vec)
        (while candidates
          locale)
       (with-current-buffer (tramp-get-connection-buffer vec)
        (while candidates
@@ -5064,7 +5151,7 @@ Return ATTR."
                    candidates nil)
            (setq candidates (cdr candidates)))))
       ;; Return value.
                    candidates nil)
            (setq candidates (cdr candidates)))))
       ;; Return value.
-      (when locale (format "LC_ALL=%s" locale)))))
+      (format "LC_ALL=%s" (or locale "C")))))
 
 (defun tramp-get-ls-command (vec)
   (with-tramp-connection-property vec "ls"
 
 (defun tramp-get-ls-command (vec)
   (with-tramp-connection-property vec "ls"
@@ -5208,7 +5295,7 @@ Return ATTR."
          (format
           "%s -t %s %s"
           result
          (format
           "%s -t %s %s"
           result
-          (format-time-string "%Y%m%d%H%M.%S" (current-time))
+          (format-time-string "%Y%m%d%H%M.%S")
           (tramp-file-name-handler 'file-remote-p tmpfile 'localname))))
        (delete-file tmpfile))
       result)))
           (tramp-file-name-handler 'file-remote-p tmpfile 'localname))))
        (delete-file tmpfile))
       result)))
@@ -5228,13 +5315,14 @@ Return ATTR."
   (with-tramp-connection-property vec "id"
     (tramp-message vec 5 "Finding POSIX `id' command")
     (catch 'id-found
   (with-tramp-connection-property vec "id"
     (tramp-message vec 5 "Finding POSIX `id' command")
     (catch 'id-found
-      (let ((dl (tramp-get-remote-path vec))
-           result)
-       (while (and dl (setq result (tramp-find-executable vec "id" dl t t)))
-         ;; Check POSIX parameter.
-         (when (tramp-send-command-and-check vec (format "%s -u" result))
-           (throw 'id-found result))
-         (setq dl (cdr dl)))))))
+      (dolist (cmd '("id" "gid"))
+       (let ((dl (tramp-get-remote-path vec))
+             result)
+         (while (and dl (setq result (tramp-find-executable vec cmd dl t t)))
+           ;; Check POSIX parameter.
+           (when (tramp-send-command-and-check vec (format "%s -u" result))
+             (throw 'id-found result))
+           (setq dl (cdr dl))))))))
 
 (defun tramp-get-remote-uid-with-id (vec id-format)
   (tramp-send-command-and-read
 
 (defun tramp-get-remote-uid-with-id (vec id-format)
   (tramp-send-command-and-read
@@ -5272,17 +5360,20 @@ Return ATTR."
 
 (defun tramp-get-remote-uid (vec id-format)
   (with-tramp-connection-property vec (format "uid-%s" id-format)
 
 (defun tramp-get-remote-uid (vec id-format)
   (with-tramp-connection-property vec (format "uid-%s" id-format)
-    (let ((res (cond
-               ((tramp-get-remote-id vec)
-                (tramp-get-remote-uid-with-id vec id-format))
-               ((tramp-get-remote-perl vec)
-                (tramp-get-remote-uid-with-perl vec id-format))
-               ((tramp-get-remote-python vec)
-                (tramp-get-remote-uid-with-python vec id-format))
-               (t (tramp-error
-                   vec 'file-error "Cannot determine remote uid")))))
-      ;; The command might not always return a number.
-      (if (and (equal id-format 'integer) (not (integerp res))) -1 res))))
+    (let ((res
+          (ignore-errors
+            (cond
+             ((tramp-get-remote-id vec)
+              (tramp-get-remote-uid-with-id vec id-format))
+             ((tramp-get-remote-perl vec)
+              (tramp-get-remote-uid-with-perl vec id-format))
+             ((tramp-get-remote-python vec)
+              (tramp-get-remote-uid-with-python vec id-format))))))
+      ;; Ensure there is a valid result.
+      (cond
+       ((and (equal id-format 'integer) (not (integerp res))) -1)
+       ((and (equal id-format 'string) (not (stringp res))) "UNKNOWN")
+       (t res)))))
 
 (defun tramp-get-remote-gid-with-id (vec id-format)
   (tramp-send-command-and-read
 
 (defun tramp-get-remote-gid-with-id (vec id-format)
   (tramp-send-command-and-read
@@ -5313,17 +5404,20 @@ Return ATTR."
 
 (defun tramp-get-remote-gid (vec id-format)
   (with-tramp-connection-property vec (format "gid-%s" id-format)
 
 (defun tramp-get-remote-gid (vec id-format)
   (with-tramp-connection-property vec (format "gid-%s" id-format)
-    (let ((res (cond
-               ((tramp-get-remote-id vec)
-                (tramp-get-remote-gid-with-id vec id-format))
-               ((tramp-get-remote-perl vec)
-                (tramp-get-remote-gid-with-perl vec id-format))
-               ((tramp-get-remote-python vec)
-                (tramp-get-remote-gid-with-python vec id-format))
-               (t (tramp-error
-                   vec 'file-error "Cannot determine remote gid")))))
-      ;; The command might not always return a number.
-      (if (and (equal id-format 'integer) (not (integerp res))) -1 res))))
+    (let ((res
+          (ignore-errors
+            (cond
+             ((tramp-get-remote-id vec)
+              (tramp-get-remote-gid-with-id vec id-format))
+             ((tramp-get-remote-perl vec)
+              (tramp-get-remote-gid-with-perl vec id-format))
+             ((tramp-get-remote-python vec)
+              (tramp-get-remote-gid-with-python vec id-format))))))
+      ;; Ensure there is a valid result.
+      (cond
+       ((and (equal id-format 'integer) (not (integerp res))) -1)
+       ((and (equal id-format 'string) (not (stringp res))) "UNKNOWN")
+       (t res)))))
 
 ;; Some predefined connection properties.
 (defun tramp-get-inline-compress (vec prop size)
 
 ;; Some predefined connection properties.
 (defun tramp-get-inline-compress (vec prop size)