* Sync up to flim-1_10_5 from flim-1_10_2.
[elisp/flim.git] / eword-decode.el
1 ;;; eword-decode.el --- RFC 2047 based encoded-word decoder for GNU Emacs
2
3 ;; Copyright (C) 1995,1996,1997,1998 Free Software Foundation, Inc.
4
5 ;; Author: ENAMI Tsugutomo <enami@sys.ptg.sony.co.jp>
6 ;;         MORIOKA Tomohiko <morioka@jaist.ac.jp>
7 ;;         Tanaka Akira <akr@jaist.ac.jp>
8 ;; Maintainer: Tanaka Akira <akr@jaist.ac.jp>
9 ;; Created: 1995/10/03
10 ;; Original: 1992/07/20 ENAMI Tsugutomo's `mime.el'.
11 ;;      Renamed: 1993/06/03 to tiny-mime.el
12 ;;      Renamed: 1995/10/03 from tiny-mime.el (split off encoder)
13 ;;      Renamed: 1997/02/22 from tm-ew-d.el
14 ;; Keywords: encoded-word, MIME, multilingual, header, mail, news
15
16 ;; This file is part of FLAM (Faithful Library About MIME).
17
18 ;; This program is free software; you can redistribute it and/or
19 ;; modify it under the terms of the GNU General Public License as
20 ;; published by the Free Software Foundation; either version 2, or (at
21 ;; your option) any later version.
22
23 ;; This program is distributed in the hope that it will be useful, but
24 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
25 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
26 ;; General Public License for more details.
27
28 ;; You should have received a copy of the GNU General Public License
29 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
30 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
31 ;; Boston, MA 02111-1307, USA.
32
33 ;;; Code:
34
35 (require 'std11)
36 (require 'mel)
37 (require 'mime-def)
38
39 (require 'ew-dec)
40
41 (defgroup eword-decode nil
42   "Encoded-word decoding"
43   :group 'mime)
44
45 ;;; TEST
46
47 (defvar rotate-memo nil)
48 (defmacro rotate-memo (var val)
49   `(when rotate-memo
50      (unless (boundp ',var) (setq ,var ()))
51      (setq ,var (cons ,val ,var))
52      (let ((tmp (last ,var (- (length ,var) 100))))
53        (when tmp (setcdr tmp nil)))
54      ,var))
55
56 ;;; @ variables
57 ;;;
58
59 (defcustom eword-decode-sticked-encoded-word nil
60   "*If non-nil, decode encoded-words sticked on atoms,
61 other encoded-words, etc.
62 however this behaviour violates RFC2047."
63   :group 'eword-decode
64   :type 'boolean)
65
66 (defcustom eword-decode-quoted-encoded-word nil
67   "*If non-nil, decode encoded-words in quoted-string
68 however this behaviour violates RFC2047."
69   :group 'eword-decode
70   :type 'boolean)
71
72 (defcustom eword-max-size-to-decode 1000
73   "*Max size to decode header field."
74   :group 'eword-decode
75   :type '(choice (integer :tag "Limit (bytes)")
76                  (const :tag "Don't limit" nil)))
77
78
79 ;;; @ MIME encoded-word definition
80 ;;;
81
82 (defconst eword-encoded-word-prefix-regexp
83   (concat (regexp-quote "=?")
84           "\\(" mime-charset-regexp "\\)"
85           (regexp-quote "?")
86           "\\(B\\|Q\\)"
87           (regexp-quote "?")))
88 (defconst eword-encoded-word-suffix-regexp
89   (regexp-quote "?="))
90
91 (defconst eword-encoded-text-in-unstructured-regexp "[!->@-~]+")
92 (defconst eword-encoded-word-in-unstructured-regexp
93   (concat eword-encoded-word-prefix-regexp
94           "\\(" eword-encoded-text-in-unstructured-regexp "\\)"
95           eword-encoded-word-suffix-regexp))
96 (defconst eword-after-encoded-word-in-unstructured-regexp "\\([ \t]\\|$\\)")
97
98 (defconst eword-encoded-text-in-phrase-regexp "[-A-Za-z0-9!*+/=_]+")
99 (defconst eword-encoded-word-in-phrase-regexp
100   (concat eword-encoded-word-prefix-regexp
101           "\\(" eword-encoded-text-in-phrase-regexp "\\)"
102           eword-encoded-word-suffix-regexp))
103 (defconst eword-after-encoded-word-in-phrase-regexp "\\([ \t]\\|$\\)")
104
105 (defconst eword-encoded-text-in-comment-regexp "[]!-'*->@-[^-~]+")
106 (defconst eword-encoded-word-in-comment-regexp
107   (concat eword-encoded-word-prefix-regexp
108           "\\(" eword-encoded-text-in-comment-regexp "\\)"
109           eword-encoded-word-suffix-regexp))
110 (defconst eword-after-encoded-word-in-comment-regexp "\\([ \t()\\\\]\\|$\\)")
111
112 (defconst eword-encoded-text-in-quoted-string-regexp "[]!#->@-[^-~]+")
113 (defconst eword-encoded-word-in-quoted-string-regexp
114   (concat eword-encoded-word-prefix-regexp
115           "\\(" eword-encoded-text-in-quoted-string-regexp "\\)"
116           eword-encoded-word-suffix-regexp))
117 (defconst eword-after-encoded-word-in-quoted-string-regexp "\\([ \t\"\\\\]\\|$\\)")
118
119 ; obsolete
120 (defconst eword-encoded-text-regexp eword-encoded-text-in-unstructured-regexp)
121 (defconst eword-encoded-word-regexp eword-encoded-word-in-unstructured-regexp)
122
123
124 ;;; @ internal utilities
125 ;;;
126
127 (defun eword-decode-first-encoded-words (string
128                                          eword-regexp
129                                          after-regexp
130                                          &optional must-unfold)
131   "Decode MIME encoded-words in beginning of STRING.
132
133 EWORD-REGEXP is the regexp that matches a encoded-word.
134 Usual value is
135 eword-encoded-word-in-unstructured-regexp, 
136 eword-encoded-text-in-phrase-regexp,
137 eword-encoded-word-in-comment-regexp or
138 eword-encoded-word-in-quoted-string-regexp.
139
140 AFTER-REGEXP is the regexp that matches a after encoded-word.
141 Usual value is
142 eword-after-encoded-word-in-unstructured-regexp, 
143 eword-after-encoded-text-in-phrase-regexp,
144 eword-after-encoded-word-in-comment-regexp or
145 eword-after-encoded-word-in-quoted-string-regexp.
146
147 If beginning of STRING matches EWORD-REGEXP with AFTER-REGEXP,
148 returns a cons cell of decoded string(sequence of characters) and 
149 the rest(sequence of octets).
150
151 If beginning of STRING does not matches EWORD-REGEXP and AFTER-REGEXP,
152 returns nil.
153
154 If an encoded-word is broken or your emacs implementation can not
155 decode the charset included in it, it is returned in decoded part
156 as encoded-word form.
157
158 If MUST-UNFOLD is non-nil, it unfolds and eliminates line-breaks even
159 if there are in decoded encoded-words (generated by bad manner MUA
160 such as a version of Net$cape)."
161   (if eword-decode-sticked-encoded-word (setq after-regexp ""))
162   (let* ((between-ewords-regexp
163            (if eword-decode-sticked-encoded-word
164              "\\(\n?[ \t]\\)*"
165              "\\(\n?[ \t]\\)+"))
166          (between-ewords-eword-after-regexp
167            (concat "\\`\\(" between-ewords-regexp "\\)"
168                       "\\(" eword-regexp "\\)"
169                       after-regexp))
170          (eword-after-regexp
171            (concat "\\`\\(" eword-regexp "\\)" after-regexp))
172          (src string)   ; sequence of octets.
173          (dst ""))      ; sequence of characters.
174     (if (string-match eword-after-regexp src)
175       (let* (p
176              (q (match-end 1))
177              (ew (substring src 0 q))
178              (dw (eword-decode-encoded-word ew must-unfold)))
179         (setq dst (concat dst dw)
180               src (substring src q))
181         (if (not (string= ew dw))
182           (progn
183             (while
184               (and
185                 (string-match between-ewords-eword-after-regexp src)
186                 (progn
187                   (setq p (match-end 1)
188                         q (match-end 3)
189                         ew (substring src p q)
190                         dw (eword-decode-encoded-word ew must-unfold))
191                   (if (string= ew dw)
192                     (progn
193                       (setq dst (concat dst (substring src 0 q))
194                             src (substring src q))
195                       nil)
196                     t)))
197               (setq dst (concat dst dw)
198                     src (substring src q)))))
199         (cons dst src))
200       nil)))
201
202 (defun eword-decode-entire-string (string
203                                    eword-regexp
204                                    after-regexp
205                                    safe-regexp
206                                    escape ; ?\\ or nil.
207                                    delimiters ; list of chars.
208                                    chars-must-be-quote
209                                    must-unfold
210                                    code-conversion)
211   (if (and code-conversion
212            (not (mime-charset-to-coding-system code-conversion)))
213       (setq code-conversion default-mime-charset))
214   (let ((equal-safe-regexp (concat "\\`=?" safe-regexp))
215         (dst "")
216         (buf "")
217         (src string)
218         (ew-enable t))
219     (while (< 0 (length src))
220       (let ((ch (aref src 0))
221             (decoded (and
222                         ew-enable
223                         (eword-decode-first-encoded-words src
224                           eword-regexp after-regexp must-unfold))))
225         (if (and (not (string= buf ""))
226                  (or decoded (memq ch delimiters)))
227           (setq dst (concat dst
228                       (std11-wrap-as-quoted-pairs
229                         (decode-mime-charset-string buf code-conversion)
230                         chars-must-be-quote))
231                 buf ""))
232         (cond
233           (decoded
234             (setq dst (concat dst
235                         (std11-wrap-as-quoted-pairs
236                           (car decoded)
237                           chars-must-be-quote))
238                   src (cdr decoded)))
239           ((memq ch delimiters)
240             (setq dst (concat dst (list ch))
241                   src (substring src 1)
242                   ew-enable t))
243           ((eq ch escape)
244             (setq buf (concat buf (list (aref src 1)))
245                   src (substring src 2)
246                   ew-enable t))
247           ((string-match "\\`[ \t\n]+" src)
248             (setq buf (concat buf (substring src 0 (match-end 0)))
249                   src (substring src (match-end 0))
250                   ew-enable t))
251           ((and (string-match equal-safe-regexp src)
252                 (< 0 (match-end 0)))
253             (setq buf (concat buf (substring src 0 (match-end 0)))
254                   src (substring src (match-end 0))
255                   ew-enable eword-decode-sticked-encoded-word))
256           (t (error "something wrong")))))
257     (if (not (string= buf ""))
258       (setq dst (concat dst
259                   (std11-wrap-as-quoted-pairs
260                     (decode-mime-charset-string buf code-conversion)
261                     chars-must-be-quote))))
262     dst))
263
264
265 ;;; @ for string
266 ;;;
267
268 (defun eword-decode-unstructured (string code-conversion &optional must-unfold)
269   (eword-decode-entire-string
270     string
271     eword-encoded-word-in-unstructured-regexp
272     eword-after-encoded-word-in-unstructured-regexp
273     "[^ \t\n=]*"
274     nil
275     nil
276     nil
277     must-unfold
278     code-conversion))
279
280 (defun eword-decode-comment (string code-conversion &optional must-unfold)
281   (eword-decode-entire-string
282     string
283     eword-encoded-word-in-comment-regexp
284     eword-after-encoded-word-in-comment-regexp
285     "[^ \t\n()\\\\=]*"
286     ?\\
287     '(?\( ?\))
288     '(?\( ?\) ?\\ ?\r ?\n)
289     must-unfold
290     code-conversion))
291
292 (defun eword-decode-quoted-string (string code-conversion &optional must-unfold)
293   (eword-decode-entire-string
294     string
295     eword-encoded-word-in-quoted-string-regexp
296     eword-after-encoded-word-in-quoted-string-regexp
297     "[^ \t\n\"\\\\=]*"
298     ?\\
299     '(?\")
300     '(?\" ?\\ ?\r ?\n)
301     must-unfold
302     code-conversion))
303
304 (defun eword-decode-string (string &optional must-unfold code-conversion)
305   "Decode MIME encoded-words in STRING.
306
307 STRING is unfolded before decoding.
308
309 If an encoded-word is broken or your emacs implementation can not
310 decode the charset included in it, it is not decoded.
311
312 If MUST-UNFOLD is non-nil, it unfolds and eliminates line-breaks even
313 if there are in decoded encoded-words (generated by bad manner MUA
314 such as a version of Net$cape).
315
316 If CODE-CONVERSION is nil, it decodes only encoded-words.  If it is
317 mime-charset, it decodes non-ASCII bit patterns as the mime-charset.
318 Otherwise it decodes non-ASCII bit patterns as the
319 default-mime-charset."
320   (eword-decode-unstructured
321     (std11-unfold-string string)
322     code-conversion
323     must-unfold))
324
325 (defun eword-decode-and-fold-structured-field
326   (string start-column &optional max-column must-unfold)
327   "Decode and fold (fill) STRING as structured field body.
328 It decodes non us-ascii characters in FULL-NAME encoded as
329 encoded-words or invalid \"raw\" string.  \"Raw\" non us-ascii
330 characters are regarded as variable `default-mime-charset'.
331
332 If an encoded-word is broken or your emacs implementation can not
333 decode the charset included in it, it is not decoded.
334
335 If MAX-COLUMN is omitted, `fill-column' is used.
336
337 If MUST-UNFOLD is non-nil, it unfolds and eliminates line-breaks even
338 if there are in decoded encoded-words (generated by bad manner MUA
339 such as a version of Net$cape)."
340   (rotate-memo args-eword-decode-and-fold-structured-field
341                (list string start-column max-column must-unfold))
342   (or max-column
343       (setq max-column fill-column))
344   (let* ((field-name (make-string (1- start-column) ?X))
345          (field-body (ew-lf-crlf-to-crlf string))
346          (ew-decode-field-default-syntax '(ew-scan-unibyte-std11))
347          (decoded (ew-decode-field field-name field-body)))
348     (unless (equal field-body decoded)
349       (setq decoded (ew-crlf-refold decoded start-column max-column)))
350     (ew-crlf-to-lf decoded)))
351
352 (defun eword-decode-and-unfold-structured-field (string)
353   "Decode and unfold STRING as structured field body.
354 It decodes non us-ascii characters in FULL-NAME encoded as
355 encoded-words or invalid \"raw\" string.  \"Raw\" non us-ascii
356 characters are regarded as variable `default-mime-charset'.
357
358 If an encoded-word is broken or your emacs implementation can not
359 decode the charset included in it, it is not decoded."
360   (rotate-memo args-eword-decode-and-unfold-structured-field (list string))
361   (let* ((ew-decode-field-default-syntax '(ew-scan-unibyte-std11))
362          (decoded (ew-decode-field "" (ew-lf-crlf-to-crlf string))))
363     (ew-crlf-to-lf (ew-crlf-unfold decoded))))
364
365 (defun eword-decode-structured-field-body (string &optional must-unfold
366                                                   start-column max-column)
367   "Decode non us-ascii characters in STRING as structured field body.
368 STRING is unfolded before decoding.
369
370 It decodes non us-ascii characters in FULL-NAME encoded as
371 encoded-words or invalid \"raw\" string.  \"Raw\" non us-ascii
372 characters are regarded as variable `default-mime-charset'.
373
374 If an encoded-word is broken or your emacs implementation can not
375 decode the charset included in it, it is not decoded.
376
377 If MUST-UNFOLD is non-nil, it unfolds and eliminates line-breaks even
378 if there are in decoded encoded-words (generated by bad manner MUA
379 such as a version of Net$cape)."
380   (rotate-memo args-eword-decode-structured-field-body
381                (list string must-unfold start-column max-column))
382   (if start-column
383       ;; fold with max-column
384       (eword-decode-and-fold-structured-field
385        string start-column max-column must-unfold)
386     ;; Don't fold
387     (let* ((ew-decode-field-default-syntax '(ew-scan-unibyte-std11))
388            (decoded (ew-decode-field "" (ew-lf-crlf-to-crlf string))))
389       (ew-crlf-to-lf decoded))))
390
391 (defun eword-decode-unstructured-field-body (string &optional must-unfold)
392   "Decode non us-ascii characters in STRING as unstructured field body.
393 STRING is unfolded before decoding.
394
395 It decodes non us-ascii characters in FULL-NAME encoded as
396 encoded-words or invalid \"raw\" string.  \"Raw\" non us-ascii
397 characters are regarded as variable `default-mime-charset'.
398
399 If an encoded-word is broken or your emacs implementation can not
400 decode the charset included in it, it is not decoded.
401
402 If MUST-UNFOLD is non-nil, it unfolds and eliminates line-breaks even
403 if there are in decoded encoded-words (generated by bad manner MUA
404 such as a version of Net$cape)."
405   (rotate-memo args-eword-decode-unstructured-field-body
406                (list string must-unfold))
407   (let ((decoded (ew-decode-field "" (ew-lf-crlf-to-crlf string))))
408     (ew-crlf-to-lf (ew-crlf-unfold decoded))))
409
410
411 ;;; @ for region
412 ;;;
413
414 (defun eword-decode-region (start end &optional unfolding must-unfold
415                                                 code-conversion)
416   "Decode MIME encoded-words in region between START and END.
417
418 If UNFOLDING is not nil, it unfolds before decoding.
419
420 If MUST-UNFOLD is non-nil, it unfolds and eliminates line-breaks even
421 if there are in decoded encoded-words (generated by bad manner MUA
422 such as a version of Net$cape).
423
424 If CODE-CONVERSION is nil, it decodes only encoded-words.  If it is
425 mime-charset, it decodes non-ASCII bit patterns as the mime-charset.
426 Otherwise it decodes non-ASCII bit patterns as the
427 default-mime-charset."
428   (interactive "*r")
429   (rotate-memo args-eword-decode-region
430                (list start end (buffer-substring start end) unfolding must-unfold code-conversion))
431   (save-excursion
432     (save-restriction
433       (narrow-to-region start end)
434       (if unfolding
435           (eword-decode-unfold)
436         )
437       (let ((str (eword-decode-unstructured
438                    (buffer-substring (point-min) (point-max))
439                    code-conversion
440                    must-unfold)))
441         (delete-region (point-min) (point-max))
442         (insert str)))))
443
444
445 ;;; @ for message header
446 ;;;
447
448 (defcustom eword-decode-ignored-field-list
449   '(Newsgroups Path Lines Nntp-Posting-Host Received Message-Id Date)
450   "*List of field-names to be ignored when decoding.
451 Each field name must be symbol."
452   :group 'eword-decode
453   :type '(repeat symbol))
454
455 (defcustom eword-decode-structured-field-list
456   '(Reply-To Resent-Reply-To From Resent-From Sender Resent-Sender
457              To Resent-To Cc Resent-Cc Bcc Resent-Bcc Dcc
458              Mail-Followup-To
459              Mime-Version Content-Type Content-Transfer-Encoding
460              Content-Disposition User-Agent)
461   "*List of field-names to decode as structured field.
462 Each field name must be symbol."
463   :group 'eword-decode
464   :type '(repeat symbol))
465
466 (defun eword-decode-header (&optional code-conversion separator)
467   "Decode MIME encoded-words in header fields.
468 If CODE-CONVERSION is nil, it decodes only encoded-words.  If it is
469 mime-charset, it decodes non-ASCII bit patterns as the mime-charset.
470 Otherwise it decodes non-ASCII bit patterns as the
471 default-mime-charset.
472 If SEPARATOR is not nil, it is used as header separator."
473   (interactive "*")
474   (rotate-memo args-eword-decode-header (list code-conversion))
475   (unless code-conversion
476     (message "eword-decode-header is called without code-conversion")
477     (sit-for 2))
478   (if (and code-conversion
479            (not (mime-charset-to-coding-system code-conversion)))
480       (setq code-conversion default-mime-charset))
481   (save-excursion
482     (save-restriction
483       (std11-narrow-to-header separator)
484       (rotate-memo args-h-eword-decode-header (buffer-substring (point-min) (point-max)))
485       (if code-conversion
486           (let (beg p end field-name field-body decoded)
487             (goto-char (point-min))
488             (while (re-search-forward std11-field-head-regexp nil t)
489               (setq beg (match-beginning 0)
490                     p (match-end 0)
491                     field-name (buffer-substring beg (1- p))
492                     end (std11-field-end)
493                     field-body (ew-lf-crlf-to-crlf
494                                 (buffer-substring p end))
495                     decoded (ew-decode-field
496                              field-name field-body))
497               (unless (equal field-body decoded)
498                 (setq decoded (ew-crlf-refold
499                                decoded
500                                (1+ (string-width field-name))
501                                fill-column)))
502               (delete-region p end)
503               (insert (ew-crlf-to-lf decoded))
504               (add-text-properties beg (min (1+ (point)) (point-max))
505                                    (list 'original-field-name field-name
506                                          'original-field-body field-body))
507               ))
508         (eword-decode-region (point-min) (point-max) t nil nil)
509         ))))
510
511 (defun eword-decode-unfold ()
512   (goto-char (point-min))
513   (let (field beg end)
514     (while (re-search-forward std11-field-head-regexp nil t)
515       (setq beg (match-beginning 0)
516             end (std11-field-end))
517       (setq field (buffer-substring beg end))
518       (if (string-match eword-encoded-word-regexp field)
519           (save-restriction
520             (narrow-to-region (goto-char beg) end)
521             (while (re-search-forward "\n\\([ \t]\\)" nil t)
522               (replace-match (match-string 1))
523               )
524             (goto-char (point-max))
525             ))
526       )))
527
528
529 ;;; @ encoded-word decoder
530 ;;;
531
532 (defvar eword-decode-encoded-word-error-handler
533   'eword-decode-encoded-word-default-error-handler)
534
535 (defvar eword-warning-face nil
536   "Face used for invalid encoded-word.")
537
538 (defun eword-decode-encoded-word-default-error-handler (word signal)
539   (and (add-text-properties 0 (length word)
540                             (and eword-warning-face
541                                  (list 'face eword-warning-face))
542                             word)
543        word))
544
545 (defun eword-decode-encoded-word (word &optional must-unfold)
546   "Decode WORD if it is an encoded-word.
547
548 If your emacs implementation can not decode the charset of WORD, it
549 returns WORD.  Similarly the encoded-word is broken, it returns WORD.
550
551 If MUST-UNFOLD is non-nil, it unfolds and eliminates line-breaks even
552 if there are in decoded encoded-word (generated by bad manner MUA such
553 as a version of Net$cape)."
554   (or (if (string-match eword-encoded-word-regexp word)
555           (let ((charset
556                  (substring word (match-beginning 1) (match-end 1))
557                  )
558                 (encoding
559                  (upcase
560                   (substring word (match-beginning 2) (match-end 2))
561                   ))
562                 (text
563                  (substring word (match-beginning 3) (match-end 3))
564                  ))
565             (condition-case err
566                 (eword-decode-encoded-text charset encoding text must-unfold)
567               (error
568                (funcall eword-decode-encoded-word-error-handler word err)
569                ))
570             ))
571       word))
572
573
574 ;;; @ encoded-text decoder
575 ;;;
576
577 (defun eword-decode-encoded-text (charset encoding string
578                                           &optional must-unfold)
579   "Decode STRING as an encoded-text.
580
581 If your emacs implementation can not decode CHARSET, it returns nil.
582
583 If ENCODING is not \"B\" or \"Q\", it occurs error.
584 So you should write error-handling code if you don't want break by errors.
585
586 If MUST-UNFOLD is non-nil, it unfolds and eliminates line-breaks even
587 if there are in decoded encoded-text (generated by bad manner MUA such
588 as a version of Net$cape)."
589   (let ((cs (mime-charset-to-coding-system charset)))
590     (if cs
591         (let ((dest (encoded-text-decode-string string encoding)))
592           (when dest
593             (setq dest (decode-mime-charset-string dest charset))
594             (if must-unfold
595                 (mapconcat (function
596                             (lambda (chr)
597                               (cond ((eq chr ?\n) "")
598                                     ((eq chr ?\t) " ")
599                                     (t (char-to-string chr)))
600                               ))
601                            (std11-unfold-string dest)
602                            "")
603               dest))))))
604
605
606 ;;; @ lexical analyze
607 ;;;
608
609 (defvar eword-lexical-analyze-cache nil)
610 (defvar eword-lexical-analyze-cache-max 299
611   "*Max position of eword-lexical-analyze-cache.
612 It is max size of eword-lexical-analyze-cache - 1.")
613
614 (defcustom eword-lexical-analyzers
615   '(eword-analyze-quoted-string
616     eword-analyze-domain-literal
617     eword-analyze-comment
618     eword-analyze-spaces
619     eword-analyze-special
620     eword-analyze-encoded-word
621     eword-analyze-atom)
622   "*List of functions to return result of lexical analyze.
623 Each function must have two arguments: STRING and MUST-UNFOLD.
624 STRING is the target string to be analyzed.
625 If MUST-UNFOLD is not nil, each function must unfold and eliminate
626 bare-CR and bare-LF from the result even if they are included in
627 content of the encoded-word.
628 Each function must return nil if it can not analyze STRING as its
629 format.
630
631 Previous function is preferred to next function.  If a function
632 returns nil, next function is used.  Otherwise the return value will
633 be the result."
634   :group 'eword-decode
635   :type '(repeat function))
636
637 (defun eword-analyze-quoted-string (string &optional must-unfold)
638   (let ((p (std11-check-enclosure string ?\" ?\")))
639     (if p
640         (cons (cons 'quoted-string
641                     (if eword-decode-quoted-encoded-word
642                         (eword-decode-quoted-string
643                           (substring string 0 p)
644                           default-mime-charset)
645                       (std11-wrap-as-quoted-string
646                        (decode-mime-charset-string
647                         (std11-strip-quoted-pair (substring string 1 (1- p)))
648                         default-mime-charset))))
649               (substring string p)))
650     ))
651
652 (defun eword-analyze-domain-literal (string &optional must-unfold)
653   (std11-analyze-domain-literal string))
654
655 (defun eword-analyze-comment (string &optional must-unfold)
656   (let ((len (length string)))
657     (if (and (< 0 len) (eq (aref string 0) ?\())
658         (let ((p 0))
659           (while (and p (< p len) (eq (aref string p) ?\())
660             (setq p (std11-check-enclosure string ?\( ?\) t p)))
661           (setq p (or p len))
662           (cons (cons 'comment
663                       (eword-decode-comment
664                         (std11-unfold-string (substring string 0 p))
665                         default-mime-charset))
666                 (substring string p)))
667       nil)))
668
669 (defun eword-analyze-spaces (string &optional must-unfold)
670   (std11-analyze-spaces string))
671
672 (defun eword-analyze-special (string &optional must-unfold)
673   (std11-analyze-special string))
674
675 (defun eword-analyze-encoded-word (string &optional must-unfold)
676   (let ((decoded (eword-decode-first-encoded-words
677                   string
678                   eword-encoded-word-in-phrase-regexp
679                   eword-after-encoded-word-in-phrase-regexp
680                   must-unfold)))
681     (if decoded
682         (let ((s (car decoded)))
683           (while (or (string-match std11-atom-regexp s)
684                      (string-match std11-spaces-regexp s))
685             (setq s (substring s (match-end 0))))
686           (if (= (length s) 0)
687               (cons (cons 'atom (car decoded)) (cdr decoded))
688             (cons (cons 'quoted-string
689                         (std11-wrap-as-quoted-string (car decoded)))
690                   (cdr decoded)))))))
691
692 (defun eword-analyze-atom (string &optional must-unfold)
693   (if (let ((enable-multibyte-characters nil))
694         (string-match std11-atom-regexp string))
695       (let ((end (match-end 0)))
696         (if (and eword-decode-sticked-encoded-word
697                  (string-match eword-encoded-word-in-phrase-regexp
698                                (substring string 0 end))
699                  (< 0 (match-beginning 0)))
700             (setq end (match-beginning 0)))
701         (cons (cons 'atom (decode-mime-charset-string
702                            (substring string 0 end)
703                            default-mime-charset))
704               (substring string end)
705               ))))
706
707 (defun eword-lexical-analyze-internal (string must-unfold)
708   (let ((last 'eword-analyze-spaces)
709         dest ret)
710     (while (not (string-equal string ""))
711       (setq ret
712             (let ((rest eword-lexical-analyzers)
713                   func r)
714               (while (and (setq func (car rest))
715                           (or
716                            (and
717                             (not eword-decode-sticked-encoded-word)
718                             (not (eq last 'eword-analyze-spaces))
719                             (eq func 'eword-analyze-encoded-word))
720                            (null (setq r (funcall func string must-unfold))))
721                           )
722                 (setq rest (cdr rest)))
723               (setq last func)
724               (or r `((error . ,string) . ""))
725               ))
726       (setq dest (cons (car ret) dest))
727       (setq string (cdr ret))
728       )
729     (nreverse dest)
730     ))
731
732 (defun eword-lexical-analyze (string &optional must-unfold)
733   "Return lexical analyzed list corresponding STRING.
734 It is like std11-lexical-analyze, but it decodes non us-ascii
735 characters encoded as encoded-words or invalid \"raw\" format.
736 \"Raw\" non us-ascii characters are regarded as variable
737 `default-mime-charset'."
738   (let* ((str (copy-sequence string))
739          (key (cons str (cons default-mime-charset must-unfold)))
740          ret)
741     (set-text-properties 0 (length str) nil str)
742     (if (setq ret (assoc key eword-lexical-analyze-cache))
743         (cdr ret)
744       (setq ret (eword-lexical-analyze-internal str must-unfold))
745       (setq eword-lexical-analyze-cache
746             (cons (cons key ret)
747                   (last eword-lexical-analyze-cache
748                         eword-lexical-analyze-cache-max)))
749       ret)))
750
751 (defun eword-decode-token (token)
752   (cdr token))
753
754 (defun eword-extract-address-components (string)
755   "Extract full name and canonical address from STRING.
756 Returns a list of the form (FULL-NAME CANONICAL-ADDRESS).
757 If no name can be extracted, FULL-NAME will be nil.
758 It decodes non us-ascii characters in FULL-NAME encoded as
759 encoded-words or invalid \"raw\" string.  \"Raw\" non us-ascii
760 characters are regarded as variable `default-mime-charset'."
761   (rotate-memo args-eword-extract-address-components (list string))
762   (let* ((structure (car (std11-parse-address
763                           (eword-lexical-analyze
764                            (std11-unfold-string string) 'must-unfold))))
765          (phrase  (std11-full-name-string structure))
766          (address (std11-address-string structure))
767          )
768     (list phrase address)
769     ))
770
771
772 ;;; @ end
773 ;;;
774
775 (provide 'eword-decode)
776
777 ;;; eword-decode.el ends here