aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Albinus2018-12-06 16:11:27 +0100
committerMichael Albinus2018-12-06 16:11:27 +0100
commit7d33c775b245dc011f56559a8a776728888d7246 (patch)
treeaccacfc9926248054c0bf322969797d46e396f90
parent66b49fc1d522b8d2cce7e957a5c6e7a4f6c90e0f (diff)
downloademacs-7d33c775b245dc011f56559a8a776728888d7246.tar.gz
emacs-7d33c775b245dc011f56559a8a776728888d7246.zip
Add missing handler to tramp-rclone.el, improve robustness
* lisp/net/tramp-rclone.el (tramp-adb): Require. (tramp-rclone-file-name-handler-alist): Use `tramp-adb-handle-expand-file-name'. (tramp-rclone-flush-directory-cache): New defun, derived from `tramp-rclone-flush-mount'. (tramp-rclone-do-copy-or-rename-file) (tramp-rclone-handle-delete-directory) (tramp-rclone-handle-delete-file) (tramp-rclone-handle-make-directory): Use it. (tramp-rclone-handle-directory-files) (tramp-rclone-local-file-name): Use `tramp-compat-file-name-quoted-p', `tramp-compat-file-name-quote' and ´tramp-compat-file-name-unquote'. (tramp-rclone-handle-file-executable-p) (tramp-rclone-handle-file-readable-p): Cache result. (tramp-rclone-handle-file-name-all-completions) (tramp-rclone-mounted-p, tramp-rclone-remote-file-name) (tramp-rclone-maybe-open-connection): Rewrite. * test/lisp/net/tramp-tests.el (tramp--test-rclone-p): New defun. (tramp-test05-expand-file-name-relative) (tramp--test-special-characters): Use it.
-rw-r--r--lisp/net/tramp-rclone.el178
-rw-r--r--test/lisp/net/tramp-tests.el15
2 files changed, 133 insertions, 60 deletions
diff --git a/lisp/net/tramp-rclone.el b/lisp/net/tramp-rclone.el
index 6c01d7def1a..3f3cac8ebc2 100644
--- a/lisp/net/tramp-rclone.el
+++ b/lisp/net/tramp-rclone.el
@@ -39,6 +39,7 @@
39(require 'tramp) 39(require 'tramp)
40 40
41;; TODDDDDDDDDO: REPLACE 41;; TODDDDDDDDDO: REPLACE
42(require 'tramp-adb)
42(require 'tramp-gvfs) 43(require 'tramp-gvfs)
43 44
44;;;###tramp-autoload 45;;;###tramp-autoload
@@ -85,7 +86,7 @@
85 (dired-compress-file . ignore) 86 (dired-compress-file . ignore)
86 (dired-uncache . tramp-handle-dired-uncache) 87 (dired-uncache . tramp-handle-dired-uncache)
87 (exec-path . ignore) 88 (exec-path . ignore)
88 ;; `expand-file-name' performed by default handler. 89 (expand-file-name . tramp-adb-handle-expand-file-name)
89 (file-accessible-directory-p . tramp-handle-file-accessible-directory-p) 90 (file-accessible-directory-p . tramp-handle-file-accessible-directory-p)
90 (file-acl . ignore) 91 (file-acl . ignore)
91 (file-attributes . tramp-rclone-handle-file-attributes) 92 (file-attributes . tramp-rclone-handle-file-attributes)
@@ -258,7 +259,15 @@ file names."
258 (with-parsed-tramp-file-name filename v1 259 (with-parsed-tramp-file-name filename v1
259 (tramp-flush-file-properties 260 (tramp-flush-file-properties
260 v1 (file-name-directory v1-localname)) 261 v1 (file-name-directory v1-localname))
261 (tramp-flush-file-properties v1 v1-localname))) 262 (tramp-flush-file-properties v1 v1-localname)
263 (when (tramp-rclone-file-name-p filename)
264 (tramp-rclone-flush-directory-cache v1)
265 ;; The mount point's directory cache might need time
266 ;; to flush.
267 (while (file-exists-p filename)
268 (tramp-flush-file-properties
269 v1 (file-name-directory v1-localname))
270 (tramp-flush-file-properties v1 v1-localname)))))
262 271
263 (when t2 272 (when t2
264 (with-parsed-tramp-file-name newname v2 273 (with-parsed-tramp-file-name newname v2
@@ -266,7 +275,13 @@ file names."
266 v2 (file-name-directory v2-localname)) 275 v2 (file-name-directory v2-localname))
267 (tramp-flush-file-properties v2 v2-localname) 276 (tramp-flush-file-properties v2 v2-localname)
268 (when (tramp-rclone-file-name-p newname) 277 (when (tramp-rclone-file-name-p newname)
269 (tramp-rclone-flush-mount v2))))))))) 278 (tramp-rclone-flush-directory-cache v2)
279 ;; The mount point's directory cache might need time
280 ;; to flush.
281 (while (not (file-exists-p newname))
282 (tramp-flush-file-properties
283 v2 (file-name-directory v2-localname))
284 (tramp-flush-file-properties v2 v2-localname))))))))))
270 285
271(defun tramp-rclone-handle-copy-file 286(defun tramp-rclone-handle-copy-file
272 (filename newname &optional ok-if-already-exists keep-date 287 (filename newname &optional ok-if-already-exists keep-date
@@ -289,17 +304,18 @@ file names."
289 (directory &optional recursive trash) 304 (directory &optional recursive trash)
290 "Like `delete-directory' for Tramp files." 305 "Like `delete-directory' for Tramp files."
291 (with-parsed-tramp-file-name (expand-file-name directory) nil 306 (with-parsed-tramp-file-name (expand-file-name directory) nil
307 (delete-directory (tramp-rclone-local-file-name directory) recursive trash)
292 (tramp-flush-file-properties v (file-name-directory localname)) 308 (tramp-flush-file-properties v (file-name-directory localname))
293 (tramp-flush-directory-properties v localname) 309 (tramp-flush-directory-properties v localname)
294 (delete-directory 310 (tramp-rclone-flush-directory-cache v)))
295 (tramp-rclone-local-file-name directory) recursive trash)))
296 311
297(defun tramp-rclone-handle-delete-file (filename &optional trash) 312(defun tramp-rclone-handle-delete-file (filename &optional trash)
298 "Like `delete-file' for Tramp files." 313 "Like `delete-file' for Tramp files."
299 (with-parsed-tramp-file-name (expand-file-name filename) nil 314 (with-parsed-tramp-file-name (expand-file-name filename) nil
315 (delete-file (tramp-rclone-local-file-name filename) trash)
300 (tramp-flush-file-properties v (file-name-directory localname)) 316 (tramp-flush-file-properties v (file-name-directory localname))
301 (tramp-flush-file-properties v localname) 317 (tramp-flush-file-properties v localname)
302 (delete-file (tramp-rclone-local-file-name filename) trash))) 318 (tramp-rclone-flush-directory-cache v)))
303 319
304(defun tramp-rclone-handle-directory-files 320(defun tramp-rclone-handle-directory-files
305 (directory &optional full match nosort) 321 (directory &optional full match nosort)
@@ -312,11 +328,11 @@ file names."
312 (tramp-rclone-local-file-name directory) full match))) 328 (tramp-rclone-local-file-name directory) full match)))
313 ;; Massage the result. 329 ;; Massage the result.
314 (when full 330 (when full
315 (let* ((quoted (file-name-quoted-p directory)) 331 (let* ((quoted (tramp-compat-file-name-quoted-p directory))
316 (local 332 (local
317 (concat "^" (regexp-quote (tramp-rclone-mount-point v)))) 333 (concat "^" (regexp-quote (tramp-rclone-mount-point v))))
318 (remote 334 (remote
319 (funcall (if quoted 'file-name-quote 'identity) 335 (funcall (if quoted 'tramp-compat-file-name-quote 'identity)
320 (file-remote-p directory)))) 336 (file-remote-p directory))))
321 (setq result 337 (setq result
322 (mapcar 338 (mapcar
@@ -341,15 +357,32 @@ file names."
341 357
342(defun tramp-rclone-handle-file-executable-p (filename) 358(defun tramp-rclone-handle-file-executable-p (filename)
343 "Like `file-executable-p' for Tramp files." 359 "Like `file-executable-p' for Tramp files."
344 (file-executable-p (tramp-rclone-local-file-name filename))) 360 (with-parsed-tramp-file-name (expand-file-name filename) nil
361 (with-tramp-file-property v localname "file-executable-p"
362 (file-executable-p (tramp-rclone-local-file-name filename)))))
345 363
346(defun tramp-rclone-handle-file-name-all-completions (filename directory) 364(defun tramp-rclone-handle-file-name-all-completions (filename directory)
347 "Like `file-name-all-completions' for Tramp files." 365 "Like `file-name-all-completions' for Tramp files."
348 (file-name-all-completions filename (tramp-rclone-local-file-name directory))) 366 (all-completions
367 filename
368 (delete-dups
369 (append
370 (file-name-all-completions
371 filename (tramp-rclone-local-file-name directory))
372 ;; Some storage systems do not return "." and "..".
373 (let (result)
374 (dolist (item '(".." ".") result)
375 (when (string-prefix-p filename item)
376 (catch 'match
377 (dolist (elt completion-regexp-list)
378 (unless (string-match-p elt item) (throw 'match nil)))
379 (setq result (cons (concat item "/") result))))))))))
349 380
350(defun tramp-rclone-handle-file-readable-p (filename) 381(defun tramp-rclone-handle-file-readable-p (filename)
351 "Like `file-readable-p' for Tramp files." 382 "Like `file-readable-p' for Tramp files."
352 (file-readable-p (tramp-rclone-local-file-name filename))) 383 (with-parsed-tramp-file-name (expand-file-name filename) nil
384 (with-tramp-file-property v localname "file-readable-p"
385 (file-readable-p (tramp-rclone-local-file-name filename)))))
353 386
354(defun tramp-rclone-handle-file-system-info (filename) 387(defun tramp-rclone-handle-file-system-info (filename)
355 "Like `file-system-info' for Tramp files." 388 "Like `file-system-info' for Tramp files."
@@ -401,13 +434,14 @@ file names."
401(defun tramp-rclone-handle-make-directory (dir &optional parents) 434(defun tramp-rclone-handle-make-directory (dir &optional parents)
402 "Like `make-directory' for Tramp files." 435 "Like `make-directory' for Tramp files."
403 (with-parsed-tramp-file-name (expand-file-name dir) nil 436 (with-parsed-tramp-file-name (expand-file-name dir) nil
437 (make-directory (tramp-rclone-local-file-name dir) parents)
404 ;; When PARENTS is non-nil, DIR could be a chain of non-existent 438 ;; When PARENTS is non-nil, DIR could be a chain of non-existent
405 ;; directories a/b/c/... Instead of checking, we simply flush the 439 ;; directories a/b/c/... Instead of checking, we simply flush the
406 ;; whole cache. 440 ;; whole file cache.
407 (tramp-flush-file-properties v localname) 441 (tramp-flush-file-properties v localname)
408 (tramp-flush-directory-properties 442 (tramp-flush-directory-properties
409 v (if parents "/" (file-name-directory localname))) 443 v (if parents "/" (file-name-directory localname)))
410 (make-directory (tramp-rclone-local-file-name dir) parents))) 444 (tramp-rclone-flush-directory-cache v)))
411 445
412(defun tramp-rclone-handle-rename-file 446(defun tramp-rclone-handle-rename-file
413 (filename newname &optional ok-if-already-exists) 447 (filename newname &optional ok-if-already-exists)
@@ -436,24 +470,38 @@ file names."
436 470
437(defun tramp-rclone-mounted-p (vec) 471(defun tramp-rclone-mounted-p (vec)
438 "Check, whether storage system determined by VEC is mounted." 472 "Check, whether storage system determined by VEC is mounted."
439 (with-tramp-file-property vec "/" "mounted" 473 (when (tramp-get-connection-process vec)
440 (string-match 474 ;; We cannot use `with-connection-property', because we don't want
441 (format "^%s:" (regexp-quote (tramp-file-name-host vec))) 475 ;; to cache a nil result.
442 (shell-command-to-string "mount")))) 476 (or (tramp-get-connection-property
443 477 (tramp-get-connection-process vec) "mounted" nil)
444(defun tramp-rclone-flush-mount (vec) 478 (tramp-set-connection-property
479 (tramp-get-connection-process vec) "mounted"
480 (let* ((default-directory temporary-file-directory)
481 (mount (shell-command-to-string "mount -t fuse.rclone")))
482 (tramp-message vec 6 "%s" "mount -t fuse.rclone")
483 (tramp-message vec 6 "\n%s" mount)
484 (when (string-match
485 (format
486 "^\\(%s:\\S-*\\)" (regexp-quote (tramp-file-name-host vec)))
487 mount)
488 (match-string 1 mount)))))))
489
490(defun tramp-rclone-flush-directory-cache (vec)
445 "Flush directory cache of VEC mount." 491 "Flush directory cache of VEC mount."
446 (let ((rclone-pid 492 (let ((rclone-pid
447 ;; Identify rclone process. 493 ;; Identify rclone process.
448 (with-tramp-file-property vec "/" "rclone-pid" 494 (when (tramp-get-connection-process vec)
449 (catch 'pid 495 (with-tramp-connection-property
450 (dolist (pid (list-system-processes)) ;; "pidof rclone" ? 496 (tramp-get-connection-process vec) "rclone-pid"
451 (and (string-match 497 (catch 'pid
452 (regexp-quote 498 (dolist (pid (list-system-processes)) ;; "pidof rclone" ?
453 (format "rclone mount %s:" (tramp-file-name-host vec))) 499 (and (string-match-p
454 (or (cdr (assoc 'args (process-attributes pid))) "")) 500 (regexp-quote
455 (throw 'pid pid))))))) 501 (format "rclone mount %s:" (tramp-file-name-host vec)))
456 ;; Send a SIGHUP in order to flush directory caches. 502 (or (cdr (assoc 'args (process-attributes pid))) ""))
503 (throw 'pid pid))))))))
504 ;; Send a SIGHUP in order to flush directory cache.
457 (when rclone-pid 505 (when rclone-pid
458 (tramp-message 506 (tramp-message
459 vec 6 "Send SIGHUP %d: %s" 507 vec 6 "Send SIGHUP %d: %s"
@@ -462,15 +510,16 @@ file names."
462 510
463(defun tramp-rclone-local-file-name (filename) 511(defun tramp-rclone-local-file-name (filename)
464 "Return local mount name of FILENAME." 512 "Return local mount name of FILENAME."
465 (with-parsed-tramp-file-name (expand-file-name filename) nil 513 (setq filename (tramp-compat-file-name-unquote (expand-file-name filename)))
514 (with-parsed-tramp-file-name filename nil
466 ;; As long as we call `tramp-rclone-maybe-open-connection' here, 515 ;; As long as we call `tramp-rclone-maybe-open-connection' here,
467 ;; we cache the result. 516 ;; we cache the result.
468 (with-tramp-file-property v localname "local-file-name" 517 (with-tramp-file-property v localname "local-file-name"
469 (tramp-rclone-maybe-open-connection v) 518 (tramp-rclone-maybe-open-connection v)
470 (let ((quoted (file-name-quoted-p localname)) 519 (let ((quoted (tramp-compat-file-name-quoted-p localname))
471 (localname (file-name-unquote localname))) 520 (localname (tramp-compat-file-name-unquote localname)))
472 (funcall 521 (funcall
473 (if quoted 'file-name-quote 'identity) 522 (if quoted 'tramp-compat-file-name-quote 'identity)
474 (expand-file-name 523 (expand-file-name
475 (if (file-name-absolute-p localname) 524 (if (file-name-absolute-p localname)
476 (substring localname 1) localname) 525 (substring localname 1) localname)
@@ -478,43 +527,59 @@ file names."
478 527
479(defun tramp-rclone-remote-file-name (filename) 528(defun tramp-rclone-remote-file-name (filename)
480 "Return FILENAME as used in the `rclone' command." 529 "Return FILENAME as used in the `rclone' command."
481 (setq filename (file-name-unquote (expand-file-name filename))) 530 (setq filename (tramp-compat-file-name-unquote (expand-file-name filename)))
482 (if (tramp-rclone-file-name-p filename) 531 (if (tramp-rclone-file-name-p filename)
483 (with-parsed-tramp-file-name filename nil 532 (with-parsed-tramp-file-name filename nil
484 ;; TODO: This shall be handled by `expand-file-name'. 533 ;; As long as we call `tramp-rclone-maybe-open-connection' here,
485 (setq localname (replace-regexp-in-string "^\\." "" (or localname ""))) 534 ;; we cache the result.
486 (format "%s:%s" host localname)) 535 (with-tramp-file-property v localname "remote-file-name"
536 (tramp-rclone-maybe-open-connection v)
537 ;; TODO: This shall be handled by `expand-file-name'.
538 (setq localname
539 (replace-regexp-in-string "^\\." "" (or localname "")))
540 (format "%s%s" (tramp-rclone-mounted-p v) localname)))
541 ;; It is a local file name.
487 filename)) 542 filename))
488 543
489(defun tramp-rclone-maybe-open-connection (vec) 544(defun tramp-rclone-maybe-open-connection (vec)
490 "Maybe open a connection VEC. 545 "Maybe open a connection VEC.
491Does not do anything if a connection is already open, but re-opens the 546Does not do anything if a connection is already open, but re-opens the
492connection if a previous connection has died for some reason." 547connection if a previous connection has died for some reason."
493 (unless (tramp-rclone-mounted-p vec) 548 (let ((host (tramp-file-name-host vec)))
494 (let ((host (tramp-file-name-host vec))) 549 (when (rassoc `(,host) (tramp-rclone-parse-device-names nil))
495 (if (zerop (length host)) 550 (if (zerop (length host))
496 (tramp-error vec 'file-error "Storage %s not connected" host)) 551 (tramp-error vec 'file-error "Storage %s not connected" host))
497 (with-tramp-progress-reporter vec 3 "Mounting rclone storage" 552
498 (unless (file-directory-p (tramp-rclone-mount-point vec)) 553 ;; We need a process bound to the connection buffer. Therefore,
499 (make-directory (tramp-rclone-mount-point vec) 'parents)) 554 ;; we create a dummy process. Maybe there is a better solution?
500 (let* ((buf (tramp-get-connection-buffer vec)) 555 (unless (get-buffer-process (tramp-get-connection-buffer vec))
501 (coding-system-for-read 'utf-8-dos) ;is this correct? 556 (let ((p (make-network-process
502 (process-connection-type tramp-process-connection-type) 557 :name (tramp-buffer-name vec)
503 (args `("mount" ,(concat host ":") 558 :buffer (tramp-get-connection-buffer vec)
504 ,(tramp-rclone-mount-point vec) 559 :server t :host 'local :service t :noquery t)))
505 ,(tramp-get-method-parameter vec 'tramp-mount-args))) 560 (process-put p 'vector vec)
506 (p (let ((default-directory
507 (tramp-compat-temporary-file-directory)))
508 (apply 'start-process (tramp-get-connection-name vec) buf
509 tramp-rclone-program (delq nil args)))))
510 (tramp-set-file-property vec "/" "mounted" t)
511 (tramp-message
512 vec 6 "%s" (mapconcat 'identity (process-command p) " "))
513 (process-put p 'adjust-window-size-function 'ignore)
514 (set-process-query-on-exit-flag p nil) 561 (set-process-query-on-exit-flag p nil)
515 562
516 ;; Set connection-local variables. 563 ;; Set connection-local variables.
517 (tramp-set-connection-local-variables vec))))) 564 (tramp-set-connection-local-variables vec)))
565
566 ;; Create directory.
567 (unless (file-directory-p (tramp-rclone-mount-point vec))
568 (make-directory (tramp-rclone-mount-point vec) 'parents))
569
570 ;; Mount. This command does not return, so we use 0 as
571 ;; DESTINATION of `tramp-call-process'.
572 (unless (tramp-rclone-mounted-p vec)
573 (apply
574 'tramp-call-process
575 vec tramp-rclone-program nil 0 nil
576 (delq nil
577 `("mount" ,(concat host ":/")
578 ,(tramp-rclone-mount-point vec)
579 ;; This could be nil.
580 ,(tramp-get-method-parameter vec 'tramp-mount-args))))
581 (while (not (file-exists-p (tramp-make-tramp-file-name vec 'localname)))
582 (tramp-cleanup-connection vec 'keep-debug 'keep-password)))))
518 583
519 ;; In `tramp-check-cached-permissions', the connection properties 584 ;; In `tramp-check-cached-permissions', the connection properties
520 ;; {uig,gid}-{integer,string} are used. We set them to proper values. 585 ;; {uig,gid}-{integer,string} are used. We set them to proper values.
@@ -529,7 +594,6 @@ connection if a previous connection has died for some reason."
529 594
530(defun tramp-rclone-send-command (vec &rest args) 595(defun tramp-rclone-send-command (vec &rest args)
531 "Send the COMMAND to connection VEC." 596 "Send the COMMAND to connection VEC."
532; (tramp-rclone-maybe-open-connection vec)
533 (with-current-buffer (tramp-get-connection-buffer vec) 597 (with-current-buffer (tramp-get-connection-buffer vec)
534 (erase-buffer) 598 (erase-buffer)
535 (let ((flags (tramp-get-method-parameter 599 (let ((flags (tramp-get-method-parameter
diff --git a/test/lisp/net/tramp-tests.el b/test/lisp/net/tramp-tests.el
index 15a120704eb..1fcecb85ebe 100644
--- a/test/lisp/net/tramp-tests.el
+++ b/test/lisp/net/tramp-tests.el
@@ -1997,7 +1997,7 @@ handled properly. BODY shall not contain a timeout."
1997 (skip-unless (tramp--test-enabled)) 1997 (skip-unless (tramp--test-enabled))
1998 1998
1999 ;; These are the methods the test doesn't fail. 1999 ;; These are the methods the test doesn't fail.
2000 (when (or (tramp--test-adb-p) (tramp--test-gvfs-p) 2000 (when (or (tramp--test-adb-p) (tramp--test-gvfs-p) (tramp--test-rclone-p)
2001 (tramp-smb-file-name-p tramp-test-temporary-file-directory)) 2001 (tramp-smb-file-name-p tramp-test-temporary-file-directory))
2002 (setf (ert-test-expected-result-type 2002 (setf (ert-test-expected-result-type
2003 (ert-get-test 'tramp-test05-expand-file-name-relative)) 2003 (ert-get-test 'tramp-test05-expand-file-name-relative))
@@ -4551,6 +4551,11 @@ This does not support external Emacs calls."
4551 (string-equal 4551 (string-equal
4552 "nextcloud" (file-remote-p tramp-test-temporary-file-directory 'method))) 4552 "nextcloud" (file-remote-p tramp-test-temporary-file-directory 'method)))
4553 4553
4554(defun tramp--test-rclone-p ()
4555 "Check, whether the remote host is offered by rclone.
4556This requires restrictions of file name syntax."
4557 (tramp-rclone-file-name-p tramp-test-temporary-file-directory))
4558
4554(defun tramp--test-rsync-p () 4559(defun tramp--test-rsync-p ()
4555 "Check, whether the rsync method is used. 4560 "Check, whether the rsync method is used.
4556This does not support special file names." 4561This does not support special file names."
@@ -4755,7 +4760,9 @@ This requires restrictions of file name syntax."
4755 ;; expanded to <TAB>. 4760 ;; expanded to <TAB>.
4756 (let ((files 4761 (let ((files
4757 (list 4762 (list
4758 (if (or (tramp--test-gvfs-p) (tramp--test-windows-nt-or-smb-p)) 4763 (if (or (tramp--test-gvfs-p)
4764 (tramp--test-rclone-p)
4765 (tramp--test-windows-nt-or-smb-p))
4759 "foo bar baz" 4766 "foo bar baz"
4760 (if (or (tramp--test-adb-p) 4767 (if (or (tramp--test-adb-p)
4761 (tramp--test-docker-p) 4768 (tramp--test-docker-p)
@@ -4781,7 +4788,9 @@ This requires restrictions of file name syntax."
4781 (if (or (tramp--test-gvfs-p) (tramp--test-windows-nt-or-smb-p)) 4788 (if (or (tramp--test-gvfs-p) (tramp--test-windows-nt-or-smb-p))
4782 "!foo!bar!baz!" 4789 "!foo!bar!baz!"
4783 "!foo|bar!baz|") 4790 "!foo|bar!baz|")
4784 (if (or (tramp--test-gvfs-p) (tramp--test-windows-nt-or-smb-p)) 4791 (if (or (tramp--test-gvfs-p)
4792 (tramp--test-rclone-p)
4793 (tramp--test-windows-nt-or-smb-p))
4785 ";foo;bar;baz;" 4794 ";foo;bar;baz;"
4786 ":foo;bar:baz;") 4795 ":foo;bar:baz;")
4787 (unless (or (tramp--test-gvfs-p) (tramp--test-windows-nt-or-smb-p)) 4796 (unless (or (tramp--test-gvfs-p) (tramp--test-windows-nt-or-smb-p))