changeset 7060:58d613f69b39

(mail-extr-all-top-level-domains): Renamed from all-top-level-domains. Major changes by jwz and drw.
author Richard M. Stallman <rms@gnu.org>
date Sun, 24 Apr 1994 03:51:13 +0000
parents 6a55de48ade5
children 9f27a57b7662
files lisp/mail/mail-extr.el
diffstat 1 files changed, 1220 insertions(+), 790 deletions(-) [+]
line wrap: on
line diff
--- a/lisp/mail/mail-extr.el	Sun Apr 24 03:10:24 1994 +0000
+++ b/lisp/mail/mail-extr.el	Sun Apr 24 03:51:13 1994 +0000
@@ -1,17 +1,17 @@
 ;;; mail-extr.el --- extract full name and address from RFC 822 mail header.
 
-;; Copyright (C) 1992 Free Software Foundation, Inc.
+;; Copyright (C) 1991, 1992, 1993, 1994 Free Software Foundation, Inc.
 
 ;; Author: Joe Wells <jbw@cs.bu.edu>
-;; Version: 1.0
-;; Adapted-By: ESR
+;; Maintainer: Jamie Zawinski <jwz@lucid.com>
+;; Version: 1.8
 ;; Keywords: mail
 
 ;; This file is part of GNU Emacs.
 
 ;; GNU Emacs is free software; you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 1, or (at your option)
+;; the Free Software Foundation; either version 2, or (at your option)
 ;; any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
@@ -25,51 +25,62 @@
 
 ;;; Commentary:
 
-;; Here is `mail-extr', a package for extracting full names and canonical
-;; addresses from RFC 822 mail headers.  It is intended to be hooked into
-;; other Emacs Lisp packages that deal with RFC 822 format messages, such as
-;; Gnews, GNUS, RMAIL, MH-E, BBDB, VM, Supercite, etc.  Thus, this release is
-;; mainly for Emacs Lisp developers.
-
+;; The entry point of this code is
+;;
+;;    mail-extract-address-components: (address)
+;;  
+;;    Given an RFC-822 ADDRESS, extract full name and canonical address.
+;;    Returns a list of the form (FULL-NAME CANONICAL-ADDRESS).
+;;    If no name can be extracted, FULL-NAME will be nil.
+;;    ADDRESS may be a string or a buffer.  If it is a buffer, the visible 
+;;     (narrowed) portion of the buffer will be interpreted as the address.
+;;     (This feature exists so that the clever caller might be able to avoid
+;;     consing a string.)
+;;    If ADDRESS contains more than one RFC-822 address, only the first is
+;;     returned.
+;;
+;; This code is more correct (and more heuristic) parser than the code in
+;; rfc822.el.  And despite its size, it's fairly fast.
+;;
 ;; There are two main benefits:
-
+;;
 ;; 1. Higher probability of getting the correct full name for a human than
-;;    any other package I know of.  (On the other hand, it will cheerfully
+;;    any other package we know of.  (On the other hand, it will cheerfully
 ;;    mangle non-human names/comments.)
 ;; 2. Address part is put in a canonical form.
-
-;; The interface is not yet carved in stone; please give me suggestions.
-
-;; I have an extensive test-case collection of funny addresses if you want to
+;;
+;; The interface is not yet carved in stone; please give us suggestions.
+;;
+;; We have an extensive test-case collection of funny addresses if you want to
 ;; work with the code.  Developing this code requires frequent testing to
-;; make sure you're not breaking functionality.  I'm not posting the
-;; test-cases because they take over 100K.
-
-;; If you find an address that mail-extr fails on, please send it to me along
-;; with what you think the correct results should be.  I do not consider it a
-;; bug if mail-extr mangles a comment that does not correspond to a real
-;; human full name, although I would prefer that mail-extr would return the
-;; comment as-is.
-
+;; make sure you're not breaking functionality.  The test cases aren't included
+;; because they are over 100K.
+;;
+;; If you find an address that mail-extr fails on, please send it to the 
+;; maintainer along with what you think the correct results should be.  We do
+;; not consider it a bug if mail-extr mangles a comment that does not
+;; correspond to a real human full name, although we would prefer that 
+;; mail-extr would return the comment as-is.
+;;
 ;; Features:
-
+;;
 ;; * Full name handling:
-
+;;
 ;;   * knows where full names can be found in an address.
 ;;   * avoids using empty comments and quoted text.
 ;;   * extracts full names from mailbox names.
 ;;   * recognizes common formats for comments after a full name.
 ;;   * puts a period and a space after each initial.
-;;   * understands & referring to the mailbox name capitalized.
-;;   * strips name prefixes like "Prof.", etc..
+;;   * understands & referring to the mailbox name, capitalized.
+;;   * strips name prefixes like "Prof.", etc.
 ;;   * understands what characters can occur in names (not just letters).
 ;;   * figures out middle initial from mailbox name.
 ;;   * removes funny nicknames.
 ;;   * keeps suffixes such as Jr., Sr., III, etc.
 ;;   * reorders "Last, First" type names.
-
+;;
 ;; * Address handling:
-
+;;
 ;;   * parses rfc822 quoted text, comments, and domain literals.
 ;;   * parses rfc822 multi-line headers.
 ;;   * does something reasonable with rfc822 GROUP addresses.
@@ -79,13 +90,13 @@
 ;;     * converts rfc822 ROUTE addresses to %-style addresses.
 ;;     * truncates %-style addresses at leftmost fully qualified domain name.
 ;;     * handles local relative precedence of ! vs. % and @ (untested).
-
+;;
 ;; It does almost no string creation.  It primarily uses the built-in
 ;; parsing routines with the appropriate syntax tables.  This should
 ;; result in greater speed.
-
+;;
 ;; TODO:
-
+;;
 ;; * handle all test cases.  (This will take forever.)
 ;; * software to pick the correct header to use (eg., "Senders-Name:").
 ;; * multiple addresses in the "From:" header (almost all of the necessary
@@ -102,12 +113,76 @@
 ;; * delete unused variables.
 ;; * arrange for testing with different relative precedences of ! vs. @
 ;;   and %.
-;; * put mail-variant-method back into mail-extract-address-components.
 ;; * insert documentation strings!
 ;; * handle X.400-gatewayed addresses according to RFC 1148.
 
 ;;; Change Log: 
 ;; 
+;; Thu Feb 17 17:57:33 1994  Jamie Zawinski (jwz@lucid.com)
+;;
+;;	* merged with jbw's latest version
+;;
+;; Wed Feb  9 21:56:27 1994  Jamie Zawinski (jwz@lucid.com)
+;;
+;;      * high-bit chars in comments weren't treated as word syntax
+;;
+;; Sat Feb  5 03:13:40 1994  Jamie Zawinski (jwz@lucid.com)
+;;
+;;      * call replace-match with fixed-case arg
+;;
+;; Thu Dec 16 21:56:45 1993  Jamie Zawinski (jwz@lucid.com)
+;;
+;;      * some more cleanup, doc, added provide
+;;
+;; Tue Mar 23 21:23:18 1993  Joe Wells  (jbw at csd.bu.edu)
+;; 
+;; 	* Made mail-full-name-prefixes a user-customizable variable.
+;;        Allow passing the address as a buffer as well as as a string.
+;;        Allow [ and ] as name characters (Finnish character set).
+;; 
+;; Mon Mar 22 21:20:56 1993  Joe Wells  (jbw at bigbird.bu.edu)
+;; 
+;; 	* Handle "null" addresses.  Handle = used for spacing in mailbox
+;; 	  name.  Fix bug in handling of ROUTE-ADDR-type addresses that are
+;; 	  missing their brackets.  Handle uppercase "JR".  Extract full
+;; 	  names from X.400 addresses encoded in RFC-822.  Fix bug in
+;;        handling of multiple addresses where first has trailing comment.
+;;        Handle more kinds of telephone extension lead-ins.
+;; 
+;; Mon Mar 22 20:16:57 1993  Joe Wells  (jbw at bigbird.bu.edu)
+;; 
+;; 	* Handle HZ encoding for embedding GB encoded chinese characters.
+;; 
+;; Mon Mar 22 00:46:12 1993  Joe Wells  (jbw at bigbird.bu.edu)
+;; 
+;; 	* Fixed too broad matching of ham radio call signs.  Fixed bug in
+;; 	  handling an unmatched ' in a name string.  Enhanced recognition
+;; 	  of when . in the mailbox name terminates the name portion.
+;; 	  Narrowed conversion of . to space to only the necessary
+;; 	  situation.  Deal with VMS's stupid date stamps.  Handle a unique
+;; 	  way of introducing an alternate address.  Fixed spacing bug I
+;; 	  introduced in switching last name order.  Fixed bug in handling
+;; 	  address with ! and % but no @.  Narrowed the cases in which
+;; 	  certain trailing words are discarded.
+;; 
+;; Sun Mar 21 21:41:06 1993  Joe Wells  (jbw at bigbird.bu.edu)
+;; 
+;; 	* Fixed bugs in handling GROUP addresses.  Certain words in the
+;; 	  middle of a name no longer terminate it.  Handle LISTSERV list
+;;        names.  Ignore comment field containing mailbox name.
+;; 
+;; Sun Mar 21 14:39:38 1993  Joe Wells  (jbw at bigbird.bu.edu)
+;; 
+;; 	* Moved variant-method code back into main function.  Handle
+;; 	underscores as spaces in comments.  Handle leading nickname.  Add
+;; 	flag to ignore single-word names.  Other changes.
+;; 
+;; Mon Feb  1 22:23:31 1993  Joe Wells  (jbw at bigbird.bu.edu)
+;; 
+;; 	* Added in changes by Rod Whitby and Jamie Zawinski.  This
+;;        includes the flag mail-extr-guess-middle-initial and the fix for
+;;        handling multiple addresses correctly.
+;; 
 ;; Mon Apr  6 23:59:09 1992  Joe Wells  (jbw at bigbird.bu.edu)
 ;; 
 ;; 	* Cleaned up some more.  Release version 1.0 to world.
@@ -127,9 +202,37 @@
 
 ;;; Code:
 
-;; Variable definitions.
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; User configuration variable definitions.
+;;
+
+(defvar mail-extr-guess-middle-initial nil
+  "*Whether to try to guess middle initial from mail address.
+If true, then when we see an address like \"John Smith <jqs@host.com>\"
+we will assume that \"John Q. Smith\" is the fellow's name.")
+
+(defvar mail-extr-ignore-single-names t
+  "*Whether to ignore a name that is just a single word.
+If true, then when we see an address like \"Idiot <dumb@stupid.com>\"
+we will act as though we couldn't find a full name in the address.")
 
-(defvar mail-@-binds-tighter-than-! nil)
+;; Matches a leading title that is not part of the name (does not
+;; contribute to uniquely identifying the person).
+(defvar mail-extr-full-name-prefixes
+  (purecopy
+   "\\(Prof\\|D[Rr]\\|Mrs?\\|Rev\\|Rabbi\\|SysOp\\|LCDR\\)\\.?[ \t\n]")
+  "*Matches prefixes to the full name that identify a person's position.
+These are stripped from the full name because they do not contribute to
+uniquely identifying the person.")
+
+(defvar mail-extr-@-binds-tighter-than-! nil
+  "*Whether the local mail transport agent looks at ! before @.")
+
+(defvar mail-extr-mangle-uucp nil
+  "*Whether to throw away information in UUCP addresses
+by translating things like \"foo!bar!baz@host\" into \"baz@bar.UUCP\".")
 
 ;;----------------------------------------------------------------------
 ;; what orderings are meaningful?????
@@ -142,120 +245,253 @@
 ;; arbitrary address.
 ;;----------------------------------------------------------------------
 
-(defconst mail-space-char 32)
+
 
-(defconst mail-whitespace " \t\n")
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Constant definitions.
+;;
 
-;; Any character that can occur in a name in an RFC822 address.
-;; Yes, there are weird people with digits in their names.
-(defconst mail-all-letters "A-Za-z---{|}'~0-9`.")
+;;           Codes in
+;; Names in  ISO 8859-1 Name
+;; ISO 10XXX ISO 8859-2 in
+;; ISO 6937  ISO 10646  RFC            Swedish
+;; etc.      Hex Oct    1345 TeX Split ASCII Description
+;; --------- ---------- ---- --- ----- ----- -------------------------------
+;; %a        E4  344    a:   \"a ae    {     latin small   a + diaeresis   d
+;; %o        F6  366    o:   \"o oe    |     latin small   o + diaeresis   v
+;; @a        E5  345    aa   \oa aa    }     latin small   a + ring above  e
+;; %u        FC  374    u:   \"u ue    ~     latin small   u + diaeresis   |
+;; /e        E9  351    e'   \'e       `     latin small   e + acute       i
+;; %A        C4  304    A:   \"A AE    [     latin capital a + diaeresis   D
+;; %O        D6  326    O:   \"O OE    \     latin capital o + diaeresis   V
+;; @A        C5  305    AA   \oA AA    ]     latin capital a + ring above  E
+;; %U        DC  334    U:   \"U UE    ^     latin capital u + diaeresis   \
+;; /E        C9  311    E'   \'E       @     latin capital e + acute       I
+
+;; NOTE: @a and @A are not in ISO 8859-2 (the codes mentioned above invoke
+;; /l and /L).  Some of this data was retrieved from
+;; listserv@jhuvm.hcf.jhu.edu.
 
 ;; Any character that can occur in a name, not counting characters that
-;; separate parts of a multipart name.
-(defconst mail-all-letters-but-separators "A-Za-z{|}'~0-9`")
+;; separate parts of a multipart name (hyphen and period).
+;; Yes, there are weird people with digits in their names.
+;; You will also notice the consideration for the
+;; Swedish/Finnish/Norwegian character set.
+;; #### (go to \376 instead of \377 to work around bug in search.c...)
+(defconst mail-extr-all-letters-but-separators
+  (purecopy "][A-Za-z{|}'~0-9`\200-\376"))
 
