Subject: introduction of MIME tools and small MIME FAQ

From: [email-masked] (守岡 知彦 (MORIOKA Tomohiko))
Date: Sun, 14 Nov 1993 15:48:47 GMT

MIME 対応 tool 紹介 第 0.1 版をお届けします。リストに関する御指摘や
感想を頂けたら幸いです。
┏『健康と美容のために食後に一杯の紅茶』━━━━━━━━━━━━━━┓
┃                    守岡 知彦 (MORIOKA Tomohiko)                  ┃
┃　                                    Email: <[email-masked]>┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

---

From: [email-masked] (enami tsugutomo)
Date: 15 Nov 93 12:55:45 GMT

In article <[email-masked]>,
	[email-masked] (守岡 知彦 (MORIOKA Tomohiko)) writes:

>   mime/decode-message-header 関数は, unfolding(encode された時, 折り
> 返されたものを元に戻す)を指定しないと, 正しく decode しないというバグ?
> があるようです.

	mime って space があったかどうかを保存できないような気がするの
	ですが, どうなんでしょう?

	たとえば, 

		.... 漢字 改行空白 漢字

	が

		.... MIME 改行空白 MIME

	と encode されたのと, 

		.... 漢字漢字

	が長すぎて

		.... MIME 改行空白 MIME

	となったのの区別がつかないんじゃないかということです.  RFC
	1542 には, 単に区切れと書いてあるだけで元からあった空白との区
     	別についての記述は見付けられませんでした(探し方がわるい?).

>   また, encoder も含まれていますが, encode 結果が RFC 1522 に違反する
> ので encoder は使用しない方が良いでしょう.

	前の version でも一応 JIS X0208 については最大 18 文字しか 
	encode しないようになっていましたが, もうすこしまともにしまし
	た.  tspecial と空白以外の, 漢字を含む文字列を encode 後の長さ
	を気にしながら encode させてます.

	それでも encode のほうはあまり信用しないほうがいいでしょう. 
	書いた本人が使ってませんから:).

# vc.el 楽だわ:).
えなみ つぐとも
--- 1.68	1993/10/23 04:59:24
+++ mime-kit.el 1.74	1993/11/15 12:33:29
@@ -26,7 +26,7 @@
 ;;	o us-ascii charset.
 ;; Please send bug report or suggestions to [email-masked]'.
 ;;
-;; $Id: mime-kit.el,v 1.68 1993/10/23 04:59:24 enami Rel $
+;; $Id: mime-kit.el,v 1.74 1993/11/15 12:33:29 enami Rel $
 ;;
 ;; o From [email-masked] (Kei Ichiki),
 ;;	- 2 functions (mime/base64-encode-region,
@@ -35,13 +35,14 @@
 ;;
 
 (defconst mime/version
-  "$Id: mime-kit.el,v 1.68 1993/10/23 04:59:24 enami Rel $"
+  "$Id: mime-kit.el,v 1.74 1993/11/15 12:33:29 enami Rel $"
   "vesion of mime-kit.")
 (defun mime/version () (interactive) (message mime/version))
 
 ;;(defconst mime/tspecials "()<>@,;:\\\"/[]?.=")
 (defconst mime/tspecials "][()<>@,;:\\\"/?.=")
 (defconst mime/tspecials-regexp "[][()<>@,;:\\\"/?.=]")
+
 (defconst mime/charset-regexp "[A-Za-z0-9!#$%&'*+---^_`{}|~]")
 (defconst mime/base64-charset (concat "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
 				      "abcdefghijklmnopqrstuvwxyz"
@@ -109,6 +110,9 @@
 					   "\\)"
 					   (regexp-quote
 					    mime/encoded-word-end)))
+(defconst mime/max-word-length 75
+  "mime encoded word should not exceeds this.")
+
 
 ;;
 ;; utility functions
@@ -356,13 +360,11 @@
 	 (save-excursion (re-search-forward "\\cj" nil t)))
        (defun mime/iso8859-*-region-p ()
 	 (save-excursion (re-search-forward "\\cl" nil t)))
