Description: <short summary of the patch>
 TODO: Put a short summary on the line above and replace this paragraph
 with a longer explanation of this change. Complete the meta-information
 with other relevant fields (see below for details). To make it easier, the
 information below has been extracted from the changelog. Adjust it or drop
 it.
 .
 bbdb3 (3.2-5) unstable; urgency=medium
 .
   * track upstream mods (wanderlust fixes)
   * enhances: wanderlust
   * add pointers to relevant documentation in debian/NOTES.org, installed as doc
   * bump standards version
Author: Barak A. Pearlmutter <bap@debian.org>

---
The information above should follow the Patch Tagging Guidelines, please
checkout http://dep.debian.net/deps/dep3/ to learn about the format. Here
are templates for supplementary fields that you might want to add:

Origin: <vendor|upstream|other>, <url of original patch>
Bug: <url in upstream bugtracker>
Bug-Debian: https://bugs.debian.org/<bugnumber>
Bug-Ubuntu: https://launchpad.net/bugs/<bugnumber>
Forwarded: <no|not-needed|url proving that it has been forwarded>
Reviewed-By: <name and email of someone who approved the patch>
Last-Update: 2018-10-08

--- bbdb3-3.2.orig/ChangeLog
+++ bbdb3-3.2/ChangeLog
@@ -1,3 +1,53 @@
+2018-09-06  Roland Winkler  <winkler@gnu.org>
+	Fix support for Wanderlust.
+	* lisp/bbdb-mua.el (bbdb-mua-mode-alist): Include mime-view-mode.
+	* lisp/bbdb-wl.el (bbdb/wl-header): Use with-current-buffer.
+
+2018-05-02  Roland Winkler  <winkler@gnu.org>
+	* lisp/bbdb-mua.el (bbdb-message-header): Use rmail-get-header.
+	* lisp/bbdb-rmail.el (bbdb/rmail-header): Remove.
+
+2018-03-30  Roland Winkler  <winkler@gnu.org>
+	Unify the handling of new records, new fields and modified fields.
+
+	* bbdb.el (bbdb-check-name): Handle both names and akas.  New
+	optional arg warn.
+	(bbdb-check-mail): New function.
+	(bbdb-register-record): Use bbdb-check-name and bbdb-check-mail.
+	New optional arg warn.
+	(bbdb-change-record): Fix docstring.
+
+	* bbdb.el (bbdb-record-set-field):
+	* bbdb-com.el (bbdb-read-record, bbdb-create-internal):
+	Use bbdb-check-name and bbdb-check-mail.
+
+	* bbdb-com.el (bbdb-record-edit-address): Allow arg address be nil
+	for new addresses.
+	(bbdb-record-edit-phone): Operate only on the phone number that we
+	change.
+	(bbdb-read-record, bbdb-read-field): Use bbdb-record-edit-phone.
+	Always use bbdb-record-set-field.
+
+2018-03-26  Roland Winkler  <winkler@gnu.org>
+	* bbdb.el (bbdb-register-record): Don't be fooled by existing
+	records.
+
+2018-03-17  Vincent Belaïche  <vincentb1@users.sourceforge.net>
+	* lisp/Makefile.am (abs_target): Make absolute path for
+	bbdb-loaddefs.el MSWindows aware.
+
+2018-03-17  Roland Winkler  <winkler@gnu.org>
+	* bbdb.el (bbdb-register-record): New function renamed from
+	bbdb-hash-record.
+	(bbdb-parse-records, bbdb-change-record): Use it.
+
+2018-03-17  Roland Winkler  <winkler@gnu.org>
+	* bbdb-com.el (bbdb-create-internal): Bug fix.  Support old
+	calling sequence with warning.
+
+2018-03-02  Stefan Monnier <monnier@iro.umontreal.ca>
+	* bbdb.el: Add missing "Maintainer:" header
+
 2018-01-06  Roland Winkler  <winkler@gnu.org>
 	* configure.ac: Increase version number to 3.2.
 
--- bbdb3-3.2.orig/lisp/Makefile.am
+++ bbdb3-3.2/lisp/Makefile.am
@@ -87,9 +87,14 @@ bbdb-loaddefs.el: $(dist_lisp_LISP)
 	@echo "" >> $@;
 #	Generated autoload-file must have an absolute path,
 #	$srcdir can be relative.
+#
+#       The equivalent command line with bash $(...) construct is:
+#       abs_target=$$($(CYGPATH_W) $(abs_builddir)/$@ | sed 's/\\/\\\\/g');
+#       See http://lists.nongnu.org/archive/html/bbdb-user/2018-02/msg00000.html
+	abs_target=`$(CYGPATH_W) $(abs_builddir)/$@ | sed 's/\\\\/\\\\\\\\/g'`; \
 	$(EMACS) --batch $(AM_ELCFLAGS) $(ELCFLAGS) \
 		--load autoload \
