Import Oort Gnus v0.16.
[elisp/gnus.git-] / lisp / mm-util.el
index 0b8f48a..bfc3906 100644 (file)
@@ -1,5 +1,6 @@
 ;;; mm-util.el --- Utility functions for Mule and low level things
-;; Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+;; Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003
+;;   Free Software Foundation, Inc.
 
 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
 ;;     MORIOKA Tomohiko <morioka@jaist.ac.jp>
 (eval-when-compile (require 'cl))
 (require 'mail-prsvr)
 
-(defvar mm-mime-mule-charset-alist
-  `((us-ascii ascii)
-    (iso-8859-1 latin-iso8859-1)
-    (iso-8859-2 latin-iso8859-2)
-    (iso-8859-3 latin-iso8859-3)
-    (iso-8859-4 latin-iso8859-4)
-    (iso-8859-5 cyrillic-iso8859-5)
-    ;; Non-mule (X)Emacs uses the last mule-charset for 8bit characters.
-    ;; The fake mule-charset, gnus-koi8-r, tells Gnus that the default
-    ;; charset is koi8-r, not iso-8859-5.
-    (koi8-r cyrillic-iso8859-5 gnus-koi8-r)
-    (iso-8859-6 arabic-iso8859-6)
-    (iso-8859-7 greek-iso8859-7)
-    (iso-8859-8 hebrew-iso8859-8)
-    (iso-8859-9 latin-iso8859-9)
-    (iso-8859-14 latin-iso8859-14)
-    (iso-8859-15 latin-iso8859-15)
-    (viscii vietnamese-viscii-lower)
-    (iso-2022-jp latin-jisx0201 japanese-jisx0208 japanese-jisx0208-1978)
-    (euc-kr korean-ksc5601)
-    (gb2312 chinese-gb2312)
-    (big5 chinese-big5-1 chinese-big5-2)
-    (tibetan tibetan)
-    (thai-tis620 thai-tis620)
-    (iso-2022-7bit ethiopic arabic-1-column arabic-2-column)
-    (iso-2022-jp-2 latin-iso8859-1 greek-iso8859-7
-                  latin-jisx0201 japanese-jisx0208-1978
-                  chinese-gb2312 japanese-jisx0208
-                  korean-ksc5601 japanese-jisx0212
-                  katakana-jisx0201)
-    (iso-2022-int-1 latin-iso8859-1 greek-iso8859-7
-                   latin-jisx0201 japanese-jisx0208-1978
-                   chinese-gb2312 japanese-jisx0208
-                   korean-ksc5601 japanese-jisx0212
-                   chinese-cns11643-1 chinese-cns11643-2)
-    (iso-2022-int-1 latin-iso8859-1 latin-iso8859-2
-                   cyrillic-iso8859-5 greek-iso8859-7
-                   latin-jisx0201 japanese-jisx0208-1978
-                   chinese-gb2312 japanese-jisx0208
-                   korean-ksc5601 japanese-jisx0212
-                   chinese-cns11643-1 chinese-cns11643-2
-                   chinese-cns11643-3 chinese-cns11643-4
-                   chinese-cns11643-5 chinese-cns11643-6
-                   chinese-cns11643-7)
-    ,(if (or (not (fboundp 'charsetp)) ;; non-Mule case
-            (not (fboundp 'coding-system-p))
-            (charsetp 'unicode-a)
-            (not (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+.
-       (delete 'ascii (coding-system-get 'mule-utf-8 'safe-charsets))))
-  "Alist of MIME-charset/MULE-charsets.")
-
 (eval-and-compile
   (mapcar
    (lambda (elem)
      (coding-system-list . ignore)
      (decode-coding-region . ignore)
      (char-int . identity)
-     (device-type . ignore)
      (coding-system-equal . equal)
      (annotationp . ignore)
      (set-buffer-file-coding-system . ignore)
      (make-char
       . (lambda (charset int)
          (int-to-char int)))
-     (read-coding-system
-      . (lambda (prompt)
-         "Prompt the user for a coding system."
-         (completing-read
-          prompt (mapcar (lambda (s) (list (symbol-name (car s))))
-                         mm-mime-mule-charset-alist))))
      (read-charset
       . (lambda (prompt)
          "Return a charset."
      (subst-char-in-string
       . (lambda (from to string) ;; stolen (and renamed) from nnheader.el
          "Replace characters in STRING from FROM to TO."
-         (let ((string (substring string 0))   ;Copy string.
+         (let ((string (substring string 0)) ;Copy string.
                (len (length string))
                (idx 0))
            ;; Replace all occurrences of FROM with TO.
              (setq idx (1+ idx)))
            string)))
      (string-as-unibyte . identity)
-     (multibyte-string-p . ignore)
-     )))
+     (string-make-unibyte . identity)
+     (string-as-multibyte . identity)
+     (multibyte-string-p . ignore))))
 
 (eval-and-compile
   (defalias 'mm-char-or-char-int-p
      ((fboundp 'char-valid-p) 'char-valid-p)
      (t 'identity))))
 
+(eval-and-compile
+  (defalias 'mm-read-coding-system
+    (cond
+     ((fboundp 'read-coding-system)
+      (if (and (featurep 'xemacs)
+              (<= (string-to-number emacs-version) 21.1))
+         (lambda (prompt &optional default-coding-system)
+           (read-coding-system prompt))
+       'read-coding-system))
+     (t (lambda (prompt &optional default-coding-system)
+         "Prompt the user for a coding system."
+         (completing-read
+          prompt (mapcar (lambda (s) (list (symbol-name (car s))))
+                         mm-mime-mule-charset-alist)))))))
+
 (defvar mm-coding-system-list nil)
 (defun mm-get-coding-system-list ()
   "Get the coding system list."
       (memq sym (mm-get-coding-system-list))))
 
 (defvar mm-charset-synonym-alist
-  `((big5 . cn-big5)
-    (gb2312 . cn-gb-2312)
-    (cn-gb . cn-gb-2312)
+  `(
+    ;; Perfectly fine?  A valid MIME name, anyhow.
+    ,@(unless (mm-coding-system-p 'big5)
+       '((big5 . cn-big5)))
+    ;; Not in XEmacs, but it's not a proper MIME charset anyhow.
+    ,@(unless (mm-coding-system-p 'x-ctext)
+       '((x-ctext . ctext)))
+    ;; Apparently not defined in Emacs 20, but is a valid MIME name.
+    ,@(unless (mm-coding-system-p 'gb2312)
+       '((gb2312 . cn-gb-2312)))
+    ;; ISO-8859-15 is very similar to ISO-8859-1.
+    ,@(unless (mm-coding-system-p 'iso-8859-15) ; Emacs 21 defines it.
+       '((iso-8859-15 . iso-8859-1)))
     ;; Windows-1252 is actually a superset of Latin-1.  See also
     ;; `gnus-article-dumbquotes-map'.
-    ,(unless (mm-coding-system-p 'windows-1252) ; should be defined eventually
-       '(windows-1252 . iso-8859-1))
-    (x-ctext . ctext))
+    ,@(unless (mm-coding-system-p 'windows-1252)
+       (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.
+    ,@(if (and (not (mm-coding-system-p 'windows-1250))
+              (mm-coding-system-p 'cp1250))
+         '((windows-1250 . cp1250)))
+    )
   "A mapping from invalid charset names to the real charset names.")
 
 (defvar mm-binary-coding-system
    (t mm-binary-coding-system))
   "Coding system of auto save file.")
 
+(defvar mm-universal-coding-system mm-auto-save-coding-system
+  "The universal coding system.")
+
+;; Fixme: some of the cars here aren't valid MIME charsets.  That
+;; should only matter with XEmacs, though.
+(defvar mm-mime-mule-charset-alist
+  `((us-ascii ascii)
+    (iso-8859-1 latin-iso8859-1)
+    (iso-8859-2 latin-iso8859-2)
+    (iso-8859-3 latin-iso8859-3)
+    (iso-8859-4 latin-iso8859-4)
+    (iso-8859-5 cyrillic-iso8859-5)
+    ;; Non-mule (X)Emacs uses the last mule-charset for 8bit characters.
+    ;; The fake mule-charset, gnus-koi8-r, tells Gnus that the default
+    ;; charset is koi8-r, not iso-8859-5.
+    (koi8-r cyrillic-iso8859-5 gnus-koi8-r)
+    (iso-8859-6 arabic-iso8859-6)
+    (iso-8859-7 greek-iso8859-7)
+    (iso-8859-8 hebrew-iso8859-8)
+    (iso-8859-9 latin-iso8859-9)
+    (iso-8859-14 latin-iso8859-14)
+    (iso-8859-15 latin-iso8859-15)
+    (viscii vietnamese-viscii-lower)
+    (iso-2022-jp latin-jisx0201 japanese-jisx0208 japanese-jisx0208-1978)
+    (euc-kr korean-ksc5601)
+    (gb2312 chinese-gb2312)
+    (big5 chinese-big5-1 chinese-big5-2)
+    (tibetan tibetan)
+    (thai-tis620 thai-tis620)
+    (iso-2022-7bit ethiopic arabic-1-column arabic-2-column)
+    (iso-2022-jp-2 latin-iso8859-1 greek-iso8859-7
+                  latin-jisx0201 japanese-jisx0208-1978
+                  chinese-gb2312 japanese-jisx0208
+                  korean-ksc5601 japanese-jisx0212
+                  katakana-jisx0201)
+    (iso-2022-int-1 latin-iso8859-1 greek-iso8859-7
+                   latin-jisx0201 japanese-jisx0208-1978
+                   chinese-gb2312 japanese-jisx0208
+                   korean-ksc5601 japanese-jisx0212
+                   chinese-cns11643-1 chinese-cns11643-2)
+    (iso-2022-int-1 latin-iso8859-1 latin-iso8859-2
+                   cyrillic-iso8859-5 greek-iso8859-7
+                   latin-jisx0201 japanese-jisx0208-1978
+                   chinese-gb2312 japanese-jisx0208
+                   korean-ksc5601 japanese-jisx0212
+                   chinese-cns11643-1 chinese-cns11643-2
+                   chinese-cns11643-3 chinese-cns11643-4
+                   chinese-cns11643-5 chinese-cns11643-6
+                   chinese-cns11643-7)
+    ,(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)))))
+  "Alist of MIME-charset/MULE-charsets.")
+
+;; Correct by construction, but should be unnecessary:
+;; XEmacs hates it.
+(when (and (not (featurep 'xemacs))
+          (fboundp 'coding-system-list)
+          (fboundp 'sort-coding-systems))
+  (setq mm-mime-mule-charset-alist
+       (apply
+        'nconc
+        (mapcar
+         (lambda (cs)
+           (when (and (coding-system-get cs 'mime-charset)
+                      (not (eq t (coding-system-get cs 'safe-charsets))))
+             (list (cons (coding-system-get cs 'mime-charset)
+                         (delq 'ascii
+                               (coding-system-get cs 'safe-charsets))))))
+         (sort-coding-systems (coding-system-list 'base-only))))))
+
+(defvar mm-hack-charsets '(iso-8859-15 iso-2022-jp-2)
+  "A list of special charsets.
+Valid elements include:
+`iso-8859-15'    convert ISO-8859-1, -9 to ISO-8859-15 if ISO-8859-15 exists.
+`iso-2022-jp-2'  convert ISO-2022-jp to ISO-2022-jp-2 if ISO-2022-jp-2 exists."
+)
+
+(defvar mm-iso-8859-15-compatible
+  '((iso-8859-1 "\xA4\xA6\xA8\xB4\xB8\xBC\xBD\xBE")
+    (iso-8859-9 "\xA4\xA6\xA8\xB4\xB8\xBC\xBD\xBE\xD0\xDD\xDE\xF0\xFD\xFE"))
+  "ISO-8859-15 exchangeable coding systems and inconvertible characters.")
+
+(defvar mm-iso-8859-x-to-15-table
+  (and (fboundp 'coding-system-p)
+       (mm-coding-system-p 'iso-8859-15)
+       (mapcar
+       (lambda (cs)
+         (if (mm-coding-system-p (car cs))
+             (let ((c (string-to-char
+                       (decode-coding-string "\341" (car cs)))))
+               (cons (char-charset c)
+                     (cons
+                      (- (string-to-char
+                          (decode-coding-string "\341" 'iso-8859-15)) c)
+                      (string-to-list (decode-coding-string (car (cdr cs))
+                                                            (car cs))))))
+           '(gnus-charset 0)))
+       mm-iso-8859-15-compatible))
+  "A table of the difference character between ISO-8859-X and ISO-8859-15.")
+
+(defcustom mm-coding-system-priorities
+  (if (boundp 'current-language-environment)
+      (let ((lang (symbol-value 'current-language-environment)))
+       (cond ((string= lang "Japanese")
+              ;; Japanese users may prefer iso-2022-jp to shift-jis.
+              '(iso-2022-jp iso-2022-jp-2 japanese-shift-jis
+                            iso-latin-1 utf-8)))))
+  "Preferred coding systems for encoding outgoing mails.
+
+More than one suitable coding systems may be found for some texts.  By
+default, a coding system with the highest priority is used to encode
+outgoing mails (see `sort-coding-systems').  If this variable is set,
+it overrides the default priority."
+  :type '(repeat (symbol :tag "Coding system"))
+  :group 'mime)
+
+(defvar mm-use-find-coding-systems-region
+  (fboundp 'find-coding-systems-region)
+  "Use `find-coding-systems-region' to find proper coding systems.
+
+Setting it to nil is useful on Emacsen supporting Unicode if sending
+mail with multiple parts is preferred to sending a Unicode one.")
+
 ;;; Internal variables:
 
 ;;; Functions:
 
 (defun mm-mule-charset-to-mime-charset (charset)
   "Return the MIME charset corresponding to the given Mule CHARSET."
-  (let ((alist mm-mime-mule-charset-alist)
-       out)
-    (while alist
-      (when (memq charset (cdar alist))
-       (setq out (caar alist)
-             alist nil))
-      (pop alist))
-    out))
+  (if (fboundp 'find-coding-systems-for-charsets)
+      (let (mime)
+       (dolist (cs (sort-coding-systems
+                    (copy-sequence
+                     (find-coding-systems-for-charsets (list charset)))))
+         (unless mime
+           (when cs
+             (setq mime (coding-system-get cs 'mime-charset)))))
+       mime)
+    (let ((alist mm-mime-mule-charset-alist)
+         out)
+      (while alist
+       (when (memq charset (cdar alist))
+         (setq out (caar alist)
+               alist nil))
+       (pop alist))
+      out)))
 
 (defun mm-charset-to-coding-system (charset &optional lbt)
   "Return coding-system corresponding to CHARSET.
@@ -210,12 +323,11 @@ If optional argument LBT (`unix', `dos' or `mac') is specified, it is
 used as the line break code type of the coding system."
   (when (stringp charset)
     (setq charset (intern (downcase charset))))
-  (setq charset
-       (or (cdr (assq charset mm-charset-synonym-alist))
-           charset))
   (when lbt
     (setq charset (intern (format "%s-%s" charset lbt))))
   (cond
+   ((null charset)
+    charset)
    ;; Running in a non-MULE environment.
    ((null (mm-get-coding-system-list))
     charset)
@@ -224,57 +336,96 @@ used as the line break code type of the coding system."
     'ascii)
    ;; Check to see whether we can handle this charset.  (This depends
    ;; on there being some coding system matching each `mime-charset'
-   ;; coding sysytem property defined, as there should be.)
-   ((memq charset (mm-get-coding-system-list))
+   ;; property defined, as there should be.)
+   ((and (mm-coding-system-p charset)
+;;; Doing this would potentially weed out incorrect charsets.
+;;;     charset
+;;;     (eq charset (coding-system-get charset 'mime-charset))
+        )
     charset)
-   ;; Nope.
-   (t
-    nil)))
+   ;; Translate invalid charsets.
+   ((let ((cs (cdr (assq charset mm-charset-synonym-alist))))
+      (and cs (mm-coding-system-p cs) 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).
+   ((let (cs)
+      ;; mm-get-coding-system-list returns a list of cs without lbt.
+      ;; Do we need -lbt?
+      (dolist (c (mm-get-coding-system-list))
+       (if (and (null cs)
+                (eq charset (coding-system-get c 'mime-charset)))
+           (setq cs c)))
+      cs))))
 
 (defsubst mm-replace-chars-in-string (string from to)
   (mm-subst-char-in-string from to string))
 
-(defsubst mm-enable-multibyte ()
-  "Set the multibyte flag of the current buffer.
+(eval-and-compile
+  (defvar mm-emacs-mule (and (not (featurep 'xemacs))
+                            (boundp 'default-enable-multibyte-characters)
+                            default-enable-multibyte-characters
+                            (fboundp 'set-buffer-multibyte))
+    "Emacs mule.")
+
+  (defvar mm-mule4-p (and mm-emacs-mule
+                         (fboundp 'charsetp)
+                         (not (charsetp 'eight-bit-control)))
+    "Mule version 4.")
+
+  (if mm-emacs-mule
+      (defun mm-enable-multibyte ()
+       "Set the multibyte flag of the current buffer.
 Only do this if the default value of `enable-multibyte-characters' is
 non-nil.  This is a no-op in XEmacs."
-  (when (and (not (featurep 'xemacs))
-             (boundp 'default-enable-multibyte-characters)
-            default-enable-multibyte-characters
-            (fboundp 'set-buffer-multibyte))
-    (set-buffer-multibyte t)))
-
-(defsubst mm-disable-multibyte ()
-  "Unset the multibyte flag of in the current buffer.
+       (set-buffer-multibyte t))
+    (defalias 'mm-enable-multibyte 'ignore))
+
+  (if mm-emacs-mule
+      (defun mm-disable-multibyte ()
+       "Unset the multibyte flag of in the current buffer.
 This is a no-op in XEmacs."
-  (when (and (not (featurep 'xemacs))
-            (fboundp 'set-buffer-multibyte))
-    (set-buffer-multibyte nil)))
+       (set-buffer-multibyte nil))
+    (defalias 'mm-disable-multibyte 'ignore))
 
-(defsubst mm-enable-multibyte-mule4 ()
-  "Enable multibyte in the current buffer.
+  (if mm-mule4-p
+      (defun mm-enable-multibyte-mule4  ()
+       "Enable multibyte in the current buffer.
 Only used in Emacs Mule 4."
-  (when (and (not (featurep 'xemacs))
-             (boundp 'default-enable-multibyte-characters)
-            default-enable-multibyte-characters
-            (fboundp 'set-buffer-multibyte)
-            (fboundp 'charsetp)
-            (not (charsetp 'eight-bit-control)))
-    (set-buffer-multibyte t)))
-
-(defsubst mm-disable-multibyte-mule4 ()
-  "Disable multibyte in the current buffer.
+       (set-buffer-multibyte t))
+    (defalias 'mm-enable-multibyte-mule4 'ignore))
+
+  (if mm-mule4-p
+      (defun mm-disable-multibyte-mule4 ()
+       "Disable multibyte in the current buffer.
 Only used in Emacs Mule 4."
-  (when (and (not (featurep 'xemacs))
-            (fboundp 'set-buffer-multibyte)
-            (fboundp 'charsetp)
-            (not (charsetp 'eight-bit-control)))
-    (set-buffer-multibyte nil)))
+       (set-buffer-multibyte nil))
+    (defalias 'mm-disable-multibyte-mule4 'ignore)))
 
 (defun mm-preferred-coding-system (charset)
   ;; A typo in some Emacs versions.
-  (or (get-charset-property charset 'prefered-coding-system)
-      (get-charset-property charset 'preferred-coding-system)))
+  (or (get-charset-property charset 'preferred-coding-system)
+      (get-charset-property charset 'prefered-coding-system)))
+
+(defsubst mm-guess-charset ()
+  "Guess Mule charset from the language environment."
+  (or
+   mail-parse-mule-charset ;; cached mule-charset
+   (progn
+     (setq mail-parse-mule-charset
+          (and (boundp 'current-language-environment)
+               (car (last
+                     (assq 'charset
+                           (assoc current-language-environment
+                                  language-info-alist))))))
+     (if (or (not mail-parse-mule-charset)
+            (eq mail-parse-mule-charset 'ascii))
+        (setq mail-parse-mule-charset
+              (or (car (last (assq mail-parse-charset
+                                   mm-mime-mule-charset-alist)))
+                  ;; default
+                  'latin-iso8859-1)))
+     mail-parse-mule-charset)))
 
 (defun mm-charset-after (&optional pos)
   "Return charset of a character in current buffer at position POS.
@@ -292,28 +443,12 @@ If the charset is `composition', return the actual one."
        (if (and charset (not (memq charset '(ascii eight-bit-control
                                                    eight-bit-graphic))))
            charset
-         (or
-          mail-parse-mule-charset ;; cached mule-charset
-          (progn
-            (setq mail-parse-mule-charset
-                  (and (boundp 'current-language-environment)
-                     (car (last
-                           (assq 'charset
-                                 (assoc current-language-environment
-                                        language-info-alist))))))
-            (if (or (not mail-parse-mule-charset)
-                    (eq mail-parse-mule-charset 'ascii))
-                (setq mail-parse-mule-charset
-                      (or (car (last (assq mail-parse-charset
-                                           mm-mime-mule-charset-alist)))
-                          ;; Fixme: don't fix that!
-                          'latin-iso8859-1)))
-            mail-parse-mule-charset)))))))
+         (mm-guess-charset))))))
 
 (defun mm-mime-charset (charset)
-  "Return the MIME charset corresponding to the MULE CHARSET."
+  "Return the MIME charset corresponding to the given Mule CHARSET."
   (if (eq charset 'unknown)
-      (error "The message contains non-printable characters, please use attachment."))
+      (error "The message contains non-printable characters, please use attachment"))
   (if (and (fboundp 'coding-system-get) (fboundp 'get-charset-property))
       ;; This exists in Emacs 20.
       (or
@@ -328,7 +463,7 @@ If the charset is `composition', return the actual one."
     (mm-mule-charset-to-mime-charset charset)))
 
 (defun mm-delete-duplicates (list)
-  "Simple  substitute for CL `delete-duplicates', testing with `equal'."
+  "Simple substitute for CL `delete-duplicates', testing with `equal'."
   (let (result head)
     (while list
       (setq head (car list))
@@ -336,27 +471,80 @@ If the charset is `composition', return the actual one."
       (setq result (cons head result)))
     (nreverse result)))
 
-(defun mm-find-mime-charset-region (b e)
-  "Return the MIME charsets needed to encode the region between B and E."
-  (let ((charsets (mapcar 'mm-mime-charset
-                         (delq 'ascii
-                               (mm-find-charset-region b e)))))
-    (when (memq 'iso-2022-jp-2 charsets)
-      (setq charsets (delq 'iso-2022-jp charsets)))
-    (setq charsets (mm-delete-duplicates charsets))
+(if (and (not (featurep 'xemacs))
+        (boundp 'enable-multibyte-characters))
+    (defalias 'mm-multibyte-p
+      (lambda ()
+       "Say whether multibyte is enabled in the current buffer."
+       enable-multibyte-characters))
+  (defalias 'mm-multibyte-p (lambda () (featurep 'mule))))
+
+(defun mm-iso-8859-x-to-15-region (&optional b e)
+  (if (fboundp 'char-charset)
+      (let (charset item c inconvertible)
+       (save-restriction
+         (if e (narrow-to-region b e))
+         (goto-char (point-min))
+         (skip-chars-forward "\0-\177")
+         (while (not (eobp))
+           (cond
+            ((not (setq item (assq (char-charset (setq c (char-after)))
+                                   mm-iso-8859-x-to-15-table)))
+             (forward-char))
+            ((memq c (cdr (cdr item)))
+             (setq inconvertible t)
+             (forward-char))
+            (t
+             (insert-before-markers (prog1 (+ c (car (cdr item)))
+                                      (delete-char 1)))))
+           (skip-chars-forward "\0-\177")))
+       (not inconvertible))))
+
+(defun mm-sort-coding-systems-predicate (a b)
+  (> (length (memq a mm-coding-system-priorities))
+     (length (memq b mm-coding-system-priorities))))
+
+(defun mm-find-mime-charset-region (b e &optional hack-charsets)
+  "Return the MIME charsets needed to encode the region between B and E.
+nil means ASCII, a single-element list represents an appropriate MIME
+charset, and a longer list means no appropriate charset."
+  (let (charsets)
+    ;; The return possibilities of this function are a mess...
+    (or (and (mm-multibyte-p)
+            mm-use-find-coding-systems-region
+            ;; Find the mime-charset of the most preferred coding
+            ;; system that has one.
+            (let ((systems (find-coding-systems-region b e)))
+              (when mm-coding-system-priorities
+                (setq systems
+                      (sort systems 'mm-sort-coding-systems-predicate)))
+              ;; Fixme: The `mime-charset' (`x-ctext') of `compound-text'
+              ;; is not in the IANA list.
+              (setq systems (delq 'compound-text systems))
+              (unless (equal systems '(undecided))
+                (while systems
+                  (let ((cs (coding-system-get (pop systems) 'mime-charset)))
+                    (if cs
+                        (setq systems nil
+                              charsets (list cs))))))
+              charsets))
+       ;; Otherwise we're not multibyte, XEmacs or a single coding
+       ;; system won't cover it.
+       (setq charsets
+             (mm-delete-duplicates
+              (mapcar 'mm-mime-charset
+                      (delq 'ascii
+                            (mm-find-charset-region b e))))))
     (if (and (> (length charsets) 1)
-            (fboundp 'find-coding-systems-region)
-            (let ((cs (find-coding-systems-region b e)))
-              (or (memq 'utf-8 cs) (memq 'mule-utf-8 cs))))
-       '(utf-8)
-      charsets)))
-
-(defsubst mm-multibyte-p ()
-  "Say whether multibyte is enabled."
-  (if (and (not (featurep 'xemacs))
-          (boundp 'enable-multibyte-characters))
-      enable-multibyte-characters
-    (featurep 'mule)))
+            (memq 'iso-8859-15 charsets)
+            (memq 'iso-8859-15 hack-charsets)
+            (save-excursion (mm-iso-8859-x-to-15-region b e)))
+       (mapcar (lambda (x) (setq charsets (delq (car x) charsets)))
+               mm-iso-8859-15-compatible))
+    (if (and (memq 'iso-2022-jp-2 charsets)
+            (memq 'iso-2022-jp-2 hack-charsets))
+       (setq charsets (delq 'iso-2022-jp charsets)))
+    charsets))
 
 (defmacro mm-with-unibyte-buffer (&rest forms)
   "Create a temporary buffer, and evaluate FORMS there like `progn'.
@@ -370,18 +558,17 @@ Use unibyte mode for this."
   "Evaluate FORMS with current current buffer temporarily made unibyte.
 Also bind `default-enable-multibyte-characters' to nil.
 Equivalent to `progn' in XEmacs"
-  (let ((buffer (make-symbol "buffer")))
-    `(if (and (not (featurep 'xemacs))
-             (boundp 'enable-multibyte-characters)
-             enable-multibyte-characters
-             (fboundp 'set-buffer-multibyte))
-        (let ((,buffer (current-buffer)))
+  (let ((multibyte (make-symbol "multibyte"))
+       (buffer (make-symbol "buffer")))
+    `(if mm-emacs-mule
+        (let ((,multibyte enable-multibyte-characters)
+              (,buffer (current-buffer)))
           (unwind-protect
               (let (default-enable-multibyte-characters)
                 (set-buffer-multibyte nil)
                 ,@forms)
             (set-buffer ,buffer)
-            (set-buffer-multibyte t)))
+            (set-buffer-multibyte ,multibyte)))
        (let (default-enable-multibyte-characters)
         ,@forms))))
 (put 'mm-with-unibyte-current-buffer 'lisp-indent-function 0)
@@ -390,20 +577,17 @@ Equivalent to `progn' in XEmacs"
 (defmacro mm-with-unibyte-current-buffer-mule4 (&rest forms)
   "Evaluate FORMS there like `progn' in current buffer.
 Mule4 only."
-  (let ((buffer (make-symbol "buffer")))
-    `(if (and (not (featurep 'xemacs))
-             (boundp 'enable-multibyte-characters)
-             enable-multibyte-characters
-             (fboundp 'set-buffer-multibyte)
-             (fboundp 'charsetp)
-             (not (charsetp 'eight-bit-control))) ;; For Emacs Mule 4 only.
-       (let ((,buffer (current-buffer)))
-        (unwind-protect
-            (let (default-enable-multibyte-characters)
-              (set-buffer-multibyte nil)
-              ,@forms)
-          (set-buffer ,buffer)
-          (set-buffer-multibyte t)))
+  (let ((multibyte (make-symbol "multibyte"))
+       (buffer (make-symbol "buffer")))
+    `(if mm-mule4-p
+        (let ((,multibyte enable-multibyte-characters)
+              (,buffer (current-buffer)))
+          (unwind-protect
+              (let (default-enable-multibyte-characters)
+                (set-buffer-multibyte nil)
+                ,@forms)
+            (set-buffer ,buffer)
+            (set-buffer-multibyte ,multibyte)))
        (let (default-enable-multibyte-characters)
         ,@forms))))
 (put 'mm-with-unibyte-current-buffer-mule4 'lisp-indent-function 0)
@@ -420,12 +604,13 @@ Mule4 only."
   "Return a list of Emacs charsets in the region B to E."
   (cond
    ((and (mm-multibyte-p)
-        (fboundp 'find-charset-region))
+        (fboundp 'find-charset-region))
     ;; Remove composition since the base charsets have been included.
     ;; Remove eight-bit-*, treat them as ascii.
     (let ((css (find-charset-region b e)))
       (mapcar (lambda (cs) (setq css (delq cs css)))
-             '(composition eight-bit-control eight-bit-graphic))
+             '(composition eight-bit-control eight-bit-graphic
+                           control-1))
       css))
    (t
     ;; We are in a unibyte buffer or XEmacs non-mule, so we futz around a bit.
@@ -490,7 +675,7 @@ If INHIBIT is non-nil, inhibit mm-inhibit-file-name-handlers.
        (auto-mode-alist (if inhibit nil (mm-auto-mode-alist)))
        (default-major-mode 'fundamental-mode)
        (enable-local-variables nil)
-        (after-insert-file-functions nil)
+       (after-insert-file-functions nil)
        (enable-local-eval nil)
        (find-file-hooks nil)
        (inhibit-file-name-operation (if inhibit
@@ -510,7 +695,7 @@ START, END and FILENAME.  START and END are buffer positions
 saying what text to write.
 Optional fourth argument specifies the coding system to use when
 encoding the file.
-If INHIBIT is non-nil, inhibit mm-inhibit-file-name-handlers."
+If INHIBIT is non-nil, inhibit `mm-inhibit-file-name-handlers'."
   (let ((coding-system-for-write
         (or codesys mm-text-coding-system-for-write
             mm-text-coding-system))
@@ -528,7 +713,7 @@ If INHIBIT is non-nil, inhibit mm-inhibit-file-name-handlers."
                              coding-system inhibit)
 
   "Like `write-region'.
-If INHIBIT is non-nil, inhibit mm-inhibit-file-name-handlers."
+If INHIBIT is non-nil, inhibit `mm-inhibit-file-name-handlers'."
   (let ((coding-system-for-write
         (or coding-system mm-text-coding-system-for-write
             mm-text-coding-system))
@@ -552,6 +737,70 @@ If INHIBIT is non-nil, inhibit mm-inhibit-file-name-handlers."
          (push dir result))
       (push path result))))
 
+(if (fboundp 'detect-coding-region)
+    (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))))
+       (or (car-safe coding-systems)
+           coding-systems)))
+  (defun mm-detect-coding-region (start end)
+    (let ((point (point)))
+      (goto-char start)
+      (skip-chars-forward "\0-\177" end)
+      (prog1
+         (if (eq (point) end) 'ascii (mm-guess-charset))
+       (goto-char point)))))
+
+(if (fboundp 'coding-system-get)
+    (defun mm-detect-mime-charset-region (start end)
+      "Detect MIME charset of the text in the region between START and END."
+      (let ((cs (mm-detect-coding-region start end)))
+       (coding-system-get cs 'mime-charset)))
+  (defun mm-detect-mime-charset-region (start end)
+    "Detect MIME charset of the text in the region between START and END."
+    (let ((cs (mm-detect-coding-region start end)))
+      cs)))
+
+(defun mm-guess-mime-charset ()
+  "Guess the default MIME charset from the language environment."
+  (let ((language-info
+        (and (boundp 'current-language-environment)
+             (assoc current-language-environment
+                    language-info-alist)))
+       item)
+    (cond
+     ((null language-info)
+      'iso-8859-1)
+     ((setq item
+           (cadr
+            (or (assq 'coding-priority language-info)
+                (assq 'coding-system language-info))))
+      (if (fboundp 'coding-system-get)
+         (or (coding-system-get item 'mime-charset)
+             item)
+       item))
+     ((setq item (car (last (assq 'charset language-info))))
+      (if (eq item 'ascii)
+         'iso-8859-1
+       (mm-mime-charset item)))
+     (t
+      'iso-8859-1))))
+
+;; It is not a MIME function, but some MIME functions use it.
+(defalias 'mm-make-temp-file
+  (if (fboundp 'make-temp-file)
+      '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))))
+
 (provide 'mm-util)
 
 ;;; mm-util.el ends here