-       (defconst mime/encode-word-regexp-iso2022-jp
-	 ;; max 18 chars.  should include ASCII?
-	 (concat "\\(\\cj\\cj\\cj\\cj\\cj\\cj\\cj\\cj\\)?"
-		 "\\(\\cj\\cj\\cj\\cj\\)?"
-		 "\\(\\cj\\cj\\)?"
-		 "\\cj?\\cj?\\cj?\\cj"))
-       (defconst mime/encode-word-regexp-iso8859-* "\\S *\\cl+\\S *")
+       (defun mime/word-regexp (base)
+	 (concat "[^" mime/tspecials " \t" "]*" base 
+		 "[^" mime/tspecials " \t" "]*"))
+       (defconst mime/encode-word-regexp-iso2022-jp (mime/word-regexp"\\cj+"))
+       (defconst mime/encode-word-regexp-iso8859-* (mime/word-regexp "\\cl+"))
        (defconst mime/encode-word-regexp
 	 (concat "\\(" mime/encode-word-regexp-iso2022-jp
 		 "\\|" mime/encode-word-regexp-iso8859-* "\\)"))
@@ -381,6 +383,14 @@
 	 (let ((name (car (rassq lc mime/iso8859-charset))))
 	   (or name
 	       (error "invalid ISO 8859 leading char: 0x%02x" lc))))
+       (defun mime/encode-length-1 (prev char)
+	 (let ((plc (char-leading-char (if prev prev lc-ascii)))
+	       (lc (if char (char-leading-char char))))
+	   (+ (if (eq plc lc) 0 3)	; designation
+	      (cond ((null lc) 0)
+		    ((= lc lc-jp) 2)
+		    ((= lc lc-ascii) 1)
+		    (t (error "unknown leading-char: %c" lc))))))
        )
       ((and (boundp 'NEMACS) NEMACS)
        (defun mime/convert-string-iso2022-to-emacs (string)
@@ -394,28 +404,43 @@
        (defun mime/iso8859-*-region-p () nil)
        (defun mime/convert-string-iso8859-*-to-emacs (charset string)
 	 (mime/quoted-encode-string string))
-       (defconst mime/encode-word-regexp-iso2022-jp "[\200-\377]+")
-       (defconst mime/encode-word-regexp
-	 mime/encode-word-regexp-iso2022-jp)
+       (defconst mime/encode-word-regexp-iso2022-jp "\\S *[\200-\377]+\\S *")
+       (defconst mime/encode-word-regexp mime/encode-word-regexp-iso2022-jp)
        (defun mime/quoted-encode-char (char)
 	 "Encode the CHAR into string with Quoted Printable method."
 	 (upcase (cond ((= char ?\040) "_")
 		       (t (format "=%02x" char)))))
+       (defun mime/encode-length-1 (prev char)
+	 (let ((plc (if prev 128 0))
+	       ;; 128 or nil
+	       (lc (if char 128)))
+	   (+ (if (eq plc lc) 0 3)	; designation
+	      (cond ((null lc) 0)
+		    ((= lc 128) 2)
+		    ((= lc 0) 1)
+		    (t (error "unknown leading-char: %c" lc))))))
        ))
 
-(defconst mime/iso2022-encode-max 18)
-(defun mime/iso2022-encode-max-chars (col)
-  "Maximum number of chars to fit COL columns."
-  (+ (/ (- (* (/ (- col 18) 4) 3) 8) 2) 1) )
+;; (mime/base64-encode-size 18)
+(defun mime/base64-encode-size (offset)
+  (* (/ (- mime/max-word-length offset) 4) 3))
+
+(defun mime/forward-maximum-encodable (max)
+  (catch 'break
+    (let ((prev nil)
+	  (char nil)
+	  (total 0))
+      (while (not (eobp))
+	(setq char (following-char))
+	(setq total (+ total (mime/encode-length-1 prev char)))
+	(if (> (+ total (mime/encode-length-1 char nil)) max)
+	    (throw 'break t))
+	(setq prev char)
+	(forward-char)))))
 
-(defun mime/encoded-size (begin end)
-  (interactive "r")
-  (mime/narrow-to-region begin end nil
-    (cond ((mime/iso2022-jp-region-p)
-	   (+ (length "=?ISO-2022-JP?B??=")
-	      (* (/ (+ (length (mime/convert-string-emacs-to-iso2022
-				(buffer-substring begin end))) 2) 3) 4)))
-	  (t 0))))
+(defun mime/preceeding-non-space ()
+  (save-excursion
+    (- (point) (progn (skip-chars-backward "^ \t") (point)))))
 
 ;; (mime/decode-encoded-word "=?ISO-8859-1?Q?*GAR=C7ONS*?=")
 ;; (mime/decode-encoded-word "=?ISO-2022-JP?B?GyRAOjRGI0stGyhK?=")
@@ -457,16 +482,18 @@
   (let ((decoded nil)			; decode result
 	(last nil)			; last decoded point
 	(result t))
-    (mime/narrow-to-region begin end t
+    (mime/narrow-to-region begin end nil
       (while (re-search-forward mime/encoded-word-regexp nil t)
 	(setq begin (match-beginning 0)
-	      end (match-end 0)
+	      ;; end (match-end 0)
 	      decoded (condition-case error
 			  (mime/decode-encoded-word (mime/nth-buffer-string 0))
 			(error (if (not mime/decode-quiet)
 				   (signal (car error) (cdr error)) ))))
 	(if (not decoded)
 	    (setq result nil)
+	  ;; delete after insert.
+	  (save-excursion (insert decoded))
 	  (delete-region (if (and concatenate
 				  last
 				  ;; if there is only white space.
@@ -476,9 +503,8 @@
 					(= begin (match-end 0)) )))
 			     last
 			   begin)
-			 end)
-	  (insert decoded)
-	  (setq last (point)) ))
+			 (point))
+	  (setq last (+ (length decoded) (point))) ))
       result)))
 
 ;; (mime/decode-string "ABC =?ISO-8859-1?Q?*GAR=C7ONS*?= DEF")
