+ ;; 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))
+
+(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."
+ (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."
+ (set-buffer-multibyte nil))
+ (defalias 'mm-disable-multibyte 'ignore))
+
+ (if mm-mule4-p
+ (defun mm-enable-multibyte-mule4 ()
+ "Enable multibyte in the current buffer.
+Only used in Emacs Mule 4."
+ (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."
+ (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 '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.
+If POS is nil, it defauls to the current point.
+If POS is out of range, the value is nil.
+If the charset is `composition', return the actual one."
+ (let ((char (char-after pos)) charset)
+ (if (< (mm-char-int char) 128)
+ (setq charset 'ascii)
+ ;; charset-after is fake in some Emacsen.
+ (setq charset (and (fboundp 'char-charset) (char-charset char)))
+ (if (eq charset 'composition)
+ (let ((p (or pos (point))))
+ (cadr (find-charset-region p (1+ p))))
+ (if (and charset (not (memq charset '(ascii eight-bit-control
+ eight-bit-graphic))))
+ charset
+ (mm-guess-charset))))))
+
+(defun mm-mime-charset (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"))
+ (if (and (fboundp 'coding-system-get) (fboundp 'get-charset-property))
+ ;; This exists in Emacs 20.
+ (or
+ (and (mm-preferred-coding-system charset)
+ (coding-system-get
+ (mm-preferred-coding-system charset) 'mime-charset))
+ (and (eq charset 'ascii)
+ 'us-ascii)
+ (mm-preferred-coding-system charset)
+ (mm-mule-charset-to-mime-charset charset))
+ ;; 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 (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 (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'.
+Use unibyte mode for this."
+ `(let (default-enable-multibyte-characters)
+ (with-temp-buffer ,@forms)))
+(put 'mm-with-unibyte-buffer 'lisp-indent-function 0)
+(put 'mm-with-unibyte-buffer 'edebug-form-spec '(body))
+
+(defmacro mm-with-unibyte-current-buffer (&rest forms)
+ "Evaluate FORMS with current current buffer temporarily made unibyte.
+Also bind `default-enable-multibyte-characters' to nil.
+Equivalent to `progn' in XEmacs"
+ (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 ,multibyte)))
+ (let (default-enable-multibyte-characters)
+ ,@forms))))
+(put 'mm-with-unibyte-current-buffer 'lisp-indent-function 0)
+(put 'mm-with-unibyte-current-buffer 'edebug-form-spec '(body))
+
+(defmacro mm-with-unibyte-current-buffer-mule4 (&rest forms)
+ "Evaluate FORMS there like `progn' in current buffer.
+Mule4 only."
+ (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)
+(put 'mm-with-unibyte-current-buffer-mule4 'edebug-form-spec '(body))
+
+(defmacro mm-with-unibyte (&rest forms)
+ "Eval the FORMS with the default value of `enable-multibyte-characters' nil, ."
+ `(let (default-enable-multibyte-characters)
+ ,@forms))
+(put 'mm-with-unibyte 'lisp-indent-function 0)
+(put 'mm-with-unibyte 'edebug-form-spec '(body))
+
+(defun mm-find-charset-region (b e)
+ "Return a list of Emacs charsets in the region B to E."
+ (cond
+ ((and (mm-multibyte-p)
+ (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
+ control-1))
+ css))