| 1 |
|
|---|
| 2 |
|
|---|
| 3 |
|
|---|
| 4 |
|
|---|
| 5 |
|
|---|
| 6 |
|
|---|
| 7 |
|
|---|
| 8 |
|
|---|
| 9 |
|
|---|
| 10 |
|
|---|
| 11 |
|
|---|
| 12 |
|
|---|
| 13 |
|
|---|
| 14 |
|
|---|
| 15 |
|
|---|
| 16 |
|
|---|
| 17 |
|
|---|
| 18 |
|
|---|
| 19 |
|
|---|
| 20 |
|
|---|
| 21 |
|
|---|
| 22 |
|
|---|
| 23 |
|
|---|
| 24 |
|
|---|
| 25 |
|
|---|
| 26 |
|
|---|
| 27 |
|
|---|
| 28 |
|
|---|
| 29 |
|
|---|
| 30 |
(eval-when-compile (require 'url-parse)) |
|---|
| 31 |
|
|---|
| 32 |
(eval-when-compile (require 'mm-decode)) |
|---|
| 33 |
|
|---|
| 34 |
|
|---|
| 35 |
|
|---|
| 36 |
|
|---|
| 37 |
|
|---|
| 38 |
|
|---|
| 39 |
|
|---|
| 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 |
|
|---|
| 48 |
|
|---|
| 49 |
|
|---|
| 50 |
|
|---|
| 51 |
|
|---|
| 52 |
|
|---|
| 53 |
|
|---|
| 54 |
|
|---|
| 55 |
|
|---|
| 56 |
|
|---|
| 57 |
|
|---|
| 58 |
|
|---|
| 59 |
|
|---|
| 60 |
|
|---|
| 61 |
|
|---|
| 62 |
|
|---|
| 63 |
|
|---|
| 64 |
|
|---|
| 65 |
|
|---|
| 66 |
|
|---|
| 67 |
|
|---|
| 68 |
|
|---|
| 69 |
|
|---|
| 70 |
|
|---|
| 71 |
|
|---|
| 72 |
|
|---|
| 73 |
|
|---|
| 74 |
|
|---|
| 75 |
|
|---|
| 76 |
|
|---|
| 77 |
|
|---|
| 78 |
|
|---|
| 79 |
|
|---|
| 80 |
|
|---|
| 81 |
|
|---|
| 82 |
|
|---|
| 83 |
|
|---|
| 84 |
|
|---|
| 85 |
|
|---|
| 86 |
|
|---|
| 87 |
|
|---|
| 88 |
|
|---|
| 89 |
|
|---|
| 90 |
|
|---|
| 91 |
|
|---|
| 92 |
|
|---|
| 93 |
|
|---|
| 94 |
|
|---|
| 95 |
|
|---|
| 96 |
|
|---|
| 97 |
|
|---|
| 98 |
(defvar url-handler-regexp |
|---|
| 99 |
"\\`\\(https?\\|ftp\\|file\\|nfs\\)://" |
|---|
| 100 |
"*A regular expression for matching URLs handled by file-name-handler-alist. |
|---|
| 101 |
Some valid URL protocols just do not make sense to visit interactively |
|---|
| 102 |
\(about, data, info, irc, mailto, etc\). This regular expression |
|---|
| 103 |
avoids conflicts with local files that look like URLs \(Gnus is |
|---|
| 104 |
particularly bad at this\).") |
|---|
| 105 |
|
|---|
| 106 |
|
|---|
| 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 |
|
|---|
| 112 |
(setq url-handler-mode nil) |
|---|
| 113 |
|
|---|
| 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 |
|
|---|
| 129 |
(defun url-file-handler (operation &rest args) |
|---|
| 130 |
"Function called from the `file-name-handler-alist' routines. |
|---|
| 131 |
OPERATION is what needs to be done (`file-exists-p', etc). ARGS are |
|---|
| 132 |
the arguments that would have been passed to OPERATION." |
|---|
| 133 |
(let ((fn (or (get operation 'url-file-handlers) |
|---|
| 134 |
(intern-soft (format "url-%s" operation)))) |
|---|
| 135 |
(val nil) |
|---|
| 136 |
(hooked nil)) |
|---|
| 137 |
(if (and fn (fboundp fn)) |
|---|
| 138 |
(setq hooked t |
|---|
| 139 |
val (apply fn args)) |
|---|
| 140 |
(setq hooked nil |
|---|
| 141 |
val (url-run-real-handler operation args))) |
|---|
| 142 |
(url-debug 'handlers "%s %S%S => %S" (if hooked "Hooked" "Real") |
|---|
| 143 |
operation args val) |
|---|
| 144 |
val)) |
|---|
| 145 |
|
|---|
| 146 |
(defun url-file-handler-identity (&rest args) |
|---|
| 147 |
|
|---|
| 148 |
(car args)) |
|---|
| 149 |
|
|---|
| 150 |
|
|---|
| 151 |
(put 'file-readable-p 'url-file-handlers 'url-file-exists-p) |
|---|
| 152 |
(put 'substitute-in-file-name 'url-file-handlers 'url-file-handler-identity) |
|---|
| 153 |
(put 'file-name-absolute-p 'url-file-handlers (lambda (&rest ignored) t)) |
|---|
| 154 |
(put 'expand-file-name 'url-file-handlers 'url-handler-expand-file-name) |
|---|
| 155 |
(put 'directory-file-name 'url-file-handlers 'url-handler-directory-file-name) |
|---|
| 156 |
(put 'unhandled-file-name-directory 'url-file-handlers 'url-handler-unhandled-file-name-directory) |
|---|
| 157 |
|
|---|
| 158 |
|
|---|
| 159 |
|
|---|
| 160 |
(put 'file-writable-p 'url-file-handlers 'ignore) |
|---|
| 161 |
(put 'file-symlink-p 'url-file-handlers 'ignore) |
|---|
| 162 |
|
|---|
| 163 |
|
|---|
| 164 |
(put 'vc-registered 'url-file-handlers 'ignore) |
|---|
| 165 |
|
|---|
| 166 |
(defun url-handler-expand-file-name (file &optional base) |
|---|
| 167 |
|
|---|
| 168 |
|
|---|
| 169 |
|
|---|
| 170 |
|
|---|
| 171 |
|
|---|
| 172 |
(if (file-name-absolute-p file) |
|---|
| 173 |
(expand-file-name file "/") |
|---|
| 174 |
(url-expand-file-name file base))) |
|---|
| 175 |
|
|---|
| 176 |
|
|---|
| 177 |
|
|---|
| 178 |
|
|---|
| 179 |
|
|---|
| 180 |
|
|---|
| 181 |
|
|---|
| 182 |
|
|---|
| 183 |
(defun url-handler-directory-file-name (dir) |
|---|
| 184 |
|
|---|
| 185 |
(if (string-match "//\\'" dir) dir |
|---|
| 186 |
(url-run-real-handler 'directory-file-name (list dir)))) |
|---|
| 187 |
|
|---|
| 188 |
(defun url-handler-unhandled-file-name-directory (filename) |
|---|
| 189 |
|
|---|
| 190 |
|
|---|
| 191 |
|
|---|
| 192 |
|
|---|
| 193 |
(expand-file-name "~/")) |
|---|
| 194 |
|
|---|
| 195 |
|
|---|
| 196 |
|
|---|
| 197 |
(defun url-copy-file (url newname &optional ok-if-already-exists keep-time) |
|---|
| 198 |
"Copy URL to NEWNAME. Both args must be strings. |
|---|
| 199 |
Signals a `file-already-exists' error if file NEWNAME already exists, |
|---|
| 200 |
unless a third argument OK-IF-ALREADY-EXISTS is supplied and non-nil. |
|---|
| 201 |
A number as third arg means request confirmation if NEWNAME already exists. |
|---|
| 202 |
This is what happens in interactive use with M-x. |
|---|
| 203 |
Fourth arg KEEP-TIME non-nil means give the new file the same |
|---|
| 204 |
last-modified time as the old one. (This works on only some systems.) |
|---|
| 205 |
A prefix arg makes KEEP-TIME non-nil." |
|---|
| 206 |
(if (and (file-exists-p newname) |
|---|
| 207 |
(not ok-if-already-exists)) |
|---|
| 208 |
(error "Opening output file: File already exists, %s" newname)) |
|---|
| 209 |
(let ((buffer (url-retrieve-synchronously url)) |
|---|
| 210 |
(handle nil)) |
|---|
| 211 |
(if (not buffer) |
|---|
| 212 |
(error "Opening input file: No such file or directory, %s" url)) |
|---|
| 213 |
(with-current-buffer buffer |
|---|
| 214 |
(setq handle (mm-dissect-buffer t))) |
|---|
| 215 |
(mm-save-part-to-file handle newname) |
|---|
| 216 |
(kill-buffer buffer) |
|---|
| 217 |
(mm-destroy-parts handle))) |
|---|
| 218 |
|
|---|
| 219 |
|
|---|
| 220 |
(defun url-file-local-copy (url &rest ignored) |
|---|
| 221 |
"Copy URL into a temporary file on this machine. |
|---|
| 222 |
Returns the name of the local copy, or nil, if FILE is directly |
|---|
| 223 |
accessible." |
|---|
| 224 |
(let ((filename (make-temp-file "url"))) |
|---|
| 225 |
(url-copy-file url filename 'ok-if-already-exists) |
|---|
| 226 |
filename)) |
|---|
| 227 |
|
|---|
| 228 |
(defun url-insert (buffer &optional beg end) |
|---|
| 229 |
"Insert the body of a URL object. |
|---|
| 230 |
BUFFER should be a complete URL buffer as returned by `url-retrieve'. |
|---|
| 231 |
If the headers specify a coding-system, it is applied to the body before it is inserted. |
|---|
| 232 |
Returns a list of the form (SIZE CHARSET), where SIZE is the size in bytes |
|---|
| 233 |
of the inserted text and CHARSET is the charset that was specified in the header, |
|---|
| 234 |
or nil if none was found. |
|---|
| 235 |
BEG and END can be used to only insert a subpart of the body. |
|---|
| 236 |
They count bytes from the beginning of the body." |
|---|
| 237 |
(let* ((handle (with-current-buffer buffer (mm-dissect-buffer t))) |
|---|
| 238 |
(data (with-current-buffer (mm-handle-buffer handle) |
|---|
| 239 |
(if beg |
|---|
| 240 |
(buffer-substring (+ (point-min) beg) |
|---|
| 241 |
(if end (+ (point-min) end) (point-max))) |
|---|
| 242 |
(buffer-string)))) |
|---|
| 243 |
(charset (mail-content-type-get (mm-handle-type handle) |
|---|
| 244 |
'charset))) |
|---|
| 245 |
(mm-destroy-parts handle) |
|---|
| 246 |
(if charset |
|---|
| 247 |
(insert (mm-decode-string data (mm-charset-to-coding-system charset))) |
|---|
| 248 |
(insert data)) |
|---|
| 249 |
(list (length data) charset))) |
|---|
| 250 |
|
|---|
| 251 |
|
|---|
| 252 |
(defun url-insert-file-contents (url &optional visit beg end replace) |
|---|
| 253 |
(let ((buffer (url-retrieve-synchronously url))) |
|---|
| 254 |
(if (not buffer) |
|---|
| 255 |
(error "Opening input file: No such file or directory, %s" url)) |
|---|
| 256 |
(if visit (setq buffer-file-name url)) |
|---|
| 257 |
(save-excursion |
|---|
| 258 |
(let* ((start (point)) |
|---|
| 259 |
(size-and-charset (url-insert buffer beg end))) |
|---|
| 260 |
(kill-buffer buffer) |
|---|
| 261 |
(when replace |
|---|
| 262 |
(delete-region (point-min) start) |
|---|
| 263 |
(delete-region (point) (point-max))) |
|---|
| 264 |
(unless (cadr size-and-charset) |
|---|
| 265 |
|
|---|
| 266 |
|
|---|
| 267 |
(decode-coding-inserted-region start (point) url visit beg end replace)) |
|---|
| 268 |
(list url (car size-and-charset)))))) |
|---|
| 269 |
|
|---|
| 270 |
(defun url-file-name-completion (url directory) |
|---|
| 271 |
(error "Unimplemented")) |
|---|
| 272 |
|
|---|
| 273 |
(defun url-file-name-all-completions (file directory) |
|---|
| 274 |
(error "Unimplemented")) |
|---|
| 275 |
|
|---|
| 276 |
|
|---|
| 277 |
(defmacro url-handlers-create-wrapper (method args) |
|---|
| 278 |
`(defun ,(intern (format "url-%s" method)) ,args |
|---|
| 279 |
,(format "URL file-name-handler wrapper for `%s' call.\n---\n%s" method |
|---|
| 280 |
(or (documentation method t) "No original documentation.")) |
|---|
| 281 |
(setq url (url-generic-parse-url url)) |
|---|
| 282 |
(when (url-type url) |
|---|
| 283 |
(funcall (url-scheme-get-property (url-type url) (quote ,method)) |
|---|
| 284 |
,@(remove '&rest (remove '&optional args)))))) |
|---|
| 285 |
|
|---|
| 286 |
(url-handlers-create-wrapper file-exists-p (url)) |
|---|
| 287 |
(url-handlers-create-wrapper file-attributes (url &optional id-format)) |
|---|
| 288 |
(url-handlers-create-wrapper file-symlink-p (url)) |
|---|
| 289 |
(url-handlers-create-wrapper file-writable-p (url)) |
|---|
| 290 |
(url-handlers-create-wrapper file-directory-p (url)) |
|---|
| 291 |
(url-handlers-create-wrapper file-executable-p (url)) |
|---|
| 292 |
(url-handlers-create-wrapper directory-files (url &optional full match nosort)) |
|---|
| 293 |
(url-handlers-create-wrapper file-truename (url &optional counter prev-dirs)) |
|---|
| 294 |
|
|---|
| 295 |
(add-hook 'find-file-hook 'url-handlers-set-buffer-mode) |
|---|
| 296 |
|
|---|
| 297 |
(defun url-handlers-set-buffer-mode () |
|---|
| 298 |
"Set correct modes for the current buffer if visiting a remote file." |
|---|
| 299 |
(and (stringp buffer-file-name) |
|---|
| 300 |
(string-match url-handler-regexp buffer-file-name) |
|---|
| 301 |
(auto-save-mode 0))) |
|---|
| 302 |
|
|---|
| 303 |
(provide 'url-handlers) |
|---|
| 304 |
|
|---|
| 305 |
|
|---|
| 306 |
|
|---|
| 307 |
|
|---|