1
0
mirror of https://git.savannah.gnu.org/git/emacs.git synced 2024-12-29 11:02:01 +00:00
emacs/lisp/net/tramp-archive.el

667 lines
28 KiB
EmacsLisp
Raw Normal View History

;;; tramp-archive.el --- Tramp archive manager -*- lexical-binding:t -*-
;; Copyright (C) 2017-2019 Free Software Foundation, Inc.
;; Author: Michael Albinus <michael.albinus@gmx.de>
;; Keywords: comm, processes
;; Package: tramp
;; This file is part of GNU Emacs.
;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; Access functions for file archives. This is possible only on
;; machines which have installed the virtual file system for the Gnome
;; Desktop (GVFS). Internally, file archives are mounted via the GVFS
;; "archive" method.
;; A file archive is a regular file of kind "/path/to/dir/file.EXT".
;; The extension ".EXT" identifies the type of the file archive. A
;; file inside a file archive, called archive file name, has the name
;; "/path/to/dir/file.EXT/dir/file".
;; Most of the magic file name operations are implemented for archive
;; file names, exceptions are all operations which write into a file
;; archive, and process related operations. Therefore, functions like
;; (copy-file "/path/to/dir/file.tar/dir/file" "/somewhere/else")
;; work out of the box. This is also true for file name completion,
;; and for libraries like `dired' or `ediff', which accept archive
;; file names as well.
;; File archives are identified by the file name extension ".EXT".
;; Since GVFS uses internally the library libarchive(3), all suffixes,
;; which are accepted by this library, work also for archive file
;; names. Accepted suffixes are listed in the constant
;; `tramp-archive-suffixes'. They are
;; * ".7z" - 7-Zip archives
;; * ".apk" - Android package kits
;; * ".ar" - UNIX archiver formats
;; * ".cab", ".CAB" - Microsoft Windows cabinets
;; * ".cpio" - CPIO archives
;; * ".deb" - Debian packages
;; * ".depot" - HP-UX SD depots
;; * ".exe" - Self extracting Microsoft Windows EXE files
;; * ".iso" - ISO 9660 images
;; * ".jar" - Java archives
;; * ".lzh", ".LZH" - Microsoft Windows compressed LHA archives
;; * ".msu", ".MSU" - Microsoft Windows Update packages
;; * ".mtree" - BSD mtree format
;; * ".odb" ".odf" ".odg" ".odp" ".ods" ".odt" - OpenDocument formats
;; * ".pax" - Posix archives
;; * ".rar" - RAR archives
;; * ".rpm" - Red Hat packages
;; * ".shar" - Shell archives
;; * ".tar", ".tbz", ".tgz", ".tlz", ".txz" - (Compressed) tape archives
;; * ".warc" - Web archives
;; * ".xar" - macOS XAR archives
;; * ".xpi" - XPInstall Mozilla addons
;; * ".xps" - Open XML Paper Specification (OpenXPS) documents
;; * ".zip", ".ZIP" - ZIP archives
;; File archives could also be compressed, identified by an additional
;; compression suffix. Valid compression suffixes are listed in the
;; constant `tramp-archive-compression-suffixes'. They are ".bz2",
;; ".gz", ".lrz", ".lz", ".lz4", ".lzma", ".lzo", ".uu", ".xz" and
;; ".Z". A valid archive file name would be
;; "/path/to/dir/file.tar.gz/dir/file". Even several suffixes in a
;; row are possible, like "/path/to/dir/file.tar.gz.uu/dir/file".
;; An archive file name could be a remote file name, as in
;; "/ftp:anonymous@ftp.gnu.org:/gnu/tramp/tramp-2.3.2.tar.gz/INSTALL".
;; Since all file operations are mapped internally to GVFS operations,
;; remote file names supported by tramp-gvfs.el perform better,
;; because no local copy of the file archive must be downloaded first.
;; For example, "/sftp:user@host:..." performs better than the similar
;; "/scp:user@host:...". See the constant
;; `tramp-archive-all-gvfs-methods' for a complete list of
;; tramp-gvfs.el supported method names.
;; If `url-handler-mode' is enabled, archives could be visited via
;; URLs, like "https://ftp.gnu.org/gnu/tramp/tramp-2.3.2.tar.gz/INSTALL".
;; This allows complex file operations like
;; (ediff-directories
;; "https://ftp.gnu.org/gnu/tramp/tramp-2.3.1.tar.gz/tramp-2.3.1"
;; "https://ftp.gnu.org/gnu/tramp/tramp-2.3.2.tar.gz/tramp-2.3.2" "")
;; It is even possible to access file archives in file archives, as
;; (find-file
;; "http://ftp.debian.org/debian/pool/main/c/coreutils/coreutils_8.28-1_amd64.deb/control.tar.gz/control")
;;; Code:
(eval-when-compile (require 'cl-lib))
;; Sometimes, compilation fails with "Variable binding depth exceeds
;; max-specpdl-size".
(eval-and-compile
(let ((max-specpdl-size (* 2 max-specpdl-size))) (require 'tramp-gvfs)))
(autoload 'dired-uncache "dired")
(autoload 'url-tramp-convert-url-to-tramp "url-tramp")
(defvar url-handler-mode-hook)
(defvar url-handler-regexp)
(defvar url-tramp-protocols)
;; We cannot check `tramp-gvfs-enabled' in loaddefs.el, because this
;; would load Tramp. So we make a cheaper check.
;;;###autoload
(defvar tramp-archive-enabled (featurep 'dbusbind)
"Non-nil when file archive support is available.")
;; After loading tramp-gvfs.el, we know it better.
(setq tramp-archive-enabled tramp-gvfs-enabled)
;; <https://github.com/libarchive/libarchive/wiki/LibarchiveFormats>
2019-05-18 09:11:23 +00:00
;; Note: "arc" and "zoo" are supported by `archive-mode', but they
;; don't work here.
;;;###autoload
(defconst tramp-archive-suffixes
;; "cab", "lzh", "msu" and "zip" are included with lower and upper
;; letters, because Microsoft Windows provides them often with
;; capital letters.
'("7z" ;; 7-Zip archives.
"apk" ;; Android package kits. Not in libarchive testsuite.
"ar" ;; UNIX archiver formats.
"cab" "CAB" ;; Microsoft Windows cabinets.
"cpio" ;; CPIO archives.
"deb" ;; Debian packages. Not in libarchive testsuite.
"depot" ;; HP-UX SD depot. Not in libarchive testsuite.
"exe" ;; Self extracting Microsoft Windows EXE files.
"iso" ;; ISO 9660 images.
"jar" ;; Java archives. Not in libarchive testsuite.
"lzh" "LZH" ;; Microsoft Windows compressed LHA archives.
"msu" "MSU" ;; Microsoft Windows Update packages. Not in testsuite.
"mtree" ;; BSD mtree format.
"odb" "odf" "odg" "odp" "ods" "odt" ;; OpenDocument formats. Not in testsuite.
"pax" ;; Posix archives.
"rar" ;; RAR archives.
"rpm" ;; Red Hat packages.
"shar" ;; Shell archives. Not in libarchive testsuite.
"tar" "tbz" "tgz" "tlz" "txz" ;; (Compressed) tape archives.
"warc" ;; Web archives.
"xar" ;; macOS XAR archives. Not in libarchive testsuite.
"xpi" ;; XPInstall Mozilla addons. Not in libarchive testsuite.
"xps" ;; Open XML Paper Specification (OpenXPS) documents.
"zip" "ZIP") ;; ZIP archives.
"List of suffixes which indicate a file archive.
It must be supported by libarchive(3).")
;; <http://unix-memo.readthedocs.io/en/latest/vfs.html>
;; read and write: tar, cpio, pax , gzip , zip, bzip2, xz, lzip, lzma, ar, mtree, iso9660, compress.
;; read only: 7-Zip, mtree, xar, lha/lzh, rar, microsoft cab.
;;;###autoload
(defconst tramp-archive-compression-suffixes
'("bz2" "gz" "lrz" "lz" "lz4" "lzma" "lzo" "uu" "xz" "Z")
"List of suffixes which indicate a compressed file.
It must be supported by libarchive(3).")
;; The definition of `tramp-archive-file-name-regexp' contains calls
;; to `regexp-opt', which cannot be autoloaded while loading
;; loaddefs.el. So we use a macro, which is evaluated only when needed.
;;;###autoload
(progn (defmacro tramp-archive-autoload-file-name-regexp ()
"Regular expression matching archive file names."
Replace insignificant backquotes Replace most insignificant occurrences of '`' with a straight quote, sharp quote or nothing. This includes backquotes in 'pcase' patterns. * admin/admin.el: * lisp/apropos.el: * lisp/arc-mode.el: * lisp/auth-source.el: * lisp/avoid.el: * lisp/bindings.el: * lisp/bs.el: * lisp/calculator.el: * lisp/calendar/todo-mode.el: * lisp/cedet/semantic.el: * lisp/cedet/semantic/analyze/debug.el: * lisp/cedet/semantic/bovine.el: * lisp/cedet/semantic/dep.el: * lisp/cedet/semantic/grammar.el: * lisp/cedet/semantic/wisent/comp.el: * lisp/cedet/semantic/wisent/grammar.el: * lisp/cedet/srecode/mode.el: * lisp/cus-edit.el: * lisp/doc-view.el: * lisp/elec-pair.el: * lisp/electric.el: * lisp/emacs-lisp/autoload.el: * lisp/emacs-lisp/benchmark.el: * lisp/emacs-lisp/byte-opt.el: * lisp/emacs-lisp/bytecomp.el: * lisp/emacs-lisp/cconv.el: * lisp/emacs-lisp/cl-extra.el: * lisp/emacs-lisp/cl-generic.el: * lisp/emacs-lisp/cl-macs.el: * lisp/emacs-lisp/copyright.el: * lisp/emacs-lisp/debug.el: * lisp/emacs-lisp/eieio-compat.el: * lisp/emacs-lisp/ert.el: * lisp/emacs-lisp/generator.el: * lisp/emacs-lisp/inline.el: * lisp/emacs-lisp/macroexp.el: * lisp/emacs-lisp/map.el: * lisp/emacs-lisp/package-x.el: * lisp/emacs-lisp/package.el: * lisp/emacs-lisp/radix-tree.el: * lisp/emacs-lisp/smie.el: * lisp/epa.el: * lisp/erc/erc-dcc.el: * lisp/erc/erc-track.el: * lisp/erc/erc.el: * lisp/eshell/em-ls.el: * lisp/eshell/esh-cmd.el: * lisp/files.el: * lisp/filesets.el: * lisp/font-lock.el: * lisp/frameset.el: * lisp/gnus/gnus-agent.el: * lisp/gnus/gnus-art.el: * lisp/gnus/gnus-cite.el: * lisp/gnus/gnus-group.el: * lisp/gnus/gnus-msg.el: * lisp/gnus/gnus-salt.el: * lisp/gnus/gnus-srvr.el: * lisp/gnus/gnus-sum.el: * lisp/gnus/gnus-topic.el: * lisp/gnus/gnus-util.el: * lisp/gnus/gnus.el: * lisp/gnus/message.el: * lisp/gnus/mm-util.el: * lisp/gnus/mml.el: * lisp/gnus/nnheader.el: * lisp/gnus/nnimap.el: * lisp/gnus/nnmairix.el: * lisp/gnus/spam.el: * lisp/hexl.el: * lisp/hi-lock.el: * lisp/ibuf-ext.el: * lisp/ibuffer.el: * lisp/ido.el: * lisp/info.el: * lisp/international/mule-cmds.el: * lisp/international/mule-util.el: * lisp/json.el: * lisp/jsonrpc.el: * lisp/language/cyrillic.el: * lisp/language/european.el: * lisp/language/georgian.el: * lisp/language/tibetan.el: * lisp/language/utf-8-lang.el: * lisp/language/vietnamese.el: * lisp/ldefs-boot.el: * lisp/mail/mail-extr.el: * lisp/man.el: * lisp/menu-bar.el: * lisp/mh-e/mh-acros.el: * lisp/mh-e/mh-folder.el: * lisp/mh-e/mh-mime.el: * lisp/mh-e/mh-show.el: * lisp/mh-e/mh-speed.el: * lisp/minibuffer.el: * lisp/mpc.el: * lisp/net/ange-ftp.el: * lisp/net/hmac-def.el: * lisp/net/newst-backend.el: * lisp/net/quickurl.el: * lisp/net/tramp-archive.el: * lisp/net/tramp-compat.el: * lisp/notifications.el: * lisp/obsolete/pgg-parse.el: * lisp/obsolete/vc-arch.el: * lisp/obsolete/xesam.el: * lisp/org/ob-C.el: * lisp/org/ob-core.el: * lisp/org/ob-exp.el: * lisp/org/ob-groovy.el: * lisp/org/ob-haskell.el: * lisp/org/ob-io.el: * lisp/org/ob-lisp.el: * lisp/org/ob-lob.el: * lisp/org/ob-lua.el: * lisp/org/ob-octave.el: * lisp/org/ob-perl.el: * lisp/org/ob-python.el: * lisp/org/ob-ref.el: * lisp/org/ob-ruby.el: * lisp/org/ob-sql.el: * lisp/org/org-agenda.el: * lisp/org/org-capture.el: * lisp/org/org-clock.el: * lisp/org/org-colview.el: * lisp/org/org-duration.el: * lisp/org/org-element.el: * lisp/org/org-entities.el: * lisp/org/org-gnus.el: * lisp/org/org-indent.el: * lisp/org/org-info.el: * lisp/org/org-inlinetask.el: * lisp/org/org-lint.el: * lisp/org/org-list.el: * lisp/org/org-mouse.el: * lisp/org/org-plot.el: * lisp/org/org-src.el: * lisp/org/org-table.el: * lisp/org/org.el: * lisp/org/ox-ascii.el: * lisp/org/ox-html.el: * lisp/org/ox-latex.el: * lisp/org/ox-man.el: * lisp/org/ox-md.el: * lisp/org/ox-org.el: * lisp/org/ox-publish.el: * lisp/org/ox-texinfo.el: * lisp/org/ox.el: * lisp/play/bubbles.el: * lisp/play/gamegrid.el: * lisp/progmodes/autoconf.el: * lisp/progmodes/cc-defs.el: * lisp/progmodes/cc-engine.el: * lisp/progmodes/cc-fonts.el: * lisp/progmodes/cc-langs.el: * lisp/progmodes/cperl-mode.el: * lisp/progmodes/ebrowse.el: * lisp/progmodes/elisp-mode.el: * lisp/progmodes/flymake-cc.el: * lisp/progmodes/flymake.el: * lisp/progmodes/fortran.el: * lisp/progmodes/grep.el: * lisp/progmodes/gud.el: * lisp/progmodes/idlwave.el: * lisp/progmodes/js.el: * lisp/progmodes/m4-mode.el: * lisp/progmodes/make-mode.el: * lisp/progmodes/mixal-mode.el: * lisp/progmodes/modula2.el: * lisp/progmodes/octave.el: * lisp/progmodes/opascal.el: * lisp/progmodes/prolog.el: * lisp/progmodes/ps-mode.el: * lisp/progmodes/python.el: * lisp/progmodes/ruby-mode.el: * lisp/progmodes/sh-script.el: * lisp/progmodes/sql.el: * lisp/progmodes/verilog-mode.el: * lisp/ps-mule.el: * lisp/rtree.el: * lisp/ruler-mode.el: * lisp/ses.el: * lisp/simple.el: * lisp/startup.el: * lisp/subr.el: * lisp/term/ns-win.el: * lisp/textmodes/bibtex.el: * lisp/textmodes/conf-mode.el: * lisp/textmodes/css-mode.el: * lisp/textmodes/refill.el: * lisp/textmodes/sgml-mode.el: * lisp/textmodes/tex-mode.el: * lisp/tutorial.el: * lisp/url/url-dav.el: * lisp/url/url-gw.el: * lisp/url/url-http.el: * lisp/url/url-methods.el: * lisp/url/url-privacy.el: * lisp/vc/cvs-status.el: * lisp/vc/diff-mode.el: * lisp/vc/ediff-init.el: * lisp/vc/ediff-ptch.el: * lisp/vc/log-edit.el: * lisp/vc/log-view.el: * lisp/vc/pcvs-info.el: * lisp/vc/pcvs.el: * lisp/vc/smerge-mode.el: * lisp/vc/vc-git.el: * lisp/vc/vc-hg.el: * lisp/vc/vc-mtn.el: * lisp/vc/vc-rcs.el: * lisp/whitespace.el: * lisp/window.el: * test/lisp/electric-tests.el: * test/lisp/emacs-lisp/cl-lib-tests.el: * test/lisp/emacs-lisp/ert-tests.el: * test/lisp/epg-tests.el: * test/lisp/jsonrpc-tests.el: * test/src/data-tests.el: * test/src/json-tests.el: Replace most insignificant backquotes.
2018-11-05 00:22:15 +00:00
'(concat
"\\`" "\\(" ".+" "\\."
;; Default suffixes ...
(regexp-opt tramp-archive-suffixes)
;; ... with compression.
"\\(?:" "\\." (regexp-opt tramp-archive-compression-suffixes) "\\)*"
"\\)" ;; \1
"\\(" "/" ".*" "\\)" "\\'"))) ;; \2
;; In older Emacsen (prior 27.1), `tramp-archive-autoload-file-name-regexp'
;; is not autoloaded. So we cannot expect it to be known in
;; tramp-loaddefs.el. But it exists, when tramp-archive.el is loaded.
;;;###tramp-autoload
(defconst tramp-archive-file-name-regexp
(ignore-errors (tramp-archive-autoload-file-name-regexp))
"Regular expression matching archive file names.")
;;;###tramp-autoload
(defconst tramp-archive-method "archive"
"Method name for archives in GVFS.")
(defconst tramp-archive-all-gvfs-methods
(cons tramp-archive-method
(let ((values (cdr (cadr (get 'tramp-gvfs-methods 'custom-type)))))
(setq values (mapcar #'last values)
values (mapcar #'car values))))
"List of all methods `tramp-gvfs-methods' offers.")
;; New handlers should be added here.
;;;###tramp-autoload
(defconst tramp-archive-file-name-handler-alist
'((access-file . tramp-archive-handle-access-file)
(add-name-to-file . tramp-archive-handle-not-implemented)
;; `byte-compiler-base-file-name' performed by default handler.
;; `copy-directory' performed by default handler.
(copy-file . tramp-archive-handle-copy-file)
(delete-directory . tramp-archive-handle-not-implemented)
(delete-file . tramp-archive-handle-not-implemented)
;; `diff-latest-backup-file' performed by default handler.
(directory-file-name . tramp-archive-handle-directory-file-name)
(directory-files . tramp-handle-directory-files)
(directory-files-and-attributes
. tramp-handle-directory-files-and-attributes)
(dired-compress-file . tramp-archive-handle-not-implemented)
(dired-uncache . tramp-archive-handle-dired-uncache)
Implement command completion in remote shells. (Bug#31704) * doc/lispref/files.texi (Locating Files): Describe optional argument REMOTE of `executable-find'. (Magic File Names): Add `exec-path'. * doc/lispref/processes.texi (Subprocess Creation): Describe function `exec-path'. * doc/misc/tramp.texi (Remote programs): Explain refresh of search paths by `tramp-cleanup-this-connection'. * etc/NEWS: Mention 'exec-path' and 'executable-find'. * lisp/files.el (exec-path): New defun. (executable-find): Add optional argument REMOTE. * lisp/shell.el (shell-completion-vars): Set `comint-file-name-prefix'. (shell--command-completion-data): Use `(exec-path)'. (Bug#31704) * lisp/net/ange-ftp.el (exec-path): * lisp/net/tramp.el (tramp-file-name-for-operation): * lisp/net/tramp-adb.el (tramp-adb-file-name-handler-alist): * lisp/net/tramp-archive.el (tramp-archive-file-name-handler-alist): * lisp/net/tramp-gvfs.el (tramp-gvfs-file-name-handler-alist): * lisp/net/tramp-sh.el (tramp-sh-file-name-handler-alist): * lisp/net/tramp-smb.el (tramp-smb-file-name-handler-alist) <exec-path>: Add handler. * lisp/net/tramp-adb.el (tramp-adb-handle-exec-path): New defun. (tramp-adb-maybe-open-connection): Do not set "remote-path" connection property. * lisp/net/tramp-compat.el (tramp-compat-exec-path): New defun. * lisp/net/tramp-sh.el (tramp-sh-handle-exec-path): New defun. * lisp/net/tramp.el (tramp-eshell-directory-change): Use it. * test/lisp/net/tramp-archive-tests.el (tramp-archive-test38-make-nearby-temp-file) (tramp-archive-test41-file-system-info) (tramp-archive-test43-auto-load) (tramp-archive-test43-delay-load): Rename. * test/lisp/net/tramp-tests.el (tramp-test34-exec-path): New test. (tramp-test36-make-auto-save-file-name) (tramp-test37-find-backup-file-name) (tramp-test38-make-nearby-temp-file) (tramp-test39-special-characters) (tramp-test39-special-characters-with-stat) (tramp-test39-special-characters-with-perl) (tramp-test39-special-characters-with-ls, tramp-test40-utf8) (tramp-test40-utf8-with-stat, tramp-test40-utf8-with-perl) (tramp-test40-utf8-with-ls, tramp-test41-file-system-info) (tramp-test42-asynchronous-requests, tramp-test43-auto-load) (tramp-test43-delay-load, tramp-test43-recursive-load) (tramp-test43-remote-load-path, tramp-test44-unload): Rename.
2018-06-20 10:13:56 +00:00
(exec-path . ignore)
;; `expand-file-name' performed by default handler.
(file-accessible-directory-p . tramp-handle-file-accessible-directory-p)
(file-acl . ignore)
(file-attributes . tramp-archive-handle-file-attributes)
(file-directory-p . tramp-handle-file-directory-p)
(file-equal-p . tramp-handle-file-equal-p)
(file-executable-p . tramp-archive-handle-file-executable-p)
(file-exists-p . tramp-handle-file-exists-p)
(file-in-directory-p . tramp-handle-file-in-directory-p)
(file-local-copy . tramp-archive-handle-file-local-copy)
(file-modes . tramp-handle-file-modes)
(file-name-all-completions . tramp-archive-handle-file-name-all-completions)
;; `file-name-as-directory' performed by default handler.
(file-name-case-insensitive-p . ignore)
(file-name-completion . tramp-handle-file-name-completion)
;; `file-name-directory' performed by default handler.
;; `file-name-nondirectory' performed by default handler.
;; `file-name-sans-versions' performed by default handler.
(file-newer-than-file-p . tramp-handle-file-newer-than-file-p)
(file-notify-add-watch . ignore)
(file-notify-rm-watch . ignore)
(file-notify-valid-p . ignore)
(file-ownership-preserved-p . ignore)
(file-readable-p . tramp-archive-handle-file-readable-p)
(file-regular-p . tramp-handle-file-regular-p)
;; `file-remote-p' performed by default handler.
(file-selinux-context . tramp-handle-file-selinux-context)
(file-symlink-p . tramp-handle-file-symlink-p)
(file-system-info . tramp-archive-handle-file-system-info)
(file-truename . tramp-archive-handle-file-truename)
(file-writable-p . ignore)
(find-backup-file-name . ignore)
;; `get-file-buffer' performed by default handler.
(insert-directory . tramp-archive-handle-insert-directory)
(insert-file-contents . tramp-archive-handle-insert-file-contents)
(load . tramp-archive-handle-load)
(make-auto-save-file-name . ignore)
(make-directory . tramp-archive-handle-not-implemented)
(make-directory-internal . tramp-archive-handle-not-implemented)
(make-nearby-temp-file . tramp-handle-make-nearby-temp-file)
(make-process . ignore)
(make-symbolic-link . tramp-archive-handle-not-implemented)
(process-file . ignore)
(rename-file . tramp-archive-handle-not-implemented)
(set-file-acl . ignore)
(set-file-modes . tramp-archive-handle-not-implemented)
(set-file-selinux-context . ignore)
(set-file-times . tramp-archive-handle-not-implemented)
(set-visited-file-modtime . tramp-handle-set-visited-file-modtime)
(shell-command . tramp-archive-handle-not-implemented)
(start-file-process . tramp-archive-handle-not-implemented)
;; `substitute-in-file-name' performed by default handler.
(temporary-file-directory . tramp-archive-handle-temporary-file-directory)
;; `tramp-set-file-uid-gid' performed by default handler.
(unhandled-file-name-directory . ignore)
(vc-registered . ignore)
(verify-visited-file-modtime . tramp-handle-verify-visited-file-modtime)
(write-region . tramp-archive-handle-not-implemented))
"Alist of handler functions for file archive method.
Operations not mentioned here will be handled by the default Emacs primitives.")
(defsubst tramp-archive-file-name-for-operation (operation &rest args)
"Like `tramp-file-name-for-operation', but for archive file name syntax."
(cl-letf (((symbol-function #'tramp-tramp-file-p)
#'tramp-archive-file-name-p))
(apply #'tramp-file-name-for-operation operation args)))
(defun tramp-archive-run-real-handler (operation args)
"Invoke normal file name handler for OPERATION.
First arg specifies the OPERATION, second arg is a list of arguments to
pass to the OPERATION."
(let* ((inhibit-file-name-handlers
`(tramp-archive-file-name-handler
.
,(and (eq inhibit-file-name-operation operation)
inhibit-file-name-handlers)))
(inhibit-file-name-operation operation))
(apply operation args)))
;;;###tramp-autoload
(defun tramp-archive-file-name-handler (operation &rest args)
"Invoke the file archive related OPERATION.
First arg specifies the OPERATION, second arg is a list of arguments to
pass to the OPERATION."
(if (not tramp-archive-enabled)
;; Unregister `tramp-archive-file-name-handler'.
(progn
(tramp-register-file-name-handlers)
(tramp-archive-run-real-handler operation args))
(let* ((filename (apply #'tramp-archive-file-name-for-operation
operation args))
(archive (tramp-archive-file-name-archive filename)))
;; `filename' could be a quoted file name. Or the file
;; archive could be a directory, see Bug#30293.
(if (or (null archive)
(tramp-archive-run-real-handler
#'file-directory-p (list archive)))
(tramp-archive-run-real-handler operation args)
;; Now run the handler.
(let ((tramp-methods (cons `(,tramp-archive-method) tramp-methods))
(tramp-gvfs-methods tramp-archive-all-gvfs-methods)
;; Set uid and gid. gvfsd-archive could do it, but it doesn't.
(tramp-unknown-id-integer (user-uid))
(tramp-unknown-id-string (user-login-name))
(fn (assoc operation tramp-archive-file-name-handler-alist)))
(when (eq (cdr fn) #'tramp-archive-handle-not-implemented)
(setq args (cons operation args)))
(if fn
(save-match-data (apply (cdr fn) args))
(tramp-archive-run-real-handler operation args)))))))
;;;###autoload
(defalias
'tramp-archive-autoload-file-name-handler #'tramp-autoload-file-name-handler)
;;;###autoload
(progn (defun tramp-register-archive-file-name-handler ()
"Add archive file name handler to `file-name-handler-alist'."
(when tramp-archive-enabled
(add-to-list 'file-name-handler-alist
(cons (tramp-archive-autoload-file-name-regexp)
#'tramp-archive-autoload-file-name-handler))
(put 'tramp-archive-autoload-file-name-handler 'safe-magic t))))
;;;###autoload
(progn
(add-hook 'after-init-hook #'tramp-register-archive-file-name-handler)
(add-hook
'tramp-archive-unload-hook
(lambda ()
(remove-hook
'after-init-hook #'tramp-register-archive-file-name-handler))))
;; In older Emacsen (prior 27.1), the autoload above does not exist.
;; So we call it again; it doesn't hurt.
(tramp-register-archive-file-name-handler)
;; Mark `operations' the handler is responsible for.
(put 'tramp-archive-file-name-handler 'operations
(mapcar #'car tramp-archive-file-name-handler-alist))
;; `tramp-archive-file-name-handler' must be placed before `url-file-handler'.
(when url-handler-mode (tramp-register-file-name-handlers))
(eval-after-load 'url-handler
'(progn
(add-hook 'url-handler-mode-hook #'tramp-register-file-name-handlers)
(add-hook
'tramp-archive-unload-hook
(lambda ()
(remove-hook
'url-handler-mode-hook #'tramp-register-file-name-handlers)))))
;; File name conversions.
(defun tramp-archive-file-name-p (name)
"Return t if NAME is a string with archive file name syntax."
(and (stringp name)
;; `tramp-archive-file-name-regexp' does not suppress quoted file names.
(not (tramp-compat-file-name-quoted-p name t))
Rework Tramp wrt string-match-p, looking-at-p, save-match-data * lisp/net/tramp.el (tramp-find-method, tramp-find-user) (tramp-find-host, tramp-dissect-file-name, tramp-make-tramp-file-name) (tramp-completion-make-tramp-file-name, tramp-debug-message) (tramp-message, tramp-progress-reporter-update) (tramp-set-completion-function) (tramp-rfn-eshadow-update-overlay) (tramp-find-file-name-coding-system-alist) (tramp-file-name-for-operation) (tramp-use-absolute-autoload-file-names) (tramp-get-completion-methods, tramp-get-completion-user-host) (tramp-handle-directory-files) (tramp-handle-file-name-case-insensitive-p) (tramp-handle-file-name-completion, tramp-handle-file-truename) (tramp-handle-insert-directory, tramp-handle-load) (tramp-handle-shell-command, tramp-action-yesno) (tramp-action-yn, tramp-process-actions) (tramp-mode-string-to-int, tramp-get-local-locale) (tramp-local-host-p): * lisp/net/tramp-adb.el (tramp-adb-handle-file-system-info) (tramp-adb-handle-directory-files-and-attributes) (tramp-adb--gnu-switches-to-ash, tramp-adb-sh-fix-ls-output) (tramp-adb-handle-file-name-all-completions) (tramp-adb-handle-shell-command) (tramp-adb-handle-start-file-process): * lisp/net/tramp-archive.el (tramp-archive-dissect-file-name): * lisp/net/tramp-cache.el (tramp-get-hash-table) (tramp-flush-directory-properties, tramp-flush-file-function): * lisp/net/tramp-cmds.el (tramp-reporter-dump-variable) (tramp-append-tramp-buffers): * lisp/net/tramp-compat.el (tramp-compat-process-running-p): * lisp/net/tramp-gvfs.el (tramp-gvfs-handle-expand-file-name) (tramp-gvfs-get-file-attributes) (tramp-gvfs-handle-file-attributes) (tramp-gvfs-monitor-process-filter) (tramp-gvfs-handler-mounted-unmounted) (tramp-gvfs-connection-mounted-p, tramp-gvfs-mount-spec-entry) (tramp-gvfs-mount-spec, tramp-gvfs-maybe-open-connection): * lisp/net/tramp-sh.el (tramp-do-file-attributes-with-ls) (tramp-do-file-attributes-with-stat) (tramp-sh-handle-file-selinux-context) (tramp-sh-handle-directory-files-and-attributes) (tramp-do-directory-files-and-attributes-with-stat) (tramp-sh-handle-file-name-all-completions) (tramp-sh-handle-dired-compress-file) (tramp-sh-handle-insert-directory) (tramp-sh-handle-expand-file-name) (tramp-sh-handle-start-file-process) (tramp-sh-handle-process-file, tramp-sh-handle-write-region) (tramp-sh-handle-file-notify-add-watch) (tramp-sh-gio-monitor-process-filter) (tramp-sh-gvfs-monitor-dir-process-filter) (tramp-sh-inotifywait-process-filter) (tramp-sh-handle-file-system-info, tramp-maybe-send-script) (tramp-find-executable, tramp-open-shell, tramp-find-shell) (tramp-open-connection-setup-interactive-shell) (tramp-find-inline-encoding, tramp-call-local-coding-command) (tramp-compute-multi-hops, tramp-maybe-open-connection) (tramp-convert-file-attributes) (tramp-make-copy-program-file-name, tramp-get-remote-locale) (tramp-get-test-nt-command, tramp-get-remote-stat) (tramp-get-inline-coding): * lisp/net/tramp-smb.el (tramp-smb-handle-directory-files) (tramp-smb-action-get-acl, tramp-smb-handle-file-attributes) (tramp-smb-handle-file-name-all-completions) (tramp-smb-handle-file-system-info) (tramp-smb-handle-file-writable-p) (tramp-smb-handle-insert-directory) (tramp-smb-handle-make-directory) (tramp-smb-handle-make-directory-internal) (tramp-smb-handle-start-file-process, tramp-smb-get-localname) (tramp-smb-read-file-entry): Use `string-match-p' and `looking-at-p'. Remove superfluous `save-match-data'. Apply `eval-when-compile' on constant concat data. * lisp/net/tramp-compat.el (tramp-compat-file-name-quoted-p-p): Use `string-prefix-p'. (tramp-compat-file-name-unquote): Do not use match data.
2018-12-06 15:00:05 +00:00
;; We cannot use `string-match-p', the matches are used.
(string-match tramp-archive-file-name-regexp name)
t))
(defun tramp-archive-file-name-archive (name)
"Return archive part of NAME."
(and (tramp-archive-file-name-p name)
(match-string 1 name)))
(defun tramp-archive-file-name-localname (name)
"Return localname part of NAME."
(and (tramp-archive-file-name-p name)
(match-string 2 name)))
(defvar tramp-archive-hash (make-hash-table :test 'equal)
"Hash table for archive local copies.
The hash key is the archive name. The value is a cons of the
used `tramp-file-name' structure for tramp-gvfs, and the file
name of a local copy, if any.")
(defsubst tramp-archive-gvfs-host (archive)
"Return host name of ARCHIVE as used in GVFS for mounting"
(url-hexify-string (tramp-gvfs-url-file-name archive)))
(defun tramp-archive-dissect-file-name (name)
"Return a `tramp-file-name' structure.
The structure consists of the `tramp-archive-method' method, the
hexified archive name as host, and the localname. The archive
name is kept in slot `hop'"
(save-match-data
(unless (tramp-archive-file-name-p name)
(tramp-user-error nil "Not an archive file name: \"%s\"" name))
(let* ((localname (tramp-archive-file-name-localname name))
(archive (file-truename (tramp-archive-file-name-archive name)))
2018-01-30 19:09:20 +00:00
(vec (make-tramp-file-name
:method tramp-archive-method :hop archive)))
(cond
;; The value is already in the hash table.
2018-01-30 19:09:20 +00:00
((gethash archive tramp-archive-hash)
(setq vec (car (gethash archive tramp-archive-hash))))
;; File archives inside file archives.
((tramp-archive-file-name-p archive)
(let ((archive
(tramp-make-tramp-file-name
(tramp-archive-dissect-file-name archive) nil 'noarchive)))
(setf (tramp-file-name-host vec) (tramp-archive-gvfs-host archive)))
(puthash archive (list vec) tramp-archive-hash))
;; http://...
((and url-handler-mode
tramp-compat-use-url-tramp-p
Rework Tramp wrt string-match-p, looking-at-p, save-match-data * lisp/net/tramp.el (tramp-find-method, tramp-find-user) (tramp-find-host, tramp-dissect-file-name, tramp-make-tramp-file-name) (tramp-completion-make-tramp-file-name, tramp-debug-message) (tramp-message, tramp-progress-reporter-update) (tramp-set-completion-function) (tramp-rfn-eshadow-update-overlay) (tramp-find-file-name-coding-system-alist) (tramp-file-name-for-operation) (tramp-use-absolute-autoload-file-names) (tramp-get-completion-methods, tramp-get-completion-user-host) (tramp-handle-directory-files) (tramp-handle-file-name-case-insensitive-p) (tramp-handle-file-name-completion, tramp-handle-file-truename) (tramp-handle-insert-directory, tramp-handle-load) (tramp-handle-shell-command, tramp-action-yesno) (tramp-action-yn, tramp-process-actions) (tramp-mode-string-to-int, tramp-get-local-locale) (tramp-local-host-p): * lisp/net/tramp-adb.el (tramp-adb-handle-file-system-info) (tramp-adb-handle-directory-files-and-attributes) (tramp-adb--gnu-switches-to-ash, tramp-adb-sh-fix-ls-output) (tramp-adb-handle-file-name-all-completions) (tramp-adb-handle-shell-command) (tramp-adb-handle-start-file-process): * lisp/net/tramp-archive.el (tramp-archive-dissect-file-name): * lisp/net/tramp-cache.el (tramp-get-hash-table) (tramp-flush-directory-properties, tramp-flush-file-function): * lisp/net/tramp-cmds.el (tramp-reporter-dump-variable) (tramp-append-tramp-buffers): * lisp/net/tramp-compat.el (tramp-compat-process-running-p): * lisp/net/tramp-gvfs.el (tramp-gvfs-handle-expand-file-name) (tramp-gvfs-get-file-attributes) (tramp-gvfs-handle-file-attributes) (tramp-gvfs-monitor-process-filter) (tramp-gvfs-handler-mounted-unmounted) (tramp-gvfs-connection-mounted-p, tramp-gvfs-mount-spec-entry) (tramp-gvfs-mount-spec, tramp-gvfs-maybe-open-connection): * lisp/net/tramp-sh.el (tramp-do-file-attributes-with-ls) (tramp-do-file-attributes-with-stat) (tramp-sh-handle-file-selinux-context) (tramp-sh-handle-directory-files-and-attributes) (tramp-do-directory-files-and-attributes-with-stat) (tramp-sh-handle-file-name-all-completions) (tramp-sh-handle-dired-compress-file) (tramp-sh-handle-insert-directory) (tramp-sh-handle-expand-file-name) (tramp-sh-handle-start-file-process) (tramp-sh-handle-process-file, tramp-sh-handle-write-region) (tramp-sh-handle-file-notify-add-watch) (tramp-sh-gio-monitor-process-filter) (tramp-sh-gvfs-monitor-dir-process-filter) (tramp-sh-inotifywait-process-filter) (tramp-sh-handle-file-system-info, tramp-maybe-send-script) (tramp-find-executable, tramp-open-shell, tramp-find-shell) (tramp-open-connection-setup-interactive-shell) (tramp-find-inline-encoding, tramp-call-local-coding-command) (tramp-compute-multi-hops, tramp-maybe-open-connection) (tramp-convert-file-attributes) (tramp-make-copy-program-file-name, tramp-get-remote-locale) (tramp-get-test-nt-command, tramp-get-remote-stat) (tramp-get-inline-coding): * lisp/net/tramp-smb.el (tramp-smb-handle-directory-files) (tramp-smb-action-get-acl, tramp-smb-handle-file-attributes) (tramp-smb-handle-file-name-all-completions) (tramp-smb-handle-file-system-info) (tramp-smb-handle-file-writable-p) (tramp-smb-handle-insert-directory) (tramp-smb-handle-make-directory) (tramp-smb-handle-make-directory-internal) (tramp-smb-handle-start-file-process, tramp-smb-get-localname) (tramp-smb-read-file-entry): Use `string-match-p' and `looking-at-p'. Remove superfluous `save-match-data'. Apply `eval-when-compile' on constant concat data. * lisp/net/tramp-compat.el (tramp-compat-file-name-quoted-p-p): Use `string-prefix-p'. (tramp-compat-file-name-unquote): Do not use match data.
2018-12-06 15:00:05 +00:00
(string-match-p url-handler-regexp archive)
(string-match-p
"https?" (url-type (url-generic-parse-url archive))))
(let* ((url-tramp-protocols
(cons
(url-type (url-generic-parse-url archive))
url-tramp-protocols))
(archive (url-tramp-convert-url-to-tramp archive)))
(setf (tramp-file-name-host vec) (tramp-archive-gvfs-host archive)))
2018-01-30 19:09:20 +00:00
(puthash archive (list vec) tramp-archive-hash))
;; GVFS supported schemes.
((or (tramp-gvfs-file-name-p archive)
(not (file-remote-p archive)))
(setf (tramp-file-name-host vec) (tramp-archive-gvfs-host archive))
(puthash archive (list vec) tramp-archive-hash))
;; Anything else. Here we call `file-local-copy', which we
;; have avoided so far.
(t (let* ((inhibit-file-name-operation #'file-local-copy)
2018-01-30 19:09:20 +00:00
(inhibit-file-name-handlers
(cons #'jka-compr-handler inhibit-file-name-handlers))
2018-01-30 19:09:20 +00:00
(copy (file-local-copy archive)))
(setf (tramp-file-name-host vec) (tramp-archive-gvfs-host copy))
2018-01-30 19:09:20 +00:00
(puthash archive (cons vec copy) tramp-archive-hash))))
;; So far, `vec' handles just the mount point. Add `localname',
;; which shouldn't be pushed to the hash.
(setf (tramp-file-name-localname vec) localname)
vec)))
(defun tramp-archive-cleanup-hash ()
"Remove local copies of archives, used by GVFS."
(maphash
(lambda (key value)
;; Unmount local copy.
(ignore-errors
(tramp-message (car value) 3 "Unmounting %s" (or (cdr value) key))
(tramp-gvfs-unmount (car value)))
;; Delete local copy.
(ignore-errors (delete-file (cdr value)))
(remhash key tramp-archive-hash))
tramp-archive-hash)
(clrhash tramp-archive-hash))
(add-hook 'tramp-cleanup-all-connections-hook #'tramp-archive-cleanup-hash)
(add-hook 'kill-emacs-hook #'tramp-archive-cleanup-hash)
(add-hook 'tramp-archive-unload-hook
(lambda ()
(remove-hook 'tramp-cleanup-all-connections-hook
#'tramp-archive-cleanup-hash)
(remove-hook 'kill-emacs-hook
#'tramp-archive-cleanup-hash)))
(defsubst tramp-file-name-archive (vec)
"Extract the archive file name from VEC.
VEC is expected to be a `tramp-file-name', with the method being
`tramp-archive-method', and the host being a coded URL. The
archive name is extracted from the hop part of the VEC structure."
(and (tramp-file-name-p vec)
(string-equal (tramp-file-name-method vec) tramp-archive-method)
(tramp-file-name-hop vec)))
(defmacro with-parsed-tramp-archive-file-name (filename var &rest body)
"Parse an archive filename and make components available in the body.
This works exactly as `with-parsed-tramp-file-name' for the Tramp
file name structure returned by `tramp-archive-dissect-file-name'.
A variable `foo-archive' (or `archive') will be bound to the
archive name part of FILENAME, assuming `foo' (or nil) is the
value of VAR. OTOH, the variable `foo-hop' (or `hop') won't be
offered."
(declare (debug (form symbolp body))
(indent 2))
(let ((bindings
(mapcar (lambda (elem)
`(,(if var (intern (format "%s-%s" var elem)) elem)
(,(intern (format "tramp-file-name-%s" elem))
,(or var 'v))))
`,(cons
'archive
(delete 'hop (tramp-compat-tramp-file-name-slots))))))
`(let* ((,(or var 'v) (tramp-archive-dissect-file-name ,filename))
,@bindings)
;; We don't know which of those vars will be used, so we bind them all,
;; and then add here a dummy use of all those variables, so we don't get
;; flooded by warnings about those vars `body' didn't use.
(ignore ,@(mapcar #'car bindings))
,@body)))
(defun tramp-archive-gvfs-file-name (name)
"Return FILENAME in GVFS syntax."
(tramp-make-tramp-file-name
(tramp-archive-dissect-file-name name) nil 'nohop))
;; File name primitives.
(defun tramp-archive-handle-access-file (filename string)
"Like `access-file' for Tramp files."
(access-file (tramp-archive-gvfs-file-name filename) string))
(defun tramp-archive-handle-copy-file
(filename newname &optional ok-if-already-exists keep-date
preserve-uid-gid preserve-extended-attributes)
"Like `copy-file' for file archives."
(when (tramp-archive-file-name-p newname)
(tramp-error
(tramp-archive-dissect-file-name newname) 'file-error
"Permission denied: %s" newname))
(copy-file
(tramp-archive-gvfs-file-name filename) newname ok-if-already-exists
keep-date preserve-uid-gid preserve-extended-attributes))
(defun tramp-archive-handle-directory-file-name (directory)
"Like `directory-file-name' for file archives."
(with-parsed-tramp-archive-file-name directory nil
(if (and (not (zerop (length localname)))
(eq (aref localname (1- (length localname))) ?/)
(not (string= localname "/")))
(substring directory 0 -1)
;; We do not want to leave the file archive. This would require
;; unnecessary download of http-based file archives, for
;; example. So we return `directory'.
directory)))
(defun tramp-archive-handle-dired-uncache (dir)
"Like `dired-uncache' for file archives."
(dired-uncache (tramp-archive-gvfs-file-name dir)))
(defun tramp-archive-handle-file-attributes (filename &optional id-format)
"Like `file-attributes' for file archives."
(file-attributes (tramp-archive-gvfs-file-name filename) id-format))
(defun tramp-archive-handle-file-executable-p (filename)
"Like `file-executable-p' for file archives."
(file-executable-p (tramp-archive-gvfs-file-name filename)))
(defun tramp-archive-handle-file-local-copy (filename)
"Like `file-local-copy' for file archives."
(file-local-copy (tramp-archive-gvfs-file-name filename)))
(defun tramp-archive-handle-file-name-all-completions (filename directory)
"Like `file-name-all-completions' for file archives."
(file-name-all-completions filename (tramp-archive-gvfs-file-name directory)))
(defun tramp-archive-handle-file-readable-p (filename)
"Like `file-readable-p' for file archives."
(file-readable-p (tramp-archive-gvfs-file-name filename)))
(defun tramp-archive-handle-file-system-info (filename)
"Like `file-system-info' for file archives."
(with-parsed-tramp-archive-file-name filename nil
(list (tramp-compat-file-attribute-size (file-attributes archive)) 0 0)))
(defun tramp-archive-handle-file-truename (filename)
"Like `file-truename' for file archives."
(with-parsed-tramp-archive-file-name filename nil
(let ((local (or (file-symlink-p filename) localname)))
(unless (file-name-absolute-p local)
(setq local (expand-file-name local (file-name-directory localname))))
(concat (file-truename archive) local))))
(defun tramp-archive-handle-insert-directory
(filename switches &optional wildcard full-directory-p)
"Like `insert-directory' for file archives."
(insert-directory
(tramp-archive-gvfs-file-name filename) switches wildcard full-directory-p)
(goto-char (point-min))
(while (search-forward (tramp-archive-gvfs-file-name filename) nil 'noerror)
(replace-match filename)))
(defun tramp-archive-handle-insert-file-contents
(filename &optional visit beg end replace)
"Like `insert-file-contents' for file archives."
(let ((result
(insert-file-contents
(tramp-archive-gvfs-file-name filename) visit beg end replace)))
(prog1
(list (expand-file-name filename)
(cadr result))
(when visit (setq buffer-file-name filename)))))
(defun tramp-archive-handle-load
(file &optional noerror nomessage nosuffix must-suffix)
"Like `load' for file archives."
(load
(tramp-archive-gvfs-file-name file) noerror nomessage nosuffix must-suffix))
(defun tramp-archive-handle-temporary-file-directory ()
"Like `temporary-file-directory' for file archives."
;; If the default directory, the file archive, is located on a
;; mounted directory, it is returned as it. Not what we want.
(with-parsed-tramp-archive-file-name default-directory nil
(let ((default-directory (file-name-directory archive)))
(tramp-compat-temporary-file-directory))))
(defun tramp-archive-handle-not-implemented (operation &rest args)
"Generic handler for operations not implemented for file archives."
(let ((v (ignore-errors
(tramp-archive-dissect-file-name
(apply #'tramp-archive-file-name-for-operation operation args)))))
(tramp-message v 10 "%s" (cons operation args))
(tramp-error
v 'file-error
"Operation `%s' not implemented for file archives" operation)))
(add-hook 'tramp-unload-hook
(lambda ()
(unload-feature 'tramp-archive 'force)))
(provide 'tramp-archive)
;;; TODO:
;; * Check, whether we could retrieve better file attributes like uid,
;; gid, permissions. See gvfsbackendarchive.c
;; (archive_file_set_info_from_entry), where it is commented out.
;;
;; * Implement write access, when possible.
;; https://bugzilla.gnome.org/show_bug.cgi?id=589617
;;; tramp-archive.el ends here