@@ -503,7 +529,7 @@
 		      (forward-line)
 		      (if (re-search-forward "^\\S " nil t)
 			  (match-beginning 0)
-			(point)))))
+			(point-max)))))
 
 (defun mime/decode-field (&optional quiet filling)
   "decode and refill a field at point."
@@ -536,20 +562,31 @@
   "Encoder to encode iso2022-jp word")
 
 ;;; functions contributed from K.Ichiki 18-Aug-1992
-(defun mime/encode-word-iso2022-jp (begin end)
+(defun mime/encode-word-iso2022-jp (begin end &optional offset)
   "Encode text between point and mark by base64 method."
   (interactive "*r")
-  (if (= begin end)
-      nil
-    (let* ((encoder mime/word-iso2022-jp-encoder)
-	   (mnemonic (get encoder 'mime/encoder-mnemonic))
-	   (string (funcall encoder
-			    (mime/convert-string-emacs-to-iso2022 
-			     (buffer-substring begin end) ))))
-      (delete-region begin end)
-      (insert mime/encoded-word-begin
-	      "ISO-2022-JP" (format "?%c?" (upcase mnemonic)) string
-	      mime/encoded-word-end))))
+  (let* ((encoder mime/word-iso2022-jp-encoder)
+	 (mnemonic (get encoder 'mime/encoder-mnemonic))
+	 (string nil)
+	 ;; 18 == (length "=?iso-2022-jp?b??=")
+	 (max (max (mime/base64-encode-size (+ 18 (or offset 0))))))
+    (if (>= 0 (mime/base64-encode-size 18)) (setq mime/max-word-length 75))
+    (mime/narrow-to-region begin end t
+      (goto-char (point-min))
+      (while (not (eobp))
+	(mime/forward-maximum-encodable (- max (mime/preceeding-non-space)))
+	(if (= (point) (point-min))
+	    nil
+	  (setq string (funcall encoder
+				(mime/convert-string-emacs-to-iso2022 
+				 (buffer-substring (point-min) (point)))))
+	  (delete-region (point-min) (point))
+	  (insert mime/encoded-word-begin
+		  "ISO-2022-JP" (format "?%c?" (upcase mnemonic)) string
+		  mime/encoded-word-end)
+	  (or (eobp) (insert ?\040)))
+	(setq max (mime/base64-encode-size 18))
+	(narrow-to-region (point) (point-max)) ))))
 
 ;; (mime/iso8859-leader lc-ascii)
 ;; (mime/iso8859-leader lc-ltn1)
@@ -559,7 +596,7 @@
 
 (defun mime/iso8859-insert-leader (begin end leading-char)
   (interactive "r\nn")
-  (mime/narrow-to-region begin end t
+  (mime/narrow-to-region begin end nil
     (mime/insert-at (point-min) (mime/iso8859-leader leading-char))
     (mime/insert-at (point-max) mime/encoded-word-end)))
 
@@ -567,15 +604,16 @@
   "Encoder to encode iso8859-* word (now fix to Q)")
 ;; (mime/modify-string "*GAR\201\307ONS*" (mime/encode-buffer-iso8859-*))
 ;; (mime/modify-string "abcdef" (mime/encode-buffer-iso8859-*))
-(defun mime/encode-word-iso8859-* (begin end)
+(defun mime/encode-word-iso8859-* (begin end &optional offset)
   "Encode the word includes iso-8859-* character with Qouted Printable
 Encoding.  The word is indicated by region from BEGIN to END.  So white
 space shouldn't be included."
   (interactive "*r")
-  (mime/narrow-to-region begin end t
+  (mime/narrow-to-region begin end nil
     (let ((leading-char nil)
 	  (char nil)
-	  (max (- 75 (length "=?ISO-8859-*?Q??=")))
+	  (max (- mime/max-word-length
+		  (length "=?ISO-8859-*?Q??=") (or offset 0)))
 	  (regexp (concat "\\(\\cl\\|" mime/tspecials-regexp "\\)")))
       ;; \cl is category latin.
       (while (re-search-forward regexp nil t)
@@ -586,26 +624,31 @@
 	      (goto-char (+ max (point-min)))
 	      (mime/iso8859-insert-leader (point-min) (point) leading-char)
 	      (insert ?\040)
-	      (narrow-to-region (point) (point-max)))
+	      (narrow-to-region (point) (point-max))
+	      (setq max (- mime/max-word-length (length "=?ISO-8859-*?Q??="))))
 	  (setq char (following-char))
 	  (if (or (null leading-char)
 		  (= leading-char (char-leading-char char)))
 	      nil
 	    (mime/iso8859-insert-leader (point-min) (point) leading-char)
 	    (insert ?\040)
-	    (narrow-to-region (point) (point-max)))
+	    (narrow-to-region (point) (point-max))
+	    (setq max (- mime/max-word-length (length "=?ISO-8859-*?Q??="))))
 	  (setq leading-char (char-leading-char char))
-	  (delete-char 1)
-	  (insert (mime/quoted-encode-char char)) ))
+	  ;; delete after insert.
+	  (insert (mime/quoted-encode-char char))
+	  (delete-char 1) ))
       (if leading-char
 	  (mime/iso8859-insert-leader (point-min) (point-max) leading-char)))))
 
 (defun mime/encode-buffer-iso8859-* ()
   (mime/encode-word-iso8859-* (point-min) (point-max)) )
 
