1;;; url-handlers.el --- file-name-handler stuff for URL loading 2 3;; Copyright (C) 1996, 1997, 1998, 1999, 2004, 4;; 2005, 2006, 2007 Free Software Foundation, Inc. 5 6;; Keywords: comm, data, processes, hypermedia 7 8;; This file is part of GNU Emacs. 9;; 10;; GNU Emacs is free software; you can redistribute it and/or modify 11;; it under the terms of the GNU General Public License as published by 12;; the Free Software Foundation; either version 2, or (at your option) 13;; any later version. 14;; 15;; GNU Emacs is distributed in the hope that it will be useful, 16;; but WITHOUT ANY WARRANTY; without even the implied warranty of 17;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18;; GNU General Public License for more details. 19;; 20;; You should have received a copy of the GNU General Public License 21;; along with GNU Emacs; see the file COPYING. If not, write to the 22;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 23;; Boston, MA 02110-1301, USA. 24 25;;; Commentary: 26 27;;; Code: 28 29;; (require 'url) 30(eval-when-compile (require 'url-parse)) 31;; (require 'url-util) 32(eval-when-compile (require 'mm-decode)) 33;; (require 'mailcap) 34;; The following functions in the byte compiler's warnings are known not 35;; to cause any real problem for the following reasons: 36;; - mm-save-part-to-file, mm-destroy-parts: always used 37;; after mm-dissect-buffer and defined in the same file. 38;; The following are autoloaded instead of `require'd to avoid eagerly 39;; loading all of URL when turning on url-handler-mode in the .emacs. 40(autoload 'url-expand-file-name "url-expand" "Convert url to a fully specified url, and canonicalize it.") 41(autoload 'mm-dissect-buffer "mm-decode" "Dissect the current buffer and return a list of MIME handles.") 42(autoload 'url-scheme-get-property "url-methods" "Get property of a URL SCHEME.") 43 44(eval-when-compile 45 (require 'cl)) 46 47;; Implementation status 48;; --------------------- 49;; Function Status 50;; ------------------------------------------------------------ 51;; add-name-to-file Needs DAV Bindings 52;; copy-file Broken (assumes 1st item is URL) 53;; delete-directory Finished (DAV) 54;; delete-file Finished (DAV) 55;; diff-latest-backup-file 56;; directory-file-name unnecessary (what about VMS)? 57;; directory-files Finished (DAV) 58;; dired-call-process 59;; dired-compress-file 60;; dired-uncache 61;; expand-file-name Finished 62;; file-accessible-directory-p 63;; file-attributes Finished, better with DAV 64;; file-directory-p Needs DAV, finished 65;; file-executable-p Finished 66;; file-exists-p Finished 67;; file-local-copy 68;; file-modes 69;; file-name-all-completions Finished (DAV) 70;; file-name-as-directory 71;; file-name-completion Finished (DAV) 72;; file-name-directory 73;; file-name-nondirectory 74;; file-name-sans-versions why? 75;; file-newer-than-file-p 76;; file-ownership-preserved-p No way to know 77;; file-readable-p Finished 78;; file-regular-p !directory_p 79;; file-symlink-p Needs DAV bindings 80;; file-truename Needs DAV bindings 81;; file-writable-p Check for LOCK? 82;; find-backup-file-name why? 83;; get-file-buffer why? 84;; insert-directory Use DAV 85;; insert-file-contents Finished 86;; load 87;; make-directory Finished (DAV) 88;; make-symbolic-link Needs DAV bindings 89;; rename-file Finished (DAV) 90;; set-file-modes Use mod_dav specific executable flag? 91;; set-visited-file-modtime Impossible? 92;; shell-command Impossible? 93;; unhandled-file-name-directory 94;; vc-registered Finished (DAV) 95;; verify-visited-file-modtime 96;; write-region 97 98(defvar url-handler-regexp 99 "\\`\\(https?\\|ftp\\|file\\|nfs\\)://" 100 "*A regular expression for matching URLs handled by file-name-handler-alist. 101Some valid URL protocols just do not make sense to visit interactively 102\(about, data, info, irc, mailto, etc\). This regular expression 103avoids conflicts with local files that look like URLs \(Gnus is 104particularly bad at this\).") 105 106;;;###autoload 107(define-minor-mode url-handler-mode 108 "Use URL to handle URL-like file names." 109 :global t :group 'url 110 (if (not (boundp 'file-name-handler-alist)) 111 ;; Can't be turned ON anyway. 112 (setq url-handler-mode nil) 113 ;; Remove old entry, if any. 114 (setq file-name-handler-alist 115 (delq (rassq 'url-file-handler file-name-handler-alist) 116 file-name-handler-alist)) 117 (if url-handler-mode 118 (push (cons url-handler-regexp 'url-file-handler) 119 file-name-handler-alist)))) 120 121(defun url-run-real-handler (operation args) 122 (let ((inhibit-file-name-handlers (cons 'url-file-handler 123 (if (eq operation inhibit-file-name-operation) 124 inhibit-file-name-handlers))) 125 (inhibit-file-name-operation operation)) 126 (apply operation args))) 127 128(defun url-file-handler (operation &rest args) 129 "Function called from the `file-name-handler-alist' routines. 130OPERATION is what needs to be done (`file-exists-p', etc). ARGS are 131the arguments that would have been passed to OPERATION." 132 (let ((fn (or (get operation 'url-file-handlers) 133 (intern-soft (format "url-%s" operation)))) 134 (val nil) 135 (hooked nil)) 136 (if (and fn (fboundp fn)) 137 (setq hooked t 138 val (apply fn args)) 139 (setq hooked nil 140 val (url-run-real-handler operation args))) 141 (url-debug 'handlers "%s %S%S => %S" (if hooked "Hooked" "Real") 142 operation args val) 143 val)) 144 145(defun url-file-handler-identity (&rest args) 146 ;; Identity function 147 (car args)) 148 149;; These are operations that we can fully support 150(put 'file-readable-p 'url-file-handlers 'url-file-exists-p) 151(put 'substitute-in-file-name 'url-file-handlers 'url-file-handler-identity) 152(put 'file-name-absolute-p 'url-file-handlers (lambda (&rest ignored) t)) 153(put 'expand-file-name 'url-file-handlers 'url-handler-expand-file-name) 154(put 'directory-file-name 'url-file-handlers 'url-handler-directory-file-name) 155;; (put 'file-name-as-directory 'url-file-handlers 'url-handler-file-name-as-directory) 156 157;; These are operations that we do not support yet (DAV!!!) 158(put 'file-writable-p 'url-file-handlers 'ignore) 159(put 'file-symlink-p 'url-file-handlers 'ignore) 160;; Just like for ange-ftp: let's not waste time trying to look for RCS/foo,v 161;; files and such since we can't do anything clever with them anyway. 162(put 'vc-registered 'url-file-handlers 'ignore) 163 164(defun url-handler-expand-file-name (file &optional base) 165 ;; When we see "/foo/bar" in a file whose working dir is "http://bla/bla", 166 ;; there are two interpretations possible: either it's a local "/foo/bar" 167 ;; or it's "http:/bla/foo/bar". When working with URLs, the second 168 ;; interpretation is the right one, but when working with Emacs file 169 ;; names, the first is preferred. 170 (if (file-name-absolute-p file) 171 (expand-file-name file "/") 172 (url-expand-file-name file base))) 173 174;; directory-file-name and file-name-as-directory are kind of hard to 175;; implement really right for URLs since URLs can have repeated / chars. 176;; We'd want the following behavior: 177;; idempotence: (d-f-n (d-f-n X) == (d-f-n X) 178;; idempotence: (f-n-a-d (f-n-a-d X) == (f-n-a-d X) 179;; reversible: (d-f-n (f-n-a-d (d-f-n X))) == (d-f-n X) 180;; reversible: (f-n-a-d (d-f-n (f-n-a-d X))) == (f-n-a-d X) 181(defun url-handler-directory-file-name (dir) 182 ;; When there's more than a single /, just don't touch the slashes at all. 183 (if (string-match "//\\'" dir) dir 184 (url-run-real-handler 'directory-file-name (list dir)))) 185 186;; The actual implementation 187;;;###autoload 188(defun url-copy-file (url newname &optional ok-if-already-exists keep-time) 189 "Copy URL to NEWNAME. Both args must be strings. 190Signals a `file-already-exists' error if file NEWNAME already exists, 191unless a third argument OK-IF-ALREADY-EXISTS is supplied and non-nil. 192A number as third arg means request confirmation if NEWNAME already exists. 193This is what happens in interactive use with M-x. 194Fourth arg KEEP-TIME non-nil means give the new file the same 195last-modified time as the old one. (This works on only some systems.) 196A prefix arg makes KEEP-TIME non-nil." 197 (if (and (file-exists-p newname) 198 (not ok-if-already-exists)) 199 (error "Opening output file: File already exists, %s" newname)) 200 (let ((buffer (url-retrieve-synchronously url)) 201 (handle nil)) 202 (if (not buffer) 203 (error "Opening input file: No such file or directory, %s" url)) 204 (with-current-buffer buffer 205 (setq handle (mm-dissect-buffer t))) 206 (mm-save-part-to-file handle newname) 207 (kill-buffer buffer) 208 (mm-destroy-parts handle))) 209 210;;;###autoload 211(defun url-file-local-copy (url &rest ignored) 212 "Copy URL into a temporary file on this machine. 213Returns the name of the local copy, or nil, if FILE is directly 214accessible." 215 (let ((filename (make-temp-file "url"))) 216 (url-copy-file url filename 'ok-if-already-exists) 217 filename)) 218 219(defun url-insert (buffer &optional beg end) 220 "Insert the body of a URL object. 221BUFFER should be a complete URL buffer as returned by `url-retrieve'. 222If the headers specify a coding-system, it is applied to the body before it is inserted. 223Returns a list of the form (SIZE CHARSET), where SIZE is the size in bytes 224of the inserted text and CHARSET is the charset that was specified in the header, 225or nil if none was found. 226BEG and END can be used to only insert a subpart of the body. 227They count bytes from the beginning of the body." 228 (let* ((handle (with-current-buffer buffer (mm-dissect-buffer t))) 229 (data (with-current-buffer (mm-handle-buffer handle) 230 (if beg 231 (buffer-substring (+ (point-min) beg) 232 (if end (+ (point-min) end) (point-max))) 233 (buffer-string)))) 234 (charset (mail-content-type-get (mm-handle-type handle) 235 'charset))) 236 (mm-destroy-parts handle) 237 (if charset 238 (insert (mm-decode-string data (mm-charset-to-coding-system charset))) 239 (insert data)) 240 (list (length data) charset))) 241 242;;;###autoload 243(defun url-insert-file-contents (url &optional visit beg end replace) 244 (let ((buffer (url-retrieve-synchronously url))) 245 (if (not buffer) 246 (error "Opening input file: No such file or directory, %s" url)) 247 (if visit (setq buffer-file-name url)) 248 (save-excursion 249 (let* ((start (point)) 250 (size-and-charset (url-insert buffer beg end))) 251 (kill-buffer buffer) 252 (when replace 253 (delete-region (point-min) start) 254 (delete-region (point) (point-max))) 255 (unless (cadr size-and-charset) 256 ;; If the headers don't specify any particular charset, use the 257 ;; usual heuristic/rules that we apply to files. 258 (decode-coding-inserted-region start (point) url visit beg end replace)) 259 (list url (car size-and-charset)))))) 260 261(defun url-file-name-completion (url directory) 262 (error "Unimplemented")) 263 264(defun url-file-name-all-completions (file directory) 265 (error "Unimplemented")) 266 267;; All other handlers map onto their respective backends. 268(defmacro url-handlers-create-wrapper (method args) 269 `(defun ,(intern (format "url-%s" method)) ,args 270 ,(format "URL file-name-handler wrapper for `%s' call.\n---\n%s" method 271 (or (documentation method t) "No original documentation.")) 272 (setq url (url-generic-parse-url url)) 273 (when (url-type url) 274 (funcall (url-scheme-get-property (url-type url) (quote ,method)) 275 ,@(remove '&rest (remove '&optional args)))))) 276 277(url-handlers-create-wrapper file-exists-p (url)) 278(url-handlers-create-wrapper file-attributes (url &optional id-format)) 279(url-handlers-create-wrapper file-symlink-p (url)) 280(url-handlers-create-wrapper file-writable-p (url)) 281(url-handlers-create-wrapper file-directory-p (url)) 282(url-handlers-create-wrapper file-executable-p (url)) 283(url-handlers-create-wrapper directory-files (url &optional full match nosort)) 284(url-handlers-create-wrapper file-truename (url &optional counter prev-dirs)) 285 286(add-hook 'find-file-hook 'url-handlers-set-buffer-mode) 287 288(defun url-handlers-set-buffer-mode () 289 "Set correct modes for the current buffer if visiting a remote file." 290 (and (stringp buffer-file-name) 291 (string-match url-handler-regexp buffer-file-name) 292 (auto-save-mode 0))) 293 294(provide 'url-handlers) 295 296;; arch-tag: 7300b99c-cc83-42ff-9147-79b2723c62ac 297;;; url-handlers.el ends here 298