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.54 1996-08-28 18:12:56 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)
43 (defalias 'rfc822/get-field-names 'std11-collect-field-names)
49 (defalias `rfc822/field-end 'std11-field-end)
50 (defalias 'rfc822/get-field-body 'std11-find-field-body)
51 (defalias 'rfc822/get-field-bodies 'std11-find-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 (defalias 'rfc822/unfolding-string 'std11-unfold-string)
117 ;;; @ lexical analyze
120 (defalias 'rfc822/analyze-spaces 'std11-analyze-spaces)
121 (defalias 'rfc822/analyze-special 'std11-analyze-special)
122 (defalias 'rfc822/analyze-atom 'std11-analyze-atom)
123 (defalias 'rfc822/analyze-quoted-string 'std11-analyze-quoted-string)
124 (defalias 'rfc822/analyze-domain-literal 'std11-analyze-domain-literal)
125 (defalias 'rfc822/analyze-comment 'std11-analyze-comment)
127 (defalias 'rfc822/lexical-analyze 'std11-lexical-analyze)
133 (defun rfc822/ignored-token-p (token)
134 (let ((type (car token)))
135 (or (eq type 'spaces)(eq type 'comment))
138 (defun rfc822/parse-token (lal)
142 (setq token (car lal))
143 (rfc822/ignored-token-p token)
146 (setq itl (cons token itl))
148 (cons (nreverse (cons token itl))
152 (defun rfc822/parse-ascii-token (lal)
153 (let (token itl parsed token-value)
155 (setq token (car lal))
156 (if (and (setq token-value (cdr token))
157 (find-charset-string token-value)
160 (rfc822/ignored-token-p token)
163 (setq itl (cons token itl))
166 (setq parsed (nreverse (cons token itl)))
168 (cons parsed (cdr lal))
171 (defun rfc822/parse-token-or-comment (lal)
175 (setq token (car lal))
176 (eq (car token) 'spaces)
179 (setq itl (cons token itl))
181 (cons (nreverse (cons token itl))
185 (defun rfc822/parse-word (lal)
186 (let ((ret (rfc822/parse-ascii-token lal)))
188 (let ((elt (car ret))
191 (if (or (assq 'atom elt)
192 (assq 'quoted-string elt))
193 (cons (cons 'word elt) rest)
196 (defun rfc822/parse-word-or-comment (lal)
197 (let ((ret (rfc822/parse-token-or-comment lal)))
199 (let ((elt (car ret))
202 (cond ((or (assq 'atom elt)
203 (assq 'quoted-string elt))
204 (cons (cons 'word elt) rest)
207 (cons (cons 'comment-word elt) rest)
211 (defun rfc822/parse-phrase (lal)
213 (while (setq ret (rfc822/parse-word-or-comment lal))
214 (setq phrase (append phrase (cdr (car ret))))
218 (cons (cons 'phrase phrase) lal)
221 (defun rfc822/parse-local-part (lal)
222 (let ((ret (rfc822/parse-word lal)))
224 (let ((local-part (cdr (car ret))) dot)
226 (while (and (setq ret (rfc822/parse-ascii-token lal))
228 (string-equal (cdr (assq 'specials dot)) ".")
229 (setq ret (rfc822/parse-word (cdr ret)))
231 (append local-part dot (cdr (car ret)))
235 (cons (cons 'local-part local-part) lal)
238 (defun rfc822/parse-sub-domain (lal)
239 (let ((ret (rfc822/parse-ascii-token lal)))
241 (let ((sub-domain (car ret)))
242 (if (or (assq 'atom sub-domain)
243 (assq 'domain-literal sub-domain)
245 (cons (cons 'sub-domain sub-domain)
250 (defun rfc822/parse-domain (lal)
251 (let ((ret (rfc822/parse-sub-domain lal)))
253 (let ((domain (cdr (car ret))) dot)
255 (while (and (setq ret (rfc822/parse-ascii-token lal))
257 (string-equal (cdr (assq 'specials dot)) ".")
258 (setq ret (rfc822/parse-sub-domain (cdr ret)))
260 (append domain dot (cdr (car ret)))
264 (cons (cons 'domain domain) lal)
267 (defun rfc822/parse-at-domain (lal)
268 (let ((ret (rfc822/parse-ascii-token lal)) at-sign)
270 (setq at-sign (car ret))
271 (string-equal (cdr (assq 'specials at-sign)) "@")
272 (setq ret (rfc822/parse-domain (cdr ret)))
274 (cons (cons 'at-domain (append at-sign (cdr (car ret))))
278 (defun rfc822/parse-addr-spec (lal)
279 (let ((ret (rfc822/parse-local-part lal))
283 (setq addr (cdr (car ret)))
285 (and (setq ret (rfc822/parse-at-domain lal))
286 (setq addr (append addr (cdr (car ret))))
289 (cons (cons 'addr-spec addr) lal)
292 (defun rfc822/parse-route (lal)
293 (let ((ret (rfc822/parse-at-domain lal))
297 (setq route (cdr (car ret)))
299 (while (and (setq ret (rfc822/parse-ascii-token lal))
300 (setq comma (car ret))
301 (string-equal (cdr (assq 'specials comma)) ",")
302 (setq ret (rfc822/parse-at-domain (cdr ret)))
304 (setq route (append route comma (cdr (car ret))))
307 (and (setq ret (rfc822/parse-ascii-token lal))
308 (setq colon (car ret))
309 (string-equal (cdr (assq 'specials colon)) ":")
310 (setq route (append route colon))
313 (cons (cons 'route route)
318 (defun rfc822/parse-route-addr (lal)
319 (let ((ret (rfc822/parse-ascii-token lal))
323 (string-equal (cdr (assq 'specials <)) "<")
325 (progn (and (setq ret (rfc822/parse-route lal))
326 (setq route (cdr (car ret)))
329 (setq ret (rfc822/parse-addr-spec lal))
331 (setq addr-spec (cdr (car ret)))
333 (setq ret (rfc822/parse-ascii-token lal))
335 (string-equal (cdr (assq 'specials >)) ">")
337 (cons (cons 'route-addr (append route addr-spec))
342 (defun rfc822/parse-phrase-route-addr (lal)
343 (let ((ret (rfc822/parse-phrase lal)) phrase)
346 (setq phrase (cdr (car ret)))
349 (if (setq ret (rfc822/parse-route-addr lal))
350 (cons (list 'phrase-route-addr
356 (defun rfc822/parse-mailbox (lal)
357 (let ((ret (or (rfc822/parse-phrase-route-addr lal)
358 (rfc822/parse-addr-spec lal)))
362 (setq mbox (car ret))
364 (if (and (setq ret (rfc822/parse-token-or-comment lal))
365 (setq comment (cdr (assq 'comment (car ret))))
369 (cons (list 'mailbox mbox comment)
373 (defun rfc822/parse-group (lal)
374 (let ((ret (rfc822/parse-phrase lal))
375 phrase colon comma mbox semicolon)
377 (setq phrase (cdr (car ret)))
379 (setq ret (rfc822/parse-ascii-token lal))
380 (setq colon (car ret))
381 (string-equal (cdr (assq 'specials colon)) ":")
384 (and (setq ret (rfc822/parse-mailbox lal))
385 (setq mbox (list (car ret)))
388 (while (and (setq ret (rfc822/parse-ascii-token lal))
389 (setq comma (car ret))
391 (cdr (assq 'specials comma)) ",")
393 (setq ret (rfc822/parse-mailbox lal))
394 (setq mbox (cons (car ret) mbox))
398 (and (setq ret (rfc822/parse-ascii-token lal))
399 (setq semicolon (car ret))
400 (string-equal (cdr (assq 'specials semicolon)) ";")
402 (cons (list 'group phrase (nreverse mbox))
407 (defun rfc822/parse-address (lal)
408 (or (rfc822/parse-group lal)
409 (rfc822/parse-mailbox lal)
412 (defun rfc822/parse-addresses (lal)
413 (let ((ret (rfc822/parse-address lal)))
415 (let ((dest (list (car ret))))
417 (while (and (setq ret (rfc822/parse-ascii-token lal))
418 (string-equal (cdr (assq 'specials (car ret))) ",")
419 (setq ret (rfc822/parse-address (cdr ret)))
421 (setq dest (cons (car ret) dest))
427 (defun rfc822/addr-to-string (seq)
430 (if (eq (car token) 'spaces)
437 (defun rfc822/address-string (address)
438 (cond ((eq (car address) 'group)
439 (mapconcat (function rfc822/address-string)
443 ((eq (car address) 'mailbox)
444 (let ((addr (nth 1 address)))
445 (rfc822/addr-to-string
446 (if (eq (car addr) 'phrase-route-addr)
452 (defun rfc822/full-name-string (address)
453 (cond ((eq (car address) 'group)
460 ((eq (car address) 'mailbox)
461 (let ((addr (nth 1 address))
462 (comment (nth 2 address))
464 (if (eq (car addr) 'phrase-route-addr)
465 (setq phrase (mapconcat (function
474 (defun rfc822/extract-address-components (str)
475 "Extract full name and canonical address from STR.
476 Returns a list of the form (FULL-NAME CANONICAL-ADDRESS).
477 If no name can be extracted, FULL-NAME will be nil. [tl-822.el]"
478 (let* ((structure (car
479 (rfc822/parse-address
480 (rfc822/lexical-analyze str)
482 (phrase (rfc822/full-name-string structure))
483 (address (rfc822/address-string structure))
485 (list phrase address)
494 ;;; tl-822.el ends here