update.
[elisp/flim.git] / mel-q.el
1 ;;; mel-q.el --- Quoted-Printable encoder/decoder.
2
3 ;; Copyright (C) 1995,96,97,98,99,2000,2001 Free Software Foundation, Inc.
4
5 ;; Author: MORIOKA Tomohiko <tomo@m17n.org>
6 ;; Created: 1995/6/25
7 ;; Keywords: MIME, Quoted-Printable, Q-encoding
8
9 ;; This file is part of FLIM (Faithful Library about Internet Message).
10
11 ;; This program is free software; you can redistribute it and/or
12 ;; modify it under the terms of the GNU General Public License as
13 ;; published by the Free Software Foundation; either version 2, or (at
14 ;; your option) any later version.
15
16 ;; This program is distributed in the hope that it will be useful, but
17 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19 ;; General Public License for more details.
20
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with this program; see the file COPYING.  If not, write to the
23 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24 ;; Boston, MA 02110-1301, USA.
25
26 ;;; Code:
27
28 (require 'mime-def)
29 (require 'path-util)
30 (eval-when-compile
31   ;; XXX: should provide char-list instead of string-to-char-list.
32   ;; XXx: and also the macro `as-binary-process' should be provided
33   ;; XXx: by the module "pces" which will be loaded by way of "poem".
34   (require 'poem))
35
36
37 ;;; @ Quoted-Printable encoder
38 ;;;
39
40 (defsubst quoted-printable-quote-char (character)
41   (concat
42    "="
43    (char-to-string (aref quoted-printable-hex-chars
44                          (ash (logand character 255) -4)))
45    (char-to-string (aref quoted-printable-hex-chars (logand character 15)))))
46
47 (defun quoted-printable-internal-encode-region (start end)
48   (save-excursion
49     (save-restriction
50       (narrow-to-region (goto-char start) end)
51       (let ((col 0)
52             chr)
53         (while (not (eobp))
54           (cond
55            ((>= col 75)                 ; soft line break.
56             (insert "=\n")
57             (setq col 0))
58            ((eolp)                      ; end of line.
59             (forward-char)
60             (setq col 0))
61            (t
62             (setq chr (logand (char-after (point)) 255))
63             (cond
64              ((and (memq chr '(?  ?\t)) ; encode WSP char before CRLF.
65                    (eq (char-after (1+ (point))) ?\n))
66               (forward-char)
67               (insert "=\n")
68               (forward-char)
69               (setq col 0))
70              ((and (bolp)               ; "^From " is not safe.
71                    (eq chr                        ?F)
72                    (eq (char-after (1+  (point))) ?r)
73                    (eq (char-after (+ 2 (point))) ?o)
74                    (eq (char-after (+ 3 (point))) ?m)
75                    (eq (char-after (+ 4 (point))) ? ))
76               (delete-region (point)(1+ (point)))
77               (insert "=46")            ; moved to ?r.
78               (forward-char 4)          ; skip "rom ".
79               (setq col 7))
80              ((or (= chr ?\t)           ; skip safe char.
81                   (and (<= 32 chr)(/= chr ?=)(< chr 127)))
82               (forward-char)
83               (setq col (1+ col)))
84              ((>= col 73)               ; soft line break.
85               (insert "=\n")
86               (setq col 0))
87              (t                         ; encode unsafe char.
88               (delete-region (point)(1+ (point)))
89               ;; (insert (quoted-printable-quote-char chr))
90               (insert
91                ?=
92                (aref quoted-printable-hex-chars (ash chr -4))
93                (aref quoted-printable-hex-chars (logand chr 15)))
94               (setq col (+ col 3)))))))))))
95
96
97 (defvar quoted-printable-external-encoder '("mmencode" "-q")
98   "*list of quoted-printable encoder program name and its arguments.")
99
100 (defun quoted-printable-external-encode-region (start end)
101   (save-excursion
102     (save-restriction
103       (narrow-to-region start end)
104       (as-binary-process
105        (apply (function call-process-region)
106               start end (car quoted-printable-external-encoder)
107               t t nil
108               (cdr quoted-printable-external-encoder)))
109       ;; for OS/2
110       ;;   regularize line break code
111       (goto-char (point-min))
112       (while (re-search-forward "\r$" nil t)
113         (replace-match "")))))
114
115
116 (defvar quoted-printable-internal-encoding-limit
117   (if (and (featurep 'xemacs)(featurep 'mule))
118       0
119     (require 'path-util)
120     (if (exec-installed-p "mmencode")
121         1000
122       ;; XXX: Fix this message, or simply remove it.
123       ;; (message "Don't found external encoder for Quoted-Printable!")
124       nil))
125   "*limit size to use internal quoted-printable encoder.
126 If size of input to encode is larger than this limit,
127 external encoder is called.")
128
129 (defun quoted-printable-encode-region (start end)
130   "Encode current region by quoted-printable.
131 START and END are buffer positions.
132 This function calls internal quoted-printable encoder if size of
133 region is smaller than `quoted-printable-internal-encoding-limit',
134 otherwise it calls external quoted-printable encoder specified by
135 `quoted-printable-external-encoder'.  In this case, you must install
136 the program (maybe mmencode included in metamail or XEmacs package)."
137   (interactive "*r")
138   (if (and quoted-printable-internal-encoding-limit
139            (> (- end start) quoted-printable-internal-encoding-limit))
140       (quoted-printable-external-encode-region start end)
141     (quoted-printable-internal-encode-region start end)))
142
143 (defun quoted-printable-encode-string (string)
144   "Encode STRING to quoted-printable, and return the result."
145   (with-temp-buffer
146     (insert string)
147     (quoted-printable-encode-region (point-min)(point-max))
148     (buffer-string)))
149
150
151 (mel-define-method-function
152  (mime-encode-string string (nil "quoted-printable"))
153  'quoted-printable-encode-string)
154
155 (mel-define-method-function
156  (mime-encode-region start end (nil "quoted-printable"))
157  'quoted-printable-encode-region)
158
159 (mel-define-method mime-insert-encoded-file (filename (nil "quoted-printable"))
160   "Encode contents of file FILENAME to quoted-printable, and insert the result.
161 It calls external quoted-printable encoder specified by
162 `quoted-printable-external-encoder'.  So you must install the program
163 \(maybe mmencode included in metamail or XEmacs package)."
164   (interactive "*fInsert encoded file: ")
165   (apply (function call-process)
166          (car quoted-printable-external-encoder)
167          filename t nil
168          (cdr quoted-printable-external-encoder)))
169
170
171 ;;; @ Quoted-Printable decoder
172 ;;;
173
174 (defsubst quoted-printable-hex-char-to-num (chr)
175   (cond ((<= ?a chr) (+ (- chr ?a) 10))
176         ((<= ?A chr) (+ (- chr ?A) 10))
177         ((<= ?0 chr) (- chr ?0))
178         ))
179
180 (if (eval-when-compile
181       (> (string-to-char (string-as-multibyte "\200")) 128))
182     (defsubst quoted-printable-num-to-raw-byte-char (chr)
183       (if (and chr
184                (> chr 127))
185           (logior chr
186                   (eval-when-compile
187                     (- (string-to-char (string-as-multibyte "\200")) 128)))
188         chr))
189   (defsubst quoted-printable-num-to-raw-byte-char (chr)
190     chr))
191
192 (defun quoted-printable-internal-decode-region (start end)
193   (save-excursion
194     (save-restriction
195       (narrow-to-region start end)
196       (goto-char (point-min))
197       (while (search-forward "=" nil t)
198         (cond
199          ((eolp)
200           ;; unfold soft line break.
201           (delete-region (1- (point))(1+ (point))))
202          ((and (memq (char-after (point))
203                      (eval-when-compile
204                        ;; XXX: should provide char-list instead.
205                        (string-to-char-list quoted-printable-hex-chars)))
206                (memq (char-after (1+ (point)))
207                      (eval-when-compile
208                        ;; XXX: should provide char-list instead.
209                        (string-to-char-list quoted-printable-hex-chars))))
210           ;; encoded char.
211           (insert
212            (prog1
213                (quoted-printable-num-to-raw-byte-char
214                 (logior
215                  (ash (quoted-printable-hex-char-to-num (char-after (point))) 4)
216                  (quoted-printable-hex-char-to-num (char-after (1+ (point))))))
217              (delete-region (1- (point))(+ 2 (point))))))
218          (t
219           ;; invalid encoding.
220           ))))))
221
222 (defvar quoted-printable-external-decoder '("mmencode" "-q" "-u")
223   "*list of quoted-printable decoder program name and its arguments.")
224
225 (defun quoted-printable-external-decode-region (start end)
226   (save-excursion
227     (as-binary-process
228      (apply (function call-process-region)
229             start end (car quoted-printable-external-decoder)
230             t t nil
231             (cdr quoted-printable-external-decoder)))))
232
233
234 (defvar quoted-printable-internal-decoding-limit nil
235   "*limit size to use internal quoted-printable decoder.
236 If size of input to decode is larger than this limit,
237 external decoder is called.")
238
239 (defun quoted-printable-decode-region (start end)
240   "Decode current region by quoted-printable.
241 START and END are buffer positions.
242 This function calls internal quoted-printable decoder if size of
243 region is smaller than `quoted-printable-internal-decoding-limit',
244 otherwise it calls external quoted-printable decoder specified by
245 `quoted-printable-external-decoder'.  In this case, you must install
246 the program (maybe mmencode included in metamail or XEmacs package)."
247   (interactive "*r")
248   (if (and quoted-printable-internal-decoding-limit
249            (> (- end start) quoted-printable-internal-decoding-limit))
250       (quoted-printable-external-decode-region start end)
251     (quoted-printable-internal-decode-region start end)))
252
253 (defun quoted-printable-decode-string (string)
254   "Decode STRING which is encoded in quoted-printable, and return the result."
255   (with-temp-buffer
256     (insert string)
257     (quoted-printable-decode-region (point-min)(point-max))
258     (buffer-string)))
259
260
261 (mel-define-method-function
262  (mime-decode-string string (nil "quoted-printable"))
263  'quoted-printable-decode-string)
264
265 (mel-define-method-function
266  (mime-decode-region start end (nil "quoted-printable"))
267  'quoted-printable-decode-region)
268
269
270 (defvar quoted-printable-external-decoder-option-to-specify-file '("-o")
271   "*list of options of quoted-printable decoder program to specify file.
272 If the quoted-printable decoder does not have such option, set this as nil.")
273
274 (mel-define-method mime-write-decoded-region (start end filename
275                                                     (nil "quoted-printable"))
276   "Decode and write current region encoded by quoted-printable into FILENAME.
277 START and END are buffer positions."
278   (interactive "*r\nFWrite decoded region to file: ")
279   (as-binary-process
280    (apply (function call-process-region)
281           start end (car quoted-printable-external-decoder)
282           (null quoted-printable-external-decoder-option-to-specify-file)
283           (unless quoted-printable-external-decoder-option-to-specify-file
284             (list (current-buffer) nil))
285           nil
286           (delq nil
287                 (append
288                  (cdr quoted-printable-external-decoder)
289                  quoted-printable-external-decoder-option-to-specify-file
290                  (when quoted-printable-external-decoder-option-to-specify-file
291                    (list filename))))))
292   (unless quoted-printable-external-decoder-option-to-specify-file
293     (write-region-as-binary (point-min) (point-max) filename)))
294
295 \f
296 ;;; @ Q-encoding encode/decode string
297 ;;;
298
299 (defconst q-encoding-special-chars-alist
300   '((text       ?= ?? ?_)
301     (comment    ?= ?? ?_ ?\( ?\) ?\\)
302     (phrase     ?= ?? ?_ ?\( ?\) ?\\ ?\" ?# ?$ ?% ?& ?' ?, ?. ?/
303                 ?: ?\; ?< ?> ?@ ?\[ ?\] ?^ ?` ?{ ?| ?} ?~)
304     ))
305
306 (defun q-encoding-encode-string (string &optional mode)
307   "Encode STRING to Q-encoding of encoded-word, and return the result.
308 MODE allows `text', `comment', `phrase' or nil.  Default value is
309 `phrase'."
310   (let ((specials (cdr (or (assq mode q-encoding-special-chars-alist)
311                            (assq 'phrase q-encoding-special-chars-alist)))))
312     (mapconcat (function
313                 (lambda (chr)
314                   (cond ((eq chr ? ) "_")
315                         ((or (< chr 32) (< 126 chr)
316                              (memq chr specials))
317                          (quoted-printable-quote-char chr))
318                         (t
319                          (char-to-string chr)))))
320                string "")))
321
322 (defun q-encoding-decode-string (string)
323   "Decode STRING which is encoded in Q-encoding and return the result."
324   (let (q h l)
325     (mapconcat (function
326                 (lambda (chr)
327                   (cond ((eq chr ?_) " ")
328                         ((eq chr ?=)
329                          (setq q t)
330                          "")
331                         (q (setq h (quoted-printable-hex-char-to-num chr))
332                            (setq q nil)
333                            "")
334                         (h (setq l (quoted-printable-hex-char-to-num chr))
335                            (prog1
336                                (char-to-string
337                                 (quoted-printable-num-to-raw-byte-char
338                                  (logior (ash h 4) l)))
339                              (setq h nil)))
340                         (t (char-to-string chr)))))
341                string "")))
342
343 (mel-define-method-function (encoded-text-encode-string string (nil "Q"))
344                             'q-encoding-encode-string)
345
346 (mel-define-method encoded-text-decode-string (string (nil "Q"))
347   (if (string-match (eval-when-compile
348                       (concat "\\`" Q-encoded-text-regexp "\\'"))
349                     string)
350       (q-encoding-decode-string string)
351     (error "Invalid encoded-text %s" string)))
352
353
354 ;;; @ end
355 ;;;
356
357 (provide 'mel-q)
358
359 ;;; mel-q.el ends here.