-;; Any character that can start a name
-(defconst mail-first-letters "A-Za-z")
+;; Any character that can occur in a name in an RFC822 address including
+;; the separator (hyphen and possibly period) for multipart names.
+;; #### should . be in here?
+(defconst mail-extr-all-letters
+  (purecopy (concat mail-extr-all-letters-but-separators "---")))
+
+;; Any character that can start a name.
+;; Keep this set as minimal as possible.
+(defconst mail-extr-first-letters (purecopy "A-Za-z"))
 
 ;; Any character that can end a name.
-(defconst mail-last-letters "A-Za-z`'.")
+;; Keep this set as minimal as possible.
+(defconst mail-extr-last-letters (purecopy "[A-Za-z`'."))
+
+(defconst mail-extr-leading-garbage
+  (purecopy (format "[^%s]+" mail-extr-first-letters)))
+
+;; (defconst mail-extr-non-name-chars 
+;;   (purecopy (concat "^" mail-extr-all-letters ".")))
+;; (defconst mail-extr-non-begin-name-chars
+;;   (purecopy (concat "^" mail-extr-first-letters)))
+;; (defconst mail-extr-non-end-name-chars
+;;   (purecopy (concat "^" mail-extr-last-letters)))
 
 ;; Matches an initial not followed by both a period and a space. 
-(defconst mail-bad-initials-pattern
-  (format "\\(\\([^%s]\\|\\`\\)[%s]\\)\\(\\.\\([^ ]\\)\\| \\|\\([^%s .]\\)\\|\\'\\)"
-	  mail-all-letters mail-first-letters mail-all-letters))
-
-(defconst mail-non-name-chars (concat "^" mail-all-letters "."))
-
-(defconst mail-non-begin-name-chars (concat "^" mail-first-letters))
-
-(defconst mail-non-end-name-chars (concat "^" mail-last-letters))
+;; (defconst mail-extr-bad-initials-pattern
+;;   (purecopy 
+;;    (format "\\(\\([^%s]\\|\\`\\)[%s]\\)\\(\\.\\([^ ]\\)\\| \\|\\([^%s .]\\)\\|\\'\\)"
+;;            mail-extr-all-letters mail-extr-first-letters mail-extr-all-letters)))
 
 ;; Matches periods used instead of spaces.  Must not match the period
 ;; following an initial.
-(defconst mail-bad-\.-pattern
-  (format "\\([%s][%s]\\)\\.+\\([%s]\\)"
-	  mail-all-letters mail-last-letters mail-first-letters))
+(defconst mail-extr-bad-dot-pattern
+  (purecopy
+   (format "\\([%s][%s]\\)\\.+\\([%s]\\)"
+	   mail-extr-all-letters
+	   mail-extr-last-letters
+	   mail-extr-first-letters)))
 
 ;; Matches an embedded or leading nickname that should be removed.
