X-Git-Url: http://cgit.sxemacs.org/?a=blobdiff_plain;f=lisp%2Fmailcap.el;h=aaa29fa4d8f6d29eac7e2205d20901c0c92fed42;hb=0651fabaac80cf08698f066dae0af33f29b91a9a;hp=ab716953a8d516a24c0c64623247a9f61cbd0788;hpb=1774ae25842f79d393cc8dd84a43e8eb9224d4ce;p=gnus diff --git a/lisp/mailcap.el b/lisp/mailcap.el index ab716953a..aaa29fa4d 100644 --- a/lisp/mailcap.el +++ b/lisp/mailcap.el @@ -1,5 +1,5 @@ ;;; mailcap.el --- Functions for displaying MIME parts -;; Copyright (C) 1998 Free Software Foundation, Inc. +;; Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. ;; Author: William M. Perry ;; Lars Magne Ingebrigtsen @@ -26,9 +26,9 @@ ;;; Code: -(eval-and-compile - (require 'cl)) +(eval-when-compile (require 'cl)) (require 'mail-parse) +(require 'mm-util) (defvar mailcap-parse-args-syntax-table (let ((table (copy-syntax-table emacs-lisp-mode-syntax-table))) @@ -51,7 +51,8 @@ (type . "application/x-x509-user-cert")) ("octet-stream" (viewer . mailcap-save-binary-file) - (type ."application/octet-stream")) + (non-viewer . t) + (type . "application/octet-stream")) ("dvi" (viewer . "open %s") (type . "application/dvi") @@ -70,6 +71,7 @@ (type . "application/emacs-lisp")) ("x-tar" (viewer . mailcap-save-binary-file) + (non-viewer . t) (type . "application/x-tar")) ("x-latex" (viewer . tex-mode) @@ -93,6 +95,7 @@ (type . "application/tex")) ("zip" (viewer . mailcap-save-binary-file) + (non-viewer . t) (type . "application/zip") ("copiousoutput")) ("pdf" @@ -103,7 +106,7 @@ (type . "application/postscript") (test . (eq (mm-device-type) 'ns))) ("postscript" - (viewer . "ghostview %s") + (viewer . "ghostview -dSAFER %s") (type . "application/postscript") (test . (eq (mm-device-type) 'x)) ("needsx11")) @@ -116,35 +119,30 @@ ("x-mpeg" (viewer . "maplay %s") (type . "audio/x-mpeg")) - (".*" - (viewer . mailcap-save-binary-file) - (test . (or (featurep 'nas-sound) - (featurep 'native-sound))) - (type . "audio/*")) (".*" (viewer . "showaudio") (type . "audio/*"))) ("message" ("rfc-*822" - (viewer . gnus-article-prepare-display) + (viewer . mm-view-message) (test . (and (featurep 'gnus) (gnus-alive-p))) - (type . "message/rfc-822")) + (type . "message/rfc822")) ("rfc-*822" (viewer . vm-mode) (test . (fboundp 'vm-mode)) - (type . "message/rfc-822")) + (type . "message/rfc822")) ("rfc-*822" (viewer . w3-mode) (test . (fboundp 'w3-mode)) - (type . "message/rfc-822")) + (type . "message/rfc822")) ("rfc-*822" (viewer . view-mode) (test . (fboundp 'view-mode)) - (type . "message/rfc-822")) + (type . "message/rfc822")) ("rfc-*822" (viewer . fundamental-mode) - (type . "message/rfc-822"))) + (type . "message/rfc822"))) ("image" ("x-xwd" (viewer . "xwud -in %s") @@ -169,7 +167,12 @@ (type . "image/*") (test . (eq (mm-device-type) 'ns))) (".*" - (viewer . "xv -perfect %s") + (viewer . "display %s") + (type . "image/*") + (test . (eq (mm-device-type) 'x)) + ("needsx11")) + (".*" + (viewer . "ee %s") (type . "image/*") (test . (eq (mm-device-type) 'x)) ("needsx11"))) @@ -210,7 +213,7 @@ (viewer . tar-mode) (type . "archive/tar") (test . (fboundp 'tar-mode))))) - "*The mailcap structure is an assoc list of assoc lists. + "The mailcap structure is an assoc list of assoc lists. 1st assoc list is keyed on the major content-type 2nd assoc list is keyed on the minor content-type (which can be a regexp) @@ -241,7 +244,10 @@ not.") (defvar mailcap-download-directory nil "*Where downloaded files should go by default.") -(defvar mailcap-temporary-directory (or (getenv "TMPDIR") "/tmp") +(defvar mailcap-temporary-directory + (cond ((fboundp 'temp-directory) (temp-directory)) + ((boundp 'temporary-file-directory) temporary-file-directory) + ("/tmp/")) "*Where temporary files go.") ;;; @@ -249,7 +255,7 @@ not.") ;;; (defun mailcap-generate-unique-filename (&optional fmt) - "Generate a unique filename in mailcap-temporary-directory" + "Generate a unique filename in mailcap-temporary-directory." (if (not fmt) (let ((base (format "mailcap-tmp.%d" (user-real-uid))) (fname "") @@ -272,18 +278,50 @@ not.") (defun mailcap-save-binary-file () (goto-char (point-min)) - (let ((file (read-file-name - "Filename to save as: " - (or mailcap-download-directory "~/"))) - (require-final-newline nil)) - (write-region (point-min) (point-max) file) + (unwind-protect + (let ((file (read-file-name + "Filename to save as: " + (or mailcap-download-directory "~/"))) + (require-final-newline nil)) + (write-region (point-min) (point-max) file)) (kill-buffer (current-buffer)))) +(defvar mailcap-maybe-eval-warning + "*** WARNING *** + +This MIME part contains untrusted and possibly harmful content. +If you evaluate the Emacs Lisp code contained in it, a lot of nasty +things can happen. Please examine the code very carefully before you +instruct Emacs to evaluate it. You can browse the buffer containing +the code using \\[scroll-other-window]. + +If you are unsure what to do, please answer \"no\"." + "Text of warning message displayed by `mailcap-maybe-eval'. +Make sure that this text consists only of few text lines. Otherwise, +Gnus might fail to display all of it.") + (defun mailcap-maybe-eval () - "Maybe evaluate a buffer of emacs lisp code" - (if (yes-or-no-p "This is emacs-lisp code, evaluate it? ") - (eval-buffer (current-buffer)) - (emacs-lisp-mode))) + "Maybe evaluate a buffer of emacs lisp code." + (let ((lisp-buffer (current-buffer))) + (goto-char (point-min)) + (when + (save-window-excursion + (delete-other-windows) + (let ((buffer (get-buffer-create (generate-new-buffer-name + "*Warning*")))) + (unwind-protect + (with-current-buffer buffer + (insert (substitute-command-keys + mailcap-maybe-eval-warning)) + (goto-char (point-min)) + (display-buffer buffer) + (yes-or-no-p "This is potentially dangerous emacs-lisp code, evaluate it? ")) + (kill-buffer buffer)))) + (eval-buffer (current-buffer))) + (when (buffer-live-p lisp-buffer) + (with-current-buffer lisp-buffer + (emacs-lisp-mode))))) + ;;; ;;; The mailcap parser @@ -298,8 +336,12 @@ not.") (defvar mailcap-parsed-p nil) (defun mailcap-parse-mailcaps (&optional path force) - "Parse out all the mailcaps specified in a unix-style path string PATH. -If FORCE, re-parse even if already parsed." + "Parse out all the mailcaps specified in a path string PATH. +Components of PATH are separated by the `path-separator' character +appropriate for this system. If FORCE, re-parse even if already +parsed. If PATH is omitted, use the value of environment variable +MAILCAPS if set; otherwise (on Unix) use the path from RFC 1524, plus +/usr/local/etc/mailcap." (interactive (list nil t)) (when (or (not mailcap-parsed-p) force) @@ -307,26 +349,24 @@ If FORCE, re-parse even if already parsed." (path nil) ((getenv "MAILCAPS") (setq path (getenv "MAILCAPS"))) ((memq system-type '(ms-dos ms-windows windows-nt)) - (setq path (mapconcat 'expand-file-name '("~/mail.cap" "~/etc/mail.cap") - ";"))) - (t (setq path (mapconcat 'expand-file-name - '("~/.mailcap" - "/etc/mailcap:/usr/etc/mailcap" - "/usr/local/etc/mailcap") ":")))) + (setq path '("~/.mailcap" "~/mail.cap" "~/etc/mail.cap"))) + (t (setq path + ;; This is per RFC 1524, specifically + ;; with /usr before /usr/local. + '("~/.mailcap" "/etc/mailcap" "/usr/etc/mailcap" + "/usr/local/etc/mailcap")))) (let ((fnames (reverse - (split-string - path (if (memq system-type - '(ms-dos ms-windows windows-nt)) - ";" - ":")))) + (if (stringp path) + (parse-colon-path path) + path))) fname) (while fnames (setq fname (car fnames)) - (if (and (file-exists-p fname) (file-readable-p fname) - (file-regular-p fname)) - (mailcap-parse-mailcap (car fnames))) + (if (and (file-readable-p fname) + (file-regular-p fname)) + (mailcap-parse-mailcap fname)) (setq fnames (cdr fnames)))) - (setq mailcap-parsed-p t))) + (setq mailcap-parsed-p t))) (defun mailcap-parse-mailcap (fname) ;; Parse out the mailcap file specified by FNAME @@ -340,54 +380,67 @@ If FORCE, re-parse even if already parsed." (insert-file-contents fname) (set-syntax-table mailcap-parse-args-syntax-table) (mailcap-replace-regexp "#.*" "") ; Remove all comments + (mailcap-replace-regexp "\\\\[ \t]*\n" " ") ; And collapse spaces (mailcap-replace-regexp "\n+" "\n") ; And blank lines - (mailcap-replace-regexp "\\\\[ \t\n]+" " ") ; And collapse spaces - (mailcap-replace-regexp (concat (regexp-quote "\\") "[ \t]*\n") "") (goto-char (point-max)) (skip-chars-backward " \t\n") (delete-region (point) (point-max)) - (goto-char (point-min)) - (while (not (eobp)) - (skip-chars-forward " \t\n") + (while (not (bobp)) + (skip-chars-backward " \t\n") + (beginning-of-line) (setq save-pos (point) info nil) - (skip-chars-forward "^/;") + (skip-chars-forward "^/; \t\n") (downcase-region save-pos (point)) (setq major (buffer-substring save-pos (point))) - (skip-chars-forward "/ \t\n") - (setq save-pos (point)) - (skip-chars-forward "^;") - (downcase-region save-pos (point)) - (setq minor - (cond - ((= ?* (or (char-after save-pos) 0)) ".*") - ((= (point) save-pos) ".*") - (t (buffer-substring save-pos (point))))) - (skip-chars-forward "; \t\n") + (skip-chars-forward " \t") + (setq minor "") + (when (eq (char-after) ?/) + (forward-char) + (skip-chars-forward " \t") + (setq save-pos (point)) + (skip-chars-forward "^; \t\n") + (downcase-region save-pos (point)) + (setq minor + (cond + ((eq ?* (or (char-after save-pos) 0)) ".*") + ((= (point) save-pos) ".*") + (t (regexp-quote (buffer-substring save-pos (point))))))) + (skip-chars-forward " \t") ;;; Got the major/minor chunks, now for the viewers/etc ;;; The first item _must_ be a viewer, according to the ;;; RFC for mailcap files (#1343) - (skip-chars-forward "; \t\n") - (setq save-pos (point)) - (skip-chars-forward "^;\n") - (if (= (or (char-after save-pos) 0) ?') - (setq viewer (progn - (narrow-to-region (1+ save-pos) (point)) - (goto-char (point-min)) - (prog1 - (read (current-buffer)) - (goto-char (point-max)) - (widen)))) - (setq viewer (buffer-substring save-pos (point)))) + (setq viewer "") + (when (eq (char-after) ?\;) + (forward-char) + (skip-chars-forward " \t") + (setq save-pos (point)) + (skip-chars-forward "^;\n") + ;; skip \; + (while (eq (char-before) ?\\) + (backward-delete-char 1) + (forward-char) + (skip-chars-forward "^;\n")) + (if (eq (or (char-after save-pos) 0) ?') + (setq viewer (progn + (narrow-to-region (1+ save-pos) (point)) + (goto-char (point-min)) + (prog1 + (read (current-buffer)) + (goto-char (point-max)) + (widen)))) + (setq viewer (buffer-substring save-pos (point))))) (setq save-pos (point)) (end-of-line) - (setq info (nconc (list (cons 'viewer viewer) - (cons 'type (concat major "/" - (if (string= minor ".*") - "*" minor)))) - (mailcap-parse-mailcap-extras save-pos (point)))) - (mailcap-mailcap-entry-passes-test info) - (mailcap-add-mailcap-entry major minor info))))) + (unless (equal viewer "") + (setq info (nconc (list (cons 'viewer viewer) + (cons 'type (concat major "/" + (if (string= minor ".*") + "*" minor)))) + (mailcap-parse-mailcap-extras save-pos (point)))) + (mailcap-mailcap-entry-passes-test info) + (mailcap-add-mailcap-entry major minor info)) + (beginning-of-line))))) (defun mailcap-parse-mailcap-extras (st nd) ;; Grab all the extra stuff from a mailcap entry @@ -405,14 +458,13 @@ If FORCE, re-parse even if already parsed." (skip-chars-forward " \n\t;") (while (not (eobp)) (setq done nil) - (skip-chars-forward " \";\n\t") (setq name-pos (point)) - (skip-chars-forward "^ \n\t=") + (skip-chars-forward "^ \n\t=;") (downcase-region name-pos (point)) (setq name (buffer-substring name-pos (point))) (skip-chars-forward " \t\n") - (if (/= (or (char-after (point)) 0) ?=) ; There is no value - (setq value nil) + (if (not (eq (char-after (point)) ?=)) ; There is no value + (setq value t) (skip-chars-forward " \t\n=") (setq val-pos (point)) (if (memq (char-after val-pos) '(?\" ?')) @@ -425,13 +477,14 @@ If FORCE, re-parse even if already parsed." (error (goto-char (point-max))))) (while (not done) (skip-chars-forward "^;") - (if (= (or (char-after (1- (point))) 0) ?\\ ) + (if (eq (char-after (1- (point))) ?\\ ) (progn (subst-char-in-region (1- (point)) (point) ?\\ ? ) (skip-chars-forward ";")) (setq done t)))) (setq value (buffer-substring val-pos (point)))) - (setq results (cons (cons name value) results))) + (setq results (cons (cons name value) results)) + (skip-chars-forward " \";\n\t")) results))) (defun mailcap-mailcap-entry-passes-test (info) @@ -441,7 +494,10 @@ If FORCE, re-parse even if already parsed." (test (assq 'test info)) ; The test clause ) (setq status (and test (split-string (cdr test) " "))) - (if (and (assoc "needsx11" info) (not (getenv "DISPLAY"))) + (if (and (or (assoc "needsterm" info) + (assoc "needsterminal" info) + (assoc "needsx11" info)) + (not (getenv "DISPLAY"))) (setq status nil) (cond ((and (equal (nth 0 status) "test") @@ -473,7 +529,7 @@ If FORCE, re-parse even if already parsed." ((and minor (string-match (car (car major)) minor)) (setq wildcard (cons (cdr (car major)) wildcard)))) (setq major (cdr major))) - (nconc (nreverse exact) (nreverse wildcard)))) + (nconc exact wildcard))) (defun mailcap-unescape-mime-test (test type-info) (let (save-pos save-chr subst) @@ -566,16 +622,32 @@ If FORCE, re-parse even if already parsed." (setq mailcap-mime-data (cons (cons major (list (cons minor info))) mailcap-mime-data)) - (let ((cur-minor (assoc minor old-major))) - (cond - ((or (null cur-minor) ; New minor area, or - (assq 'test info)) ; Has a test, insert at beginning - (setcdr old-major (cons (cons minor info) (cdr old-major)))) - ((and (not (assq 'test info)) ; No test info, replace completely - (not (assq 'test cur-minor))) - (setcdr cur-minor info)) - (t - (setcdr old-major (cons (cons minor info) (cdr old-major))))))))) + (let ((cur-minor (assoc minor old-major))) + (cond + ((or (null cur-minor) ; New minor area, or + (assq 'test info)) ; Has a test, insert at beginning + (setcdr old-major (cons (cons minor info) (cdr old-major)))) + ((and (not (assq 'test info)) ; No test info, replace completely + (not (assq 'test cur-minor)) + (equal (assq 'viewer info) ; Keep alternative viewer + (assq 'viewer cur-minor))) + (setcdr cur-minor info)) + (t + (setcdr old-major (cons (cons minor info) (cdr old-major)))))) + ))) + +(defun mailcap-add (type viewer &optional test) + "Add VIEWER as a handler for TYPE. +If TEST is not given, it defaults to t." + (let ((tl (split-string type "/"))) + (when (or (not (car tl)) + (not (cadr tl))) + (error "%s is not a valid MIME type" type)) + (mailcap-add-mailcap-entry + (car tl) (cadr tl) + `((viewer . ,viewer) + (test . ,(if test test t)) + (type . ,type))))) ;;; ;;; The main whabbo @@ -588,12 +660,12 @@ If FORCE, re-parse even if already parsed." (x-lisp (not (stringp (or (cdr-safe (assq 'viewer x)) "")))) (y-lisp (not (stringp (or (cdr-safe (assq 'viewer y)) ""))))) (cond - ((and x-lisp (not y-lisp)) - t) - ((and (not y-lisp) x-wild (not y-wild)) - t) + ((and x-wild (not y-wild)) + nil) ((and (not x-wild) y-wild) t) + ((and (not y-lisp) x-lisp) + t) (t nil)))) (defun mailcap-mime-info (string &optional request) @@ -633,7 +705,7 @@ this type is returned." (if (mailcap-viewer-passes-test (car viewers) info) (setq passed (cons (car viewers) passed))) (setq viewers (cdr viewers))) - (setq passed (sort (nreverse passed) 'mailcap-viewer-lessp)) + (setq passed (sort passed 'mailcap-viewer-lessp)) (setq viewer (car passed)))) (when (and (stringp (cdr (assq 'viewer viewer))) passed) @@ -651,7 +723,7 @@ this type is returned." passed) (t ;; MUST make a copy *sigh*, else we modify mailcap-mime-data - (setq viewer (copy-tree viewer)) + (setq viewer (copy-sequence viewer)) (let ((view (assq 'viewer viewer)) (test (assq 'test viewer))) (if view (setcdr view (mailcap-unescape-mime-test (cdr view) info))) @@ -676,7 +748,9 @@ this type is returned." (".cdf" . "application/x-netcdr") (".cpio" . "application/x-cpio") (".csh" . "application/x-csh") + (".css" . "text/css") (".dvi" . "application/x-dvi") + (".diff" . "text/x-patch") (".el" . "application/emacs-lisp") (".eps" . "application/postscript") (".etx" . "text/x-setext") @@ -711,6 +785,7 @@ this type is returned." (".nc" . "application/x-netcdf") (".nc" . "application/x-netcdf") (".oda" . "application/oda") + (".patch" . "text/x-patch") (".pbm" . "image/x-portable-bitmap") (".pdf" . "application/pdf") (".pgm" . "image/portable-graymap") @@ -746,39 +821,54 @@ this type is returned." (".wav" . "audio/x-wav") (".wrl" . "x-world/x-vrml") (".xbm" . "image/xbm") - (".xpm" . "image/x-pixmap") + (".xpm" . "image/xpm") (".xwd" . "image/windowdump") (".zip" . "application/zip") (".ai" . "application/postscript") (".jpe" . "image/jpeg") (".jpeg" . "image/jpeg")) - "*An assoc list of file extensions and the MIME content-types they -correspond to.") - -(defun mailcap-parse-mimetypes (&optional path) - ;; Parse out all the mimetypes specified in a unix-style path string PATH - (cond - (path nil) - ((getenv "MIMETYPES") (setq path (getenv "MIMETYPES"))) - ((memq system-type '(ms-dos ms-windows windows-nt)) - (setq path (mapconcat 'expand-file-name - '("~/mime.typ" "~/etc/mime.typ") ";"))) - (t (setq path (mapconcat 'expand-file-name - '("~/.mime-types" - "/etc/mime-types:/usr/etc/mime-types" - "/usr/local/etc/mime-types" - "/usr/local/www/conf/mime-types") ":")))) - (let ((fnames (reverse - (split-string path - (if (memq system-type - '(ms-dos ms-windows windows-nt)) - ";" ":")))) - fname) - (while fnames - (setq fname (car fnames)) - (if (and (file-exists-p fname) (file-readable-p fname)) - (mailcap-parse-mimetype-file (car fnames))) - (setq fnames (cdr fnames))))) + "An assoc list of file extensions and corresponding MIME content-types.") + +(defvar mailcap-mimetypes-parsed-p nil) + +(defun mailcap-parse-mimetypes (&optional path force) + "Parse out all the mimetypes specified in a unix-style path string PATH. +Components of PATH are separated by the `path-separator' character +appropriate for this system. If PATH is omitted, use the value of +environment variable MIMETYPES if set; otherwise use a default path. +If FORCE, re-parse even if already parsed." + (interactive (list nil t)) + (when (or (not mailcap-mimetypes-parsed-p) + force) + (cond + (path nil) + ((getenv "MIMETYPES") (setq path (getenv "MIMETYPES"))) + ((memq system-type '(ms-dos ms-windows windows-nt)) + (setq path '("~/mime.typ" "~/etc/mime.typ"))) + (t (setq path + ;; mime.types seems to be the normal name, definitely so + ;; on current GNUish systems. The search order follows + ;; that for mailcap. + '("~/.mime.types" + "/etc/mime.types" + "/usr/etc/mime.types" + "/usr/local/etc/mime.types" + "/usr/local/www/conf/mime.types" + "~/.mime-types" + "/etc/mime-types" + "/usr/etc/mime-types" + "/usr/local/etc/mime-types" + "/usr/local/www/conf/mime-types")))) + (let ((fnames (reverse (if (stringp path) + (parse-colon-path path) + path))) + fname) + (while fnames + (setq fname (car fnames)) + (if (and (file-readable-p fname)) + (mailcap-parse-mimetype-file fname)) + (setq fnames (cdr fnames)))) + (setq mailcap-mimetypes-parsed-p t))) (defun mailcap-parse-mimetype-file (fname) ;; Parse out a mime-types file @@ -798,7 +888,7 @@ correspond to.") (while (not (eobp)) (skip-chars-forward " \t\n") (setq save-pos (point)) - (skip-chars-forward "^ \t") + (skip-chars-forward "^ \t\n") (downcase-region save-pos (point)) (setq type (buffer-substring save-pos (point))) (while (not (eolp)) @@ -817,6 +907,7 @@ correspond to.") (defun mailcap-extension-to-mime (extn) "Return the MIME content type of the file extensions EXTN." + (mailcap-parse-mimetypes) (if (and (stringp extn) (not (eq (string-to-char extn) ?.))) (setq extn (concat "." extn))) @@ -843,6 +934,27 @@ The path of COMMAND will be returned iff COMMAND is a command." (not (file-directory-p file))) (throw 'found file)))))))) +(defun mailcap-mime-types () + "Return a list of MIME media types." + (mailcap-parse-mimetypes) + (mm-delete-duplicates + (nconc + (mapcar 'cdr mailcap-mime-extensions) + (apply + 'nconc + (mapcar + (lambda (l) + (delq nil + (mapcar + (lambda (m) + (let ((type (cdr (assq 'type (cdr m))))) + (if (equal (cadr (split-string type "/")) + "*") + nil + type))) + (cdr l)))) + mailcap-mime-data))))) + (provide 'mailcap) ;;; mailcap.el ends here