Commits

youngs  committed f33182d

Sync BBDB to version 2.32

Add ibuffer.el and search-buffers.el to edit-utils

Sync Eicq to version 0.2.14

Sync ilisp to version 5.11.1

Sync mailcrypt to version 3.5.6

  • Participants
  • Parent commits 8a668bc
  • Tags bbdb-1_13, xemacs-sumo-2001-07-08 1
    1. xemacs-sumo-2001-07-09

Comments (0)

Files changed (25)

+2001-07-01  Steve Youngs  <youngs@xemacs.org>
+
+	* Import BBDB 2.32
+
 2000-11-07  Martin Buchholz <martin@xemacs.org>
 
 	* ./texinfo/bbdb.texinfo: Doc fix.
 To compile and install the BBDB on systems with `make':
 
-  1) Configure the compilation process
-       Edit the Makefile, setting (if necessary) the paths to the Mail
-       and News reader(s), the XEmacs package installation paths (if
-       applicable), and the miscellaneous (but still important) build
-       tool names and paths.
+  1) Configure the compilation process:
 
-       NOTE:  Gnu Emacs 19.34 users should add the path to Custom
-       1.9962 if they plan to build Gnus support.  Gnus support will
-       not build under Gnu Emacs 19.34 without Custom 1.9962.
+       First of all, you should run the `configure' script at the toplevel of
+       the distribution. This script will perform a number of checks on your
+       system and generate the Makefile's accordingly.
 
-  2) Build the BBDB
+       The `configure' script comes with a number of options that might be of
+       interest for you:
+
+       `--with-emacs=PROG' lets you select an Emacs program to use. By
+         default, "emacs" and "xemacs" will be tried in that order.
+
+       `--with-{gnus,mhe,vm}-dir=DIR' let you specify the paths where Gnus,
+         MH-E and VM can respectively be found, if Emacs can't find them by
+         itself (like, if they're not in the load-path by default).
+
+       `--with-other-dirs=DIRS' lets you specify additional paths (space or
+         colon spearated) to find other libraries that might be needed (see
+         the "Byte Compiling the Lisp files" section of the BBDB manual for
+         more information).
+
+         NOTE: Gnu Emacs 19.34 users should add the path to Custom 1.9962
+         here if they plan to build Gnus support.  Gnus support will not build
+         under Gnu Emacs 19.34 without Custom 1.9962.
+
+       `--with-package-dir=DIR' lets you specify the installation path for
+         for XEmacs package installation. By default,
+         "/usr/local/lib/xemacs/site-packages" is used.
+
+       `--with-symlinks' will make the installation process use symbolic links
+         to the sources instead of copying the files.
+
+       `--with-linkpath=PATH' lets you specify the path to link from, if your
+         `pwd' command doesn't work properly.
+
+
+  2) Build the BBDB:
+
        To build the BBDB with support for all of the supported mail
        and news programs (gnus, mh-e, rmail, and vm), issue the
        command 'make all'.  (The BBDB also supports send-mail mode -
        program (except for send-mail mode), issue the command
        'make bbdb'.
 
-  3) Install the BBDB
+
+  3) Install the BBDB:
+
        The three most typical installations are outlined below:
 
         a) In-place.
                   (or make a new directory, copy the files to it, and
                   add the new directory to the Info search path)
         c) XEmacs Package
-             NOTE: This installation option is only available to users 
-                   running XEmacs 20.3 or higher.  At the time of this 
-                   writing, this includes only XEmacs beta testers.
-             i)    Follow steps 1 and 2, making sure to set the
-                   appropriate variables in the XEmacs package
-                   section.
+             NOTE: This installation option is only available to users
+                   running XEmacs 20.3 or higher.
+             i)    Follow steps 1 and 2.
              ii)   Issue the 'make install-pkg' command.
 
