2010-03-10 Kazuhiro Ito <kzhr@d1.dion.ne.jp>
[elisp/flim.git] / mel-q-ccl.el
1 ;;; mel-q-ccl.el --- Quoted-Printable encoder/decoder using CCL.
2
3 ;; Copyright (C) 1998,1999 Tanaka Akira
4
5 ;; Author: Tanaka Akira <akr@jaist.ac.jp>
6 ;; Created: 1998/9/17
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 'ccl)
29 (require 'pccl)
30 (require 'mime-def)
31
32
33 ;;; @ constants
34 ;;;
35
36 (eval-when-compile
37
38 (defconst mel-ccl-16-table
39   '(  0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15))
40
41 (defconst mel-ccl-28-table
42   '(  0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15
43      16  17  18  19  20  21  22  23  24  25  26  27))
44
45 (defconst mel-ccl-256-table
46   '(  0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15
47      16  17  18  19  20  21  22  23  24  25  26  27  28  29  30  31
48      32  33  34  35  36  37  38  39  40  41  42  43  44  45  46  47
49      48  49  50  51  52  53  54  55  56  57  58  59  60  61  62  63
50      64  65  66  67  68  69  70  71  72  73  74  75  76  77  78  79
51      80  81  82  83  84  85  86  87  88  89  90  91  92  93  94  95
52      96  97  98  99 100 101 102 103 104 105 106 107 108 109 110 111
53     112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
54     128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
55     144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
56     160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
57     176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
58     192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
59     208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
60     224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
61     240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255))
62
63 (defconst mel-ccl-256-to-16-table
64   '(nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil
65     nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil
66     nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil
67       0   1   2   3   4   5   6   7   8   9 nil nil nil nil nil nil
68     nil  10  11  12  13  14  15 nil nil nil nil nil nil nil nil nil
69     nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil
70     nil  10  11  12  13  14  15 nil nil nil nil nil nil nil nil nil
71     nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil
72     nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil
73     nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil
74     nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil
75     nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil
76     nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil
77     nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil
78     nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil
79     nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil))
80
81 (defconst mel-ccl-16-to-256-table
82   (mapcar 'char-int "0123456789ABCDEF"))
83
84 (defconst mel-ccl-high-table
85   (vconcat
86    (mapcar
87     (lambda (v) (nth (lsh v -4) mel-ccl-16-to-256-table))
88     mel-ccl-256-table)))
89
90 (defconst mel-ccl-low-table
91   (vconcat
92    (mapcar
93     (lambda (v) (nth (logand v 15) mel-ccl-16-to-256-table))
94     mel-ccl-256-table)))
95
96 (defconst mel-ccl-u-raw
97   (mapcar
98    'char-int
99    "0123456789\
100 ABCDEFGHIJKLMNOPQRSTUVWXYZ\
101 abcdefghijklmnopqrstuvwxyz\
102 !@#$%&'()*+,-./:;<>@[\\]^`{|}~"))
103
104 (defconst mel-ccl-c-raw
105   (mapcar
106    'char-int
107    "0123456789\
108 ABCDEFGHIJKLMNOPQRSTUVWXYZ\
109 abcdefghijklmnopqrstuvwxyz\
110 !@#$%&'*+,-./:;<>@[]^`{|}~"))
111
112 (defconst mel-ccl-p-raw
113   (mapcar
114    'char-int
115    "0123456789\
116 ABCDEFGHIJKLMNOPQRSTUVWXYZ\
117 abcdefghijklmnopqrstuvwxyz\
118 !*+-/"))
119
120 (defconst mel-ccl-qp-table
121   [enc enc enc enc enc enc enc enc enc wsp lf  enc enc cr  enc enc
122    enc enc enc enc enc enc enc enc enc enc enc enc enc enc enc enc
123    wsp raw raw raw raw raw raw raw raw raw raw raw raw raw raw raw
124    raw raw raw raw raw raw raw raw raw raw raw raw raw enc raw raw
125    raw raw raw raw raw raw raw raw raw raw raw raw raw raw raw raw
126    raw raw raw raw raw raw raw raw raw raw raw raw raw raw raw raw
127    raw raw raw raw raw raw raw raw raw raw raw raw raw raw raw raw
128    raw raw raw raw raw raw raw raw raw raw raw raw raw raw raw enc
129    enc enc enc enc enc enc enc enc enc enc enc enc enc enc enc enc
130    enc enc enc enc enc enc enc enc enc enc enc enc enc enc enc enc
131    enc enc enc enc enc enc enc enc enc enc enc enc enc enc enc enc
132    enc enc enc enc enc enc enc enc enc enc enc enc enc enc enc enc
133    enc enc enc enc enc enc enc enc enc enc enc enc enc enc enc enc
134    enc enc enc enc enc enc enc enc enc enc enc enc enc enc enc enc
135    enc enc enc enc enc enc enc enc enc enc enc enc enc enc enc enc
136    enc enc enc enc enc enc enc enc enc enc enc enc enc enc enc enc])
137
138 )
139
140
141 ;;; @ CCL programs
142 ;;;
143
144 ;;; Q
145
146 (define-ccl-program mel-ccl-decode-q
147   `(1
148     ((loop
149       (read r0)
150       (branch
151        (r0 & 255)
152        ,@(mapcar
153           (lambda (r0)
154             (cond
155              ((= r0 (char-int ?_))
156               `(write-repeat ? ))
157              ((= r0 (char-int ?=))
158               `((loop
159                  (read-branch
160                   r1
161                   ,@(mapcar
162                      (lambda (v)
163                        (if (integerp v)
164                            `((r0 = ,v) (break))
165                          '(repeat)))
166                      mel-ccl-256-to-16-table)))
167                 (loop
168                  (read-branch
169                   r1
170                   ,@(mapcar
171                      (lambda (v)
172                        (if (integerp v)
173                            `((write r0 ,(vconcat
174                                          (mapcar
175                                           (lambda (r0)
176                                             (logior (lsh r0 4) v))
177                                           mel-ccl-16-table)))
178                              (break))
179                          '(repeat)))
180                      mel-ccl-256-to-16-table)))
181                 (repeat)))
182              (t
183               `(write-repeat ,r0))))
184           mel-ccl-256-table))))))
185
186 (eval-when-compile
187
188 (defun mel-ccl-encode-q-generic (raw)
189   `(3
190     (loop
191      (loop
192       (read r0)
193       (r0 &= 255)
194       (branch
195        r0
196        ,@(mapcar
197           (lambda (r0)
198             (cond
199              ((= r0 32) `(write-repeat ?_))
200              ((member r0 raw) `(write-repeat ,r0))
201              (t '(break))))
202           mel-ccl-256-table)))
203      (write ?=)
204      (write r0 ,mel-ccl-high-table)
205      (write r0 ,mel-ccl-low-table)
206      (repeat))))
207
208 ;; On xemacs, generated program counts iso-8859-1 8bit character as 6bytes.
209 (defun mel-ccl-count-q-length (raw)
210   `(0
211     ((r0 = 0)
212      (loop
213       (read-branch
214        r1
215        ,@(mapcar
216           (lambda (r1)
217             (if (or (= r1 32) (member r1 raw))
218                 '((r0 += 1) (repeat))
219               '((r0 += 3) (repeat))))
220           mel-ccl-256-table))))))
221
222 )
223
224 (define-ccl-program mel-ccl-encode-uq
225   (mel-ccl-encode-q-generic mel-ccl-u-raw))
226 (define-ccl-program mel-ccl-encode-cq
227   (mel-ccl-encode-q-generic mel-ccl-c-raw))
228 (define-ccl-program mel-ccl-encode-pq
229   (mel-ccl-encode-q-generic mel-ccl-p-raw))
230
231 (define-ccl-program mel-ccl-count-uq
232   (mel-ccl-count-q-length mel-ccl-u-raw))
233 (define-ccl-program mel-ccl-count-cq
234   (mel-ccl-count-q-length mel-ccl-c-raw))
235 (define-ccl-program mel-ccl-count-pq
236   (mel-ccl-count-q-length mel-ccl-p-raw))
237
238 ;; Quoted-Printable
239
240 (eval-when-compile
241
242 (defvar eof-block-branches)
243 (defvar eof-block-reg)
244 (defun mel-ccl-set-eof-block (branch)
245   (let ((p (assoc branch eof-block-branches)))
246     (unless p
247       (setq p (cons branch (length eof-block-branches))
248             eof-block-branches (cons p eof-block-branches)))
249     `(,eof-block-reg = ,(cdr p))))
250
251 )
252
253 (eval-when-compile
254
255 (defun mel-ccl-try-to-read-crlf (input-crlf reg
256                                             succ
257                                             cr-eof cr-fail
258                                             lf-eof lf-fail
259                                             crlf-eof crlf-fail)
260   (if input-crlf
261       `(,(mel-ccl-set-eof-block cr-eof)
262         (read-if (,reg == ?\r)
263           (,(mel-ccl-set-eof-block lf-eof)
264            (read-if (,reg == ?\n)
265              ,succ
266              ,lf-fail))
267           ,cr-fail))
268     `(,(mel-ccl-set-eof-block crlf-eof)
269       (read-if (,reg == ?\n)
270         ,succ
271         ,crlf-fail))))
272
273 )
274
275 (eval-when-compile
276
277 ;; Generated CCL program works not properly on 20.2 because CCL_EOF_BLOCK
278 ;; is not executed.
279 (defun mel-ccl-encode-quoted-printable-generic (input-crlf output-crlf)
280   (let ((hard (if output-crlf "\r\n" "\n"))
281         (soft (if output-crlf "=\r\n" "=\n"))
282         (eof-block-branches nil)
283         (eof-block-reg 'r4)
284         (after-wsp 'r5)
285         (column 'r6)
286         (type 'r3)
287         (current 'r0)
288         (type-raw 0)
289         (type-enc 1)
290         (type-wsp 2)
291         (type-brk 3)
292         )
293     `(4
294       ((,column = 0)
295        (,after-wsp = 0)
296        ,(mel-ccl-set-eof-block '(end))
297        (read r0)
298        (loop    ; invariant: column <= 75
299         (loop
300          (loop
301           (r0 &= 255)
302           (branch
303            r0
304            ,@(mapcar
305               (lambda (r0)
306                 (let ((tmp (aref mel-ccl-qp-table r0)))
307                   (cond
308                    ((eq r0 (char-int ?F))
309                     `(if (,column == 0)
310                          (,(mel-ccl-set-eof-block '((write "F") (end)))
311                           (read-if (r0 == ?r)
312                             (,(mel-ccl-set-eof-block '((write "Fr") (end)))
313                              (read-if (r0 == ?o)
314                                (,(mel-ccl-set-eof-block '((write "Fro") (end)))
315                                 (read-if (r0 == ?m)
316                                   (,(mel-ccl-set-eof-block '((write "From") (end)))
317                                    (read-if (r0 == ? )
318                                      ((,column = 7)
319                                       (,after-wsp = 1)
320                                       ,(mel-ccl-set-eof-block '((write "From=20") (end)))
321                                       (read r0)
322                                       (write-repeat "=46rom "))
323                                      ((,column = 4)
324                                       (write-repeat "From"))))
325                                   ((,column = 3)
326                                    (write-repeat "Fro"))))
327                                ((,column = 2)
328                                 (write-repeat "Fr"))))
329                             ((,column = 1)
330                              (write-repeat "F"))))
331                        ((,type = ,type-raw) (break)) ; RAW
332                        ))
333                    ((eq r0 (char-int ?.))
334                     `(if (,column == 0)
335                          ,(mel-ccl-try-to-read-crlf
336                             input-crlf 'r0
337                             ;; "." CR LF (input-crlf: t)
338                             ;; "." LF (input-crlf: nil)
339                             `((write ,(concat "=2E" hard))
340                               ,(mel-ccl-set-eof-block '(end))
341                               (read r0)
342                               (repeat))
343                             ;; "." <EOF>
344                             '((write ".") (end))
345                             ;; "." noCR (input-crlf: t)
346                             `((,column = 1)
347                               (write-repeat "."))
348                             ;; "." CR <EOF> (input-crlf: t)
349                             '((write ".=0D") (end))
350                             ;; "." CR noLF (input-crlf: t)
351                             `((,column = 4)
352                               (write-repeat ".=0D"))
353                             ;; "." <EOF> (input-crlf: nil)
354                             '((write ".") (end))
355                             ;; "." noLF (input-crlf: nil)
356                             `((,column = 1)
357                               (write-repeat ".")))
358                        ((,type = ,type-raw) (break)) ; RAW
359                        ))
360                    ((eq tmp 'raw) `((,type = ,type-raw) (break)))
361                    ((eq tmp 'enc) `((,type = ,type-enc) (break)))
362                    ((eq tmp 'wsp) `((,type = ,type-wsp) (break)))
363                    ((eq tmp 'cr) `((,type = ,(if input-crlf type-brk type-enc))
364                                    (break)))
365                    ((eq tmp 'lf) `((,type = ,(if input-crlf type-enc type-brk))
366                                    (break)))
367                    )))
368               mel-ccl-256-table)))
369          ;; r0:type{raw,enc,wsp,brk}
370          (branch
371           ,type
372           ;; r0:type-raw
373           (if (,column < 75)
374               ((,column += 1)
375                (,after-wsp = 0)
376                ,(mel-ccl-set-eof-block '(end))
377                (write-read-repeat r0))
378             ((r1 = (r0 + 0))
379              (,after-wsp = 0)
380              ,@(mel-ccl-try-to-read-crlf
381                 input-crlf 'r0
382                 `((,column = 0)
383                   (write r1)
384                   ,(mel-ccl-set-eof-block `((write ,hard) (end)))
385                   (read r0)
386                   (write-repeat ,hard))
387                 '((write r1) (end))
388                 `((,column = 1)
389                   (write ,soft) (write-repeat r1))
390                 `((write ,soft) (write r1) (write "=0D") (end))
391                 `((,column = 4)
392                   (write ,soft) (write r1) (write-repeat "=0D"))
393                 '((write r1) (end))
394                 `((,column = 1)
395                   (write ,soft) (write-repeat r1)))))
396           ;; r0:type-enc
397           ((,after-wsp = 0)
398            (if (,column < 73)
399                ((,column += 3)
400                 (write "=")
401                 (write r0 ,mel-ccl-high-table)
402                 ,(mel-ccl-set-eof-block '(end))
403                 (write-read-repeat r0 ,mel-ccl-low-table))
404              (if (,column < 74)
405                  ((r1 = (r0 + 0))
406                   (,after-wsp = 0)
407                   ,@(mel-ccl-try-to-read-crlf
408                      input-crlf 'r0
409                      `((,column = 0)
410                        (write "=")
411                        (write r1 ,mel-ccl-high-table)
412                        (write r1 ,mel-ccl-low-table)
413                        (write ,hard)
414                        ,(mel-ccl-set-eof-block '(end))
415                        (read r0)
416                        (repeat))
417                      `((write "=")
418                        (write r1 ,mel-ccl-high-table)
419                        (write r1 ,mel-ccl-low-table)
420                        (end))
421                      `((,column = 3)
422                        (write ,(concat soft "="))
423                        (write r1 ,mel-ccl-high-table)
424                        (write r1 ,mel-ccl-low-table)
425                        (repeat))
426                      `((write ,(concat soft "="))
427                        (write r1 ,mel-ccl-high-table)
428                        (write r1 ,mel-ccl-low-table)
429                        (write "=0D")
430                        (end))
431                      `((,column = 6)
432                        (write ,(concat soft "="))
433                        (write r1 ,mel-ccl-high-table)
434                        (write r1 ,mel-ccl-low-table)
435                        (write-repeat "=0D"))
436                      `((write "=")
437                        (write r1 ,mel-ccl-high-table)
438                        (write r1 ,mel-ccl-low-table)
439                        (end))
440                      `((,column = 3)
441                        (write ,(concat soft "="))
442                        (write r1 ,mel-ccl-high-table)
443                        (write r1 ,mel-ccl-low-table)
444                        (repeat))))
445                ((,column = 3)
446                 (write ,(concat soft "="))
447                 (write r0 ,mel-ccl-high-table)
448                 ,(mel-ccl-set-eof-block '(end))
449                 (write-read-repeat r0 ,mel-ccl-low-table)))))
450           ;; r0:type-wsp
451           (if (,column < 73)
452               ((r1 = (r0 + 0))
453                ,@(mel-ccl-try-to-read-crlf
454                   input-crlf 'r0
455                   `((,column = 0)
456                     (,after-wsp = 0)
457                     (write "=")
458                     (write r1 ,mel-ccl-high-table)
459                     (write r1 ,mel-ccl-low-table)
460                     (write ,hard)
461                     ,(mel-ccl-set-eof-block `(end))
462                     (read r0)
463                     (repeat))
464                   `((write "=")
465                     (write r1 ,mel-ccl-high-table)
466                     (write r1 ,mel-ccl-low-table)
467                     (end))
468                   `((,column += 1)
469                     (,after-wsp = 1)
470                     (write-repeat r1))
471                   `((write r1)
472                     (write "=0D")
473                     (end))
474                   `((,column += 4)
475                     (,after-wsp = 0)
476                     (write r1)
477                     (write-repeat "=0D"))
478                   `((write "=")
479                     (write r1 ,mel-ccl-high-table)
480                     (write r1 ,mel-ccl-low-table)
481                     (end))
482                   `((,column += 1)
483                     (,after-wsp = 1)
484                     (write-repeat r1))))
485             (if (,column < 74)
486                 ((r1 = (r0 + 0))
487                  ,@(mel-ccl-try-to-read-crlf
488                     input-crlf 'r0
489                     `((,column = 0)
490                       (,after-wsp = 0)
491                       (write "=")
492                       (write r1 ,mel-ccl-high-table)
493                       (write r1 ,mel-ccl-low-table)
494                       (write ,hard)
495                       ,(mel-ccl-set-eof-block `(end))
496                       (read r0)
497                       (repeat))
498                     `((write "=")
499                       (write r1 ,mel-ccl-high-table)
500                       (write r1 ,mel-ccl-low-table)
501                       (end))
502                     `((,column += 1)
503                       (,after-wsp = 1)
504                       (write-repeat r1))
505                     `((write r1)
506                       (write ,(concat soft "=0D"))
507                       (end))
508                     `((,column = 3)
509                       (,after-wsp = 0)
510                       (write r1)
511                       (write-repeat ,(concat soft "=0D")))
512                     `((write "=")
513                       (write r1 ,mel-ccl-high-table)
514                       (write r1 ,mel-ccl-low-table)
515                       (end))
516                     `((,column += 1)
517                       (,after-wsp = 1)
518                       (write-repeat r1))))
519               (if (,column < 75)
520                   ((,column += 1)
521                    (,after-wsp = 1)
522                    ,(mel-ccl-set-eof-block `((write ,soft) (end)))
523                    (write-read-repeat r0))
524                 ((write ,soft)
525                  (,column = 0)
526                  (,after-wsp = 0)
527                  (repeat)))))
528           ;; r0:type-brk
529           ,(if input-crlf
530                ;; r0{CR}:type-brk
531                `((if ((,column > 73) & ,after-wsp)
532                      ((,column = 0)
533                       (,after-wsp = 0)
534                       (write ,soft)))
535                  ,(mel-ccl-set-eof-block `((if (,column > 73) (write ,soft))
536                                            (write "=0D") (end)))
537                  (read-if (r0 == ?\n)
538                    (if ,after-wsp
539                        ((,after-wsp = 0)
540                         (,column = 0)
541                         (write ,(concat soft hard))
542                         ,(mel-ccl-set-eof-block '(end))
543                         (read r0)
544                         (repeat))
545                      ((,after-wsp = 0)
546                       (,column = 0)
547                       (write ,hard)
548                       ,(mel-ccl-set-eof-block '(end))
549                       (read r0)
550                       (repeat)))
551                    (if (,column < 73)
552                        ((,after-wsp = 0)
553                         (,column += 3)
554                         (write-repeat "=0D"))
555                      (if (,column < 74)
556                          (if (r0 == ?\r)
557                              ((,after-wsp = 0)
558                               ,(mel-ccl-set-eof-block
559                                 `((write ,(concat soft "=0D=0D")) (end)))
560                               (read-if (r0 == ?\n)
561                                 ((,column = 0)
562                                  ,(mel-ccl-set-eof-block
563                                    `((write ,(concat "=0D" hard)) (end)))
564                                  (read r0)
565                                  (write-repeat ,(concat "=0D" hard)))
566                                 ((,column = 6)
567                                  (write-repeat ,(concat soft "=0D=0D")))))
568                            ((,after-wsp = 0)
569                             (,column = 3)
570                             (write-repeat ,(concat soft "=0D"))))
571                        ((,after-wsp = 0)
572                         (,column = 3)
573                         (write-repeat ,(concat soft "=0D")))))))
574              ;; r0{LF}:type-brk
575              `(if ,after-wsp
576                   ;; WSP ; r0{LF}:type-brk
577                   ((,after-wsp = 0)
578                    (,column = 0)
579                    (write ,(concat soft (if output-crlf "\r" "")))
580                    ,(mel-ccl-set-eof-block `(end))
581                    (write-read-repeat r0))
582                 ;; noWSP ; r0{LF}:type-brk
583                 ((,after-wsp = 0)
584                  (,column = 0)
585                  ,@(if output-crlf '((write ?\r)) '())
586                  ,(mel-ccl-set-eof-block `(end))
587                  (write-read-repeat r0)))
588              )))))
589       (branch
590        ,eof-block-reg
591        ,@(reverse (mapcar 'car eof-block-branches))))))
592
593 (defun mel-ccl-decode-quoted-printable-generic (input-crlf output-crlf)
594   `(1
595     ((read r0)
596      (loop
597       (branch
598        (r0 & 255)
599        ,@(mapcar
600           (lambda (r0)
601             (let ((tmp (aref mel-ccl-qp-table r0)))
602               (cond
603                ((eq tmp 'raw) `(write-read-repeat r0))
604                ((eq tmp 'wsp) (if (eq r0 (char-int ? ))
605                                   `(r1 = 1)
606                                 `(r1 = 0)))
607                ((eq tmp 'cr)
608                 (if input-crlf
609                     ;; r0='\r'
610                     `((read r0)
611                       ;; '\r' r0
612                       (if (r0 == ?\n)
613                           ;; '\r' r0='\n'
614                           ;; hard line break found.
615                           ,(if output-crlf
616                                '((write ?\r)
617                                  (write-read-repeat r0))
618                              '(write-read-repeat r0))
619                         ;; '\r' r0:[^\n]
620                         ;; invalid control character (bare CR) found.
621                         ;; -> ignore it and rescan from r0.
622                         (repeat)))
623                   ;; r0='\r'
624                   ;; invalid character (bare CR) found.
625                   ;; -> ignore.
626                   `((read r0)
627                     (repeat))))
628                ((eq tmp 'lf)
629                 (if input-crlf
630                     ;; r0='\n'
631                     ;; invalid character (bare LF) found.
632                     ;; -> ignore.
633                     `((read r0)
634                       (repeat))
635                   ;; r0='\r\n'
636                   ;; hard line break found.
637                   (if output-crlf
638                       '((write ?\r)
639                         (write-read-repeat r0))
640                     '(write-read-repeat r0))))
641                ((eq r0 (char-int ?=))
642                 ;; r0='='
643                 `((read r0)
644                   ;; '=' r0
645                   (r1 = (r0 == ?\t))
646                   (if ((r0 == ? ) | r1)
647                       ;; '=' r0:[\t ]
648                       ;; Skip transport-padding.
649                       ;; It should check CR LF after
650                       ;; transport-padding.
651                       (loop
652                        (read-if (r0 == ?\t)
653                                 (repeat)
654                                 (if (r0 == ? )
655                                     (repeat)
656                                   (break)))))
657                   ;; '=' [\t ]* r0:[^\t ]
658                   (branch
659                    r0
660                    ,@(mapcar
661                       (lambda (r0)
662                         (cond
663                          ((eq r0 (char-int ?\r))
664                           (if input-crlf
665                               ;; '=' [\t ]* r0='\r'
666                               `((read r0)
667                                 ;; '=' [\t ]* '\r' r0
668                                 (if (r0 == ?\n)
669                                     ;; '=' [\t ]* '\r' r0='\n'
670                                     ;; soft line break found.
671                                     ((read r0)
672                                      (repeat))
673                                   ;; '=' [\t ]* '\r' r0:[^\n]
674                                   ;; invalid input ->
675                                   ;; output "=" and rescan from r0.
676                                   ((write "=")
677                                    (repeat))))
678                             ;; '=' [\t ]* r0='\r'
679                             ;; invalid input (bare CR found) -> 
680                             ;; output "=" and rescan from next.
681                             `((write ?=)
682                               (read r0)
683                               (repeat))))
684                          ((eq r0 (char-int ?\n))
685                           (if input-crlf
686                               ;; '=' [\t ]* r0='\n'
687                               ;; invalid input (bare LF found) -> 
688                               ;; output "=" and rescan from next.
689                               `((write ?=)
690                                 (read r0)
691                                 (repeat))
692                             ;; '=' [\t ]* r0='\r\n'
693                             ;; soft line break found.
694                             `((read r0)
695                               (repeat))))
696                          ((setq tmp (nth r0 mel-ccl-256-to-16-table))
697                           ;; '=' [\t ]* r0:[0-9A-F]
698                           ;; upper nibble of hexadecimal digit found.
699                           `((r1 = (r0 + 0))
700                             (r0 = ,tmp)))
701                          (t
702                           ;; '=' [\t ]* r0:[^\r0-9A-F]
703                           ;; invalid input ->
704                           ;; output "=" and rescan from r0.
705                           `((write ?=)
706                             (repeat)))))
707                       mel-ccl-256-table))
708                   ;; '=' [\t ]* r1:r0:[0-9A-F]
709                   (read-branch
710                    r2
711                    ,@(mapcar
712                       (lambda (r2)
713                         (if (setq tmp (nth r2 mel-ccl-256-to-16-table))
714                             ;; '=' [\t ]* r1:r0:[0-9A-F] r2:[0-9A-F]
715                             `(write-read-repeat
716                               r0
717                               ,(vconcat
718                                 (mapcar
719                                  (lambda (r0)
720                                    (logior (lsh r0 4) tmp))
721                                  mel-ccl-16-table)))
722                           ;; '=' [\t ]* r1:r0:[0-9A-F] r2:[^0-9A-F]
723                           ;; invalid input
724                           `(r3 = 0)     ; nop
725                           ))
726                       mel-ccl-256-table))
727                   ;; '=' [\t ]* r1:r0:[0-9A-F] r2:[^0-9A-F]
728                   ;; invalid input ->
729                   ;; output "=" with hex digit and rescan from r2.
730                   (write ?=)
731                   (r0 = (r2 + 0))
732                   (write-repeat r1)))
733                (t
734                 ;; r0:[^\t\r -~]
735                 ;; invalid character found.
736                 ;; -> output as is.
737                 `((write-read-repeat r0))))))
738           mel-ccl-256-table))
739       ;; r1[0]:[\t ]
740       (loop
741        ,@(apply
742           'append
743           (mapcar
744            (lambda (regnum)
745              (let ((reg (aref [r1 r2 r3 r4 r5] regnum)))
746                (apply
747                 'append
748                 (mapcar
749                  (lambda (bit)
750                    (if (= bit 0)
751                        (if (= regnum 0)
752                            nil
753                          `((read r0)
754                            (if (r0 == ?\t)
755                                (,reg = 0)
756                              (if (r0 == ?\ )
757                                  (,reg = 1)
758                                ((r6 = ,(+ (* regnum 28) bit))
759                                 (break))))))
760                      `((read r0)
761                        (if (r0 == ?\ )
762                            (,reg |= ,(lsh 1 bit))
763                          (if (r0 != ?\t)
764                              ((r6 = ,(+ (* regnum 28) bit))
765                               (break)))))))
766                  mel-ccl-28-table))))
767            '(0 1 2 3 4)))
768        ;; white space buffer exhaust.
769        ;; error: line length limit (76bytes) violation.
770        ;; -> ignore these white spaces.
771        (repeat))
772       ,(if input-crlf
773            `(if (r0 == ?\r)
774                 ((read r0)
775                  (if (r0 == ?\n)
776                      ;; trailing white spaces found.
777                      ;; -> ignore these white spacs.
778                      ((write ,(if output-crlf "\r\n" "\n"))
779                       (read r0)
780                       (repeat))
781                    ;; [\t ]* \r r0:[^\n]
782                    ;; error: bare CR found.
783                    ;; -> output white spaces and ignore bare CR.
784                    ))
785               ;; [\t ]* r0:[^\r]
786               ;; middle white spaces found.
787               )
788          `(if (r0 == ?\n)
789               ;; trailing white spaces found.
790               ;; -> ignore these white spacs.
791               ((write ,(if output-crlf "\r\n" "\n"))
792                (read r0)
793                (repeat))
794             ;; [\t ]* r0:[^\n]
795             ;; middle white spaces found.
796             ))
797       ,@(apply
798          'append
799          (mapcar
800           (lambda (regnum)
801             (let ((reg (aref [r1 r2 r3 r4 r5] regnum)))
802               (apply
803                'append
804                (mapcar
805                 (lambda (bit)
806                   `((if (,reg & ,(lsh 1 bit))
807                         (write ?\ )
808                       (write ?\t))
809                     (if (r6 == ,(+ (* regnum 28) bit 1))
810                         (repeat))))
811                 mel-ccl-28-table))))
812           '(0 1 2 3 4)))
813       (repeat)
814       ))))
815
816 )
817
818 (define-ccl-program mel-ccl-encode-quoted-printable-crlf-crlf
819   (mel-ccl-encode-quoted-printable-generic t t))
820
821 (define-ccl-program mel-ccl-encode-quoted-printable-crlf-lf
822   (mel-ccl-encode-quoted-printable-generic t nil))
823
824 (define-ccl-program mel-ccl-encode-quoted-printable-lf-crlf
825   (mel-ccl-encode-quoted-printable-generic nil t))
826
827 (define-ccl-program mel-ccl-encode-quoted-printable-lf-lf
828   (mel-ccl-encode-quoted-printable-generic nil nil))
829
830 (define-ccl-program mel-ccl-decode-quoted-printable-crlf-crlf
831   (mel-ccl-decode-quoted-printable-generic t t))
832
833 (define-ccl-program mel-ccl-decode-quoted-printable-crlf-lf
834   (mel-ccl-decode-quoted-printable-generic t nil))
835
836 (define-ccl-program mel-ccl-decode-quoted-printable-lf-crlf
837   (mel-ccl-decode-quoted-printable-generic nil t))
838
839 (define-ccl-program mel-ccl-decode-quoted-printable-lf-lf
840   (mel-ccl-decode-quoted-printable-generic nil nil))
841
842
843 ;;; @ coding system
844 ;;;
845
846 (make-ccl-coding-system
847  'mel-ccl-uq-rev ?Q "MIME Q-encoding in unstructured field (reversed)"
848  'mel-ccl-encode-uq 'mel-ccl-decode-q)
849
850 (make-ccl-coding-system
851  'mel-ccl-cq-rev ?Q "MIME Q-encoding in comment (reversed)"
852  'mel-ccl-encode-cq 'mel-ccl-decode-q)
853
854 (make-ccl-coding-system
855  'mel-ccl-pq-rev ?Q "MIME Q-encoding in phrase (reversed)"
856  'mel-ccl-encode-pq 'mel-ccl-decode-q)
857
858 (make-ccl-coding-system
859  'mel-ccl-quoted-printable-crlf-crlf-rev
860  ?Q "MIME Quoted-Printable-encoding (reversed)"
861  'mel-ccl-encode-quoted-printable-crlf-crlf
862  'mel-ccl-decode-quoted-printable-crlf-crlf)
863
864 (make-ccl-coding-system
865  'mel-ccl-quoted-printable-lf-crlf-rev
866  ?Q "MIME Quoted-Printable-encoding (LF encoding) (reversed)"
867  'mel-ccl-encode-quoted-printable-crlf-lf
868  'mel-ccl-decode-quoted-printable-lf-crlf)
869
870 (make-ccl-coding-system
871  'mel-ccl-quoted-printable-crlf-lf-rev
872  ?Q "MIME Quoted-Printable-encoding (LF internal) (reversed)"
873  'mel-ccl-encode-quoted-printable-lf-crlf
874  'mel-ccl-decode-quoted-printable-crlf-lf)
875
876 (make-ccl-coding-system
877  'mel-ccl-quoted-printable-lf-lf-rev
878  ?Q "MIME Quoted-Printable-encoding (LF encoding) (LF internal) (reversed)"
879  'mel-ccl-encode-quoted-printable-lf-lf
880  'mel-ccl-decode-quoted-printable-lf-lf)
881
882
883 ;;; @ quoted-printable
884 ;;;
885
886 (check-broken-facility ccl-execute-eof-block-on-decoding-some)
887
888 (unless-broken ccl-execute-eof-block-on-decoding-some
889
890   (cond
891    ((eval-when-compile
892       (and (eq emacs-major-version 23)
893            (eq emacs-minor-version 1)))
894     (defun quoted-printable-ccl-encode-string (string)
895       "Encode STRING with quoted-printable encoding."
896       (ccl-execute-on-string 'mel-ccl-encode-quoted-printable-lf-lf
897                              (make-vector 9 0) string nil t))
898     (defun quoted-printable-ccl-encode-region (start end)
899       "Encode the region from START to END with quoted-printable encoding."
900       (interactive "*r")
901       (save-excursion
902         (goto-char start)
903         (insert (prog1 (quoted-printable-ccl-encode-string
904                         (buffer-substring start end))
905                   (delete-region start end)))))
906
907     (defun quoted-printable-ccl-insert-encoded-file (filename)
908       "Encode contents of the file named as FILENAME, and insert it."
909       (interactive "*fInsert encoded file: ")
910       (insert
911        (ccl-execute-on-string 'mel-ccl-encode-quoted-printable-lf-lf
912                               (make-vector 9 0)
913                               (with-temp-buffer
914                                 (set-buffer-multibyte nil)
915                                 (insert-file-contents-as-binary filename)
916                                 (buffer-string))
917                               nil t))))
918    (t
919     (defun quoted-printable-ccl-encode-string (string)
920       "Encode STRING with quoted-printable encoding."
921       (decode-coding-string
922        string
923        'mel-ccl-quoted-printable-lf-lf-rev))
924
925     (defun quoted-printable-ccl-encode-region (start end)
926       "Encode the region from START to END with quoted-printable encoding."
927       (interactive "*r")
928       (decode-coding-region start end 'mel-ccl-quoted-printable-lf-lf-rev))
929
930     (defun quoted-printable-ccl-insert-encoded-file (filename)
931       "Encode contents of the file named as FILENAME, and insert it."
932       (interactive "*fInsert encoded file: ")
933       (insert
934        (decode-coding-string
935         (with-temp-buffer
936           (set-buffer-multibyte nil)
937           (insert-file-contents-as-binary filename)
938           (buffer-string))
939         'mel-ccl-quoted-printable-lf-lf-rev)))))
940
941   (mel-define-method-function
942    (mime-encode-string string (nil "quoted-printable"))
943    'quoted-printable-ccl-encode-string)
944   (mel-define-method-function
945    (mime-encode-region start end (nil "quoted-printable"))
946    'quoted-printable-ccl-encode-region)
947   (mel-define-method-function
948    (mime-insert-encoded-file filename (nil "quoted-printable"))
949    'quoted-printable-ccl-insert-encoded-file)
950   )
951
952   (cond
953    ((eval-when-compile
954       (and (eq emacs-major-version 23)
955            (eq emacs-minor-version 1)))
956     (defun quoted-printable-ccl-decode-string (string)
957       "Decode quoted-printable encoded STRING."
958       (ccl-execute-on-string 'mel-ccl-decode-quoted-printable-lf-lf
959                              (make-vector 9 0) string nil t))
960
961     (defun quoted-printable-ccl-decode-region (start end)
962       "Decode the region from START to END with quoted-printable
963 encoding."
964       (interactive "*r")
965       (save-excursion
966         (goto-char start)
967         (insert (prog1 (quoted-printable-ccl-decode-string
968                         (buffer-substring start end))
969                   (delete-region start end)))))
970
971     (defun quoted-printable-ccl-write-decoded-region (start end filename)
972       "Decode quoted-printable encoded current region and write out to FILENAME."
973       (interactive "*r\nFWrite decoded region to file: ")
974       (let ((string (quoted-printable-ccl-decode-string
975                      (buffer-substring start end)))
976             (coding-system-for-write 'binary)
977             jka-compr-compression-info-list jam-zcat-filename-list)
978         (with-temp-file filename
979           (insert string)))))
980    (t
981     (defun quoted-printable-ccl-decode-string (string)
982       "Decode quoted-printable encoded STRING."
983       (encode-coding-string
984        string
985        'mel-ccl-quoted-printable-lf-lf-rev))
986
987     (defun quoted-printable-ccl-decode-region (start end)
988       "Decode the region from START to END with quoted-printable
989 encoding."
990       (interactive "*r")
991       (encode-coding-region start end 'mel-ccl-quoted-printable-lf-lf-rev))
992
993     (defun quoted-printable-ccl-write-decoded-region (start end filename)
994       "Decode quoted-printable encoded current region and write out to FILENAME."
995       (interactive "*r\nFWrite decoded region to file: ")
996       (let ((coding-system-for-write
997              (if (coding-system-p 'mel-ccl-quoted-printable-lf-lf-rev-unix)
998                  'mel-ccl-quoted-printable-lf-lf-rev-unix
999                'mel-ccl-quoted-printable-lf-lf-rev))
1000             jka-compr-compression-info-list jam-zcat-filename-list)
1001         (write-region start end filename)))))
1002
1003 (mel-define-method-function
1004  (mime-decode-string string (nil "quoted-printable"))
1005  'quoted-printable-ccl-decode-string)
1006 (mel-define-method-function
1007  (mime-decode-region start end (nil "quoted-printable"))
1008  'quoted-printable-ccl-decode-region)
1009 (mel-define-method-function
1010  (mime-write-decoded-region start end filename (nil "quoted-printable"))
1011  'quoted-printable-ccl-write-decoded-region)
1012
1013
1014 ;;; @ Q
1015 ;;;
1016
1017   (cond
1018    ((eval-when-compile
1019       (and (eq emacs-major-version 23)
1020            (eq emacs-minor-version 1)))
1021     (defun q-encoding-ccl-encode-string (string &optional mode)
1022       "Encode STRING to Q-encoding of encoded-word, and return the result.
1023 MODE allows `text', `comment', `phrase' or nil.  Default value is
1024 `phrase'."
1025       (ccl-execute-on-string
1026        (cond
1027         ((eq mode 'text) 'mel-ccl-encode-uq)
1028         ((eq mode 'comment) 'mel-ccl-encode-cq)
1029         (t 'mel-ccl-encode-pq))
1030        (make-vector 9 0) string nil t))
1031
1032     (defun q-encoding-ccl-decode-string (string)
1033       "Decode Q encoded STRING and return the result."
1034       (ccl-execute-on-string 'mel-ccl-decode-q
1035                              (make-vector 9 0) string nil t)))
1036    (t
1037     (defun q-encoding-ccl-encode-string (string &optional mode)
1038       "Encode STRING to Q-encoding of encoded-word, and return the result.
1039 MODE allows `text', `comment', `phrase' or nil.  Default value is
1040 `phrase'."
1041       (decode-coding-string
1042        string
1043        (cond
1044         ((eq mode 'text) 'mel-ccl-uq-rev)
1045         ((eq mode 'comment) 'mel-ccl-cq-rev)
1046         (t 'mel-ccl-pq-rev))))
1047
1048     (defun q-encoding-ccl-decode-string (string)
1049       "Decode Q encoded STRING and return the result."
1050       (encode-coding-string
1051        string
1052        'mel-ccl-uq-rev))))
1053
1054 (unless (featurep 'xemacs)
1055   (defun q-encoding-ccl-encoded-length (string &optional mode)
1056     (let ((status [nil nil nil nil nil nil nil nil nil]))
1057       (fillarray status nil)            ; XXX: Is this necessary?
1058       (ccl-execute-on-string
1059        (cond
1060         ((eq mode 'text) 'mel-ccl-count-uq)
1061         ((eq mode 'comment) 'mel-ccl-count-cq)
1062         (t 'mel-ccl-count-pq))
1063        status
1064        string)
1065       (aref status 0)))
1066   )
1067
1068 (mel-define-method-function (encoded-text-encode-string string (nil "Q"))
1069                             'q-encoding-ccl-encode-string)
1070
1071 (mel-define-method encoded-text-decode-string (string (nil "Q"))
1072   (if (string-match (eval-when-compile
1073                       (concat "\\`" Q-encoded-text-regexp "\\'"))
1074                     string)
1075       (q-encoding-ccl-decode-string string)
1076     (error "Invalid encoded-text %s" string)))
1077
1078
1079 ;;; @ end
1080 ;;;
1081
1082 (provide 'mel-q-ccl)
1083
1084 ;;; mel-q-ccl.el ends here.