2001-08-19 Simon Josefsson <jas@extundo.com>
[gnus] / lisp / mm-util.el
index 70172f4..80c182a 100644 (file)
@@ -1,5 +1,5 @@
-;;; mm-util.el --- Utility functions for MIME things
-;; Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
+;;; mm-util.el --- Utility functions for Mule and low level things
+;; Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
 
 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
 ;;     MORIOKA Tomohiko <morioka@jaist.ac.jp>
 
 ;;; Code:
 
+(eval-when-compile (require 'cl))
 (require 'mail-prsvr)
 
 (defvar mm-mime-mule-charset-alist
-  '((us-ascii ascii)
+  `((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 
+  ;; 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)
-    (cn-gb-2312 chinese-gb2312)
-    (cn-big5 chinese-big5-1 chinese-big5-2)
+    (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)
                    chinese-cns11643-3 chinese-cns11643-4
                    chinese-cns11643-5 chinese-cns11643-6
                    chinese-cns11643-7)
-    (utf-8 unicode-a unicode-b unicode-c unicode-d unicode-e))
+    ,(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
      (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."
            prompt
            (mapcar (lambda (e) (list (symbol-name (car e))))
                    mm-mime-mule-charset-alist)
-           nil t)))))))
+           nil t))))
+     (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.
+               (len (length string))
+               (idx 0))
+           ;; Replace all occurrences of FROM with TO.
+           (while (< idx len)
+             (when (= (aref string idx) from)
+               (aset string idx to))
+             (setq idx (1+ idx)))
+           string)))
+     (string-as-unibyte . identity)
+     (string-as-multibyte . identity)
+     (multibyte-string-p . ignore))))
 
 (eval-and-compile
   (defalias 'mm-char-or-char-int-p
-    (cond 
+    (cond
      ((fboundp 'char-or-char-int-p) 'char-or-char-int-p)
-     ((fboundp 'char-valid-p) 'char-valid-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."
   (or mm-coding-system-list
       (setq mm-coding-system-list (mm-coding-system-list))))
 
-(defvar mm-charset-synonym-alist
-  '((big5 . cn-big5)
-    (gb2312 . cn-gb-2312)
-    (x-ctext . ctext))
-  "A mapping from invalid charset names to the real charset names.")
-
 (defun mm-coding-system-p (sym)
   "Return non-nil if SYM is a coding system."
   (or (and (fboundp 'coding-system-p) (coding-system-p sym))
       (memq sym (mm-get-coding-system-list))))
 
+(defvar mm-charset-synonym-alist
+  `((big5 . cn-big5)
+    (gb2312 . cn-gb-2312)
+    (cn-gb . cn-gb-2312)
+    ;; 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))
+  "A mapping from invalid charset names to the real charset names.")
+
 (defvar mm-binary-coding-system
-  (cond 
+  (cond
    ((mm-coding-system-p 'binary) 'binary)
    ((mm-coding-system-p 'no-conversion) 'no-conversion)
    (t nil))
   "Text coding system for write.")
 
 (defvar mm-auto-save-coding-system
-  (cond 
+  (cond
    ((mm-coding-system-p 'emacs-mule)
     (if (memq system-type '(windows-nt ms-dos ms-windows))
-       (if (mm-coding-system-p 'emacs-mule-dos) 
+       (if (mm-coding-system-p 'emacs-mule-dos)
            'emacs-mule-dos mm-binary-coding-system)
       'emacs-mule))
    ((mm-coding-system-p 'escape-quoted) 'escape-quoted)
    (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.")
+
 ;;; Internal variables:
 
 ;;; Functions:
 
 (defun mm-mule-charset-to-mime-charset (charset)
-  "Return the MIME charset corresponding to MULE CHARSET."
+  "Return the MIME charset corresponding to the given Mule CHARSET."
   (let ((alist mm-mime-mule-charset-alist)
        out)
     (while alist
@@ -193,38 +234,53 @@ used as the line break code type of the coding system."
    ;; ascii
    ((eq charset 'us-ascii)
     'ascii)
-   ;; Check to see whether we can handle this charset.
+   ;; 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))
     charset)
    ;; Nope.
    (t
     nil)))
 
-(if (fboundp 'subst-char-in-string)
-    (defsubst mm-replace-chars-in-string (string from to)
-      (subst-char-in-string from to string))
-  (defun mm-replace-chars-in-string (string from to)
-    "Replace characters in STRING from FROM to TO."
-    (let ((string (substring string 0))        ;Copy string.
-         (len (length string))
-         (idx 0))
-      ;; Replace all occurrences of FROM with TO.
-      (while (< idx len)
-       (when (= (aref string idx) from)
-         (aset string idx to))
-       (setq idx (1+ idx)))
-      string)))
+(defsubst mm-replace-chars-in-string (string from to)
+  (mm-subst-char-in-string from to string))
 
 (defsubst mm-enable-multibyte ()
-  "Enable multibyte in the current buffer."
-  (when (and (fboundp 'set-buffer-multibyte)
-             (boundp 'enable-multibyte-characters)
-            (default-value 'enable-multibyte-characters))
+  "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 ()
-  "Disable multibyte in the current buffer."
-  (when (fboundp 'set-buffer-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)))
+
+(defsubst 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.
+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)))
 
 (defun mm-preferred-coding-system (charset)
@@ -253,20 +309,23 @@ If the charset is `composition', return the actual one."
           (progn
             (setq mail-parse-mule-charset
                   (and (boundp 'current-language-environment)
-                     (car (last 
-                           (assq 'charset 
-                                 (assoc current-language-environment 
-                                        language-info-alist))))))
+                       (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)))))))
 
 (defun mm-mime-charset (charset)
   "Return the MIME charset corresponding to the 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
@@ -299,87 +358,90 @@ If the charset is `composition', return the actual one."
     (setq charsets (mm-delete-duplicates charsets))
     (if (and (> (length charsets) 1)
             (fboundp 'find-coding-systems-region)
-            (memq 'utf-8 (find-coding-systems-region b e)))
+            (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 (boundp 'enable-multibyte-characters)
+  (if (and (not (featurep 'xemacs))
+          (boundp 'enable-multibyte-characters))
       enable-multibyte-characters
     (featurep 'mule)))
 
 (defmacro mm-with-unibyte-buffer (&rest forms)
   "Create a temporary buffer, and evaluate FORMS there like `progn'.
-See also `with-temp-file' and `with-output-to-string'."
-  (let ((temp-buffer (make-symbol "temp-buffer"))
-       (multibyte (make-symbol "multibyte")))
-    `(if (or (featurep 'xemacs)
-            (not (boundp 'enable-multibyte-characters)))
-        (with-temp-buffer ,@forms)
-       (let ((,multibyte (default-value 'enable-multibyte-characters))
-            ,temp-buffer)
-        (unwind-protect
-            (progn
-              (setq-default enable-multibyte-characters nil)
-              (setq ,temp-buffer
-                    (get-buffer-create (generate-new-buffer-name " *temp*")))
-              (unwind-protect
-                  (with-current-buffer ,temp-buffer
-                    (let ((buffer-file-coding-system mm-binary-coding-system)
-                          (coding-system-for-read mm-binary-coding-system)
-                          (coding-system-for-write mm-binary-coding-system))
-                      ,@forms))
-                (and (buffer-name ,temp-buffer)
-                     (kill-buffer ,temp-buffer))))
-          (setq-default enable-multibyte-characters ,multibyte))))))
+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 there like `progn' in current buffer."
-  (let ((multibyte (make-symbol "multibyte")))
-    `(if (or (featurep 'xemacs)
-            (not (fboundp 'set-buffer-multibyte)))
-        (progn
-          ,@forms)
-       (let ((,multibyte (default-value 'enable-multibyte-characters)))
-        (unwind-protect
-            (let ((buffer-file-coding-system mm-binary-coding-system)
-                  (coding-system-for-read mm-binary-coding-system)
-                  (coding-system-for-write mm-binary-coding-system))
-              (set-buffer-multibyte nil)
-              (setq-default enable-multibyte-characters nil)
-              ,@forms)
-          (setq-default enable-multibyte-characters ,multibyte)
-          (set-buffer-multibyte ,multibyte))))))
+  "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)))
+          (unwind-protect
+              (let (default-enable-multibyte-characters)
+                (set-buffer-multibyte nil)
+                ,@forms)
+            (set-buffer ,buffer)
+            (set-buffer-multibyte t)))
+       (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 ((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 (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)
-  "Set default `enable-multibyte-characters' to `nil', eval the FORMS."
-  (let ((multibyte (make-symbol "multibyte")))
-    `(if (or (featurep 'xemacs)
-            (not (boundp 'enable-multibyte-characters)))
-        (progn ,@forms)
-       (let ((,multibyte (default-value 'enable-multibyte-characters)))
-        (unwind-protect
-            (progn
-              (setq-default enable-multibyte-characters nil)
-              ,@forms)
-          (setq-default enable-multibyte-characters ,multibyte))))))
+  "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 charsets in the region."
+  "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.
-    (delq 'composition (find-charset-region b e)))
+    ;; 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))
    (t
-    ;; We are in a unibyte buffer or XEmacs non-mule, so we futz around a bit.
+;; We are in a unibyte buffer or XEmacs non-mule, so we futz around a bit.
     (save-excursion
       (save-restriction
        (narrow-to-region b e)
@@ -390,8 +452,8 @@ See also `with-temp-file' and `with-output-to-string'."
          (let (charset)
            (setq charset
                  (and (boundp 'current-language-environment)
-                      (car (last (assq 'charset 
-                                       (assoc current-language-environment 
+                      (car (last (assq 'charset
+                                       (assoc current-language-environment
                                               language-info-alist))))))
            (if (eq charset 'ascii) (setq charset nil))
            (or charset
@@ -426,7 +488,7 @@ See also `with-temp-file' and `with-output-to-string'."
     (nreverse out)))
 
 (defvar mm-inhibit-file-name-handlers
-  '(jka-compr-handler)
+  '(jka-compr-handler image-file-handler)
   "A list of handlers doing (un)compression (etc) thingies.")
 
 (defun mm-insert-file-contents (filename &optional visit beg end replace
@@ -441,15 +503,15 @@ 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 
+       (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 
+            (append mm-inhibit-file-name-handlers
                     inhibit-file-name-handlers)
           inhibit-file-name-handlers)))
     (insert-file-contents filename visit beg end replace)))
@@ -462,37 +524,47 @@ 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."
-  (let ((coding-system-for-write 
-        (or codesys mm-text-coding-system-for-write 
+  (let ((coding-system-for-write
+        (or codesys mm-text-coding-system-for-write
             mm-text-coding-system))
-       (inhibit-file-name-operation (if inhibit 
+       (inhibit-file-name-operation (if inhibit
                                         'append-to-file
                                       inhibit-file-name-operation))
        (inhibit-file-name-handlers
         (if inhibit
-            (append mm-inhibit-file-name-handlers 
+            (append mm-inhibit-file-name-handlers
                     inhibit-file-name-handlers)
           inhibit-file-name-handlers)))
     (append-to-file start end filename)))
 
-(defun mm-write-region (start end filename &optional append visit lockname 
+(defun mm-write-region (start end filename &optional append visit lockname
                              coding-system inhibit)
 
   "Like `write-region'.
 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 
+  (let ((coding-system-for-write
+        (or coding-system mm-text-coding-system-for-write
             mm-text-coding-system))
-       (inhibit-file-name-operation (if inhibit 
+       (inhibit-file-name-operation (if inhibit
                                         'write-region
                                       inhibit-file-name-operation))
        (inhibit-file-name-handlers
         (if inhibit
-            (append mm-inhibit-file-name-handlers 
+            (append mm-inhibit-file-name-handlers
                     inhibit-file-name-handlers)
           inhibit-file-name-handlers)))
     (write-region start end filename append visit lockname)))
 
+(defun mm-image-load-path (&optional package)
+  (let (dir result)
+    (dolist (path load-path (nreverse result))
+      (if (file-directory-p
+          (setq dir (concat (file-name-directory
+                             (directory-file-name path))
+                            "etc/" (or package "gnus/"))))
+         (push dir result))
+      (push path result))))
+
 (provide 'mm-util)
 
 ;;; mm-util.el ends here