Update FSF's address in GPL notices.
[elisp/flim.git] / eword-encode.el
index 1bca5cf..5b62199 100644 (file)
@@ -1,8 +1,8 @@
 ;;; eword-encode.el --- RFC 2047 based encoded-word encoder for GNU Emacs
 
-;; Copyright (C) 1995,1996,1997,1998,1999 Free Software Foundation, Inc.
+;; Copyright (C) 1995,1996,1997,1998,1999,2000 Free Software Foundation, Inc.
 
-;; Author: MORIOKA Tomohiko <morioka@jaist.ac.jp>
+;; Author: MORIOKA Tomohiko <tomo@m17n.org>
 ;; Keywords: encoded-word, MIME, multilingual, header, mail, news
 
 ;; This file is part of FLIM (Faithful Library about Internet Message).
 
 ;; 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.
 
 ;;; Code:
 
-(require 'poem)
+(require 'mime-def)
 (require 'mel)
 (require 'std11)
-(require 'mime-def)
 (require 'eword-decode)
 
 
 ;;; @ variables
 ;;;
 
-(defgroup eword-encode nil
-  "Encoded-word encoding"
-  :group 'mime)
-
-(defcustom eword-field-encoding-method-alist
-  '(("X-Nsubject" . iso-2022-jp-2)
-    ("Newsgroups" . nil)
-    ("Message-ID" . nil)
-    (t            . mime)
-    )
-  "*Alist to specify field encoding method.
-Its key is field-name, value is encoding method.
-
-If method is `mime', this field will be encoded into MIME format.
-
-If method is a MIME-charset, this field will be encoded as the charset
-when it must be convert into network-code.
-
-If method is `default-mime-charset', this field will be encoded as
-variable `default-mime-charset' when it must be convert into
-network-code.
-
-If method is nil, this field will not be encoded."
-  :group 'eword-encode
-  :type '(repeat (cons (choice :tag "Field"
-                              (string :tag "Name")
-                              (const :tag "Default" t))
-                      (choice :tag "Method"
-                              (const :tag "MIME conversion" mime)
-                              (symbol :tag "non-MIME conversion")
-                              (const :tag "no-conversion" nil)))))
-
-(defvar eword-charset-encoding-alist
+;; User options are defined in mime-def.el.
+
+(defvar mime-header-charset-encoding-alist
   '((us-ascii          . nil)
     (iso-8859-1                . "Q")
     (iso-8859-2                . "Q")
@@ -77,7 +46,10 @@ If method is nil, this field will not be encoded."
     (iso-8859-7                . "Q")
     (iso-8859-8                . "Q")
     (iso-8859-9                . "Q")
+    (iso-8859-14       . "Q")
+    (iso-8859-15       . "Q")
     (iso-2022-jp       . "B")
+    (iso-2022-jp-3     . "B")
     (iso-2022-kr       . "B")
     (gb2312            . "B")
     (cn-gb             . "B")
@@ -89,6 +61,19 @@ If method is nil, this field will not be encoded."
     (utf-8             . "B")
     ))
 
+(defvar mime-header-default-charset-encoding "Q")
+
+(defvar mime-header-encode-method-alist
+  '((eword-encode-address-list
+     . (Reply-To
+       From Sender
+       Resent-Reply-To Resent-From
+       Resent-Sender To Resent-To
+       Cc Resent-Cc Bcc Resent-Bcc
+       Dcc))
+    (eword-encode-in-reply-to . (In-Reply-To))
+    (eword-encode-structured-field-body . (Mime-Version User-Agent))
+    (eword-encode-unstructured-field-body)))
 
 ;;; @ encoded-text encoder
 ;;;
@@ -99,7 +84,7 @@ CHARSET is a symbol to indicate MIME charset of the encoded-word.
 ENCODING allows \"B\" or \"Q\".
 MODE is allows `text', `comment', `phrase' or nil.  Default value is
 `phrase'."
-  (let ((text (encoded-text-encode-string string encoding)))
+  (let ((text (encoded-text-encode-string string encoding mode)))
     (if text
        (concat "=?" (upcase (symbol-name charset)) "?"
                encoding "?" text "?=")
@@ -119,21 +104,23 @@ MODE is allows `text', `comment', `phrase' or nil.  Default value is
   (let ((len (length string))
        dest)
     (while (> len 0)
-      (let* ((chr (sref string 0))
+      (let* ((chr (aref string 0))
+             ;; (chr (sref string 0))
             (charset (eword-encode-char-type chr))
-            (i (char-length chr)))
+             (i 1)
+            ;; (i (char-length chr))
+            )
        (while (and (< i len)
-                   (setq chr (sref string i))
-                   (eq charset (eword-encode-char-type chr))
-                   )
-         (setq i (char-next-index chr i))
+                   (setq chr (aref string i))
+                    ;; (setq chr (sref string i))
+                   (eq charset (eword-encode-char-type chr)))
+         (setq i (1+ i))
+          ;; (setq i (char-next-index chr i))
          )
        (setq dest (cons (cons charset (substring string 0 i)) dest)
              string (substring string i)
-             len (- len i)
-             )))
-    (nreverse dest)
-    ))
+             len (- len i))))
+    (nreverse dest)))
 
 
 ;;; @ word
@@ -184,30 +171,52 @@ MODE is allows `text', `comment', `phrase' or nil.  Default value is
 (defmacro ew-rword-type (rword)
   (` (car (cdr (cdr (cdr (, rword)))))))
 
-(defun tm-eword::find-charset-rule (charsets)
+(defun ew-find-charset-rule (charsets)
   (if charsets
-      (let* ((charset (charsets-to-mime-charset charsets))
-            (encoding (cdr (assq charset eword-charset-encoding-alist)))
-            )
-       (list charset encoding)
-       )))
+      (let* ((charset (find-mime-charset-by-charsets charsets))
+            (encoding
+             (cdr (or (assq charset mime-header-charset-encoding-alist)
+                      (cons charset mime-header-default-charset-encoding)))))
+       (list charset encoding))))
+
+;; [tomo:2002-11-05] The following code is a quick-fix for emacsen
+;; which is not depended on the Mule model.  We should redesign
+;; `eword-encode-split-string' to avoid to depend on the Mule model.
+(if (featurep 'utf-2000)
+;; for CHISE Architecture
+(defun tm-eword::words-to-ruled-words (wl &optional mode)
+  (let (mcs)
+    (mapcar (function
+            (lambda (word)
+              (setq mcs (detect-mime-charset-string (cdr word)))
+              (make-ew-rword
+               (cdr word)
+               mcs
+               (cdr (or (assq mcs mime-header-charset-encoding-alist)
+                        (cons mcs mime-header-default-charset-encoding)))
+               mode)
+              ))
+           wl)))
 
+;; for legacy Mule
 (defun tm-eword::words-to-ruled-words (wl &optional mode)
   (mapcar (function
           (lambda (word)
-            (let ((ret (tm-eword::find-charset-rule (car word))))
+            (let ((ret (ew-find-charset-rule (car word))))
               (make-ew-rword (cdr word) (car ret)(nth 1 ret) mode)
               )))
          wl))
+)
 
-(defun tm-eword::space-process (seq)
+(defun ew-space-process (seq)
   (let (prev a ac b c cc)
     (while seq
       (setq b (car seq))
       (setq seq (cdr seq))
       (setq c (car seq))
       (setq cc (ew-rword-charset c))
-      (if (null (ew-rword-charset b))
+      (if (and (null (ew-rword-charset b))
+              (not (eq (ew-rword-type b) 'special)))
          (progn
            (setq a (car prev))
            (setq ac (ew-rword-charset a))
@@ -236,7 +245,7 @@ MODE is allows `text', `comment', `phrase' or nil.  Default value is
     ))
 
 (defun eword-encode-split-string (str &optional mode)
-  (tm-eword::space-process
+  (ew-space-process
    (tm-eword::words-to-ruled-words
     (eword-encode-charset-words-to-words
      (eword-encode-divide-into-charset-words str))
@@ -268,117 +277,118 @@ MODE is allows `text', `comment', `phrase' or nil.  Default value is
 ;;; @ encode-string
 ;;;
 
-(defun tm-eword::encode-string-1 (column rwl)
-  (let* ((rword (car rwl))
-        (ret (tm-eword::encoded-word-length rword))
-        string len)
-    (if (null ret)
-       (cond ((and (setq string (car rword))
-                   (or (<= (setq len (+ (length string) column)) 76)
-                       (<= column 1))
+(defun ew-encode-rword-1 (column rwl &optional must-output)
+  (catch 'can-not-output
+    (let* ((rword (car rwl))
+          (ret (tm-eword::encoded-word-length rword))
+          string len)
+      (if (null ret)
+         (cond ((and (setq string (car rword))
+                     (or (<= (setq len (+ (length string) column)) 76)
+                         (<= column 1))
+                     )
+                (setq rwl (cdr rwl))
+                )
+               ((memq (aref string 0) '(?  ?\t))
+                (setq string (concat "\n" string)
+                      len (length string)
+                      rwl (cdr rwl))
+                )
+               (must-output
+                (setq string "\n "
+                      len 1)
+                )
+               (t
+                (throw 'can-not-output nil)
+                ))
+       (cond ((and (setq len (car ret))
+                   (<= (+ column len) 76)
                    )
+              (setq string
+                    (eword-encode-text
+                     (ew-rword-charset rword)
+                     (ew-rword-encoding rword)
+                     (cdr ret)
+                     (ew-rword-type rword)
+                     ))
+              (setq len (+ (length string) column))
               (setq rwl (cdr rwl))
               )
              (t
-              (setq string "\n ")
-              (setq len 1)
-              ))
-      (cond ((and (setq len (car ret))
-                 (<= (+ column len) 76)
-                 )
-            (setq string
-                  (eword-encode-text
-                   (ew-rword-charset rword)
-                   (ew-rword-encoding rword)
-                   (cdr ret)
-                   (ew-rword-type rword)
-                   ))
-            (setq len (+ (length string) column))
-            (setq rwl (cdr rwl))
-            )
-           (t
-            (setq string (car rword))
-            (let* ((p 0) np
-                   (str "") nstr)
-              (while (and (< p len)
-                          (progn
-                            (setq np (char-next-index (sref string p) p))
-                            (setq nstr (substring string 0 np))
-                            (setq ret (tm-eword::encoded-word-length
-                                       (cons nstr (cdr rword))
-                                       ))
-                            (setq nstr (cdr ret))
-                            (setq len (+ (car ret) column))
-                            (<= len 76)
-                            ))
-                (setq str nstr
-                      p np))
-              (if (string-equal str "")
-                  (setq string "\n "
-                        len 1)
-                (setq rwl (cons (cons (substring string p) (cdr rword))
-                                (cdr rwl)))
-                (setq string
-                      (eword-encode-text
-                       (ew-rword-charset rword)
-                       (ew-rword-encoding rword)
-                       str
-                       (ew-rword-type rword)))
-                (setq len (+ (length string) column))
-                )
-              )))
-      )
-    (list string len rwl)
-    ))
+              (setq string (car rword))
+              (let* ((p 0) np
+                     (str "") nstr)
+                (while (and (< p len)
+                            (progn
+                              (setq np (1+ p))
+                              ;;(setq np (char-next-index (sref string p) p))
+                              (setq nstr (substring string 0 np))
+                              (setq ret (tm-eword::encoded-word-length
+                                         (cons nstr (cdr rword))
+                                         ))
+                              (setq nstr (cdr ret))
+                              (setq len (+ (car ret) column))
+                              (<= len 76)
+                              ))
+                  (setq str nstr
+                        p np))
+                (if (string-equal str "")
+                    (if must-output
+                        (setq string "\n "
+                              len 1)
+                      (throw 'can-not-output nil))
+                  (setq rwl (cons (cons (substring string p) (cdr rword))
+                                  (cdr rwl)))
+                  (setq string
+                        (eword-encode-text
+                         (ew-rword-charset rword)
+                         (ew-rword-encoding rword)
+                         str
+                         (ew-rword-type rword)))
+                  (setq len (+ (length string) column))
+                  )
+                )))
+       )
+      (list string len rwl)
+      )))
 
 (defun eword-encode-rword-list (column rwl)
-  (let (ret dest ps special str ew-f pew-f bew)
+  (let (ret dest str ew-f pew-f folded-points)
     (while rwl
       (setq ew-f (nth 2 (car rwl)))
       (if (and pew-f ew-f)
          (setq rwl (cons '(" ") rwl)
-               bew t
                pew-f nil)
-       (setq pew-f ew-f
-             bew nil)
+       (setq pew-f ew-f)
        )
-      (setq ret (tm-eword::encode-string-1 column rwl))
+      (if (null (setq ret (ew-encode-rword-1 column rwl)))
+         (let ((i (1- (length dest)))
+               c s r-dest r-column)
+           (catch 'success
+             (while (catch 'found
+                      (while (>= i 0)
+                        (cond ((memq (setq c (aref dest i)) '(?  ?\t))
+                               (if (memq i folded-points)
+                                   (throw 'found nil)
+                                 (setq folded-points (cons i folded-points))
+                                 (throw 'found i))
+                               )
+                              ((eq c ?\n)
+                               (throw 'found nil)
+                               ))
+                        (setq i (1- i))))
+               (setq s (substring dest i)
+                     r-column (length s)
+                     r-dest (concat (substring dest 0 i) "\n" s))
+               (when (setq ret (ew-encode-rword-1 r-column rwl))
+                 (setq dest r-dest
+                       column r-column)
+                 (throw 'success t)
+                 ))
+             (setq ret (ew-encode-rword-1 column rwl 'must-output))
+             )))
       (setq str (car ret))
-      (if (eq (elt str 0) ?\n)
-         (cond
-          ((eq special ?\()
-           (setq dest (concat dest "\n ("))
-           (setq ret (tm-eword::encode-string-1 2 rwl))
-           (setq str (car ret)))
-          ((eq bew t)
-           (setq dest (concat dest "\n "))
-           (setq ret (tm-eword::encode-string-1 1 (cdr rwl)))
-           (setq str (car ret))))
-       (cond ((eq special ? )
-              (if (string= str "(")
-                  (setq ps t)
-                (setq dest (concat dest " "))
-                (setq ps nil)
-                ))
-             ((eq special ?\()
-              (if ps
-                  (progn
-                    (setq dest (concat dest " ("))
-                    (setq ps nil)
-                    )
-                (setq dest (concat dest "("))
-                )
-              )))
-      (cond ((string= str " ")
-            (setq special ? )
-            )
-           ((string= str "(")
-            (setq special ?\()
-            )
-           (t
-            (setq special nil)
-            (setq dest (concat dest str))
-            ))
+      (setq dest (concat dest str))
       (setq column (nth 1 ret)
            rwl (nth 2 ret))
       )
@@ -399,8 +409,8 @@ MODE is allows `text', `comment', `phrase' or nil.  Default value is
             (setq dest
                   (append dest
                           (list
-                           (let ((ret (tm-eword::find-charset-rule
-                                       (find-non-ascii-charset-string str))))
+                           (let ((ret (ew-find-charset-rule
+                                       (find-charset-string str))))
                              (make-ew-rword
                               str (car ret)(nth 1 ret) 'phrase)
                              )
@@ -409,13 +419,13 @@ MODE is allows `text', `comment', `phrase' or nil.  Default value is
            ((eq type 'comment)
             (setq dest
                   (append dest
-                          '(("(" nil nil))
+                          '(("(" nil nil special))
                           (tm-eword::words-to-ruled-words
                            (eword-encode-charset-words-to-words
                             (eword-encode-divide-into-charset-words
                              (cdr token)))
                            'comment)
-                          '((")" nil nil))
+                          '((")" nil nil special))
                           ))
             )
            (t
@@ -429,7 +439,7 @@ MODE is allows `text', `comment', `phrase' or nil.  Default value is
             ))
       (setq phrase (cdr phrase))
       )
-    (tm-eword::space-process dest)
+    (ew-space-process dest)
     ))
 
 (defun eword-encode-addr-seq-to-rword-list (seq)
@@ -463,7 +473,8 @@ MODE is allows `text', `comment', `phrase' or nil.  Default value is
                     (if (or (eq pname 'spaces)
                             (eq pname 'comment))
                         (nconc dest (list (list (cdr token) nil nil)))
-                      (nconc (butlast dest)
+                      (nconc (nreverse (cdr (reverse dest)))
+                             ;; (butlast dest)
                              (list
                               (list (concat (car (car (last dest)))
                                             (cdr token))
@@ -516,17 +527,37 @@ MODE is allows `text', `comment', `phrase' or nil.  Default value is
                      )))
     dest))
 
+(defsubst eword-encode-mailboxes-to-rword-list (mboxes)
+  (let ((dest (eword-encode-mailbox-to-rword-list (car mboxes))))
+    (if dest
+       (while (setq mboxes (cdr mboxes))
+         (setq dest
+               (nconc dest
+                      (list '("," nil nil))
+                      (eword-encode-mailbox-to-rword-list
+                       (car mboxes))))))
+    dest))
+
+(defsubst eword-encode-address-to-rword-list (address)
+  (cond
+   ((eq (car address) 'mailbox)
+    (eword-encode-mailbox-to-rword-list address))
+   ((eq (car address) 'group)
+    (nconc
+     (eword-encode-phrase-to-rword-list (nth 1 address))
+     (list (list ":" nil nil))
+     (eword-encode-mailboxes-to-rword-list (nth 2 address))
+     (list (list ";" nil nil))))))
+
 (defsubst eword-encode-addresses-to-rword-list (addresses)
-  (let ((dest (eword-encode-mailbox-to-rword-list (car addresses))))
+  (let ((dest (eword-encode-address-to-rword-list (car addresses))))
     (if dest
        (while (setq addresses (cdr addresses))
          (setq dest
                (nconc dest
                       (list '("," nil nil))
                       ;; (list '(" " nil nil))
-                      (eword-encode-mailbox-to-rword-list (car addresses))
-                      ))
-         ))
+                      (eword-encode-address-to-rword-list (car addresses))))))
     dest))
 
 (defsubst eword-encode-msg-id-to-rword-list (msg-id)
@@ -554,10 +585,8 @@ MODE is allows `text', `comment', `phrase' or nil.  Default value is
 ;;; @ application interfaces
 ;;;
 
-(defcustom eword-encode-default-start-column 10
-  "Default start column if it is omitted."
-  :group 'eword-encode
-  :type 'integer)
+(defvar eword-encode-default-start-column 10
+  "Default start column if it is omitted.")
 
 (defun eword-encode-string (string &optional column mode)
   "Encode STRING as encoded-words, and return the result.
@@ -600,46 +629,42 @@ Optional argument COLUMN is start-position of the field."
        (or column eword-encode-default-start-column)
        (eword-encode-split-string string 'text))))
 
-(defun eword-encode-field-body (field-body field-name)
+;;;###autoload
+(defun mime-encode-field-body (field-body field-name)
   "Encode FIELD-BODY as FIELD-NAME, and return the result.
 A lexical token includes non-ASCII character is encoded as MIME
 encoded-word.  ASCII token is not encoded."
   (setq field-body (std11-unfold-string field-body))
   (if (string= field-body "")
       ""
-    (let (start)
+    (let ((method-alist mime-header-encode-method-alist)
+         start ret)
       (if (symbolp field-name)
          (setq start (1+ (length (symbol-name field-name))))
        (setq start (1+ (length field-name))
              field-name (intern (capitalize field-name))))
-      (cond ((memq field-name
-                  '(Reply-To
-                    From Sender
-                    Resent-Reply-To Resent-From
-                    Resent-Sender To Resent-To
-                    Cc Resent-Cc Bcc Resent-Bcc
-                    Dcc))
-            (eword-encode-address-list field-body start)
-            )
-           ((eq field-name 'In-Reply-To)
-            (eword-encode-in-reply-to field-body start)
-            )
-           ((memq field-name '(Mime-Version User-Agent))
-            (eword-encode-structured-field-body field-body start)
-            )
-           (t
-            (eword-encode-unstructured-field-body field-body start)
-            ))
-      )))
+      (while (car method-alist)
+       (if (or (not (cdr (car method-alist)))
+               (memq field-name
+                     (cdr (car method-alist))))
+           (progn
+             (setq ret
+                   (apply (caar method-alist) (list field-body start)))
+             (setq method-alist nil)))
+       (setq method-alist (cdr method-alist)))
+      ret)))
+(defalias 'eword-encode-field-body 'mime-encode-field-body)
+(make-obsolete 'eword-encode-field-body 'mime-encode-field-body)
 
 (defun eword-in-subject-p ()
   (let ((str (std11-field-body "Subject")))
     (if (and str (string-match eword-encoded-word-regexp str))
        str)))
+(make-obsolete 'eword-in-subject-p "Don't use it.")
 
 (defsubst eword-find-field-encoding-method (field-name)
   (setq field-name (downcase field-name))
-  (let ((alist eword-field-encoding-method-alist))
+  (let ((alist mime-field-encoding-method-alist))
     (catch 'found
       (while alist
        (let* ((pair (car alist))
@@ -649,13 +674,13 @@ encoded-word.  ASCII token is not encoded."
              (throw 'found (cdr pair))
            ))
        (setq alist (cdr alist)))
-      (cdr (assq t eword-field-encoding-method-alist))
+      (cdr (assq t mime-field-encoding-method-alist))
       )))
 
-(defun eword-encode-header (&optional code-conversion)
+;;;###autoload
+(defun mime-encode-header-in-buffer (&optional code-conversion)
   "Encode header fields to network representation, such as MIME encoded-word.
-
-It refer variable `eword-field-encoding-method-alist'."
+It refers the `mime-field-encoding-method-alist' variable."
   (interactive "*")
   (save-excursion
     (save-restriction
@@ -665,29 +690,31 @@ It refer variable `eword-field-encoding-method-alist'."
            bbeg end field-name)
        (while (re-search-forward std11-field-head-regexp nil t)
          (setq bbeg (match-end 0)
-               field-name (buffer-substring (match-beginning 0) (1- bbeg))
+               field-name (buffer-substring-no-properties (match-beginning 0)
+                                                          (1- bbeg))
                end (std11-field-end))
-         (and (find-non-ascii-charset-region bbeg end)
+         (and (delq 'ascii (find-charset-region bbeg end))
               (let ((method (eword-find-field-encoding-method
                              (downcase field-name))))
                 (cond ((eq method 'mime)
-                       (let ((field-body
-                              (buffer-substring-no-properties bbeg end)
-                              ))
-                         (delete-region bbeg end)
-                         (insert (eword-encode-field-body field-body
-                                                          field-name))
-                         ))
+                       (let* ((field-body
+                               (buffer-substring-no-properties bbeg end))
+                              (encoded-body
+                               (mime-encode-field-body
+                                field-body field-name)))
+                         (if (not encoded-body)
+                             (error "Cannot encode %s:%s"
+                                    field-name field-body)
+                           (delete-region bbeg end)
+                           (insert encoded-body))))
                       (code-conversion
                        (let ((cs
                               (or (mime-charset-to-coding-system
                                    method)
                                   default-cs)))
-                         (encode-coding-region bbeg end cs)
-                         )))
-                ))
-         ))
-      )))
+                         (encode-coding-region bbeg end cs)))))))))))
+(defalias 'eword-encode-header 'mime-encode-header-in-buffer)
+(make-obsolete 'eword-encode-header 'mime-encode-header-in-buffer)
 
 
 ;;; @ end