-		--eval '(setq generated-autoload-file "'$(abs_builddir)/$@'")' \
+		--eval '(setq generated-autoload-file "'"$$abs_target"'")' \
 		--eval '(setq make-backup-files nil)' \
 		--funcall batch-update-autoloads $(srcdir)
 
--- bbdb3-3.2.orig/lisp/bbdb-com.el
+++ bbdb3-3.2/lisp/bbdb-com.el
@@ -1,6 +1,6 @@
 ;;; bbdb-com.el --- user-level commands of BBDB -*- lexical-binding: t -*-
 
-;; Copyright (C) 2010-2017  Free Software Foundation, Inc.
+;; Copyright (C) 2010-2018  Free Software Foundation, Inc.
 
 ;; This file is part of the Insidious Big Brother Database (aka BBDB),
 
@@ -803,7 +803,7 @@ but does ensure that there will not be n
     (let (name)
       (bbdb-error-retry
        (setq name (bbdb-read-name first-and-last))
-       (bbdb-check-name (car name) (cdr name)))
+       (bbdb-check-name name))
       (bbdb-record-set-firstname record (car name))
       (bbdb-record-set-lastname record (cdr name)))
 
@@ -811,36 +811,31 @@ but does ensure that there will not be n
     (bbdb-record-set-organization record (bbdb-read-organization))
 
     ;; mail
-    (bbdb-record-set-mail
-     record (bbdb-split 'mail (bbdb-read-string "E-Mail Addresses: ")))
+    (let (mail)
+      (bbdb-error-retry
+       (setq mail (bbdb-split 'mail (bbdb-read-string "E-Mail Addresses: ")))
+       (bbdb-check-mail mail))
+      (bbdb-record-set-mail record mail))
+
     ;; address
-    (let (addresses label address)
+    (let (addresses label)
       (while (not (string= ""
                            (setq label
                                  (bbdb-read-string
                                   "Snail Mail Address Label [RET when done]: "
-                                  nil
-                                  bbdb-address-label-list))))
-        (setq address (make-vector bbdb-address-length nil))
-        (bbdb-record-edit-address address label t)
-        (push address addresses))
+                                  nil bbdb-address-label-list))))
+        ;; Here we could also already update the completion lists.  Bother?
+        (push (bbdb-record-edit-address nil label) addresses))
       (bbdb-record-set-address record (nreverse addresses)))
 
     ;; phones
-    (let (phones phone-list label)
+    (let (phones label)
       (while (not (string= ""
                            (setq label
                                  (bbdb-read-string
-                                  "Phone Label [RET when done]: " nil
-                                  bbdb-phone-label-list))))
-        (setq phone-list
-              (bbdb-error-retry
-               (bbdb-parse-phone
-                (read-string "Phone: "
-                             (and (integerp bbdb-default-area-code)
-                                  (format "(%03d) "
-                                          bbdb-default-area-code))))))
-        (push (apply 'vector label phone-list) phones))
+                                  "Phone Label [RET when done]: "
+                                  nil bbdb-phone-label-list))))
+        (push (bbdb-record-edit-phone nil label) phones))
       (bbdb-record-set-phone record (nreverse phones)))
 
     ;; `bbdb-default-xfield'
@@ -912,13 +907,15 @@ The following keywords are supported in
                    and `bbdb-allow-duplicates' is nil.
 :affix VAL         List of strings.
 :aka VAL           List of strings.
+                   An error is thrown if an aka in VAL is already in use
+                   and `bbdb-allow-duplicates' is nil.
 :organization VAL  List of strings.
 :mail VAL          String with comma-separated mail address
                    or a list of strings.
-                   An error is thrown if a mail address in MAIL is already
+                   An error is thrown if a mail address in VAL is already
                    in use and `bbdb-allow-duplicates' is nil.
 :phone VAL         List of phone-number objects.  A phone-number is a vector
