Update copyright notices for 2013.
[bpt/emacs.git] / lisp / url / url-util.el
CommitLineData
8c8b8430 1;;; url-util.el --- Miscellaneous helper routines for URL library
a2fd1462 2
ab422c4d
PE
3;; Copyright (C) 1996-1999, 2001, 2004-2013 Free Software Foundation,
4;; Inc.
a2fd1462 5
8c8b8430 6;; Author: Bill Perry <wmperry@gnu.org>
8c8b8430
SM
7;; Keywords: comm, data, processes
8
a2fd1462
SM
9;; This file is part of GNU Emacs.
10;;
4936186e 11;; GNU Emacs is free software: you can redistribute it and/or modify
a2fd1462 12;; it under the terms of the GNU General Public License as published by
4936186e
GM
13;; the Free Software Foundation, either version 3 of the License, or
14;; (at your option) any later version.
15
a2fd1462
SM
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
4936186e 20
a2fd1462 21;; You should have received a copy of the GNU General Public License
4936186e 22;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
a2fd1462
SM
23
24;;; Commentary:
25
26;;; Code:
8c8b8430
SM
27
28(require 'url-parse)
e17d428f 29(require 'url-vars)
8c8b8430
SM
30(autoload 'timezone-parse-date "timezone")
31(autoload 'timezone-make-date-arpa-standard "timezone")
c6bfe6e7 32(autoload 'mail-header-extract "mailheader")
8c8b8430
SM
33
34(defvar url-parse-args-syntax-table
35 (copy-syntax-table emacs-lisp-mode-syntax-table)
36 "A syntax table for parsing sgml attributes.")
37
38(modify-syntax-entry ?' "\"" url-parse-args-syntax-table)
39(modify-syntax-entry ?` "\"" url-parse-args-syntax-table)
40(modify-syntax-entry ?{ "(" url-parse-args-syntax-table)
41(modify-syntax-entry ?} ")" url-parse-args-syntax-table)
42
43;;;###autoload
44(defcustom url-debug nil
a5cda60e 45 "What types of debug messages from the URL library to show.
8c8b8430
SM
46Debug messages are logged to the *URL-DEBUG* buffer.
47
48If t, all messages will be logged.
49If a number, all messages will be logged, as well shown via `message'.
50If a list, it is a list of the types of messages to be logged."
51 :type '(choice (const :tag "none" nil)
52 (const :tag "all" t)
53 (checklist :tag "custom"
54 (const :tag "HTTP" :value http)
55 (const :tag "DAV" :value dav)
56 (const :tag "General" :value retrieval)
57 (const :tag "Filename handlers" :value handlers)
58 (symbol :tag "Other")))
59 :group 'url-hairy)
60
61;;;###autoload
62(defun url-debug (tag &rest args)
63 (if quit-flag
64 (error "Interrupted!"))
65 (if (or (eq url-debug t)
66 (numberp url-debug)
67 (and (listp url-debug) (memq tag url-debug)))
a2fd1462 68 (with-current-buffer (get-buffer-create "*URL-DEBUG*")
8c8b8430
SM
69 (goto-char (point-max))
70 (insert (symbol-name tag) " -> " (apply 'format args) "\n")
71 (if (numberp url-debug)
72 (apply 'message args)))))
73
74;;;###autoload
75(defun url-parse-args (str &optional nodowncase)
76 ;; Return an assoc list of attribute/value pairs from an RFC822-type string
77 (let (
78 name ; From name=
79 value ; its value
80 results ; Assoc list of results
81 name-pos ; Start of XXXX= position
82 val-pos ; Start of value position
83 st
84 nd
85 )
86 (save-excursion
87 (save-restriction
88 (set-buffer (get-buffer-create " *urlparse-temp*"))
89 (set-syntax-table url-parse-args-syntax-table)
90 (erase-buffer)
91 (insert str)
92 (setq st (point-min)
93 nd (point-max))
94 (set-syntax-table url-parse-args-syntax-table)
95 (narrow-to-region st nd)
96 (goto-char (point-min))
97 (while (not (eobp))
98 (skip-chars-forward "; \n\t")
99 (setq name-pos (point))
100 (skip-chars-forward "^ \n\t=;")
101 (if (not nodowncase)
102 (downcase-region name-pos (point)))
103 (setq name (buffer-substring name-pos (point)))
104 (skip-chars-forward " \t\n")
105 (if (/= (or (char-after (point)) 0) ?=) ; There is no value
106 (setq value nil)
107 (skip-chars-forward " \t\n=")
108 (setq val-pos (point)
109 value
110 (cond
111 ((or (= (or (char-after val-pos) 0) ?\")
112 (= (or (char-after val-pos) 0) ?'))
113 (buffer-substring (1+ val-pos)
114 (condition-case ()
115 (prog2
116 (forward-sexp 1)
117 (1- (point))
118 (skip-chars-forward "\""))
119 (error
120 (skip-chars-forward "^ \t\n")
121 (point)))))
122 (t
123 (buffer-substring val-pos
124 (progn
125 (skip-chars-forward "^;")
126 (skip-chars-backward " \t")
127 (point)))))))
128 (setq results (cons (cons name value) results))
129 (skip-chars-forward "; \n\t"))
130 results))))
131
132;;;###autoload
133(defun url-insert-entities-in-string (string)
134 "Convert HTML markup-start characters to entity references in STRING.
135Also replaces the \" character, so that the result may be safely used as
04558d31
BG
136an attribute value in a tag. Returns a new string with the result of the
137conversion. Replaces these characters as follows:
8c8b8430
SM
138 & ==> &amp;
139 < ==> &lt;
140 > ==> &gt;
141 \" ==> &quot;"
142 (if (string-match "[&<>\"]" string)
937e6a56 143 (with-current-buffer (get-buffer-create " *entity*")
8c8b8430
SM
144 (erase-buffer)
145 (buffer-disable-undo (current-buffer))
146 (insert string)
147 (goto-char (point-min))
148 (while (progn
149 (skip-chars-forward "^&<>\"")
150 (not (eobp)))
151 (insert (cdr (assq (char-after (point))
152 '((?\" . "&quot;")
153 (?& . "&amp;")
154 (?< . "&lt;")
155 (?> . "&gt;")))))
156 (delete-char 1))
157 (buffer-string))
158 string))
159
160;;;###autoload
161(defun url-normalize-url (url)
162 "Return a 'normalized' version of URL.
163Strips out default port numbers, etc."
07e9b3f0 164 (let (type data retval)
8c8b8430
SM
165 (setq data (url-generic-parse-url url)
166 type (url-type data))
167 (if (member type '("www" "about" "mailto" "info"))
168 (setq retval url)
4968291f
GM
169 ;; FIXME all this does, and all this function seems to do in
170 ;; most cases, is remove any trailing "#anchor" part of a url.
d18ec89f 171 (setf (url-target data) nil)
8c8b8430
SM
172 (setq retval (url-recreate-url data)))
173 retval))
174
175;;;###autoload
176(defun url-lazy-message (&rest args)
177 "Just like `message', but is a no-op if called more than once a second.
a2fd1462 178Will not do anything if `url-show-status' is nil."
08b8ba9f
LMI
179 (if (or (and url-current-object
180 (url-silent url-current-object))
181 (null url-show-status)
8c8b8430
SM
182 (active-minibuffer-window)
183 (= url-lazy-message-time
184 (setq url-lazy-message-time (nth 1 (current-time)))))
185 nil
186 (apply 'message args)))
187
188;;;###autoload
189(defun url-get-normalized-date (&optional specified-time)
d626f888
TZ
190 "Return a 'real' date string that most HTTP servers can understand."
191 (let ((system-time-locale "C"))
192 (format-time-string "%a, %d %b %Y %T GMT"
193 (or specified-time (current-time)) t)))
8c8b8430
SM
194
195;;;###autoload
196(defun url-eat-trailing-space (x)
197 "Remove spaces/tabs at the end of a string."
198 (let ((y (1- (length x)))
199 (skip-chars (list ? ?\t ?\n)))
200 (while (and (>= y 0) (memq (aref x y) skip-chars))
201 (setq y (1- y)))
202 (substring x 0 (1+ y))))
203
204;;;###autoload
205(defun url-strip-leading-spaces (x)
206 "Remove spaces at the front of a string."
207 (let ((y (1- (length x)))
208 (z 0)
209 (skip-chars (list ? ?\t ?\n)))
210 (while (and (<= z y) (memq (aref x z) skip-chars))
211 (setq z (1+ z)))
212 (substring x z nil)))
213
214;;;###autoload
215(defun url-pretty-length (n)
216 (cond
217 ((< n 1024)
218 (format "%d bytes" n))
219 ((< n (* 1024 1024))
220 (format "%dk" (/ n 1024.0)))
221 (t
222 (format "%2.2fM" (/ n (* 1024 1024.0))))))
223
224;;;###autoload
225(defun url-display-percentage (fmt perc &rest args)
08b8ba9f
LMI
226 (when (and url-show-status
227 (or (null url-current-object)
228 (not (url-silent url-current-object))))
48d2bac4
MH
229 (if (null fmt)
230 (if (fboundp 'clear-progress-display)
231 (clear-progress-display))
232 (if (and (fboundp 'progress-display) perc)
233 (apply 'progress-display fmt perc args)
234 (apply 'message fmt args)))))
8c8b8430
SM
235
236;;;###autoload
237(defun url-percentage (x y)
238 (if (fboundp 'float)
239 (round (* 100 (/ x (float y))))
240 (/ (* x 100) y)))
241
08299ea7
CY
242;;;###autoload
243(defalias 'url-basepath 'url-file-directory)
244
8c8b8430 245;;;###autoload
76fb5862
RS
246(defun url-file-directory (file)
247 "Return the directory part of FILE, for a URL."
8c8b8430
SM
248 (cond
249 ((null file) "")
5589b70e 250 ((string-match "\\?" file)
577d5eea
DE
251 (url-file-directory (substring file 0 (match-beginning 0))))
252 ((string-match "\\(.*\\(/\\|%2[fF]\\)\\)" file)
253 (match-string 1 file))))
8c8b8430 254
76fb5862
RS
255;;;###autoload
256(defun url-file-nondirectory (file)
257 "Return the nondirectory part of FILE, for a URL."
258 (cond
259 ((null file) "")
5589b70e 260 ((string-match "\\?" file)
577d5eea
DE
261 (url-file-nondirectory (substring file 0 (match-beginning 0))))
262 ((string-match ".*\\(?:/\\|%2[fF]\\)\\(.*\\)" file)
263 (match-string 1 file))
264 (t file)))
76fb5862 265
8c8b8430 266;;;###autoload
6fd388f3 267(defun url-parse-query-string (query &optional downcase allow-newlines)
8c8b8430 268 (let (retval pairs cur key val)
530e968e 269 (setq pairs (split-string query "[;&]"))
8c8b8430
SM
270 (while pairs
271 (setq cur (car pairs)
272 pairs (cdr pairs))
530e968e
TZ
273 (unless (string-match "=" cur)
274 (setq cur (concat cur "=")))
275
276 (when (string-match "=" cur)
277 (setq key (url-unhex-string (substring cur 0 (match-beginning 0))
278 allow-newlines))
279 (setq val (url-unhex-string (substring cur (match-end 0) nil)
280 allow-newlines))
281 (if downcase
282 (setq key (downcase key)))
283 (setq cur (assoc key retval))
284 (if cur
285 (setcdr cur (cons val (cdr cur)))
286 (setq retval (cons (list key val) retval)))))
8c8b8430
SM
287 retval))
288
530e968e
TZ
289;;;###autoload
290(defun url-build-query-string (query &optional semicolons keep-empty)
291 "Build a query-string.
292
293Given a QUERY in the form:
294'((key1 val1)
295 (key2 val2)
296 (key3 val1 val2)
297 (key4)
04558d31 298 (key5 \"\"))
530e968e
TZ
299
300\(This is the same format as produced by `url-parse-query-string')
301
302This will return a string
303\"key1=val1&key2=val2&key3=val1&key3=val2&key4&key5\". Keys may
304be strings or symbols; if they are symbols, the symbol name will
305be used.
306
307When SEMICOLONS is given, the separator will be \";\".
308
309When KEEP-EMPTY is given, empty values will show as \"key=\"
310instead of just \"key\" as in the example above."
311 (mapconcat
312 (lambda (key-vals)
313 (let ((escaped
314 (mapcar (lambda (sym)
315 (url-hexify-string (format "%s" sym))) key-vals)))
316 (mapconcat (lambda (val)
317 (let ((vprint (format "%s" val))
318 (eprint (format "%s" (car escaped))))
319 (concat eprint
320 (if (or keep-empty
321 (and val (not (zerop (length vprint)))))
322 "="
323 "")
324 vprint)))
325 (or (cdr escaped) '("")) (if semicolons ";" "&"))))
326 query (if semicolons ";" "&")))
327
8c8b8430
SM
328(defun url-unhex (x)
329 (if (> x ?9)
330 (if (>= x ?a)
331 (+ 10 (- x ?a))
332 (+ 10 (- x ?A)))
333 (- x ?0)))
334
c6bfe6e7
SM
335;; Fixme: Is this definition better, and does it ever matter?
336
337;; (defun url-unhex-string (str &optional allow-newlines)
338;; "Remove %XX, embedded spaces, etc in a url.
339;; If optional second argument ALLOW-NEWLINES is non-nil, then allow the
340;; decoding of carriage returns and line feeds in the string, which is normally
341;; forbidden in URL encoding."
342;; (setq str (or str ""))
343;; (setq str (replace-regexp-in-string "%[[:xdigit:]]\\{2\\}"
344;; (lambda (match)
345;; (string (string-to-number
346;; (substring match 1) 16)))
347;; str t t))
348;; (if allow-newlines
349;; (replace-regexp-in-string "[\n\r]" (lambda (match)
350;; (format "%%%.2X" (aref match 0)))
351;; str t t)
352;; str))
353
8c8b8430 354;;;###autoload
8d8d31f9 355(defun url-unhex-string (str &optional allow-newlines)
d1ce47b0 356 "Remove %XX embedded spaces, etc in a URL.
8c8b8430
SM
357If optional second argument ALLOW-NEWLINES is non-nil, then allow the
358decoding of carriage returns and line feeds in the string, which is normally
8d8d31f9 359forbidden in URL encoding."
8c8b8430
SM
360 (setq str (or str ""))
361 (let ((tmp "")
362 (case-fold-search t))
363 (while (string-match "%[0-9a-f][0-9a-f]" str)
364 (let* ((start (match-beginning 0))
365 (ch1 (url-unhex (elt str (+ start 1))))
366 (code (+ (* 16 ch1)
367 (url-unhex (elt str (+ start 2))))))
368 (setq tmp (concat
369 tmp (substring str 0 start)
370 (cond
371 (allow-newlines
c3bb441d 372 (byte-to-string code))
8c8b8430
SM
373 ((or (= code ?\n) (= code ?\r))
374 " ")
c3bb441d 375 (t (byte-to-string code))))
8c8b8430 376 str (substring str (match-end 0)))))
d3f7611b 377 (concat tmp str)))
8c8b8430
SM
378
379(defconst url-unreserved-chars
ce7b18ec 380 '(?a ?b ?c ?d ?e ?f ?g ?h ?i ?j ?k ?l ?m ?n ?o ?p ?q ?r ?s ?t ?u ?v ?w ?x ?y ?z
8c8b8430
SM
381 ?A ?B ?C ?D ?E ?F ?G ?H ?I ?J ?K ?L ?M ?N ?O ?P ?Q ?R ?S ?T ?U ?V ?W ?X ?Y ?Z
382 ?0 ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9
ce7b18ec
CY
383 ?- ?_ ?. ?~)
384 "List of characters that are unreserved in the URL spec.
385This is taken from RFC 3986 (section 2.3).")
386
387(defconst url-encoding-table
388 (let ((vec (make-vector 256 nil)))
389 (dotimes (byte 256)
bdac2d37
CY
390 ;; RFC 3986 (Section 2.1): For consistency, URI producers and
391 ;; normalizers should use uppercase hexadecimal digits for all
392 ;; percent-encodings.
393 (aset vec byte (format "%%%02X" byte)))
ce7b18ec
CY
394 vec)
395 "Vector translating bytes to URI-encoded %-sequences.")
396
397(defun url--allowed-chars (char-list)
398 "Return an \"allowed character\" mask (a 256-slot vector).
399The Nth element is non-nil if character N is in CHAR-LIST. The
400result can be passed as the second arg to `url-hexify-string'."
401 (let ((vec (make-vector 256 nil)))
402 (dolist (byte char-list)
403 (ignore-errors (aset vec byte t)))
404 vec))
8c8b8430
SM
405
406;;;###autoload
ce7b18ec
CY
407(defun url-hexify-string (string &optional allowed-chars)
408 "URI-encode STRING and return the result.
409If STRING is multibyte, it is first converted to a utf-8 byte
410string. Each byte corresponding to an allowed character is left
411as-is, while all other bytes are converted to a three-character
bdac2d37 412string: \"%\" followed by two upper-case hex digits.
ce7b18ec
CY
413
414The allowed characters are specified by ALLOWED-CHARS. If this
415argument is nil, the list `url-unreserved-chars' determines the
416allowed characters. Otherwise, ALLOWED-CHARS should be a vector
417whose Nth element is non-nil if character N is allowed."
418 (unless allowed-chars
419 (setq allowed-chars (url--allowed-chars url-unreserved-chars)))
07e9b3f0 420 (mapconcat (lambda (byte)
ce7b18ec
CY
421 (if (aref allowed-chars byte)
422 (char-to-string byte)
423 (aref url-encoding-table byte)))
424 (if (multibyte-string-p string)
425 (encode-coding-string string 'utf-8)
426 string)
427 ""))
428
429(defconst url-host-allowed-chars
430 ;; Allow % to avoid re-encoding %-encoded sequences.
431 (url--allowed-chars (append '(?% ?! ?$ ?& ?' ?\( ?\) ?* ?+ ?, ?\; ?=)
432 url-unreserved-chars))
433 "Allowed-character byte mask for the host segment of a URI.
434These characters are specified in RFC 3986, Appendix A.")
435
436(defconst url-path-allowed-chars
437 (let ((vec (copy-sequence url-host-allowed-chars)))
438 (aset vec ?/ t)
439 (aset vec ?: t)
440 (aset vec ?@ t)
441 vec)
442 "Allowed-character byte mask for the path segment of a URI.
443These characters are specified in RFC 3986, Appendix A.")
444
445(defconst url-query-allowed-chars
446 (let ((vec (copy-sequence url-path-allowed-chars)))
447 (aset vec ?? t)
448 vec)
449 "Allowed-character byte mask for the query segment of a URI.
450These characters are specified in RFC 3986, Appendix A.")
451
452;;;###autoload
453(defun url-encode-url (url)
454 "Return a properly URI-encoded version of URL.
455This function also performs URI normalization, e.g. converting
456the scheme to lowercase if it is uppercase. Apart from
457normalization, if URL is already URI-encoded, this function
458should return it unchanged."
459 (if (multibyte-string-p url)
460 (setq url (encode-coding-string url 'utf-8)))
461 (let* ((obj (url-generic-parse-url url))
462 (user (url-user obj))
463 (pass (url-password obj))
464 (host (url-host obj))
9f9aa044
CY
465 (path-and-query (url-path-and-query obj))
466 (path (car path-and-query))
467 (query (cdr path-and-query))
468 (frag (url-target obj)))
ce7b18ec
CY
469 (if user
470 (setf (url-user obj) (url-hexify-string user)))
471 (if pass
472 (setf (url-password obj) (url-hexify-string pass)))
9f9aa044
CY
473 ;; No special encoding for IPv6 literals.
474 (and host
475 (not (string-match "\\`\\[.*\\]\\'" host))
476 (setf (url-host obj)
477 (url-hexify-string host url-host-allowed-chars)))
478
479 (if path
480 (setq path (url-hexify-string path url-path-allowed-chars)))
481 (if query
482 (setq query (url-hexify-string query url-query-allowed-chars)))
483 (setf (url-filename obj) (if query (concat path "?" query) path))
484
ce7b18ec
CY
485 (if frag
486 (setf (url-target obj)
487 (url-hexify-string frag url-query-allowed-chars)))
488 (url-recreate-url obj)))
8c8b8430
SM
489
490;;;###autoload
491(defun url-file-extension (fname &optional x)
492 "Return the filename extension of FNAME.
d1ce47b0
JB
493If optional argument X is t, then return the basename
494of the file with the extension stripped off."
8c8b8430 495 (if (and fname
76fb5862 496 (setq fname (url-file-nondirectory fname))
8c8b8430
SM
497 (string-match "\\.[^./]+$" fname))
498 (if x (substring fname 0 (match-beginning 0))
499 (substring fname (match-beginning 0) nil))
500 ;;
501 ;; If fname has no extension, and x then return fname itself instead of
502 ;; nothing. When caching it allows the correct .hdr file to be produced
503 ;; for filenames without extension.
504 ;;
505 (if x
506 fname
507 "")))
508
509;;;###autoload
510(defun url-truncate-url-for-viewing (url &optional width)
d9e52e92 511 "Return a shortened version of URL that is WIDTH characters wide or less.
8c8b8430
SM
512WIDTH defaults to the current frame width."
513 (let* ((fr-width (or width (frame-width)))
514 (str-width (length url))
8c8b8430
SM
515 (fname nil)
516 (modified 0)
517 (urlobj nil))
518 ;; The first thing that can go are the search strings
519 (if (and (>= str-width fr-width)
520 (string-match "?" url))
521 (setq url (concat (substring url 0 (match-beginning 0)) "?...")
07e9b3f0 522 str-width (length url)))
8c8b8430
SM
523 (if (< str-width fr-width)
524 nil ; Hey, we are done!
525 (setq urlobj (url-generic-parse-url url)
526 fname (url-filename urlobj)
527 fr-width (- fr-width 4))
528 (while (and (>= str-width fr-width)
529 (string-match "/" fname))
530 (setq fname (substring fname (match-end 0) nil)
531 modified (1+ modified))
d18ec89f 532 (setf (url-filename urlobj) fname)
8c8b8430
SM
533 (setq url (url-recreate-url urlobj)
534 str-width (length url)))
535 (if (> modified 1)
536 (setq fname (concat "/.../" fname))
537 (setq fname (concat "/" fname)))
d18ec89f 538 (setf (url-filename urlobj) fname)
8c8b8430
SM
539 (setq url (url-recreate-url urlobj)))
540 url))
541
542;;;###autoload
543(defun url-view-url (&optional no-show)
544 "View the current document's URL.
545Optional argument NO-SHOW means just return the URL, don't show it in
546the minibuffer.
547
548This uses `url-current-object', set locally to the buffer."
549 (interactive)
550 (if (not url-current-object)
551 nil
552 (if no-show
553 (url-recreate-url url-current-object)
554 (message "%s" (url-recreate-url url-current-object)))))
555
5589b70e
GM
556(defvar url-get-url-filename-chars "-%.?@a-zA-Z0-9()_/:~=&"
557 "Valid characters in a URL.")
8c8b8430
SM
558
559(defun url-get-url-at-point (&optional pt)
560 "Get the URL closest to point, but don't change position.
561Has a preference for looking backward when not directly on a symbol."
562 ;; Not at all perfect - point must be right in the name.
563 (save-excursion
564 (if pt (goto-char pt))
565 (let (start url)
566 (save-excursion
567 ;; first see if you're just past a filename
568 (if (not (eobp))
569 (if (looking-at "[] \t\n[{}()]") ; whitespace or some parens
570 (progn
571 (skip-chars-backward " \n\t\r({[]})")
572 (if (not (bobp))
573 (backward-char 1)))))
574 (if (and (char-after (point))
5589b70e 575 (string-match (concat "[" url-get-url-filename-chars "]")
8c8b8430
SM
576 (char-to-string (char-after (point)))))
577 (progn
578 (skip-chars-backward url-get-url-filename-chars)
579 (setq start (point))
580 (skip-chars-forward url-get-url-filename-chars))
581 (setq start (point)))
582 (setq url (buffer-substring-no-properties start (point))))
583 (if (and url (string-match "^(.*)\\.?$" url))
584 (setq url (match-string 1 url)))
585 (if (and url (string-match "^URL:" url))
586 (setq url (substring url 4 nil)))
587 (if (and url (string-match "\\.$" url))
588 (setq url (substring url 0 -1)))
589 (if (and url (string-match "^www\\." url))
590 (setq url (concat "http://" url)))
591 (if (and url (not (string-match url-nonrelative-link url)))
592 (setq url nil))
593 url)))
594
595(defun url-generate-unique-filename (&optional fmt)
596 "Generate a unique filename in `url-temporary-directory'."
59f7af81 597 (declare (obsolete make-temp-file "23.1"))
f0cfa8f0
GM
598 ;; This variable is obsolete, but so is this function.
599 (let ((tempdir (with-no-warnings url-temporary-directory)))
600 (if (not fmt)
601 (let ((base (format "url-tmp.%d" (user-real-uid)))
602 (fname "")
603 (x 0))
604 (setq fname (format "%s%d" base x))
605 (while (file-exists-p
606 (expand-file-name fname tempdir))
607 (setq x (1+ x)
608 fname (concat base (int-to-string x))))
609 (expand-file-name fname tempdir))
610 (let ((base (concat "url" (int-to-string (user-real-uid))))
8c8b8430
SM
611 (fname "")
612 (x 0))
f0cfa8f0 613 (setq fname (format fmt (concat base (int-to-string x))))
8c8b8430 614 (while (file-exists-p
f0cfa8f0 615 (expand-file-name fname tempdir))
8c8b8430 616 (setq x (1+ x)
f0cfa8f0
GM
617 fname (format fmt (concat base (int-to-string x)))))
618 (expand-file-name fname tempdir)))))
8c8b8430
SM
619
620(defun url-extract-mime-headers ()
621 "Set `url-current-mime-headers' in current buffer."
622 (save-excursion
623 (goto-char (point-min))
624 (unless url-current-mime-headers
625 (set (make-local-variable 'url-current-mime-headers)
626 (mail-header-extract)))))
627
8703ea53
GM
628(defun url-make-private-file (file)
629 "Make FILE only readable and writable by the current user.
630Creates FILE and its parent directories if they do not exist."
631 (let ((dir (file-name-directory file)))
632 (when dir
633 ;; For historical reasons.
634 (make-directory dir t)))
635 ;; Based on doc-view-make-safe-dir.
636 (condition-case nil
637 (let ((umask (default-file-modes)))
638 (unwind-protect
639 (progn
640 (set-default-file-modes #o0600)
641 (with-temp-buffer
642 (write-region (point-min) (point-max)
643 file nil 'silent nil 'excl)))
644 (set-default-file-modes umask)))
645 (file-already-exists
646 (if (file-symlink-p file)
647 (error "Danger: `%s' is a symbolic link" file))
648 (set-file-modes file #o0600))))
649
8c8b8430 650(provide 'url-util)
e5566bd5 651
a2fd1462 652;;; url-util.el ends here