X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/a51e9ff76cc887e0e6df95ff2895d80e0c00e9b9..ce8c5107906ef282a1858c401c4400d2b54700b8:/lisp/net/tramp-adb.el diff --git a/lisp/net/tramp-adb.el b/lisp/net/tramp-adb.el index 0974c62c56..5388847321 100644 --- a/lisp/net/tramp-adb.el +++ b/lisp/net/tramp-adb.el @@ -1,6 +1,6 @@ ;;; tramp-adb.el --- Functions for calling Android Debug Bridge from Tramp -;; Copyright (C) 2011-2013 Free Software Foundation, Inc. +;; Copyright (C) 2011-2014 Free Software Foundation, Inc. ;; Author: Juergen Hoetzel ;; Keywords: comm, processes @@ -23,10 +23,11 @@ ;;; Commentary: -;; The Android Debug Bridge must be installed on your local machine. -;; Add the following form into your .emacs: +;; The Android Debug Bridge "adb" must be installed on your local +;; machine. If it is not in your $PATH, add the following form into +;; your .emacs: ;; -;; (setq tramp-adb-sdk-dir "/path/to/android/sdk") +;; (setq tramp-adb-program "/path/to/adb") ;; ;; Due to security it is not possible to access non-root devices. @@ -35,20 +36,21 @@ (require 'tramp) (require 'time-date) -(defvar dired-move-to-filename-regexp) +;; Pacify byte-compiler. +(defvar directory-sep-char) -(defcustom tramp-adb-sdk-dir "~/Android/sdk" - "Set to the directory containing the Android SDK." - :type 'string +(defcustom tramp-adb-program "adb" + "Name of the Android Debug Bridge program." + :group 'tramp :version "24.4" - :group 'tramp) + :type 'string) ;;;###tramp-autoload (defconst tramp-adb-method "adb" "*When this method name is used, forward all calls to Android Debug Bridge.") (defcustom tramp-adb-prompt - "^\\(?:[[:alnum:]]*@[[:alnum:]]*[^#\\$]*\\)?[#\\$][[:space:]]" + "^\\(?:[[:digit:]]*|?\\)?\\(?:[[:alnum:]]*@[[:alnum:]]*[^#\\$]*\\)?[#\\$][[:space:]]" "Regexp used as prompt in almquist shell." :type 'string :version "24.4" @@ -84,51 +86,74 @@ (cons 'tramp-adb-file-name-p 'tramp-adb-file-name-handler)) (defconst tramp-adb-file-name-handler-alist - '((directory-file-name . tramp-handle-directory-file-name) + '((access-file . ignore) + (add-name-to-file . tramp-adb-handle-copy-file) + ;; `byte-compiler-base-file-name' performed by default handler. + ;; `copy-directory' performed by default handler. + (copy-file . tramp-adb-handle-copy-file) + (delete-directory . tramp-adb-handle-delete-directory) + (delete-file . tramp-adb-handle-delete-file) + ;; `diff-latest-backup-file' performed by default handler. + (directory-file-name . tramp-handle-directory-file-name) + (directory-files . tramp-handle-directory-files) + (directory-files-and-attributes + . tramp-adb-handle-directory-files-and-attributes) + (dired-call-process . ignore) + (dired-compress-file . ignore) (dired-uncache . tramp-handle-dired-uncache) + (expand-file-name . tramp-adb-handle-expand-file-name) + (file-accessible-directory-p . tramp-handle-file-accessible-directory-p) + (file-acl . ignore) + (file-attributes . tramp-adb-handle-file-attributes) + (file-directory-p . tramp-adb-handle-file-directory-p) + ;; `file-equal-p' performed by default handler. + ;; FIXME: This is too sloppy. + (file-executable-p . tramp-handle-file-exists-p) + (file-exists-p . tramp-handle-file-exists-p) + ;; `file-in-directory-p' performed by default handler. + (file-local-copy . tramp-adb-handle-file-local-copy) + (file-modes . tramp-handle-file-modes) + (file-name-all-completions . tramp-adb-handle-file-name-all-completions) (file-name-as-directory . tramp-handle-file-name-as-directory) (file-name-completion . tramp-handle-file-name-completion) - (file-name-all-completions . tramp-adb-handle-file-name-all-completions) - (file-attributes . tramp-adb-handle-file-attributes) (file-name-directory . tramp-handle-file-name-directory) (file-name-nondirectory . tramp-handle-file-name-nondirectory) - (file-truename . tramp-adb-handle-file-truename) + ;; `file-name-sans-versions' performed by default handler. (file-newer-than-file-p . tramp-handle-file-newer-than-file-p) - (file-name-as-directory . tramp-handle-file-name-as-directory) + (file-notify-add-watch . tramp-handle-file-notify-add-watch) + (file-notify-rm-watch . tramp-handle-file-notify-rm-watch) + (file-ownership-preserved-p . ignore) + (file-readable-p . tramp-handle-file-exists-p) (file-regular-p . tramp-handle-file-regular-p) (file-remote-p . tramp-handle-file-remote-p) - (file-accessible-directory-p . tramp-handle-file-accessible-directory-p) - (file-directory-p . tramp-adb-handle-file-directory-p) + (file-selinux-context . ignore) (file-symlink-p . tramp-handle-file-symlink-p) - ;; FIXME: This is too sloppy. - (file-executable-p . tramp-handle-file-exists-p) - (file-exists-p . tramp-handle-file-exists-p) - (file-readable-p . tramp-handle-file-exists-p) + (file-truename . tramp-adb-handle-file-truename) (file-writable-p . tramp-adb-handle-file-writable-p) - (file-local-copy . tramp-adb-handle-file-local-copy) - (file-modes . tramp-handle-file-modes) - (expand-file-name . tramp-adb-handle-expand-file-name) (find-backup-file-name . tramp-handle-find-backup-file-name) - (directory-files . tramp-handle-directory-files) - (directory-files-and-attributes - . tramp-adb-handle-directory-files-and-attributes) - (make-directory . tramp-adb-handle-make-directory) - (delete-directory . tramp-adb-handle-delete-directory) - (delete-file . tramp-adb-handle-delete-file) - (load . tramp-handle-load) - (insert-directory . tramp-adb-handle-insert-directory) + ;; `find-file-noselect' performed by default handler. + ;; `get-file-buffer' performed by default handler. + (insert-directory . tramp-handle-insert-directory) (insert-file-contents . tramp-handle-insert-file-contents) - (substitute-in-file-name . tramp-handle-substitute-in-file-name) - (unhandled-file-name-directory . tramp-handle-unhandled-file-name-directory) - (vc-registered . ignore) ;no vc control files on Android devices - (write-region . tramp-adb-handle-write-region) + (load . tramp-handle-load) + (make-auto-save-file-name . tramp-handle-make-auto-save-file-name) + (make-directory . tramp-adb-handle-make-directory) + (make-directory-internal . ignore) + (make-symbolic-link . tramp-handle-make-symbolic-link) + (process-file . tramp-adb-handle-process-file) + (rename-file . tramp-adb-handle-rename-file) + (set-file-acl . ignore) (set-file-modes . tramp-adb-handle-set-file-modes) + (set-file-selinux-context . ignore) (set-file-times . tramp-adb-handle-set-file-times) - (copy-file . tramp-adb-handle-copy-file) - (rename-file . tramp-adb-handle-rename-file) - (process-file . tramp-adb-handle-process-file) + (set-visited-file-modtime . tramp-handle-set-visited-file-modtime) (shell-command . tramp-adb-handle-shell-command) - (start-file-process . tramp-adb-handle-start-file-process)) + (start-file-process . tramp-adb-handle-start-file-process) + (substitute-in-file-name . tramp-handle-substitute-in-file-name) + (unhandled-file-name-directory . tramp-handle-unhandled-file-name-directory) + (vc-registered . ignore) + (verify-visited-file-modtime . tramp-handle-verify-visited-file-modtime) + (write-region . tramp-adb-handle-write-region)) "Alist of handler functions for Tramp ADB method.") ;; It must be a `defsubst' in order to push the whole code into @@ -149,17 +174,20 @@ pass to the OPERATION." (save-match-data (apply (cdr fn) args)) (tramp-run-real-handler operation args)))) -;; This cannot be a constant, because `tramp-adb-sdk-dir' is customizable. -(defun tramp-adb-program () - "The Android Debug Bridge." - (expand-file-name "platform-tools/adb" tramp-adb-sdk-dir)) - ;;;###tramp-autoload -(defun tramp-adb-parse-device-names (ignore) +(defun tramp-adb-parse-device-names (_ignore) "Return a list of (nil host) tuples allowed to access." - (with-temp-buffer - (when (zerop (call-process (tramp-adb-program) nil t nil "devices")) - (let (result) + (with-timeout (10) + (with-temp-buffer + ;; `call-process' does not react on timer under MS Windows. + ;; That's why we use `start-process'. + (let ((p (start-process + tramp-adb-program (current-buffer) tramp-adb-program "devices")) + result) + (tramp-compat-set-process-query-on-exit-flag p nil) + (while (eq 'run (process-status p)) + (accept-process-output p 0.1)) + (accept-process-output p 0.1) (goto-char (point-min)) (while (search-forward-regexp "^\\(\\S-+\\)[[:space:]]+device$" nil t) (add-to-list 'result (list nil (match-string 1)))) @@ -198,100 +226,107 @@ pass to the OPERATION." ;; This is derived from `tramp-sh-handle-file-truename'. Maybe the ;; code could be shared? -(defun tramp-adb-handle-file-truename (filename &optional counter prev-dirs) +(defun tramp-adb-handle-file-truename (filename) "Like `file-truename' for Tramp files." - (with-parsed-tramp-file-name (expand-file-name filename) nil - (with-tramp-file-property v localname "file-truename" - (let ((result nil)) ; result steps in reverse order - (tramp-message v 4 "Finding true name for `%s'" filename) - (let* ((directory-sep-char ?/) - (steps (tramp-compat-split-string localname "/")) - (localnamedir (tramp-run-real-handler - 'file-name-as-directory (list localname))) - (is-dir (string= localname localnamedir)) - (thisstep nil) - (numchase 0) - ;; Don't make the following value larger than - ;; necessary. People expect an error message in a - ;; timely fashion when something is wrong; otherwise - ;; they might think that Emacs is hung. Of course, - ;; correctness has to come first. - (numchase-limit 20) - symlink-target) - (while (and steps (< numchase numchase-limit)) - (setq thisstep (pop steps)) - (tramp-message - v 5 "Check %s" - (mapconcat 'identity - (append '("") (reverse result) (list thisstep)) - "/")) - (setq symlink-target - (nth 0 (file-attributes - (tramp-make-tramp-file-name - method user host - (mapconcat 'identity - (append '("") - (reverse result) - (list thisstep)) - "/"))))) - (cond ((string= "." thisstep) - (tramp-message v 5 "Ignoring step `.'")) - ((string= ".." thisstep) - (tramp-message v 5 "Processing step `..'") - (pop result)) - ((stringp symlink-target) - ;; It's a symlink, follow it. - (tramp-message v 5 "Follow symlink to %s" symlink-target) - (setq numchase (1+ numchase)) - (when (file-name-absolute-p symlink-target) - (setq result nil)) - ;; If the symlink was absolute, we'll get a string - ;; like "/user@host:/some/target"; extract the - ;; "/some/target" part from it. - (when (tramp-tramp-file-p symlink-target) - (unless (tramp-equal-remote filename symlink-target) - (tramp-error - v 'file-error - "Symlink target `%s' on wrong host" symlink-target)) - (setq symlink-target localname)) - (setq steps - (append (tramp-compat-split-string - symlink-target "/") - steps))) - (t - ;; It's a file. - (setq result (cons thisstep result))))) - (when (>= numchase numchase-limit) - (tramp-error - v 'file-error - "Maximum number (%d) of symlinks exceeded" numchase-limit)) - (setq result (reverse result)) - ;; Combine list to form string. - (setq result - (if result - (mapconcat 'identity (cons "" result) "/") - "/")) - (when (and is-dir (or (string= "" result) - (not (string= (substring result -1) "/")))) - (setq result (concat result "/")))) - - (tramp-message v 4 "True name of `%s' is `%s'" filename result) - (tramp-make-tramp-file-name method user host result))))) + (format + "%s%s" + (with-parsed-tramp-file-name (expand-file-name filename) nil + (tramp-make-tramp-file-name + method user host + (with-tramp-file-property v localname "file-truename" + (let ((result nil)) ; result steps in reverse order + (tramp-message v 4 "Finding true name for `%s'" filename) + (let* ((directory-sep-char ?/) + (steps (tramp-compat-split-string localname "/")) + (localnamedir (tramp-run-real-handler + 'file-name-as-directory (list localname))) + (is-dir (string= localname localnamedir)) + (thisstep nil) + (numchase 0) + ;; Don't make the following value larger than + ;; necessary. People expect an error message in a + ;; timely fashion when something is wrong; otherwise + ;; they might think that Emacs is hung. Of course, + ;; correctness has to come first. + (numchase-limit 20) + symlink-target) + (while (and steps (< numchase numchase-limit)) + (setq thisstep (pop steps)) + (tramp-message + v 5 "Check %s" + (mapconcat 'identity + (append '("") (reverse result) (list thisstep)) + "/")) + (setq symlink-target + (nth 0 (file-attributes + (tramp-make-tramp-file-name + method user host + (mapconcat 'identity + (append '("") + (reverse result) + (list thisstep)) + "/"))))) + (cond ((string= "." thisstep) + (tramp-message v 5 "Ignoring step `.'")) + ((string= ".." thisstep) + (tramp-message v 5 "Processing step `..'") + (pop result)) + ((stringp symlink-target) + ;; It's a symlink, follow it. + (tramp-message v 5 "Follow symlink to %s" symlink-target) + (setq numchase (1+ numchase)) + (when (file-name-absolute-p symlink-target) + (setq result nil)) + ;; If the symlink was absolute, we'll get a string + ;; like "/user@host:/some/target"; extract the + ;; "/some/target" part from it. + (when (tramp-tramp-file-p symlink-target) + (unless (tramp-equal-remote filename symlink-target) + (tramp-error + v 'file-error + "Symlink target `%s' on wrong host" symlink-target)) + (setq symlink-target localname)) + (setq steps + (append (tramp-compat-split-string + symlink-target "/") + steps))) + (t + ;; It's a file. + (setq result (cons thisstep result))))) + (when (>= numchase numchase-limit) + (tramp-error + v 'file-error + "Maximum number (%d) of symlinks exceeded" numchase-limit)) + (setq result (reverse result)) + ;; Combine list to form string. + (setq result + (if result + (mapconcat 'identity (cons "" result) "/") + "/")) + (when (and is-dir (or (string= "" result) + (not (string= (substring result -1) "/")))) + (setq result (concat result "/")))) + + (tramp-message v 4 "True name of `%s' is `%s'" localname result) + result)))) + + ;; Preserve trailing "/". + (if (string-equal (file-name-nondirectory filename) "") "/" ""))) (defun tramp-adb-handle-file-attributes (filename &optional id-format) "Like `file-attributes' for Tramp files." (unless id-format (setq id-format 'integer)) - (ignore-errors - (with-parsed-tramp-file-name filename nil - (with-tramp-file-property - v localname (format "file-attributes-%s" id-format) - (tramp-adb-barf-unless-okay - v (format "%s -d -l %s" - (tramp-adb-get-ls-command v) - (tramp-shell-quote-argument localname)) "") - (with-current-buffer (tramp-get-buffer v) - (tramp-adb-sh-fix-ls-output) - (cdar (tramp-do-parse-file-attributes-with-ls v id-format))))))) + (with-parsed-tramp-file-name filename nil + (with-tramp-file-property + v localname (format "file-attributes-%s" id-format) + (and + (tramp-adb-send-command-and-check + v (format "%s -d -l %s" + (tramp-adb-get-ls-command v) + (tramp-shell-quote-argument localname))) + (with-current-buffer (tramp-get-buffer v) + (tramp-adb-sh-fix-ls-output) + (cdar (tramp-do-parse-file-attributes-with-ls v id-format))))))) (defun tramp-do-parse-file-attributes-with-ls (vec &optional id-format) "Parse `file-attributes' for Tramp files using the ls(1) command." @@ -338,11 +373,19 @@ pass to the OPERATION." (with-tramp-file-property v localname (format "directory-files-attributes-%s-%s-%s-%s" full match id-format nosort) - (tramp-adb-barf-unless-okay - v (format "%s -a -l %s" - (tramp-adb-get-ls-command v) - (tramp-shell-quote-argument localname)) "") (with-current-buffer (tramp-get-buffer v) + (when (tramp-adb-send-command-and-check + v (format "%s -a -l %s" + (tramp-adb-get-ls-command v) + (tramp-shell-quote-argument localname))) + ;; We insert also filename/. and filename/.., because "ls" doesn't. + (narrow-to-region (point) (point)) + (tramp-adb-send-command + v (format "%s -d -a -l %s %s" + (tramp-adb-get-ls-command v) + (concat (file-name-as-directory localname) ".") + (concat (file-name-as-directory localname) ".."))) + (widen)) (tramp-adb-sh-fix-ls-output) (let ((result (tramp-do-parse-file-attributes-with-ls v (or id-format 'integer)))) @@ -364,24 +407,13 @@ pass to the OPERATION." (defun tramp-adb-get-ls-command (vec) (with-tramp-connection-property vec "ls" (tramp-message vec 5 "Finding a suitable `ls' command") - (if (zerop (tramp-adb-command-exit-status - vec "ls --color=never -al /dev/null")) + (if (tramp-adb-send-command-and-check vec "ls --color=never -al /dev/null") ;; On CyanogenMod based system BusyBox is used and "ls" output ;; coloring is enabled by default. So we try to disable it ;; when possible. "ls --color=never" "ls"))) -(defun tramp-adb-get-toolbox (vec) - "Get shell toolbox implementation: `toolbox' for original distributions -or `busybox' for CyanogenMod based distributions" - (with-tramp-connection-property vec "toolbox" - (tramp-message vec 5 "Checking shell toolbox implementation") - (cond - ((zerop (tramp-adb-command-exit-status vec "busybox")) 'busybox) - ((zerop (tramp-adb-command-exit-status vec "toolbox")) 'toolbox) - (t 'unknown)))) - (defun tramp-adb--gnu-switches-to-ash (switches) "Almquist shell can't handle multiple arguments. @@ -389,9 +421,9 @@ Convert (\"-al\") to (\"-a\" \"-l\"). Remove arguments like \"--dired\"." (split-string (apply 'concat (mapcar (lambda (s) - (replace-regexp-in-string + (tramp-compat-replace-regexp-in-string "\\(.\\)" " -\\1" - (replace-regexp-in-string "^-" "" s))) + (tramp-compat-replace-regexp-in-string "^-" "" s))) ;; FIXME: Warning about removed switches (long and non-dash). (delq nil (mapcar @@ -399,35 +431,6 @@ Convert (\"-al\") to (\"-a\" \"-l\"). Remove arguments like \"--dired\"." (and (not (string-match "\\(^--\\|^[^-]\\)" s)) s)) switches)))))) -(defun tramp-adb-handle-insert-directory - (filename switches &optional wildcard full-directory-p) - "Like `insert-directory' for Tramp files." - (when (stringp switches) - (setq switches (tramp-adb--gnu-switches-to-ash (split-string switches)))) - (with-parsed-tramp-file-name (file-truename filename) nil - (with-current-buffer (tramp-get-buffer v) - (let ((name (tramp-shell-quote-argument (directory-file-name localname))) - (switch-d (member "-d" switches)) - (switch-t (member "-t" switches)) - (switches (mapconcat 'identity (remove "-t" switches) " "))) - (tramp-adb-barf-unless-okay - v (format "%s %s %s" (tramp-adb-get-ls-command v) switches name) - "Cannot insert directory listing: %s" filename) - (unless switch-d - ;; We insert also filename/. and filename/.., because "ls" doesn't. - (narrow-to-region (point) (point)) - (ignore-errors - (tramp-adb-barf-unless-okay - v (format "%s -d %s %s %s" - (tramp-adb-get-ls-command v) - switches - (concat (file-name-as-directory name) ".") - (concat (file-name-as-directory name) "..")) - "Cannot insert directory listing: %s" filename)) - (widen)) - (tramp-adb-sh-fix-ls-output switch-t))) - (insert-buffer-substring (tramp-get-buffer v)))) - (defun tramp-adb-sh-fix-ls-output (&optional sort-by-time) "Insert dummy 0 in empty size columns. Androids \"ls\" command doesn't insert size column for directories: @@ -471,9 +474,9 @@ Emacs dired can't find files." (defun tramp-adb-ls-output-name-less-p (a b) "Sort \"ls\" output by name, ascending." (let (posa posb) - (string-match dired-move-to-filename-regexp a) + (string-match directory-listing-before-filename-regexp a) (setq posa (match-end 0)) - (string-match dired-move-to-filename-regexp b) + (string-match directory-listing-before-filename-regexp b) (setq posb (match-end 0)) (string-lessp (substring a posa) (substring b posb)))) @@ -488,7 +491,8 @@ Emacs dired can't find files." (tramp-adb-barf-unless-okay v (format "mkdir %s" (tramp-shell-quote-argument localname)) "Couldn't make directory %s" dir) - (tramp-flush-directory-property v (file-name-directory localname)))) + (tramp-flush-file-property v (file-name-directory localname)) + (tramp-flush-directory-property v localname))) (defun tramp-adb-handle-delete-directory (directory &optional recursive) "Like `delete-directory' for Tramp files." @@ -502,7 +506,7 @@ Emacs dired can't find files." (tramp-shell-quote-argument localname)) "Couldn't delete %s" directory))) -(defun tramp-adb-handle-delete-file (filename &optional trash) +(defun tramp-adb-handle-delete-file (filename &optional _trash) "Like `delete-file' for Tramp files." (setq filename (expand-file-name filename)) (with-parsed-tramp-file-name filename nil @@ -520,20 +524,22 @@ Emacs dired can't find files." (with-tramp-file-property v localname "file-name-all-completions" (save-match-data (tramp-adb-send-command - v (format "%s %s" + v (format "%s -a %s" (tramp-adb-get-ls-command v) (tramp-shell-quote-argument localname))) (mapcar (lambda (f) - (if (file-directory-p f) + (if (file-directory-p (expand-file-name f directory)) (file-name-as-directory f) f)) (with-current-buffer (tramp-get-buffer v) - (delq - nil - (mapcar - (lambda (l) (and (not (string-match "^[[:space:]]*$" l)) l)) - (split-string (buffer-string) "\n")))))))))) + (append + '("." "..") + (delq + nil + (mapcar + (lambda (l) (and (not (string-match "^[[:space:]]*$" l)) l)) + (split-string (buffer-string) "\n"))))))))))) (defun tramp-adb-handle-file-local-copy (filename) "Like `file-local-copy' for Tramp files." @@ -549,7 +555,10 @@ Emacs dired can't find files." (delete-file tmpfile) (tramp-error v 'file-error "Cannot make local copy of file `%s'" filename)) - (set-file-modes tmpfile (file-modes filename))) + (set-file-modes + tmpfile + (logior (or (file-modes filename) 0) + (tramp-compat-octal-to-decimal "0400")))) tmpfile))) (defun tramp-adb-handle-file-writable-p (filename) @@ -559,9 +568,8 @@ But handle the case, if the \"test\" command is not available." (with-tramp-file-property v localname "file-writable-p" (if (tramp-adb-find-test-command v) (if (file-exists-p filename) - (zerop - (tramp-adb-command-exit-status - v (format "test -w %s" (tramp-shell-quote-argument localname)))) + (tramp-adb-send-command-and-check + v (format "test -w %s" (tramp-shell-quote-argument localname))) (and (file-directory-p (file-name-directory filename)) (file-writable-p (file-name-directory filename)))) @@ -581,9 +589,6 @@ But handle the case, if the \"test\" command is not available." "Like `write-region' for Tramp files." (setq filename (expand-file-name filename)) (with-parsed-tramp-file-name filename nil - (when append - (tramp-error - v 'file-error "Cannot append to file using Tramp (`%s')" filename)) (when (and confirm (file-exists-p filename)) (unless (y-or-n-p (format "File %s exists; overwrite anyway? " filename)) @@ -594,6 +599,12 @@ But handle the case, if the \"test\" command is not available." (tramp-flush-file-property v localname) (let* ((curbuf (current-buffer)) (tmpfile (tramp-compat-make-temp-file filename))) + (when (and append (file-exists-p filename)) + (copy-file filename tmpfile 'ok) + (set-file-modes + tmpfile + (logior (or (file-modes tmpfile) 0) + (tramp-compat-octal-to-decimal "0600")))) (tramp-run-real-handler 'write-region (list start end tmpfile append 'no-message lockname confirm)) @@ -604,6 +615,9 @@ But handle the case, if the \"test\" command is not available." (tramp-error v 'file-error "Cannot write: `%s' filename")) (delete-file tmpfile))) + (when (or (eq visit t) (stringp visit)) + (set-visited-file-modtime)) + (unless (equal curbuf (current-buffer)) (tramp-error v 'file-error @@ -613,9 +627,8 @@ But handle the case, if the \"test\" command is not available." "Like `set-file-modes' for Tramp files." (with-parsed-tramp-file-name filename nil (tramp-flush-file-property v localname) - (tramp-adb-barf-unless-okay - v (format "chmod %s %s" (tramp-compat-decimal-to-octal mode) localname) - "Error while changing file's mode %s" filename))) + (tramp-adb-send-command-and-check + v (format "chmod %s %s" (tramp-compat-decimal-to-octal mode) localname)))) (defun tramp-adb-handle-set-file-times (filename &optional time) "Like `set-file-times' for Tramp files." @@ -624,15 +637,15 @@ But handle the case, if the \"test\" command is not available." (let ((time (if (or (null time) (equal time '(0 0))) (current-time) time))) - (tramp-adb-command-exit-status - ;; use shell arithmetic because of Emacs integer size limit + (tramp-adb-send-command-and-check + ;; Use shell arithmetic because of Emacs integer size limit. v (format "touch -t $(( %d * 65536 + %d )) %s" (car time) (cadr time) (tramp-shell-quote-argument localname)))))) (defun tramp-adb-handle-copy-file (filename newname &optional ok-if-already-exists keep-date - preserve-uid-gid preserve-extended-attributes) + _preserve-uid-gid _preserve-extended-attributes) "Like `copy-file' for Tramp files. PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are completely ignored." (setq filename (expand-file-name filename) @@ -641,7 +654,8 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are completely ignored." (if (file-directory-p filename) (tramp-file-name-handler 'copy-directory filename newname keep-date t) (with-tramp-progress-reporter - (tramp-dissect-file-name (if (file-remote-p filename) filename newname)) + (tramp-dissect-file-name + (if (tramp-tramp-file-p filename) filename newname)) 0 (format "Copying %s to %s" filename newname) (let ((tmpfile (file-local-copy filename))) @@ -682,32 +696,36 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are completely ignored." (setq filename (expand-file-name filename) newname (expand-file-name newname)) - (with-parsed-tramp-file-name - (if (file-remote-p filename) filename newname) nil - (with-tramp-progress-reporter - v 0 (format "Renaming %s to %s" newname filename) - - (if (and (tramp-equal-remote filename newname) - (not (file-directory-p filename))) - (progn - (when (and (not ok-if-already-exists) - (file-exists-p newname)) - (tramp-error v 'file-already-exists newname)) - ;; We must also flush the cache of the directory, because - ;; `file-attributes' reads the values from there. - (tramp-flush-file-property v (file-name-directory localname)) - (tramp-flush-file-property v localname) - ;; Short track. - (tramp-adb-barf-unless-okay - v (format - "mv %s %s" - (tramp-file-name-handler 'file-remote-p filename 'localname) - localname) - "Error renaming %s to %s" filename newname)) - - ;; Rename by copy. - (copy-file filename newname ok-if-already-exists t t) - (delete-file filename))))) + (let ((t1 (tramp-tramp-file-p filename)) + (t2 (tramp-tramp-file-p newname))) + (with-parsed-tramp-file-name (if t1 filename newname) nil + (with-tramp-progress-reporter + v 0 (format "Renaming %s to %s" filename newname) + + (if (and t1 t2 + (tramp-equal-remote filename newname) + (not (file-directory-p filename))) + (let ((l1 (tramp-file-name-handler + 'file-remote-p filename 'localname)) + (l2 (tramp-file-name-handler + 'file-remote-p newname 'localname))) + (when (and (not ok-if-already-exists) + (file-exists-p newname)) + (tramp-error v 'file-already-exists newname)) + ;; We must also flush the cache of the directory, because + ;; `file-attributes' reads the values from there. + (tramp-flush-file-property v (file-name-directory l1)) + (tramp-flush-file-property v l1) + (tramp-flush-file-property v (file-name-directory l2)) + (tramp-flush-file-property v l2) + ;; Short track. + (tramp-adb-barf-unless-okay + v (format "mv %s %s" l1 l2) + "Error renaming %s to %s" filename newname)) + + ;; Rename by copy. + (copy-file filename newname ok-if-already-exists t t) + (delete-file filename)))))) (defun tramp-adb-handle-process-file (program &optional infile destination display &rest args) @@ -778,13 +796,11 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are completely ignored." ;; directory. (condition-case nil (progn - (setq ret 0 - ret - (tramp-adb-barf-unless-okay - v (format "(cd %s; %s)" - (tramp-shell-quote-argument localname) - command) - "")) + (setq ret 0) + (tramp-adb-barf-unless-okay + v (format "(cd %s; %s)" + (tramp-shell-quote-argument localname) command) + "") ;; We should show the output anyway. (when outbuf (with-current-buffer outbuf @@ -855,7 +871,7 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are completely ignored." (when p (if (yes-or-no-p "A command is running. Kill it? ") (ignore-errors (kill-process p)) - (error "Shell command in progress"))) + (tramp-user-error p "Shell command in progress"))) (if current-buffer-p (progn @@ -981,17 +997,16 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are completely ignored." (setq args (append (list "-s" (tramp-file-name-host vec)) args))) (with-temp-buffer (prog1 - (unless (zerop (apply 'call-process (tramp-adb-program) nil t nil args)) + (unless + (zerop (apply 'tramp-call-process tramp-adb-program nil t nil args)) (buffer-string)) - (tramp-message - vec 6 "%s %s\n%s" - (tramp-adb-program) (mapconcat 'identity args " ") (buffer-string))))) + (tramp-message vec 6 "%s" (buffer-string))))) (defun tramp-adb-find-test-command (vec) "Checks, whether the ash has a builtin \"test\" command. This happens for Android >= 4.0." (with-tramp-connection-property vec "test" - (zerop (tramp-adb-command-exit-status vec "type test")))) + (tramp-adb-send-command-and-check vec "type test"))) ;; Connection functions @@ -1014,34 +1029,31 @@ This happens for Android >= 4.0." (while (re-search-forward "\r+$" nil t) (replace-match "" nil nil))))) -(defun tramp-adb-barf-unless-okay (vec command fmt &rest args) - "Run COMMAND, check exit status, throw error if exit status not okay. -FMT and ARGS are passed to `error'." - (tramp-adb-send-command vec (format "%s; echo tramp_exit_status $?" command)) - (with-current-buffer (tramp-get-connection-buffer vec) - (goto-char (point-max)) - (unless (re-search-backward "tramp_exit_status [0-9]+" nil t) - (tramp-error - vec 'file-error "Couldn't find exit status of `%s'" command)) - (skip-chars-forward "^ ") - (unless (zerop (read (current-buffer))) - (apply 'tramp-error vec 'file-error fmt args)) - (let (buffer-read-only) - (delete-region (match-beginning 0) (point-max))))) - -(defun tramp-adb-command-exit-status +(defun tramp-adb-send-command-and-check (vec command) - "Run COMMAND and return its exit status. + "Run COMMAND and and check its exit status. Sends `echo $?' along with the COMMAND for checking the exit status. If COMMAND is nil, just sends `echo $?'. Returns the exit status found." - (tramp-adb-send-command vec (format "%s; echo tramp_exit_status $?" command)) + (tramp-adb-send-command + vec (if command + (format "%s; echo tramp_exit_status $?" command) + "echo tramp_exit_status $?")) (with-current-buffer (tramp-get-connection-buffer vec) (goto-char (point-max)) (unless (re-search-backward "tramp_exit_status [0-9]+" nil t) (tramp-error vec 'file-error "Couldn't find exit status of `%s'" command)) (skip-chars-forward "^ ") - (read (current-buffer)))) + (prog1 + (zerop (read (current-buffer))) + (let (buffer-read-only) + (delete-region (match-beginning 0) (point-max)))))) + +(defun tramp-adb-barf-unless-okay (vec command fmt &rest args) + "Run COMMAND, check exit status, throw error if exit status not okay. +FMT and ARGS are passed to `error'." + (unless (tramp-adb-send-command-and-check vec command) + (apply 'tramp-error vec 'file-error fmt args))) (defun tramp-adb-wait-for-output (proc &optional timeout) "Wait for output from remote command." @@ -1077,14 +1089,28 @@ COMMAND is nil, just sends `echo $?'. Returns the exit status found." "Maybe open a connection VEC. Does not do anything if a connection is already open, but re-opens the connection if a previous connection has died for some reason." + (tramp-check-proper-method-and-host vec) + (let* ((buf (tramp-get-connection-buffer vec)) (p (get-buffer-process buf)) (host (tramp-file-name-host vec)) - (devices (mapcar 'cadr (tramp-adb-parse-device-names nil)))) + (user (tramp-file-name-user vec)) + devices) + + ;; Maybe we know already that "su" is not supported. We cannot + ;; use a connection property, because we have not checked yet + ;; whether it is still the same device. + (when (and user (not (tramp-get-file-property vec "" "su-command-p" t))) + (tramp-error vec 'file-error "Cannot switch to user `%s'" user)) + (unless (and p (processp p) (memq (process-status p) '(run open))) (save-match-data (when (and p (processp p)) (delete-process p)) + (setq tramp-current-method (tramp-file-name-method vec) + tramp-current-user (tramp-file-name-user vec) + tramp-current-host (tramp-file-name-host vec) + devices (mapcar 'cadr (tramp-adb-parse-device-names nil))) (if (not devices) (tramp-error vec 'file-error "No device connected")) (if (and (> (length host) 0) (not (member host devices))) @@ -1102,13 +1128,14 @@ connection if a previous connection has died for some reason." (p (let ((default-directory (tramp-compat-temporary-file-directory))) (apply 'start-process (tramp-get-connection-name vec) buf - (tramp-adb-program) args)))) + tramp-adb-program args)))) (tramp-message vec 6 "%s" (mapconcat 'identity (process-command p) " ")) ;; Wait for initial prompt. - (tramp-adb-wait-for-output p) + (tramp-adb-wait-for-output p 30) (unless (eq 'run (process-status p)) (tramp-error vec 'file-error "Terminated!")) + (tramp-set-connection-property p "vector" vec) (tramp-compat-set-process-query-on-exit-flag p nil) ;; Check whether the properties have been changed. If @@ -1128,13 +1155,22 @@ connection if a previous connection has died for some reason." (read (current-buffer)))))) (when (and (stringp old-getprop) (not (string-equal old-getprop new-getprop))) - (tramp-cleanup vec) (tramp-message vec 3 "Connection reset, because remote host changed from `%s' to `%s'" old-getprop new-getprop) + (tramp-cleanup-connection vec t) (tramp-adb-maybe-open-connection vec))) + ;; Change user if indicated. + (when user + (tramp-adb-send-command vec (format "su %s" user)) + (unless (tramp-adb-send-command-and-check vec nil) + (delete-process p) + (tramp-set-file-property vec "" "su-command-p" nil) + (tramp-error + vec 'file-error "Cannot switch to user `%s'" user))) + ;; Set "remote-path" connection property. This is needed ;; for eshell. (tramp-adb-send-command vec "echo \\\"$PATH\\\"") @@ -1147,5 +1183,9 @@ connection if a previous connection has died for some reason." (read (current-buffer))) ":" 'omit-nulls)))))))) +(add-hook 'tramp-unload-hook + (lambda () + (unload-feature 'tramp-adb 'force))) + (provide 'tramp-adb) ;;; tramp-adb.el ends here