Merge from gnus--rel--5.10
[gnus] / lisp / mm-util.el
index 453dffd..9e8fcd1 100644 (file)
@@ -1,6 +1,7 @@
 ;;; mm-util.el --- Utility functions for Mule and low level things
-;; Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
-;;   Free Software Foundation, Inc.
+
+;; Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+;;   2005, 2006 Free Software Foundation, Inc.
 
 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
 ;;     MORIOKA Tomohiko <morioka@jaist.ac.jp>
@@ -18,8 +19,8 @@
 
 ;; You should have received a copy of the GNU General Public License
 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
-;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-;; Boston, MA 02111-1307, USA.
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
 
 ;;; Commentary:
 
 (eval-when-compile (require 'cl))
 (require 'mail-prsvr)
 
+(eval-and-compile
+  (if (featurep 'xemacs)
+      (unless (ignore-errors
+               (require 'timer-funcs))
+       (require 'timer))
+    (require 'timer)))
+
 (eval-and-compile
   (mapcar
    (lambda (elem)
@@ -54,7 +62,8 @@
                    mm-mime-mule-charset-alist)
            nil t))))
      (subst-char-in-string
-      . (lambda (from to string &optional inplace) ;; stolen (and renamed) from nnheader.el
+      . (lambda (from to string &optional inplace)
+         ;; stolen (and renamed) from nnheader.el
          "Replace characters in STRING from FROM to TO.
          Unless optional argument INPLACE is non-nil, return a new string."
          (let ((string (if inplace string (copy-sequence string)))
          (replace-regexp-in-string regexp rep string nil literal)))
      (string-as-unibyte . identity)
      (string-make-unibyte . identity)
+     ;; string-as-multibyte often doesn't really do what you think it does.
+     ;; Example:
+     ;;    (aref (string-as-multibyte "\201") 0) -> 129 (aka ?\201)
+     ;;    (aref (string-as-multibyte "\300") 0) -> 192 (aka ?\300)
+     ;;    (aref (string-as-multibyte "\300\201") 0) -> 192 (aka ?\300)
+     ;;    (aref (string-as-multibyte "\300\201") 1) -> 129 (aka ?\201)
+     ;; but
+     ;;    (aref (string-as-multibyte "\201\300") 0) -> 2240
+     ;;    (aref (string-as-multibyte "\201\300") 1) -> <error>
+     ;; Better use string-to-multibyte or encode-coding-string.
+     ;; If you really need string-as-multibyte somewhere it's usually
+     ;; because you're using the internal emacs-mule representation (maybe
+     ;; because you're using string-as-unibyte somewhere), which is
+     ;; generally a problem in itself.
+     ;; Here is an approximate equivalence table to help think about it:
+     ;; (string-as-multibyte s)   ~= (decode-coding-string s 'emacs-mule)
+     ;; (string-to-multibyte s)   ~= (decode-coding-string s 'binary)
+     ;; (string-make-multibyte s) ~= (decode-coding-string s locale-coding-system)
      (string-as-multibyte . identity)
      (string-to-multibyte
       . (lambda (string)
           (lambda (ch) (mm-string-as-multibyte (char-to-string ch)))
           string "")))
      (multibyte-string-p . ignore)
-     ;; It is not a MIME function, but some MIME functions use it.
-     (make-temp-file . (lambda (prefix &optional dir-flag)
-                        (let ((file (expand-file-name
-                                     (make-temp-name prefix)
-                                     (if (fboundp 'temp-directory)
-                                         (temp-directory)
-                                       temporary-file-directory))))
-                          (if dir-flag
-                              (make-directory file))
-                          file)))
      (insert-byte . insert-char)
-     (multibyte-char-to-unibyte . identity))))
+     (multibyte-char-to-unibyte . identity)
+     (special-display-p
+      . (lambda (buffer-name)
+         "Returns non-nil if a buffer named BUFFER-NAME gets a special frame."
+         (and special-display-function
+              (or (and (member buffer-name special-display-buffer-names) t)
+                  (cdr (assoc buffer-name special-display-buffer-names))
+                  (catch 'return
+                    (dolist (elem special-display-regexps)
+                      (and (stringp elem)
+                           (string-match elem buffer-name)
+                           (throw 'return t))
+                      (and (consp elem)
+                           (stringp (car elem))
+                           (string-match (car elem) buffer-name)
+                           (throw 'return (cdr elem))))))))))))
 
 (eval-and-compile
   (defalias 'mm-char-or-char-int-p
@@ -135,30 +167,53 @@ In XEmacs, also return non-nil if CS is a coding system object.
 If CS is available, return CS itself in Emacs, and return a coding
 system object in XEmacs."
   (if (fboundp 'find-coding-system)
-      (find-coding-system cs)
+      (and cs (find-coding-system cs))
     (if (fboundp 'coding-system-p)
        (when (coding-system-p cs)
          cs)
-      ;; Is this branch ever actually useful?
+      ;; no-MULE XEmacs:
       (car (memq cs (mm-get-coding-system-list))))))
 
+(defun mm-codepage-setup (number &optional alias)
+  "Create a coding system cpNUMBER.
+The coding system is created using `codepage-setup'.  If ALIAS is
+non-nil, an alias is created and added to
+`mm-charset-synonym-alist'.  If ALIAS is a string, it's used as
+the alias.  Else windows-NUMBER is used."
+  (interactive
+   (let ((completion-ignore-case t)
+        (candidates (cp-supported-codepages)))
+     (list (completing-read "Setup DOS Codepage: (default 437) " candidates
+                           nil t nil nil "437"))))
+  (when alias
+    (setq alias (if (stringp alias)
+                   (intern alias)
+                 (intern (format "windows-%s" number)))))
+  (let* ((cp (intern (format "cp%s" number))))
+    (unless (mm-coding-system-p cp)
+      (codepage-setup number))
+    (when (and alias
+              ;; Don't add alias if setup of cp failed.
+              (mm-coding-system-p cp))
+      (add-to-list 'mm-charset-synonym-alist (cons alias cp)))))
+
 (defvar mm-charset-synonym-alist
   `(
     ;; Not in XEmacs, but it's not a proper MIME charset anyhow.
     ,@(unless (mm-coding-system-p 'x-ctext)
-       '((x-ctext . ctext)))
+       '((x-ctext . ctext)))
     ;; ISO-8859-15 is very similar to ISO-8859-1.  But it's _different_!
     ,@(unless (mm-coding-system-p 'iso-8859-15)
-       '((iso-8859-15 . iso-8859-1)))
+       '((iso-8859-15 . iso-8859-1)))
     ;; BIG-5HKSCS is similar to, but different than, BIG-5.
     ,@(unless (mm-coding-system-p 'big5-hkscs)
        '((big5-hkscs . big5)))
     ;; Windows-1252 is actually a superset of Latin-1.  See also
     ;; `gnus-article-dumbquotes-map'.
     ,@(unless (mm-coding-system-p 'windows-1252)
-       (if (mm-coding-system-p 'cp1252)
-          '((windows-1252 . cp1252))
-        '((windows-1252 . iso-8859-1))))
+       (if (mm-coding-system-p 'cp1252)
+           '((windows-1252 . cp1252))
+         '((windows-1252 . iso-8859-1))))
     ;; Windows-1250 is a variant of Latin-2 heavily used by Microsoft
     ;; Outlook users in Czech republic. Use this to allow reading of their
     ;; e-mails. cp1250 should be defined by M-x codepage-setup.
@@ -174,8 +229,56 @@ system object in XEmacs."
        (if (mm-coding-system-p 'cp949)
            '((ks_c_5601-1987 . cp949))
          '((ks_c_5601-1987 . euc-kr))))
+    ;; Windows-31J is Windows Codepage 932.
+    ,@(if (and (not (mm-coding-system-p 'windows-31j))
+              (mm-coding-system-p 'cp932))
+         '((windows-31j . cp932)))
     )
-  "A mapping from invalid charset names to the real charset names.")
+  "A mapping from unknown or invalid charset names to the real charset names.")
+
+(defcustom mm-charset-override-alist
+  `((iso-8859-1 . windows-1252))
+  "A mapping from undesired charset names to their replacement.
+
+You may add pairs like (iso-8859-1 . windows-1252) here,
+i.e. treat iso-8859-1 as windows-1252.  windows-1252 is a
+superset of iso-8859-1."
+  :type '(list (set :inline t
+                   (const (iso-8859-1 . windows-1252))
+                   (const (undecided  . windows-1252)))
+              (repeat :inline t
+                      :tag "Other options"
+                      (cons (symbol :tag "From charset")
+                            (symbol :tag "To charset"))))
+  :version "22.1" ;; Gnus 5.10.9
+  :group 'mime)
+
+(defcustom mm-charset-eval-alist
+  (if (featurep 'xemacs)
+      nil ;; I don't know what would be useful for XEmacs.
+    '(;; Emacs 21 offers 1250 1251 1253 1257.  Emacs 22 provides autoloads for
+      ;; 1250-1258 (i.e. `mm-codepage-setup' does nothing).
+      (windows-1250 . (mm-codepage-setup 1250 t))
+      (windows-1251 . (mm-codepage-setup 1251 t))
+      (windows-1253 . (mm-codepage-setup 1253 t))
+      (windows-1257 . (mm-codepage-setup 1257 t))))
+  "An alist of (CHARSET . FORM) pairs.
+If an article is encoded in an unknown CHARSET, FORM is
+evaluated.  This allows to load additional libraries providing
+charsets on demand.  If supported by your Emacs version, you
+could use `autoload-coding-system' here."
+  :version "22.1" ;; Gnus 5.10.9
+  :type '(list (set :inline t
+                   (const (windows-1250 . (mm-codepage-setup 1250 t)))
+                   (const (windows-1251 . (mm-codepage-setup 1251 t)))
+                   (const (windows-1253 . (mm-codepage-setup 1253 t)))
+                   (const (windows-1257 . (mm-codepage-setup 1257 t)))
+                   (const (cp850 . (mm-codepage-setup 850 nil))))
+              (repeat :inline t
+                      :tag "Other options"
+                      (cons (symbol :tag "charset")
+                            (symbol :tag "form"))))
+  :group 'mime)
 
 (defvar mm-binary-coding-system
   (cond
@@ -262,14 +365,17 @@ system object in XEmacs."
     (iso-2022-jp-3 latin-jisx0201 japanese-jisx0208-1978 japanese-jisx0208
                   japanese-jisx0213-1 japanese-jisx0213-2)
     (shift_jis latin-jisx0201 katakana-jisx0201 japanese-jisx0208)
-    ,(if (or (not (fboundp 'charsetp)) ;; non-Mule case
-            (charsetp 'unicode-a)
-            (not (mm-coding-system-p 'mule-utf-8)))
-        '(utf-8 unicode-a unicode-b unicode-c unicode-d unicode-e)
-       ;; If we have utf-8 we're in Mule 5+.
-       (append '(utf-8)
-              (delete 'ascii
-                      (coding-system-get 'mule-utf-8 'safe-charsets)))))
+    ,(cond ((fboundp 'unicode-precedence-list)
+           (cons 'utf-8 (delq 'ascii (mapcar 'charset-name
+                                             (unicode-precedence-list)))))
+          ((or (not (fboundp 'charsetp)) ;; non-Mule case
+               (charsetp 'unicode-a)
+               (not (mm-coding-system-p 'mule-utf-8)))
+           '(utf-8 unicode-a unicode-b unicode-c unicode-d unicode-e))
+          (t ;; If we have utf-8 we're in Mule 5+.
+           (append '(utf-8)
+                   (delete 'ascii
+                           (coding-system-get 'mule-utf-8 'safe-charsets))))))
   "Alist of MIME-charset/MULE-charsets.")
 
 (defun mm-enrich-utf-8-by-mule-ucs ()
@@ -277,10 +383,6 @@ system object in XEmacs."
 This function will run when the `un-define' module is loaded under
 XEmacs, and fill the `utf-8' entry in `mm-mime-mule-charset-alist'
 with Mule charsets.  It is completely useless for Emacs."
-  (unless (cdr (delete '(mm-enrich-utf-8-by-mule-ucs)
-                      (assoc "un-define" after-load-alist)))
-    (setq after-load-alist
-         (delete '("un-define") after-load-alist)))
   (when (boundp 'unicode-basic-translation-charset-order-list)
     (condition-case nil
        (let ((val (delq
@@ -305,7 +407,7 @@ with Mule charsets.  It is completely useless for Emacs."
          cs mime mule alist)
       (while css
        (setq cs (pop css)
-             mime (or (coding-system-get cs :mime-charset) ; Emacs 22
+             mime (or (coding-system-get cs :mime-charset) ; Emacs 23 (unicode)
                       (coding-system-get cs 'mime-charset)))
        (when (and mime
                   (not (eq t (setq mule
@@ -400,11 +502,17 @@ mail with multiple parts is preferred to sending a Unicode one.")
        (pop alist))
       out)))
 
-(defun mm-charset-to-coding-system (charset &optional lbt)
+(defun mm-charset-to-coding-system (charset &optional lbt
+                                           allow-override)
   "Return coding-system corresponding to CHARSET.
 CHARSET is a symbol naming a MIME charset.
 If optional argument LBT (`unix', `dos' or `mac') is specified, it is
-used as the line break code type of the coding system."
+used as the line break code type of the coding system.
+
+If ALLOW-OVERRIDE is given, use `mm-charset-override-alist' to
+map undesired charset names to their replacement.  This should
+only be used for decoding, not for encoding."
+  ;; OVERRIDE is used (only) in `mm-decode-body' and `mm-decode-string'.
   (when (stringp charset)
     (setq charset (intern (downcase charset))))
   (when lbt
@@ -416,6 +524,11 @@ used as the line break code type of the coding system."
    ((or (null (mm-get-coding-system-list))
        (not (fboundp 'coding-system-get)))
     charset)
+   ;; Check override list quite early.  Should only used for decoding, not for
+   ;; encoding!
+   ((and allow-override
+        (let ((cs (cdr (assq charset mm-charset-override-alist))))
+          (and cs (mm-coding-system-p cs) cs))))
    ;; ascii
    ((eq charset 'us-ascii)
     'ascii)
@@ -428,9 +541,27 @@ used as the line break code type of the coding system."
 ;;;     (eq charset (coding-system-get charset 'mime-charset))
         )
     charset)
+   ;; Eval expressions from `mm-charset-eval-alist'
+   ((let* ((el (assq charset mm-charset-eval-alist))
+          (cs (car el))
+          (form (cdr el)))
+      (and cs
+          form
+          (prog2
+              ;; Avoid errors...
+              (condition-case nil (eval form) (error nil))
+              ;; (message "Failed to eval `%s'" form))
+              (mm-coding-system-p cs)
+            (message "Added charset `%s' via `mm-charset-eval-alist'" cs))
+          cs)))
    ;; Translate invalid charsets.
    ((let ((cs (cdr (assq charset mm-charset-synonym-alist))))
-      (and cs (mm-coding-system-p cs) cs)))
+      (and cs
+          (mm-coding-system-p cs)
+          ;; (message
+          ;;  "Using synonym `%s' from `mm-charset-synonym-alist' for `%s'"
+          ;;  cs charset)
+          cs)))
    ;; Last resort: search the coding system list for entries which
    ;; have the right mime-charset in case the canonical name isn't
    ;; defined (though it should be).
@@ -442,6 +573,11 @@ used as the line break code type of the coding system."
                 (eq charset (or (coding-system-get c :mime-charset)
                                 (coding-system-get c 'mime-charset))))
            (setq cs c)))
+      (unless cs
+       ;; Warn the user about unknown charset:
+       (if (fboundp 'gnus-message)
+           (gnus-message 7 "Unknown charset: %s" charset)
+         (message "Unknown charset: %s" charset)))
       cs))))
 
 (eval-and-compile
@@ -529,14 +665,21 @@ If the charset is `composition', return the actual one."
     ;; This is for XEmacs.
     (mm-mule-charset-to-mime-charset charset)))
 
-(defun mm-delete-duplicates (list)
-  "Simple substitute for CL `delete-duplicates', testing with `equal'."
-  (let (result head)
-    (while list
-      (setq head (car list))
-      (setq list (delete head list))
-      (setq result (cons head result)))
-    (nreverse result)))
+(if (fboundp 'delete-dups)
+    (defalias 'mm-delete-duplicates 'delete-dups)
+  (defun mm-delete-duplicates (list)
+    "Destructively remove `equal' duplicates from LIST.
+Store the result in LIST and return it.  LIST must be a proper list.
+Of several `equal' occurrences of an element in LIST, the first
+one is kept.
+
+This is a compatibility function for Emacsen without `delete-dups'."
+    ;; Code from `subr.el' in Emacs 22:
+    (let ((tail list))
+      (while tail
+       (setcdr tail (delete (car tail) (cdr tail)))
+       (setq tail (cdr tail))))
+    list))
 
 ;; Fixme:  This is used in places when it should be testing the
 ;; default multibyteness.  See mm-default-multibyte-p.
@@ -665,7 +808,7 @@ But this is very much a corner case, so don't worry about it."
 
 (defmacro mm-xemacs-find-mime-charset (begin end)
   (when (featurep 'xemacs)
-    `(mm-xemacs-find-mime-charset-1 ,begin ,end)))
+    `(and (featurep 'mule) (mm-xemacs-find-mime-charset-1 ,begin ,end))))
 
 (defun mm-find-mime-charset-region (b e &optional hack-charsets)
   "Return the MIME charsets needed to encode the region between B and E.
@@ -728,6 +871,17 @@ charset, and a longer list means no appropriate charset."
     (if (and (memq 'iso-2022-jp-2 charsets)
             (memq 'iso-2022-jp-2 hack-charsets))
        (setq charsets (delq 'iso-2022-jp charsets)))
+    ;; Attempt to reduce the number of charsets if utf-8 is available.
+    (if (and (featurep 'xemacs)
+            (> (length charsets) 1)
+            (mm-coding-system-p 'utf-8))
+       (let ((mm-coding-system-priorities
+              (cons 'utf-8 mm-coding-system-priorities)))
+         (setq charsets
+               (mm-delete-duplicates
+                (mapcar 'mm-mime-charset
+                        (delq 'ascii
+                              (mm-find-charset-region b e)))))))
     charsets))
 
 (defmacro mm-with-unibyte-buffer (&rest forms)
@@ -749,11 +903,18 @@ Use multibyte mode for this."
 (defmacro mm-with-unibyte-current-buffer (&rest forms)
   "Evaluate FORMS with current buffer temporarily made unibyte.
 Also bind `default-enable-multibyte-characters' to nil.
-Equivalent to `progn' in XEmacs"
+Equivalent to `progn' in XEmacs
+
+NOTE: Use this macro with caution in multibyte buffers (it is not
+worth using this macro in unibyte buffers of course).  Use of
+`(set-buffer-multibyte t)', which is run finally, is generally
+harmful since it is likely to modify existing data in the buffer.
+For instance, it converts \"\\300\\255\" into \"\\255\" in
+Emacs 23 (unicode)."
   (let ((multibyte (make-symbol "multibyte"))
        (buffer (make-symbol "buffer")))
     `(if mm-emacs-mule
-        (let ((,multibyte enable-multibyte-characters)
+        (let ((,multibyte enable-multibyte-characters)
               (,buffer (current-buffer)))
           (unwind-protect
               (let (default-enable-multibyte-characters)
@@ -836,22 +997,28 @@ to advanced Emacs features, such as file-name-handlers, format decoding,
 `find-file-hooks', etc.
 If INHIBIT is non-nil, inhibit `mm-inhibit-file-name-handlers'.
   This function ensures that none of these modifications will take place."
-  (let ((format-alist nil)
-       (auto-mode-alist (if inhibit nil (mm-auto-mode-alist)))
-       (default-major-mode 'fundamental-mode)
-       (enable-local-variables nil)
-       (after-insert-file-functions nil)
-       (enable-local-eval nil)
-       (find-file-hooks nil)
-       (inhibit-file-name-operation (if inhibit
-                                        'insert-file-contents
-                                      inhibit-file-name-operation))
-       (inhibit-file-name-handlers
-        (if inhibit
-            (append mm-inhibit-file-name-handlers
-                    inhibit-file-name-handlers)
-          inhibit-file-name-handlers)))
-    (insert-file-contents filename visit beg end replace)))
+  (let* ((format-alist nil)
+        (auto-mode-alist (if inhibit nil (mm-auto-mode-alist)))
+        (default-major-mode 'fundamental-mode)
+        (enable-local-variables nil)
+        (after-insert-file-functions nil)
+        (enable-local-eval nil)
+        (inhibit-file-name-operation (if inhibit
+                                         'insert-file-contents
+                                       inhibit-file-name-operation))
+        (inhibit-file-name-handlers
+         (if inhibit
+             (append mm-inhibit-file-name-handlers
+                     inhibit-file-name-handlers)
+           inhibit-file-name-handlers))
+        (ffh (if (boundp 'find-file-hook)
+                 'find-file-hook
+               'find-file-hooks))
+        (val (symbol-value ffh)))
+    (set ffh nil)
+    (unwind-protect
+       (insert-file-contents filename visit beg end replace)
+      (set ffh val))))
 
 (defun mm-append-to-file (start end filename &optional codesys inhibit)
   "Append the contents of the region to the end of file FILENAME.
@@ -893,6 +1060,74 @@ If INHIBIT is non-nil, inhibit `mm-inhibit-file-name-handlers'."
           inhibit-file-name-handlers)))
     (write-region start end filename append visit lockname)))
 
+;; It is not a MIME function, but some MIME functions use it.
+(if (and (fboundp 'make-temp-file)
+        (ignore-errors
+          (let ((def (symbol-function 'make-temp-file)))
+            (and (byte-code-function-p def)
+                 (setq def (if (fboundp 'compiled-function-arglist)
+                               ;; XEmacs
+                               (eval (list 'compiled-function-arglist def))
+                             (aref def 0)))
+                 (>= (length def) 4)
+                 (eq (nth 3 def) 'suffix)))))
+    (defalias 'mm-make-temp-file 'make-temp-file)
+  ;; Stolen (and modified for XEmacs) from Emacs 22.
+  (defun mm-make-temp-file (prefix &optional dir-flag suffix)
+    "Create a temporary file.
+The returned file name (created by appending some random characters at the end
+of PREFIX, and expanding against `temporary-file-directory' if necessary),
+is guaranteed to point to a newly created empty file.
+You can then use `write-region' to write new data into the file.
+
+If DIR-FLAG is non-nil, create a new empty directory instead of a file.
+
+If SUFFIX is non-nil, add that at the end of the file name."
+    (let ((umask (default-file-modes))
+         file)
+      (unwind-protect
+         (progn
+           ;; Create temp files with strict access rights.  It's easy to
+           ;; loosen them later, whereas it's impossible to close the
+           ;; time-window of loose permissions otherwise.
+           (set-default-file-modes 448)
+           (while (condition-case err
+                      (progn
+                        (setq file
+                              (make-temp-name
+                               (expand-file-name
+                                prefix
+                                (if (fboundp 'temp-directory)
+                                    ;; XEmacs
+                                    (temp-directory)
+                                  temporary-file-directory))))
+                        (if suffix
+                            (setq file (concat file suffix)))
+                        (if dir-flag
+                            (make-directory file)
+                          (if (featurep 'xemacs)
+                              ;; NOTE: This is unsafe if XEmacs users
+                              ;; don't use a secure temp directory.
+                              (if (file-exists-p file)
+                                  (signal 'file-already-exists
+                                          (list "File exists" file))
+                                (write-region "" nil file nil 'silent))
+                            (write-region "" nil file nil 'silent
+                                          nil 'excl)))
+                        nil)
+                    (file-already-exists t)
+                    ;; The XEmacs version of `make-directory' issues
+                    ;; `file-error'.
+                    (file-error (or (and (featurep 'xemacs)
+                                         (file-exists-p file))
+                                    (signal (car err) (cdr err)))))
+             ;; the file was somehow created by someone else between
+             ;; `make-temp-name' and `write-region', let's try again.
+             nil)
+           file)
+       ;; Reset the umask.
+       (set-default-file-modes umask)))))
+
 (defun mm-image-load-path (&optional package)
   (let (dir result)
     (dolist (path load-path (nreverse result))
@@ -900,7 +1135,7 @@ If INHIBIT is non-nil, inhibit `mm-inhibit-file-name-handlers'."
                 (file-directory-p
                  (setq dir (concat (file-name-directory
                                     (directory-file-name path))
-                                   "etc/" (or package "gnus/")))))
+                                   "etc/images/" (or package "gnus/")))))
        (push dir result))
       (push path result))))
 
@@ -909,7 +1144,7 @@ If INHIBIT is non-nil, inhibit `mm-inhibit-file-name-handlers'."
     (defun mm-detect-coding-region (start end)
       "Like `detect-coding-region' except returning the best one."
       (let ((coding-systems
-            (detect-coding-region (point) (point-max))))
+            (detect-coding-region start end)))
        (or (car-safe coding-systems)
            coding-systems)))
   (defun mm-detect-coding-region (start end)
@@ -939,8 +1174,9 @@ If INHIBIT is non-nil, inhibit `mm-inhibit-file-name-handlers'."
   "Return the MIME charset corresponding to CODING-SYSTEM.
 To make this function work with XEmacs, the APEL package is required."
   (when coding-system
-    (or (coding-system-get coding-system :mime-charset)
-       (coding-system-get coding-system 'mime-charset)
+    (or (and (fboundp 'coding-system-get)
+            (or (coding-system-get coding-system :mime-charset)
+                (coding-system-get coding-system 'mime-charset)))
        (and (featurep 'xemacs)
             (or (and (fboundp 'coding-system-to-mime-charset)
                      (not (eq (symbol-function 'coding-system-to-mime-charset)
@@ -951,44 +1187,73 @@ To make this function work with XEmacs, the APEL package is required."
                      (fboundp 'coding-system-to-mime-charset)))
             (coding-system-to-mime-charset coding-system)))))
 
-(defun mm-decompress-buffer (filename &optional inplace)
-  "Decompress buffer's contents according to the extension of FILENAME.
-If INPLACE is nil, return a decompressed string or nil, and the buffer
-will not be modified.  Otherwise, replace the buffer's contents with
-the decompressed one.  Decompression is done only when the extension
-is \".gz\" or \".bz2\" which does not follow \".tar\"."
-  (let ((decomp (cond ((or (not filename)
-                          (string-match "\\.tar\\.[^.]+\\'" filename))
-                      nil)
-                     ((string-match "\\.gz\\'" filename)
-                      '("gzip" "-c" "-d" "-q"))
-                     ((string-match "\\.bz2\\'" filename)
-                      '("bzip2" "-d")))))
-    (when decomp
-      (let ((coding-system-for-read mm-binary-coding-system)
-           (coding-system-for-write mm-binary-coding-system)
-           cur mod)
-       (if inplace
-           (prog1
-               nil
-             (setq cur (buffer-string)
-                   mod (buffer-modified-p))
-             (condition-case nil
-                 (apply 'call-process-region (point-min) (point-max)
-                        (car decomp) t t nil (cdr decomp))
-               (error
-                (erase-buffer)
-                (insert cur)
-                (set-buffer-modified-p mod))))
-         (setq cur (current-buffer))
+(eval-when-compile
+  (require 'jka-compr))
+
+(defun mm-decompress-buffer (filename &optional inplace force)
+  "Decompress buffer's contents, depending on jka-compr.
+Only when FORCE is t or `auto-compression-mode' is enabled and FILENAME
+agrees with `jka-compr-compression-info-list', decompression is done.
+Signal an error if FORCE is neither nil nor t and compressed data are
+not decompressed because `auto-compression-mode' is disabled.
+If INPLACE is nil, return decompressed data or nil without modifying
+the buffer.  Otherwise, replace the buffer's contents with the
+decompressed data.  The buffer's multibyteness must be turned off."
+  (when (and filename
+            (if force
+                (prog1 t (require 'jka-compr))
+              (and (fboundp 'jka-compr-installed-p)
+                   (jka-compr-installed-p))))
+    (let ((info (jka-compr-get-compression-info filename)))
+      (when info
+       (unless (or (memq force (list nil t))
+                   (jka-compr-installed-p))
+         (error ""))
+       (let ((prog (jka-compr-info-uncompress-program info))
+             (args (jka-compr-info-uncompress-args info))
+             (msg (format "%s %s..."
+                          (jka-compr-info-uncompress-message info)
+                          filename))
+             (err-file (jka-compr-make-temp-name))
+             (cur (current-buffer))
+             (coding-system-for-read mm-binary-coding-system)
+             (coding-system-for-write mm-binary-coding-system)
+             retval err-msg)
+         (message "%s" msg)
          (mm-with-unibyte-buffer
            (insert-buffer-substring cur)
-           (condition-case nil
+           (condition-case err
                (progn
-                 (apply 'call-process-region (point-min) (point-max)
-                        (car decomp) t t nil (cdr decomp))
-                 (buffer-string))
-             (error nil))))))))
+                 (unless (memq (apply 'call-process-region
+                                      (point-min) (point-max)
+                                      prog t (list t err-file) nil args)
+                               jka-compr-acceptable-retval-list)
+                   (erase-buffer)
+                   (insert (mapconcat
+                            'identity
+                            (delete "" (split-string
+                                        (prog2
+                                            (insert-file-contents err-file)
+                                            (buffer-string)
+                                          (erase-buffer))))
+                            " ")
+                           "\n")
+                   (setq err-msg
+                         (format "Error while executing \"%s %s < %s\""
+                                 prog (mapconcat 'identity args " ")
+                                 filename)))
+                 (setq retval (buffer-string)))
+             (error
+              (setq err-msg (error-message-string err)))))
+         (when (file-exists-p err-file)
+           (ignore-errors (jka-compr-delete-temp-file err-file)))
+         (when inplace
+           (unless err-msg
+             (delete-region (point-min) (point-max))
+             (insert retval))
+           (setq retval nil))
+         (message "%s" (or err-msg (concat msg "done")))
+         retval)))))
 
 (eval-when-compile
   (unless (fboundp 'coding-system-name)
@@ -1002,11 +1267,17 @@ is \".gz\" or \".bz2\" which does not follow \".tar\"."
   "Find coding system used to decode the contents of the current buffer.
 This function looks for the coding system magic cookie or examines the
 coding system specified by `file-coding-system-alist' being associated
-with FILENAME which defaults to `buffer-file-name'."
+with FILENAME which defaults to `buffer-file-name'.  Data compressed by
+gzip, bzip2, etc. are allowed."
   (unless filename
     (setq filename buffer-file-name))
   (save-excursion
-    (let ((decomp (mm-decompress-buffer filename)))
+    (let ((decomp (unless ;; No worth to examine charset of tar files.
+                     (and filename
+                          (string-match
+                           "\\.\\(?:tar\\.[^.]+\\|tbz\\|tgz\\)\\'"
+                           filename))
+                   (mm-decompress-buffer filename nil t))))
       (when decomp
        (set-buffer (let (default-enable-multibyte-characters)
                      (generate-new-buffer " *temp*")))
@@ -1079,5 +1350,5 @@ with FILENAME which defaults to `buffer-file-name'."
 
 (provide 'mm-util)
 
-;;; arch-tag: 94dc5388-825d-4fd1-bfa5-2100aa351238
+;; arch-tag: 94dc5388-825d-4fd1-bfa5-2100aa351238
 ;;; mm-util.el ends here