-(defun mime/encode-word (begin end)
+(defun mime/encode-word (begin end &optional offset)
   (interactive "*r")
-  (mime/narrow-to-region begin end t
+  (mime/narrow-to-region begin end nil
+    ;;(goto-char (point-max)) (insert ?\040)
+    ;;(goto-char (point-min)) (insert ?\040)
     (funcall (cond ((mime/iso2022-jp-region-p)
 		    (function mime/encode-word-iso2022-jp))
 		   ((mime/iso8859-*-region-p)
@@ -613,33 +656,38 @@
 		   (t
 		    ;; will encode as US ASCII
 		    (function mime/encode-word-iso8859-*)))
-	     (point-min) (point-max))))
+	     (point-min) (point-max) offset)))
 
 ;;;###autoload
-(defun mime/encode-region (begin end)
-  "Encode the region from BEGIN to END by Base64 or Quoted Printable method."
+(defun mime/encode-region (begin end &optional offset)
+  "Encode the region from BEGIN to END by Base64 or Quoted Printable method.
+If optional third argument OFFSET are non nil, consider first column position."
   (interactive "r")
-  (mime/narrow-to-region begin end t
+  (mime/narrow-to-region begin end nil
     (while (re-search-forward mime/encode-word-regexp nil t)
-      (setq begin (match-beginning 0) end (match-end 0))
-      (if (looking-at mime/encode-word-regexp)
-	  (insert ?\040))
-      (mime/encode-word begin end))))
+      (mime/encode-word (match-beginning 0) (match-end 0)
+			(if offset
+			    (save-excursion
+			      (save-restriction 
+				(widen)
+				(goto-char begin)
+				(current-column))))))))
 
 ;; (mime/encode-string "ABC *GAR\201\307ONS* DEF")
 ;;;###autoload
