1 ;;; tl-822.el --- RFC 822 parser for GNU Emacs
3 ;; Copyright (C) 1995,1996 Free Software Foundation, Inc.
5 ;; Author: MORIOKA Tomohiko <morioka@jaist.ac.jp>
6 ;; Keywords: mail, news, RFC 822
8 ;; This file is part of tl (Tiny Library).
10 ;; This program is free software; you can redistribute it and/or
11 ;; modify it under the terms of the GNU General Public License as
12 ;; published by the Free Software Foundation; either version 2, or (at
13 ;; your option) any later version.
15 ;; This program is distributed in the hope that it will be useful, but
16 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 ;; General Public License for more details.
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with This program; see the file COPYING. If not, write to
22 ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 ;; Boston, MA 02111-1307, USA.
32 (defconst rfc822/RCS-ID
33 "$Id: tl-822.el,v 7.42 1996-08-28 13:10:17 morioka Exp $")
34 (defconst rfc822/version (get-version-string rfc822/RCS-ID))
40 (defalias 'rfc822/narrow-to-header 'std11-narrow-to-header)
41 (defalias 'rfc822/get-header-string 'std11-header-string)
42 (defalias 'rfc822/get-header-string-except 'std11-header-string-except)
48 (defalias `rfc822/field-end 'std11-field-end)
49 (defalias 'rfc822/get-field-body 'std11-field-body)
50 (defalias 'rfc822/get-field-names 'std11-field-names)
51 (defalias 'rfc822/get-field-bodies 'std11-field-bodies)
57 (defconst rfc822/linear-white-space-regexp "\\(\n?[ \t]\\)+")
58 (defconst rfc822/quoted-pair-regexp "\\\\.")
59 (defconst rfc822/non-qtext-char-list '(?\" ?\\ ?\r ?\n))
60 (defconst rfc822/qtext-regexp
61 (concat "[^" (char-list-to-string rfc822/non-qtext-char-list) "]"))
62 (defconst rfc822/quoted-string-regexp
65 (regexp-or rfc822/qtext-regexp rfc822/quoted-pair-regexp)
69 (defun rfc822/wrap-as-quoted-string (str)
70 "Wrap string STR as RFC 822 quoted-string. [tl-822.el]"
74 (if (memq chr rfc822/non-qtext-char-list)
75 (concat "\\" (char-to-string chr))
81 (defun rfc822/strip-quoted-pair (str)
87 (setq chr (elt str i))
88 (if (or flag (not (eq chr ?\\)))
90 (setq dest (concat dest (char-to-string chr)))
99 (defun rfc822/strip-quoted-string (str)
100 (rfc822/strip-quoted-pair
101 (let ((max (- (length str) 1))
103 (if (and (eq (elt str 0) ?\")
104 (eq (elt str max) ?\")
106 (substring str 1 max)
114 (defun rfc822/unfolding-string (str)
116 (while (string-match "\n\\s +" str)
117 (setq dest (concat dest (substring str 0 (match-beginning 0)) " "))
118 (setq str (substring str (match-end 0)))
124 ;;; @ lexical analyze
127 (defconst rfc822/special-chars "][()<>@,;:\\<>.\"")
128 (defconst rfc822/space-chars " \t\n")
129 (defconst rfc822/non-atom-chars
130 (concat rfc822/special-chars rfc822/space-chars))
131 (defconst rfc822/non-dtext-chars "][")
132 (defconst rfc822/non-ctext-chars "()")
134 (defun rfc822/analyze-spaces (str)
135 (let ((i (string-match (concat "[^" rfc822/space-chars "]") str)))
138 (cons (cons 'spaces (substring str 0 i))
141 (if (not (string-equal str ""))
142 (cons (cons 'spaces str) "")
145 (defun rfc822/analyze-special (str)
146 (if (and (> (length str) 0)
147 (find (elt str 0) rfc822/special-chars)
149 (cons (cons 'specials (substring str 0 1))
154 (defun rfc822/analyze-atom (str)
155 (let ((i (string-match (concat "[" rfc822/non-atom-chars "]") str)))
158 (cons (cons 'atom (substring str 0 i))
161 (if (not (string-equal str ""))
162 (cons (cons 'spaces str) "")
165 (defun rfc822/analyze-quoted-string (str)
166 (let ((len (length str)))
170 (let ((i 1) chr dest)
173 (setq chr (aref str i))
179 (setq dest (concat dest (char-to-string (aref str i))))
183 (cons (cons 'quoted-string dest)
184 (substring str (1+ i)))
188 (setq dest (concat dest (char-to-string (aref str i))))
193 (defun rfc822/analyze-domain-literal (str)
194 (if (and (> (length str) 0)
195 (eq (aref str 0) ?\[)
197 (let* ((i (string-match (concat "[" rfc822/non-dtext-chars "]") str 1))
198 (rest (and i (substring str i)))
202 (eq (aref rest 0) ?\])
204 (cons (cons 'domain-literal (substring str 1 i))
209 (defun rfc822/analyze-comment (str)
210 (if (and (> (length str) 0)
215 (setq str (substring str 1))
217 (while (not (string-equal str ""))
218 (setq p (string-match (concat "[" rfc822/non-ctext-chars "]") str))
220 (setq dest (concat dest (substring str 0 p)))
221 (setq str (substring str p))
223 ((setq ret (rfc822/analyze-comment str))
224 (setq dest (concat dest "(" (cdr (car ret)) ")"))
230 (if (and (> (length str) 0)
233 (cons (cons 'comment dest)
238 (defun rfc822/lexical-analyze (str)
240 (while (not (string-equal str ""))
242 (or (rfc822/analyze-quoted-string str)
243 (rfc822/analyze-domain-literal str)
244 (rfc822/analyze-comment str)
245 (rfc822/analyze-spaces str)
246 (rfc822/analyze-special str)
247 (rfc822/analyze-atom str)
250 (setq dest (cons (car ret) dest))
260 (defun rfc822/ignored-token-p (token)
261 (let ((type (car token)))
262 (or (eq type 'spaces)(eq type 'comment))
265 (defun rfc822/parse-token (lal)
269 (setq token (car lal))
270 (rfc822/ignored-token-p token)
273 (setq itl (cons token itl))
275 (cons (nreverse (cons token itl))
279 (defun rfc822/parse-ascii-token (lal)
280 (let (token itl parsed token-value)
282 (setq token (car lal))
283 (if (and (setq token-value (cdr token))
284 (find-charset-string token-value)
287 (rfc822/ignored-token-p token)
290 (setq itl (cons token itl))
293 (setq parsed (nreverse (cons token itl)))
295 (cons parsed (cdr lal))
298 (defun rfc822/parse-token-or-comment (lal)
302 (setq token (car lal))
303 (eq (car token) 'spaces)
306 (setq itl (cons token itl))
308 (cons (nreverse (cons token itl))
312 (defun rfc822/parse-word (lal)
313 (let ((ret (rfc822/parse-ascii-token lal)))
315 (let ((elt (car ret))
318 (if (or (assq 'atom elt)
319 (assq 'quoted-string elt))
320 (cons (cons 'word elt) rest)
323 (defun rfc822/parse-word-or-comment (lal)
324 (let ((ret (rfc822/parse-token-or-comment lal)))
326 (let ((elt (car ret))
329 (cond ((or (assq 'atom elt)
330 (assq 'quoted-string elt))
331 (cons (cons 'word elt) rest)
334 (cons (cons 'comment-word elt) rest)
338 (defun rfc822/parse-phrase (lal)
340 (while (setq ret (rfc822/parse-word-or-comment lal))
341 (setq phrase (append phrase (cdr (car ret))))
345 (cons (cons 'phrase phrase) lal)
348 (defun rfc822/parse-local-part (lal)
349 (let ((ret (rfc822/parse-word lal)))
351 (let ((local-part (cdr (car ret))) dot)
353 (while (and (setq ret (rfc822/parse-ascii-token lal))
355 (string-equal (cdr (assq 'specials dot)) ".")
356 (setq ret (rfc822/parse-word (cdr ret)))
358 (append local-part dot (cdr (car ret)))
362 (cons (cons 'local-part local-part) lal)
365 (defun rfc822/parse-sub-domain (lal)
366 (let ((ret (rfc822/parse-ascii-token lal)))
368 (let ((sub-domain (car ret)))
369 (if (or (assq 'atom sub-domain)
370 (assq 'domain-literal sub-domain)
372 (cons (cons 'sub-domain sub-domain)
377 (defun rfc822/parse-domain (lal)
378 (let ((ret (rfc822/parse-sub-domain lal)))
380 (let ((domain (cdr (car ret))) dot)
382 (while (and (setq ret (rfc822/parse-ascii-token lal))
384 (string-equal (cdr (assq 'specials dot)) ".")
385 (setq ret (rfc822/parse-sub-domain (cdr ret)))
387 (append domain dot (cdr (car ret)))
391 (cons (cons 'domain domain) lal)
394 (defun rfc822/parse-at-domain (lal)
395 (let ((ret (rfc822/parse-ascii-token lal)) at-sign)
397 (setq at-sign (car ret))
398 (string-equal (cdr (assq 'specials at-sign)) "@")
399 (setq ret (rfc822/parse-domain (cdr ret)))
401 (cons (cons 'at-domain (append at-sign (cdr (car ret))))
405 (defun rfc822/parse-addr-spec (lal)
406 (let ((ret (rfc822/parse-local-part lal))
410 (setq addr (cdr (car ret)))
412 (and (setq ret (rfc822/parse-at-domain lal))
413 (setq addr (append addr (cdr (car ret))))
416 (cons (cons 'addr-spec addr) lal)
419 (defun rfc822/parse-route (lal)
420 (let ((ret (rfc822/parse-at-domain lal))
424 (setq route (cdr (car ret)))
426 (while (and (setq ret (rfc822/parse-ascii-token lal))
427 (setq comma (car ret))
428 (string-equal (cdr (assq 'specials comma)) ",")
429 (setq ret (rfc822/parse-at-domain (cdr ret)))
431 (setq route (append route comma (cdr (car ret))))
434 (and (setq ret (rfc822/parse-ascii-token lal))
435 (setq colon (car ret))
436 (string-equal (cdr (assq 'specials colon)) ":")
437 (setq route (append route colon))
440 (cons (cons 'route route)
445 (defun rfc822/parse-route-addr (lal)
446 (let ((ret (rfc822/parse-ascii-token lal))
450 (string-equal (cdr (assq 'specials <)) "<")
452 (progn (and (setq ret (rfc822/parse-route lal))
453 (setq route (cdr (car ret)))
456 (setq ret (rfc822/parse-addr-spec lal))
458 (setq addr-spec (cdr (car ret)))
460 (setq ret (rfc822/parse-ascii-token lal))
462 (string-equal (cdr (assq 'specials >)) ">")
464 (cons (cons 'route-addr (append route addr-spec))
469 (defun rfc822/parse-phrase-route-addr (lal)
470 (let ((ret (rfc822/parse-phrase lal)) phrase)
473 (setq phrase (cdr (car ret)))
476 (if (setq ret (rfc822/parse-route-addr lal))
477 (cons (list 'phrase-route-addr
483 (defun rfc822/parse-mailbox (lal)
484 (let ((ret (or (rfc822/parse-phrase-route-addr lal)
485 (rfc822/parse-addr-spec lal)))
489 (setq mbox (car ret))
491 (if (and (setq ret (rfc822/parse-token-or-comment lal))
492 (setq comment (cdr (assq 'comment (car ret))))
496 (cons (list 'mailbox mbox comment)
500 (defun rfc822/parse-group (lal)
501 (let ((ret (rfc822/parse-phrase lal))
502 phrase colon comma mbox semicolon)
504 (setq phrase (cdr (car ret)))
506 (setq ret (rfc822/parse-ascii-token lal))
507 (setq colon (car ret))
508 (string-equal (cdr (assq 'specials colon)) ":")
511 (and (setq ret (rfc822/parse-mailbox lal))
512 (setq mbox (list (car ret)))
515 (while (and (setq ret (rfc822/parse-ascii-token lal))
516 (setq comma (car ret))
518 (cdr (assq 'specials comma)) ",")
520 (setq ret (rfc822/parse-mailbox lal))
521 (setq mbox (cons (car ret) mbox))
525 (and (setq ret (rfc822/parse-ascii-token lal))
526 (setq semicolon (car ret))
527 (string-equal (cdr (assq 'specials semicolon)) ";")
529 (cons (list 'group phrase (nreverse mbox))
534 (defun rfc822/parse-address (lal)
535 (or (rfc822/parse-group lal)
536 (rfc822/parse-mailbox lal)
539 (defun rfc822/parse-addresses (lal)
540 (let ((ret (rfc822/parse-address lal)))
542 (let ((dest (list (car ret))))
544 (while (and (setq ret (rfc822/parse-ascii-token lal))
545 (string-equal (cdr (assq 'specials (car ret))) ",")
546 (setq ret (rfc822/parse-address (cdr ret)))
548 (setq dest (cons (car ret) dest))
554 (defun rfc822/addr-to-string (seq)
557 (if (eq (car token) 'spaces)
564 (defun rfc822/address-string (address)
565 (cond ((eq (car address) 'group)
566 (mapconcat (function rfc822/address-string)
570 ((eq (car address) 'mailbox)
571 (let ((addr (nth 1 address)))
572 (rfc822/addr-to-string
573 (if (eq (car addr) 'phrase-route-addr)
579 (defun rfc822/full-name-string (address)
580 (cond ((eq (car address) 'group)
587 ((eq (car address) 'mailbox)
588 (let ((addr (nth 1 address))
589 (comment (nth 2 address))
591 (if (eq (car addr) 'phrase-route-addr)
592 (setq phrase (mapconcat (function
601 (defun rfc822/extract-address-components (str)
602 "Extract full name and canonical address from STR.
603 Returns a list of the form (FULL-NAME CANONICAL-ADDRESS).
604 If no name can be extracted, FULL-NAME will be nil. [tl-822.el]"
605 (let* ((structure (car
606 (rfc822/parse-address
607 (rfc822/lexical-analyze str)
609 (phrase (rfc822/full-name-string structure))
610 (address (rfc822/address-string structure))
612 (list phrase address)
621 ;;; tl-822.el ends here