-                   [\"label\" areacode prefix suffix extension-or-nil]
+                   [\"label\" area-code prefix suffix extension-or-nil]
                    or [\"label\" \"phone-number\"]
 :address VAL       List of addresses.  An address is a vector of the form
                    \[\"label\" (\"line1\" \"line2\" ... ) \"City\"
@@ -929,6 +926,22 @@ The following keywords are supported in
 :check             If present, throw an error if a field value is not
                    syntactically correct."
   (bbdb-editable)
+
+  (when (not (keywordp (car spec)))
+    ;; Old format for backward compatibility (BBDB version < 3.2)
+    (unless (get 'bbdb-create-internal 'bbdb-outdated)
+      (put 'bbdb-create-internal 'bbdb-outdated t)
+      (message "Outdated usage of `bbdb-create-internal'")
+      (sit-for 2))
+    (let (newspec val)
+      (dolist (key '(:name :affix :aka :organization :mail :phone :address
+                           :xfields))
+        (if (setq val (pop spec))
+            (push (list key val) newspec)))
+      (if (setq val (pop spec))
+          (push (list :check) newspec))
+      (setq spec (apply 'append newspec))))
+
   (let ((record (bbdb-empty-record))
         (record-type (cdr bbdb-record-type))
         (check (prog1 (memq :check spec)
@@ -946,11 +959,9 @@ The following keywords are supported in
                  (check (bbdb-check-type name '(or (const nil)
                                                    (cons string string))
                                          t)))
-           (let ((firstname (car name))
-                 (lastname (cdr name)))
-             (bbdb-check-name firstname lastname) ; check for duplicates
-             (bbdb-record-set-firstname record firstname)
-             (bbdb-record-set-lastname record lastname))))
+           (bbdb-check-name name) ; check for duplicates
+           (bbdb-record-set-firstname record (car name))
+           (bbdb-record-set-lastname record (cdr name))))
 
         (`:affix
          (let ((affix (bbdb-split-maybe 'affix (pop spec))))
@@ -966,21 +977,19 @@ The following keywords are supported in
         (`:aka
          (let ((aka (bbdb-split-maybe 'aka (pop spec))))
            (if check (bbdb-check-type aka (bbdb-record-aka record-type) t))
+           (bbdb-check-name aka)
            (bbdb-record-set-aka record aka)))
 
         (`:mail
          (let ((mail (bbdb-split-maybe 'mail (pop spec))))
            (if check (bbdb-check-type mail (bbdb-record-mail record-type) t))
-           (unless bbdb-allow-duplicates
-             (dolist (elt mail)
-               (if (bbdb-gethash elt '(mail))
-                   (error "%s is already in the database" elt))))
+           (bbdb-check-mail mail)
            (bbdb-record-set-mail record mail)))
 
         (`:phone
          (let ((phone (pop spec)))
            (if check (bbdb-check-type phone (bbdb-record-phone record-type) t))
-           (bbdb-record-set-phone phone record)))
+           (bbdb-record-set-phone record phone)))
 
         (`:address
          (let ((address (pop spec)))
@@ -1112,19 +1121,10 @@ A non-nil prefix arg is passed on to `bb
            (let ((bbdb-phone-style
                   (if flag (if (eq bbdb-phone-style 'nanp) nil 'nanp)
                     bbdb-phone-style)))
-             (apply 'vector
-                    (bbdb-read-string "Label: " nil bbdb-phone-label-list)
-                    (bbdb-error-retry
-                     (bbdb-parse-phone
-                      (read-string "Phone: "
-                                   (and (integerp bbdb-default-area-code)
-                                        (format "(%03d) "
-                                                bbdb-default-area-code))))))))
+             (bbdb-record-edit-phone)))
           ;; Address
           ((eq field 'address)
-           (let ((address (make-vector bbdb-address-length nil)))
-             (bbdb-record-edit-address address nil t)
-             address))
+           (bbdb-record-edit-address))
           ;; xfield
           ((or (memq field bbdb-xfield-label-list)
                ;; New xfield
@@ -1181,10 +1181,17 @@ a phone number or address with VALUE bei
 
           ((eq field 'phone)
            (unless value (error "No phone specified"))
-           (bbdb-record-edit-phone (bbdb-record-phone record) value))
+           (bbdb-record-set-field
+            record field
+            ;; Splice new phone value into list of phones.
+            (let ((phones (bbdb-record-phone record)))
+              (setcar (memq value phones)
+                      (bbdb-record-edit-phone value))
+              phones)))
           ((eq field 'address)
            (unless value (error "No address specified"))
-           (bbdb-record-edit-address value nil flag))
+           (bbdb-record-edit-address value nil flag)
+           (bbdb-record-set-field record field (bbdb-record-address record)))
           ((eq field 'organization)
            (bbdb-record-set-field
             record field
@@ -1202,8 +1209,9 @@ a phone number or address with VALUE bei
            (bbdb-record-set-field
             record 'uuid (bbdb-read-string "uuid (edit at your own risk): " (bbdb-record-uuid record))))
           ((eq field 'creation-date)
-           (bbdb-record-set-creation-date
-            record (bbdb-read-string "creation-date: " (bbdb-record-creation-date record))))
+           (bbdb-record-set-field
+            record 'creation-date
+            (bbdb-read-string "creation-date: " (bbdb-record-creation-date record))))
           ;; The timestamp is set automatically whenever we save a modified record.
           ;; So any editing gets overwritten.
           ((eq field 'timestamp)) ; do nothing
@@ -1322,12 +1330,15 @@ This calls bbdb-read-xfield-FIELD if it
                                   nil nil init))
     (bbdb-split 'organization (bbdb-read-string "Organizations: " init))))
 
-(defun bbdb-record-edit-address (address &optional label ignore-country)
-  "Edit ADDRESS.
+;; The name `bbdb-read-address' might fit better.
+(defun bbdb-record-edit-address (&optional address label ignore-country)
+  "Edit and return ADDRESS.
 If LABEL is nil, edit the label sub-field of the address as well.
 If the country field of ADDRESS is nonempty and IGNORE-COUNTRY is nil,
 use the rule from `bbdb-address-format-list' matching this country.
 Otherwise, use the default rule according to `bbdb-address-format-list'."
+  (unless address
+    (setq address (make-vector bbdb-address-length nil)))
   (unless label
     (setq label (bbdb-read-string "Label: "
                                   (bbdb-address-label address)
@@ -1387,7 +1398,8 @@ Otherwise, use the default rule accordin
         ;; The following is a temporary fix.  Ideally, we would simply discard
         ;; the entire address, but that requires bigger hacking.
         (bbdb-address-set-country address "Emacs")
-      (bbdb-address-set-country address (elt new-addr 4)))))
+      (bbdb-address-set-country address (elt new-addr 4)))
+    address))
 
 (defun bbdb-edit-address-street (streets)
   "Edit list STREETS."
@@ -1400,10 +1412,13 @@ Otherwise, use the default rule accordin
       (setq n (1+ n)))
     (reverse list)))
 
-;; This function can provide some guidance for writing
-;; your own address editing function
+;; This function can provide some guidance for writing your own
+;; address editing function for `bbdb-address-format-list'.
+;; Such a function should return a list or vector with five elements,
+;; a list of streets, city, state, postcode, country.
+;; These elements should be strings or nil.
 (defun bbdb-edit-address-default (address)
-  "Function to use for address editing.
+  "Function for editing ADDRESS to be used by `bbdb-address-format-list'.
 The sub-fields and the prompts used are:
 Street, line n:  (nth n street)
 City:            city
@@ -1422,21 +1437,26 @@ Country:         country"
                                           bbdb-default-country)
                           bbdb-country-list)))
 
-(defun bbdb-record-edit-phone (phones phone)
-  "For list PHONES edit PHONE number."
+;; The name `bbdb-read-phone' might fit better.
+(defun bbdb-record-edit-phone (&optional phone label)
+  "Edit and return PHONE.
+If LABEL is nil, edit the label sub-field of PHONE as well."
   ;; Phone numbers are special.  They are vectors with either
   ;; two or four elements.  We do not know whether after editing PHONE
   ;; we still have a number requiring the same format as PHONE.
-  ;; So we take all numbers PHONES of the record so that we can
-  ;; replace the element PHONE in PHONES.
-  (setcar (memq phone phones)
-          (apply 'vector
-                 (bbdb-read-string "Label: "
-                                   (bbdb-phone-label phone)
-                                   bbdb-phone-label-list)
-                 (bbdb-error-retry
-                  (bbdb-parse-phone
-                   (read-string "Phone: " (bbdb-phone-string phone)))))))
+  ;; So we throw away the argument PHONE and return a new vector.
+  (apply 'vector
+         (or label
+             (bbdb-read-string "Label: "
+                               (and phone (bbdb-phone-label phone))
+                               bbdb-phone-label-list))
+         (bbdb-error-retry
+          (bbdb-parse-phone
+           (read-string "Phone: "
+                        (or (and phone (bbdb-phone-string phone))
+                            (and (integerp bbdb-default-area-code)
+                                 (format "(%03d) "
+                                         bbdb-default-area-code))))))))
 
 ;; (bbdb-list-transpose '(a b c d) 1 3)
 (defun bbdb-list-transpose (list i j)
--- bbdb3-3.2.orig/lisp/bbdb-mua.el
+++ bbdb3-3.2/lisp/bbdb-mua.el
@@ -1,6 +1,6 @@
 ;;; bbdb-mua.el --- various MUA functionality for BBDB -*- lexical-binding: t -*-
 
-;; Copyright (C) 2010-2017  Free Software Foundation, Inc.
+;; Copyright (C) 2010-2018  Free Software Foundation, Inc.
 
 ;; This file is part of the Insidious Big Brother Database (aka BBDB),
 
@@ -48,7 +48,7 @@
   (autoload 'vm-check-for-killed-summary "vm-misc")
   (autoload 'vm-error-if-folder-empty "vm-misc")
 
-  (autoload 'bbdb/rmail-header "bbdb-rmail")
+  (autoload 'rmail-get-header "rmail")
   (defvar rmail-buffer)
 
   (autoload 'bbdb/mh-header "bbdb-mhe")
@@ -68,7 +68,7 @@
     (rmail rmail-mode rmail-summary-mode)
     (mh mhe-mode mhe-summary-mode mh-folder-mode)
     (mu4e mu4e-view-mode)  ; Tackle `mu4e-headers-mode' later
-    (wl wl-summary-mode wl-draft-mode)
+    (wl wl-summary-mode wl-draft-mode mime-view-mode)
     (message message-mode mu4e-compose-mode notmuch-message-mode)
     (mail mail-mode))
   "Alist of MUA modes supported by BBDB.
@@ -115,7 +115,9 @@ MIME encoded headers are decoded.  Retur
                      ;; See http://permalink.gmane.org/gmane.emacs.gnus.general/78741
                      (eq mua 'gnus) (gnus-fetch-original-field header))
                     ((eq mua 'vm) (bbdb/vm-header header))
-                    ((eq mua 'rmail) (bbdb/rmail-header header))
+                    ((eq mua 'rmail)
+                     (with-current-buffer rmail-buffer
+                       (rmail-get-header header)))
                     ((eq mua 'mh) (bbdb/mh-header header))
                     ((eq mua 'mu4e) (message-field-value header))
                     ((eq mua 'wl) (bbdb/wl-header header))
--- bbdb3-3.2.orig/lisp/bbdb-rmail.el
+++ bbdb3-3.2/lisp/bbdb-rmail.el
@@ -1,6 +1,6 @@
 ;;; bbdb-rmail.el --- BBDB interface to Rmail -*- lexical-binding: t -*-
 
-;; Copyright (C) 2010-2017  Free Software Foundation, Inc.
+;; Copyright (C) 2010-2018  Free Software Foundation, Inc.
 
 ;; This file is part of the Insidious Big Brother Database (aka BBDB),
 
@@ -26,18 +26,7 @@
 (require 'bbdb)
 (require 'bbdb-com)
 (require 'bbdb-mua)
-(require 'rmail)
 (require 'rmailsum)
-(require 'mailheader)
-
-;;;###autoload
-(defun bbdb/rmail-header (header)
-  "Pull HEADER out of Rmail header."
-  (with-current-buffer rmail-buffer
-    (save-restriction
-      (with-no-warnings (rmail-narrow-to-non-pruned-header))
-      (mail-header (intern-soft (downcase header))
-                   (mail-header-extract)))))
 
 ;;;###autoload
 (defun bbdb-insinuate-rmail ()
--- bbdb3-3.2.orig/lisp/bbdb-wl.el
+++ bbdb3-3.2/lisp/bbdb-wl.el
@@ -1,6 +1,6 @@
 ;;; bbdb-wl.el --- BBDB interface to Wanderlust -*- lexical-binding: t -*-
 
-;; Copyright (C) 2015-2017  Free Software Foundation, Inc.
+;; Copyright (C) 2015-2018  Free Software Foundation, Inc.
 
 ;; This file is part of the Insidious Big Brother Database (aka BBDB),
 
@@ -28,6 +28,7 @@
 
 (defvar wl-summary-mode-map)
 (defvar wl-draft-mode-map)
+(defvar wl-summary-buffer-name)
 (defvar wl-summary-buffer-elmo-folder)
 (eval-and-compile
   (autoload 'wl-summary-message-number "wl-summary")
@@ -36,10 +37,12 @@
 
 ;;;###autoload
 (defun bbdb/wl-header (header)
-  (elmo-message-entity-field
-   (elmo-message-entity wl-summary-buffer-elmo-folder
-                        (wl-summary-message-number))
-   (intern (downcase header)) 'string))
+  (with-current-buffer wl-summary-buffer-name
+    (elmo-message-entity-field
+     (elmo-message-entity wl-summary-buffer-elmo-folder
+                          (wl-summary-message-number))
+     (intern (downcase header))
+     'string)))
 
 ;;;###autoload
 (defun bbdb-insinuate-wl ()
--- bbdb3-3.2.orig/lisp/bbdb.el
+++ bbdb3-3.2/lisp/bbdb.el
@@ -1,7 +1,8 @@
 ;;; bbdb.el --- core of BBDB -*- lexical-binding: t -*-
 
-;; Copyright (C) 2010-2017  Free Software Foundation, Inc.
+;; Copyright (C) 2010-2018  Free Software Foundation, Inc.
 
+;; Maintainer: Roland Winkler <winkler@gnu.org>
 ;; Version: 3.2
 ;; Package-Requires: ((emacs "24"))
 
@@ -2404,8 +2405,8 @@ It is the caller's responsibility to mak
 ;; `bbdb-hashtable' associates with each KEY a list of matching records.
 ;; KEY includes fl-name, lf-name, organizations, AKAs and email addresses.
 ;; When loading the database the hash table is initialized by calling
-;; `bbdb-hash-record' for each record.  This function is also called
-;; when new records are added to the database.
+;; `bbdb-register-record' for each record.  This function is also called
+;; when adding new records to the database.
 ;; `bbdb-delete-record-internal' with arg REMHASH non-nil removes a record
 ;; from the hash table (besides deleting the record from the database).
 ;; When an existing record is modified, the code that modifies the record
@@ -2480,19 +2481,6 @@ KEY must be a string or nil.  Empty stri
               (puthash key records bbdb-hashtable)
             (remhash key bbdb-hashtable))))))
 
-(defun bbdb-hash-record (record)
-  "Insert RECORD in `bbdb-hashtable'.
-This performs all initializations required for a new record.
-Do not call this for existing records that require updating."
-  (bbdb-puthash (bbdb-record-name record) record)
-  (bbdb-puthash (bbdb-record-name-lf record) record)
-  (dolist (organization (bbdb-record-organization record))
-    (bbdb-puthash organization record))
-  (dolist (aka (bbdb-record-aka record))
-    (bbdb-puthash aka record))
-  (bbdb-puthash-mail record)
-  (puthash (bbdb-record-uuid record) record bbdb-uuid-table))
-
 (defun bbdb-puthash-mail (record)
   "For RECORD put mail into `bbdb-hashtable'."
   (let (mail-aka mail-canon address)
@@ -2516,18 +2504,54 @@ Both OLD and NEW are lists of values."
   (dolist (elt new)
     (bbdb-puthash elt record)))
 
-(defun bbdb-check-name (first last &optional record)
-  "Check whether the name FIRST LAST is a valid name.
-This throws an error if the name is already used by another record
-and `bbdb-allow-duplicates' is nil.  If RECORD is non-nil, FIRST and LAST
-may correspond to RECORD without raising an error."
-  ;; Are there more useful checks for names beyond checking for duplicates?
+(defun bbdb-check-name (name &optional record warn)
+  "Check whether NAME is a valid name.
+This throws an error if NAME is already used by another record
+and `bbdb-allow-duplicates' is nil.
+NAME may be a string, a cons (FIRST . LAST) or a list of name strings.
+If RECORD is non-nil, NAME may correspond to RECORD without raising an error.
+If WARN is non-nil, issue a warning instead of raising an error."
+  ;; Are there other useful checks for names beyond checking for duplicates?
   (unless bbdb-allow-duplicates
-    (let* ((name (bbdb-concat 'name-first-last first last))
-           (records (bbdb-gethash name '(fl-name lf-name aka))))
-      (if (or (and (not record) records)
-              (remq record records))
-          (error "%s is already in BBDB" name)))))
+    (cl-flet ((fun (name)
+                   (let* ((tmp (bbdb-gethash name '(fl-name lf-name aka)))
+                          (records (if record (remq record tmp) tmp)))
+                     (if records
+                         ;; Be verbose as the duplicates may be AKAs.
+                         (let ((msg (format "Name `%s' is already in BBDB: %s"
+                                            name (mapconcat 'bbdb-record-name
+                                                            records ", "))))
+                           (if (not warn)
+                               (error msg)
+                             (message msg)
+                             (sit-for 1)))))))
+      (cond ((stringp name)
+             (fun name))
+            ((and (consp name) (stringp (cdr name)))
+             (fun (bbdb-concat 'name-first-last (car name) (cdr name))))
+            (t (mapc #'fun name))))))
+
+(defun bbdb-check-mail (mail &optional record warn)
+  "Check whether MAIL is a valid mail address.
+This throws an error if MAIL is already used by another record
+and `bbdb-allow-duplicates' is nil.
+MAIL may be a mail string or a list of mail strings.
+If RECORD is non-nil, MAIL may appear in RECORD without raising an error.
+If WARN is non-nil, issue a warning instead of raising an error."
+  ;; Are there other useful checks for mail addresses beyond checking
+  ;; for duplicates?
+  (unless bbdb-allow-duplicates
+    (dolist (m (if (listp mail) mail (list mail)))
+      (let* ((tmp (bbdb-gethash (nth 1 (bbdb-decompose-bbdb-address m))
+                                '(mail)))
+             (records (if record (remq record tmp) tmp)))
+        (if records
+            (let ((msg (format "Mail `%s' is already in BBDB: %s" m
+                               (mapconcat 'bbdb-record-name records ", "))))
+              (if (not warn)
+                  (error msg)
+                (message msg)
+                (sit-for 1))))))))
 
 (defun bbdb-record-name (record)
   "Record cache function: Return the full name FIRST_LAST of RECORD.
@@ -2828,14 +2852,14 @@ See also `bbdb-record-field'."
     (cond ((eq field 'firstname) ; First name
            (if merge (error "Does not merge names"))
            (if check (bbdb-check-type value (bbdb-record-firstname record-type) t))
-           (bbdb-check-name value (bbdb-record-lastname record) record)
+           (bbdb-check-name (cons value (bbdb-record-lastname record)) record)
            (bbdb-record-set-name record value t))
 
           ;; Last name
           ((eq field 'lastname)
            (if merge (error "Does not merge names"))
            (if check (bbdb-check-type value (bbdb-record-lastname record-type) t))
-           (bbdb-check-name (bbdb-record-firstname record) value record)
+           (bbdb-check-name (cons (bbdb-record-firstname record) value) record)
            (bbdb-record-set-name record t value))
 
           ;; Name
@@ -2844,9 +2868,8 @@ See also `bbdb-record-field'."
            (if (stringp value)
                (setq value (bbdb-divide-name value))
              (if check (bbdb-check-type value '(cons string string) t)))
-           (let ((fn (car value)) (ln (cdr value)))
-             (bbdb-check-name fn ln record)
-             (bbdb-record-set-name record fn ln)))
+           (bbdb-check-name value record)
+           (bbdb-record-set-name record (car value) (cdr value)))
 
           ;; Affix
           ((eq field 'affix)
@@ -2873,11 +2896,7 @@ See also `bbdb-record-field'."
                                                    value 'bbdb-string=)))
            (if check (bbdb-check-type value (bbdb-record-aka record-type) t))
            (setq value (bbdb-list-strings value))
-           (unless bbdb-allow-duplicates
-             (dolist (aka value)
-               (let ((old (remq record (bbdb-gethash aka '(fl-name lf-name aka)))))
-                 (if old (error "Alternate name address \"%s\" is used by \"%s\""
-                                aka (mapconcat 'bbdb-record-name old ", "))))))
+           (bbdb-check-name value record)
            (bbdb-hash-update record (bbdb-record-aka record) value)
            (bbdb-record-set-aka record value))
 
@@ -2887,11 +2906,7 @@ See also `bbdb-record-field'."
                                                    value 'bbdb-string=)))
            (if check (bbdb-check-type value (bbdb-record-mail record-type) t))
            (setq value (bbdb-list-strings value))
-           (unless bbdb-allow-duplicates
-             (dolist (mail value)
-               (let ((old (remq record (bbdb-gethash mail '(mail)))))
-                 (if old (error "Mail address \"%s\" is used by \"%s\""
-                                mail (mapconcat 'bbdb-record-name old ", "))))))
+           (bbdb-check-mail value record)
            (dolist (aka (bbdb-record-mail-aka record))
              (bbdb-remhash aka record))
            (dolist (mail (bbdb-record-mail-canon record))
@@ -3382,46 +3397,10 @@ If `bbdb-file' uses an outdated format,
                 ;; We are just loading BBDB, so we are not yet ready
                 ;; for sophisticated solutions.
                 (error "Duplicate UUID %s" (bbdb-record-uuid record)))
-
-            ;; Set the completion lists
-            (dolist (phone (bbdb-record-phone record))
-              (bbdb-pushnew (bbdb-phone-label phone) bbdb-phone-label-list))
-            (dolist (address (bbdb-record-address record))
-              (bbdb-pushnew (bbdb-address-label address) bbdb-address-label-list)
-              (mapc (lambda (street) (bbdb-pushnewt street bbdb-street-list))
-                    (bbdb-address-streets address))
-              (bbdb-pushnewt (bbdb-address-city address) bbdb-city-list)
-              (bbdb-pushnewt (bbdb-address-state address) bbdb-state-list)
-              (bbdb-pushnewt (bbdb-address-postcode address) bbdb-postcode-list)
-              (bbdb-pushnewt (bbdb-address-country address) bbdb-country-list))
-            (dolist (xfield (bbdb-record-xfields record))
-              (bbdb-pushnewq (car xfield) bbdb-xfield-label-list))
-            (dolist (organization (bbdb-record-organization record))
-              (bbdb-pushnew organization bbdb-organization-list))
-
-            (let ((name (bbdb-concat 'name-first-last
-                                     (bbdb-record-firstname record)
-                                     (bbdb-record-lastname record))))
-              (when (and (not bbdb-allow-duplicates)
-                         (bbdb-gethash name '(fl-name aka)))
-                ;; This does not check for duplicate mail fields.
-                ;; Yet under normal circumstances, this should really
-                ;; not be necessary each time BBDB is loaded as BBDB checks
-                ;; whether creating a new record or modifying an existing one
-                ;; results in duplicates.
-                ;; Alternatively, you can use `bbdb-search-duplicates'.
-                (message "Duplicate BBDB record encountered: %s" name)
-                (sit-for 1)))
-
-            ;; If `bbdb-allow-duplicates' is non-nil, we allow that two records
-            ;; (with different uuids) refer to the same person (same name etc.).
-            ;; Such duplicate records are always hashed.
-            ;; Otherwise, an unhashed record would not be available for things
-            ;; like completion (and we would not know which record to keeep
-            ;; and which one to hide).  We trust the user she knows what
-            ;; she wants if she keeps duplicate records in the database though
-            ;; `bbdb-allow-duplicates' is nil.
-            (bbdb-hash-record record))
+            ;; With `bbdb-allow-duplicates' nil, BBDB would become unusable
+            ;; if duplicates threw an error upon loading BBDB.  Thus we only
+            ;; issue a message.
+            (bbdb-register-record record t))
 
           ;; Note that `bbdb-xfield-label-list' serves two purposes:
           ;;  - check whether an xfield is new to BBDB
@@ -3450,6 +3429,52 @@ If `bbdb-file' uses an outdated format,
           (unless bbdb-silent (message "Parsing BBDB file `%s'...done" file))
           bbdb-records)))))
 
+(defun bbdb-register-record (record &optional warn)
+  "Register RECORD with BBDB.
+This performs the registration (including hash tables and cache) required both
+for records that are loaded from the database and for new records added to BBDB.
+If `bbdb-allow-duplicates' is nil, this throws an error if the name,
+an aka or mail address of RECORD is already in BBDB.  If WARN is non-nil,
+issue a warning instead.
+Do not call this function directly.  Call instead `bbdb-change-record'."
+  (bbdb-check-name (cons (bbdb-record-firstname record)
+                         (bbdb-record-lastname record))
+                   record warn)
+  (bbdb-check-mail (bbdb-record-mail record) record warn)
+
+  ;; If `bbdb-allow-duplicates' is non-nil, we allow that two records
+  ;; (with different uuids) refer to the same person (same name etc.).
+  ;; Such duplicate records are always hashed.
+  ;; Otherwise, an unhashed record would not be available for things
+  ;; like completion (and we would not know which record to keeep
+  ;; and which one to hide).  We trust the user she knows what
+  ;; she wants if she keeps duplicate records in the database though
+  ;; `bbdb-allow-duplicates' is nil.
+  (bbdb-puthash (bbdb-record-name record) record)
+  (bbdb-puthash (bbdb-record-name-lf record) record)
+  (dolist (organization (bbdb-record-organization record))
+    (bbdb-puthash organization record))
+  (dolist (aka (bbdb-record-aka record))
+    (bbdb-puthash aka record))
+  (bbdb-puthash-mail record)
+  (puthash (bbdb-record-uuid record) record bbdb-uuid-table)
+
+  ;; Update the completion lists
+  (dolist (phone (bbdb-record-phone record))
+    (bbdb-pushnew (bbdb-phone-label phone) bbdb-phone-label-list))
+  (dolist (address (bbdb-record-address record))
+    (bbdb-pushnew (bbdb-address-label address) bbdb-address-label-list)
+    (mapc (lambda (street) (bbdb-pushnewt street bbdb-street-list))
+          (bbdb-address-streets address))
+    (bbdb-pushnewt (bbdb-address-city address) bbdb-city-list)
+    (bbdb-pushnewt (bbdb-address-state address) bbdb-state-list)
+    (bbdb-pushnewt (bbdb-address-postcode address) bbdb-postcode-list)
+    (bbdb-pushnewt (bbdb-address-country address) bbdb-country-list))
+  (dolist (xfield (bbdb-record-xfields record))
+    (bbdb-pushnewq (car xfield) bbdb-xfield-label-list))
+  (dolist (organization (bbdb-record-organization record))
+    (bbdb-pushnew organization bbdb-organization-list)))
+
 (defun bbdb-before-save ()
   "Run before saving `bbdb-file' as buffer-local part of `before-save-hook'."
   (when (and bbdb-file-remote
@@ -3472,9 +3497,9 @@ If `bbdb-file' uses an outdated format,
   "Update the database after a change of RECORD.
 Return RECORD if RECORD got changed compared with the database,
 return nil otherwise.
-Hash RECORD if it is new.  If RECORD is not new, it is the the caller's
-responsibility to update the hashtables for RECORD.  (Up-to-date hashtables are
-ensured if the fields are modified by calling `bbdb-record-set-field'.)
+Register RECORD if it is new.  If RECORD is not new, it is the caller's
+responsibility to update this information for RECORD.  (This is ensured
+if the fields of RECORD are modified by calling `bbdb-record-set-field'.)
 Redisplay RECORD if it is not new.
 
 Args IGNORED are ignored and their use is discouraged.
@@ -3550,8 +3575,8 @@ They are present only for backward compa
           (bbdb-record-set-timestamp
            record (format-time-string bbdb-time-stamp-format nil t))
           (run-hook-with-args 'bbdb-change-hook record)
+          (bbdb-register-record record) ; Call this earlier?
           (bbdb-insert-record-internal record)
-          (bbdb-hash-record record)
           (bbdb-pushnewq record bbdb-changed-records)
           (run-hook-with-args 'bbdb-after-change-hook record)
           record)))))
