(kmacro-lambda-form, kmacro-extract-lambda): Add.
[bpt/emacs.git] / lisp / gnus / mailcap.el
CommitLineData
c0393b5e 1;;; mailcap.el --- MIME media types configuration
23f87bed
MB
2;; Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003
3;; Free Software Foundation, Inc.
c113de23
GM
4
5;; Author: William M. Perry <wmperry@aventail.com>
6;; Lars Magne Ingebrigtsen <larsi@gnus.org>
c0393b5e 7;; Keywords: news, mail, multimedia
c113de23
GM
8
9;; This file is part of GNU Emacs.
10
11;; GNU Emacs is free software; you can redistribute it and/or modify
12;; it under the terms of the GNU General Public License as published by
13;; the Free Software Foundation; either version 2, or (at your option)
14;; any later version.
15
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.
20
21;; You should have received a copy of the GNU General Public License
22;; along with GNU Emacs; see the file COPYING. If not, write to the
23;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24;; Boston, MA 02111-1307, USA.
25
26;;; Commentary:
27
c0393b5e
DL
28;; Provides configuration of MIME media types from directly from Lisp
29;; and via the usual mailcap mechanism (RFC 1524). Deals with
30;; mime.types similarly.
31
c113de23
GM
32;;; Code:
33
34(eval-when-compile (require 'cl))
35(require 'mail-parse)
36(require 'mm-util)
37
c0393b5e
DL
38(defgroup mailcap nil
39 "Definition of viewers for MIME types."
40 :version "21.1"
41 :group 'mime)
42
c113de23
GM
43(defvar mailcap-parse-args-syntax-table
44 (let ((table (copy-syntax-table emacs-lisp-mode-syntax-table)))
45 (modify-syntax-entry ?' "\"" table)
46 (modify-syntax-entry ?` "\"" table)
47 (modify-syntax-entry ?{ "(" table)
48 (modify-syntax-entry ?} ")" table)
49 table)
23f87bed
MB
50 "A syntax table for parsing SGML attributes.")
51
52(eval-and-compile
53 (when (featurep 'xemacs)
54 (condition-case nil
55 (require 'lpr)
56 (error nil))))
57
58(defvar mailcap-print-command
59 (mapconcat 'identity
60 (cons (if (boundp 'lpr-command)
61 lpr-command
62 "lpr")
63 (when (boundp 'lpr-switches)
64 (if (stringp lpr-switches)
65 (list lpr-switches)
66 lpr-switches)))
67 " ")
68 "Shell command (including switches) used to print Postscript files.")
c113de23 69
c0393b5e
DL
70;; Postpone using defcustom for this as it's so big and we essentially
71;; have to have two copies of the data around then. Perhaps just
72;; customize the Lisp viewers and rely on the normal configuration
73;; files for the rest? -- fx
c113de23 74(defvar mailcap-mime-data
23f87bed
MB
75 `(("application"
76 ("vnd.ms-excel"
77 (viewer . "gnumeric %s")
78 (test . (getenv "DISPLAY"))
79 (type . "application/vnd.ms-excel"))
c113de23
GM
80 ("x-x509-ca-cert"
81 (viewer . ssl-view-site-cert)
82 (test . (fboundp 'ssl-view-site-cert))
83 (type . "application/x-x509-ca-cert"))
84 ("x-x509-user-cert"
85 (viewer . ssl-view-user-cert)
86 (test . (fboundp 'ssl-view-user-cert))
87 (type . "application/x-x509-user-cert"))
88 ("octet-stream"
89 (viewer . mailcap-save-binary-file)
90 (non-viewer . t)
91 (type . "application/octet-stream"))
c113de23 92 ("dvi"
23f87bed
MB
93 (viewer . "xdvi -safer %s")
94 (test . (eq window-system 'x))
c113de23 95 ("needsx11")
23f87bed
MB
96 (type . "application/dvi")
97 ("print" . "dvips -qRP %s"))
c113de23
GM
98 ("dvi"
99 (viewer . "dvitty %s")
100 (test . (not (getenv "DISPLAY")))
23f87bed
MB
101 (type . "application/dvi")
102 ("print" . "dvips -qRP %s"))
c113de23
GM
103 ("emacs-lisp"
104 (viewer . mailcap-maybe-eval)
105 (type . "application/emacs-lisp"))
23f87bed
MB
106 ("x-emacs-lisp"
107 (viewer . mailcap-maybe-eval)
108 (type . "application/x-emacs-lisp"))
c113de23
GM
109 ("x-tar"
110 (viewer . mailcap-save-binary-file)
111 (non-viewer . t)
112 (type . "application/x-tar"))
113 ("x-latex"
114 (viewer . tex-mode)
115 (test . (fboundp 'tex-mode))
116 (type . "application/x-latex"))
117 ("x-tex"
118 (viewer . tex-mode)
119 (test . (fboundp 'tex-mode))
120 (type . "application/x-tex"))
121 ("latex"
122 (viewer . tex-mode)
123 (test . (fboundp 'tex-mode))
124 (type . "application/latex"))
125 ("tex"
126 (viewer . tex-mode)
127 (test . (fboundp 'tex-mode))
128 (type . "application/tex"))
129 ("texinfo"
130 (viewer . texinfo-mode)
131 (test . (fboundp 'texinfo-mode))
132 (type . "application/tex"))
133 ("zip"
134 (viewer . mailcap-save-binary-file)
135 (non-viewer . t)
136 (type . "application/zip")
137 ("copiousoutput"))
c0393b5e
DL
138 ;; Prefer free viewers.
139 ("pdf"
23f87bed 140 (viewer . "gv -safer %s")
c0393b5e 141 (type . "application/pdf")
23f87bed
MB
142 (test . window-system)
143 ("print" . ,(concat "pdf2ps %s - | " mailcap-print-command)))
c0393b5e
DL
144 ("pdf"
145 (viewer . "xpdf %s")
146 (type . "application/pdf")
23f87bed
MB
147 ("print" . ,(concat "pdftops %s - | " mailcap-print-command))
148 (test . (eq window-system 'x)))
c113de23
GM
149 ("pdf"
150 (viewer . "acroread %s")
23f87bed
MB
151 (type . "application/pdf")
152 ("print" . ,(concat "cat %s | acroread -toPostScript | "
153 mailcap-print-command))
154 (test . window-system))
155 ("pdf"
156 (viewer . ,(concat "pdftotext %s -"))
157 (type . "application/pdf")
158 ("print" . ,(concat "pdftops %s - | " mailcap-print-command))
159 ("copiousoutput"))
c113de23 160 ("postscript"
c0393b5e
DL
161 (viewer . "gv -safer %s")
162 (type . "application/postscript")
163 (test . window-system)
23f87bed 164 ("print" . ,(concat mailcap-print-command " %s"))
c0393b5e 165 ("needsx11"))
c113de23
GM
166 ("postscript"
167 (viewer . "ghostview -dSAFER %s")
168 (type . "application/postscript")
23f87bed
MB
169 (test . (eq window-system 'x))
170 ("print" . ,(concat mailcap-print-command " %s"))
c113de23
GM
171 ("needsx11"))
172 ("postscript"
173 (viewer . "ps2ascii %s")
174 (type . "application/postscript")
175 (test . (not (getenv "DISPLAY")))
23f87bed
MB
176 ("print" . ,(concat mailcap-print-command " %s"))
177 ("copiousoutput"))
178 ("sieve"
179 (viewer . sieve-mode)
180 (test . (fboundp 'sieve-mode))
181 (type . "application/sieve"))
182 ("pgp-keys"
183 (viewer . "gpg --import --interactive --verbose")
184 (type . "application/pgp-keys")
185 ("needsterminal")))
c113de23
GM
186 ("audio"
187 ("x-mpeg"
188 (viewer . "maplay %s")
189 (type . "audio/x-mpeg"))
190 (".*"
191 (viewer . "showaudio")
192 (type . "audio/*")))
193 ("message"
194 ("rfc-*822"
195 (viewer . mm-view-message)
196 (test . (and (featurep 'gnus)
197 (gnus-alive-p)))
198 (type . "message/rfc822"))
199 ("rfc-*822"
200 (viewer . vm-mode)
201 (test . (fboundp 'vm-mode))
202 (type . "message/rfc822"))
203 ("rfc-*822"
204 (viewer . w3-mode)
205 (test . (fboundp 'w3-mode))
206 (type . "message/rfc822"))
207 ("rfc-*822"
208 (viewer . view-mode)
c113de23
GM
209 (type . "message/rfc822")))
210 ("image"
211 ("x-xwd"
212 (viewer . "xwud -in %s")
213 (type . "image/x-xwd")
214 ("compose" . "xwd -frame > %s")
23f87bed 215 (test . (eq window-system 'x))
c113de23
GM
216 ("needsx11"))
217 ("x11-dump"
218 (viewer . "xwud -in %s")
219 (type . "image/x-xwd")
220 ("compose" . "xwd -frame > %s")
23f87bed 221 (test . (eq window-system 'x))
c113de23
GM
222 ("needsx11"))
223 ("windowdump"
224 (viewer . "xwud -in %s")
225 (type . "image/x-xwd")
226 ("compose" . "xwd -frame > %s")
23f87bed 227 (test . (eq window-system 'x))
c113de23 228 ("needsx11"))
c113de23
GM
229 (".*"
230 (viewer . "display %s")
231 (type . "image/*")
23f87bed 232 (test . (eq window-system 'x))
c113de23
GM
233 ("needsx11"))
234 (".*"
235 (viewer . "ee %s")
236 (type . "image/*")
23f87bed 237 (test . (eq window-system 'x))
c113de23
GM
238 ("needsx11")))
239 ("text"
240 ("plain"
241 (viewer . w3-mode)
242 (test . (fboundp 'w3-mode))
243 (type . "text/plain"))
244 ("plain"
245 (viewer . view-mode)
246 (test . (fboundp 'view-mode))
247 (type . "text/plain"))
248 ("plain"
249 (viewer . fundamental-mode)
250 (type . "text/plain"))
251 ("enriched"
23f87bed 252 (viewer . enriched-decode)
c113de23
GM
253 (test . (fboundp 'enriched-decode))
254 (type . "text/enriched"))
255 ("html"
256 (viewer . mm-w3-prepare-buffer)
257 (test . (fboundp 'w3-prepare-buffer))
258 (type . "text/html")))
259 ("video"
260 ("mpeg"
261 (viewer . "mpeg_play %s")
262 (type . "video/mpeg")
23f87bed 263 (test . (eq window-system 'x))
c113de23
GM
264 ("needsx11")))
265 ("x-world"
266 ("x-vrml"
267 (viewer . "webspace -remote %s -URL %u")
268 (type . "x-world/x-vrml")
269 ("description"
270 "VRML document")))
271 ("archive"
272 ("tar"
273 (viewer . tar-mode)
274 (type . "archive/tar")
275 (test . (fboundp 'tar-mode)))))
276 "The mailcap structure is an assoc list of assoc lists.
2771st assoc list is keyed on the major content-type
2782nd assoc list is keyed on the minor content-type (which can be a regexp)
279
280Which looks like:
281-----------------
282 ((\"application\"
283 (\"postscript\" . <info>))
284 (\"text\"
285 (\"plain\" . <info>)))
286
287Where <info> is another assoc list of the various information
c0393b5e 288related to the mailcap RFC 1524. This is keyed on the lowercase
c113de23 289attribute name (viewer, test, etc). This looks like:
c0393b5e
DL
290 ((viewer . VIEWERINFO)
291 (test . TESTINFO)
292 (xxxx . \"STRING\")
293 FLAG)
c113de23 294
c0393b5e 295Where VIEWERINFO specifies how the content-type is viewed. Can be
c113de23
GM
296a string, in which case it is run through a shell, with
297appropriate parameters, or a symbol, in which case the symbol is
c0393b5e
DL
298`funcall'ed, with the buffer as an argument.
299
300TESTINFO is a test for the viewer's applicability, or nil. If nil, it
301means the viewer is always valid. If it is a Lisp function, it is
302called with a list of items from any extra fields from the
303Content-Type header as argument to return a boolean value for the
304validity. Otherwise, if it is a non-function Lisp symbol or list
305whose car is a symbol, it is `eval'led to yield the validity. If it
306is a string or list of strings, it represents a shell command to run
307to return a true or false shell value for the validity.")
f8bbbafb 308(put 'mailcap-mime-data 'risky-local-variable t)
c0393b5e
DL
309
310(defcustom mailcap-download-directory nil
003dabb7 311 "*Directory to which `mailcap-save-binary-file' downloads files by default.
f0529b5b 312nil means your home directory."
003dabb7
DL
313 :type '(choice (const :tag "Home directory" nil)
314 directory)
c0393b5e 315 :group 'mailcap)
c113de23 316
23f87bed
MB
317(defvar mailcap-poor-system-types
318 '(ms-dos ms-windows windows-nt win32 w32 mswindows)
319 "Systems that don't have a Unix-like directory hierarchy.")
320
c113de23
GM
321;;;
322;;; Utility functions
323;;;
324
c113de23
GM
325(defun mailcap-save-binary-file ()
326 (goto-char (point-min))
327 (unwind-protect
328 (let ((file (read-file-name
329 "Filename to save as: "
330 (or mailcap-download-directory "~/")))
331 (require-final-newline nil))
332 (write-region (point-min) (point-max) file))
333 (kill-buffer (current-buffer))))
334
335(defvar mailcap-maybe-eval-warning
336 "*** WARNING ***
337
c0393b5e 338This MIME part contains untrusted and possibly harmful content.
c113de23
GM
339If you evaluate the Emacs Lisp code contained in it, a lot of nasty
340things can happen. Please examine the code very carefully before you
341instruct Emacs to evaluate it. You can browse the buffer containing
342the code using \\[scroll-other-window].
343
344If you are unsure what to do, please answer \"no\"."
345 "Text of warning message displayed by `mailcap-maybe-eval'.
346Make sure that this text consists only of few text lines. Otherwise,
347Gnus might fail to display all of it.")
a1506d29 348
c113de23 349(defun mailcap-maybe-eval ()
c0393b5e 350 "Maybe evaluate a buffer of Emacs Lisp code."
c113de23
GM
351 (let ((lisp-buffer (current-buffer)))
352 (goto-char (point-min))
353 (when
354 (save-window-excursion
355 (delete-other-windows)
356 (let ((buffer (get-buffer-create (generate-new-buffer-name
357 "*Warning*"))))
358 (unwind-protect
359 (with-current-buffer buffer
c0393b5e 360 (insert (substitute-command-keys
c113de23
GM
361 mailcap-maybe-eval-warning))
362 (goto-char (point-min))
363 (display-buffer buffer)
364 (yes-or-no-p "This is potentially dangerous emacs-lisp code, evaluate it? "))
365 (kill-buffer buffer))))
366 (eval-buffer (current-buffer)))
367 (when (buffer-live-p lisp-buffer)
368 (with-current-buffer lisp-buffer
369 (emacs-lisp-mode)))))
370
371
372;;;
373;;; The mailcap parser
374;;;
375
376(defun mailcap-replace-regexp (regexp to-string)
377 ;; Quiet replace-regexp.
378 (goto-char (point-min))
379 (while (re-search-forward regexp nil t)
380 (replace-match to-string t nil)))
381
382(defvar mailcap-parsed-p nil)
383
384(defun mailcap-parse-mailcaps (&optional path force)
385 "Parse out all the mailcaps specified in a path string PATH.
386Components of PATH are separated by the `path-separator' character
387appropriate for this system. If FORCE, re-parse even if already
388parsed. If PATH is omitted, use the value of environment variable
389MAILCAPS if set; otherwise (on Unix) use the path from RFC 1524, plus
390/usr/local/etc/mailcap."
391 (interactive (list nil t))
392 (when (or (not mailcap-parsed-p)
393 force)
394 (cond
395 (path nil)
396 ((getenv "MAILCAPS") (setq path (getenv "MAILCAPS")))
23f87bed 397 ((memq system-type mailcap-poor-system-types)
c113de23
GM
398 (setq path '("~/.mailcap" "~/mail.cap" "~/etc/mail.cap")))
399 (t (setq path
400 ;; This is per RFC 1524, specifically
401 ;; with /usr before /usr/local.
402 '("~/.mailcap" "/etc/mailcap" "/usr/etc/mailcap"
403 "/usr/local/etc/mailcap"))))
404 (let ((fnames (reverse
405 (if (stringp path)
9b198954 406 (delete "" (split-string path path-separator))
c113de23
GM
407 path)))
408 fname)
409 (while fnames
410 (setq fname (car fnames))
411 (if (and (file-readable-p fname)
412 (file-regular-p fname))
413 (mailcap-parse-mailcap fname))
414 (setq fnames (cdr fnames))))
415 (setq mailcap-parsed-p t)))
416
417(defun mailcap-parse-mailcap (fname)
c0393b5e 418 "Parse out the mailcap file specified by FNAME."
c113de23
GM
419 (let (major ; The major mime type (image/audio/etc)
420 minor ; The minor mime type (gif, basic, etc)
421 save-pos ; Misc saved positions used in parsing
422 viewer ; How to view this mime type
423 info ; Misc info about this mime type
424 )
425 (with-temp-buffer
426 (insert-file-contents fname)
427 (set-syntax-table mailcap-parse-args-syntax-table)
428 (mailcap-replace-regexp "#.*" "") ; Remove all comments
429 (mailcap-replace-regexp "\\\\[ \t]*\n" " ") ; And collapse spaces
430 (mailcap-replace-regexp "\n+" "\n") ; And blank lines
431 (goto-char (point-max))
432 (skip-chars-backward " \t\n")
433 (delete-region (point) (point-max))
434 (while (not (bobp))
435 (skip-chars-backward " \t\n")
436 (beginning-of-line)
437 (setq save-pos (point)
438 info nil)
439 (skip-chars-forward "^/; \t\n")
440 (downcase-region save-pos (point))
441 (setq major (buffer-substring save-pos (point)))
442 (skip-chars-forward " \t")
443 (setq minor "")
444 (when (eq (char-after) ?/)
445 (forward-char)
446 (skip-chars-forward " \t")
447 (setq save-pos (point))
448 (skip-chars-forward "^; \t\n")
449 (downcase-region save-pos (point))
450 (setq minor
451 (cond
452 ((eq ?* (or (char-after save-pos) 0)) ".*")
453 ((= (point) save-pos) ".*")
454 (t (regexp-quote (buffer-substring save-pos (point)))))))
455 (skip-chars-forward " \t")
456 ;;; Got the major/minor chunks, now for the viewers/etc
457 ;;; The first item _must_ be a viewer, according to the
c0393b5e 458 ;;; RFC for mailcap files (#1524)
c113de23 459 (setq viewer "")
c0393b5e 460 (when (eq (char-after) ?\;)
c113de23
GM
461 (forward-char)
462 (skip-chars-forward " \t")
463 (setq save-pos (point))
464 (skip-chars-forward "^;\n")
465 ;; skip \;
466 (while (eq (char-before) ?\\)
467 (backward-delete-char 1)
468 (forward-char)
469 (skip-chars-forward "^;\n"))
470 (if (eq (or (char-after save-pos) 0) ?')
471 (setq viewer (progn
472 (narrow-to-region (1+ save-pos) (point))
473 (goto-char (point-min))
474 (prog1
475 (read (current-buffer))
476 (goto-char (point-max))
477 (widen))))
478 (setq viewer (buffer-substring save-pos (point)))))
479 (setq save-pos (point))
480 (end-of-line)
c0393b5e 481 (unless (equal viewer "")
c113de23
GM
482 (setq info (nconc (list (cons 'viewer viewer)
483 (cons 'type (concat major "/"
484 (if (string= minor ".*")
485 "*" minor))))
486 (mailcap-parse-mailcap-extras save-pos (point))))
487 (mailcap-mailcap-entry-passes-test info)
488 (mailcap-add-mailcap-entry major minor info))
489 (beginning-of-line)))))
490
491(defun mailcap-parse-mailcap-extras (st nd)
c0393b5e 492 "Grab all the extra stuff from a mailcap entry."
c113de23
GM
493 (let (
494 name ; From name=
495 value ; its value
496 results ; Assoc list of results
497 name-pos ; Start of XXXX= position
498 val-pos ; Start of value position
499 done ; Found end of \'d ;s?
500 )
501 (save-restriction
502 (narrow-to-region st nd)
503 (goto-char (point-min))
504 (skip-chars-forward " \n\t;")
505 (while (not (eobp))
506 (setq done nil)
507 (setq name-pos (point))
508 (skip-chars-forward "^ \n\t=;")
509 (downcase-region name-pos (point))
510 (setq name (buffer-substring name-pos (point)))
511 (skip-chars-forward " \t\n")
512 (if (not (eq (char-after (point)) ?=)) ; There is no value
513 (setq value t)
514 (skip-chars-forward " \t\n=")
515 (setq val-pos (point))
516 (if (memq (char-after val-pos) '(?\" ?'))
517 (progn
518 (setq val-pos (1+ val-pos))
519 (condition-case nil
520 (progn
521 (forward-sexp 1)
522 (backward-char 1))
523 (error (goto-char (point-max)))))
524 (while (not done)
525 (skip-chars-forward "^;")
526 (if (eq (char-after (1- (point))) ?\\ )
527 (progn
528 (subst-char-in-region (1- (point)) (point) ?\\ ? )
529 (skip-chars-forward ";"))
530 (setq done t))))
531 (setq value (buffer-substring val-pos (point))))
532 (setq results (cons (cons name value) results))
533 (skip-chars-forward " \";\n\t"))
534 results)))
535
536(defun mailcap-mailcap-entry-passes-test (info)
c0393b5e
DL
537 "Return non-nil iff mailcap entry INFO passes its test clause.
538Also return non-nil if no test clause is present."
539 (let ((test (assq 'test info)) ; The test clause
540 status)
c113de23
GM
541 (setq status (and test (split-string (cdr test) " ")))
542 (if (and (or (assoc "needsterm" info)
543 (assoc "needsterminal" info)
544 (assoc "needsx11" info))
545 (not (getenv "DISPLAY")))
546 (setq status nil)
547 (cond
548 ((and (equal (nth 0 status) "test")
549 (equal (nth 1 status) "-n")
550 (or (equal (nth 2 status) "$DISPLAY")
551 (equal (nth 2 status) "\"$DISPLAY\"")))
552 (setq status (if (getenv "DISPLAY") t nil)))
553 ((and (equal (nth 0 status) "test")
554 (equal (nth 1 status) "-z")
555 (or (equal (nth 2 status) "$DISPLAY")
556 (equal (nth 2 status) "\"$DISPLAY\"")))
557 (setq status (if (getenv "DISPLAY") nil t)))
558 (test nil)
559 (t nil)))
560 (and test (listp test) (setcdr test status))))
561
562;;;
563;;; The action routines.
564;;;
565
566(defun mailcap-possible-viewers (major minor)
c0393b5e 567 "Return a list of possible viewers from MAJOR for minor type MINOR."
c113de23
GM
568 (let ((exact '())
569 (wildcard '()))
570 (while major
571 (cond
572 ((equal (car (car major)) minor)
573 (setq exact (cons (cdr (car major)) exact)))
23f87bed 574 ((and minor (string-match (concat "^" (car (car major)) "$") minor))
c113de23
GM
575 (setq wildcard (cons (cdr (car major)) wildcard))))
576 (setq major (cdr major)))
577 (nconc exact wildcard)))
578
579(defun mailcap-unescape-mime-test (test type-info)
580 (let (save-pos save-chr subst)
581 (cond
582 ((symbolp test) test)
583 ((and (listp test) (symbolp (car test))) test)
584 ((or (stringp test)
585 (and (listp test) (stringp (car test))
586 (setq test (mapconcat 'identity test " "))))
587 (with-temp-buffer
588 (insert test)
589 (goto-char (point-min))
590 (while (not (eobp))
591 (skip-chars-forward "^%")
592 (if (/= (- (point)
593 (progn (skip-chars-backward "\\\\")
594 (point)))
595 0) ; It is an escaped %
596 (progn
597 (delete-char 1)
598 (skip-chars-forward "%."))
599 (setq save-pos (point))
600 (skip-chars-forward "%")
601 (setq save-chr (char-after (point)))
c0393b5e
DL
602 ;; Escapes:
603 ;; %s: name of a file for the body data
604 ;; %t: content-type
605 ;; %{<parameter name}: value of parameter in mailcap entry
606 ;; %n: number of sub-parts for multipart content-type
607 ;; %F: a set of content-type/filename pairs for multiparts
c113de23
GM
608 (cond
609 ((null save-chr) nil)
610 ((= save-chr ?t)
611 (delete-region save-pos (progn (forward-char 1) (point)))
612 (insert (or (cdr (assq 'type type-info)) "\"\"")))
c0393b5e 613 ((memq save-chr '(?M ?n ?F))
c113de23
GM
614 (delete-region save-pos (progn (forward-char 1) (point)))
615 (insert "\"\""))
616 ((= save-chr ?{)
617 (forward-char 1)
618 (skip-chars-forward "^}")
619 (downcase-region (+ 2 save-pos) (point))
620 (setq subst (buffer-substring (+ 2 save-pos) (point)))
621 (delete-region save-pos (1+ (point)))
622 (insert (or (cdr (assoc subst type-info)) "\"\"")))
623 (t nil))))
624 (buffer-string)))
c0393b5e 625 (t (error "Bad value to mailcap-unescape-mime-test: %s" test)))))
c113de23
GM
626
627(defvar mailcap-viewer-test-cache nil)
628
629(defun mailcap-viewer-passes-test (viewer-info type-info)
c0393b5e 630 "Return non-nil iff viewer specified by VIEWER-INFO passes its test clause.
23f87bed 631Also return non-nil if it has no test clause. TYPE-INFO is an argument
c0393b5e 632to supply to the test."
c113de23
GM
633 (let* ((test-info (assq 'test viewer-info))
634 (test (cdr test-info))
635 (otest test)
636 (viewer (cdr (assoc 'viewer viewer-info)))
637 (default-directory (expand-file-name "~/"))
638 status parsed-test cache result)
639 (if (setq cache (assoc test mailcap-viewer-test-cache))
640 (cadr cache)
641 (setq
642 result
643 (cond
644 ((not test-info) t) ; No test clause
645 ((not test) nil) ; Already failed test
646 ((eq test t) t) ; Already passed test
c0393b5e 647 ((functionp test) ; Lisp function as test
c113de23
GM
648 (funcall test type-info))
649 ((and (symbolp test) ; Lisp variable as test
650 (boundp test))
651 (symbol-value test))
652 ((and (listp test) ; List to be eval'd
653 (symbolp (car test)))
654 (eval test))
655 (t
656 (setq test (mailcap-unescape-mime-test test type-info)
657 test (list shell-file-name nil nil nil
658 shell-command-switch test)
659 status (apply 'call-process test))
23f87bed 660 (eq 0 status))))
c113de23
GM
661 (push (list otest result) mailcap-viewer-test-cache)
662 result)))
663
664(defun mailcap-add-mailcap-entry (major minor info)
665 (let ((old-major (assoc major mailcap-mime-data)))
666 (if (null old-major) ; New major area
667 (setq mailcap-mime-data
668 (cons (cons major (list (cons minor info)))
669 mailcap-mime-data))
23f87bed
MB
670 (let ((cur-minor (assoc minor old-major)))
671 (cond
672 ((or (null cur-minor) ; New minor area, or
673 (assq 'test info)) ; Has a test, insert at beginning
674 (setcdr old-major (cons (cons minor info) (cdr old-major))))
675 ((and (not (assq 'test info)) ; No test info, replace completely
676 (not (assq 'test cur-minor))
c113de23
GM
677 (equal (assq 'viewer info) ; Keep alternative viewer
678 (assq 'viewer cur-minor)))
23f87bed
MB
679 (setcdr cur-minor info))
680 (t
681 (setcdr old-major (cons (cons minor info) (cdr old-major))))))
c113de23
GM
682 )))
683
684(defun mailcap-add (type viewer &optional test)
685 "Add VIEWER as a handler for TYPE.
686If TEST is not given, it defaults to t."
687 (let ((tl (split-string type "/")))
688 (when (or (not (car tl))
689 (not (cadr tl)))
690 (error "%s is not a valid MIME type" type))
691 (mailcap-add-mailcap-entry
692 (car tl) (cadr tl)
693 `((viewer . ,viewer)
694 (test . ,(if test test t))
695 (type . ,type)))))
696
697;;;
698;;; The main whabbo
699;;;
700
701(defun mailcap-viewer-lessp (x y)
c0393b5e 702 "Return t iff viewer X is more desirable than viewer Y."
c113de23
GM
703 (let ((x-wild (string-match "[*?]" (or (cdr-safe (assq 'type x)) "")))
704 (y-wild (string-match "[*?]" (or (cdr-safe (assq 'type y)) "")))
705 (x-lisp (not (stringp (or (cdr-safe (assq 'viewer x)) ""))))
706 (y-lisp (not (stringp (or (cdr-safe (assq 'viewer y)) "")))))
707 (cond
708 ((and x-wild (not y-wild))
709 nil)
710 ((and (not x-wild) y-wild)
711 t)
712 ((and (not y-lisp) x-lisp)
713 t)
714 (t nil))))
715
716(defun mailcap-mime-info (string &optional request)
717 "Get the MIME viewer command for STRING, return nil if none found.
718Expects a complete content-type header line as its argument.
719
720Second argument REQUEST specifies what information to return. If it is
721nil or the empty string, the viewer (second field of the mailcap
722entry) will be returned. If it is a string, then the mailcap field
723corresponding to that string will be returned (print, description,
724whatever). If a number, then all the information for this specific
725viewer is returned. If `all', then all possible viewers for
726this type is returned."
727 (let (
728 major ; Major encoding (text, etc)
729 minor ; Minor encoding (html, etc)
730 info ; Other info
731 save-pos ; Misc. position during parse
732 major-info ; (assoc major mailcap-mime-data)
733 minor-info ; (assoc minor major-info)
734 test ; current test proc.
735 viewers ; Possible viewers
736 passed ; Viewers that passed the test
737 viewer ; The one and only viewer
738 ctl)
739 (save-excursion
740 (setq ctl (mail-header-parse-content-type (or string "text/plain")))
741 (setq major (split-string (car ctl) "/"))
742 (setq minor (cadr major)
743 major (car major))
744 (when (setq major-info (cdr (assoc major mailcap-mime-data)))
745 (when (setq viewers (mailcap-possible-viewers major-info minor))
746 (setq info (mapcar (lambda (a) (cons (symbol-name (car a))
747 (cdr a)))
748 (cdr ctl)))
749 (while viewers
750 (if (mailcap-viewer-passes-test (car viewers) info)
751 (setq passed (cons (car viewers) passed)))
752 (setq viewers (cdr viewers)))
753 (setq passed (sort passed 'mailcap-viewer-lessp))
754 (setq viewer (car passed))))
755 (when (and (stringp (cdr (assq 'viewer viewer)))
756 passed)
757 (setq viewer (car passed)))
758 (cond
759 ((and (null viewer) (not (equal major "default")) request)
760 (mailcap-mime-info "default" request))
761 ((or (null request) (equal request ""))
762 (mailcap-unescape-mime-test (cdr (assq 'viewer viewer)) info))
763 ((stringp request)
23f87bed
MB
764 (mailcap-unescape-mime-test
765 (cdr-safe (assoc request viewer)) info))
c113de23
GM
766 ((eq request 'all)
767 passed)
768 (t
769 ;; MUST make a copy *sigh*, else we modify mailcap-mime-data
770 (setq viewer (copy-sequence viewer))
771 (let ((view (assq 'viewer viewer))
772 (test (assq 'test viewer)))
773 (if view (setcdr view (mailcap-unescape-mime-test (cdr view) info)))
774 (if test (setcdr test (mailcap-unescape-mime-test (cdr test) info))))
775 viewer)))))
776
777;;;
778;;; Experimental MIME-types parsing
779;;;
780
781(defvar mailcap-mime-extensions
c0393b5e
DL
782 '(("" . "text/plain")
783 (".abs" . "audio/x-mpeg")
784 (".aif" . "audio/aiff")
785 (".aifc" . "audio/aiff")
786 (".aiff" . "audio/aiff")
787 (".ano" . "application/x-annotator")
788 (".au" . "audio/ulaw")
789 (".avi" . "video/x-msvideo")
790 (".bcpio" . "application/x-bcpio")
791 (".bin" . "application/octet-stream")
792 (".cdf" . "application/x-netcdr")
793 (".cpio" . "application/x-cpio")
794 (".csh" . "application/x-csh")
795 (".css" . "text/css")
796 (".dvi" . "application/x-dvi")
797 (".diff" . "text/x-patch")
798 (".el" . "application/emacs-lisp")
799 (".eps" . "application/postscript")
800 (".etx" . "text/x-setext")
801 (".exe" . "application/octet-stream")
802 (".fax" . "image/x-fax")
803 (".gif" . "image/gif")
804 (".hdf" . "application/x-hdf")
805 (".hqx" . "application/mac-binhex40")
806 (".htm" . "text/html")
807 (".html" . "text/html")
808 (".icon" . "image/x-icon")
809 (".ief" . "image/ief")
810 (".jpg" . "image/jpeg")
811 (".macp" . "image/x-macpaint")
812 (".man" . "application/x-troff-man")
813 (".me" . "application/x-troff-me")
814 (".mif" . "application/mif")
815 (".mov" . "video/quicktime")
816 (".movie" . "video/x-sgi-movie")
817 (".mp2" . "audio/x-mpeg")
818 (".mp3" . "audio/x-mpeg")
819 (".mp2a" . "audio/x-mpeg2")
820 (".mpa" . "audio/x-mpeg")
821 (".mpa2" . "audio/x-mpeg2")
822 (".mpe" . "video/mpeg")
823 (".mpeg" . "video/mpeg")
824 (".mpega" . "audio/x-mpeg")
825 (".mpegv" . "video/mpeg")
826 (".mpg" . "video/mpeg")
827 (".mpv" . "video/mpeg")
828 (".ms" . "application/x-troff-ms")
829 (".nc" . "application/x-netcdf")
830 (".nc" . "application/x-netcdf")
831 (".oda" . "application/oda")
832 (".patch" . "text/x-patch")
833 (".pbm" . "image/x-portable-bitmap")
834 (".pdf" . "application/pdf")
835 (".pgm" . "image/portable-graymap")
836 (".pict" . "image/pict")
837 (".png" . "image/png")
838 (".pnm" . "image/x-portable-anymap")
839 (".ppm" . "image/portable-pixmap")
840 (".ps" . "application/postscript")
841 (".qt" . "video/quicktime")
842 (".ras" . "image/x-raster")
843 (".rgb" . "image/x-rgb")
844 (".rtf" . "application/rtf")
845 (".rtx" . "text/richtext")
846 (".sh" . "application/x-sh")
847 (".sit" . "application/x-stuffit")
23f87bed 848 (".siv" . "application/sieve")
c0393b5e
DL
849 (".snd" . "audio/basic")
850 (".src" . "application/x-wais-source")
851 (".tar" . "archive/tar")
852 (".tcl" . "application/x-tcl")
853 (".tex" . "application/x-tex")
854 (".texi" . "application/texinfo")
855 (".tga" . "image/x-targa")
856 (".tif" . "image/tiff")
857 (".tiff" . "image/tiff")
858 (".tr" . "application/x-troff")
859 (".troff" . "application/x-troff")
860 (".tsv" . "text/tab-separated-values")
861 (".txt" . "text/plain")
862 (".vbs" . "video/mpeg")
863 (".vox" . "audio/basic")
864 (".vrml" . "x-world/x-vrml")
865 (".wav" . "audio/x-wav")
23f87bed 866 (".xls" . "application/vnd.ms-excel")
c0393b5e
DL
867 (".wrl" . "x-world/x-vrml")
868 (".xbm" . "image/xbm")
869 (".xpm" . "image/xpm")
870 (".xwd" . "image/windowdump")
871 (".zip" . "application/zip")
872 (".ai" . "application/postscript")
873 (".jpe" . "image/jpeg")
874 (".jpeg" . "image/jpeg"))
875 "An alist of file extensions and corresponding MIME content-types.
876This exists for you to customize the information in Lisp. It is
877merged with values from mailcap files by `mailcap-parse-mimetypes'.")
c113de23
GM
878
879(defvar mailcap-mimetypes-parsed-p nil)
880
881(defun mailcap-parse-mimetypes (&optional path force)
c0393b5e 882 "Parse out all the mimetypes specified in a Unix-style path string PATH.
c113de23
GM
883Components of PATH are separated by the `path-separator' character
884appropriate for this system. If PATH is omitted, use the value of
885environment variable MIMETYPES if set; otherwise use a default path.
886If FORCE, re-parse even if already parsed."
887 (interactive (list nil t))
888 (when (or (not mailcap-mimetypes-parsed-p)
889 force)
890 (cond
891 (path nil)
892 ((getenv "MIMETYPES") (setq path (getenv "MIMETYPES")))
23f87bed 893 ((memq system-type mailcap-poor-system-types)
c113de23
GM
894 (setq path '("~/mime.typ" "~/etc/mime.typ")))
895 (t (setq path
896 ;; mime.types seems to be the normal name, definitely so
897 ;; on current GNUish systems. The search order follows
898 ;; that for mailcap.
899 '("~/.mime.types"
900 "/etc/mime.types"
901 "/usr/etc/mime.types"
902 "/usr/local/etc/mime.types"
903 "/usr/local/www/conf/mime.types"
904 "~/.mime-types"
905 "/etc/mime-types"
906 "/usr/etc/mime-types"
907 "/usr/local/etc/mime-types"
908 "/usr/local/www/conf/mime-types"))))
909 (let ((fnames (reverse (if (stringp path)
9b198954 910 (delete "" (split-string path path-separator))
c113de23
GM
911 path)))
912 fname)
913 (while fnames
914 (setq fname (car fnames))
915 (if (and (file-readable-p fname))
916 (mailcap-parse-mimetype-file fname))
917 (setq fnames (cdr fnames))))
918 (setq mailcap-mimetypes-parsed-p t)))
919
920(defun mailcap-parse-mimetype-file (fname)
c0393b5e 921 "Parse out a mime-types file FNAME."
c113de23
GM
922 (let (type ; The MIME type for this line
923 extns ; The extensions for this line
924 save-pos ; Misc. saved buffer positions
925 )
926 (with-temp-buffer
927 (insert-file-contents fname)
928 (mailcap-replace-regexp "#.*" "")
929 (mailcap-replace-regexp "\n+" "\n")
930 (mailcap-replace-regexp "[ \t]+$" "")
931 (goto-char (point-max))
932 (skip-chars-backward " \t\n")
933 (delete-region (point) (point-max))
934 (goto-char (point-min))
935 (while (not (eobp))
936 (skip-chars-forward " \t\n")
937 (setq save-pos (point))
938 (skip-chars-forward "^ \t\n")
939 (downcase-region save-pos (point))
940 (setq type (buffer-substring save-pos (point)))
941 (while (not (eolp))
942 (skip-chars-forward " \t")
943 (setq save-pos (point))
944 (skip-chars-forward "^ \t\n")
945 (setq extns (cons (buffer-substring save-pos (point)) extns)))
946 (while extns
947 (setq mailcap-mime-extensions
948 (cons
949 (cons (if (= (string-to-char (car extns)) ?.)
950 (car extns)
951 (concat "." (car extns))) type)
952 mailcap-mime-extensions)
953 extns (cdr extns)))))))
954
955(defun mailcap-extension-to-mime (extn)
956 "Return the MIME content type of the file extensions EXTN."
957 (mailcap-parse-mimetypes)
958 (if (and (stringp extn)
959 (not (eq (string-to-char extn) ?.)))
960 (setq extn (concat "." extn)))
961 (cdr (assoc (downcase extn) mailcap-mime-extensions)))
962
c0393b5e
DL
963;; Unused?
964(defalias 'mailcap-command-p 'executable-find)
c113de23
GM
965
966(defun mailcap-mime-types ()
967 "Return a list of MIME media types."
968 (mailcap-parse-mimetypes)
9b198954
DL
969 (mm-delete-duplicates
970 (nconc
971 (mapcar 'cdr mailcap-mime-extensions)
972 (apply
973 'nconc
974 (mapcar
975 (lambda (l)
976 (delq nil
977 (mapcar
978 (lambda (m)
979 (let ((type (cdr (assq 'type (cdr m)))))
980 (if (equal (cadr (split-string type "/"))
981 "*")
982 nil
983 type)))
984 (cdr l))))
985 mailcap-mime-data)))))
c113de23
GM
986
987(provide 'mailcap)
988
ab5796a9 989;;; arch-tag: 1fd4f9c9-c305-4d2e-9747-3a4d45baa0bd
c113de23 990;;; mailcap.el ends here