-(defun mime/encode-string (string)
+(defun mime/encode-string (string &optional offset)
   "Encode the STRING by Base64 or Quoted Printable method."
   (mime/modify-string string
-    (mime/encode-region (point-min) (point-max)) ))
+    (mime/encode-region (point-min) (point-max) offset) ))
 
 (defun mime/encode-field ()
   "encode and refill a field at point."
   (let ((at (point)))
     (mime/narrow-to-region (point-min) (point-max) nil
       (mime/narrow-to-field at)
-      (mime/encode-region (point-min) (point-max))
-      (let ((fill-prefix " "))
+      (mime/encode-region (point-min) (point-max) t)
+      (let ((fill-prefix " ")
+	    (fill-column (1+ mime/max-word-length)))
 	(fill-region-as-paragraph (point-min) (point-max)) ))))
 
 ;;;###autoload

---

From: [email-masked] (守岡 知彦 (MORIOKA Tomohiko))
Date: Mon, 15 Nov 1993 18:36:24 GMT

>>>>> <[email-masked]> にて、
	[email-masked] (enami tsugutomo) 氏曰く、

えなみ> 	mime って space があったかどうかを保存できないような気がするの
えなみ> 	ですが, どうなんでしょう?

えなみ> 	たとえば, 

えなみ> 		.... 漢字 改行空白 漢字

えなみ> 	が

えなみ> 		.... MIME 改行空白 MIME

えなみ> 	と encode されたのと, 

えなみ> 		.... 漢字漢字

えなみ> 	が長すぎて

えなみ> 		.... MIME 改行空白 MIME

えなみ> 	となったのの区別がつかないんじゃないかということです.  RFC
えなみ> 	1542 には, 単に区切れと書いてあるだけで元からあった空白との区
えなみ>      	別についての記述は見付けられませんでした(探し方がわるい?).

  その通りだと思います。一般に、RFC 822 では、空白と折り返しの区別はつ
かないし、個数だって判らないわけですが、encoded-word と encoded-word 
の間の空白は decode 時に除去されるので、ちょっと問題です。だから、
tiny-mime.el では、漢字と漢字の間に linear-white-space が入る場合は、
それらをくっつけて、一つの encoded-word として出力するようにしています。

---

From: [email-masked] (Wataru Hirayama)
Date: Mon, 15 Nov 1993 10:31:58 GMT

平山＠ＮＥＣ技術情報システム開発です。

＃守岡さんご苦労さまです。

私の元記事関連のところですが、

In article <[email-masked]>
[email-masked] writes:

>Q: MH-6.8.1-JP.2c をインストールし、mh-e に tiny-mime パッチを当てて使
>   うと、日本語のメールそのものが化けてしまいます。

これは上記のような特殊な状況ではなくても、Nemacs(Mule)でmhlの
define-program-kanji-codeを設定していない場合に、mhlに日本語を通そうと
するだけで発生すると思われます。

＃私はtiny-mimeは普段は使っていません。でも化けました:-)。

いかがでしょうか。

|   お酒飲む人       | ＮＥＣ技術情報システム開発(株)
|       花なら桜     | 平　山　　度 (ひらやま  わたる)
|   今日もさけさけ   | E-Mail:[email-masked]
|       明日もさけ   |        [email-masked]