-(defconst mail-nickname-pattern
-  (format "\\([ .]\\|\\`\\)[\"'`\[\(]\\([ .%s]+\\)[\]\"'\)] "
-	  mail-all-letters))
-
-;; Matches a leading title that is not part of the name (does not
-;; contribute to uniquely identifying the person).
-(defconst mail-full-name-prefixes
-      '"\\` *\\(Prof\\|Dr\\|Mrs?\\|Rev\\|Rabbi\\|SysOp\\|LCDR\\)\\.? ")
+;; (defconst mail-extr-nickname-pattern
+;;   (purecopy
+;;    (format "\\([ .]\\|\\`\\)[\"'`\[\(]\\([ .%s]+\\)[\]\"'\)] "
+;;            mail-extr-all-letters)))
 
 ;; Matches the occurrence of a generational name suffix, and the last
-;; character of the preceding name.
-(defconst mail-full-name-suffix-pattern
-  (format
-   "\\(,? ?\\([JjSs]r\\.?\\|V?I+V?\\)\\)\\([^%s]\\([^%s]\\|\\'\\)\\|\\'\\)"
-   mail-all-letters mail-all-letters))
+;; character of the preceding name.  This is important because we want to
+;; keep such suffixes: they help to uniquely identify the person.
+;; *** Perhaps this should be a user-customizable variable.  However, the
+;; *** regular expression is fairly tricky to alter, so maybe not.
+(defconst mail-extr-full-name-suffix-pattern
+  (purecopy
+   (format
+    "\\(,? ?\\([JjSs][Rr]\\.?\\|V?I+V?\\)\\)\\([^%s]\\([^%s]\\|\\'\\)\\|\\'\\)"
+    mail-extr-all-letters mail-extr-all-letters)))
 
-(defconst mail-roman-numeral-pattern
-  "V?I+V?\\b")
+(defconst mail-extr-roman-numeral-pattern (purecopy "V?I+V?\\b"))
 
 ;; Matches a trailing uppercase (with other characters possible) acronym.
 ;; Must not match a trailing uppercase last name or trailing initial
-(defconst mail-weird-acronym-pattern "\\([A-Z]+[-_/]\\|[A-Z][A-Z][A-Z]?\\b\\)")
+(defconst mail-extr-weird-acronym-pattern
+  (purecopy "\\([A-Z]+[-_/]\\|[A-Z][A-Z][A-Z]?\\b\\)"))
       
 ;; Matches a mixed-case or lowercase name (not an initial).
-(defconst mail-mixed-case-name-pattern
-  (format
-   "\\b\\([a-z][%s]*[%s]\\|[%s][%s]*[a-z][%s]*[%s]\\|[%s][%s]*[a-z]\\)"
-   mail-all-letters mail-last-letters
-   mail-first-letters mail-all-letters mail-all-letters mail-last-letters
-   mail-first-letters mail-all-letters))
+;; #### Match Latin1 lower case letters here too?
+;; (defconst mail-extr-mixed-case-name-pattern
+;;   (purecopy
+;;    (format
+;;     "\\b\\([a-z][%s]*[%s]\\|[%s][%s]*[a-z][%s]*[%s]\\|[%s][%s]*[a-z]\\)"
+;;     mail-extr-all-letters mail-extr-last-letters
+;;     mail-extr-first-letters mail-extr-all-letters mail-extr-all-letters
+;;     mail-extr-last-letters mail-extr-first-letters mail-extr-all-letters)))
 
 ;; Matches a trailing alternative address.
-(defconst mail-alternative-address-pattern "[a-zA-Z.]+[!@][a-zA-Z.]")
+;; #### Match Latin1 letters here too?
+;; #### Match _ before @ here too?  
+(defconst mail-extr-alternative-address-pattern
+  (purecopy "\\(aka *\\)?[a-zA-Z.]+[!@][a-zA-Z.]"))
 
 ;; Matches a variety of trailing comments not including comma-delimited
 ;; comments.
-(defconst mail-trailing-comment-start-pattern " [-{]\\|--\\|[+@#></\;]")
+(defconst mail-extr-trailing-comment-start-pattern
+  (purecopy " [-{]\\|--\\|[+@#></\;]"))
 
 ;; Matches a name (not an initial).
 ;; This doesn't force a word boundary at the end because sometimes a
 ;; comment is separated by a `-' with no preceding space.
-(defconst mail-name-pattern
-  (format
-   "\\b[%s][%s]*[%s]"
-   mail-first-letters mail-all-letters mail-last-letters))
+(defconst mail-extr-name-pattern
+  (purecopy (format "\\b[%s][%s]*[%s]"
+		    mail-extr-first-letters
+		    mail-extr-all-letters
+		    mail-extr-last-letters)))
 
-(defconst mail-initial-pattern
-  (format "\\b[%s]\\([. ]\\|\\b\\)" mail-first-letters))
+(defconst mail-extr-initial-pattern
+  (purecopy (format "\\b[%s]\\([. ]\\|\\b\\)" mail-extr-first-letters)))
 
 ;; Matches a single name before a comma.
-(defconst mail-last-name-first-pattern
-  (concat "\\`" mail-name-pattern ","))
+;; (defconst mail-extr-last-name-first-pattern
+;;   (purecopy (concat "\\`" mail-extr-name-pattern ",")))
 
 ;; Matches telephone extensions.
-(defconst mail-telephone-extension-pattern
-  "\\(\\([Ee]xt\\|[Tt]el\\|[Xx]\\).?\\)? *\\+?[0-9][- 0-9]+")
+(defconst mail-extr-telephone-extension-pattern
+  (purecopy
+   "\\(\\([Ee]xt\\|\\|[Tt]ph\\|[Tt]el\\|[Xx]\\).?\\)? *\\+?[0-9][- 0-9]+"))
 
 ;; Matches ham radio call signs.
-(defconst mail-ham-call-sign-pattern
-  "\\b[A-Z]+[0-9][A-Z0-9]*")
+;; Help from: Mat Maessen N2NJZ <maessm@rpi.edu>, Mark Feit
+;; <mark@era.com>, Michael Covington <mcovingt@ai.uga.edu>.
+;; Examples: DX504 DX515 K5MRU K8DHK KA9WGN KA9WGN KD3FU KD6EUI KD6HBW
+;; KE9TV KF0NV N1API N3FU N3GZE N3IGS N4KCC N7IKQ N9HHU W4YHF W6ANK WA2SUH
+;; WB7VZI N2NJZ NR3G KJ4KK AB4UM AL7NI KH6OH WN3KBT N4TMI W1A N0NZO
+(defconst mail-extr-ham-call-sign-pattern
+  (purecopy "\\b\\(DX[0-9]+\\|[AKNW][A-Z]?[0-9][A-Z][A-Z]?[A-Z]?\\)"))
+
+;; Possible trailing suffixes: "\\(/\\(KT\\|A[AEG]\\|[R0-9]\\)\\)?"
+;; /KT == Temporary Technician (has CSC but not "real" license)
+;; /AA == Temporary Advanced
+;; /AE == Temporary Extra
+;; /AG == Temporary General
+;; /R  == repeater
+;; /#  == stations operating out of home district
+;; I don't include these in the regexp above because I can't imagine
+;; anyone putting them with their name in an e-mail address.
 
 ;; Matches normal single-part name
-(defconst mail-normal-name-pattern
-  (format
-   "\\b[%s][%s]+[%s]"
-   mail-first-letters mail-all-letters-but-separators mail-last-letters))
+(defconst mail-extr-normal-name-pattern
+  (purecopy (format "\\b[%s][%s]+[%s]"
+		    mail-extr-first-letters
+		    mail-extr-all-letters-but-separators
+		    mail-extr-last-letters)))
 
+;; Matches a single word name.
+;; (defconst mail-extr-one-name-pattern
+;;   (purecopy (concat "\\`" mail-extr-normal-name-pattern "\\'")))
+  
 ;; Matches normal two names with missing middle initial
-(defconst mail-two-name-pattern
-  (concat "\\`\\(" mail-normal-name-pattern
-	  "\\|" mail-initial-pattern
-	  "\\) +\\(" mail-normal-name-pattern "\\)\\(,\\|\\'\\)"))
+;; The first name is not allowed to have a hyphen because this can cause
+;; false matches where the "middle initial" is actually the first letter
+;; of the second part of the first name.
+(defconst mail-extr-two-name-pattern
+  (purecopy
+   (concat "\\`\\(" mail-extr-normal-name-pattern
+	   "\\|" mail-extr-initial-pattern
+	   "\\) +\\(" mail-extr-name-pattern "\\)\\(,\\|\\'\\)")))
+
+(defconst mail-extr-listserv-list-name-pattern
+  (purecopy "Multiple recipients of list \\([-A-Z]+\\)"))
+
+(defconst mail-extr-stupid-vms-date-stamp-pattern
+  (purecopy
+   "[0-9][0-9]-[JFMASOND][aepuco][nbrylgptvc]-[0-9][0-9][0-9][0-9] [0-9]+ *"))
+
+;;; HZ -- GB (PRC Chinese character encoding) in ASCII embedding protocol
+;;
+;; In ASCII mode, a byte is interpreted as an ASCII character, unless a '~' is
+;; encountered. The character '~' is an escape character. By convention, it
+;; must be immediately followed ONLY by '~', '{' or '\n' (<LF>), with the
+;; following special meaning.
+;; 
+;; o The escape sequence '~~' is interpreted as a '~'.
+;; o The escape-to-GB sequence '~{' switches the mode from ASCII to GB.
+;; o The escape sequence '~\n' is a line-continuation marker to be consumed
+;;   with no output produced.
+;; 
+;; In GB mode, characters are interpreted two bytes at a time as (pure) GB
+;; codes until the escape-from-GB code '~}' is read. This code switches the
+;; mode from GB back to ASCII.  (Note that the escape-from-GB code '~}'
+;; ($7E7D) is outside the defined GB range.)
+(defconst mail-extr-hz-embedded-gb-encoded-chinese-pattern
+  (purecopy "~{\\([^~].\\|~[^\}]\\)+~}"))
 
-(defvar address-syntax-table (make-syntax-table))
-(defvar address-comment-syntax-table (make-syntax-table))
-(defvar address-domain-literal-syntax-table (make-syntax-table))
-(defvar address-text-comment-syntax-table (make-syntax-table))
-(defvar address-text-syntax-table (make-syntax-table))
+;; The leading optional lowercase letters are for a bastardized version of
+;; the encoding, as is the optional nature of the final slash.
+(defconst mail-extr-x400-encoded-address-pattern
+  (purecopy "[a-z]?[a-z]?\\(/[A-Za-z]+\\(\\.[A-Za-z]+\\)?=[^/]+\\)+/?\\'"))
+
+(defconst mail-extr-x400-encoded-address-field-pattern-format
+  (purecopy "/%s=\\([^/]+\\)\\(/\\|\\'\\)"))
+
+(defconst mail-extr-x400-encoded-address-surname-pattern
+  ;; S stands for Surname (family name).
+  (purecopy
+   (format mail-extr-x400-encoded-address-field-pattern-format "[Ss]")))
+
+(defconst mail-extr-x400-encoded-address-given-name-pattern
+  ;; G stands for Given name.
+  (purecopy
+   (format mail-extr-x400-encoded-address-field-pattern-format "[Gg]")))
+
+(defconst mail-extr-x400-encoded-address-full-name-pattern
+  ;; PN stands for Personal Name.  When used it represents the combination
+  ;; of the G and S fields.
+  ;; "The one system I used having this field asked it with the prompt
+  ;; `Personal Name'.  But they mapped it into G and S on outgoing real
+  ;; X.400 addresses.  As they mapped G and S into PN on incoming..."
+  (purecopy
+   (format mail-extr-x400-encoded-address-field-pattern-format "[Pp][Nn]")))
+
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Syntax tables used for quick parsing.
+;;
+
+(defconst mail-extr-address-syntax-table (make-syntax-table))
+(defconst mail-extr-address-comment-syntax-table (make-syntax-table))
+(defconst mail-extr-address-domain-literal-syntax-table (make-syntax-table))
+(defconst mail-extr-address-text-comment-syntax-table (make-syntax-table))
+(defconst mail-extr-address-text-syntax-table (make-syntax-table))
 (mapcar
  (function
   (lambda (pair)
@@ -264,20 +500,23 @@
        (function
 	(lambda (item)
 	  (if (eq 2 (length item))
+	      ;; modifying syntax of a single character
 	      (modify-syntax-entry (car item) (car (cdr item)) syntax-table)
-	    (let ((char (car item))
-		  (bound (car (cdr item)))
-		  (syntax (car (cdr (cdr item)))))
+	    ;; modifying syntax of a range of characters
+	    (let ((char (nth 0 item))
+		  (bound (nth 1 item))
+		  (syntax (nth 2 item)))
 	      (while (<= char bound)
 		(modify-syntax-entry char syntax syntax-table)
 		(setq char (1+ char)))))))
        (cdr pair)))))
- '((address-syntax-table
-    (0  31   "w")			;control characters
-    (32      " ")			;SPC
-    (?! ?~   "w")			;printable characters
-    (127     "w")			;DEL
-    (128 255 "w")			;high-bit-on characters
+ '((mail-extr-address-syntax-table
+    (?\000 ?\037 "w")			;control characters
+    (?\040	 " ")			;SPC
+    (?! ?~	 "w")			;printable characters
+    (?\177	 "w")			;DEL
+    (?\200 ?\377 "w")			;high-bit-on characters
+    (?\240	 " ")			;nobreakspace
     (?\t " ")
     (?\r " ")
     (?\n " ")
@@ -296,20 +535,35 @@
     (?\] ".")
     ;; % and ! aren't RFC822 characters, but it is convenient to pretend
     (?%  ".")
-    (?!  ".")
+    (?!  ".") ;; this needs to be word-constituent when not in .UUCP mode
     )
-   (address-comment-syntax-table
-    (0 255 "w")
+   (mail-extr-address-comment-syntax-table
+    (?\000 ?\377 "w")
+    (?\040 " ")
+    (?\240 " ")
+    (?\t " ")
+    (?\r " ")
+    (?\n " ")
     (?\( "\(\)")
     (?\) "\)\(")
     (?\\ "\\"))
-   (address-domain-literal-syntax-table
-    (0 255 "w")
+   (mail-extr-address-domain-literal-syntax-table
+    (?\000 ?\377 "w")
+    (?\040 " ")
+    (?\240 " ")
+    (?\t " ")
+    (?\r " ")
+    (?\n " ")
     (?\[ "\(\]")			;??????
     (?\] "\)\[")			;??????
     (?\\ "\\"))
-   (address-text-comment-syntax-table
-    (0 255 "w")
+   (mail-extr-address-text-comment-syntax-table
+    (?\000 ?\377 "w")
+    (?\040 " ")
+    (?\240 " ")
+    (?\t " ")
+    (?\r " ")
+    (?\n " ")
     (?\( "\(\)")
     (?\) "\)\(")
     (?\[ "\(\]")
@@ -321,8 +575,13 @@
     ;; (?\' "\)\`")
     ;; (?\` "\(\'")
     )
-   (address-text-syntax-table
-    (0 255 ".")
+   (mail-extr-address-text-syntax-table
+    (?\000 ?\177 ".")
+    (?\200 ?\377 "w")
+    (?\040 " ")
+    (?\t " ")
+    (?\r " ")
+    (?\n " ")
     (?A ?Z "w")
     (?a ?z "w")
     (?-    "w")
@@ -335,110 +594,172 @@
    ))
 
 
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
 ;; Utility functions and macros.
+;;
+
+(defmacro mail-extr-delete-char (n)
+  ;; in v19, delete-char is compiled as a function call, but delete-region
+  ;; is byte-coded, so it's much much faster.
+  (list 'delete-region '(point) (list '+ '(point) n)))
 
-(defmacro mail-undo-backslash-quoting (beg end)
+(defmacro mail-extr-skip-whitespace-forward ()
+  ;; v19 fn skip-syntax-forward is more tasteful, but not byte-coded.
+  '(skip-chars-forward " \t\n\r\240"))
+
+(defmacro mail-extr-skip-whitespace-backward ()
+  ;; v19 fn skip-syntax-backward is more tasteful, but not byte-coded.
+  '(skip-chars-backward " \t\n\r\240"))
+
+
+(defmacro mail-extr-undo-backslash-quoting (beg end)
   (`(save-excursion
       (save-restriction
 	(narrow-to-region (, beg) (, end))
 	(goto-char (point-min))
 	;; undo \ quoting
-	(while (re-search-forward "\\\\\\(.\\)" nil t)
-	  (replace-match "\\1")
-	  ;; CHECK: does this leave point after the replacement?
+	(while (search-forward "\\" nil t)
+	  (mail-extr-delete-char -1)
+	  (or (eobp)
+	      (forward-char 1))
 	  )))))
 
-(defmacro mail-nuke-char-at (pos)
+(defmacro mail-extr-nuke-char-at (pos)
   (` (save-excursion
        (goto-char (, pos))
-       (delete-char 1)
-       (insert mail-space-char))))
+       (mail-extr-delete-char 1)
+       (insert ?\ ))))
+
+(put 'mail-extr-nuke-outside-range
+     'edebug-form-spec '(symbolp &optional form form atom))
 
-(defmacro mail-nuke-elements-outside-range (list-symbol beg-symbol end-symbol
-							&optional no-replace)
-  (` (progn
-       (setq temp (, list-symbol))
+(defmacro mail-extr-nuke-outside-range (list-symbol
+					beg-symbol end-symbol
+					&optional no-replace)
+  ;; LIST-SYMBOL names a variable holding a list of buffer positions
+  ;; BEG-SYMBOL and END-SYMBOL name variables delimiting a range
+  ;; Each element of LIST-SYMBOL which lies outside of the range is
+  ;;  deleted from the list.
+  ;; Unless NO-REPLACE is true, at each of the positions in LIST-SYMBOL
+  ;;  which lie outside of the range, one character at that position is
+  ;;  replaced with a SPC.
+  (or (memq no-replace '(t nil))
+      (error "no-replace must be t or nil, evalable at macroexpand-time."))
+  (` (let ((temp (, list-symbol))
+	   ch)
        (while temp
-	 (cond ((or (> (car temp) (, end-symbol))
-		    (< (car temp) (, beg-symbol)))
-		(, (or no-replace
-		       (` (mail-nuke-char-at (car temp)))))
+	 (setq ch (car temp))
+	 (cond ((or (> ch (, end-symbol))
+		    (< ch (, beg-symbol)))
+		(,@ (if no-replace
+			nil
+		      (` ((mail-extr-nuke-char-at ch)))))
 		(setcar temp nil)))
 	 (setq temp (cdr temp)))
        (setq (, list-symbol) (delq nil (, list-symbol))))))
 
-(defun mail-demarkerize (marker)
-  (and marker
-       (if (markerp marker)
-	   (let ((temp (marker-position marker)))
-	     (set-marker marker nil)
-	     temp)
-	 marker)))
+(defun mail-extr-demarkerize (marker)
+  ;; if arg is a marker, destroys the marker, then returns the old value.
+  ;; otherwise returns the arg.
+  (if (markerp marker)
+      (let ((temp (marker-position marker)))
+	(set-marker marker nil)
+	temp)
+    marker))
 
-(defun mail-markerize (pos)
-  (and pos
-       (if (markerp pos)
-	   pos
-	 (copy-marker pos))))
+(defun mail-extr-markerize (pos)
+  ;; coerces pos to a marker if non-nil.
+  (if (or (markerp pos) (null pos))
+      pos
+    (copy-marker pos)))
 
-(defmacro mail-last-element (list)
-  "Return last element of LIST."
+(defmacro mail-extr-last (list)
+  ;; Returns last element of LIST.
+  ;; Could be a subst.
   (` (let ((list (, list)))
        (while (not (null (cdr list)))
 	 (setq list (cdr list)))
        (car list))))
   
-(defmacro mail-safe-move-sexp (arg)
-  "Safely skip over one balanced sexp, if there is one.  Return t if success."
+(defmacro mail-extr-safe-move-sexp (arg)
+  ;; Safely skip over one balanced sexp, if there is one.  Return t if success.
   (` (condition-case error
 	 (progn
 	   (goto-char (scan-sexps (point) (, arg)))
 	   t)
        (error
+	;; #### kludge kludge kludge kludge kludge kludge kludge !!!
 	(if (string-equal (nth 1 error) "Unbalanced parentheses")
 	    nil
 	  (while t
 	    (signal (car error) (cdr error))))))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; The main function to grind addresses
+;;
 
-
-;; The main function to grind addresses
+(defvar disable-initial-guessing-flag)	; dynamic assignment
+(defvar cbeg)				; dynamic assignment
+(defvar cend)				; dynamic assignment
 
+;;;###autoload
 (defun mail-extract-address-components (address)
-  "Given an rfc 822 ADDRESS, extract full name and canonical address.
-Returns a list of the form (FULL-NAME CANONICAL-ADDRESS)."
-  (let ((canonicalization-buffer (get-buffer-create "*canonical address*"))
-	(extraction-buffer (get-buffer-create "*extract address components*"))
-	(foo 'bar)
+  "Given an RFC-822 ADDRESS, extract full name and canonical address.
+Returns a list of the form (FULL-NAME CANONICAL-ADDRESS).
+If no name can be extracted, FULL-NAME will be nil.
+ADDRESS may be a string or a buffer.  If it is a buffer, the visible 
+ (narrowed) portion of the buffer will be interpreted as the address.
+ (This feature exists so that the clever caller might be able to avoid
+ consing a string.)
+If ADDRESS contains more than one RFC-822 address, only the first is
+ returned.  Some day this function may be extended to extract multiple
+ addresses, or perhaps return the position at which parsing stopped."
+  (let ((canonicalization-buffer (get-buffer-create " *canonical address*"))
+	(extraction-buffer (get-buffer-create " *extract address components*"))
 	char
-	multiple-addresses
+;;	multiple-addresses
 	<-pos >-pos @-pos :-pos ,-pos !-pos %-pos \;-pos
 	group-:-pos group-\;-pos route-addr-:-pos
 	record-pos-symbol
 	first-real-pos last-real-pos
 	phrase-beg phrase-end
-	comment-beg comment-end
+	cbeg cend			; dynamically set from -voodoo
 	quote-beg quote-end
 	atom-beg atom-end
 	mbox-beg mbox-end
 	\.-ends-name
 	temp
-	name-suffix
-	saved-point
-	fi mi li
+;;	name-suffix
+	fi mi li			; first, middle, last initial
 	saved-%-pos saved-!-pos saved-@-pos
-	domain-pos \.-pos insert-point)
+	domain-pos \.-pos insert-point
+;;	mailbox-name-processed-flag
+	disable-initial-guessing-flag	; dynamically set from -voodoo
+	)
     
     (save-excursion
       (set-buffer extraction-buffer)
+      (fundamental-mode)
+      (kill-all-local-variables)
       (buffer-disable-undo extraction-buffer)
-      (set-syntax-table address-syntax-table)
+      (set-syntax-table mail-extr-address-syntax-table)
       (widen)
       (erase-buffer)
       (setq case-fold-search nil)
       
       ;; Insert extra space at beginning to allow later replacement with <
       ;; without having to move markers.
-      (insert mail-space-char address)
+      (insert ?\ )
+
+      ;; Insert the address itself.
+      (cond ((stringp address)
+	     (insert address))
+	    ((bufferp address)
+	     (insert-buffer-substring address))
+	    (t
+	     (error "Illegal address: %s" address)))
       
       ;; stolen from rfc822.el
       ;; Unfold multiple lines.
@@ -449,7 +770,7 @@
       ;; first pass grabs useful information about address
       (goto-char (point-min))
       (while (progn
-	       (skip-chars-forward mail-whitespace)
+	       (mail-extr-skip-whitespace-forward)
 	       (not (eobp)))
 	(setq char (char-after (point)))
 	(or first-real-pos
@@ -458,51 +779,61 @@
 	(cond
 	 ;; comment
 	 ((eq char ?\()
-	  (set-syntax-table address-comment-syntax-table)
+	  (set-syntax-table mail-extr-address-comment-syntax-table)
 	  ;; only record the first non-empty comment's position
-	  (if (and (not comment-beg)
+	  (if (and (not cbeg)
 		   (save-excursion
 		     (forward-char 1)
-		     (skip-chars-forward mail-whitespace)
+		     (mail-extr-skip-whitespace-forward)
 		     (not (eq ?\) (char-after (point))))))
-	      (setq comment-beg (point)))
+	      (setq cbeg (point)))
 	  ;; TODO: don't record if unbalanced
-	  (or (mail-safe-move-sexp 1)
+	  (or (mail-extr-safe-move-sexp 1)
 	      (forward-char 1))
-	  (set-syntax-table address-syntax-table)
-	  (if (and comment-beg
-		   (not comment-end))
-	      (setq comment-end (point))))
+	  (set-syntax-table mail-extr-address-syntax-table)
+	  (if (and cbeg
+		   (not cend))
+	      (setq cend (point))))
 	 ;; quoted text
 	 ((eq char ?\")
 	  ;; only record the first non-empty quote's position
 	  (if (and (not quote-beg)
 		   (save-excursion
 		     (forward-char 1)
-		     (skip-chars-forward mail-whitespace)
+		     (mail-extr-skip-whitespace-forward)
 		     (not (eq ?\" (char-after (point))))))
 	      (setq quote-beg (point)))
 	  ;; TODO: don't record if unbalanced
-	  (or (mail-safe-move-sexp 1)
+	  (or (mail-extr-safe-move-sexp 1)
 	      (forward-char 1))
 	  (if (and quote-beg
 		   (not quote-end))
 	      (setq quote-end (point))))
 	 ;; domain literals
 	 ((eq char ?\[)
-	  (set-syntax-table address-domain-literal-syntax-table)
-	  (or (mail-safe-move-sexp 1)
+	  (set-syntax-table mail-extr-address-domain-literal-syntax-table)
+	  (or (mail-extr-safe-move-sexp 1)
 	      (forward-char 1))
-	  (set-syntax-table address-syntax-table))
+	  (set-syntax-table mail-extr-address-syntax-table))
 	 ;; commas delimit addresses when outside < > pairs.
 	 ((and (eq char ?,)
-	       (or (null <-pos)
+	       (or (and (null <-pos)
+			;; Handle ROUTE-ADDR address that is missing its <.
+			(not (eq ?@ (char-after (1+ (point))))))
 		   (and >-pos
 			;; handle weird munged addresses
-			(> (mail-last-element <-pos) (car >-pos)))))
-	  (setq multiple-addresses t)
-	  (delete-char 1)
-	  (narrow-to-region (point-min) (point)))
+			;; BUG FIX: This test was reversed.  Thanks to the
+			;; brilliant Rod Whitby <rwhitby@research.canon.oz.au>
+			;; for discovering this!
+			(< (mail-extr-last <-pos) (car >-pos)))))
+;; It'd be great if some day this worked, but for now, punt.
+;;	  (setq multiple-addresses t)
+;;	  ;; *** Why do I want this:
+;;	  (mail-extr-delete-char 1)
+;;	  (narrow-to-region (point-min) (point))
+	  (delete-region (point) (point-max))
+	  (setq char ?\() ; HAVE I NO SHAME??
+	  )
 	 ;; record the position of various interesting chars, determine
 	 ;; legality later.
 	 ((setq record-pos-symbol
@@ -524,30 +855,35 @@
 		       ;; domain literals, and comments
 		       ?\\
 		       ))
-	  (mail-nuke-char-at (point))
+	  (mail-extr-nuke-char-at (point))
 	  (forward-char 1))
 	 (t
 	  (forward-word 1)))
 	(or (eq char ?\()
+	    ;; At the end of first address of a multiple address header.
+	    (and (eq char ?,)
+		 (eobp))
 	    (setq last-real-pos (point))))
       
       ;; Use only the leftmost <, if any.  Replace all others with spaces.
       (while (cdr <-pos)
-	(mail-nuke-char-at (car <-pos))
+	(mail-extr-nuke-char-at (car <-pos))
 	(setq <-pos (cdr <-pos)))
       
       ;; Use only the rightmost >, if any.  Replace all others with spaces.
       (while (cdr >-pos)
-	(mail-nuke-char-at (nth 1 >-pos))
+	(mail-extr-nuke-char-at (nth 1 >-pos))
 	(setcdr >-pos (nthcdr 2 >-pos)))
       
       ;; If multiple @s and a :, but no < and >, insert around buffer.
+      ;; Example: @foo.bar.dom,@xxx.yyy.zzz:mailbox@aaa.bbb.ccc
       ;; This commonly happens on the UUCP "From " line.  Ugh.
       (cond ((and (> (length @-pos) 1)
-		  :-pos			;TODO: check if between @s
+		  (eq 1 (length :-pos))	;TODO: check if between last two @s
+		  (not \;-pos)
 		  (not <-pos))
 	     (goto-char (point-min))
-	     (delete-char 1)
+	     (mail-extr-delete-char 1)
 	     (setq <-pos (list (point)))
 	     (insert ?<)))
       
@@ -561,7 +897,7 @@
       ;; If > but no <, replace > with space.
       (cond ((and >-pos
 		  (null <-pos))
-	     (mail-nuke-char-at (car >-pos))
+	     (mail-extr-nuke-char-at (car >-pos))
 	     (setq >-pos nil)))
 
       ;; Turn >-pos and <-pos into non-lists
@@ -573,12 +909,12 @@
       (cond (<-pos			; don't need to check >-pos also
 	     ;; handle bozo software that violates RFC 822 by sticking
 	     ;; punctuation marks outside of a < > pair
-	     (mail-nuke-elements-outside-range @-pos <-pos >-pos t)
+	     (mail-extr-nuke-outside-range @-pos <-pos >-pos t)
 	     ;; RFC 822 says nothing about these two outside < >, but
 	     ;; remove those positions from the lists to make things
 	     ;; easier.
-	     (mail-nuke-elements-outside-range !-pos <-pos >-pos t)
-	     (mail-nuke-elements-outside-range %-pos <-pos >-pos t)))
+	     (mail-extr-nuke-outside-range !-pos <-pos >-pos t)
+	     (mail-extr-nuke-outside-range %-pos <-pos >-pos t)))
       
       ;; Check for : that indicates GROUP list and for : part of
       ;; ROUTE-ADDR spec.
@@ -593,7 +929,7 @@
 		       (< (length @-pos) 2)
 		       (> temp (car @-pos))
 		       (< temp (nth 1 @-pos)))
-		   (mail-nuke-char-at temp)
+		   (mail-extr-nuke-char-at temp)
 		 (setq route-addr-:-pos temp)))
 	      ((or (not <-pos)
 		   (and <-pos
@@ -608,39 +944,57 @@
 	(cond ((and <-pos >-pos
 		    (> temp <-pos)
 		    (< temp >-pos))
-	       (mail-nuke-char-at temp))
+	       (mail-extr-nuke-char-at temp))
 	      ((and (or (not group-:-pos)
 			(> temp group-:-pos))
 		    (not group-\;-pos))
 	       (setq group-\;-pos temp))))
       
+      ;; Nuke unmatched GROUP syntax characters.
+      (cond ((and group-:-pos (not group-\;-pos))
+	     ;; *** Do I really need to erase it?
+	     (mail-extr-nuke-char-at group-:-pos)
+	     (setq group-:-pos nil)))
+      (cond ((and group-\;-pos (not group-:-pos))
+	     ;; *** Do I really need to erase it?
+	     (mail-extr-nuke-char-at group-\;-pos)
+	     (setq group-\;-pos nil)))
+      
       ;; Handle junk like ";@host.company.dom" that sendmail adds.
       ;; **** should I remember comment positions?
-      (and group-\;-pos
-	   ;; this is fine for now
-	   (mail-nuke-elements-outside-range !-pos group-:-pos group-\;-pos t)
-	   (mail-nuke-elements-outside-range @-pos group-:-pos group-\;-pos t)
-	   (mail-nuke-elements-outside-range %-pos group-:-pos group-\;-pos t)
-	   (mail-nuke-elements-outside-range ,-pos group-:-pos group-\;-pos t)
-	   (and last-real-pos
-		(> last-real-pos (1+ group-\;-pos))
-		(setq last-real-pos (1+ group-\;-pos)))
-	   (and comment-end
-		(> comment-end group-\;-pos)
-		(setq comment-end nil
-		      comment-beg nil))
-	   (and quote-end
-		(> quote-end group-\;-pos)
-		(setq quote-end nil
-		      quote-beg nil))
-	   (narrow-to-region (point-min) group-\;-pos))
+      (cond
+       (group-\;-pos
+	;; this is fine for now
+	(mail-extr-nuke-outside-range !-pos group-:-pos group-\;-pos t)
+	(mail-extr-nuke-outside-range @-pos group-:-pos group-\;-pos t)
+	(mail-extr-nuke-outside-range %-pos group-:-pos group-\;-pos t)
+	(mail-extr-nuke-outside-range ,-pos group-:-pos group-\;-pos t)
+	(and last-real-pos
+	     (> last-real-pos (1+ group-\;-pos))
+	     (setq last-real-pos (1+ group-\;-pos)))
+	;; *** This may be wrong:
+        (and cend
+             (> cend group-\;-pos)
+             (setq cend nil
+                   cbeg nil))
+	(and quote-end
+	     (> quote-end group-\;-pos)
+	     (setq quote-end nil
+		   quote-beg nil))
+	;; This was both wrong and unnecessary:
+	;;(narrow-to-region (point-min) group-\;-pos)
+
+	;; *** The entire handling of GROUP addresses seems rather lame.
+	;; *** It deserves a complete rethink, except that these addresses
+	;; *** are hardly ever seen.
+	))
       
       ;; Any commas must be between < and : of ROUTE-ADDR.  Nuke any
       ;; others.
       ;; Hell, go ahead an nuke all of the commas.
       ;; **** This will cause problems when we start handling commas in
       ;; the PHRASE part .... no it won't ... yes it will ... ?????
-      (mail-nuke-elements-outside-range ,-pos 1 1)
+      (mail-extr-nuke-outside-range ,-pos 1 1)
       
       ;; can only have multiple @s inside < >.  The fact that some MTAs
       ;; put de-bracketed ROUTE-ADDRs in the UUCP-style "From " line is
@@ -649,11 +1003,11 @@
       ;; Locate PHRASE part of ROUTE-ADDR.
       (cond (<-pos
 	     (goto-char <-pos)
-	     (skip-chars-backward mail-whitespace)
+	     (mail-extr-skip-whitespace-backward)
 	     (setq phrase-end (point))
 	     (goto-char (or ;;group-:-pos
 			    (point-min)))
-	     (skip-chars-forward mail-whitespace)
+	     (mail-extr-skip-whitespace-forward)
 	     (if (< (point) phrase-end)
 		 (setq phrase-beg (point))
 	       (setq phrase-end nil))))
@@ -671,7 +1025,7 @@
 	     (insert-before-markers ?X)
 	     (goto-char (car @-pos))
 	     (while (setq @-pos (cdr @-pos))
-	       (delete-char 1)
+	       (mail-extr-delete-char 1)
 	       (setq %-pos (cons (point-marker) %-pos))
 	       (insert "%")
 	       (goto-char (1- >-pos))
@@ -683,12 +1037,12 @@
 		   (setq saved-@-pos (list (point)))))
 	     (setq @-pos saved-@-pos)
 	     (goto-char >-pos)
-	     (delete-char -1)
-	     (mail-nuke-char-at route-addr-:-pos)
-	     (mail-demarkerize route-addr-:-pos)
+	     (mail-extr-delete-char -1)
+	     (mail-extr-nuke-char-at route-addr-:-pos)
+	     (mail-extr-demarkerize route-addr-:-pos)
 	     (setq route-addr-:-pos nil
-		   >-pos (mail-demarkerize >-pos)
-		   %-pos (mapcar 'mail-demarkerize %-pos))))
+		   >-pos (mail-extr-demarkerize >-pos)
+		   %-pos (mapcar 'mail-extr-demarkerize %-pos))))
       
       ;; de-listify @-pos
       (setq @-pos (car @-pos))
@@ -696,9 +1050,10 @@
       ;; TODO: remove comments in the middle of an address
       
       (set-buffer canonicalization-buffer)
-      
+      (fundamental-mode)
+      (kill-all-local-variables)
       (buffer-disable-undo canonicalization-buffer)
-      (set-syntax-table address-syntax-table)
+      (set-syntax-table mail-extr-address-syntax-table)
       (setq case-fold-search nil)
       
       (widen)
@@ -708,43 +1063,57 @@
       (if <-pos
 	  (narrow-to-region (progn
 			      (goto-char (1+ <-pos))
-			      (skip-chars-forward mail-whitespace)
+			      (mail-extr-skip-whitespace-forward)
 			      (point))
 			    >-pos)
-	;; ****** Oh no!  What if the address is completely empty!
-	(narrow-to-region first-real-pos last-real-pos))
+	(if (and first-real-pos last-real-pos)
+	    (narrow-to-region first-real-pos last-real-pos)
+	  ;; ****** Oh no!  What if the address is completely empty!
+	  ;; *** Is this correct?
+	  (narrow-to-region (point-max) (point-max))
+	  ))
       
       (and @-pos %-pos
-	   (mail-nuke-elements-outside-range %-pos (point-min) @-pos))
+	   (mail-extr-nuke-outside-range %-pos (point-min) @-pos))
       (and %-pos !-pos
-	   (mail-nuke-elements-outside-range !-pos (point-min) (car %-pos)))
+	   (mail-extr-nuke-outside-range !-pos (point-min) (car %-pos)))
       (and @-pos !-pos (not %-pos)
-	   (mail-nuke-elements-outside-range !-pos (point-min) @-pos))
+	   (mail-extr-nuke-outside-range !-pos (point-min) @-pos))
       
       ;; Error condition:?? (and %-pos (not @-pos))
+      
+      ;; WARNING: THIS CODE IS DUPLICATED BELOW.
+      (cond ((and %-pos
+		  (not @-pos))
+	     (goto-char (car %-pos))
+	     (mail-extr-delete-char 1)
+	     (setq @-pos (point))
+	     (insert "@")
+	     (setq %-pos (cdr %-pos))))
 
+      (if mail-extr-mangle-uucp
       (cond (!-pos
 	     ;; **** I don't understand this save-restriction and the
 	     ;; narrow-to-region inside it.  Why did I do that?
 	     (save-restriction
 	       (cond ((and @-pos
-			   mail-@-binds-tighter-than-!)
+			   mail-extr-@-binds-tighter-than-!)
 		      (goto-char @-pos)
 		      (setq %-pos (cons (point) %-pos)
 			    @-pos nil)
-		      (delete-char 1)
+		      (mail-extr-delete-char 1)
 		      (insert "%")
 		      (setq insert-point (point-max)))
-		     (mail-@-binds-tighter-than-!
+		     (mail-extr-@-binds-tighter-than-!
 		      (setq insert-point (point-max)))
 		     (%-pos
-		      (setq insert-point (mail-last-element %-pos)
-			    saved-%-pos (mapcar 'mail-markerize %-pos)
+		      (setq insert-point (mail-extr-last %-pos)
+			    saved-%-pos (mapcar 'mail-extr-markerize %-pos)
 			    %-pos nil
-			    @-pos (mail-markerize @-pos)))
+			    @-pos (mail-extr-markerize @-pos)))
 		     (@-pos
 		      (setq insert-point @-pos)
-		      (setq @-pos (mail-markerize @-pos)))
+		      (setq @-pos (mail-extr-markerize @-pos)))
 		     (t
 		      (setq insert-point (point-max))))
 	       (narrow-to-region (point-min) insert-point)
@@ -765,31 +1134,35 @@
 		      (1+ (nth 1 !-pos))
 		    (point-min))
 		  (car !-pos))
-		 (delete-char 1)
+		 (mail-extr-delete-char 1)
 		 (or (save-excursion
-		       (mail-safe-move-sexp -1)
-		       (skip-chars-backward mail-whitespace)
+		       (mail-extr-safe-move-sexp -1)
+		       (mail-extr-skip-whitespace-backward)
 		       (eq ?. (preceding-char)))
 		     (insert-before-markers
 		      (if (save-excursion
-			    (skip-chars-backward mail-whitespace)
+			    (mail-extr-skip-whitespace-backward)
 			    (eq ?. (preceding-char)))
 			  ""
 			".")
 		      "uucp"))
 		 (setq !-pos (cdr !-pos))))
 	     (and saved-%-pos
-		  (setq %-pos (append (mapcar 'mail-demarkerize saved-%-pos)
-					%-pos)))
-	     (setq @-pos (mail-demarkerize @-pos))
-	     (narrow-to-region (1+ saved-!-pos) (point-max))))
+		  (setq %-pos (append (mapcar 'mail-extr-demarkerize
+					      saved-%-pos)
+				      %-pos)))
+	     (setq @-pos (mail-extr-demarkerize @-pos))
+	     (narrow-to-region (1+ saved-!-pos) (point-max)))))
+
+      ;; WARNING: THIS CODE IS DUPLICATED ABOVE.
       (cond ((and %-pos
 		  (not @-pos))
 	     (goto-char (car %-pos))
-	     (delete-char 1)
+	     (mail-extr-delete-char 1)
 	     (setq @-pos (point))
 	     (insert "@")
 	     (setq %-pos (cdr %-pos))))
+
       (setq %-pos (nreverse %-pos))
       ;; RFC 1034 doesn't approve of this, oh well:
       (downcase-region (or (car %-pos) @-pos (point-max)) (point-max))
@@ -799,20 +1172,21 @@
 	       (while temp
 		 (goto-char (or (nth 1 temp)
 				@-pos))
-		 (skip-chars-backward mail-whitespace)
+		 (mail-extr-skip-whitespace-backward)
 		 (save-excursion
-		   (mail-safe-move-sexp -1)
+		   (mail-extr-safe-move-sexp -1)
 		   (setq domain-pos (point))
-		   (skip-chars-backward mail-whitespace)
+		   (mail-extr-skip-whitespace-backward)
 		   (setq \.-pos (eq ?. (preceding-char))))
 		 (cond ((and \.-pos
-			     (get
-			      (intern
-			       (buffer-substring domain-pos (point)))
-			      'domain-name))
+			     ;; #### string consing
+			     (let ((s (intern-soft
+				       (buffer-substring domain-pos (point))
+				       mail-extr-all-top-level-domains)))
+			       (and s (get s 'domain-name))))
 			(narrow-to-region (point-min) (point))
 			(goto-char (car temp))
-			(delete-char 1)
+			(mail-extr-delete-char 1)
 			(setq @-pos (point))
 			(setcdr temp nil)
 			(setq %-pos (delq @-pos %-pos))
@@ -828,214 +1202,181 @@
       
       (set-buffer extraction-buffer)
       
-      ;; Find the full name
-      
-      (cond ((and phrase-beg
+      ;; Decide what part of the address to search to find the full name.
+      (cond (
+	     ;; Example: "First M. Last" <fml@foo.bar.dom>
+	     (and phrase-beg
 		  (eq quote-beg phrase-beg)
 		  (<= quote-end phrase-end))
 	     (narrow-to-region (1+ quote-beg) (1- quote-end))
-	     (mail-undo-backslash-quoting (point-min) (point-max)))
+	     (mail-extr-undo-backslash-quoting (point-min) (point-max)))
+
+	    ;; Example: First Last <fml@foo.bar.dom>
 	    (phrase-beg
 	     (narrow-to-region phrase-beg phrase-end))
-	    (comment-beg
-	     (narrow-to-region (1+ comment-beg) (1- comment-end))
-	     (mail-undo-backslash-quoting (point-min) (point-max)))
+
+	    ;; Example: fml@foo.bar.dom (First M. Last)
+	    (cbeg
+	     (narrow-to-region (1+ cbeg) (1- cend))
+	     (mail-extr-undo-backslash-quoting (point-min) (point-max))
+	     
+	     ;; Deal with spacing problems
+	     (goto-char (point-min))
+;	     (cond ((not (search-forward " " nil t))
+;		    (goto-char (point-min))
+;		    (cond ((search-forward "_" nil t)
+;			   ;; Handle the *idiotic* use of underlines as spaces.
+;			   ;; Example: fml@foo.bar.dom (First_M._Last)
+;			   (goto-char (point-min))
+;			   (while (search-forward "_" nil t)
+;			     (replace-match " " t)))
+;			  ((search-forward "." nil t)
+;			   ;; Fix . used as space
+;			   ;; Example: danj1@cb.att.com (daniel.jacobson)
+;			   (goto-char (point-min))
+;			   (while (re-search-forward mail-extr-bad-dot-pattern nil t)
+;			     (replace-match "\\1 \\2" t))))))
+	     )
+	    
+	    ;; Otherwise we try to get the name from the mailbox portion
+	    ;; of the address.
+	    ;; Example: First_M_Last@foo.bar.dom
 	    (t
 	     ;; *** Work in canon buffer instead?  No, can't.  Hmm.
-	     (delete-region (point-min) (point-max))
+	     (goto-char (point-max))
+	     (narrow-to-region (point) (point))
 	     (insert-buffer-substring canonicalization-buffer
 				      mbox-beg mbox-end)
 	     (goto-char (point-min))
-	     (setq \.-ends-name (search-forward "_" nil t))
+	     
+	     ;; Example: First_Last.XXX@foo.bar.dom
+	     (setq \.-ends-name (re-search-forward "[_0-9]" nil t))
+	     
 	     (goto-char (point-min))
+
+	     (if (not mail-extr-mangle-uucp)
+		 (modify-syntax-entry ?! "w" (syntax-table)))
+
 	     (while (progn
-		      (skip-chars-forward mail-whitespace)
+		      (mail-extr-skip-whitespace-forward)
 		      (not (eobp)))
 	       (setq char (char-after (point)))
 	       (cond
 		((eq char ?\")
 		 (setq quote-beg (point))
-		 (or (mail-safe-move-sexp 1)
+		 (or (mail-extr-safe-move-sexp 1)
 		     ;; TODO: handle this error condition!!!!!
 		     (forward-char 1))
 		 ;; take into account deletions
 		 (setq quote-end (- (point) 2))
 		 (save-excursion
 		   (backward-char 1)
-		   (delete-char 1)
+		   (mail-extr-delete-char 1)
 		   (goto-char quote-beg)
-		   (delete-char 1))
-		 (mail-undo-backslash-quoting quote-beg quote-end)
-		 (or (eq mail-space-char (char-after (point)))
+		   (mail-extr-delete-char 1))
+		 (mail-extr-undo-backslash-quoting quote-beg quote-end)
+		 (or (eq ?\  (char-after (point)))
 		     (insert " "))
+;;		 (setq mailbox-name-processed-flag t)
 		 (setq \.-ends-name t))
 		((eq char ?.)
-		 (if (eq (char-after (1+ (point))) ?_)
+		 (if (memq (char-after (1+ (point))) '(?_ ?=))
 		     (progn
 		       (forward-char 1)
-		       (delete-char 1)
-		       (insert mail-space-char))
+		       (mail-extr-delete-char 1)
+		       (insert ?\ ))
 		   (if \.-ends-name
 		       (narrow-to-region (point-min) (point))
-		     (delete-char 1)
-		     (insert " "))))
+		     (mail-extr-delete-char 1)
+		     (insert " ")))
+;;		 (setq mailbox-name-processed-flag t)
+		 )
 		((memq (char-syntax char) '(?. ?\\))
-		 (delete-char 1)
-		 (insert " "))
+		 (mail-extr-delete-char 1)
+		 (insert " ")
+;;		 (setq mailbox-name-processed-flag t)
+		 )
 		(t
 		 (setq atom-beg (point))
 		 (forward-word 1)
 		 (setq atom-end (point))
+		 (goto-char atom-beg)
 		 (save-restriction
 		   (narrow-to-region atom-beg atom-end)
-		   (goto-char (point-min))
-		   (while (re-search-forward "\\([^_]+\\)_" nil t)
-		     (replace-match "\\1 "))
-		   (goto-char (point-max))))))))
+		   (cond
+		    
+		    ;; Handle X.400 addresses encoded in RFC-822.
+		    ;; *** Shit!  This has to handle the case where it is
+		    ;; *** embedded in a quote too!
+		    ;; *** Shit!  The input is being broken up into atoms
+		    ;; *** by periods!
+		    ((looking-at mail-extr-x400-encoded-address-pattern)
+		     
+		     ;; Copy the contents of the individual fields that
+		     ;; might hold name data to the beginning.
+		     (mapcar
+		      (function
+		       (lambda (field-pattern)
+			 (cond
+			  ((save-excursion
+			     (re-search-forward field-pattern nil t))
+			   (insert-buffer-substring (current-buffer)
+						    (match-beginning 1)
+						    (match-end 1))
+			   (insert " ")))))
+		      (list mail-extr-x400-encoded-address-given-name-pattern
+			    mail-extr-x400-encoded-address-surname-pattern
+			    mail-extr-x400-encoded-address-full-name-pattern))
+		     
+		     ;; Discard the rest, since it contains stuff like
+		     ;; routing information, not part of a name.
+		     (mail-extr-skip-whitespace-backward)
+		     (delete-region (point) (point-max))
+		     
+		     ;; Handle periods used for spacing.
+		     (while (re-search-forward mail-extr-bad-dot-pattern nil t)
+		       (replace-match "\\1 \\2" t))
+		     
+;;		     (setq mailbox-name-processed-flag t)
+		     )
+		    
+		    ;; Handle normal addresses.
+		    (t
+		     (goto-char (point-min))
+		     ;; Handle _ and = used for spacing.
+		     (while (re-search-forward "\\([^_=]+\\)[_=]" nil t)
+		       (replace-match "\\1 " t)
+;;		       (setq mailbox-name-processed-flag t)
+		       )
+		     (goto-char (point-max))))))))
+
+	     ;; undo the dirty deed
+	     (if (not mail-extr-mangle-uucp)
+		 (modify-syntax-entry ?! "." (syntax-table)))
+	     ;;
+	     ;; If we derived the name from the mailbox part of the address,
+	     ;; and we only got one word out of it, don't treat that as a
+	     ;; name.  "foo@bar" --> (nil "foo@bar"), not ("foo" "foo@bar")
+             ;; (if (not mailbox-name-processed-flag)
+             ;;     (delete-region (point-min) (point-max)))
+	     ))
       
-      (set-syntax-table address-text-syntax-table)
+      (set-syntax-table mail-extr-address-text-syntax-table)
       
-      (setq xxx (mail-variant-method (buffer-string)))
-      (delete-region (point-min) (point-max))
-      (insert xxx)
+      (mail-extr-voodoo mbox-beg mbox-end canonicalization-buffer)
       (goto-char (point-min))
 
-;;       ;; Compress whitespace
-;;       (goto-char (point-min))
-;;       (while (re-search-forward "[ \t\n]+" nil t)
-;; 	(replace-match " "))
-;;       
-;;       ;; Fix . used as space
-;;       (goto-char (point-min))
-;;       (while (re-search-forward mail-bad-\.-pattern nil t)
-;; 	(replace-match "\\1 \\2"))
-;; 
-;;       ;; Delete trailing parenthesized comment
-;;       (goto-char (point-max))
-;;       (skip-chars-backward mail-whitespace)
-;;       (cond ((memq (char-after (1- (point))) '(?\) ?\} ?\]))
-;; 	     (setq comment-end (point))
-;; 	     (set-syntax-table address-text-comment-syntax-table)
-;; 	     (or (mail-safe-move-sexp -1)
-;; 		 (backward-char 1))
-;; 	     (set-syntax-table address-text-syntax-table)
-;; 	     (setq comment-beg (point))
-;; 	     (skip-chars-backward mail-whitespace)
-;; 	     (if (bobp)
-;; 		 (narrow-to-region (1+ comment-beg) (1- comment-end))
-;; 	       (narrow-to-region (point-min) (point)))))
-;;       
-;;       ;; Find, save, and delete any name suffix
-;;       ;; *** Broken!
-;;       (goto-char (point-min))
-;;       (cond ((re-search-forward mail-full-name-suffix-pattern nil t)
-;; 	     (setq name-suffix (buffer-substring (match-beginning 3)
-;; 						 (match-end 3)))
-;; 	     (replace-match "\\1 \\4")))
-;;       
-;;       ;; Delete ALL CAPS words and after, if preceded by mixed-case or
-;;       ;; lowercase words.  Eg. XT-DEM.
-;;       (goto-char (point-min))
-;;       ;; ## This will lose on something like "SMITH MAX".
-;;       ;; ## maybe it should be
-;;       ;; ##  " \\([A-Z]+[-_/][A-Z]+\\|[A-Z][A-Z][A-Z]\\)\\b.*[^A-Z \t]"
-;;       ;; ## that is, three-letter-upper-case-word with non-upper-case
-;;       ;; ## characters following it.
-;;       (if (re-search-forward mail-mixed-case-name-pattern nil t)
-;; 	  (if (re-search-forward mail-weird-acronym-pattern nil t)
-;; 	      (narrow-to-region (point-min) (match-beginning 0))))
-;;       
-;;       ;; Delete trailing alternative address
-;;       (goto-char (point-min))
-;;       (if (re-search-forward mail-alternative-address-pattern nil t)
-;; 	  (narrow-to-region (point-min) (match-beginning 0)))
-;;       
-;;       ;; Delete trailing comment
-;;       (goto-char (point-min))
-;;       (if (re-search-forward mail-trailing-comment-start-pattern nil t)
-;; 	  (or (progn
-;; 		(goto-char (match-beginning 0))
-;; 		(skip-chars-backward mail-whitespace)
-;; 		(bobp))
-;; 	      (narrow-to-region (point-min) (match-beginning 0))))
-;;       
-;;       ;; Delete trailing comma-separated comment
-;;       (goto-char (point-min))
-;;       ;; ## doesn't this break "Smith, John"?  Yes.
-;;       (re-search-forward mail-last-name-first-pattern nil t)
-;;       (while (search-forward "," nil t)
-;; 	(or (save-excursion
-;; 	      (backward-char 2)
-;; 	      (looking-at mail-full-name-suffix-pattern))
-;; 	    (narrow-to-region (point-min) (1- (point)))))
-;;       
-;;       ;; Delete telephone numbers and ham radio call signs
-;;       (goto-char (point-min))
-;;       (if (re-search-forward mail-telephone-extension-pattern nil t)
-;; 	  (narrow-to-region (point-min) (match-beginning 0)))
-;;       (goto-char (point-min))
-;;       (if (re-search-forward mail-ham-call-sign-pattern nil t)
-;; 	  (if (eq (match-beginning 0) (point-min))
-;; 	      (narrow-to-region (match-end 0) (point-max))
-;; 	    (narrow-to-region (point-min) (match-beginning 0))))
-;;       
-;;       ;; Delete trailing word followed immediately by .
-;;       (goto-char (point-min))
-;;       ;; ## what's this for?  doesn't it mess up "Public, Harry Q."?  No.
-;;       (if (re-search-forward "\\b[A-Za-z][A-Za-z]+\\. *\\'" nil t)
-;; 	  (narrow-to-region (point-min) (match-beginning 0)))
-;;       
-;;       ;; Handle & substitution
-;;       ;; TODO: remember to disable middle initial guessing
-;;       (goto-char (point-min))
-;;       (cond ((re-search-forward "\\( \\|\\`\\)&\\( \\|\\'\\)" nil t)
-;; 	     (goto-char (match-end 1))
-;; 	     (delete-char 1)
-;; 	     (capitalize-region
-;; 	      (point)
-;; 	      (progn
-;; 		(insert-buffer-substring canonicalization-buffer
-;; 					 mbox-beg mbox-end)
-;; 		(point)))))
-;;       
-;;       ;; Delete nickname
-;;       (goto-char (point-min))
-;;       (if (re-search-forward mail-nickname-pattern nil t)
-;; 	  (replace-match (if (eq (match-beginning 2) (1- (match-end 2)))
-;; 			     " \\2 "
-;; 			   " ")))
-;;       
-;;       ;; Fixup initials
-;;       (while (progn
-;; 	       (goto-char (point-min))
-;; 	       (re-search-forward mail-bad-initials-pattern nil t))
-;; 	(replace-match
-;; 	 (if (match-beginning 4)
-;; 	     "\\1. \\4"
-;; 	   (if (match-beginning 5)
-;; 	       "\\1. \\5"
-;; 	     "\\1. "))))
-;;       
-;;       ;; Delete title
-;;       (goto-char (point-min))
-;;       (if (re-search-forward mail-full-name-prefixes nil t)
-;; 	  (narrow-to-region (point) (point-max)))
-;;       
-;;       ;; Delete trailing and preceding non-name characters
-;;       (goto-char (point-min))
-;;       (skip-chars-forward mail-non-begin-name-chars)
-;;       (narrow-to-region (point) (point-max))
-;;       (goto-char (point-max))
-;;       (skip-chars-backward mail-non-end-name-chars)
-;;       (narrow-to-region (point-min) (point))
-      
       ;; If name is "First Last" and userid is "F?L", then assume
       ;; the middle initial is the second letter in the userid.
-      ;; initially by Jamie Zawinski <jwz@lucid.com>
-      (cond ((and (eq 3 (- mbox-end mbox-beg))
+      ;; Initial code by Jamie Zawinski <jwz@lucid.com>
+      ;; *** Make it work when there's a suffix as well.
+      (goto-char (point-min))
+      (cond ((and mail-extr-guess-middle-initial
+		  (not disable-initial-guessing-flag)
+		  (eq 3 (- mbox-end mbox-beg))
 		  (progn
 		    (goto-char (point-min))
-		    (looking-at mail-two-name-pattern)))
+		    (looking-at mail-extr-two-name-pattern)))
 	     (setq fi (char-after (match-beginning 0))
 		   li (char-after (match-beginning 3)))
 	     (save-excursion
@@ -1052,417 +1393,506 @@
 		    (goto-char (match-beginning 3))
 		    (insert (upcase mi) ". ")))))
       
-;;       ;; Restore suffix
-;;       (cond (name-suffix
-;; 	     (goto-char (point-max))
-;; 	     (insert ", " name-suffix)
-;; 	     (backward-word 1)
-;; 	     (cond ((memq (following-char) '(?j ?J ?s ?S))
-;; 		    (capitalize-word 1)
-;; 		    (or (eq (following-char) ?.)
-;; 			(insert ?.)))
-;; 		   (t
-;; 		    (upcase-word 1)))))
+      ;; Nuke name if it is the same as mailbox name.
+      (let ((buffer-length (- (point-max) (point-min)))
+	    (i 0)
+	    (names-match-flag t))
+	(cond ((and (> buffer-length 0)
+		    (eq buffer-length (- mbox-end mbox-beg)))
+	       (goto-char (point-max))
+	       (insert-buffer-substring canonicalization-buffer
+					mbox-beg mbox-end)
+	       (while (and names-match-flag
+			   (< i buffer-length))
+		 (or (eq (downcase (char-after (+ i (point-min))))
+			 (downcase
+			  (char-after (+ i buffer-length (point-min)))))
+		     (setq names-match-flag nil))
+		 (setq i (1+ i)))
+	       (delete-region (+ (point-min) buffer-length) (point-max))
+	       (if names-match-flag
+		   (narrow-to-region (point) (point))))))
+      
+      ;; Nuke name if it's just one word.
+      (goto-char (point-min))
+      (and mail-extr-ignore-single-names
+	   (not (re-search-forward "[- ]" nil t))
+	   (narrow-to-region (point) (point)))
       
       ;; Result
-      (list (buffer-string)
+      (list (if (not (= (point-min) (point-max)))
+		(buffer-string))
 	    (progn
 	      (set-buffer canonicalization-buffer)
-	      (buffer-string)))
+	      (if (not (= (point-min) (point-max)))
+		  (buffer-string))))
       )))
 
-;; TODO: put this back in the above function now that it's proven:
-(defun mail-variant-method (string)
-  (let ((variant-buffer (get-buffer-create "*variant method buffer*"))
-	(word-count 0)
-	mixed-case-flag lower-case-flag upper-case-flag
+(defun mail-extr-voodoo (mbox-beg mbox-end canonicalization-buffer)
+  (let ((word-count 0)
+	(case-fold-search nil)
+	mixed-case-flag lower-case-flag ;;upper-case-flag
 	suffix-flag last-name-comma-flag
-	comment-beg comment-end initial beg end
+	;;cbeg cend
+	initial
+	begin-again-flag
+	drop-this-word-if-trailing-flag
+	drop-last-word-if-trailing-flag
+	word-found-flag
+	this-word-beg last-word-beg
+	name-beg name-end
+	name-done-flag
 	)
     (save-excursion
-      (set-buffer variant-buffer)
-      (buffer-disable-undo variant-buffer)
-      (set-syntax-table address-text-syntax-table)
-      (widen)
-      (erase-buffer)
-      (setq case-fold-search nil)
+      (set-syntax-table mail-extr-address-text-syntax-table)
       
-      (insert string)
-      
+      ;; This was moved above.
       ;; Fix . used as space
+      ;; But it belongs here because it occurs not only as
+      ;;   rypens@reks.uia.ac.be (Piet.Rypens)
+      ;; but also as
+      ;;   "Piet.Rypens" <rypens@reks.uia.ac.be>
+      ;;(goto-char (point-min))
+      ;;(while (re-search-forward mail-extr-bad-dot-pattern nil t)
+      ;;  (replace-match "\\1 \\2" t))
+
+      (cond ((not (search-forward " " nil t))
+	     (goto-char (point-min))
+	     (cond ((search-forward "_" nil t)
+		    ;; Handle the *idiotic* use of underlines as spaces.
+		    ;; Example: fml@foo.bar.dom (First_M._Last)
+		    (goto-char (point-min))
+		    (while (search-forward "_" nil t)
+		      (replace-match " " t)))
+		   ((search-forward "." nil t)
+		    ;; Fix . used as space
+		    ;; Example: danj1@cb.att.com (daniel.jacobson)
+		    (goto-char (point-min))
+		    (while (re-search-forward mail-extr-bad-dot-pattern nil t)
+		      (replace-match "\\1 \\2" t))))))
+
+
+      ;; Loop over the words (and other junk) in the name.
       (goto-char (point-min))
-      (while (re-search-forward mail-bad-\.-pattern nil t)
-	(replace-match "\\1 \\2"))
+      (while (not name-done-flag)
+	
+	(cond (word-found-flag
+	       ;; Last time through this loop we skipped over a word.
+	       (setq last-word-beg this-word-beg)
+	       (setq drop-last-word-if-trailing-flag
+		     drop-this-word-if-trailing-flag)
+	       (setq word-found-flag nil)))
 
-      ;; Skip any initial garbage.
-      (goto-char (point-min))
-      (skip-chars-forward mail-non-begin-name-chars)
-      (skip-chars-backward "& \"")
-      (narrow-to-region (point) (point-max))
-      
-      (catch 'stop
-	(while t
-	  (skip-chars-forward mail-whitespace)
-	  
+	(cond (begin-again-flag
+	       ;; Last time through the loop we found something that
+	       ;; indicates we should pretend we are beginning again from
+	       ;; the start.
+	       (setq word-count 0)
+	       (setq last-word-beg nil)
+	       (setq drop-last-word-if-trailing-flag nil)
+	       (setq mixed-case-flag nil)
+	       (setq lower-case-flag nil)
+;;	       (setq upper-case-flag nil)
+	       (setq begin-again-flag nil)
+	       ))
+	
+	;; Initialize for this iteration of the loop.
+	(mail-extr-skip-whitespace-forward)
+	(if (eq word-count 0) (narrow-to-region (point) (point-max)))
+	(setq this-word-beg (point))
+	(setq drop-this-word-if-trailing-flag nil)
+	
+	;; Decide what to do based on what we are looking at.
+	(cond
+	 
+	 ;; Delete title
+	 ((and (eq word-count 0)
+	       (looking-at mail-extr-full-name-prefixes))
+	  (goto-char (match-end 0))
+	  (narrow-to-region (point) (point-max)))
+	 
+	 ;; Stop after name suffix
+	 ((and (>= word-count 2)
+	       (looking-at mail-extr-full-name-suffix-pattern))
+	  (mail-extr-skip-whitespace-backward)
+	  (setq suffix-flag (point))
+	  (if (eq ?, (following-char))
+	      (forward-char 1)
+	    (insert ?,))
+	  ;; Enforce at least one space after comma
+	  (or (eq ?\  (following-char))
+	      (insert ?\ ))
+	  (mail-extr-skip-whitespace-forward)
+	  (cond ((memq (following-char) '(?j ?J ?s ?S))
+		 (capitalize-word 1)
+		 (if (eq (following-char) ?.)
+		     (forward-char 1)
+		   (insert ?.)))
+		(t
+		 (upcase-word 1)))
+	  (setq word-found-flag t)
+	  (setq name-done-flag t))
+	 
+	 ;; Handle SCA names
+	 ((looking-at "MKA \\(.+\\)")	; "Mundanely Known As"
+	  (goto-char (match-beginning 1))
+	  (narrow-to-region (point) (point-max))
+	  (setq begin-again-flag t))
+	 
+	 ;; Check for initial last name followed by comma
+	 ((and (eq ?, (following-char))
+	       (eq word-count 1))
+	  (forward-char 1)
+	  (setq last-name-comma-flag t)
+	  (or (eq ?\  (following-char))
+	      (insert ?\ )))
+	 
+	 ;; Stop before trailing comma-separated comment
+	 ;; THIS CASE MUST BE AFTER THE PRECEDING CASES.
+	 ;; *** This case is redundant???
+	 ;;((eq ?, (following-char))
+	 ;; (setq name-done-flag t))
+	 
+	 ;; Delete parenthesized/quoted comment/nickname
+	 ((memq (following-char) '(?\( ?\{ ?\[ ?\" ?\' ?\`))
+	  (setq cbeg (point))
+	  (set-syntax-table mail-extr-address-text-comment-syntax-table)
+	  (cond ((memq (following-char) '(?\' ?\`))
+		 (or (search-forward "'" nil t
+				     (if (eq ?\' (following-char)) 2 1))
+		     (mail-extr-delete-char 1)))
+		(t
+		 (or (mail-extr-safe-move-sexp 1)
+		     (goto-char (point-max)))))
+	  (set-syntax-table mail-extr-address-text-syntax-table)
+	  (setq cend (point))
 	  (cond
-	   
-	   ;; Delete title
+	   ;; Handle case of entire name being quoted
 	   ((and (eq word-count 0)
-		 (looking-at mail-full-name-prefixes))
-	    (goto-char (match-end 0))
-	    (narrow-to-region (point) (point-max)))
-	   
-	   ;; Stop after name suffix
-	   ((and (>= word-count 2)
-		 (looking-at mail-full-name-suffix-pattern))
-	    (skip-chars-backward mail-whitespace)
-	    (setq suffix-flag (point))
-	    (if (eq ?, (following-char))
-		(forward-char 1)
-	      (insert ?,))
-	    ;; Enforce at least one space after comma
-	    (or (eq mail-space-char (following-char))
-		(insert mail-space-char))
-	    (skip-chars-forward mail-whitespace)
-	    (cond ((memq (following-char) '(?j ?J ?s ?S))
-		   (capitalize-word 1)
-		   (if (eq (following-char) ?.)
-		       (forward-char 1)
-		     (insert ?.)))
-		  (t
-		   (upcase-word 1)))
-	    (setq word-count (1+ word-count))
-	    (throw 'stop t))
-	   
-	   ;; Handle SCA names
-	   ((looking-at "MKA \\(.+\\)")	; "Mundanely Known As"
-	    (setq word-count 0)
-	    (goto-char (match-beginning 1))
-	    (narrow-to-region (point) (point-max)))
-	   
-	   ;; Various stopping points
-	   ((or
-	     ;; Stop before ALL CAPS acronyms, if preceded by mixed-case or
-	     ;; lowercase words.  Eg. XT-DEM.
-	     (and (>= word-count 2)
-		  (or mixed-case-flag lower-case-flag)
-		  (looking-at mail-weird-acronym-pattern)
-		  (not (looking-at mail-roman-numeral-pattern)))
-	     ;; Stop before 4-or-more letter lowercase words preceded by
-	     ;; mixed case or uppercase words.
-	     (and (>= word-count 2)
-		  (or upper-case-flag mixed-case-flag)
-		  (looking-at "[a-z][a-z][a-z][a-z]+\\b"))
-	     ;; Stop before trailing alternative address
-	     (looking-at mail-alternative-address-pattern)
-	     ;; Stop before trailing comment not introduced by comma
-	     (looking-at mail-trailing-comment-start-pattern)
-	     ;; Stop before telephone numbers
-	     (looking-at mail-telephone-extension-pattern))
-	    (throw 'stop t))
+		 (looking-at " *\\'")
+		 (>= (- cend cbeg) 2))
+	    (narrow-to-region (1+ cbeg) (1- cend))
+	    (goto-char (point-min)))
+	   (t
+	    ;; Handle case of quoted initial
+	    (if (and (or (= 3 (- cend cbeg))
+			 (and (= 4 (- cend cbeg))
+			      (eq ?. (char-after (+ 2 cbeg)))))
+		     (not (looking-at " *\\'")))
+		(setq initial (char-after (1+ cbeg)))
+	      (setq initial nil))
+	    (delete-region cbeg cend)
+	    (if initial
+		(insert initial ". ")))))
+	 
+	 ;; Handle & substitution
+	 ((and (or (bobp)
+		   (eq ?\  (preceding-char)))
+	       (looking-at "&\\( \\|\\'\\)"))
+	  (mail-extr-delete-char 1)
+	  (capitalize-region
+	   (point)
+	   (progn
+	     (insert-buffer-substring canonicalization-buffer
+				      mbox-beg mbox-end)
+	     (point)))
+	  (setq disable-initial-guessing-flag t)
+	  (setq word-found-flag t))
+	 
+	 ;; Handle *Stupid* VMS date stamps
+	 ((looking-at mail-extr-stupid-vms-date-stamp-pattern)
+	  (replace-match "" t))
+	 
+	 ;; Handle Chinese characters.
+	 ((looking-at mail-extr-hz-embedded-gb-encoded-chinese-pattern)
+	  (goto-char (match-end 0))
+	  (setq word-found-flag t))
+	 
+	 ;; Skip initial garbage characters.
+	 ;; THIS CASE MUST BE AFTER THE PRECEDING CASES.
+	 ((and (eq word-count 0)
+	       (looking-at mail-extr-leading-garbage))
+	  (goto-char (match-end 0))
+	  ;; *** Skip backward over these???
+	  ;; (skip-chars-backward "& \"")
+	  (narrow-to-region (point) (point-max)))
+	 
+	 ;; Various stopping points
+	 ((or
 	   
-	   ;; Check for initial last name followed by comma
-	   ((and (eq ?, (following-char))
-		 (eq word-count 1))
-	    (forward-char 1)
-	    (setq last-name-comma-flag t)
-	    (or (eq mail-space-char (following-char))
-		(insert mail-space-char)))
-	   
-	   ;; Stop before trailing comma-separated comment
-	   ((eq ?, (following-char))
-	    (throw 'stop t))
+	   ;; Stop before ALL CAPS acronyms, if preceded by mixed-case
+	   ;; words.  Example: XT-DEM.
+	   (and (>= word-count 2)
+		mixed-case-flag
+		(looking-at mail-extr-weird-acronym-pattern)
+		(not (looking-at mail-extr-roman-numeral-pattern)))
 	   
-	   ;; Delete parenthesized/quoted comment/nickname
-	   ((memq (following-char) '(?\( ?\{ ?\[ ?\" ?\' ?\`))
-	    (setq comment-beg (point))
-	    (set-syntax-table address-text-comment-syntax-table)
-	    (cond ((memq (following-char) '(?\' ?\`))
-		   (if (eq ?\' (following-char))
-		       (forward-char 1))
-		   (or (search-forward "'" nil t)
-		       (delete-char 1)))
-		  (t
-		   (or (mail-safe-move-sexp 1)
-		       (goto-char (point-max)))))
-	    (set-syntax-table address-text-syntax-table)
-	    (setq comment-end (point))
-	    (cond
-	     ;; Handle case of entire name being quoted
-	     ((and (eq word-count 0)
-		   (looking-at " *\\'")
-		   (>= (- comment-end comment-beg) 2))
-	      (narrow-to-region (1+ comment-beg) (1- comment-end))
-	      (goto-char (point-min)))
-	     (t
-	      ;; Handle case of quoted initial
-	      (if (and (or (= 3 (- comment-end comment-beg))
-			   (and (= 4 (- comment-end comment-beg))
-				(eq ?. (char-after (+ 2 comment-beg)))))
-		       (not (looking-at " *\\'")))
-		  (setq initial (char-after (1+ comment-beg)))
-		(setq initial nil))
-	      (delete-region comment-beg comment-end)
-	      (if initial
-		  (insert initial ". ")))))
+	   ;; Stop before trailing alternative address
+	   (looking-at mail-extr-alternative-address-pattern)
+	   
+	   ;; Stop before trailing comment not introduced by comma
+	   ;; THIS CASE MUST BE AFTER AN EARLIER CASE.
+	   (looking-at mail-extr-trailing-comment-start-pattern)
 	   
-	   ;; Delete ham radio call signs
-	   ((looking-at mail-ham-call-sign-pattern)
-	    (delete-region (match-beginning 0) (match-end 0)))
-	   
-	   ;; Handle & substitution
-	   ;; TODO: remember to disable middle initial guessing
-	   ((and (or (bobp)
-		     (eq mail-space-char (preceding-char)))
-		 (looking-at "&\\( \\|\\'\\)"))
-	    (delete-char 1)
-	    (capitalize-region
-	     (point)
-	     (progn
-	       (insert-buffer-substring canonicalization-buffer
-					mbox-beg mbox-end)
-	       (point))))
-	   
-	   ;; Fixup initials
-	   ((looking-at mail-initial-pattern)
-	    (or (eq (following-char) (upcase (following-char)))
+	   ;; Stop before telephone numbers
+	   (looking-at mail-extr-telephone-extension-pattern))
+	  (setq name-done-flag t))
+	 
+	 ;; Delete ham radio call signs
+	 ((looking-at mail-extr-ham-call-sign-pattern)
+	  (delete-region (match-beginning 0) (match-end 0)))
+	 
+	 ;; Fixup initials
+	 ((looking-at mail-extr-initial-pattern)
+	  (or (eq (following-char) (upcase (following-char)))
+	      (setq lower-case-flag t))
+	  (forward-char 1)
+	  (if (eq ?. (following-char))
+	      (forward-char 1)
+	    (insert ?.))
+	  (or (eq ?\  (following-char))
+	      (insert ?\ ))
+	  (setq word-found-flag t))
+	 
+	 ;; Handle BITNET LISTSERV list names.
+	 ((and (eq word-count 0)
+	       (looking-at mail-extr-listserv-list-name-pattern))
+	  (narrow-to-region (match-beginning 1) (match-end 1))
+	  (setq word-found-flag t)
+	  (setq name-done-flag t))
+	 
+	 ;; Regular name words
+	 ((looking-at mail-extr-name-pattern)
+	  (setq name-beg (point))
+	  (setq name-end (match-end 0))
+	  
+	  ;; Certain words will be dropped if they are at the end.
+	  (and (>= word-count 2)
+	       (not lower-case-flag)
+	       (or
+		;; A trailing 4-or-more letter lowercase words preceded by
+		;; mixed case or uppercase words will be dropped.
+		(looking-at "[a-z][a-z][a-z][a-z]+[ \t]*\\'")
+		;; Drop a trailing word which is terminated with a period.
+		(eq ?. (char-after (1- name-end))))
+	       (setq drop-this-word-if-trailing-flag t))
+	  
+	  ;; Set the flags that indicate whether we have seen a lowercase
+	  ;; word, a mixed case word, and an uppercase word.
+	  (if (re-search-forward "[a-z]" name-end t)
+	      (if (progn
+		    (goto-char name-beg)
+		    (re-search-forward "[A-Z]" name-end t))
+		  (setq mixed-case-flag t)
 		(setq lower-case-flag t))
-	    (forward-char 1)
-	    (if (eq ?. (following-char))
-		(forward-char 1)
-	      (insert ?.))
-	    (or (eq mail-space-char (following-char))
-		(insert mail-space-char))
-	    (setq word-count (1+ word-count)))
-	   
-	   ;; Regular name words
-	   ((looking-at mail-name-pattern)
-	    (setq beg (point))
-	    (setq end (match-end 0))
-	    (set (if (re-search-forward "[a-z]" end t)
-		     (if (progn
-			   (goto-char beg)
-			   (re-search-forward "[A-Z]" end t))
-			 'mixed-case-flag
-		       'lower-case-flag)
-		   'upper-case-flag) t)
-	    (goto-char end)
-	    (setq word-count (1+ word-count)))
+;;	    (setq upper-case-flag t)
+	    )
+	  
+	  (goto-char name-end)
+	  (setq word-found-flag t))
 
-	   (t
-	    (throw 'stop t)))))
+	 (t
+	  (setq name-done-flag t)
+	  ))
+	
+	;; Count any word that we skipped over.
+	(if word-found-flag
+	    (setq word-count (1+ word-count))))
       
-      (narrow-to-region (point-min) (point))
+      ;; If the last thing in the name is 2 or more periods, or one or more
+      ;; other sentence terminators (but not a single period) then keep them
+      ;; and the preceeding word.  This is for the benefit of whole sentences
+      ;; in the name field: it's better behavior than dropping the last word
+      ;; of the sentence...
+      (if (and (not suffix-flag)
+	       (looking-at "\\(\\.+\\|[?!;:.][?!;:.]+\\|[?!;:][?!;:.]*\\)\\'"))
+	  (goto-char (setq suffix-flag (point-max))))
 
-      ;; Delete trailing word followed immediately by .
+      ;; Drop everything after point and certain trailing words.
+      (narrow-to-region (point-min)
+			(or (and drop-last-word-if-trailing-flag
+				 last-word-beg)
+			    (point)))
+      
+      ;; Xerox's mailers SUCK!!!!!!
+      ;; We simply refuse to believe that any last name is PARC or ADOC.
+      ;; If it looks like that is the last name, that there is no meaningful
+      ;; here at all.  Actually I guess it would be best to map patterns
+      ;; like foo.hoser@xerox.com into foo@hoser.xerox.com, but I don't
+      ;; actually know that that is what's going on.
       (cond ((not suffix-flag)
 	     (goto-char (point-min))
-	     (if (re-search-forward "\\b[A-Za-z][A-Za-z]+\\. *\\'" nil t)
-		 (narrow-to-region (point-min) (match-beginning 0)))))
-      
+	     (let ((case-fold-search t))
+	       (if (looking-at "[-A-Za-z_]+[. ]\\(PARC\\|ADOC\\)\\'")
+		   (erase-buffer)))))
+
       ;; If last name first put it at end (but before suffix)
       (cond (last-name-comma-flag
 	     (goto-char (point-min))
 	     (search-forward ",")
-	     (setq end (1- (point)))
+	     (setq name-end (1- (point)))
 	     (goto-char (or suffix-flag (point-max)))
-	     (or (eq mail-space-char (preceding-char))
-		 (insert mail-space-char))
-	     (insert-buffer-substring (current-buffer) (point-min) end)
-	     (narrow-to-region (1+ end) (point-max))))
+	     (or (eq ?\  (preceding-char))
+		 (insert ?\ ))
+	     (insert-buffer-substring (current-buffer) (point-min) name-end)
+	     (goto-char name-end)
+	     (skip-chars-forward "\t ,")
+	     (narrow-to-region (point) (point-max))))
       
-      (goto-char (point-max))
-      (skip-chars-backward mail-non-end-name-chars)
-      (if (eq ?. (following-char))
-	  (forward-char 1))
-      (narrow-to-region (point)
-			(progn
-			  (goto-char (point-min))
-			  (skip-chars-forward mail-non-begin-name-chars)
-			  (point)))
+      ;; Delete leading and trailing junk characters.
+      ;; *** This is probably completly unneeded now.
+      ;;(goto-char (point-max))
+      ;;(skip-chars-backward mail-extr-non-end-name-chars)
+      ;;(if (eq ?. (following-char))
+      ;;    (forward-char 1))
+      ;;(narrow-to-region (point)
+      ;;                  (progn
+      ;;                    (goto-char (point-min))
+      ;;                    (skip-chars-forward mail-extr-non-begin-name-chars)
+      ;;                    (point)))
       
       ;; Compress whitespace
       (goto-char (point-min))
       (while (re-search-forward "[ \t\n]+" nil t)
-	(replace-match " "))
-
-      (buffer-substring (point-min) (point-max))
-
+	(replace-match (if (eobp) "" " ") t))
       )))
 
-;; The country names are just in there for show right now, and because
-;; Jamie thought it would be neat.  They aren't used yet.
+
 
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Table of top-level domain names.
+;;
+;; This is used during address canonicalization; be careful of format changes.
 ;; Keep in mind that the country abbreviations follow ISO-3166.  There is
 ;; a U.S. FIPS that specifies a different set of two-letter country
 ;; abbreviations.
 
-;; TODO: put this in its own obarray, instead of cluttering up the main
-;; symbol table with junk.
+(defconst mail-extr-all-top-level-domains
+  (let ((ob (make-vector 509 0)))
+    (mapcar
+     (function
+      (lambda (x)
+	(put (intern (downcase (car x)) ob)
+	     'domain-name
+	     (if (nth 2 x)
+		 (format (nth 2 x) (nth 1 x))
+	       (nth 1 x)))))
+     '(("ag" "Antigua")
+       ("ar" "Argentina"	"Argentine Republic")
+       ("arpa" t		"Advanced Projects Research Agency")
+       ("at" "Austria"		"The Republic of %s")
+       ("au" "Australia")
+       ("bb" "Barbados")
+       ("be" "Belgium"		"The Kingdom of %s")
+       ("bg" "Bulgaria")
+       ("bitnet" t		"Because It's Time NET")
+       ("bo" "Bolivia"		"Republic of %s")
+       ("br" "Brazil"		"The Federative Republic of %s")
+       ("bs" "Bahamas")
+       ("bz" "Belize")
+       ("ca" "Canada")
+       ("ch" "Switzerland"	"The Swiss Confederation")
+       ("cl" "Chile"		"The Republic of %s")
+       ("cn" "China"		"The People's Republic of %s")
+       ("co" "Columbia")
+       ("com" t			"Commercial")
+       ("cr" "Costa Rica"	"The Republic of %s")
+       ("cs" "Czechoslovakia")
+       ("de" "Germany")
+       ("dk" "Denmark")
+       ("dm" "Dominica")
+       ("do" "Dominican Republic"	"The %s")
+       ("ec" "Ecuador"		"The Republic of %s")
+       ("edu" t			"Educational")
+       ("eg" "Egypt"		"The Arab Republic of %s")
+       ("es" "Spain"		"The Kingdom of %s")
+       ("fi" "Finland"		"The Republic of %s")
+       ("fj" "Fiji")
+       ("fr" "France")
+       ("gov" t			"Government (U.S.A.)")
+       ("gr" "Greece"		"The Hellenic Republic (%s)")
+       ("hk" "Hong Kong")
+       ("hu" "Hungary"		"The Hungarian People's Republic")	;???
+       ("ie" "Ireland")
+       ("il" "Israel"		"The State of %s")
+       ("in" "India"		"The Republic of %s")
+       ("int" t			"(something British, don't know what)")
+       ("is" "Iceland"		"The Republic of %s")
+       ("it" "Italy"		"The Italian Republic")
+       ("jm" "Jamaica")
+       ("jp" "Japan")
+       ("kn" "St. Kitts and Nevis")
+       ("kr" "South Korea")
+       ("lc" "St. Lucia")
+       ("lk" "Sri Lanka"	"The Democratic Socialist Republic of %s")
+       ("mil" t			"Military (U.S.A.)")
+       ("mx" "Mexico"		"The United Mexican States")
+       ("my" "Malaysia"		"%s (changed to Myanmar?)")		;???
+       ("na" "Namibia")
+       ("nato" t		"North Atlantic Treaty Organization")
+       ("net" t			"Network")
+       ("ni" "Nicaragua"	"The Republic of %s")
+       ("nl" "Netherlands"	"The Kingdom of the %s")
+       ("no" "Norway"		"The Kingdom of %s")
+       ("nz" "New Zealand")
+       ("org" t			"Organization")
+       ("pe" "Peru")
+       ("pg" "Papua New Guinea")
+       ("ph" "Philippines"	"The Republic of the %s")
+       ("pl" "Poland")
+       ("pr" "Puerto Rico")
+       ("pt" "Portugal"		"The Portugese Republic")
+       ("py" "Paraguay")
+       ("se" "Sweden"		"The Kingdom of %s")
+       ("sg" "Singapore"	"The Republic of %s")
+       ("sr" "Suriname")
+       ("su" "Soviet Union")
+       ("th" "Thailand"		"The Kingdom of %s")
+       ("tn" "Tunisia")
+       ("tr" "Turkey"		"The Republic of %s")
+       ("tt" "Trinidad and Tobago")
+       ("tw" "Taiwan")
+       ("uk" "United Kingdom"	"The %s of Great Britain")
+       ("unter-dom" t		"(something German)")
+       ("us" "U.S.A."		"The United States of America")
+       ("uucp" t		"Unix to Unix CoPy")
+       ("uy" "Uruguay"		"The Eastern Republic of %s")
+       ("vc" "St. Vincent and the Grenadines")
+       ("ve" "Venezuela"	"The Republic of %s")
+       ("yu" "Yugoslavia"	"The Socialist Federal Republic of %s")
+       ;; Also said to be Zambia ... (why not Zaire???)
+       ("za" "South Africa"	"The Republic of %s (or Zambia? Zaire?)")
+       ("zw" "Zimbabwe"		"Republic of %s")
+;; fipnet
+       ))
+    ob))
 
-(mapcar
- (function
-  (lambda (x)
-    (if (symbolp x)
-	(put x 'domain-name t)
-      (put (car x) 'domain-name (nth 1 x)))))
- '((ag "Antigua")
-   (ar "Argentina")			; Argentine Republic
-   arpa					; Advanced Projects Research Agency
-   (at "Austria")			; The Republic of _
-   (au "Australia")
-   (bb "Barbados")
-   (be "Belgium")			; The Kingdom of _
-   (bg "Bulgaria")
-   bitnet				; Because It's Time NET
-   (bo "Bolivia")			; Republic of _
-   (br "Brazil")			; The Federative Republic of _
-   (bs "Bahamas")
-   (bz "Belize")
-   (ca "Canada")
-   (ch "Switzerland")			; The Swiss Confederation
-   (cl "Chile")				; The Republic of _
-   (cn "China")				; The People's Republic of _
-   (co "Columbia")
-   com					; Commercial
-   (cr "Costa Rica")			; The Republic of _
-   (cs "Czechoslovakia")
-   (de "Germany")
-   (dk "Denmark")
-   (dm "Dominica")
-   (do "Dominican Republic")		; The _
-   (ec "Ecuador")			; The Republic of _
-   edu					; Educational
-   (eg "Egypt")				; The Arab Republic of _
-   (es "Spain")				; The Kingdom of _
-   (fi "Finland")			; The Republic of _
-   (fj "Fiji")
-   (fr "France")
-   gov					; Government (U.S.A.)
-   (gr "Greece")			; The Hellenic Republic
-   (hk "Hong Kong")
-   (hu "Hungary")			; The Hungarian People's Republic (???)
-   (ie "Ireland")
-   (il "Israel")			; The State of _
-   (in "India")				; The Republic of _
-   int					; something British, don't know what
-   (is "Iceland")			; The Republic of _
-   (it "Italy")				; The Italian Republic
-   (jm "Jamaica")
-   (jp "Japan")
-   (kn "St. Kitts and Nevis")
-   (kr "South Korea")
-   (lc "St. Lucia")
-   (lk "Sri Lanka")		       ; The Democratic Socialist Republic of _
-   mil					; Military (U.S.A.)
-   (mx "Mexico")			; The United Mexican States
-   (my "Malaysia")			; changed to Myanmar????
-   (na "Namibia")
-   nato					; North Atlantic Treaty Organization
-   net					; Network
-   (ni "Nicaragua")			; The Republic of _
-   (nl "Netherlands")			; The Kingdom of the _
-   (no "Norway")			; The Kingdom of _
-   (nz "New Zealand")
-   org					; Organization
-   (pe "Peru")
-   (pg "Papua New Guinea")
-   (ph "Philippines")			; The Republic of the _
-   (pl "Poland")
-   (pr "Puerto Rico")
-   (pt "Portugal")			; The Portugese Republic
-   (py "Paraguay")
-   (se "Sweden")			; The Kingdom of _
-   (sg "Singapore")			; The Republic of _
-   (sr "Suriname")
-   (su "Soviet Union")
-   (th "Thailand")			; The Kingdom of _
-   (tn "Tunisia")
-   (tr "Turkey")			; The Republic of _
-   (tt "Trinidad and Tobago")
-   (tw "Taiwan")
-   (uk "United Kingdom")		; The _ of Great Britain
-   unter-dom				; something German
-   (us "U.S.A.")			; The United States of America
-   uucp					; Unix to Unix CoPy
-   (uy "Uruguay")			; The Eastern Republic of _
-   (vc "St. Vincent and the Grenadines")
-   (ve "Venezuela")			; The Republic of _
-   (yu "Yugoslavia")			; The Socialist Federal Republic of _
-   ;; Also said to be Zambia ...
-   (za "South Africa")			; The Republic of _ (why not Zaire???)
-   (zw "Zimbabwe")			; Republic of _
-   ))
-;; fipnet
+;;;###autoload
+(defun what-domain (domain)
+  "Convert mail domain to country tit corresponds to."
+  (interactive
+   (let ((completion-ignore-case t))
+     (list (completing-read "Domain: "
+			    mail-extr-all-top-level-domains nil t))))
+  (or (setq domain (intern-soft (downcase domain)
+				mail-extr-all-top-level-domains))
+      (error "no such domain"))
+  (message "%s: %s" (upcase (symbol-name domain)) (get domain 'domain-name)))
 
 
-;; Code for testing.
-
-(defun time-extract ()
-  (let (times list)
-    (setq times (cons (current-time-string) times)
-	  list problem-address-alist)
-    (while list
-      (mail-extract-address-components (car (car list)))
-      (setq list (cdr list)))
-    (setq times (cons (current-time-string) times))
-    (nreverse times)))
-
-(defun test-extract (&optional starting-point)
-  (interactive)
-  (set-buffer (get-buffer-create "*Testing*"))
-  (erase-buffer)
-  (sit-for 0)
-  (mapcar 'test-extract-internal
-	  (if starting-point
-	      (memq starting-point problem-address-alist)
-	     problem-address-alist)))
-
-(defvar failed-item)
-(defun test-extract-internal (item)
-  (setq failed-item item)
-  (let* ((address (car item))
-	 (correct-name (nth 1 item))
-	 (correct-canon (nth 2 item))
-	 (result (mail-extract-address-components address))
-	 (name (car result))
-	 (canon (nth 1 result))
-	 (name-correct (or (null correct-name)
-			   (string-equal (downcase correct-name)
-					 (downcase name))))
-	 (canon-correct (or (null correct-canon)
-			    (string-equal correct-canon canon))))
-    (cond ((not (and name-correct canon-correct))
-	   (pop-to-buffer "*Testing*")
-	   (select-window (get-buffer-window (current-buffer)))
-	   (goto-char (point-max))
-	   (insert "Address: " address "\n")
-	   (if (not name-correct)
-	       (insert " Correct Name:  [" correct-name
-		       "]\; Result: [" name "]\n"))
-	   (if (not canon-correct)
-	       (insert " Correct Canon: [" correct-canon
-		       "]\; Result: [" canon "]\n"))
-	   (insert "\n")
-	   (sit-for 0))))
-  (setq failed-item nil))
-
-(defun test-continue-extract ()
-  (interactive)
-  (test-extract failed-item))
+;(let ((all nil))
+;  (mapatoms #'(lambda (x)
+;		(if (and (boundp x) 
+;			 (string-match "^mail-extr-" (symbol-name x)))
+;		    (setq all (cons x all)))))
+;  (setq all (sort all #'string-lessp))
+;  (cons 'setq
+;	(apply 'nconc (mapcar #'(lambda (x)
+;				  (list x (symbol-value x)))
+;			      all))))
 
 
-;; Assorted junk.
-
-;;	warsaw@nlm.nih.gov (A Bad Dude -- Barry Warsaw)
-
-;;'(from
-;;  reply-to
-;;  return-path
-;;  x-uucp-from
-;;  sender
-;;  resent-from
-;;  resent-sender
-;;  resent-reply-to)
+(provide 'mail-extr)
 
 ;;; mail-extr.el ends here