2010-02-09 Kazuhiro Ito <kzhr@d1.dion.ne.jp>
[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 (defun quoted-printable-internal-decode-region (start end)
181   (save-excursion
182     (save-restriction
183       (narrow-to-region start end)
184       (goto-char (point-min))
185       (while (search-forward "=" nil t)
186         (cond
187          ((eolp)
188           ;; unfold soft line break.
189           (delete-region (1- (point))(1+ (point))))
190          ((and (memq (char-after (point))
191                      (eval-when-compile
192                        ;; XXX: should provide char-list instead.
193                        (string-to-char-list quoted-printable-hex-chars)))
194                (memq (char-after (1+ (point)))
195                      (eval-when-compile
196                        ;; XXX: should provide char-list instead.
197                        (string-to-char-list quoted-printable-hex-chars))))
198           ;; encoded char.
199           (insert
200            (prog1
201                (logior
202                 (ash (quoted-printable-hex-char-to-num (char-after (point))) 4)
203                 (quoted-printable-hex-char-to-num (char-after (1+ (point)))))
204              (delete-region (1- (point))(+ 2 (point))))))
205          (t
206           ;; invalid encoding.
207           ))))))
208
209 (defvar quoted-printable-external-decoder '("mmencode" "-q" "-u")
210   "*list of quoted-printable decoder program name and its arguments.")
211
212 (defun quoted-printable-external-decode-region (start end)
213   (save-excursion
214     (as-binary-process
215      (apply (function call-process-region)
216             start end (car quoted-printable-external-decoder)
217             t t nil
218             (cdr quoted-printable-external-decoder)))))
219
220
221 (defvar quoted-printable-internal-decoding-limit nil
222   "*limit size to use internal quoted-printable decoder.
223 If size of input to decode is larger than this limit,
224 external decoder is called.")
225
226 (defun quoted-printable-decode-region (start end)
227   "Decode current region by quoted-printable.
228 START and END are buffer positions.
229 This function calls internal quoted-printable decoder if size of
230 region is smaller than `quoted-printable-internal-decoding-limit',
231 otherwise it calls external quoted-printable decoder specified by
232 `quoted-printable-external-decoder'.  In this case, you must install
233 the program (maybe mmencode included in metamail or XEmacs package)."
234   (interactive "*r")
235   (if (and quoted-printable-internal-decoding-limit
236            (> (- end start) quoted-printable-internal-decoding-limit))
237       (quoted-printable-external-decode-region start end)
238     (quoted-printable-internal-decode-region start end)))
239
240 (defun quoted-printable-decode-string (string)
241   "Decode STRING which is encoded in quoted-printable, and return the result."
242   (with-temp-buffer
243     (insert string)
244     (quoted-printable-decode-region (point-min)(point-max))
245     (buffer-string)))
246
247
248 (mel-define-method-function
249  (mime-decode-string string (nil "quoted-printable"))
250  'quoted-printable-decode-string)
251
252 (mel-define-method-function
253  (mime-decode-region start end (nil "quoted-printable"))
254  'quoted-printable-decode-region)
255
256
257 (defvar quoted-printable-external-decoder-option-to-specify-file '("-o")
258   "*list of options of quoted-printable decoder program to specify file.
259 If the quoted-printable decoder does not have such option, set this as nil.")
260
261 (mel-define-method mime-write-decoded-region (start end filename
262                                                     (nil "quoted-printable"))
263   "Decode and write current region encoded by quoted-printable into FILENAME.
264 START and END are buffer positions."
265   (interactive "*r\nFWrite decoded region to file: ")
266   (as-binary-process
267    (apply (function call-process-region)
268           start end (car quoted-printable-external-decoder)
269           (null quoted-printable-external-decoder-option-to-specify-file)
270           (unless quoted-printable-external-decoder-option-to-specify-file
271             (list (current-buffer) nil))
272           nil
273           (delq nil
274                 (append
275                  (cdr quoted-printable-external-decoder)
276                  quoted-printable-external-decoder-option-to-specify-file
277                  (when quoted-printable-external-decoder-option-to-specify-file
278                    (list filename))))))
279   (unless quoted-printable-external-decoder-option-to-specify-file
280     (write-region-as-binary (point-min) (point-max) filename)))
281
282 \f
283 ;;; @ Q-encoding encode/decode string
284 ;;;
285
286 (defconst q-encoding-special-chars-alist
287   '((text       ?= ?? ?_)
288     (comment    ?= ?? ?_ ?\( ?\) ?\\)
289     (phrase     ?= ?? ?_ ?\( ?\) ?\\ ?\" ?# ?$ ?% ?& ?' ?, ?. ?/
290                 ?: ?\; ?< ?> ?@ ?\[ ?\] ?^ ?` ?{ ?| ?} ?~)
291     ))
292
293 (defun q-encoding-encode-string (string &optional mode)
294   "Encode STRING to Q-encoding of encoded-word, and return the result.
295 MODE allows `text', `comment', `phrase' or nil.  Default value is
296 `phrase'."
297   (let ((specials (cdr (or (assq mode q-encoding-special-chars-alist)
298                            (assq 'phrase q-encoding-special-chars-alist)))))
299     (mapconcat (function
300                 (lambda (chr)
301                   (cond ((eq chr ? ) "_")
302                         ((or (< chr 32) (< 126 chr)
303                              (memq chr specials))
304                          (quoted-printable-quote-char chr))
305                         (t
306                          (char-to-string chr)))))
307                string "")))
308
309 (defun q-encoding-decode-string (string)
310   "Decode STRING which is encoded in Q-encoding and return the result."
311   (let (q h l)
312     (mapconcat (function
313                 (lambda (chr)
314                   (cond ((eq chr ?_) " ")
315                         ((eq chr ?=)
316                          (setq q t)
317                          "")
318                         (q (setq h (quoted-printable-hex-char-to-num chr))
319                            (setq q nil)
320                            "")
321                         (h (setq l (quoted-printable-hex-char-to-num chr))
322                            (prog1
323                                (char-to-string (logior (ash h 4) l))
324                              (setq h nil)))
325                         (t (char-to-string chr)))))
326                string "")))
327
328 (mel-define-method-function (encoded-text-encode-string string (nil "Q"))
329                             'q-encoding-encode-string)
330
331 (mel-define-method encoded-text-decode-string (string (nil "Q"))
332   (if (string-match (eval-when-compile
333                       (concat "\\`" Q-encoded-text-regexp "\\'"))
334                     string)
335       (q-encoding-decode-string string)
336     (error "Invalid encoded-text %s" string)))
337
338
339 ;;; @ end
340 ;;;
341
342 (provide 'mel-q)
343
344 ;;; mel-q.el ends here.