+
+
 To compile and install the BBDB on systems without `make':
 
   If at all possible, use make to automatically build the BBDB as
 *                                                                         *
 ***************************************************************************
 
-For information on post-installation BBDB configuration and setup, see 
+For information on post-installation BBDB configuration and setup, see
 the info file.
 
 Questions, Comments, Suggestions, and Bug Reports may be directed to
-the BBDB mailing list at bbdb-info@xemacs.org.  To subscribe, send
-mail to bbdb-info-request@xemacs.org, with 'subscribe' as the subject.
+the BBDB mailing list at bbdb-info@lists.sourceforge.net.  To
+subscribe, send mail to bbdb-info-request@lists.sourceforge.net, with
+'subscribe' as the subject.

File lisp/bbdb-com.el

 ;;; -*- Mode:Emacs-Lisp -*-
 
 ;;; This file is part of the Insidious Big Brother Database (aka BBDB),
-;;; copyright (c) 1991, 1992, 1993 Jamie Zawinski <jwz@jwz.org>.
+;;; copyright (c) 1991, 1992, 1993 Jamie Zawinski <jwz@netscape.com>.
 ;;; It contains most of the user-level interactive commands for BBDB.
 ;;; See bbdb.texinfo.
 
 ;;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
 ;;
-;; bbdb-com.el,v 1.65 1999/01/25 06:08:39 simmonmt Exp
+;; $Id$
 ;;
 
 (require 'bbdb)
 
+(eval-when-compile (require 'cl)) ; for `flet'
+
+(eval-when-compile              ; pacify the compiler
+ ;; defined in mailabbrev.el
+ (defvar mail-alias-separator-string)
+ (defvar mail-abbrevs)
+ (defvar mail-aliases))
+
+(autoload 'mail-abbrev-expand-hook "mailabbrev.el")
+
 (defmacro bbdb-grovel-elide-arg (arg)
   (list 'if arg
-	(list 'not (list 'eq arg 0))
-	'bbdb-elided-display))
+        (list 'not (list 'eq arg 0))
+        'bbdb-elided-display))
 
 
 (defmacro bbdb-search (records &optional name company net notes phone)
     (or (stringp notes) (symbolp notes) (error "notes must be atomic"))
     (or (stringp phone) (symbolp phone) (error "phone must be atomic"))
     (if phone
-	(setq clauses
-	      (cons
-	       (` (let ((rest-of-phones (bbdb-record-phones record))
-			(done nil))
-		    (if rest-of-phones
-			(while (and rest-of-phones (not done))
-			  (setq done (string-match (, phone)
-						   ;; way way wasteful...
-						   (bbdb-phone-string
-						    (car rest-of-phones)))
-				rest-of-phones (cdr rest-of-phones)))
-		      ;; so that "^$" can be used to find entries that
-		      ;; have no phones
-		      (setq done (string-match (, phone) "")))
-		    done))
-	       clauses)))
+        (setq clauses
+              (cons
+               (` (let ((rest-of-phones (bbdb-record-phones record))
+                        (done nil))
+                    (if rest-of-phones
+                        (while (and rest-of-phones (not done))
+                          (setq done (string-match (, phone)
+                                                   ;; way way wasteful...
+                                                   (bbdb-phone-string
+                                                    (car rest-of-phones)))
+                                rest-of-phones (cdr rest-of-phones)))
+                        ;; so that "^$" can be used to find entries that
+                        ;; have no phones
+                        (setq done (string-match (, phone) "")))
+                    done))
+               clauses)))
     (if notes
-	(setq clauses
-	      (cons
-	       (` (if (stringp (, notes))
-		      (string-match (, notes)
-				    (or (bbdb-record-notes record) ""))
-		    (if (eq (car (, notes)) '*)
-			(let ((fields all-fields) done tmp)
-			  (if (bbdb-record-raw-notes record)
-			      (while (and (not done) fields)
-				(setq tmp (bbdb-record-getprop
-					   record (car fields))
-				      done (and tmp (string-match
-						     (cdr (, notes))
-						     tmp))
-				      fields (cdr fields)))
-			    ;; so that "^$" can be used to find entries that
-			    ;; have no notes
-			    (setq done (string-match (cdr (, notes)) "")))
-			  done)
-		      (string-match (cdr (, notes))
-				    (or (bbdb-record-getprop
-					 record (car (, notes))) "")))))
-	       clauses)))
+        (setq clauses
+              (cons
+               (` (if (stringp (, notes))
+                      (string-match (, notes)
+                                    (or (bbdb-record-notes record) ""))
+                      (if (eq (car (, notes)) '*)
+                          (let ((fields all-fields) done tmp)
+                            (if (bbdb-record-raw-notes record)
+                                (while (and (not done) fields)
+                                  (setq tmp (bbdb-record-getprop
+                                             record (car fields))
+                                        done (and tmp (string-match
+                                                       (cdr (, notes))
+                                                       tmp))
+                                        fields (cdr fields)))
+                                ;; so that "^$" can be used to find entries that
+                                ;; have no notes
+                                (setq done (string-match (cdr (, notes)) "")))
+                            done)
+                          (string-match (cdr (, notes))
+                                        (or (bbdb-record-getprop
+                                             record (car (, notes))) "")))))
+               clauses)))
     (if name
-	(setq clauses
-	      (append
-	       (` ((string-match (, name) (or (bbdb-record-name record) ""))
-		   (let ((rest-of-aka (bbdb-record-aka record))
-			 (done nil))
-		     (while (and rest-of-aka (not done))
-		       (setq done (string-match (, name) (car rest-of-aka))
-			     rest-of-aka (cdr rest-of-aka)))
-		     done)))
-	       clauses)))
+        (setq clauses
+              (append
+               (` ((string-match (, name) (or (bbdb-record-name record) ""))
+                   (let ((rest-of-aka (bbdb-record-aka record))
+                         (done nil))
+                     (while (and rest-of-aka (not done))
+                       (setq done (string-match (, name) (car rest-of-aka))
+                             rest-of-aka (cdr rest-of-aka)))
+                     done)))
+               clauses)))
     (if net
-	(setq clauses
-	      (cons
-	       (` (let ((rest-of-nets (bbdb-record-net record))
-			(done nil))
-		    (if rest-of-nets
-			(while (and rest-of-nets (not done))
-			  (setq done (string-match (, net) (car rest-of-nets))
-				rest-of-nets (cdr rest-of-nets)))
-		      ;; so that "^$" can be used to find entries that
-		      ;; have no net addresses.
-		      (setq done (string-match (, net) "")))
-		    done))
-	       clauses)))
+        (setq clauses
+              (cons
+               (` (let ((rest-of-nets (bbdb-record-net record))
+                        (done nil))
+                    (if rest-of-nets
+                        (while (and rest-of-nets (not done))
+                          (setq done (string-match (, net) (car rest-of-nets))
+                                rest-of-nets (cdr rest-of-nets)))
+                        ;; so that "^$" can be used to find entries that
+                        ;; have no net addresses.
+                        (setq done (string-match (, net) "")))
+                    done))
+               clauses)))
     (if company
-	(setq clauses
-	      (cons
-	       (` (string-match (, company)
-				(or (bbdb-record-company record) "")))
-	       clauses)))
+        (setq clauses
+              (cons
+               (` (string-match (, company)
+                                (or (bbdb-record-company record) "")))
+               clauses)))
 
     (` (let ((matches '())
-	     (,@ (if notes
-		     '((all-fields (cons 'notes
-					 (mapcar
-					  (function (lambda (x)
-						      (intern (car x))))
-					  (bbdb-propnames)))))
-		   nil))
-	     (case-fold-search bbdb-case-fold-search)
-	     (records (, records))
-	     record)
-	 (while records
-	   (setq record (car records))
-	   (if (or (,@ clauses))
-	       (setq matches (cons record matches)))
-	   (setq records (cdr records)))
-	 (nreverse matches)))))
+             (,@ (if notes
+                     '((all-fields (cons 'notes
+                                    (mapcar (lambda (x) (intern (car x)))
+                                            (bbdb-propnames)))))
+                     nil))
+             (case-fold-search bbdb-case-fold-search)
+             (records (, records))
+             record)
+         (while records
+           (setq record (car records))
+           (if (or (,@ clauses))
+               (setq matches (cons record matches)))
+           (setq records (cdr records)))
+         (nreverse matches)))))
 
 
 ;;;###autoload
 (defun bbdb (string elidep)
-  "Display all entries in the BBDB matching the regexp STRING 
+  "Display all entries in the BBDB matching the regexp STRING
 in either the name(s), company, network address, or notes."
   (interactive "sRegular Expression for General Search: \nP")
-  (let ((bbdb-elided-display (bbdb-grovel-elide-arg elidep))
-	(notes (cons '* string)))
+  (let ((bbdb-elided-display (or bbdb-elided-display (and elidep t)))
+        (notes (cons '* string)))
     (bbdb-display-records
      (bbdb-search (bbdb-records) string string string notes nil))))
 
 (defun bbdb-notes (which string elidep)
   "Display all entries in BBDB matching STRING in the named notes field."
   (interactive
-    (list (completing-read "Notes field to search (RET for all): "
-			   (append '(("notes")) (bbdb-propnames))
-			   nil t)
-	  (if (featurep 'gmhist)
-	      (read-with-history-in 'bbdb-notes-field "Regular expression: ")
-	      (read-string "Regular Expression: "))
-	  current-prefix-arg))
+   (list (completing-read "Notes field to search (RET for all): "
+                          (append '(("notes")) (bbdb-propnames))
+                          nil t)
+         (if (featurep 'gmhist)
+             (read-with-history-in 'bbdb-notes-field "Regular expression: ")
+             (read-string "Regular Expression: "))
+         current-prefix-arg))
   (let ((bbdb-elided-display (bbdb-grovel-elide-arg elidep))
-	(notes (if (string= which "")
-		   (cons '* string)
-		 (cons (intern which) string))))
+        (notes (if (string= which "")
+                   (cons '* string)
+                   (cons (intern which) string))))
     (bbdb-display-records (bbdb-search (bbdb-records) nil nil nil notes))))
 
 (defun bbdb-phones (string elidep)
   (interactive "P")
   (let ((bbdb-elided-display (bbdb-grovel-elide-arg elidep)))
     (bbdb-display-records
-      (bbdb-with-db-buffer
-	bbdb-changed-records))))
+     (bbdb-with-db-buffer
+      bbdb-changed-records))))
 
-(defun bbdb-display (record)
+(defun bbdb-display (records)
   "Prompts for and displays a single record (this is faster than searching.)"
   (interactive (list (bbdb-completing-read-record "Display record of: ")))
-  (bbdb-display-records (list record)))
+  (bbdb-display-records records))
 
 (defun bbdb-display-some (function)
   "Display records according to FUNCTION.  FUNCTION is called with one
 ;;;###autoload
 (defun bbdb-redisplay-records ()
   "Regrinds the contents of the *BBDB* buffer, without scrolling.
-If possible, you should call bbdb-redisplay-one-record instead."
+If possible, you should call `bbdb-redisplay-one-record' instead."
   (let ((p (point))
-	(m (condition-case condition (mark) (error nil))))
+        (m (condition-case condition (mark) (error nil))))
     (goto-char (window-start))
     (let ((p2 (point)))
       (bbdb-display-records-1 bbdb-records)
     (recenter 0)
     (goto-char p)
     (save-excursion
-      (run-hooks 'bbdb-list-hook))
-    ))
+      (run-hooks 'bbdb-list-hook))))
 
 (defun bbdb-redisplay-one-record (record &optional record-cons next-record-cons
-					 delete-p)
+                                  delete-p)
   "Regrind one record.  The *BBDB* buffer must be current when this is called."
   (bbdb-debug (if (not (eq (not (not delete-p))
-			   (not (not (bbdb-record-deleted-p record)))))
-		  (error "splorch.")))
+                           (not (not (bbdb-record-deleted-p record)))))
+                  (error "splorch.")))
   (if (null record-cons) (setq record-cons (assq record bbdb-records)))
   (if (null next-record-cons)
       (setq next-record-cons (car (cdr (memq record-cons bbdb-records)))))
   (beginning-of-line)
   (let ((marker (nth 2 record-cons))
-	(next-marker (nth 2 next-record-cons))
-	(buffer-read-only nil)
-	(p (point)))
+        (next-marker (nth 2 next-record-cons))
+        (buffer-read-only nil)
+        (p (point)))
     (bbdb-debug
-      (if (null record-cons) (error "doubleplus ungood: record unexists!"))
-      (if (null marker) (error "doubleplus ungood: marker unexists!")))
+     (if (null record-cons) (error "doubleplus ungood: record unexists!"))
+     (if (null marker) (error "doubleplus ungood: marker unexists!")))
     (goto-char marker)
     (if delete-p nil
-	(bbdb-format-record (car record-cons) (car (cdr record-cons))))
+        (bbdb-format-record (car record-cons) (car (cdr record-cons))))
     (delete-region (point) (or next-marker (point-max)))
     (goto-char p)
     (save-excursion
-      (run-hooks 'bbdb-list-hook))
-    ))
+      (run-hooks 'bbdb-list-hook))))
 
 
 
 (defun bbdb-parse-phone-number (string &optional number-type)
   "Parse a phone number from STRING and return a list of integers the form
 \(area-code exchange number) or (area-code exchange number extension).
-This is both lenient and strict in what it will parse - whitespace may 
+This is both lenient and strict in what it will parse - whitespace may
 appear (or not) between any of the groups of digits, parentheses around the
 area code are optional, as is a dash between the exchange and number, and
-a '1' preceding the area code; but there must be three digits in the area 
-code and exchange, and four in the number (if they are present).  An error 
-will be signalled if unparsable.  All of these are unambiguously parsable:
+a '1' preceeding the area code; but there must be three digits in the area
+code and exchange, and four in the number (if they are present).  An error
+will be signalled if unparsable.  All of these are unambigously parsable:
 
   ( 415 ) 555 - 1212 x123   ->   (415 555 1212 123)
   (415)555-1212 123         ->   (415 555 1212 123)
 Note that \"4151212123\" is ambiguous; it could be interpreted either as
 \"(415) 121-2123\" or as \"415-1212 x123\".
 
-\(And uh, oh yeah, this does little if bbdb-north-american-phone-numbers-p
+\(And uh, oh yeah, this does little if `bbdb-north-american-phone-numbers-p'
 is nil...\)"
 
   (cond ((if number-type
-	     (eq number-type 'euro)
-	   (not bbdb-north-american-phone-numbers-p))
-	 (list (bbdb-string-trim string)))
-	((string-match bbdb-phone-regexp-1 string)
-	 ;; (415) 555-1212 x123
-	 (list (bbdb-subint string 1) (bbdb-subint string 2)
-	       (bbdb-subint string 3) (bbdb-subint string 4)))
-	((string-match bbdb-phone-regexp-2 string)
-	 ;; (415) 555-1212
-	 (list (bbdb-subint string 1) (bbdb-subint string 2)
-	       (bbdb-subint string 3)))
-	((string-match bbdb-phone-regexp-3 string)
-	 ;; 555-1212 x123
-	 (list 0 (bbdb-subint string 1) (bbdb-subint string 2)
-	       (bbdb-subint string 3)))
-	((string-match bbdb-phone-regexp-4 string)
-	 ;; 555-1212
-	 (list 0 (bbdb-subint string 1) (bbdb-subint string 2)))
-	((string-match bbdb-phone-regexp-5 string)
-	 ;; x123
-	 (list 0 0 0 (bbdb-subint string 1)))
-	(t (error "phone number unparsable."))))
+             (eq number-type 'euro)
+             (not bbdb-north-american-phone-numbers-p))
+         (list (bbdb-string-trim string)))
+        ((string-match bbdb-phone-regexp-1 string)
+         ;; (415) 555-1212 x123
+         (list (bbdb-subint string 1) (bbdb-subint string 2)
+               (bbdb-subint string 3) (bbdb-subint string 4)))
+        ((string-match bbdb-phone-regexp-2 string)
+         ;; (415) 555-1212
+         (list (bbdb-subint string 1) (bbdb-subint string 2)
+               (bbdb-subint string 3)))
+        ((string-match bbdb-phone-regexp-3 string)
+         ;; 555-1212 x123
+         (list 0 (bbdb-subint string 1) (bbdb-subint string 2)
+               (bbdb-subint string 3)))
+        ((string-match bbdb-phone-regexp-4 string)
+         ;; 555-1212
+         (list 0 (bbdb-subint string 1) (bbdb-subint string 2)))
+        ((string-match bbdb-phone-regexp-5 string)
+         ;; x123
+         (list 0 0 0 (bbdb-subint string 1)))
+        (t (error "phone number unparsable."))))
 
 
 
+(defcustom bbdb-expand-mail-aliases t
+  "If non-nil, expand mail aliases in `bbdb-complete-name'."
+  :group 'bbdb-record-use
+  :type 'boolean)
+
+(defcustom bbdb-check-zip-codes-p t
+  "If non-nil, require legal zip codes when entering an address.
+The format of legal zip codes is determined by the variable
+`bbdb-legal-zip-codes'."
+  :group 'bbdb-record-creation
+  :type 'boolean)
+
+(defcustom bbdb-legal-zip-codes
+  '(;; empty string
+    "^$"
+    ;; Matches 1 to 6 digits.
+    "^[ \t\n]*[0-9][0-9]?[0-9]?[0-9]?[0-9]?[0-9]?[ \t\n]*$"
+    ;; Matches 5 digits and 3 or 4 digits.
+    "^[ \t\n]*\\([0-9][0-9][0-9][0-9][0-9]\\)[ \t\n]*-?[ \t\n]*\\([0-9][0-9][0-9][0-9]?\\)[ \t\n]*$"
+    ;; Match zip codes for Canada, UK, etc. (result is ("LL47" "U4B")).
+    "^[ \t\n]*\\([A-Za-z0-9]+\\)[ \t\n]+\\([A-Za-z0-9]+\\)[ \t\n]*$"
+    ;; Match zip codes for continental Europe.  Examples "CH-8057"
+    ;; or "F - 83320" (result is ("CH" "8057") or ("F" "83320")).
+    ;; Support for "NL-2300RA" added at request from Carsten Dominik
+    ;; <dominik@astro.uva.nl>
+    "^[ \t\n]*\\([A-Z]+\\)[ \t\n]*-?[ \t\n]*\\([0-9]+ ?[A-Z]*\\)[ \t\n]*$"
+    ;; Match zip codes from Sweden where the five digits are grouped 3+2
+    ;; at the request from Mats Lofdahl <MLofdahl@solar.stanford.edu>.
+    ;; (result is ("SE" (133 36)))
+    "^[ \t\n]*\\([A-Z]+\\)[ \t\n]*-?[ \t\n]*\\([0-9]+\\)[ \t\n]+\\([0-9]+\\)[ \t\n]*$")
+  "List of regexps that match legal zip codes.
+Whether this is used at all depends on the variable `bbdb-check-zip-codes-p'."
+  :group 'bbdb-record-creation
+  :type '(repeat regexp))
+
 (defun bbdb-parse-zip-string (string)
-  (cond ((string-match "^[ \t\n]*$" string) 0)
-	((string-match "^[ \t\n]*[0-9][0-9][0-9][0-9][0-9][ \t\n]*$" string)
-	 (string-to-int string))
-	((string-match "^[ \t\n]*\\([0-9][0-9][0-9][0-9][0-9]\\)[ \t\n]*-?[ \t\n]*\\([0-9][0-9][0-9][0-9]\\)[ \t\n]*$" string)
-	 (list (bbdb-subint string 1) (bbdb-subint string 2)))
-	;; Match zip codes for Canada, UK, etc. (result is ("LL47" "U4B")).
-	((string-match
-	  "^[ \t\n]*\\([A-Za-z0-9]+\\)[ \t\n]+\\([A-Za-z0-9]+\\)[ \t\n]*$"
-	  string)
-	 (list (substring string (match-beginning 1) (match-end 1))
-	       (substring string (match-beginning 2) (match-end 2))))
-	((string-match "-[^-]-" string)
-	 (error "too many dashes in zip code."))
-	((string-match "[^-0-9 \t\n]" string)
-	 (error "illegal characters in zip code."))
-	((string-match "[0-9][0-9][0-9][0-9][0-9][0-9]" string)
-	 (error "too many digits in zip code."))
-	((< (length string) 5)
-	 (error "not enough digits in zip code."))
-	(t (error "not a valid 5-digit or 5+4 digit zip code."))))
-
+  "Check wether STRING is a legal zip code.
+Do this only if `bbdb-check-zip-codes-p' is non-nil."
+  (if (and bbdb-check-zip-codes-p
+       (not (memq t (mapcar (lambda (regexp)
+                  ;; if it matches, (not (not index-of-match)) returns t
+                  (not (not (string-match regexp string))))
+                bbdb-legal-zip-codes))))
+      (error "not a valid zip code.")
+    string))
 
 (defun bbdb-read-new-record ()
-  "Prompt for and return a completely new bbdb-record.  Doesn't insert it in to
-the database or update the hashtables, but does insure that there will not be
-name collisions."
-  (bbdb-records) ; make sure database is loaded
+  "Prompt for and return a completely new BBDB record.
+Doesn't insert it in to the database or update the hashtables, but does
+insure that there will not be name collisions."
+  (bbdb-records)                ; make sure database is loaded
   (if bbdb-readonly-p (error "The Insidious Big Brother Database is read-only."))
   (let (firstname lastname)
     (bbdb-error-retry
-      (progn
-	(if current-prefix-arg
-	    (setq firstname (bbdb-read-string "First Name: ")
-		  lastname (bbdb-read-string "Last Name: "))
-	  (let ((names (bbdb-divide-name (bbdb-read-string "Name: "))))
-	    (setq firstname (car names)
-		  lastname (nth 1 names))))
-	(if (string= firstname "") (setq firstname nil))
-	(if (string= lastname "") (setq lastname nil))
-	(if (bbdb-gethash (downcase (if (and firstname lastname) (concat firstname " " lastname)
-					(or firstname lastname ""))))
-	    (error "%s %s is already in the database" (or firstname "") (or lastname "")))))
+     (progn
+       (if current-prefix-arg
+           (setq firstname (bbdb-read-string "First Name: ")
+                 lastname (bbdb-read-string "Last Name: "))
+           (let ((names (bbdb-divide-name (bbdb-read-string "Name: "))))
+             (setq firstname (car names)
+                   lastname (nth 1 names))))
+       (if (string= firstname "") (setq firstname nil))
+       (if (string= lastname "") (setq lastname nil))
+       (if (and bbdb-no-duplicates-p
+                (bbdb-gethash (bbdb-build-name firstname lastname)))
+           (error "%s %s is already in the database"
+                  (or firstname "") (or lastname "")))))
     (let ((company (bbdb-read-string "Company: "))
-	  (net (bbdb-split (bbdb-read-string "Network Address: ") ","))
-	  (addrs (let (L L-tail str addr)
-		   (while (not (string= ""
-				 (setq str (bbdb-read-string "Address Description [RET when no more addrs]: "))))
-		     (setq addr (make-vector bbdb-address-length nil))
-		     (bbdb-record-edit-address addr str)
-		     (if L
-			 (progn (setcdr L-tail (cons addr nil))
-				(setq L-tail (cdr L-tail)))
-			 (setq L (cons addr nil)
-			       L-tail L)))
-		   L))
-	  (phones (let (L L-tail str)
-		    (while (not (string= ""
-					 (setq str
-					       (bbdb-read-string "Phone Location [RET when no more phones]: "))))
-		      (let* ((phonelist
-			      (bbdb-error-retry
-				(bbdb-parse-phone-number
-				  (read-string "Phone: "
-					       (and (integerp bbdb-default-area-code)
-						    (format "(%03d) " bbdb-default-area-code))))))
-			     (phone (apply 'vector str
-					   (if (= 3 (length phonelist))
-					       (nconc phonelist '(0))
-					       phonelist))))
-			(if L
-			    (progn (setcdr L-tail (cons phone nil))
-				   (setq L-tail (cdr L-tail)))
-			    (setq L (cons phone nil)
-				  L-tail L))))
-		    L))
-	  (notes (bbdb-read-string "Additional Comments: ")))
+          (net (bbdb-split (bbdb-read-string "Network Address: ") ","))
+          (addrs (let (L L-tail str addr)
+                   (while (not (string= ""
+                                        (setq str (bbdb-read-string "Address Description [RET when no more addrs]: "))))
+                     (setq addr (make-vector bbdb-address-length nil))
+                     (bbdb-record-edit-address addr str)
+                     (if L
+                         (progn (setcdr L-tail (cons addr nil))
+                                (setq L-tail (cdr L-tail)))
+                         (setq L (cons addr nil)
+                               L-tail L)))
+                   L))
+          (phones (let (L L-tail str)
+                    (while (not (string= ""
+                                         (setq str
+                                               (bbdb-read-string "Phone Location [RET when no more phones]: "))))
+                      (let* ((phonelist
+                              (bbdb-error-retry
+                               (bbdb-parse-phone-number
+                                (read-string "Phone: "
+                                             (and (integerp bbdb-default-area-code)
+                                                  (format "(%03d) " bbdb-default-area-code))))))
+                             (phone (apply 'vector str
+                                           (if (= 3 (length phonelist))
+                                               (nconc phonelist '(0))
+                                               phonelist))))
+                        (if L
+                            (progn (setcdr L-tail (cons phone nil))
+                                   (setq L-tail (cdr L-tail)))
+                            (setq L (cons phone nil)
+                                  L-tail L))))
+                    L))
+          (notes (bbdb-read-string "Additional Comments: ")))
       (if (string= company "") (setq company nil))
       (if (string= notes "") (setq notes nil))
       (let ((record
-	     (vector firstname lastname nil company phones addrs net notes
-		     (make-vector bbdb-cache-length nil))))
-	record))))
+             (vector firstname lastname nil company phones addrs net notes
+                     (make-vector bbdb-cache-length nil))))
+        record))))
 
 ;;;###autoload
 (defun bbdb-create (record)
 
 (defmacro bbdb-check-type (place predicate)
   (list 'while (list 'not (list predicate place))
-	(nconc (cond ((eq (car-safe place) 'aref)
-		      (list 'aset (nth 1 place) (nth 2 place)))
-		     ((eq (car-safe place) 'car)
-		      (list 'setcar (nth 1 place)))
-		     ((eq (car-safe place) 'cdr)
-		      (list 'setcdr (nth 1 place)))
-		     (t (list 'setq place)))
-	       (list 
-		(list 'signal ''wrong-type-argument
-		      (list 'list (list 'quote predicate) place))))))
-
+        (nconc (cond ((eq (car-safe place) 'aref)
+                      (list 'aset (nth 1 place) (nth 2 place)))
+                     ((eq (car-safe place) 'car)
+                      (list 'setcar (nth 1 place)))
+                     ((eq (car-safe place) 'cdr)
+                      (list 'setcdr (nth 1 place)))
+                     (t (list 'setq place)))
+               (list
+                (list 'signal ''wrong-type-argument
+                      (list 'list (list 'quote predicate) place))))))
 
 (defun bbdb-create-internal (name company net addrs phones notes)
   "Adds a record to the database; this function does a fair amount of
 other programs.
 
 NAME is a string, the name of the person to add.  An error is signalled
- if that name is already in use.
+ if that name is already in use and `bbdb-no-duplicates-p' is t.
 COMPANY is a string or nil.
 NET is a comma-separated list of email addresses, or a list of strings.
  An error is signalled if that name is already in use.
 ADDRS is a list of address objects.  An address is a vector of the form
-   [\"location\" \"line1\" \"line2\" \"line3\" \"City\" \"State\" zip]
- where `zip' is nil, an integer, or a cons of two integers.
+   [\"location\" (\"line1\" \"line2\" ... ) \"State\" \"Zip\" \"Country\"].
 PHONES is a list of phone-number objects.  A phone-number is a vector of
  the form
    [\"location\" areacode prefix suffix extension-or-nil]
    [\"location\" \"phone-number\"]
 NOTES is a string, or an alist associating symbols with strings."
   (let (firstname lastname aka)
-    (while (progn
-	     (setq name (and name (bbdb-divide-name name)))
-	     (setq firstname (car name) lastname (nth 1 name))
-	     (bbdb-gethash (downcase (if (and firstname lastname)
-					 (concat firstname " " lastname)
-				       (or firstname lastname "")))))
+    (while (and (progn
+                  (setq name      (and name (bbdb-divide-name name))
+                        firstname (car name)
+                        lastname  (nth 1 name))
+                  (bbdb-gethash (bbdb-build-name firstname lastname)))
+                bbdb-no-duplicates-p)
       (setq name (signal 'error
-			 (list (format "%s %s is already in the database"
-				       (or firstname "") (or lastname ""))))))
+                         (list (format "%s %s is already in the database"
+                                       (or firstname "") (or lastname ""))))))
     (and company (bbdb-check-type company stringp))
     (if (stringp net)
-	(setq net (bbdb-split net ",")))
-    (let ((rest net))
-      (while rest
-	(while (bbdb-gethash (downcase (car rest)))
-	  (setcar rest
-		  (signal 'error (list (format
-					"%s is already in the database"
-					(car rest))))))
-	(setq rest (cdr rest))))
+        (setq net (bbdb-split net ",")))
+    (if bbdb-no-duplicates-p
+        (let ((rest net))
+          (while rest
+            (while (bbdb-gethash (downcase (car rest)))
+              (setcar rest
+                      (signal 'error (list (format
+                                            "%s is already in the database"
+                                            (car rest))))))
+            (setq rest (cdr rest)))))
     (setq addrs
-	  (mapcar
-	    (function (lambda (addr)
-	      (while (or (not (vectorp addr))
-			 (/= (length addr) bbdb-address-length))
-		(setq addr (signal 'wrong-type-argument (list 'vectorp addr))))
-	      (bbdb-check-type (aref addr 0) stringp)
-	      (bbdb-check-type (aref addr 1) stringp)
-	      (bbdb-check-type (aref addr 2) stringp)
-	      (bbdb-check-type (aref addr 3) stringp)
-	      (bbdb-check-type (aref addr 4) stringp)
-	      (bbdb-check-type (aref addr 5) stringp)
-	      (while (and (aref addr 6)
-			  (not (integerp (aref addr 6)))
-			  (not (and (consp (aref addr 6))
-				    (integerp (car (aref addr 6)))
-				    (integerp (car (cdr (aref addr 6))))
-				    (null (cdr (cdr (aref addr 6)))))))
-		(aset addr 6 (signal 'wrong-type-argument
-				     (list 'zipcodep (aref addr 6)))))
-	      addr))
-	    addrs))
+      (mapcar
+       (lambda (addr)
+         (while (or (not (vectorp addr))
+            (/= (length addr) bbdb-address-length))
+           (setq addr (signal 'wrong-type-argument (list 'vectorp addr))))
+         (bbdb-check-type (aref addr 0) stringp) ;;; XXX use bbdb-addresses
+         (bbdb-check-type (aref addr 1) listp)
+         (bbdb-check-type (aref addr 2) stringp)
+         (bbdb-check-type (aref addr 3) stringp)
+         (bbdb-check-type (aref addr 4) stringp)
+         (bbdb-check-type (aref addr 5) stringp)
+         addr)
+       addrs))
     (setq phones
-	  (mapcar
-	   (function (lambda (phone)
-	     (while (or (not (vectorp phone))
-			(and (/= (length phone) 2)
-			     (/= (length phone) bbdb-phone-length)))
-	       (setq phone
-		     (signal 'wrong-type-argument (list 'vectorp phone))))
-	     (bbdb-check-type (aref phone 0) stringp)
-	     (if (= 2 (length phone))
-		 (bbdb-check-type (aref phone 1) stringp)
-	       (bbdb-check-type (aref phone 1) integerp)
-	       (bbdb-check-type (aref phone 2) integerp)
-	       (bbdb-check-type (aref phone 3) integerp)
-	       (and (aref phone 4) (bbdb-check-type (aref phone 4) integerp))
-	       (if (eq 0 (aref phone 4)) (aset phone 4 nil)))
-	     phone))
-	   phones))
+          (mapcar
+           (lambda (phone)
+             (while (or (not (vectorp phone))
+                        (and (/= (length phone) 2)
+                             (/= (length phone) bbdb-phone-length)))
+               (setq phone
+                     (signal 'wrong-type-argument (list 'vectorp phone))))
+             (bbdb-check-type (aref phone 0) stringp)
+             (if (= 2 (length phone))
+                 (bbdb-check-type (aref phone 1) stringp)
+                 (bbdb-check-type (aref phone 1) integerp)
+                 (bbdb-check-type (aref phone 2) integerp)
+                 (bbdb-check-type (aref phone 3) integerp)
+                 (and (aref phone 4) (bbdb-check-type (aref phone 4) integerp))
+                 (if (eq 0 (aref phone 4)) (aset phone 4 nil)))
+             phone)
+           phones))
     (or (stringp notes)
-	(setq notes
-	      (mapcar (function (lambda (note)
-		        (bbdb-check-type note consp)
-			(bbdb-check-type (car note) symbolp)
-			(if (consp (cdr note))
-			    (setq note (cons (car note) (car (cdr note)))))
-			(bbdb-check-type (cdr note) stringp)
-		        note))
-		      notes)))
+        (setq notes
+              (mapcar (lambda (note)
+                        (bbdb-check-type note consp)
+                        (bbdb-check-type (car note) symbolp)
+                        (if (consp (cdr note))
+                            (setq note (cons (car note) (car (cdr note)))))
+                        (bbdb-check-type (cdr note) stringp)
+                        note)
+                      notes)))
     (let ((record
-	   (vector firstname lastname aka company phones addrs net notes
-		   (make-vector bbdb-cache-length nil))))
+           (vector firstname lastname aka company phones addrs net notes
+                   (make-vector bbdb-cache-length nil))))
       (bbdb-invoke-hook 'bbdb-create-hook record)
       (bbdb-change-record record t)
       record)))
       (error "The Insidious Big Brother Database is read-only."))
   (if (not (equal bbdb-buffer-name (buffer-name (current-buffer))))
       (error "this command only works while in the \"%s\" buffer."
-	     bbdb-buffer-name))
+             bbdb-buffer-name))
   (let ((p (point))
-	(rest bbdb-records)
-	(rec nil))
+        (rest bbdb-records)
+        (rec nil))
     (while (and (cdr rest) (not rec))
       (if (> (nth 2 (car (cdr rest))) p)
-	  (setq rec (car (car rest))))
+          (setq rec (car (car rest))))
       (setq rest (cdr rest)))
     (or rec (car (car rest)))))
 
 
 ;; yow, are we object oriented yet?
 (defun bbdb-record-get-field-internal (record field)
-  (cond ((eq field 'name)	(bbdb-record-name record))
-	((eq field 'net)	(bbdb-record-net record))
-	((eq field 'aka)	(bbdb-record-aka record))
-	((eq field 'phone)	(bbdb-record-phones record))
-	((eq field 'address)	(bbdb-record-addresses record))
-	((eq field 'property)	(bbdb-record-raw-notes record))
-	(t (error "doubleplus ungood: unknown field type %s" field))))
+  (cond ((eq field 'name)   (bbdb-record-name record))
+        ((eq field 'net)    (bbdb-record-net record))
+        ((eq field 'aka)    (bbdb-record-aka record))
+        ((eq field 'phone)  (bbdb-record-phones record))
+        ((eq field 'address)    (bbdb-record-addresses record))
+        ((eq field 'property)   (bbdb-record-raw-notes record))
+        (t (error "doubleplus ungood: unknown field type %s" field))))
 
 (defun bbdb-record-store-field-internal (record field value)
-  (cond ((eq field 'name)	(error "doesn't work on names"))
-	((eq field 'net)	(bbdb-record-set-net record value))
-	((eq field 'aka)	(bbdb-record-set-aka record value))
-	((eq field 'phone)	(bbdb-record-set-phones record value))
-	((eq field 'address)	(bbdb-record-set-addresses record value))
-	((eq field 'property)	(bbdb-record-set-raw-notes record value))
-	(t (error "doubleplus ungood: unknown field type %s" field))))
+  (cond ((eq field 'name)   (error "doesn't work on names"))
+        ((eq field 'net)    (bbdb-record-set-net record value))
+        ((eq field 'aka)    (bbdb-record-set-aka record value))
+        ((eq field 'phone)  (bbdb-record-set-phones record value))
+        ((eq field 'address)    (bbdb-record-set-addresses record value))
+        ((eq field 'property)   (bbdb-record-set-raw-notes record value))
+        (t (error "doubleplus ungood: unknown field type %s" field))))
 
 (defun bbdb-record-edit-field-internal (record field &optional which)
-  (cond ((eq field 'name)	(bbdb-record-edit-name record))
-	((eq field 'net)	(bbdb-record-edit-net record))
-	((eq field 'aka)	(bbdb-record-edit-aka record))
-	((eq field 'phone)	(bbdb-record-edit-phone which))
-	((eq field 'address)	(bbdb-record-edit-address which))
-	((eq field 'property)	(bbdb-record-edit-property record (car which)))
-	(t (error "doubleplus ungood: unknown field type %s" field))))
+  (cond ((eq field 'name)   (bbdb-record-edit-name record))
+        ((eq field 'net)    (bbdb-record-edit-net record))
+        ((eq field 'aka)    (bbdb-record-edit-aka record))
+        ((eq field 'phone)  (bbdb-record-edit-phone which))
+        ((eq field 'address)    (bbdb-record-edit-address which))
+        ((eq field 'property)   (bbdb-record-edit-property record (car which)))
+        (t (error "doubleplus ungood: unknown field type %s" field))))
 
-	
+
 (defun bbdb-current-field (&optional planning-on-modifying)
   (save-excursion
     ;; get to beginning of this record
     (beginning-of-line)
     (let ((p (point)))
-      (while (not (or (eobp) (bobp) (looking-at "^[^ \t\n]")))
-	(forward-line -1))
+      ;; ' - ' is the start of a record with no name.
+      (while (not (or (eobp) (bobp) (looking-at "^\\([^ \t\n]\\| - \\)")))
+        (forward-line -1))
       (let* ((record (or (bbdb-current-record planning-on-modifying)
-			 (error "unperson")))
-	     (bbdb-elided-display (nth 1 (assq record bbdb-records)))
-	     (count 0)
-	     (tmp (nconc
-		   (list (list 'name record))
-		   (and (bbdb-field-shown-p 'phone)
-		     (mapcar (function (lambda (phone) (list 'phone phone)))
-			     (bbdb-record-phones record)))
-		   (and (bbdb-field-shown-p 'address)
-		     (apply 'nconc
-		       (mapcar (function (lambda (addr)
-				(let ((L (list 'address addr)))
-				  (nconc
-				   (if (string= "" (bbdb-address-street1 addr))
-				       nil (list L))
-				   (if (string= "" (bbdb-address-street2 addr))
-				       nil (list L))
-				   (if (string= "" (bbdb-address-street3 addr))
-				       nil (list L))
-				   (list L)))))
-			       (bbdb-record-addresses record))))
-		   (if (and (bbdb-record-net record)
-			    (bbdb-field-shown-p 'net))
-		       (list (list 'net record)))
-		   (if (and (bbdb-record-aka record)
-			    (bbdb-field-shown-p 'aka))
-		       (list (list 'aka record)))
-		   (let ((notes (bbdb-record-raw-notes record)))
-		     (if (stringp notes)
-			 (setq notes (list (cons 'notes notes))))
-		     (apply
-		       'nconc
-		       (mapcar
-			(function (lambda (note)
-			  (if (bbdb-field-shown-p (car note))
-			      (let* ((L (list 'property note))
-				     (LL (list L))
-				     (i 0)
-				     (notefun (intern (concat "bbdb-format-record-"
-							      (symbol-name (car note)))))
-				     (text (if (fboundp notefun)
-						 (funcall notefun (cdr note))
-					     (cdr note))))
-				(while (string-match "\n" text i)
-				  (setq i (match-end 0)
-					LL (cons L LL)))
-				LL))))
-			notes)))
-		   )))
-	(while (< (point) p)
-	  (setq count (1+ count))
-	  (forward-line 1))
-	(nth count tmp)))))
+                         (error "unperson")))
+             (bbdb-elided-display (nth 1 (assq record bbdb-records)))
+             (count 0)
+             (tmp (nconc
+                   (list (list 'name record))
+                   (and (bbdb-field-shown-p 'phone)
+                        (mapcar (lambda (phone) (list 'phone phone))
+                                (bbdb-record-phones record)))
+                   (and (bbdb-field-shown-p 'address)
+                        (apply 'nconc
+                               (mapcar (lambda (addr);; foreach address...
+                                         (let ((L (list 'address addr)))
+                                           (nconc
+                                            ;; XXX This block of code is dependant on how your
+                                            ;; addresses are formatted. So, if you define a new
+                                            ;; address format, this needs to be fixed. Ideally,
+                                            ;; the address format would have an accompanying
+                                            ;; function to tell you how many lines per address,
+                                            ;; or something.
+                                            ;; count street lines
+                                            (apply 'nconc (mapcar (lambda(street)
+                                                                    (unless (string= "" street)
+                                                                      (list L)))
+                                                                  (bbdb-address-streets addr)))
+
+                                            ;; these are all on the same line
+                                            (if (and (string= "" (bbdb-address-city addr))
+                                                     (string= "" (bbdb-address-state addr))
+                                                     (string= "" (bbdb-address-zip-string addr)))
+                                                nil (list L))
+
+                                            ;; separate line for country
+                                            (if (string= "" (bbdb-address-country addr))
+                                                nil (list L)))))
+                                       (bbdb-record-addresses record))))
+
+                   (if (and (bbdb-record-net record)
+                            (bbdb-field-shown-p 'net))
+                       (list (list 'net record)))
+                   (if (and (bbdb-record-aka record)
+                            (bbdb-field-shown-p 'aka))
+                       (list (list 'aka record)))
+                   (let ((notes (bbdb-record-raw-notes record)))
+                     (if (stringp notes)
+                         (setq notes (list (cons 'notes notes))))
+                     (apply
+                      'nconc
+                      (mapcar
+                       (lambda (note)
+                         (if (bbdb-field-shown-p (car note))
+                             (let* ((L (list 'property note))
+                                    (LL (list L))
+                                    (i 0)
+                                    (notefun (intern (concat "bbdb-format-record-"
+                                                             (symbol-name (car note)))))
+                                    (text (if (fboundp notefun)
+                                              (funcall notefun (cdr note))
+                                              (cdr note))))
+                               (while (string-match "\n" text i)
+                                 (setq i (match-end 0)
+                                       LL (cons L LL)))
+                               LL)))
+                       notes))))))
+        (while (< (point) p)
+          (setq count (1+ count))
+          (forward-line 1))
+        (nth count tmp)))))
 
 
 ;;;###autoload
 certain commands.\)"
   (interactive)
   (message (substitute-command-keys
-	    "\\<bbdb-mode-map>\\[bbdb-apply-next-command-to-all-records] - "))
+            "\\<bbdb-mode-map>\\[bbdb-apply-next-command-to-all-records] - "))
   (setq prefix-arg current-prefix-arg
-	last-command this-command)
+        last-command this-command)
   nil)
 
 (defmacro bbdb-do-all-records-p ()
-  "Whether the last command was bbdb-apply-next-command-to-all-records."
+  "Whether the last command was `bbdb-apply-next-command-to-all-records'."
   '(eq last-command 'bbdb-apply-next-command-to-all-records))
 
 
 argument.  A prefix arg of ^U means it's to be a euronumber, and any
 other prefix arg means it's to be a a structured north american number.
 Otherwise, which style is used is controlled by the variable
-bbdb-north-american-phone-numbers-p."
+`bbdb-north-american-phone-numbers-p'."
   (interactive (let ((name "")
-		     (completion-ignore-case t))
-		 (while (string= name "")
-		   (setq name
-			 (downcase
-			   (completing-read "Insert Field: "
-			     (append '(("phone") ("address") ("net")
-				       ("AKA") ("notes"))
-				     (bbdb-propnames))
-			     nil
-			     nil ; used to be t
-			     nil))))
-		 (setq name (intern name))
-		 (list name (bbdb-prompt-for-new-field-value name))))
+                     (completion-ignore-case t))
+                 (while (string= name "")
+                   (setq name
+                         (downcase
+                          (completing-read "Insert Field: "
+                                           (append '(("phone") ("address") ("net")
+                                                     ("AKA") ("notes"))
+                                                   (bbdb-propnames))
+                                           nil
+                                           nil ; used to be t
+                                           nil))))
+                 (setq name (intern name))
+                 (list name (bbdb-prompt-for-new-field-value name))))
   (if (null contents)
       (setq contents (bbdb-prompt-for-new-field-value name)))
   (let ((record (bbdb-current-record t)))
     (if (null record) (error "current record unexists!"))
     (cond ((eq name 'phone)
-	   (bbdb-record-set-phones record
-	     (nconc (bbdb-record-phones record) (list contents))))
-	  ((eq name 'address)
-	   (bbdb-record-set-addresses record
-	     (nconc (bbdb-record-addresses record) (list contents))))
-	  ((eq name 'net)
-	   (if (bbdb-record-net record)
-	       (error "There already are net addresses!"))
-	   (if (stringp contents)
-	       (setq contents (bbdb-split contents ",")))
-	   ;; first detect any conflicts....
-	   (let ((nets contents))
-	     (while nets
-	       (let ((old (bbdb-gethash (downcase (car nets)))))
-		 (if (and old (not (eq old record)))
-		     (error "net address \"%s\" is used by \"%s\""
-			    (car nets)
-			    (or (bbdb-record-name old) (car (bbdb-record-net old))))))
-	       (setq nets (cdr nets))))
-	   ;; then store.
-	   (let ((nets contents))
-	     (while nets
-	       (bbdb-puthash (downcase (car nets)) record)
-	       (setq nets (cdr nets))))
-	   (bbdb-record-set-net record contents)
-	   )
-	  ((eq name 'aka)
-	   (if (bbdb-record-aka record)
-	       (error "there already are alternate names!"))
-	   (if (stringp contents)
-	       (setq contents (bbdb-split contents ";")))
-	   ;; first detect any conflicts....
-	   (let ((aka contents))
-	     (while aka
-	       (let ((old (bbdb-gethash (downcase (car aka)))))
-		 (if (and old (not (eq old record)))
-		     (error "alternate name \"%s\" is used by \"%s\""
-			    (car aka)
-			    (or (bbdb-record-name old)
-				(car (bbdb-record-net old))))))
-	       (setq aka (cdr aka))))
-	   ;; then store.
-	   (let ((aka contents))
-	     (while aka
-	       (bbdb-puthash (downcase (car aka)) record)
-	       (setq aka (cdr aka))))
-	   (bbdb-record-set-aka record contents)
-	   )
-	  ((eq name 'notes)
-	   (if (bbdb-record-notes record) (error "there already are notes!"))
-	   (bbdb-record-set-notes record contents))
-	  ((assoc (symbol-name name) (bbdb-propnames))
-	   (if (and (consp (bbdb-record-raw-notes record))
-		    (assq name (bbdb-record-raw-notes record)))
-	       (error "there is already a \"%s\" note!" name))
-	   (bbdb-record-putprop record name contents))
-	  (t (error "doubleplus ungood: unknow how to set slot %s" name)))
+           (bbdb-record-set-phones record
+                                   (nconc (bbdb-record-phones record) (list contents))))
+          ((eq name 'address)
+           (bbdb-record-set-addresses record
+                                      (nconc (bbdb-record-addresses record) (list contents))))
+          ((eq name 'net)
+           (if (bbdb-record-net record)
+               (error "There already are net addresses!"))
+           (if (stringp contents)
+               (setq contents (bbdb-split contents ",")))
+           ;; first detect any conflicts....
+           (if bbdb-no-duplicates-p
+               (let ((nets contents))
+                 (while nets
+                   (let ((old (bbdb-gethash (downcase (car nets)))))
+                     (if (and old (not (eq old record)))
+                         (error "net address \"%s\" is used by \"%s\""
+                                (car nets)
+                                (or (bbdb-record-name old)
+                                    (car (bbdb-record-net old))))))
+                   (setq nets (cdr nets)))))
+           ;; then store.
+           (let ((nets contents))
+             (while nets
+               (bbdb-puthash (downcase (car nets)) record)
+               (setq nets (cdr nets))))
+           (bbdb-record-set-net record contents))
+          ((eq name 'aka)
+           (if (bbdb-record-aka record)
+               (error "there already are alternate names!"))
+           (if (stringp contents)
+               (setq contents (bbdb-split contents ";")))
+           ;; first detect any conflicts....
+           (if bbdb-no-duplicates-p
+               (let ((aka contents))
+                 (while aka
+                   (let ((old (bbdb-gethash (downcase (car aka)))))
+                     (if (and old (not (eq old record)))
+                         (error "alternate name \"%s\" is used by \"%s\""
+                                (car aka)
+                                (or (bbdb-record-name old)
+                                    (car (bbdb-record-net old))))))
+                   (setq aka (cdr aka)))))
+           ;; then store.
+           (let ((aka contents))
+             (while aka
+               (bbdb-puthash (downcase (car aka)) record)
+               (setq aka (cdr aka))))
+           (bbdb-record-set-aka record contents))
+          ((eq name 'notes)
+           (if (bbdb-record-notes record) (error "there already are notes!"))
+           (bbdb-record-set-notes record contents))
+          ((assoc (symbol-name name) (bbdb-propnames))
+           (if (and (consp (bbdb-record-raw-notes record))
+                    (assq name (bbdb-record-raw-notes record)))
+               (error "there is already a \"%s\" note!" name))
+           (bbdb-record-putprop record name contents))
+          (t (error "doubleplus ungood: unknow how to set slot %s" name)))
     (bbdb-change-record record nil)
-;    (bbdb-offer-save)
+                                ;    (bbdb-offer-save)
     (let ((bbdb-elided-display nil))
       (bbdb-redisplay-one-record record))))
 
 (defun bbdb-prompt-for-new-field-value (name)
   (cond ((eq name 'net) (bbdb-read-string "Net: "))
-	((eq name 'aka) (bbdb-read-string "Alternate Names: "))
-	((eq name 'phone)
-	 (let ((p (make-vector
-		    (if (if current-prefix-arg
-			    (numberp current-prefix-arg)
-			    bbdb-north-american-phone-numbers-p)
-			bbdb-phone-length
-			2)
-		    0)))
-	   (aset p 0 nil)
-	   (aset p 1
-		 (if (= bbdb-phone-length (length p))
-		     (if (integerp bbdb-default-area-code)
-			 bbdb-default-area-code
-		       0)
-		     nil))
-	   (bbdb-record-edit-phone p)
-	   p))
-	((eq name 'address)
-	 (let ((a (make-vector bbdb-address-length nil)))
-	   (bbdb-record-edit-address a)
-	   a))
-	((eq name 'notes) (bbdb-read-string "Notes: "))
-	((assoc (symbol-name name) (bbdb-propnames))
-	 (bbdb-read-string (format "%s: " name)))
-	(t
-	 (if (bbdb-y-or-n-p (format "\"%s\" is an unknown field name.  Define it? " name))
-	     (bbdb-set-propnames
-	       (append (bbdb-propnames) (list (list (symbol-name name)))))
-	     (error "unknown field \"%s\"" name))
-	 (bbdb-read-string (format "%s: " name)))))
+        ((eq name 'aka) (bbdb-read-string "Alternate Names: "))
+        ((eq name 'phone)
+         (let ((p (make-vector
+                   (if (if current-prefix-arg
+                           (numberp current-prefix-arg)
+                           bbdb-north-american-phone-numbers-p)
+                       bbdb-phone-length
+                       2)
+                   0)))
+           (aset p 0 nil)
+           (aset p 1
+                 (if (= bbdb-phone-length (length p))
+                     (if (integerp bbdb-default-area-code)
+                         bbdb-default-area-code
+                         0)
+                     nil))
+           (bbdb-record-edit-phone p)
+           p))
+        ((eq name 'address)
+         (let ((a (make-vector bbdb-address-length nil)))
+           (bbdb-record-edit-address a)
+           a))
+        ((eq name 'notes) (bbdb-read-string "Notes: "))
+        ((assoc (symbol-name name) (bbdb-propnames))
+         (bbdb-read-string (format "%s: " name)))
+        (t
+         (if (bbdb-y-or-n-p (format "\"%s\" is an unknown field name.  Define it? " name))
+             (bbdb-set-propnames
+              (append (bbdb-propnames) (list (list (symbol-name name)))))
+             (error "unknown field \"%s\"" name))
+         (bbdb-read-string (format "%s: " name)))))
 
+(defun bbdb-add-new-field (name)
+  "Programmatically add a new field called NAME. Returns the list of propnames."
+  ;; check that we don't have one already; if we do, return quietly.
+  (if (assoc (symbol-name name) (append '(("phone") ("address") ("net")
+                                          ("AKA") ("notes"))
+                                        (bbdb-propnames)))
+      bbdb-propnames
+      (bbdb-set-propnames (append (bbdb-propnames)
+                                  (list (list (symbol-name name)))))))
 
 ;;;###autoload
 (defun bbdb-edit-current-field ()
-  "Edit the contents of the Insidious Big Brother Database field displayed on 
-the current line (this is only meaningful in the \"*BBDB*\" buffer.)   If the 
-cursor is in the middle of a multi-line field, such as an address or comments 
+  "Edit the contents of the Insidious Big Brother Database field displayed on
+the current line (this is only meaningful in the \"*BBDB*\" buffer.)   If the
+cursor is in the middle of a multi-line field, such as an address or comments
 section, then the entire field is edited, not just the current line."
   (interactive)
   (let* ((record (bbdb-current-record t))
-	 (field (bbdb-current-field t))
-	 need-to-sort)
+         (field (bbdb-current-field t))
+         need-to-sort)
     (or field (error "on an unfield"))
     (setq need-to-sort
-	  (bbdb-record-edit-field-internal record (car field) (nth 1 field)))
+          (bbdb-record-edit-field-internal record (car field) (nth 1 field)))
     (bbdb-change-record record need-to-sort)
     (bbdb-redisplay-one-record record)
-;    (bbdb-offer-save)
+    ;; (bbdb-offer-save)
     ))
 
 (defun bbdb-record-edit-name (bbdb-record)
   (let (fn ln co need-to-sort new-name old-name)
     (bbdb-error-retry
-      (progn
-	(if current-prefix-arg
-	    (setq fn (bbdb-read-string "First Name: "
-				       (bbdb-record-firstname bbdb-record))
-		  ln (bbdb-read-string "Last Name: "
-				       (bbdb-record-lastname bbdb-record)))
-	  (let ((names (bbdb-divide-name
-			(bbdb-read-string "Name: "
-			  (bbdb-record-name bbdb-record)))))
-	    (setq fn (car names)
-		  ln (nth 1 names))))
-	(setq need-to-sort (or (not (string= fn
-					     (or (bbdb-record-firstname bbdb-record) "")))
-			       (not (string= ln
-					     (or (bbdb-record-lastname bbdb-record) "")))))
-	(if (string= "" fn) (setq fn nil))
-	(if (string= "" ln) (setq ln nil))
-	;; check for collisions
-	(setq new-name (if (and fn ln) (concat fn " " ln)
-			   (or fn ln))
-	      old-name (bbdb-record-name bbdb-record))
-	(if (and new-name
-		 (not (and old-name (string= (downcase new-name)
-					     (downcase old-name))))
-		 (bbdb-gethash (downcase new-name)))
-	    (error "%s is already in the database!" new-name))))
+     (progn
+       (if current-prefix-arg
+           (setq fn (bbdb-read-string "First Name: "
+                                      (bbdb-record-firstname bbdb-record))
+                 ln (bbdb-read-string "Last Name: "
+                                      (bbdb-record-lastname bbdb-record)))
+           (let ((names (bbdb-divide-name
+                         (bbdb-read-string "Name: "
+                                           (bbdb-record-name bbdb-record)))))
+             (setq fn (car names)
+                   ln (nth 1 names))))
+       (setq need-to-sort (or (not (string= fn
+                                            (or (bbdb-record-firstname bbdb-record) "")))
+                              (not (string= ln
+                                            (or (bbdb-record-lastname bbdb-record) "")))))
+       (if (string= "" fn) (setq fn nil))
+       (if (string= "" ln) (setq ln nil))
+       ;; check for collisions
+       (setq new-name (if (and fn ln) (concat fn " " ln)
+                          (or fn ln))
+             old-name (bbdb-record-name bbdb-record))
+       (if (and bbdb-no-duplicates-p
+                new-name
+                (not (and old-name (string= (downcase new-name)
+                                            (downcase old-name))))
+                (bbdb-gethash (downcase new-name)))
+           (error "%s is already in the database!" new-name))))
     (setq co (bbdb-read-string "Company: "
-			       (bbdb-record-company bbdb-record)))
+                               (bbdb-record-company bbdb-record)))
     (if (string= "" co) (setq co nil))
     (setq need-to-sort
-	  (or need-to-sort
-	      (not (equal (if co (downcase co) "")
-			  (downcase (or (bbdb-record-company bbdb-record)
-					""))))))
+          (or need-to-sort
+              (not (equal (if co (downcase co) "")
+                          (downcase (or (bbdb-record-company bbdb-record)
+                                        ""))))))
     ;;
     ;; delete the old hash entry
-    (and (bbdb-record-name bbdb-record)
-	 (bbdb-remhash (downcase (bbdb-record-name bbdb-record))))
+    (let ((name    (bbdb-record-name    bbdb-record))
+          (company (bbdb-record-company bbdb-record)))
+      (if (> (length name) 0)
+          (bbdb-remhash (downcase name) bbdb-record))
+      (if (> (length company) 0)
+          (bbdb-remhash (downcase company) bbdb-record)))
     (bbdb-record-set-namecache bbdb-record nil)
     (bbdb-record-set-firstname bbdb-record fn)
     (bbdb-record-set-lastname bbdb-record ln)
     (bbdb-record-set-company bbdb-record co)
     ;; add a new hash entry
     (and (or fn ln)
-	 (bbdb-puthash (downcase (bbdb-record-name bbdb-record))
-		       bbdb-record))
+         (bbdb-puthash (downcase (bbdb-record-name bbdb-record))
+                       bbdb-record))
     need-to-sort))
 
-(defun bbdb-record-edit-address (addr &optional location)
-  (let* ((loc (or location (bbdb-read-string "Location: " (bbdb-address-location addr))))
-	 (st1 (bbdb-read-string "Street, line 1: " (bbdb-address-street1 addr)))
-	 (st2 (if (string= st1 "") ""
-		  (bbdb-read-string "Street, line 2: " (bbdb-address-street2 addr))))
-	 (st3 (if (string= st2 "") ""
-		  (bbdb-read-string "Street, line 3: " (bbdb-address-street3 addr))))
-	 (cty (bbdb-read-string "City: " (bbdb-address-city addr)))
-	 (ste (bbdb-read-string "State: " (bbdb-address-state addr)))
-	 (zip (bbdb-error-retry
-		(bbdb-parse-zip-string
-		  (bbdb-read-string "Zip Code: " (bbdb-address-zip-string addr))))))
-    (bbdb-address-set-location addr loc)
-    (bbdb-address-set-street1 addr st1)
-    (bbdb-address-set-street2 addr st2)
-    (bbdb-address-set-street3 addr st3)
+(defun bbdb-address-edit-default (addr)
+  "Function to use for address editing.
+The sub-fields are queried using the default order and using the
+default names.  Set `bbdb-address-editing-function' to an alternate
+address editing function if you don't like this function.  It is
+mostly used for US style addresses.
+
+The sub-fields and the prompts used are:
+Street, line n:  (nth n street)
+City:            city
+State:           state
+Zip Code:        zip
+Country:         country"
+  (let* ((str (let ((l) (s) (n 0))
+        (while (not (string= "" (setq s (bbdb-read-string
+                         (format "Street, line %d: " (+ 1 n))
+                         (nth n (bbdb-address-streets addr))))))
+          (setq l (append l (list s)))
+          (setq n (1+ n)))
+        l))
+     (cty (bbdb-read-string "City: " (bbdb-address-city addr)))
+     (ste (bbdb-read-string "State: " (bbdb-address-state addr)))
+         (zip (bbdb-error-retry
+               (bbdb-parse-zip-string
+                (bbdb-read-string "Zip Code: " (bbdb-address-zip-string addr)))))
+     (country (bbdb-read-string "Country: " (bbdb-address-country addr))))
+    (bbdb-address-set-streets addr str)
     (bbdb-address-set-city addr cty)
     (bbdb-address-set-state addr ste)
     (bbdb-address-set-zip addr zip)
+    (bbdb-address-set-country addr country)
     nil))
 
+(defcustom bbdb-address-editing-function 'bbdb-address-edit-default
+  "Function to use for address editing.
+The function must accept a BBDB address as parameter and allow the
+user to edit it.  This variable is called from `bbdb-record-edit-address'.
+The default value is the symbol `bbdb-address-edit-default'."
+  :group 'bbdb-record-creation
+  :type 'function)
+
+(defun bbdb-record-edit-address (addr &optional location)
+  "Edit an address ADDR.
+If optional parameter LOCATION is non-nil, edit the location sub-field
+of the address as well.  The address itself is edited using the editing
+function in `bbdb-address-editing-function'."
+  (let ((loc (or location (bbdb-read-string "Location: " (bbdb-address-location addr)))))
+    (bbdb-address-set-location addr loc))
+  (funcall bbdb-address-editing-function addr))
+
 (defun bbdb-record-edit-phone (phone-number)
   (let ((newl (bbdb-read-string "Location: "
-				 (bbdb-phone-location phone-number)))
-	(newp (let ((bbdb-north-american-phone-numbers-p
-		     (= (length phone-number) bbdb-phone-length)))
-		(bbdb-error-retry
-		  (bbdb-parse-phone-number
-		    (read-string "Phone: " (bbdb-phone-string phone-number))
-		    )))))
+                                (bbdb-phone-location phone-number)))
+        (newp (let ((bbdb-north-american-phone-numbers-p
+                     (= (length phone-number) bbdb-phone-length)))
+                (bbdb-error-retry
+                 (bbdb-parse-phone-number
+                  (read-string "Phone: " (bbdb-phone-string phone-number)))))))
     (bbdb-phone-set-location phone-number newl)
     (bbdb-phone-set-area phone-number (nth 0 newp)) ; euronumbers too.
     (if (= (length phone-number) 2)
-	nil
-      (bbdb-phone-set-exchange phone-number (nth 1 newp))
-      (bbdb-phone-set-suffix phone-number (nth 2 newp))
-      (bbdb-phone-set-extension phone-number (or (nth 3 newp) 0))))
+        nil
+        (bbdb-phone-set-exchange phone-number (nth 1 newp))
+        (bbdb-phone-set-suffix phone-number (nth 2 newp))
+        (bbdb-phone-set-extension phone-number (or (nth 3 newp) 0))))
   nil)
 
 (defun bbdb-record-edit-net (bbdb-record)
   (let ((str (bbdb-read-string "Net: "
-	       (mapconcat (function identity)
-			  (bbdb-record-net bbdb-record)
-			  ", "))))
+                               (mapconcat (function identity)
+                                          (bbdb-record-net bbdb-record)
+                                          ", "))))
     (let ((oldnets (bbdb-record-net bbdb-record))
-	  (newnets (bbdb-split str ",")))
+          (newnets (bbdb-split str ",")))
       ;; first check for any conflicts...
-      (let ((rest newnets))
-	(while rest
-	  (let ((old (bbdb-gethash (downcase (car rest)))))
-	    (if (and old (not (eq old bbdb-record)))
-		(error "net address \"%s\" is used by \"%s\""
-		       (car rest) (bbdb-record-name old))))
-	  (setq rest (cdr rest))))
+      (if bbdb-no-duplicates-p
+          (let ((rest newnets))
+            (while rest
+              (let ((old (bbdb-gethash (downcase (car rest)))))
+                (if (and old (not (eq old bbdb-record)))
+                    (error "net address \"%s\" is used by \"%s\""
+                           (car rest) (bbdb-record-name old))))
+              (setq rest (cdr rest)))))
       ;; then update.
       (let ((rest oldnets))
-	(while rest
-	  (bbdb-remhash (downcase (car rest)))
-	  (setq rest (cdr rest))))
+        (while rest
+          (bbdb-remhash (downcase (car rest)) bbdb-record)
+          (setq rest (cdr rest))))
       (let ((nets newnets))
-	(while nets
-	  (bbdb-puthash (downcase (car nets)) bbdb-record)
-	  (setq nets (cdr nets))))
-      (bbdb-record-set-net bbdb-record newnets)
-      ))
+        (while nets
+          (bbdb-puthash (downcase (car nets)) bbdb-record)
+          (setq nets (cdr nets))))
+      (bbdb-record-set-net bbdb-record newnets)))
   nil)
 
 (defun bbdb-record-edit-aka (bbdb-record)
   (let ((str (bbdb-read-string "AKA: "
-	       (mapconcat (function identity)
-			  (bbdb-record-aka bbdb-record)
-			  "; "))))
+                               (mapconcat (function identity)
+                                          (bbdb-record-aka bbdb-record)
+                                          "; "))))
     (let ((oldaka (bbdb-record-aka bbdb-record))
-	  (newaka (bbdb-split str ";")))
+          (newaka (bbdb-split str ";")))
       ;; first check for any conflicts...
-      (let ((rest newaka))
-	(while rest
-	  (let ((old (bbdb-gethash (downcase (car rest)))))
-	    (if (and old (not (eq old bbdb-record)))
-		(error "alternate name address \"%s\" is used by \"%s\""
-		       (car rest) (bbdb-record-name old))))
-	  (setq rest (cdr rest))))
+      (if bbdb-no-duplicates-p
+          (let ((rest newaka))
+            (while rest
+              (let ((old (bbdb-gethash (downcase (car rest)))))
+                (if (and old (not (eq old bbdb-record)))
+                    (error "alternate name address \"%s\" is used by \"%s\""
+                           (car rest) (bbdb-record-name old))))
+              (setq rest (cdr rest)))))
       ;; then update.
       (let ((rest oldaka))
-	(while rest
-	  (bbdb-remhash (downcase (car rest)))
-	  (setq rest (cdr rest))))
+        (while rest
+          (bbdb-remhash (downcase (car rest)) bbdb-record)
+          (setq rest (cdr rest))))
       (let ((aka newaka))
-	(while aka
-	  (bbdb-puthash (downcase (car aka)) bbdb-record)
-	  (setq aka (cdr aka))))
-      (bbdb-record-set-aka bbdb-record newaka)
-      ))
+        (while aka
+          (bbdb-puthash (downcase (car aka)) bbdb-record)
+          (setq aka (cdr aka))))
+      (bbdb-record-set-aka bbdb-record newaka)))
   nil)
 
 ;;;###autoload
     (bbdb-record-set-notes bbdb-record (if (string= "" notes) nil notes)))
   (if regrind
       (save-excursion
-	(set-buffer bbdb-buffer-name)
-	(bbdb-redisplay-one-record bbdb-record)))
+        (set-buffer bbdb-buffer-name)
+        (bbdb-redisplay-one-record bbdb-record)))
   nil)
 
 ;;;###autoload
 (defun bbdb-record-edit-property (bbdb-record &optional prop regrind)
   (interactive (list (bbdb-current-record t) nil t))
   (let* ((propnames (bbdb-propnames))
-	 (propname (if prop (symbol-name prop)
-		     (completing-read
-		       (format "Edit property of %s: "
-			       (bbdb-record-name bbdb-record))
-		       (cons '("notes") propnames))))
-	 (propsym (or prop (if (equal "" propname) 'notes (intern propname))))
-	 (string (bbdb-read-string (format "%s: " propname)
-				   (bbdb-record-getprop bbdb-record propsym))))
+         (propname (if prop (symbol-name prop)
+                       (completing-read
+                        (format "Edit property of %s: "
+                                (bbdb-record-name bbdb-record))
+                        (cons '("notes") propnames))))
+         (propsym (or prop (if (equal "" propname) 'notes (intern propname))))
+         (string (bbdb-read-string (format "%s: " propname)
+                                   (bbdb-record-getprop bbdb-record propsym))))
     (bbdb-record-putprop bbdb-record propsym
-			 (if (string= "" string) nil string)))
+                         (if (string= "" string) nil string)))
   (if regrind
       (save-excursion
-	(set-buffer bbdb-buffer-name)
-	(bbdb-redisplay-one-record bbdb-record)))
+        (set-buffer bbdb-buffer-name)
+        (bbdb-redisplay-one-record bbdb-record)))
   nil)
 
 
 (defsubst bbdb-field-equal (x y)
   (if (and (consp x) (consp y))
       (and (eq (car x) (car y))
-	   (eq (car (cdr x)) (car (cdr y)))
-	   (eq (car (cdr (cdr x))) (car (cdr (cdr y)))))
-    (eq x y)))
+           (eq (car (cdr x)) (car (cdr y)))
+           (eq (car (cdr (cdr x))) (car (cdr (cdr y)))))
+      (eq x y)))
 
 (defun bbdb-next-field (&optional count planning-on-modifying)
   (or count (setq count 1))
   (beginning-of-line)
   (let* ((record (bbdb-current-record planning-on-modifying))
-	 (field (bbdb-current-field planning-on-modifying))
-	 (next-record record)
-	 (next-field field)
-	 (signum (if (< count 0) -1 1))
-	 (i 0))
+         (field (bbdb-current-field planning-on-modifying))
+         (next-record record)
+         (next-field field)
+         (signum (if (< count 0) -1 1))
+         (i 0))
     (if (< count 0) (setq count (- count)))
     (if field
-	(while (and next-field (< i count))
-	  (while (bbdb-field-equal next-field field)
-	    (forward-line signum)
-	    (setq next-record (bbdb-current-record planning-on-modifying)
-		  next-field (bbdb-current-field planning-on-modifying))
-	    (or (eq next-record record)
-		(setq next-field nil)))
-	  (setq i (1+ i))
-	  (setq field next-field)))
+        (while (and next-field (< i count))
+          (while (bbdb-field-equal next-field field)
+            (forward-line signum)
+            (setq next-record (bbdb-current-record planning-on-modifying)
+                  next-field (bbdb-current-field planning-on-modifying))
+            (or (eq next-record record)
+                (setq next-field nil)))
+          (setq i (1+ i))
+          (setq field next-field)))
     next-field))
 
 ;;;###autoload
 phone number; the order of field types is fixed.\)"
   (interactive "p")
   (let ((record (bbdb-current-record t))
-	moving-field position-after position-before
-	swap-p type list)
+        moving-field position-after position-before
+        swap-p type list)
     (if (/= arg 0)
-	(setq moving-field (or (bbdb-next-field -1 t)
-			       (error "no previous field"))
-	      position-after (bbdb-next-field arg t)
-	      position-before (bbdb-next-field (if (< arg 0) -1 1) t))
-      ;; if arg is 0, swap fields at point and mark
-      (setq swap-p t)
-      (setq position-after (bbdb-current-field))
-      (save-excursion
-	(goto-char (mark))
-	(setq moving-field (bbdb-current-field))
-	(or (eq record (bbdb-current-record)) (error "not in the same record"))
-	))
+        (setq moving-field (or (bbdb-next-field -1 t)
+                               (error "no previous field"))
+              position-after (bbdb-next-field arg t)
+              position-before (bbdb-next-field (if (< arg 0) -1 1) t))
+        ;; if arg is 0, swap fields at point and mark
+        (setq swap-p t)
+        (setq position-after (bbdb-current-field))
+        (save-excursion
+          (goto-char (mark))
+          (setq moving-field (bbdb-current-field))
+          (or (eq record (bbdb-current-record)) (error "not in the same record"))))
     (if (< arg 0)
-	(let ((x position-after))
-	  (setq position-after position-before
-		position-before x)
-	  (forward-line 2)))
+        (let ((x position-after))
+          (setq position-after position-before
+                position-before x)
+          (forward-line 2)))
     (setq type (car moving-field))
     (or position-after position-before
-	(error "that would be out of the record!"))
+        (error "that would be out of the record!"))
     (or (eq type (car position-after))
-	(eq type (car position-before))
-	(error "can't transpose fields of different types (%s and %s)"
-	       type (if (eq type (car position-after))
-			(car position-before) (car position-after))))
+        (eq type (car position-before))
+        (error "can't transpose fields of different types (%s and %s)"
+               type (if (eq type (car position-after))
+                        (car position-before) (car position-after))))
     (or (eq type (car position-after)) (setq position-after nil))
     (or (eq type (car position-before)) (setq position-before nil))
     (setq moving-field (nth 1 moving-field)
-	  position-after (nth 1 position-after)
-	  position-before (nth 1 position-before))
+          position-after (nth 1 position-after)
+          position-before (nth 1 position-before))
     (cond ((memq type '(name aka net))
-	   (error "there is only one %s field, so you can't transpose it"
-		  type))
-	  ((memq type '(phone address property))
-	   (setq list (bbdb-record-get-field-internal record type)))
-	  (t (error "doubleplus ungood: unknown field %s" type)))
+           (error "there is only one %s field, so you can't transpose it"
+                  type))
+          ((memq type '(phone address property))
+           (setq list (bbdb-record-get-field-internal record type)))
+          (t (error "doubleplus ungood: unknown field %s" type)))
     (if swap-p
-	(let ((rest list))
-	  (while rest
-	    (cond ((eq (car rest) moving-field) (setcar rest position-after))
-		  ((eq (car rest) position-after) (setcar rest moving-field)))
-	    (setq rest (cdr rest))))
-      (if (eq position-before (car list))
-	  (setq list (cons moving-field (delq moving-field list)))
-	(let ((rest list))
-	  (while (and rest (not (eq position-after (car rest))))
-	    (setq rest (cdr rest)))
-	  (or rest (error "doubleplus ungood: couldn't reorder list"))
-	  (let ((inhibit-quit t))
-	    (setq list (delq moving-field list))
-	    (setcdr rest (cons moving-field (cdr rest)))))))
+        (let ((rest list))
+          (while rest
+            (cond ((eq (car rest) moving-field) (setcar rest position-after))
+                  ((eq (car rest) position-after) (setcar rest moving-field)))
+            (setq rest (cdr rest))))
+        (if (eq position-before (car list))
+            (setq list (cons moving-field (delq moving-field list)))
+            (let ((rest list))
+              (while (and rest (not (eq position-after (car rest))))
+                (setq rest (cdr rest)))
+              (or rest (error "doubleplus ungood: couldn't reorder list"))
+              (let ((inhibit-quit t))
+                (setq list (delq moving-field list))
+                (setcdr rest (cons moving-field (cdr rest)))))))
     (bbdb-record-store-field-internal record type list)
     (bbdb-change-record record nil)
     (bbdb-redisplay-one-record record)))
 
 
 ;;;###autoload
-(defun bbdb-delete-current-field-or-record ()
+(defun bbdb-delete-current-field-or-record (&optional records noprompt)
   "Delete the line which the cursor is on; actually, delete the field which
 that line represents from the database.  If the cursor is on the first line
 of a database entry (the name/company line) then the entire entry will be
 deleted."
-  (interactive)
-  (let* ((record (bbdb-current-record t))
-	 (field (bbdb-current-field t))
-	 (type (car field))
-	 (uname (bbdb-record-name record))
-	 (name (cond ((null field) (error "on an unfield"))
-		     ((eq type 'property) (symbol-name (car (nth 1 field))))
-		     (t (symbol-name type)))))
+  (interactive (list (if (bbdb-do-all-records-p)
+             (mapcar 'car bbdb-records)
+               (list (bbdb-current-record)))
+             current-prefix-arg))
+  (let* ((do-all-p (> 1 (length records)))
+         (field (bbdb-current-field t))
+         (type (car field))
+     record
+         (name (cond ((null field) (error "on an unfield"))
+                     ((eq type 'property) (symbol-name (car (nth 1 field))))
+                     (t (symbol-name type)))))
+  (while records
+    (setq record (car records))
     (if (eq type 'name)
-	(bbdb-delete-current-record record)
-	(if (not (bbdb-y-or-n-p (format "delete this %s field (of %s)? "
-					name uname)))
-	    nil
-	  (cond ((memq type '(phone address))
-		 (bbdb-record-store-field-internal record type
-		 (delq (nth 1 field)
-		       (bbdb-record-get-field-internal record type))))
-		((memq type '(net aka))
-		 (let ((rest (bbdb-record-get-field-internal record type)))
-		   (while rest
-		     (bbdb-remhash (downcase (car rest)))
-		     (setq rest (cdr rest))))
-		 (bbdb-record-store-field-internal record type nil))
-		((eq type 'property)
-		 (bbdb-record-putprop record (car (nth 1 field)) nil))
-		(t (error "doubleplus ungood: unknown field type")))
-	  (bbdb-change-record record nil)
-	  (bbdb-redisplay-one-record record)))))
+    (bbdb-delete-current-record record noprompt)
+      (if (not (or noprompt
+           (bbdb-y-or-n-p (format "delete this %s field (of %s)? "
+                      name
+                      (bbdb-record-name record)))))
+            nil
+            (cond ((memq type '(phone address))
+           (bbdb-record-store-field-internal
+        record type
+        (delq (nth 1 field)
+              (bbdb-record-get-field-internal record type))))
+                  ((memq type '(net aka))
+                   (let ((rest (bbdb-record-get-field-internal record type)))
+                     (while rest
+                       (bbdb-remhash (downcase (car rest)) record)
+                       (setq rest (cdr rest))))
+                   (bbdb-record-store-field-internal record type nil))
+                  ((eq type 'property)
+                   (bbdb-record-putprop record (car (nth 1 field)) nil))
+                  (t (error "doubleplus ungood: unknown field type")))
+            (bbdb-change-record record nil)
+    (bbdb-redisplay-one-record record)))
+    (setq records (cdr records)))))
 
 ;;;###autoload
 (defun bbdb-delete-current-record (r &optional noprompt)
   "Delete the entire bbdb database entry which the cursor is within."
   (interactive (list (bbdb-current-record t)))
   (if (or noprompt
-	  (bbdb-y-or-n-p (format "delete the entire db entry of %s? "
-				 (or (bbdb-record-name r)
-				     (bbdb-record-company r)
-				     (car (bbdb-record-net r))))))
+          (bbdb-y-or-n-p (format "delete the entire db entry of %s? "
+                                 (or (bbdb-record-name r)
+                                     (bbdb-record-company r)
+                                     (car (bbdb-record-net r))))))
       (let* ((record-cons (assq r bbdb-records))
-	     (next-record-cons (car (cdr (memq record-cons bbdb-records)))))
-	(bbdb-debug (if (bbdb-record-deleted-p r)
-			(error "deleting deleted record")))
-	(bbdb-record-set-deleted-p r t)
-	(bbdb-delete-record-internal r)
-	(if (eq record-cons (car bbdb-records))
-	    (setq bbdb-records (cdr bbdb-records))
-	    (let ((rest bbdb-records))
-	      (while (cdr rest)
-		(if (eq record-cons (car (cdr rest)))
-		    (progn
-		      (setcdr rest (cdr (cdr rest)))
-		      (setq rest nil)))
-		(setq rest (cdr rest)))))
-	(bbdb-redisplay-one-record r record-cons next-record-cons t)
-	(bbdb-with-db-buffer
-	  (setq bbdb-changed-records (delq r bbdb-changed-records)))
-	;;(bbdb-offer-save)
-	)))
+             (next-record-cons (car (cdr (memq record-cons bbdb-records)))))
+        (bbdb-debug (if (bbdb-record-deleted-p r)
+                        (error "deleting deleted record")))
+        (bbdb-record-set-deleted-p r t)
+        (bbdb-delete-record-internal r)
+        (if (eq record-cons (car bbdb-records))
+            (setq bbdb-records (cdr bbdb-records))
+            (let ((rest bbdb-records))
+              (while (cdr rest)
+                (if (eq record-cons (car (cdr rest)))
+                    (progn
+                      (setcdr rest (cdr (cdr rest)))
+                      (setq rest nil)))
+                (setq rest (cdr rest)))))
+        (bbdb-redisplay-one-record r record-cons next-record-cons t)
+        (bbdb-with-db-buffer
+         (setq bbdb-changed-records (delq r bbdb-changed-records)))
+        ;; (bbdb-offer-save)
+        )))
 
 ;;;###autoload
 (defun bbdb-elide-record (arg)
   "Toggle whether the current record is displayed expanded or elided
-\(multi-line or one-line display.\)  With a numeric argument of 0, the 
+\(multi-line or one-line display.\)  With a numeric argument of 0, the
 current record will unconditionally be made elided; with any other argument,
 the current record will unconditionally be shown expanded.
 \\<bbdb-mode-map>
 If \"\\[bbdb-apply-next-command-to-all-records]\\[bbdb-elide-record]\" is \
 used instead of simply \"\\[bbdb-elide-record]\", then the state of all \
 records will
-be changed instead of just the one at point.  In this case, an argument 
+be changed instead of just the one at point.  In this case, an argument
 of 0 means that all records will unconditionally be made elided; any other
 numeric argument means that all of the records will unconditionally be shown
 expanded; and no numeric argument means that the records are made to be in
   (interactive "P")
   (if (bbdb-do-all-records-p)
       (bbdb-elide-all-records-internal arg)
-    (bbdb-elide-record-internal arg)))
+      (bbdb-elide-record-internal arg)))
 
 
 (defun bbdb-elide-record-internal (arg)
   (let* ((record (bbdb-current-record))
-	 (cons (assq record bbdb-records))
-	 (current-state (nth 1 cons))
-	 (desired-state
-	  (cond ((null arg) (not current-state))
-		((eq arg 0) nil)
-		(t t))))
-    (if (eq current-state desired-state)
-	nil
+         (cons (assq record bbdb-records))
+         (current-state (nth 1 cons))
+         (desired-state
+          (cond ((null arg) (not current-state))
+                ((eq arg 0) nil)
+                (t t))))
+    (unless (eq current-state desired-state)
       (setcar (cdr cons) desired-state)
       (bbdb-redisplay-one-record record))))
 
 (defun bbdb-elide-all-records-internal (arg)
   (let* ((record (bbdb-current-record))
-	 (cons (assq record bbdb-records))
-	 (current-state (nth 1 cons))
-	 (desired-state
-	  (cond ((null arg) (not current-state))
-		((eq arg 0) nil)
-		(t t)))
-	 (records bbdb-records)
-	 (any-change-p nil))
+         (cons (assq record bbdb-records))
+         (current-state (nth 1 cons))
+         (desired-state
+          (cond ((null arg) (not current-state))
+                ((eq arg 0) nil)
+                (t t)))
+         (records bbdb-records)
+         (any-change-p nil))
     (while records
-      (if (eq desired-state (nth 1 (car records)))
-	  nil
-	(setq any-change-p t)
-	(setcar (cdr (car records)) desired-state))
+      (unless (eq desired-state (nth 1 (car records)))
+        (setq any-change-p t)
+        (setcar (cdr (car records)) desired-state))
       (setq records (cdr records)))
-    (if (not any-change-p)
-	nil
+    (unless (not any-change-p)
       (bbdb-redisplay-records)
       (set-buffer bbdb-buffer-name)
       (goto-char (nth 2 (assq record bbdb-records)))
 ;;;###autoload
 (defun bbdb-omit-record (n)
   "Remove the current record from the display without deleting it from the
-database.  With a prefix argument, omit the next N records.  If negative, 
+database.  With a prefix argument, omit the next N records.  If negative,
 omit backwards."
   (interactive "p")
   (while (not (= n 0))
     (if (< n 0) (bbdb-prev-record 1))
     (let* ((record (or (bbdb-current-record) (error "no records")))
-	   (rest bbdb-records)
-	   cons next prev-tail)
+           (rest bbdb-records)
+           cons next prev-tail)
       (while rest
-	(if (eq (car (car rest)) record)
-	    (setq cons (car rest)
-		  next (car (cdr rest))
-		  rest nil)
-	  (setq prev-tail rest
-		rest (cdr rest))))
+        (if (eq (car (car rest)) record)
+            (setq cons (car rest)
+                  next (car (cdr rest))
+                  rest nil)
+            (setq prev-tail rest
+                  rest (cdr rest))))
       (or record (error "can't find current record"))
       (let ((buffer-read-only nil))
-	(delete-region (nth 2 cons) (if next (nth 2 next) (point-max))))
+        (delete-region (nth 2 cons) (if next (nth 2 next) (point-max))))
       (if prev-tail
-	  (setcdr prev-tail (cdr (cdr prev-tail)))
-	(setq bbdb-records (cdr bbdb-records)))
+          (setcdr prev-tail (cdr (cdr prev-tail)))
+          (setq bbdb-records (cdr bbdb-records)))
       (setq n (if (> n 0) (1- n) (1+ n)))))
   (bbdb-frob-mode-line (length bbdb-records)))
 
 ;;; Fixing up bogus entries
 
-(defcustom bbdb-refile-notes-generate-alist '((creation-date . bbdb-refile-notes-string-least))
+(defcustom bbdb-refile-notes-generate-alist '((creation-date . bbdb-refile-notes-string-least) (timestamp . bbdb-refile-notes-string-most))
   "*An alist defining specific merging function, based on notes field."
   :group 'bbdb-noticing-records
   :type '(repeat (cons
-		  (symbol :tag "Notes filed")
-		  (hook :tag "Generating function"))))
+                  (symbol :tag "Notes filed")
+                  (hook :tag "Generating function"))))
 
 (defcustom bbdb-refile-notes-default-merge-function 'bbdb-refile-notes-default-merge-function
   "*Default function to use for merging BBDB notes records.
 (defun bbdb-refile-notes-remove-duplicates (string1 string2)
   "Concatenate STRING1 and STRING2, but remove duplicate lines."
   (let ((note1 (split-string string1 "\n"))
-	(note2 (split-string string2 "\n")))
+        (note2 (split-string string2 "\n")))
     (while note2
       (if (not (member (car note2) note1))
-	  (setq note1 (cons (car note2) note1)))
+          (setq note1 (cons (car note2) note1)))
       (setq note2 (cdr note2)))
     (mapconcat 'identity note1 "\n")))
 
   "Returns the string that is lessp."
   (if (string-lessp string1 string2)
       string1
-    string2))
+      string2))
+
+(defun bbdb-refile-notes-string-most (string1 string2)
+  "Returns the string that is not lessp."
+  (if (string-lessp string1 string2)
+      string2
+      string1))
+
+(defun bbdb-merge-lists! (l1 l2 cmp &optional mod)
+  "Merge two lists l1 l2 (modifies l1) only adds elements from l2
+if cmp returns false for a