summaryrefslogtreecommitdiff
path: root/lisp/bbdb.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/bbdb.el')
-rw-r--r--lisp/bbdb.el3873
1 files changed, 0 insertions, 3873 deletions
diff --git a/lisp/bbdb.el b/lisp/bbdb.el
deleted file mode 100644
index 4b91509..0000000
--- a/lisp/bbdb.el
+++ /dev/null
@@ -1,3873 +0,0 @@
-;;; -*- Mode:Emacs-Lisp -*-
-;;; This file is the core of the Insidious Big Brother Database (aka BBDB),
-;;; copyright (c) 1991, 1992, 1993, 1994 Jamie Zawinski <jwz@netscape.com>.
-;;; See the file bbdb.texinfo for documentation.
-;;;
-;;; The Insidious Big Brother Database is free software; you can redistribute
-;;; it and/or modify it under the terms of the GNU General Public License as
-;;; published by the Free Software Foundation; either version 2, or (at your
-;;; option) any later version.
-;;;
-;;; BBDB is distributed in the hope that it will be useful, but WITHOUT ANY
-;;; WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-;;; FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-;;; details.
-;;;
-;;; You should have received a copy of the GNU General Public License
-;;; along with GNU Emacs; see the file COPYING. If not, write to
-;;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-;;;
-;;; ------------------------------------------------------------------------
-;;; | There is a mailing list for discussion of BBDB: |
-;;; | bbdb-info@lists.sourceforge.net |
-;;; | To join, send mail to bbdb-info-request@lists.sourceforge.net |
-;;; | (don't forget the "-request" part or you'll look silly in front of |
-;;; | lots of people who have the ability to remember it indefinitely...) |
-;;; | |
-;;; | There is also a second mailing list, to which only bug fixes and |
-;;; | new version announcements are sent; to be added to it, send mail to |
-;;; | bbdb-announce-request@lists.sourceforge.net. This is a very low |
-;;; | volume list, and if you're using BBDB, you really should be on it. |
-;;; | |
-;;; | When joining these lists or reporting bugs, please mention which |
-;;; | version you have. The preferred method of reporting bugs is to use |
-;;; | bbdb-submit-bug-report, which will include all useful version |
-;;; | information plus state information about how you have BBDB set up. |
-;;; ------------------------------------------------------------------------
-
-(require 'timezone)
-(eval-when-compile (require 'cl))
-
-(eval-when-compile ; pacify the compiler.
- (autoload 'widget-group-match "wid-edit")
- (autoload 'Electric-pop-up-window "electric")
- (autoload 'Electric-command-loop "electric")
- (autoload 'bbdb-migration-query "bbdb-migrate")
- (autoload 'bbdb-migrate "bbdb-migrate")
- (autoload 'bbdb-migrate-rewrite-all "bbdb-migrate")
- (autoload 'bbdb-migrate-update-file-version "bbdb-migrate")
- (autoload 'bbdb-unmigrate-record "bbdb-migrate")
- (autoload 'bbdb-create-internal "bbdb-com")
- (autoload 'bbdb-append-records-p "bbdb-com")
- (autoload 'bbdb-redisplay-records "bbdb-com")
- (autoload 'y-or-n-p-with-timeout "timer")
- (autoload 'mail-position-on-field "sendmail")
- (autoload 'bbdb-fontify-buffer "bbdb-gui")
- (autoload 'vm-select-folder-buffer "vm-folder")
-
- ;; can't use autoload for variables...
- (defvar bbdb-define-all-aliases-needs-rebuilt) ;; bbdb-com
- (defvar message-mode-map) ;; message.el
- (defvar mail-mode-map) ;; sendmail.el
- (defvar gnus-article-buffer) ;; gnus-art.el
- (defvar temp-buffer-setup-hook nil)
- (defvar buffer-file-coding-system nil)
- (defvar coding-system-for-write nil)
- )
-
-(defconst bbdb-version "2.36")
-
-
-(defmacro bbdb-eval-when (c &rest body)
- "Emit BODY only if C is true."
- (if (eval c)
- (backquote (progn (\,@ body)))))
-
-(put 'bbdb-eval-when 'lisp-indent-hook 'defun)
-
-(defcustom bbdb-gui (if (fboundp 'display-color-p) ; Emacs 21
- (display-color-p)
- (not (null window-system))) ; wrong for XEmacs?
- "*Non-nil means fontify the *BBDB* buffer."
- :group 'bbdb
- :type 'boolean)
-
-;; File format
-(defconst bbdb-file-format 6)
-(defvar bbdb-file-format-migration nil
- "A cons of two elements: the version read, and the version to write.
-nil if the database was read in and is to be written in the current
-version.")
-
-(defvar bbdb-no-duplicates-p nil
- "Should BBDB allow entries with duplicate names.
-This may lead to confusion when doing completion. If non-nil, it will
-prompt the users on how to merge records when duplicates are detected.")
-
-;; Definitions for things that aren't in all Emacsen and that I really
-;; would prefer not to live without.
-(eval-and-compile
- (if (fboundp 'unless) nil
- (defmacro unless (bool &rest forms) `(if ,bool nil ,@forms))
- (defmacro when (bool &rest forms) `(if ,bool (progn ,@forms))))
- (unless (fboundp 'save-current-buffer)
- (defalias 'save-current-buffer 'save-excursion))
- (if (fboundp 'mapc)
- (defalias 'bbdb-mapc 'mapc)
- (defalias 'bbdb-mapc 'mapcar))
- )
-
-(unless (fboundp 'with-current-buffer)
- (defmacro with-current-buffer (buf &rest body)
- `(save-current-buffer (set-buffer ,buf) ,@body)))
-
-(unless (fboundp 'defvaralias)
- (defun defvaralias (&rest args)))
-
-(defmacro string> (a b) (list 'not (list 'or (list 'string= a b)
- (list 'string< a b))))
-
-(eval-and-compile
- (or (fboundp 'set-keymap-prompt)
- (fset 'set-keymap-prompt 'ignore)))
-
-(eval-and-compile
- (if (fboundp 'replace-in-string)
- (defalias 'bbdb-replace-in-string 'replace-in-string)
- (if (fboundp 'replace-regexp-in-string) ; defined in e21
- (defalias 'bbdb-replace-regexp-in-string 'replace-regexp-in-string)
- ;; actually this is `dired-replace-in-string' slightly modified
- ;; We're not defining the whole thing, just enough for our purposes.
- (defun bbdb-replace-regexp-in-string (regexp newtext string &optional
- fixedcase literal)
- ;; Replace REGEXP with NEWTEXT everywhere in STRING and return result.
- ;; NEWTEXT is taken literally---no \\DIGIT escapes will be recognized.
- (let ((result "") (start 0) mb me)
- (while (string-match regexp string start)
- (setq mb (match-beginning 0)
- me (match-end 0)
- result (concat result (substring string start mb) newtext)
- start me))
- (concat result (substring string start)))))
- (defun bbdb-replace-in-string (string regexp newtext &optional literal)
- (bbdb-replace-regexp-in-string regexp newtext string nil literal))))
-
-(defun bbdb-prin1-to-string (object &optional noescape)
- (let ((print-length nil)
- (print-level nil))
- (prin1-to-string object noescape)))
-
-(defun bbdb-prin1 (object &optional stream)
- (let ((print-length nil)
- (print-level nil))
- (prin1 object stream)))
-
-;; this should really be in bbdb-com
-;;;###autoload
-(defun bbdb-submit-bug-report ()
- "Submit a bug report, with pertinent information to the BBDB info list."
- (interactive)
- (require 'reporter)
- (delete-other-windows)
- (reporter-submit-bug-report
- "bbdb-info@lists.sourceforge.net"
- (concat "BBDB " bbdb-version)
- (append
- ;; non user variables
- '(emacs-version
- bbdb-file-format
- bbdb-no-duplicates-p)
- ;; user variables
- (sort (apropos-internal "^bbdb"
- 'user-variable-p)
- (lambda (v1 v2) (string-lessp (format "%s" v1) (format "%s" v2))))
- ;; see what the user had loaded
- (list 'features)
- )
- nil
- nil
- "Please change the Subject header to a concise bug description.\nIn this report, remember to cover the basics, that is, what you expected to\nhappen and what in fact did happen. Please remove these\ninstructions from your message.")
-
- ;; insert the backtrace buffer content if present
- (let ((backtrace (get-buffer-create "*Backtrace*")))
- (when backtrace
- (goto-char (point-max))
- (insert "\n\n")
- (insert-buffer-substring backtrace)))
-
- (goto-char (point-min))
- (mail-position-on-field "Subject"))
-
-;; Make custom stuff work even without customize
-;; Courtesy of Hrvoje Niksic <hniksic@srce.hr>
-(eval-and-compile
- (condition-case ()
- (require 'custom)
- (error nil))
- (unless (and (featurep 'custom) (fboundp 'custom-declare-variable))
- ;; We have the old custom-library, hack around it!
- (defmacro defgroup (&rest args)
- nil)
- (defmacro defcustom (var value doc &rest args)
- `(defvar ,var ,value ,doc))
- (defmacro defface (var value doc &rest args)
- `(make-face ,var))
- (defmacro define-widget (&rest args)
- nil)))
-
-(defconst bbdb-have-re-char-classes (string-match "[[:alpha:]]" "x")
- "Non-nil if this Emacs supports regexp character classes.
-E.g. `[[:alnum:]]'.")
-
-;; Custom groups
-
-(defgroup bbdb nil
- "The Insidious Big Brother Database."
- :group 'news
- :group 'mail)
-
-(put 'bbdb 'custom-loads '("bbdb-hooks" "bbdb-com"))
-
-(defgroup bbdb-hooks nil
- "Hooks run at various times by the BBDB"
- :group 'bbdb)
-
-(defgroup bbdb-record-display nil
- "Variables that affect the display of BBDB records"
- :group 'bbdb)
-
-(defgroup bbdb-record-creation nil
- "Variables that affect the creation of BBDB records"
- :group 'bbdb)
-
-(defgroup bbdb-noticing-records nil
- "Variables that affect the noticing of new authors"
- :group 'bbdb-record-creation)
-(put 'bbdb-noticing-records 'custom-loads '("bbdb-hooks"))
-
-(defgroup bbdb-record-use nil
- "Variables that affect the use of BBDB records"
- :group 'bbdb)
-
-(defgroup bbdb-database nil
- "Variables that affect the database as a whole"
- :group 'bbdb)
-
-(defgroup bbdb-saving nil
- "Variables that affect saving of the BBDB"
- :group 'bbdb-database)
-
-(defgroup bbdb-mua-specific nil
- "MUA-specific customizations"
- :group 'bbdb)
-
-(defgroup bbdb-mua-specific-gnus nil
- "Gnus-specific BBDB customizations"
- :group 'bbdb-mua-specific)
-
-(put 'bbdb-mua-specific-gnus 'custom-loads '("bbdb-gnus"))
-
-(defgroup bbdb-mua-specific-gnus-scoring nil
- "Gnus-specific scoring BBDB customizations"
- :group 'bbdb-mua-specific-gnus)
-
-(put 'bbdb-mua-specific-gnus-scoring 'custom-loads '("bbdb-gnus"))
-
-(defgroup bbdb-mua-specific-gnus-splitting nil
- "Gnus-specific splitting BBDB customizations"
- :group 'bbdb-mua-specific-gnus)
-
-(put 'bbdb-mua-specific-gnus-splitting 'custom-loads '("bbdb-gnus"))
-
-(defgroup bbdb-mua-specific-vm nil
- "VM-specific BBDB customizations"
- :group 'bbdb-mua-specific)
-
-(put 'bbdb-mua-specific-vm 'custom-loads '("bbdb-vm"))
-
-(defgroup bbdb-phone-dialing nil
- "Customizations for phone number dialing"
- :group 'bbdb)
-(put 'bbdb-phone-dialing 'custom-loads '("bbdb-com"))
-
-(defgroup bbdb-utilities nil
- "Customize BBDB Utilities"
- :group 'bbdb)
-
-(defgroup bbdb-utilities-finger nil
- "Customizations for fingering from within the BBDB"
- :group 'bbdb-utilities
- :prefix "bbdb-finger")
-(put 'bbdb-utilities-finger 'custom-loads '("bbdb-com"))
-
-(defgroup bbdb-utilities-ftp nil
- "Customizations for using FTP sites stored in BBDB records."
- :group 'bbdb-utilities)
-(put 'bbdb-utilities-ftp 'custom-loads '("bbdb-ftp"))
-
-(defgroup bbdb-utilities-print nil
- "Customizations for printing the BBDB."
- :group 'bbdb-utilities
- :prefix "bbdb-print")
-(put 'bbdb-utilities-print 'custom-loads '("bbdb-print"))
-
-(defgroup bbdb-utilities-supercite nil
- "Customizations for using Supercite with the BBDB."
- :group 'bbdb-utilities
- :prefix "bbdb/sc")
-(if (or (featurep 'supercite)
- (locate-library "supercite"))
- (put 'bbdb-utilities-supercite 'custom-loads '("bbdb-sc")))
-
-(defgroup bbdb-utilities-server nil
- "Customizations for interfacing with the BBDB from external programs."
- :group 'bbdb-utilities
- :prefix "bbdb/srv")
-(if (and (or (featurep 'gnuserv) (locate-library "gnuserv"))
- (or (featurep 'itimer) (locate-library "itimer")))
- (put 'bbdb-utilities-server 'custom-loads '("bbdb-srv")))
-
-;; BBDB custom widgets
-
-(define-widget 'bbdb-alist-with-header 'group
- "My group"
- :match 'bbdb-alist-with-header-match
- :value-to-internal (lambda (widget value)
- (if value (list (car value) (cdr value))))
- :value-to-external (lambda (widget value)
- (if value (append (list (car value)) (cadr value)))))
-
-(defun bbdb-alist-with-header-match (widget value)
- (widget-group-match widget
- (widget-apply widget :value-to-internal value)))
-
-;; Customizable variables
-
-(defcustom bbdb-file "~/.bbdb"
- "*The name of the Insidious Big Brother Database file."
- :group 'bbdb-database
- :type 'file)
-
-;; this should be removed, and the following put in place:
-;; a hierarchical structure of bbdb files, some perhaps read-only,
-;; perhaps caching in the local bbdb. This way you could have, e.g. a
-;; company address book, with each person having access to it, and
-;; then a local address book with personal stuff in it.
-(defcustom bbdb-file-remote nil
- "*The remote file to save the database to.
-When this is non-nil, it should be a file name.
-When BBDB reads `bbdb-file', it checks this file,
- and if it is newer, downloads it.
-When BBDB writes `bbdb-file', it also writes this file.
-
-This feature allows one to keep the database in one place while using
-different computers, thus reducing the need for merging different files."
- :group 'bbdb-database
- :type '(choice (const :tag "none" nil)
- (file :tag "remote file name")))
-
-(defcustom bbdb-file-remote-save-always t
- "*Should the `bbdb-file-remote' file be saved whenever the database is saved?
-When nil, you will be asked."
- :group 'bbdb-database
- :type 'boolean)
-
-(defun bbdb-primep (num)
- "Return t if NUM is a prime number."
- (if (fboundp 'primep)
- (primep num)
- (and (numberp num) (> num 1) (= num (floor num))
- (let ((lim (sqrt num)) (nu 2) (prime t))
- (while (and prime (<= nu lim))
- (setq prime (/= 0 (mod num nu))
- nu (1+ nu)))
- prime))))
-
-(defcustom bbdb-hashtable-size 1021
- "*The size of the bbdb hashtable.
-BBDB hashtable is an obarray, so this must be a prime integer.
-Set this to a prime number (much) larger than the size of your database
-before loading it.
-If you change this variable outside `customize',
-you should reload `bbdb-file'."
- :group 'bbdb-database
- :type 'integer
- :set (lambda (symb val)
- (unless (bbdb-primep val)
- (error "`%s' must be prime, not %s" symb val))
- (set symb val)
- (when (fboundp 'bbdb-records)
- (bbdb-records))
- val))
-
-(defcustom bbdb-default-area-code nil
- "*The default area code to use when prompting for a new phone number.
-This variable also affects dialing."
- :group 'bbdb-record-creation
- :type '(choice (const :tag "none" nil)
- (integer :tag "Default Area Code"))
- :set (lambda( symb val )
- (if (or (and (stringp val)
- (string-match "^[0-9]+$" val))
- (integerp val)
- (null val))
- (set symb val)
- (error "%s must contain digits only." symb))))
-
-(defcustom bbdb-lastname-prefixes
- '("von" "Von" "de" "De")
- "*List of lastname prefixes recognized in name fields. Used to
-enhance dividing name strings into firstname and lastname parts."
- :group 'bbdb-record-creation
- :type '(repeat string))
-
-(defcustom bbdb-default-domain nil
- "*The default domain to append when prompting for a new net address.
-If the address entered does not contain `[@%!]', `@bbdb-default-domain'
-will be appended to it.
-
-The address will not be altered if bbdb-default-domain remains at its
-default value of nil, or if one provides a prefix argument to the
-bbdb-insert-new-field command."
- :group 'bbdb-record-creation
- :type '(choice (const :tag "none" nil)
- (string :tag "Domain" :value nil)))
-
-(defcustom bbdb-north-american-phone-numbers-p t
- "*Set this to nil if you want to enter phone numbers that aren't the same
-syntax as those in North America (that is, [[1] nnn] nnn nnnn ['x' n*]).
-If this is true, then some error checking is done so that you can't enter
-incorrect phone numbers, and all phone numbers are pretty-printed the same
-way. European phone numbers don't have as strict a syntax, however, so
-this is a harder problem for them (on which I am punting).
-
-You can have both styles of phone number in your database by providing a
-prefix argument to the bbdb-insert-new-field command."
- :group 'bbdb-record-creation
- :type 'boolean)
-
-(defcustom bbdb-electric-p nil
- "*Whether bbdb mode should be `electric' like `electric-buffer-list'."
- :group 'bbdb-record-display
- :type 'boolean)
-
-(defcustom bbdb-case-fold-search (default-value 'case-fold-search)
- "*This is the value of `case-fold-search' used by `bbdb' and friends.
-This variable lets the case-sensitivity of ^S and of the bbdb
-commands be different."
- :group 'bbdb
- :type 'boolean)
-
-(defcustom bbdb/mail-auto-create-p t
- "*If this is t, then MH, RMAIL, and VM will automatically
-create new bbdb records for people you receive mail from. If this
-is a function name or lambda, then it is called with no arguments
-to decide whether an entry should be automatically created. You
-can use this to, for example, not create records for messages
-which have reached you through a particular mailing list, or to
-only create records automatically if the mail has a particular
-subject."
- :group 'bbdb-noticing-records
- :type '(choice (const :tag "Automatically create" t)
- (const :tag "Prompt before creating" prompt)
- (const :tag "Do not automatically create" nil)
- (function :tag "Create with function" bbdb-)))
-
-(defcustom bbdb/news-auto-create-p nil
- "*If this is t, then Gnus will automatically create new bbdb
-records for people you receive mail from. If this is a function name
-or lambda, then it is called with no arguments to decide whether an
-entry should be automatically created. You can use this to, for
-example, create or not create messages which have a particular
-subject. If you want to autocreate messages based on the current
-newsgroup, it's probably a better idea to set this variable to t or
-nil from your `gnus-select-group-hook' instead."
- :group 'bbdb-noticing-records
- :type '(choice (const :tag "Automatically create" t)
- (const :tag "Prompt before creating" prompt)
- (const :tag "Do not automatically create" nil)
- (function :tag "Create with function" bbdb-)))
-
-(defcustom bbdb-quiet-about-name-mismatches nil
- "*If this is true, then BBDB will not prompt you when it notices a
-name change, that is, when the \"real name\" in a message doesn't correspond
-to a record already in the database with the same network address. As in,
-\"John Smith <jqs@frob.com>\" versus \"John Q. Smith <jqs@frob.com>\".
-Normally you will be asked if you want to change it.
-If set to a number it is the number of seconds to sit for while
-displaying the mismatch message.
-
-If set to a function it will be called with two arguments, the record and the
-new name and should return nil, t or a number.
-
-If none of the others it must be a sexp evaluating to nil, t or a number.
-
-Any other return value of the function or sexp will be considered as true."
- :group 'bbdb-noticing-records
- :type '(choice (const :tag "Prompt for name changes" nil)
- (const :tag "Do not prompt for name changes" t)
- (integer :tag
- "Instead of prompting, warn for this many seconds")
- (function :tag "User defined function")
- (sexp :tag "User defined sexp")
- (const :tag "Ignore records which has a 'readonly' field"
- (assq 'readonly (bbdb-record-raw-notes record)))))
-
-(defcustom bbdb-use-alternate-names t
- "*If this is true, then when bbdb notices a name change, it will ask you
-if you want both names to map to the same record."
- :group 'bbdb-noticing-records
- :type '(choice (const :tag "Ask to use alternate names field" t)
- (const :tag "Use alternate names field without asking" nil)))
-
-(defcustom bbdb-readonly-p nil
- "*If this is true, then nothing will attempt to change the bbdb database
-implicitly, and you will be prevented from doing it explicitly. If you have
-more than one emacs running at the same time, you might want to arrange for
-this to be set to t in all but one of them."
- :group 'bbdb-database
- :type '(choice (const :tag "Database is read-only" t)
- (const :tag "Database is writable" nil)))
-
-(defcustom bbdb-continental-zip-regexp "^\\s *[A-Z][A-Z]?\\s *-\\s *[0-9][0-9][0-9]"
- "Regexp matching continental zip codes.
-Addresses with zip codes matching the regexp will be formated using
-`bbdb-format-address-continental'. The regexp should match zip codes
-of the form CH-8052, NL-2300RA, and SE-132 54."
- :group 'bbdb-record-display
- :type 'regexp)
-
-(defcustom bbdb-auto-revert-p nil
- "*If this variable is true and the BBDB file is noticed to have changed on
-disk, it will be automatically reverted without prompting you first. Otherwise
-you will be asked. (But if the file has changed and you hae made changes in
-memory as well, you will always be asked.)"
- :group 'bbdb-saving
- :type '(choice (const :tag "Revert unchanged database without prompting" t)
- (const :tag "Ask before reverting database")))
-
-(defcustom bbdb-notice-auto-save-file nil
- "*If this is true, then the BBDB will notice when its auto-save file is
-newer than the file is was read from, and will offer to revert."
- :group 'bbdb-saving
- :type '(choice (const :tag "Check auto-save file" t)
- (const :tag "Do not check auto-save file" nil)))
-
-(defcustom bbdb-use-pop-up 'horizontal
- "*If not nil, display a continuously-updating bbdb window while in VM, MH,
-RMAIL, Gnus or a composition buffer.
-
-If 'horizontal, stack the window horizontally and give it the number of lines
-specified by `bbdb-pop-up-target-lines'.
-If 'vertical, stack the window vertically and give it the number of rows
-specified by `bbdb-pop-up-target-columns'."
- :group 'bbdb-record-display
- :type '(choice (const :tag "Automatic BBDB window, stacked vertically" 'vertical)
- (const :tag "Automatic BBDB window, stacked horizontally" 'horizontal)
- (const :tag "No Automatic BBDB window" nil)))
-
-(defcustom bbdb-pop-up-target-lines 5
- "*Desired number of lines in a horizontal BBDB buffer pop-up window.
-See `bbdb-use-pop-up' on how to select horizontal splitting."
- :group 'bbdb-record-display
- :type 'integer)
-
-(defcustom bbdb-pop-up-target-columns 20
- "*Desired number of lines in a vertical BBDB buffer pop-up window.
-See `bbdb-use-pop-up' on how to select vertical splitting."
- :group 'bbdb-record-display
- :type 'integer)
-
-(defcustom bbdb-completion-type nil
- "*Controls the behaviour of `bbdb-complete-name'. If nil, completion is
-done across the set of all full-names and user-ids in the bbdb-database;
-if the symbol 'name, completion is done on names only; if the symbol 'net,
-completion is done on network addresses only; if it is 'primary, then
-completion is done only across the set of primary network addresses (the
-first address in the list of addresses for a given user). If it is
-'primary-or-name, completion is done across primaries and real names."
- :group 'bbdb-record-use
- :type '(choice (const :tag "Complete across names and net addresses" nil)
- (const :tag "Complete across names" name)
- (const :tag "Complete across net addresses" net)
- (const :tag "Complete across primary net addresses" primary)
- (const :tag "Complete across names and primary net addresses"
- primary-or-name)))
-
-(defcustom bbdb-completion-display-record t
- "*Whether `bbdb-complete-name' (\\<mail-mode-map>\\[bbdb-complete-name]
-in mail-mode) will update the *BBDB* buffer to display the record whose email
-address has just been inserted."
- :group 'bbdb-record-use
- :type '(choice (const :tag "Update the BBDB buffer" t)
- (const :tag "Don't update the BBDB buffer" nil)))
-
-(defcustom bbdb-user-mail-names nil
- "*A regular expression identifying the addresses that belong to you.
-If a message from an address matching this is seen, the BBDB record for
-the To: line will be shown instead of the one for the From: line. If
-this is nil, it will default to the value of (user-login-name)."
- :group 'bbdb-noticing-records
- :type (list 'choice '(const :tag "Use value of (user-login-name)" nil)
- (list 'regexp :tag "Pattern matching your addresses"
- (or (user-login-name) "address"))))
-
-(defcustom bbdb-always-add-addresses 'ask
- "*If this is true, then when the Insidious Big Brother Database notices
-a new email address for a person, it will automatically add it to the list of
-addresses. If it is 'ask, you will be asked whether to add it. If it is nil
-then new network addresses will never be automatically added nor the user will
-be asked.
-
-When set to a function name the function should return one of these values.
-
-See also the variable `bbdb-new-nets-always-primary' for control of whether
-the addresses go at the front of the list or the back."
- :group 'bbdb-noticing-records
- :type '(choice (const :tag "Automatically add new addresses" t)
- (const :tag "Ask before adding new addresses" ask)
- (const :tag "Never add new addresses" nil)
- (const bbdb-ignore-some-messages-hook)
- (const bbdb-ignore-most-messages-hook)))
-
-(defcustom bbdb-new-nets-always-primary nil
- "*If this is true, then when the Insidious Big Brother Database adds a new
-address to a record, it will always add it to the front of the list of
-addresses, making it the primary address. If this is nil, you will be asked.
-If it is the symbol 'never (really, if it is any non-t, non-nil value) then
-new network addresses will always be added at the end of the list."
- :group 'bbdb-noticing-records
- :type '(choice (const :tag "New address automatically made primary" t)
- (const :tag "Ask before making new address primary" nil)
- (const :tag "Never make new address primary" never)))
-
-(defcustom bbdb-send-mail-style nil
- "*Specifies which package should be used to send mail.
-Should be 'vm, 'mh, 'mail, 'message, or 'gnus (or nil, meaning guess.)"
- :group 'bbdb-record-use
- :type '(choice (const :tag "Use VM to send mail" vm)
- (const :tag "Use MH-E to send mail" mh)
- (const :tag "Use send-mail mode to send mail" mail)
- (const :tag "Use Message to send mail" message)
- (const :tag "Use Mew to send mail" mew)
- (const :tag "Use compose-mail to send mail" compose-mail)
- (const :tag "Use gnus to send mail" gnus)
- (const :tag "Guess which package to use" nil)))
-
-(defcustom bbdb-offer-save t
- "*If t, then certain actions will cause the BBDB to ask you whether
-you wish to save the database. If nil, then the offer to save will never
-be made. If not t and not nil, then any time it would ask you, it will
-just save it without asking."
- :group 'bbdb-saving
- :type '(choice (const :tag "Offer to save the database" t)
- (const :tag "Never offer to save the database" nil)
- (const :tag "Save database without asking" savenoprompt)))
-
-(defcustom bbdb-message-caching-enabled t
- "*Whether caching of the message->bbdb-record association should be used
-for the interfaces which support it (VM, MH, and RMAIL). This can speed
-things up a lot. One implication of this variable being true is that the
-`bbdb-notice-hook' will not be called each time a message is selected, but
-only the first time. Likewise, if selecting a message would generate a
-question (whether to add an address, change the name, etc) you will only
-be asked that question the very first time the message is selected."
- :group 'bbdb
- :type '(choice (const :tag "Enable caching" t)
- (const :tag "Disable caching" nil)))
-
-(defcustom bbdb-silent-running nil
- "*If this is true, bbdb will suppress all its informational messages and
-queries. Be very very certain you want to set this, because it will suppress
-prompting to alter record names, assign names to addresses, etc."
- :group 'bbdb
- :type '(choice (const :tag "Run silently" t)
- (const :tag "Disable silent running" nil)))
-
-(defcustom bbdb-mode-hook nil
- "*Hook or hooks invoked when the *BBDB* buffer is created."
- :group 'bbdb-hooks
- :type 'hook)
-
-(defcustom bbdb-list-hook nil
- "*Hook or hooks invoked after the `bbdb-list-buffer' is filled in.
-Invoked with no arguments."
- :group 'bbdb-hooks
- :type 'hook)
-
-(defcustom bbdb-create-hook 'bbdb-creation-date-hook
- "*Hook or hooks invoked each time a new BBDB record is created. Invoked
-with one argument, the new record. This is called *before* the record is
-added to the database. Note that `bbdb-change-hook' will be called as well.
-
-Hook functions can use the variable `bbdb-update-address-class' to determine
-the class of an email address according to `bbdb-get-addresses-headers' and
-the variable `bbdb-update-address-header' is set to the header the email
-address was extracted from."
- :group 'bbdb-hooks
- :type 'hook)
-
-(defcustom bbdb-change-hook 'bbdb-timestamp-hook
- "*Hook or hooks invoked each time a BBDB record is altered. Invoked with
-one argument, the record. This is called *before* the bbdb-database buffer
-is modified. Note that if a new bbdb record is created, both this hook and
-`bbdb-create-hook' will be called."
- :group 'bbdb-hooks
- :type 'hook)
-
-(defcustom bbdb-after-change-hook nil
- "*Hook or hooks invoked each time a BBDB record is altered. Invoked with
-one argument, the record. This is called *after* the bbdb-database buffer
-is modified, so if you want to modify the record each time it is changed,
-you should use the `bbdb-change-hook' instead. Note that if a new bbdb
-record is created, both this hook and `bbdb-create-hook' will be called."
- :group 'bbdb-hooks
- :type 'hook)
-
-(defcustom bbdb-canonicalize-net-hook nil
- "*If this is non-nil, it should be a function of one arg: a network address
-string. Whenever the Insidious Big Brother Database \"notices\" a message,
-the corresponding network address will be passed to this function first, as
-a kind of \"filter\" to do whatever transformations upon it you like before
-it is compared against or added to the database. For example: it is the case
-that CS.CMU.EDU is a valid return address for all mail originating at a
-machine in the .CS.CMU.EDU domain. So, if you wanted all such addresses to
-be canonically hashed as user@CS.CMU.EDU, instead of as user@host.CS.CMU.EDU,
-you might set this variable to a function like this:
-
- (setq bbdb-canonicalize-net-hook
- '(lambda (addr)
- (cond ((string-match \"\\\\`\\\\([^@]+@\\\\).*\\\\.\\\\(CS\\\\.CMU\\\\.EDU\\\\)\\\\'\"
- addr)
- (concat (substring addr (match-beginning 1) (match-end 1))
- (substring addr (match-beginning 2) (match-end 2))))
- (t addr))))
-
-You could also use this function to rewrite UUCP-style addresses into domain-
-style addresses, or any number of things.
-
-This function will be called repeatedly until it returns a value EQ to the
-value passed in. So multiple rewrite rules might apply to a single address."
- :group 'bbdb-hooks
- :type 'function)
-
-(defcustom bbdb-canonicalize-redundant-nets-p t
- "*If this is non-nil, redundant network addresses will be ignored.
-If a record has an address of the form foo@baz.com, setting this to t
-will cause subsequently-noticed addresses like foo@bar.baz.com to be
-ignored (since we already have a more general form of that address.)
-This is similar in function to one of the possible uses of the variable
-`bbdb-canonicalize-net-hook' but is somewhat more automatic. (This
-can't quite be implemented in terms of the canonicalize-net-hook because
-it needs access to the database to determine whether an address is
-redundant, and the canonicalize-net-hook is purely a textual manipulation
-which is performed before any database access.)"
- :group 'bbdb-noticing-records
- :type '(choice (const :tag "Ignore redundant addresses" t)
- (const :tag "Don't ignore redundant addresses" nil)))
-
-(defcustom bbdb-notice-hook nil
- "*Hook or hooks invoked each time a BBDB record is \"noticed\", that is,
-each time it is displayed by the news or mail interfaces. Invoked with
-one argument, the new record. The record need not have been modified for
-this to be called - use `bbdb-change-hook' for that. You can use this to,
-for example, add something to the notes field based on the subject of the
-current message. It is up to your hook to determine whether it is running
-in Gnus, VM, MH, or RMAIL, and to act appropriately.
-
-Also note that `bbdb-change-hook' will NOT be called as a result of any
-modifications you may make to the record inside this hook.
-
-Hook functions can use the variable `bbdb-update-address-class' to determine
-the class of an email address according to `bbdb-get-addresses-headers' and
-the variable `bbdb-update-address-header' is set to the header the email
-address was extracted from.
-
-Beware that if the variable `bbdb-message-caching-enabled' is true (a good
-idea) then when you are using VM, MH, or RMAIL, this hook will be called only
-the first time that message is selected. (The Gnus interface does not use
-caching.) When debugging the value of this hook, it is a good idea to set
-caching-enabled to nil."
- :group 'bbdb-hooks
- :type 'hook)
-
-(defcustom bbdb-after-read-db-hook nil
- "*Hook or hooks invoked (with no arguments) just after the Insidious Big
-Brother Database is read in. Note that this can be called more than once if
-the BBDB is reverted."
- :group 'bbdb-hooks
- :type 'hook)
-
-(defcustom bbdb-load-hook nil
- "*Hook or hooks invoked when the BBDB code is first loaded.
-
-WARNING: This hook will be run the first time you traverse the Custom menus
- for the BBDB. As a result, nothing slow should be added to
- this hook."
- :group 'bbdb-hooks
- :type 'hook)
-
-(defcustom bbdb-initialize-hook nil
- "*Hook or hooks invoked (with no arguments) when the Insidious Big Brother
-Database initialization function `bbdb-initialize' is run."
- :group 'bbdb-hooks
- :type 'hook)
-
-;;;###autoload
-(defcustom bbdb-multiple-buffers nil
- "When non-nil we create a new buffer of every buffer causing pop-ups.
-You can also set this to a function returning a buffer name."
- :group 'bbdb-record-display
- :type '(choice (const :tag "Disabled" nil)
- (function :tag "Enabled" bbdb-multiple-buffers-default)
- (function :tag "User defined function")))
-
-(defvar bbdb-mode-map nil
- "Keymap for Insidious Big Brother Database listings.")
-(defvar bbdb-mode-search-map nil
- "Keymap for Insidious Big Brother Database searching")
-
-;; iso-2022-7bit should be OK (but not optimal for Emacs, at least --
-;; emacs-mule would be better) with both Emacs 21 and XEmacs.
-(defcustom bbdb-file-coding-system
- (bbdb-eval-when (fboundp 'coding-system-p)
- (cond ((apply 'coding-system-p '(utf-8-emacs))
- 'utf-8-emacs)
- (t 'iso-8859-1)))
- "Coding system used for reading and writing `bbdb-file'.
-This should not be changed by users.
-This should not be changed in between BBDB sessions, i.e. before loading the
-BBDB which was stored in a different coding system. Make a backup of your
-BBDB before changing this variable!"
- :group 'bbdb
- :type '(choice (const iso-8859-1)
- (const utf-8-emacs)
- (const iso-2022-7bit)))
-
-(defvar bbdb-suppress-changed-records-recording nil
- "Whether to record changed records in variable `bbdb-changed-records'.
-
-If this is false, the BBDB will cease to remember which records are changed
-as the change happens. It will still remember that records have been changed,
-so the file will still be saved, but the changed records list, and the `!!'
-in the *BBDB* buffer modeline that it depends on, will no longer be updated.
-
-You should bind this variable, not set it; the `!!' is a useful user-
-interface feature, and should only be suppressed when changes need to be
-automatically made to BBDB records which the user will not care directly
-about.")
-
-
-;;; These are the buffer-local variables we use.
-;;; They are mentioned here so that the compiler doesn't warn about them
-;;; when byte-compile-warn-about-free-variables is on.
-
-(defvar bbdb-records nil)
-(defvar bbdb-changed-records nil)
-(defvar bbdb-end-marker nil)
-(defvar bbdb-hashtable nil)
-(defvar bbdb-propnames nil)
-(defvar bbdb-message-cache nil)
-(defvar bbdb-showing-changed-ones nil)
-(defvar bbdb-modified-p nil)
-(defvar bbdb-address-print-formatting-alist) ; "bbdb-print"
-
-(defvar bbdb-debug t)
-(defmacro bbdb-debug (&rest body)
- ;; ## comment out the next line to turn off debugging.
- ;; ## You really shouldn't do this! But it will speed things up.
- (list 'and 'bbdb-debug (list 'let '((debug-on-error t)) (cons 'progn body)))
- )
-
-
-;;; internal kludge to force queries to always happen with the mouse rather
-;;; than basing the decision on the last-input-event; bind this, don't set it.
-(defvar bbdb-force-dialog-boxes nil)
-
-(defun bbdb-y-or-n-p (prompt)
- (prog1
- (funcall
- (cond ((and bbdb-force-dialog-boxes
- (fboundp 'yes-or-no-p-dialog-box))
- (when (and (fboundp 'raise-frame)
- (not (frame-visible-p (selected-frame))))
- (raise-frame (selected-frame)))
- 'yes-or-no-p-dialog-box)
- (t 'y-or-n-p))
- prompt)
- (message " ")))
-
-(defun bbdb-yes-or-no-p (prompt)
- (prog1
- (funcall (if (and bbdb-force-dialog-boxes
- (fboundp 'yes-or-no-p-dialog-box))
- 'yes-or-no-p-dialog-box
- 'yes-or-no-p)
- prompt)
- (message " ")))
-
-(defun bbdb-invoke-hook (hook arg)
- "Like `invoke-hooks', but invokes the given hook with one argument."
- (if (and (boundp hook) (setq hook (symbol-value hook)))
- (if (and (consp hook) (not (eq (car hook) 'lambda)))
- (while hook
- (funcall (car hook) arg)
- (setq hook (cdr hook)))
- (funcall hook arg))))
-
-(defun bbdb-invoke-hook-for-value (hook &rest args)
- "If HOOK is a function, invoke it with ARGS. Otherwise return it as-is."
- (cond ((eq hook nil) nil)
- ((eq hook t) t)
- ((functionp hook) (apply hook args))
- (t hook)))
-
-(defmacro bbdb-defstruct (conc-name &rest slots)
- "Make two functions, one for each slot. The functions are:
- CONC-NAME + SLOT and CONC-NAME + `set-' + SLOT
-The first one is to be used to read the element named in SLOT, and the
-second is used to set it. Also make a constant
- CONC-NAME + `length'
-that holds the number of slots."
- (setq conc-name (symbol-name conc-name))
- (let ((body '())
- (i 0)
- (L (length slots)))
- (while slots
- (setq body
- (nconc body
- (let ((readname (intern (concat conc-name (symbol-name (car slots)))))
- (setname (intern (concat conc-name "set-" (symbol-name (car slots))))))
- (list
- (list 'defmacro readname '(vector)
- (list 'list ''aref 'vector i))
- (list 'defmacro setname '(vector value)
- (if (string= setname "bbdb-record-set-net")
- (list 'setq
- 'bbdb-define-all-aliases-needs-rebuilt t))
- (list 'list ''aset 'vector i 'value))
- ;(list 'put (list 'quote readname) ''edebug-form-hook ''(form))
- ;(list 'put (list 'quote setname) ''edebug-form-hook ''(form form))
- ))))
- (setq slots (cdr slots) i (1+ i)))
- (setq body (nconc body (list (list 'defconst
- (intern (concat conc-name "length"))
- L))))
- (cons 'progn body)))
-
-;;; When reading this code, beware that "cache" refers to two things.
-;;; It refers to the cache slot of bbdb-record structures, which is
-;;; used for computed properties of the records; and it also refers
-;;; to a message-id --> bbdb-record association list which speeds up
-;;; the RMAIL, VM, and MH interfaces.
-
-;; Build reading and setting functions for firstname, lastname, aka,
-;; company, phones, addresses, net, raw-notes, and cache. These are
-;; for accessing the high-level forms for the record.
-(bbdb-defstruct bbdb-record-
- firstname lastname aka company
- phones addresses net raw-notes
- cache
- )
-
-;; HACKHACK
-;;(defmacro bbdb-record-set-net (vector value)
-;; "We redefine the set-binding for 'net to detect changes"
-;; (list 'progn
-;; (list 'aset vector 6 value)
-;; (list 'setq 'bbdb-define-all-aliases-needs-rebuilt t)))
-
-(put 'company 'field-separator "; ")
-(put 'notes 'field-separator "\n")
-
-;; Build reading and setting functions for location, area, exchange,
-;; suffix, and extension. These are for accessing the elements of the
-;; individual phone number forms.
-(bbdb-defstruct bbdb-phone-
- location area exchange suffix extension
- )
-
-;; Build reading and setting functions for location, street, city,
-;; state, zip and country. These are for accessing the elements of
-;; the individual address forms.
-(bbdb-defstruct bbdb-address-
- location streets city state zip country
- )
-
-;; Build reading and setting functions for namecache (the full name of
-;; the person referred to by the record), sortkey (the concatenation
-;; of the elements used for sorting the record), marker, and
-;; deleted-p. These are for accessing the elements of the cache form,
-;; and are generally concatenations of data existing in separate parts
-;; of the record, stored here prebuilt for speed.
-(bbdb-defstruct bbdb-cache-
- namecache sortkey marker deleted-p
- )
-
-;; Build the namecache for a record
-(defsubst bbdb-record-name-1 (record)
- (bbdb-cache-set-namecache (bbdb-record-cache record)
- (let ((fname (bbdb-record-firstname record))
- (lname (bbdb-record-lastname record)))
- (if (> (length fname) 0)
- (if (> (length lname) 0)
- (concat fname " " lname)
- fname)
- lname))))
-
-;; Return the full name from a record. If the name is not available
-;; in the namecache, the namecache value is generated (and stored).
-(defun bbdb-record-name (record)
- (or (bbdb-cache-namecache (bbdb-record-cache record))
- (bbdb-record-name-1 record)))
-
-(defun bbdb-record-lfname (record)
- (let ((fname (bbdb-record-firstname record))
- (lname (bbdb-record-lastname record)))
- (if (and (> (length fname) 0) (> (length lname) 0))
- (concat lname " " fname)
- nil)))
-
-;; Return the sortkey for a record, building (and storing) it if
-;; necessary.
-(defun bbdb-record-sortkey (record)
- (or (bbdb-cache-sortkey (bbdb-record-cache record))
- (bbdb-cache-set-sortkey (bbdb-record-cache record)
- (downcase
- (concat (bbdb-record-lastname record)
- (bbdb-record-firstname record)
- (bbdb-record-company record))))))
-
-(defmacro bbdb-record-marker (record)
- (list 'bbdb-cache-marker (list 'bbdb-record-cache record)))
-
-(defmacro bbdb-record-deleted-p (record)
- (list 'bbdb-cache-deleted-p (list 'bbdb-record-cache record)))
-
-(defmacro bbdb-record-set-deleted-p (record val)
- (list 'bbdb-cache-set-deleted-p (list 'bbdb-record-cache record) val))
-
-(defmacro bbdb-record-set-namecache (record newval)
- (list 'bbdb-cache-set-namecache (list 'bbdb-record-cache record) newval))
-
-(defmacro bbdb-record-set-sortkey (record newval)
- (list 'bbdb-cache-set-sortkey (list 'bbdb-record-cache record) newval))
-
-(defmacro bbdb-record-set-marker (record newval)
- (list 'bbdb-cache-set-marker (list 'bbdb-record-cache record) newval))
-
-
-;; The "notes" and "properties" accessors don't need to be fast.
-
-(defun bbdb-record-notes (record)
- (if (consp (bbdb-record-raw-notes record))
- (cdr (assq 'notes (bbdb-record-raw-notes record)))
- (bbdb-record-raw-notes record)))
-
-;; this works on the 'company field as well.
-(defun bbdb-record-getprop (record property)
- (if (memq property '(name address addresses phone phones net aka AKA))
- (error "bbdb: cannot access the %s field this way" property))
- (if (eq property 'company)
- (bbdb-record-company record)
- (if (consp (bbdb-record-raw-notes record))
- (cdr (assq property (bbdb-record-raw-notes record)))
- (if (and (eq property 'notes)
- (stringp (bbdb-record-raw-notes record)))
- (bbdb-record-raw-notes record)
- nil))))
-
-(defun bbdb-get-field (rec field &optional nn)
- "Get the N-th element (or all if nil) of the notes FIELD of the REC.
-If the note is absent, returns a zero length string."
- (let ((note (or (bbdb-record-getprop rec field) "")))
- (if nn
- (nth nn (split-string note " ,;\t\n\f\r\v"))
- note)))
-
-;; this works on the 'company field as well.
-(defun bbdb-record-putprop (record property newval)
- (if (memq property '(name address addresses phone phones net aka AKA))
- (error "bbdb: cannot annotate the %s field this way" property))
- (if (eq property 'company)
- (bbdb-record-set-company record
- (bbdb-record-set-company record newval))
- (if (and (eq property 'notes)
- (not (consp (bbdb-record-raw-notes record))))
- (bbdb-record-set-raw-notes record newval)
- (or (listp (bbdb-record-raw-notes record))
- (bbdb-record-set-raw-notes record
- (list (cons 'notes (bbdb-record-raw-notes record)))))
- (let ((old (assq property (bbdb-record-raw-notes record))))
- (if old
- (if newval
- (setcdr old newval)
- (bbdb-record-set-raw-notes record
- (delq old (bbdb-record-raw-notes record))))
- (and newval
- (bbdb-record-set-raw-notes record
- (append (bbdb-record-raw-notes record)
- (list (cons property newval))))))))
- ;; save some file space: if we ever end up with ((notes . "...")),
- ;; replace it with the string.
- (if (and (consp (bbdb-record-raw-notes record))
- (null (cdr (bbdb-record-raw-notes record)))
- (eq 'notes (car (car (bbdb-record-raw-notes record)))))
- (bbdb-record-set-raw-notes record
- (cdr (car (bbdb-record-raw-notes record)))))
- )
- ;; If we're changing the company, then we need to sort, since the company
- ;; is the sortkey for nameless records. This should almost never matter...
- (bbdb-change-record record (eq property 'company))
- newval)
-
-(defun bbdb-record-set-notes (record newval)
- (if (consp (bbdb-record-raw-notes record))
- (bbdb-record-putprop record 'notes newval)
- (bbdb-record-set-raw-notes record newval)
- (bbdb-change-record record nil)))
-
-(defun bbdb-phone-string (phone)
- (if (= 2 (length phone)) ; euronumbers....
- (aref phone 1)
- ;; numbers should come in two forms:
- ;; ["where" 415 555 1212 99] or ["where" "the number"]
- (if (stringp (aref phone 1))
- (error "doubleplus ungood: euronumbers unwork"))
- (concat (if (/= 0 (bbdb-phone-area phone))
- (format "(%03d) " (bbdb-phone-area phone))
- "")
- (if (/= 0 (bbdb-phone-exchange phone))
- (format "%03d-%04d"
- (bbdb-phone-exchange phone) (bbdb-phone-suffix phone))
- "")
- (if (and (bbdb-phone-extension phone)
- (/= 0 (bbdb-phone-extension phone)))
- (format " x%d" (bbdb-phone-extension phone))
- ""))))
-
-;; Legacy function. Used to convert a zip datastructure string into a
-;; formated string. As zip codes are plain strings now, use
-;; `bbdb-address-zip' instead.
-(defalias 'bbdb-address-zip-string 'bbdb-address-zip)
-
-(defmacro bbdb-record-lessp (record1 record2)
- (list 'string< (list 'bbdb-record-sortkey record1)
- (list 'bbdb-record-sortkey record2)))
-
-(defmacro bbdb-subint (string match-number)
- (list 'string-to-number
- (list 'substring string
- (list 'match-beginning match-number)
- (list 'match-end match-number))))
-
-(eval-and-compile
- (if (fboundp 'display-error)
- (fset 'bbdb-display-error 'display-error)
- (defun bbdb-display-error(msg stream)
- (message "Error: %s" (nth 1 msg)))))
-
-(defmacro bbdb-error-retry (form)
- (list 'catch ''--bbdb-error-retry--
- (list 'while ''t
- (list 'condition-case '--c--
- (list 'throw ''--bbdb-error-retry-- form)
- '(error
- (ding)
- (let ((cursor-in-echo-area t))
- (bbdb-display-error --c-- nil)
- (sit-for 2)))))))
-
-;;; Completion on labels and field data
-
-;;; Realistically speaking, it doesn't make sense to offer minibuffer
-;;; completion for some fields - like ones that don't have labels!
-;;;
-;;; Also, I could probably do this with macros similar to the
-;;; def-struct stuff.
-(defcustom bbdb-default-label-list
- '("Home" "Office" "Mobile" "Other")
- "*Default list of labels for Address and Phone fields."
- :group 'bbdb-record-creation
- :type '(repeat string))
-
-(defcustom bbdb-phones-label-list
- bbdb-default-label-list
- "*List of labels for Phone field.
-The default value is `bbdb-default-label-list'."
- :group 'bbdb-record-creation
- :type '(repeat string))
-
-(defcustom bbdb-addresses-label-list
- bbdb-default-label-list
- "*List of labels for Address field.
-The default value is `bbdb-default-label-list'."
- :group 'bbdb-record-creation
- :type '(repeat string))
-
-(defun bbdb-label-completion-list (field)
- "Figure out a completion list for the specified FIELD label.
-This evaluates the variable bbdb-FIELD-label-list, such
-as `bbdb-phones-label-list'."
- (if (boundp (intern (format "bbdb-%s-label-list" field)))
- (eval (intern (format "bbdb-%s-label-list" field)))
- ;; special-case out the ones it doesn't make sense for here?
- bbdb-default-label-list))
-
-(defun bbdb-label-completion-default (field)
- "Figure out a default label from the completion list for FIELD.
-This evaluates the variable bbdb-default-FIELD-label, such
-as `bbdb-default-phones-label', if it exists, or it takes
-the first item from the list of completions for FIELD as
-returned by `bbdb-label-completion-list'."
- (if (boundp (intern (format "bbdb-default-%s-label" field)))
- (eval (intern (format "bbdb-default-%s-label" field)))
- (nth 0 (bbdb-label-completion-list field))))
-
-;; These are so you can accumulate e.g. mail aliases or company names
-;; and have BBDB offer completion on them.
-(defun bbdb-data-completion-list (field)
- "Figure out a completion list for the specified FIELD value.
-This evaluates the variable bbdb-FIELD-data-list, such
-as `bbdb-mail-alias-data-list', if it exists, or it uses
-`bbdb-default-label-list'."
- (if (boundp (intern (format "bbdb-%s-data-list" field)))
- (eval (intern (format "bbdb-%s-data-list" field)))
- ;; special-case out the ones it doesn't make sense for here?
- bbdb-default-label-list))
-
-(defun bbdb-data-completion-default (field)
- "Figure out a default value from the completion list for FIELD.
-This evaluates the variable bbdb-default-FIELD-data, such
-as `bbdb-default-mail-alias-data', if it exists, or it takes
-the first item from the list of completions for FIELD as
-returned by `bbdb-data-completion-list'."
- (if (boundp (intern (format "bbdb-default-%s-data" field)))
- (eval (intern (format "bbdb-default-%s-data" field)))
- (nth 0 (bbdb-label-completion-list field))))
-
-;;;
-(defvar bbdb-buffer nil)
-(defun bbdb-buffer ()
- (if (and bbdb-buffer (buffer-live-p bbdb-buffer))
- bbdb-buffer
- (when (and bbdb-file-remote
- (file-newer-than-file-p bbdb-file-remote bbdb-file))
- (let ((coding-system-for-write bbdb-file-coding-system))
- (copy-file bbdb-file-remote bbdb-file t t)))
- (setq bbdb-buffer (find-file-noselect bbdb-file 'nowarn))))
-
-(defmacro bbdb-with-db-buffer (&rest body)
- (cons 'with-current-buffer
- (cons '(bbdb-buffer)
- (if (and (boundp 'bbdb-debug) bbdb-debug)
- ;; if we're debugging, and the .bbdb buffer is visible in
- ;; a window, temporarilly switch to that window so that
- ;; when we come out, that window has been scrolled to the
- ;; record we've just modified. (make w-point = b-point)
- (list
- (list 'let '((w (and bbdb-debug
- (get-buffer-window
- (buffer-name
- (get-buffer bbdb-file))))))
- (list 'save-excursion
- (cons 'save-window-excursion
- (cons '(and w (select-window w))
- body)))))
- body))))
-
-(defsubst bbdb-string-trim (string)
- "Lose leading and trailing whitespace. Also remove all properties
-from string."
- (if (string-match "\\`[ \t\n]+" string)
- (setq string (substring string (match-end 0))))
- (if (string-match "[ \t\n]+\\'" string)
- (setq string (substring string 0 (match-beginning 0))))
- ;; This is not ideologically blasphemous. It is a bad function to
- ;; use on regions of a buffer, but since this is our string, we can
- ;; do whatever we want with it. --Colin
- (set-text-properties 0 (length string) nil string)
- string)
-
-(defun bbdb-read-string (prompt &optional default completions)
- "Reads a string, trimming whitespace and text properties."
- (bbdb-string-trim
- (if completions
- (completing-read prompt completions nil nil (cons default 0))
- (bbdb-string-trim (read-string prompt default)))))
-
-;;; Address formatting.
-
-(defcustom bbdb-time-display-format "%d %b %Y"
- "The format for the timestamp to be used in the creation-date and
-timestamp fields. See the documentation for `format-time-string'."
- :group 'bbdb :type 'string)
-
-(defun bbdb-time-convert (date &optional format)
- "Convert a date from the BBDB internal format to the format
-determined by FORMAT (or `bbdb-time-display-format' if FORMAT not
-present). Returns a string containing the date in the new format."
- (let ((parts (bbdb-split date "-")))
- (format-time-string (or format bbdb-time-display-format)
- (encode-time 0 0 0 (string-to-number (caddr parts))
- (string-to-number (cadr parts))
- (string-to-number (car parts))))))
-
-(defalias 'bbdb-format-record-timestamp 'bbdb-time-convert)
-(defalias 'bbdb-format-record-creation-date 'bbdb-time-convert)
-
-(defconst bbdb-gag-messages nil
- "Bind this to t to quiet things down - do not set it!")
-
-(defvar bbdb-buffer-name "*BBDB*")
-
-(defcustom bbdb-display-layout-alist
- '((one-line (order . (phones mail-alias net notes))
- (name-end . 24)
- (toggle . t))
- (multi-line (omit . (creation-date timestamp))
- (toggle . t))
- (pop-up-multi-line)
- (full-multi-line))
- "*An alist describing each display layout.
-The format of an element is (LAYOUT-NAME OPTION-ALIST).
-
-By default there are four different layout types used by BBDB, which are
-`one-line', `multi-line', `pop-up-multi-line' (used for pop-ups) and
-`full-multi-line' (showing all fields of a record).
-
-OPTION-ALIST specifies the options for the layout. Valid options are:
-
- ------- Availability --------
- Format one-line multi-line default if unset
-------------------------------------------------------------------------------
- (toggle . BOOL) + + nil
- (order . FIELD-LIST) + + '(phones ...)
- (omit . FIELD-LIST) + + nil
- (name-end . INTEGER) + - 40
- (indentation . INTEGER) - + 14
- (primary . BOOL) - + nil
- (test . SEXP) + + nil
-
-- toggle: controls if this layout is included when toggeling the display layout
-- order: defines a user specific order for the fields, where `t' is a place
- holder for all remaining fields
-- omit: is a list of fields which should not be displayed or `t' to exclude all
- fields except those listed in the order option
-- name-end: sets the column where the name should end in one-line layout.
-- indentation: sets the level of indentation for multi-line display.
-- primary: controls wether only the primary net is shown or all are shown.
-- test: a lisp expression controlling wether the record is to be displayed.
-
-When you add a new layout FOO, you can write a corresponding layout
-function bbdb-format-record-layout-FOO. If you do not write your own
-layout function, the multi-line layout will be used."
- :group 'bbdb
- :type
- `(repeat
- (cons :tag "Layout Definition"
- (choice :tag "Layout type"
- (const one-line)
- (const multi-line)
- (const pop-up-multi-line)
- (const full-multi-line)
- (symbol))
- (set :tag "Properties"
- (cons :tag "Order"
- (const :tag "List of fields to order by" order)
- (repeat (choice (const phones)
- (const addresses)
- (const net)
- (const AKA)
- (const notes)
- (symbol :tag "other")
- (const :tag "Remaining fields" t))))
- (choice :tag "Omit"
- :value (omit . nil)
- (cons :tag "List of fields to omit"
- (const :tag "Fields not to display" omit)
- (repeat (choice (const phones)
- (const addresses)
- (const net)
- (const AKA)
- (const notes)
- (symbol :tag "other"))))
- (const :tag "Exclude all fields except those listed in the order property" t))
- (cons :tag "Indentation"
- :value (indentation . 14)
- (const :tag "Level of indentation for multi-line layout"
- indentation)
- (number :tag "Column"))
- (cons :tag "End of name field"
- :value (name-end . 24)
- (const :tag "The column where the name should end in one-line layout"
- name-end)
- (number :tag "Column"))
- (cons :tag "Toggle"
- (const :tag "The layout is included when toggling display layout" toggle)
- boolean)
- (cons :tag "Primary Net Only"
- (const :tag "Only the primary net address is included" primary)
- boolean)
- (cons :tag "Test"
- (const :tag "Show only records passing this test" test)
- (choice (const :tag "No test" nil)
- (cons :tag "List of required fields"
- (const :tag "Choose from the attributes in the following set:" and)
- (set
- (const name)
- (const company)
- (const net)
- (const phones)
- (const addresses)
- (const notes)))
- (sexp :tag "Lisp expression")))))))
-
-
-(defcustom bbdb-display-layout 'multi-line
- "*The default display layout."
- :group 'bbdb
- :type '(choice (const one-line)
- (const multi-line)
- (const full-multi-line)
- (symbol)))
-
-(defcustom bbdb-pop-up-display-layout 'one-line
- "*The default display layout pop-up BBDB buffers, i.e. mail, news."
- :group 'bbdb
- :type '(choice (const one-line)
- (const multi-line)
- (const full-multi-line)
- (symbol)))
-
-(defun bbdb-display-layout-get-option (layout option)
- (let ((layout-spec (if (listp layout)
- layout
- (assoc layout bbdb-display-layout-alist)))
- option-value)
- (and layout-spec
- (setq option-value (assoc option layout-spec))
- (cdr option-value))))
-
-(defcustom bbdb-address-formatting-alist
- '((bbdb-address-is-continental . bbdb-format-address-continental)
- (nil . bbdb-format-address-default))
- "Alist of address identifying and address formatting functions.
-The key is an identifying function which accepts an address. The
-associated value is a formatting function which inserts the formatted
-address in the current buffer. If the identifying function returns
-non-nil, the formatting function is called. When nil is used as the
-car, then the associated formatting function will always be called.
-Therefore you should always have (nil . bbdb-format-address-default) as
-the last element in the alist.
-
-All functions should take two arguments, the address and an indentation.
-The indentation argument may be optional.
-
-This alist is used in `bbdb-format-address'.
-
-See also `bbdb-address-print-formatting-alist'."
- :group 'bbdb-record-display
- :type '(repeat (cons function function)))
-
-(defvar bbdb-address-print-formatting-alist) ; "bbdb-print"
-
-(defun bbdb-address-is-continental (addr)
- "Return non-nil if the address ADDR is a continental address.
-This is done by comparing the zip code to `bbdb-continental-zip-regexp'.
-
-This is a possible identifying function for
-`bbdb-address-formatting-alist' and
-`bbdb-address-print-formatting-alist'."
- (string-match bbdb-continental-zip-regexp (bbdb-address-zip addr)))
-
-(defun bbdb-format-streets (addr indent)
- "Insert street subfields of address ADDR in current buffer.
-This may be used by formatting functions listed in
-`bbdb-address-formatting-alist'."
- (bbdb-mapc (lambda(str)
- (indent-to indent)
- (insert str "\n"))
- (bbdb-address-streets addr)))
-
-(defun bbdb-format-address-continental (addr &optional indent)
- "Insert formated continental address ADDR in current buffer.
-This format is used in western Europe, for example.
-
-This function is a possible formatting function for
-`bbdb-address-formatting-alist'.
-
-The result looks like this:
- location: street
- street
- ...
- zip city, state
- country"
- (setq indent (or indent 14))
- (let (;(fmt (format " %%%ds: " indent))
- (indent (+ 3 indent)))
- ;(insert (format fmt (bbdb-address-location addr)))
- (bbdb-format-streets addr indent)
- (let ((c (bbdb-address-city addr))
- (s (bbdb-address-state addr))
- (z (bbdb-address-zip addr)))
- (if (or (> (length c) 0)
- (> (length z) 0)
- (> (length s) 0))
- (progn
- (indent-to indent)
- (insert z (if (and (> (length z) 0)
- (> (length c) 0)) " " "")
- c (if (and (or (> (length z) 0)
- (> (length c) 0))
- (> (length s) 0)) ", " "")
- s "\n"))))
- (let ((str (bbdb-address-country addr)))
- (if (= 0 (length str)) nil
- (indent-to indent) (insert str "\n")))))
-
-(defun bbdb-format-address-default (addr &optional indent)
- "Insert formated address ADDR in current buffer.
-This is the default format; it is used in the US, for example.
-
-This function is a possible formatting function for
-`bbdb-address-formatting-alist'.
-
-The result looks like this:
- location: street
- street
- ...
- city, state zip
- country"
- (setq indent (or indent 14))
- (let (;(fmt (format " %%%ds: " indent))
- (indent (+ 3 indent)))
-; (insert (format fmt (bbdb-address-location addr)))
- (bbdb-format-streets addr indent)
- (let ((c (bbdb-address-city addr))
- (s (bbdb-address-state addr))
- (z (bbdb-address-zip addr)))
- (if (or (> (length c) 0)
- (> (length z) 0)
- (> (length s) 0))
- (progn
- (indent-to indent)
- (insert c (if (and (> (length c) 0)
- (> (length s) 0)) ", " "")
- s (if (and (or (> (length c) 0)
- (> (length s) 0))
- (> (length z) 0)) " " "")
- z "\n"))))
- (let ((str (bbdb-address-country addr)))
- (if (= 0 (length str)) nil
- (indent-to indent) (insert str "\n")))))
-
-(defun bbdb-format-address (addr &optional printing indent)
- "Call appropriate formatting function for address ADDR.
-
-If optional second argument PRINTING is non-nil, this uses the alist
-`bbdb-address-print-formatting-alist' to determine how the address is to
-formatted and inserted into the current buffer. This is used by
-`bbdb-print-format-record'.
-
-If second argument PRINTING is nil, this uses the alist
-`bbdb-address-formatting-alist' to determine how the address is to
-formatted and inserted into the current buffer. This is used by
-`bbdb-format-record'."
- ;; alist contains functions ((ident1 . format1) (ident2 . format2) ...)
- ;; the first identifying-function is (caar alist)
- ;; the first formatting-function is (cdar alist)
- (let ((alist (if printing bbdb-address-print-formatting-alist
- bbdb-address-formatting-alist)))
- ;; while there a functions left and the current function does not
- ;; identify the address, try the next function.
- (while (and (caar alist)
- (null (funcall (caar alist) addr)))
- (setq alist (cdr alist)))
- ;; if we haven't reached the end of functions, we got a hit.
- (when alist
- (if printing
- (funcall (cdar alist) addr)
- (funcall (cdar alist) addr indent)))))
-
-(defun bbdb-format-record-name-company (record)
- (let ((name (or (bbdb-record-name record) "???"))
- (company (bbdb-record-company record))
- (start (point)))
-
- (insert name)
- (put-text-property start (point) 'bbdb-field '(name))
-
- (when company
- (insert " - ")
- (setq start (point))
- (insert company)
- (put-text-property start (point) 'bbdb-field '(company)))))
-
-(defun bbdb-format-record-one-line-phones (layout record phone)
- "Insert a formatted phone number for one-line display."
- (let ((start (point)))
- (insert (format "%s " (aref phone 1)))
- (put-text-property start (point) 'bbdb-field
- (list 'phone phone (aref phone 0)))
- (setq start (point))
- (insert (format "(%s)" (aref phone 0)))
- (put-text-property start (point) 'bbdb-field
- (list 'phone phone 'field-name))))
-
-(defun bbdb-format-record-one-line-net (layout record net)
- "Insert a formatted list of nets for one-line display."
- (let ((start (point)))
- (insert net)
- (put-text-property start (point) 'bbdb-field (list 'net net))))
-
-(defun bbdb-format-record-one-line-notes (layout record notes)
- "Insert formatted notes for one-line display.
-Line breaks will be removed and white space trimmed."
- (let ((start (point)))
- (insert (bbdb-replace-in-string notes "[\r\n\t ]+" " "))
- (put-text-property start (point) 'bbdb-field (list 'notes notes))))
-
-(defun bbdb-format-record-layout-one-line (layout record field-list)
- "Record formatting function for the one-line layout.
-See `bbdb-display-layout-alist' for more."
- ;; name and company
- (bbdb-format-record-name-company record)
- (let ((name-end (or (bbdb-display-layout-get-option layout 'name-end)
- 40))
- start end)
- (save-excursion
- (setq end (point))
- (beginning-of-line)
- (setq start (point)))
- (when (> (- end start -1) name-end)
- (put-text-property (+ start name-end -4) end 'invisible t)
- (insert "..."))
- ;; guarantee one space after name - company
- (insert " ")
- (indent-to name-end))
- ;; rest of the fields
- (let (start field contentfun formatfun values value)
- (while field-list
- (setq field (car field-list)
- contentfun (intern (concat "bbdb-record-"
- (symbol-name field))))
- (if (fboundp contentfun)
- (setq values (eval (list contentfun record)))
- (setq values (bbdb-record-getprop record field)))
- (when (and (eq field 'net)
- (bbdb-display-layout-get-option layout 'primary))
- (setq values (list (car values))))
- (when values
- (if (not (listp values)) (setq values (list values)))
- (setq formatfun (intern (format "bbdb-format-record-%s-%s"
- layout field)))
- (while values
- (setq start (point)
- value (car values))
- (if (fboundp formatfun)
- (funcall formatfun layout record value)
- (insert (format "%s" value))
- (cond ((eq field 'addresses)
- (put-text-property start (point) 'bbdb-field
- (list 'address value)))
- ((eq field 'phones)
- (put-text-property start (point) 'bbdb-field
- (list 'phone value)))
- ((memq field '(name net aka))
- (put-text-property start (point) 'bbdb-field
- (list field value )))
- (t
- (put-text-property start (point) 'bbdb-field
- (list 'property (list field value))))))
- (setq values (cdr values))
- (if values (insert ", ")))
- (insert "; "))
- (setq field-list (cdr field-list))))
- ;; delete the trailing "; "
- (backward-delete-char 2)
- (insert "\n"))
-
-(defun bbdb-format-record-layout-multi-line (layout record field-list)
- "Record formatting function for the multi-line layout.
-See `bbdb-display-layout-alist' for more."
- (bbdb-format-record-name-company record)
- (insert "\n")
- (let* ((notes (bbdb-record-raw-notes record))
- (indent (or (bbdb-display-layout-get-option layout 'indentation) 14))
- (fmt (format " %%%ds: " indent))
- start field)
- (if (stringp notes)
- (setq notes (list (cons 'notes notes))))
- (while field-list
- (setq field (car field-list)
- start (point))
- (cond ((eq field 'phones)
- (let ((phones (bbdb-record-phones record))
- loc phone)
- (while phones
- (setq phone (car phones)
- start (point))
- (setq loc (format fmt (bbdb-phone-location phone)))
- (insert loc)
- (put-text-property start (point) 'bbdb-field
- (list 'phone phone 'field-name))
- (setq start (point))
- (insert (bbdb-phone-string phone) "\n")
- (put-text-property start (point) 'bbdb-field
- (list 'phone phone
- (bbdb-phone-location phone)))
- (setq phones (cdr phones))))
- (setq start nil))
- ((eq field 'addresses)
- (let ((addrs (bbdb-record-addresses record))
- loc addr)
- (while addrs
- (setq addr (car addrs)
- start (point))
- (setq loc (format fmt (bbdb-address-location addr)))
- (insert loc)
- (put-text-property start (point) 'bbdb-field
- (list 'address addr 'field-name))
- (setq start (point))
- (bbdb-format-address addr nil indent)
- (put-text-property start (point) 'bbdb-field
- (list 'address addr
- (bbdb-address-location addr)))
- (setq addrs (cdr addrs))))
- (setq start nil))
- ((eq field 'net)
- (let ((net (bbdb-record-net record)))
- (when net
- (insert (format fmt "net"))
- (put-text-property start (point) 'bbdb-field
- '(net field-name))
- (setq start (point))
- (if (bbdb-display-layout-get-option layout 'primary)
- (insert (car net) "\n")
- (insert (mapconcat (function identity) net ", ") "\n"))
- (put-text-property start (point) 'bbdb-field '(net)))))
- ((eq field 'aka)
- (let ((aka (bbdb-record-aka record)))
- (when aka
- (insert (format fmt "AKA"))
- (put-text-property start (point) 'bbdb-field
- '(aka field-name))
- (insert (mapconcat (function identity) aka ", ") "\n")
- (setq start (point))
- (put-text-property start (point) 'bbdb-field '(aka)))))
- (t
- (let ((note (assoc field notes))
- (indent (length (format fmt "")))
- p notefun)
- (when note
- (insert (format fmt field))
- (put-text-property start (point) 'bbdb-field
- (list 'property note 'field-name))
- (setq start (point))
- (setq p (point)
- notefun (intern (format "bbdb-format-record-%s" field)))
- (if (fboundp notefun)
- (insert (funcall notefun (cdr note)))
- (insert (cdr note)))
- (save-excursion
- (save-restriction
- (narrow-to-region p (1- (point)))
- (goto-char (1+ p))
- (while (search-forward "\n" nil t)
- (insert (make-string indent ?\ )))))
- (insert "\n"))
- (put-text-property start (point) 'bbdb-field
- (list 'property note)))))
- (setq field-list (cdr field-list)))))
-
-(defalias 'bbdb-format-record-layout-full-multi-line
- 'bbdb-format-record-layout-multi-line)
-
-(defalias 'bbdb-format-record-layout-pop-up-multi-line
- 'bbdb-format-record-layout-multi-line)
-
-(defun bbdb-format-record (record &optional layout)
- "Insert a formatted version of RECORD into the current buffer.
-
-LAYOUT can be a symbol describing a layout in
-`bbdb-display-layout-alist'. For compatibility reasons, LAYOUT can
-also be nil or t, where t stands for the one-line, and nil for the
-multi-line layout."
- (bbdb-debug (if (bbdb-record-deleted-p record)
- (error "plus ungood: formatting deleted record")))
- (setq layout (cond ((eq nil layout)
- 'multi-line)
- ((eq t layout)
- 'one-line)
- ((symbolp layout)
- layout)
- (t
- (error "Unknown layout `%s'" layout))))
- (let* ((layout-spec (assoc layout bbdb-display-layout-alist))
- (test (bbdb-display-layout-get-option layout-spec 'test))
- (omit-list (bbdb-display-layout-get-option layout-spec 'omit))
- (order-list (bbdb-display-layout-get-option layout-spec 'order))
- (all-fields (append '(phones addresses net aka)
- (let ((raw-notes (bbdb-record-raw-notes record)))
- (if (stringp raw-notes)
- '(notes)
- (mapcar (lambda (r) (car r)) raw-notes)))))
- format-function field-list)
- (when (or (not test)
- ;; bind some variables for the test
- (let ((name (bbdb-record-name record))
- (company (bbdb-record-company record))
- (net (bbdb-record-net record))
- (phones (bbdb-record-phones record))
- (addresses (bbdb-record-addresses record))
- (notes (bbdb-record-raw-notes record)))
- ;; this must evaluate to non-nil if the record is to be shown
- (eval test)))
- (if (functionp omit-list)
- (setq omit-list (funcall omit-list record layout)))
- (if (functionp order-list)
- (setq order-list (funcall order-list record layout)))
- ;; first omit unwanted fields
- (when (and omit-list (or (not order-list) (memq t order-list)))
- (if (not (listp omit-list))
- ;; t => show nothing
- (setq all-fields nil)
- ;; listp => show all fields except those listed here
- (while omit-list
- (setq all-fields (delete (car omit-list) all-fields)
- omit-list (cdr omit-list)))))
- ;; then order them
- (if (not order-list)
- (setq field-list all-fields)
- (if (not (memq t order-list))
- (setq field-list order-list)
- (setq order-list (reverse order-list))
- (setq all-fields (delete nil (mapcar (lambda (f)
- (if (memq f order-list)
- nil
- f))
- all-fields)))
- (while order-list
- (if (eq t (car order-list))
- (setq field-list (append all-fields field-list))
- (setq field-list (cons (car order-list) field-list)))
- (setq order-list (cdr order-list)))))
- ;; call the actual format function
- (setq format-function
- (intern (format "bbdb-format-record-layout-%s" layout)))
- (if (functionp format-function)
- (funcall format-function layout record field-list)
- (bbdb-format-record-layout-multi-line layout record field-list)))))
-
-(defun bbdb-frob-mode-line (n)
- (setq
- ;; identification
- mode-line-buffer-identification
- (if (> n 0)
- (list 24 (buffer-name) ": "
- (list 10 (format "%d/%d" n (length (bbdb-records))))
- '(bbdb-showing-changed-ones " !!" " "))
- (list (buffer-name) ": Insidious Big Brother Database v" bbdb-version " "
- mode-line-modified "-"))
- ;; modified indicator
- mode-line-modified
- '(bbdb-readonly-p "--%%%%-" (bbdb-modified-p "--**-" "-----"))))
-
-(defun bbdb-display-records-1 (records &optional append layout)
- (setq append (or append (bbdb-append-records-p)))
-
- (if (or (null records)
- (consp (car records)))
- nil
-
- ;; add layout and a marker to the local list of records
- (setq layout (or layout bbdb-display-layout))
- (setq records (mapcar (lambda (x)
- (list x layout (make-marker)))
- records)))
-
- (let ((b (current-buffer))
- (temp-buffer-setup-hook nil)
- (temp-buffer-show-function nil)
- (first (car (car records))))
-
- ;; just quiet a warning about unused vars
- (and temp-buffer-setup-hook temp-buffer-show-function)
-
- (bbdb-pop-up-bbdb-buffer)
-
- (save-excursion
- (set-buffer bbdb-buffer-name)
- (let ((inhibit-read-only t)) (erase-buffer))
-
- ;; If append is set, clear the buffer, otherwise do clean up.
- (unless append (bbdb-undisplay-records))
-
- ;; If we're appending these records to the ones already displayed,
- ;; then first remove any duplicates, and then sort them.
- (if append
- (let ((rest records))
- (while rest
- (if (assq (car (car rest)) bbdb-records)
- (setq records (delq (car rest) records)))
- (setq rest (cdr rest)))
- (setq records (append bbdb-records records))
- (setq records
- (sort records
- (lambda (x y) (bbdb-record-lessp (car x) (car y)))))))
- (make-local-variable 'mode-line-buffer-identification)
- (make-local-variable 'mode-line-modified)
- (set (make-local-variable 'bbdb-showing-changed-ones) nil)
- (let ((done nil)
- (rest records)
- (changed (bbdb-changed-records)))
- (while (and rest (not done))
- (setq done (memq (car (car rest)) changed)
- rest (cdr rest)))
- (setq bbdb-showing-changed-ones done))
- (bbdb-frob-mode-line (length records))
- (and (not bbdb-gag-messages)
- (not bbdb-silent-running)
- (message "Formatting..."))
- (bbdb-mode)
- ;; this in in the *BBDB* buffer, remember, not the .bbdb buffer.
- (set (make-local-variable 'bbdb-records) nil)
- (setq bbdb-records records)
- (let ((buffer-read-only nil)
- prs)
- (bbdb-debug (setq prs (bbdb-records)))
- (setq truncate-lines t)
- (while records
- (bbdb-debug (if (not (memq (car (car records)) prs))
- (error "record doubleplus unpresent!")))
- (set-marker (nth 2 (car records)) (point))
- (bbdb-format-record (nth 0 (car records))
- (nth 1 (car records)))
- (setq records (cdr records))))
- (and (not bbdb-gag-messages)
- (not bbdb-silent-running)
- (message "Formatting...done.")))
- (set-buffer bbdb-buffer-name)
- (if (and append first)
- (let ((cons (assq first bbdb-records))
- (window (get-buffer-window (current-buffer))))
- (if window (set-window-start window (nth 2 cons)))))
- (bbdbq)
- ;; this doesn't really belong here, but it's convenient ... and when
- ;; using electric display it would not be called otherwise.
- (save-excursion (run-hooks 'bbdb-list-hook))
- (if bbdb-gui (bbdb-fontify-buffer))
- (set-buffer-modified-p nil)
- (setq buffer-read-only t)
- (set-buffer b)))
-
-(defun bbdb-undisplay-records ()
- (let ((bbdb-display-buffer (get-buffer bbdb-buffer-name)))
- (if (bufferp bbdb-display-buffer)
- (save-excursion
- (set-buffer bbdb-display-buffer)
- (setq bbdb-showing-changed-ones nil
- mode-line-modified nil
- bbdb-records nil
- buffer-read-only nil)
- (erase-buffer)
- (setq buffer-read-only t)
- (set-buffer-modified-p nil)))))
-
- ;;; Electric display stuff
-
-(defvar bbdb-inside-electric-display nil)
-;; hack hack: a couple of specials that the electric stuff uses for state.
-(defvar bbdb-electric-execute-me)
-(defvar bbdb-electric-completed-normally)
-
-(defun electric-bbdb-display-records (records)
- (require 'electric)
- (let ((bbdb-electric-execute-me nil)) ; Hack alert! throw-to-execute sets this!
- (let ((bbdb-inside-electric-display t)
- buffer
- bbdb-electric-completed-normally ; Hack alert! throw-to-execute sets this!
- )
- (save-excursion
- (save-window-excursion
- (save-window-excursion (bbdb-display-records-1 records))
- (setq buffer (window-buffer (Electric-pop-up-window bbdb-buffer-name)))
- (set-buffer buffer)
- (if (not bbdb-gag-messages)
- (message "<<< Press Space to bury the Insidious Big Brother Database list >>>"))
- (catch 'Done
- (while t
- (catch 'Blow-off-the-error
- (setq bbdb-electric-completed-normally nil)
- (unwind-protect
- (progn
- (catch 'electric-bbdb-list-select
- (Electric-command-loop 'electric-bbdb-list-select
- "-> " t))
- (setq bbdb-electric-completed-normally t))
- ;; protected
- (if bbdb-electric-completed-normally
- (throw 'Done t)
- (ding)
- (message "BBDB-Quit")
- (throw 'Blow-off-the-error t)
- )))))
- (bury-buffer buffer))))
- (message " ")
- (if bbdb-electric-execute-me
- (eval bbdb-electric-execute-me)))
- nil)
-
-(defun bbdb-electric-throw-to-execute (form-to-execute)
- "Exit the `electric-command-loop' and evaluate the given form."
- ;; Hack alert! These variables are bound only within the scope of
- ;; bbdb-electric-display-records!
- (if (not (boundp 'bbdb-electric-execute-me))
- (error "plusungood: electrical short"))
- (setq bbdb-electric-execute-me form-to-execute
- bbdb-electric-completed-normally t)
- (throw 'electric-bbdb-list-select t))
-
-
-(defun bbdb-done-command () (interactive)
- (throw 'electric-bbdb-list-select t))
-
-(defun bbdb-bury-buffer ()
- (interactive)
- (if bbdb-inside-electric-display
- (bbdb-done-command)
- (bury-buffer)))
-
-(defun bbdb-display-records (records &optional layout append)
- (let ((bbdb-window (get-buffer-window bbdb-buffer-name)))
- (if (and bbdb-electric-p
- ;; never be electric if the buffer is already on screen.
- (not bbdb-window))
- (progn
- (define-key bbdb-mode-map " " 'bbdb-done-command)
- (electric-bbdb-display-records records))
- (bbdb-display-records-1 records append layout)
- ;; don't smash keybinding if they invoked `bbdb-display'
- ;; from inside an electric loop.
- (unless bbdb-inside-electric-display
- (define-key bbdb-mode-map " " 'undefined))
- (if (and (not bbdb-gag-messages)
- (not bbdb-window))
- (message
- (substitute-command-keys
- (if (one-window-p t)
- (if pop-up-windows
- "Type \\[delete-other-windows] to unshow the bbdb-list window."
- "Type \\[switch-to-buffer] RET to unshow the bbdb-list window.")
- "Type \\[switch-to-buffer-other-window] RET to restore old contents of the bbdb-list window.")))))))
-
-(defun bbdbq ()
- (if (not (zerop (logand (random) 31))) nil
- (let ((v '["\104\157\156\47\164\40\163\165\163\160\145\143\164\40\171\157\
-\165\162\40\156\145\151\147\150\142\157\162\72\40\162\145\160\157\162\164\40\
-\150\151\155\41" "\146\156\157\162\144" "\103\157\156\163\165\155\145\40\55\55\
-\40\102\145\40\123\151\154\145\156\164\40\55\55\40\104\151\145" "\114\157\166\
-\145\40\102\151\147\40\102\162\157\164\150\145\162" "\114\145\145\40\110\141\
-\162\166\145\171\40\117\163\167\141\154\144\40\141\143\164\145\144\40\141\154\
-\157\156\145" "\101\114\114\40\131\117\125\122\40\102\101\123\105\40\101\122\
-\105\40\102\105\114\117\116\107\40\124\117\40\125\123" "\127\141\162\40\151\
-\163\40\120\145\141\143\145" "\106\162\145\145\144\157\155\40\151\163\40\123\
-\154\141\166\145\162\171" "\111\147\156\157\162\141\156\143\145\40\151\163\40\
-\123\164\162\145\156\147\164\150" "\120\162\157\154\145\163\40\141\156\144\40\
-\141\156\151\155\141\154\163\40\141\162\145\40\146\162\145\145"]))
- (message (aref v (% (logand 255 (random)) (length v))))
- (message " "))))
-
-(defmacro bbdb-hashtable ()
- '(bbdb-with-db-buffer (bbdb-records nil t) bbdb-hashtable))
-
-(defun bbdb-changed-records ()
- (bbdb-with-db-buffer (bbdb-records nil t) bbdb-changed-records))
-
-(defmacro bbdb-build-name (f l)
- (list 'downcase
- (list 'if (list '= (list 'length f) 0) l
- (list 'if (list '= (list 'length l) 0) f
- (list 'concat f " " l)))))
-
-(defun bbdb-remove! (e l)
- (if (null l) l
- (let ((ret l)
- (n (cdr l)))
- (while n
- (if (eq e (car n))
- (setcdr l (cdr n)) ; skip n
- (setq l n)) ; keep n
- (setq n (cdr n)))
- (if (eq e (car ret)) (cdr ret)
- ret))))
-
-(defun bbdb-remove-memq-duplicates (l)
- (let (ret tail)
- (setq ret (cons '() '())
- tail ret)
- (while l
- (if (not (memq (car l) ret))
- (setq tail (setcdr tail (cons (car l) '()))))
- (setq l (cdr l)))
- (cdr ret)))
-
-(defmacro bbdb-gethash (name &optional ht)
- (list 'symbol-value
- (list 'intern-soft name
- (or ht '(bbdb-hashtable)))))
-
-(defmacro bbdb-puthash (name record &optional ht)
- (list 'let (list (list 'sym (list 'intern name (or ht '(bbdb-hashtable)))))
- (list 'set 'sym (list 'cons record
- '(and (boundp sym) (symbol-value sym))))))
-
-(defmacro bbdb-remhash (name record &optional ht)
- (list 'let (list (list 's (list 'intern-soft name
- (or ht '(bbdb-hashtable)))))
- (list 'and 's (list 'set 's (list 'bbdb-remove! record
- (list 'symbol-value 's))))))
-
-(defsubst bbdb-search-intertwingle (name net)
- "Find bbdb records matching NAME and NET.
-
-This is a more stringent version of bbdb-search-simple, which I am
-not inclined to modify for fear of damaging other code that currently
-relies on it. BBDB internals should be migrated to use this function
-to identify which record is referred to by a name/net combination,
-since search-simple has been overloaded with other functionality.
-
-The name comes from
-http://www.mozilla.org/blue-sky/misc/199805/intertwingle.html, which
-any budding BBDB hacker should be at least vaguely familiar with."
- (bbdb-records t)
- (if name (setq name (downcase name)))
- (if net (setq net (downcase net))
- (setq net ""))
- (let ((net-recs (bbdb-gethash (downcase net)))
- recs)
- (while net-recs
- (if (or (and (not name) net)
- (string= name (downcase (bbdb-record-name (car net-recs)))))
- (add-to-list 'recs (car net-recs)))
- (setq net-recs (cdr net-recs)))
- recs))
-
-(defsubst bbdb-search-simple (name net)
- "name is a string; net is a string or list of strings."
- (if (eq 0 (length name)) (setq name nil))
- (if (eq 0 (length net)) (setq net nil))
- (bbdb-records t) ; make sure db is parsed; don't check disk (faster)
- (let ((name-recs (if name ;; filter out companies from hash
- (let ((recs (bbdb-gethash (downcase name)))
- answer)
- (while recs
- (let ((n-rec (car recs)))
- (if (string= (downcase name)
- (downcase
- (or (bbdb-record-name
- n-rec)
- (bbdb-record-company
- n-rec)
- "")))
- (setq answer (append recs (list n-rec))))
- (setq recs (cdr recs))))
- answer)))
- (net-recs (if (stringp net) (bbdb-gethash (downcase net))
- (let (answer)
- (while (and net (null answer))
- (setq answer (bbdb-gethash (downcase (car net)))
- net (cdr net)))
- answer)))
- ret)
- (if (not (and name-recs net-recs))
- (or (and name-recs (car name-recs))
- (and net-recs (car net-recs)))
-
- (while name-recs
- (let ((name-rec (car name-recs))
- (nets net-recs))
- (while nets
- (if (eq (car nets) name-rec)
- (setq nets '()
- name-recs '()
- ret name-rec)
- (setq nets (cdr nets))))
- (if name-recs (setq name-recs (cdr name-recs))
- name-rec)))
- ret)))
-
-(defun bbdb-net-convert (record)
- "Given a record whose net field is a comma-separated string, convert it to
-a list of strings (the new way of doing things.) Returns the new list."
- (bbdb-record-set-net record (bbdb-split (bbdb-record-net record) ",")))
-
-(defun bbdb-split (string separators)
- "Return a list by splitting STRING at SEPARATORS.
-The inverse function of `bbdb-join'."
- (let (result
- (not-separators (concat "^" separators)))
- (save-excursion
- (set-buffer (get-buffer-create " *split*"))
- (erase-buffer)
- (insert string)
- (goto-char (point-min))
- (while (progn
- (skip-chars-forward separators)
- (skip-chars-forward " \t\n\r")
- (not (eobp)))
- (let ((begin (point))
- p)
- (skip-chars-forward not-separators)
- (setq p (point))
- (skip-chars-backward " \t\n\r")
- (setq result (cons (buffer-substring begin (point)) result))
- (goto-char p)))
- (erase-buffer))
- (nreverse result)))
-
-(defun bbdb-join (list separator)
- "Join a LIST to a string where the list elements are separated by SEPARATOR.
-The inverse function of `bbdb-split'."
- (when list
- (mapconcat 'identity list separator)))
-
-(defsubst bbdb-hash-record (record)
- "Insert the record in the appropriate hashtables. This must be called
-while the .bbdb buffer is selected."
- (let ((name (bbdb-record-name-1 record)) ; faster version
- (lfname (bbdb-record-lfname record))
- (company (bbdb-record-company record))
- (aka (bbdb-record-aka record))
- (net (bbdb-record-net record)))
- (if (> (length name) 0)
- (bbdb-puthash (downcase name) record bbdb-hashtable))
- (if (> (length lfname) 0)
- (bbdb-puthash (downcase lfname) record bbdb-hashtable))
- (if (> (length company) 0)
- (bbdb-puthash (downcase company) record bbdb-hashtable))
- (while aka
- (bbdb-puthash (downcase (car aka)) record bbdb-hashtable)
- (setq aka (cdr aka)))
- (while net
- (bbdb-puthash (downcase (car net)) record bbdb-hashtable)
- (setq net (cdr net)))))
-
-
-;;; Reading the BBDB
-
-(defvar inside-bbdb-records nil
- "Internal variable. Do not touch.")
-
-(defvar bbdb-write-file-hooks '(bbdb-write-file-hook-fn)
- "*The list of functions added to `local-write-file-hooks' in `bbdb-file'.")
-
-(defun bbdb-records (&optional dont-check-disk already-in-db-buffer)
- "Return a list of all bbdb records; read in and parse the db if necessary.
-This also notices if the disk file has changed out from under us, unless
-optional arg DONT-CHECK-DISK is non-nil (which is faster, but hazardous.)"
- (if inside-bbdb-records
- (let ((debug-on-error t))
- (error "catastrophic: bbdb-records recursed")))
- (let ((inside-bbdb-records t)
- (buf (if already-in-db-buffer (current-buffer) (bbdb-buffer)))
- shut-up)
- (with-current-buffer buf
- ;; make sure the BBDB in memory is not out of synch with disk.
- (cond (dont-check-disk nil)
- ((verify-visited-file-modtime buf) nil)
- ((and bbdb-auto-revert-p (not (buffer-modified-p buf)))
- (message "BBDB has changed on disk, reverting...")
- (setq shut-up t)
- (revert-buffer t t))
- ;; hassle the user
- ((bbdb-yes-or-no-p
- (if (buffer-modified-p buf)
- "BBDB has changed on disk; flush your changes and revert? "
- "BBDB has changed on disk; revert? "))
- (or (file-exists-p bbdb-file)
- (error "bbdb: file %s no longer exists!!" bbdb-file))
- (revert-buffer t t))
- ;; this is the case where the .bbdb file has changed; the buffer
- ;; has changed as well; and the user has answered "no" to the
- ;; "flush your changes and revert" question. The only other
- ;; alternative is to save the file right now. If they answer
- ;; no to the following question, they will be asked the
- ;; preceeding question again and again some large (but finite)
- ;; number of times. `bbdb-records' is called a lot, you see...
- ((buffer-modified-p buf)
- ;; this prompts
- (bbdb-save-db t t))
- ;; otherwise, the buffer and file are inconsistent, but we let
- ;; them stay that way.
- )
- (unless (assq 'bbdb-records (buffer-local-variables))
- (set (make-local-variable 'bbdb-records) nil)
- (set (make-local-variable 'bbdb-changed-records) nil)
- (set (make-local-variable 'bbdb-end-marker) nil)
- (set (make-local-variable 'bbdb-hashtable) nil)
- (set (make-local-variable 'bbdb-propnames) nil)
- (set (make-local-variable 'revert-buffer-function)
- 'bbdb-revert-buffer)
- (bbdb-mapc (lambda (ff) (add-hook 'local-write-file-hooks ff))
- bbdb-write-file-hooks)
- (setq bbdb-hashtable (make-vector bbdb-hashtable-size 0)))
- (setq bbdb-modified-p (buffer-modified-p)
- buffer-read-only bbdb-readonly-p)
- (or bbdb-records
- (cond ((= (point-min) (point-max)) ; special-case empty db
- ;; this doesn't need to be insert-before-markers because
- ;; there are no db-markers in this buffer.
- (insert (format ";; -*-coding: %s;-*-\n;;; file-version: %d\n"
- bbdb-file-coding-system bbdb-file-format))
- (bbdb-flush-all-caches)
- (setq bbdb-end-marker (point-marker))
- ;;(run-hooks 'bbdb-after-read-db-hook) ; run this?
- nil)
- (t
- (or shut-up bbdb-silent-running (message "Parsing BBDB..."))
- (bbdb-flush-all-caches)
- (cond ((and bbdb-notice-auto-save-file
- (file-newer-than-file-p (make-auto-save-file-name)
- buffer-file-name))
- (if (bbdb-yes-or-no-p "BBDB auto-save file is newer; recover it? ")
- (progn
- (recover-file buffer-file-name)
- (bury-buffer (current-buffer)) ; recover-file selects it
- (auto-save-mode 1) ; turn autosave back on
- (delete-file (make-auto-save-file-name))
- (message "Auto-save mode is ON in BBDB buffer. Suggest you save it soon.")
- (sleep-for 2))
- ;; delete auto-save anyway, so we don't keep asking.
- (condition-case nil
- (delete-file (make-auto-save-file-name))
- (file-error nil)))
- ;; tail-recurse and try again
- (let ((inside-bbdb-records nil))
- (bbdb-records)))
- (t
- ;; normal case
- (fillarray bbdb-hashtable 0)
- (parse-bbdb-internal)))))))))
-
-(defun bbdb-revert-buffer (arg noconfirm)
- ;; The .bbdb file's revert-buffer-function.
- ;; Don't even think of calling this.
- (kill-all-local-variables) ; clear db and caches.
- (if (get-buffer bbdb-buffer-name) ; now contains invalid records; nukem.
- (bbdb-undisplay-records))
- (let ((revert-buffer-function nil)) ; don't loop.
- (revert-buffer arg noconfirm)))
-
-(defun parse-bbdb-internal ()
- (bbdb-debug (message "Parsing BBDB... (reading...)"))
- (widen)
- (goto-char (point-min))
- ;; go to the point at which the first record begins
- (cond ((eq (following-char) ?\[) nil)
- ((search-forward "\n[" nil 0) (forward-char -1))
- (t nil)) ;; no records
- ;; look backwards for user-defined field names (for completion purposes.)
- (save-excursion
- (if (re-search-backward "^;+[ \t]*user-fields:[ \t]*\(" nil t)
- (progn
- (goto-char (1- (match-end 0)))
- (setq bbdb-propnames
- (mapcar (lambda (x) (list (symbol-name x)))
- (read (point-marker)))))))
- ;; look backwards for file version, and convert if necessary.
- ;; (at least, I'll write this code if I ever change the file format again...)
- (let ((v (save-excursion
- (if (re-search-backward
- "^;+[ \t]*file-version:[ \t]*\\([0-9]+\\)[ \t]*$" nil t)
- (car (read-from-string
- (buffer-substring
- (match-beginning 1) (match-end 1))))))))
- (if (null v) ; current version, but no file-version: line. Bootstrap it.
- (let ((modp (buffer-modified-p)))
- ;; This should never happen (not any more, anyway...)
- (bbdb-debug (error "bbdb corrupted: no file-version line"))
- (setq v 2)
- (save-excursion
- (if (re-search-backward "^;" nil t)
- (forward-line 1)
- (goto-char 1))
- ;; remember, this goes before the begin-marker of the first
- ;; record in the database!
- (insert-before-markers
- (format ";; -*-coding: %s;-*-\n;;; file-version: %d\n"
- bbdb-file-coding-system bbdb-file-format)))
- (set-buffer-modified-p modp)))
- (cond ((< v bbdb-file-format)
- (if bbdb-file-format-migration
- ;; Sanity checking.
- (if (/= (car bbdb-file-format-migration) v)
- (error
- (format
- "BBDB file format has changed on disk from %d to %d!"
- (car bbdb-file-format-migration) v)))
- (setq bbdb-file-format-migration
- (cons v (bbdb-migration-query v)))))
- ((> v bbdb-file-format)
- (error "BBDB version %s doesn't understand file format version %s."
- bbdb-version v))
- (t (setq bbdb-file-format-migration (cons bbdb-file-format
- bbdb-file-format)))))
- ;; A trap to catch a bug
- ;;(assert (not (null (car bbdb-file-format-migration))))
-
- (bbdb-debug
- (or (eobp) (looking-at "[\[]")
- (error "no following bracket: bbdb corrupted"))
- (if (save-excursion
- (save-restriction
- (widen)
- (save-excursion (search-backward "\n[" nil t))))
- (error "bbdb corrupted: records before point")))
-
- ;; Migrate only if we need to. Change the .bbdb buffer only if it
- ;; is not to be saved in the newest version.
- (if (= (car bbdb-file-format-migration) bbdb-file-format)
- (parse-bbdb-frobnicate (parse-bbdb-read))
- (let ((newrecs (parse-bbdb-frobnicate (bbdb-migrate (parse-bbdb-read)))))
- (cond ((= (cdr bbdb-file-format-migration) bbdb-file-format)
- (bbdb-migrate-rewrite-all nil newrecs)
- (bbdb-migrate-update-file-version
- (car bbdb-file-format-migration)
- (cdr bbdb-file-format-migration))))
- newrecs)))
-
-(defun parse-bbdb-read ()
- ;; narrow the buffer to skip over the rubbish before the first record.
- (narrow-to-region (point) (point-max))
- (let ((records nil))
- ;; insert parens so we can read the db in one fell swoop (down in C).
- (let ((buffer-read-only nil)
- (modp (buffer-modified-p))
- ;; Make sure those parens get cleaned up.
- ;; This code had better stay simple!
- (inhibit-quit t))
- (goto-char (point-min)) (insert "(\n")
- (goto-char (point-max)) (insert "\n)")
- (goto-char (point-min))
- (setq records (read (current-buffer)))
- (goto-char (point-min)) (delete-char 2)
- (goto-char (point-max)) (delete-char -2)
- (set-buffer-modified-p modp))
- records))
-
-(defun parse-bbdb-frobnicate (records)
- ;; now we have to come up with a marker for each record. Rather than
- ;; calling read for each record, we read them at once (already done) and
- ;; assume that the markers are at each newline. If this isn't the case,
- ;; things can go *very* wrong.
- (goto-char (point-min))
- (while (looking-at "[ \t\n\f]*;")
- (forward-line 1))
- (widen)
- (bbdb-debug (message "Parsing BBDB... (frobnicating...)"))
- (setq bbdb-records records)
- (let* ((head (cons '() records))
- (rest head)
- record)
- (while (cdr rest)
- (setq record (car (cdr rest)))
- ;; yow, are we stack-driven yet?? Damn byte-compiler...
- ;; Make a cache. Put it in the record. Put a marker in the cache.
- ;; Add record to hash tables.
- (bbdb-cache-set-marker
- (bbdb-record-set-cache record (make-vector bbdb-cache-length nil))
- (point-marker))
- (forward-line 1)
-
- ;; frob the label completion lists (and data completion when I,
- ;; uh, get around to it, maybe. this stuff should probably be
- ;; conditional, in case you're not running a 42GHz Pentium 69
- ;; with chrome tailpipes)
- (let ((ps (bbdb-record-phones record))
- (pl (bbdb-label-completion-list "phones"))
- (as (bbdb-record-addresses record))
- (al (bbdb-label-completion-list "addresses")))
- (while ps
- (let ((l (bbdb-phone-location (car ps))))
- (or (member l pl)
- (setq bbdb-phones-label-list
- (append (or bbdb-phones-label-list
- bbdb-default-label-list)
- (list l))
- pl bbdb-phones-label-list)))
- (setq ps (cdr ps)))
- ;; Yes, I cut and pasted.
- (while as
- (let ((l (bbdb-address-location (car as))))
- (or (member l al)
- (setq bbdb-addresses-label-list
- (append (or bbdb-addresses-label-list
- bbdb-default-label-list)
- (list l))
- al bbdb-addresses-label-list)))
- (setq as (cdr as))))
-
- (if bbdb-no-duplicates-p
- ;; warn the user that there is a duplicate...
- (let* ((name (bbdb-record-name record))
- (tmp (and name (bbdb-gethash (downcase name)
- bbdb-hashtable))))
- (if tmp (message "Duplicate BBDB record encountered: %s" name))))
-
- (bbdb-hash-record record)
- (setq rest (cdr rest))
-
- (bbdb-debug
- (if (and (cdr rest) (not (looking-at "[\[]")))
- (error "bbdb corrupted: junk between records at %s" (point)))))
- ;; In case we removed some of the leading entries...
- (setq bbdb-records (cdr head)))
- ;; all done.
- (setq bbdb-end-marker (point-marker))
- (run-hooks 'bbdb-after-read-db-hook)
- (bbdb-debug (message "Parsing BBDB... (frobnicating...done)"))
- bbdb-records)
-
-(defmacro bbdb-user-mail-names ()
- "Returns a regexp matching the address of the logged-in user."
- '(or bbdb-user-mail-names
- (setq bbdb-user-mail-names
- (concat "\\b" (regexp-quote (user-login-name)) "\\b"))))
-
-(defun bbdb-write-file-hook-fn ()
- "This is just for `bbdb-write-file-hooks'. Keep it there."
- ;; this is premature as the file isn't actually written yet; but it's just
- ;; for the benefit of the mode-line of the *BBDB* buffer, and there isn't
- ;; an after-write-file-hook, so it'll do.
- (save-restriction
- (widen)
- (goto-char (point-min))
-
- ;; this always rewrites the coding cookie, which is a bit
- ;; wasteful, but safer than alternatives
- (if (looking-at ";; *-\\*-coding:")
- (delete-region (point) (progn (forward-line) (point))))
- (insert-before-markers (format ";; -*-coding: %s;-*-\n"
- bbdb-file-coding-system)))
- (setq bbdb-modified-p nil
- bbdb-changed-records nil
- buffer-file-coding-system bbdb-file-coding-system)
- (let ((buf (get-buffer bbdb-buffer-name)))
- (when buf
- (with-current-buffer buf
- (setq bbdb-showing-changed-ones nil)
- (set-buffer-modified-p nil))))
- (when (and bbdb-file-remote
- (or bbdb-file-remote-save-always
- (y-or-n-p (format "Save the remote BBDB file %s too? "
- bbdb-file-remote))))
- ;; write the current buffer, which is `bbdb-file' (since this is called
- ;; from its `local-write-file-hooks'), into the `bbdb-file-remote'.
- (let ((coding-system-for-write bbdb-file-coding-system))
- (write-region (point-min) (point-max) bbdb-file-remote))))
-
-(defun bbdb-delete-record-internal (record)
- (if (null (bbdb-record-marker record)) (error "bbdb: marker unpresent"))
- (bbdb-with-db-buffer
- (if (or bbdb-suppress-changed-records-recording
- (memq record bbdb-changed-records))
- nil
- (setq bbdb-changed-records (cons record bbdb-changed-records)))
- (let ((tail (memq record bbdb-records)))
- (if (null tail) (error "bbdb: unfound %s" record))
- (setq bbdb-records (delq record bbdb-records))
- (delete-region (bbdb-record-marker record)
- (if (cdr tail)
- (bbdb-record-marker (car (cdr tail)))
- bbdb-end-marker))
- (let ((name (bbdb-record-name record))
- (lfname (bbdb-record-lfname record))
- (company (bbdb-record-company record))
- (aka (bbdb-record-aka record))
- (nets (bbdb-record-net record)))
- (if (> (length name) 0)
- (bbdb-remhash (downcase name) record bbdb-hashtable))
- (if (> (length company) 0)
- (bbdb-remhash (downcase company) record bbdb-hashtable))
- (if (> (length lfname) 0)
- (bbdb-remhash (downcase lfname) record bbdb-hashtable))
- (while nets
- (bbdb-remhash (downcase (car nets)) record bbdb-hashtable)
- (setq nets (cdr nets)))
- (while aka
- (bbdb-remhash (downcase (car aka)) record bbdb-hashtable)
- (setq aka (cdr aka)))
- )
- (bbdb-record-set-sortkey record nil)
- (setq bbdb-modified-p t))))
-
-(defun bbdb-insert-sorted (record records)
- "Inserts the RECORD into the list of RECORDS, in order.
-Assumes the list is already sorted. Returns the new head."
- (bbdb-debug (if (memq record records)
- (error "doubleplus ununique: - %s" record)))
- (let* ((rest (cons nil records))
- (top rest))
- (while (and (cdr rest)
- (bbdb-record-lessp (nth 1 rest) record))
- (setq rest (cdr rest)))
- (setcdr rest (cons record (cdr rest)))
- (cdr top)))
-
-(defun bbdb-insert-record-internal (record unmigrated)
- (if (null (bbdb-record-marker record))
- (bbdb-record-set-marker record (make-marker)))
- (bbdb-with-db-buffer
- (if (or bbdb-suppress-changed-records-recording
- (memq record bbdb-changed-records))
- nil
- (setq bbdb-changed-records (cons record bbdb-changed-records)))
- (let ((print-escape-newlines t))
- (bbdb-record-set-sortkey record nil) ; just in case...
- (setq bbdb-records
- (bbdb-insert-sorted record bbdb-records))
- (let ((next (car (cdr (memq record bbdb-records)))))
- (goto-char (if next
- (bbdb-record-marker next)
- bbdb-end-marker))
- ;; before printing the record, remove the cache \(we don't want that
- ;; written to the file.\) Ater writing, put the cache back and update
- ;; the cache's marker.
- (let ((cache (bbdb-record-cache record))
- (point (point)))
- (bbdb-debug
- (if (= point (point-min))
- (error "doubleplus ungood: inserting at point-min (%s)" point))
- (if (and (/= point bbdb-end-marker)
- (not (looking-at "[\[]")))
- (error "doubleplus ungood: not inserting before a record (%s)"
- point)))
- (bbdb-record-set-cache record nil)
- (if unmigrated (bbdb-record-set-cache unmigrated nil))
- (insert-before-markers (bbdb-prin1-to-string (or unmigrated record)) "\n")
- (set-marker (bbdb-cache-marker cache) point)
- (bbdb-record-set-cache record cache)
-;; (if (bbdb-record-name record)
-;; (bbdb-puthash (downcase (bbdb-record-name record)) record bbdb-hashtable))
-;; (let ((nets (bbdb-record-net record)))
-;; (while nets
-;; (bbdb-puthash (downcase (car nets)) record bbdb-hashtable)
-;; (setq nets (cdr nets))))
- ;; This is marginally slower because it rebuilds the namecache,
- ;; but it makes jbw's life easier. :-\)
- (bbdb-hash-record record))
- record))
- (setq bbdb-modified-p t)))
-
-(defun bbdb-overwrite-record-internal (record unmigrated)
- (bbdb-with-db-buffer
- (if (or bbdb-suppress-changed-records-recording
- (memq record bbdb-changed-records))
- nil
- (setq bbdb-changed-records (cons record bbdb-changed-records)))
- (let ((print-escape-newlines t)
- (tail bbdb-records))
- ;; Look for record after RECORD in the database. Use the
- ;; beginning marker of this record (or the marker for the end of
- ;; the database if no next record) to determine where to stop
- ;; deleting old copy of record
- (while (and tail (not (eq record (car tail))))
- (setq tail (cdr tail)))
- (if (null tail) (error "bbdb: unfound %s" record))
- (let ((cache (bbdb-record-cache record)))
-
- (bbdb-debug
- (if (<= (bbdb-cache-marker cache) (point-min))
- (error "doubleplus ungood: cache marker is %s"
- (bbdb-cache-marker cache)))
- (goto-char (bbdb-cache-marker cache))
- (if (and (/= (point) bbdb-end-marker)
- (not (looking-at "[\[]")))
- (error "doubleplus ungood: not inserting before a record (%s)"
- (point))))
-
- (goto-char (bbdb-cache-marker cache))
- (bbdb-record-set-cache record nil)
- (if unmigrated (bbdb-record-set-cache unmigrated nil))
-
- (insert (bbdb-prin1-to-string (or unmigrated record)) "\n")
- (delete-region (point)
- (if (cdr tail)
- (bbdb-record-marker (car (cdr tail)))
- bbdb-end-marker))
- (bbdb-record-set-cache record cache)
-
- (bbdb-debug
- (if (<= (if (cdr tail)
- (bbdb-record-marker (car (cdr tail)))
- bbdb-end-marker)
- (bbdb-record-marker record))
- (error "doubleplus ungood: overwrite unworks")))
-
- (setq bbdb-modified-p t)
- record))))
-
-(defvar inside-bbdb-change-record nil "hands off")
-(defvar inside-bbdb-notice-hook nil
- "Internal variable; hands off.
-Set to t by the BBDB when inside the `bbdb-notice-hook'.
-
-Calls to the `bbdb-change-hook' are suppressed when this is non-nil.")
-
-(defun bbdb-change-record (record need-to-sort)
- "Update the database after a change to the given record. Second arg
-NEED-TO-SORT is whether the name has changed. You still need to worry
-about updating the name hash-table."
- (if inside-bbdb-change-record
- record
- (let ((inside-bbdb-change-record t)
- unmigrated)
- (or inside-bbdb-notice-hook
- (bbdb-invoke-hook 'bbdb-change-hook record))
- (bbdb-debug (if (bbdb-record-deleted-p record)
- (error "bbdb: changing deleted record")))
- (if (/= (cdr bbdb-file-format-migration) bbdb-file-format)
- (bbdb-unmigrate-record (setq unmigrated (bbdb-copy-thing record))))
- ;; Do the changing
- (if (memq record (bbdb-records)) ; checks file synchronization too.
- (if (not need-to-sort) ;; If we don't need to sort, overwrite it.
- (progn
- (bbdb-overwrite-record-internal record unmigrated)
- (bbdb-debug
- (if (not (memq record (bbdb-records)))
- (error "Overwrite in change doesn't work"))))
- ;; Since we do need to sort, delete then insert
- (bbdb-delete-record-internal record)
- (bbdb-debug
- (if (memq record (bbdb-records))
- (error "Delete in need-sort change doesn't work")))
- (bbdb-insert-record-internal record unmigrated)
- (bbdb-debug
- (if (not (memq record (bbdb-records)))
- (error "Insert in need-sort change doesn't work"))))
- ;; Record isn't in database so add it.
- (bbdb-insert-record-internal record unmigrated)
- (bbdb-debug (if (not (memq record (bbdb-records)))
- (error "Insert in change doesn't work"))))
- (setq bbdb-modified-p t)
- (bbdb-invoke-hook 'bbdb-after-change-hook record)
- record)))
-
-(defun bbdb-copy-thing (thing)
- "Copy a thing. Handles vectors, strings, markers, numbers, conses,
-lists, symbols, and nil. Raises an error if it finds something it
-doesn't know how to deal with."
- (cond ((vectorp thing)
- (let ((i 0)
- (newvec (make-vector (length thing) nil)))
- (while (< i (length thing))
- (aset newvec i (bbdb-copy-thing (aref thing i)))
- (setq i (1+ i)))
- newvec))
- ((stringp thing)
- (copy-sequence thing))
- ((markerp thing)
- (copy-marker thing))
- ((numberp thing)
- thing)
- ((consp thing)
- (cons (bbdb-copy-thing (car thing))
- (bbdb-copy-thing (cdr thing))))
- ((listp thing)
- (let ((i 0) newlist)
- (while (< i (length thing))
- (setq newlist (append newlist (list (bbdb-copy-thing
- (nth i thing))))
- i (1+ i)))
- newlist))
- ((symbolp thing)
- thing)
- ((eq nil thing)
- nil)
- (t
- (error "Don't know how to copy %s" (prin1-to-string thing)))))
-
-(defmacro bbdb-propnames ()
- '(bbdb-with-db-buffer bbdb-propnames))
-
-(defun bbdb-set-propnames (newval)
- (bbdb-with-db-buffer
- (setq bbdb-propnames newval)
- (widen)
- (goto-char (point-min))
- (and (not (eq (following-char) ?\[))
- (search-forward "\n[" nil 0))
- (if (re-search-backward "^[ \t]*;+[ \t]*user-fields:[ \t]*\(" nil t)
- (progn
- (goto-char (1- (match-end 0)))
- (delete-region (point) (progn (end-of-line) (point))))
- (and (re-search-backward "^[ \t]*;.*\n" nil t)
- (goto-char (match-end 0)))
- ;; remember, this goes before the begin-marker of the first
- ;; record in the database!
- (insert-before-markers ";;; user-fields: \n")
- (forward-char -1))
- (bbdb-prin1 (mapcar (lambda (x) (intern (car x)))
- bbdb-propnames)
- (current-buffer))
- bbdb-propnames))
-
-
-;;; BBDB mode
-
-(defun bbdb-mode ()
- "Major mode for viewing and editing the Insidious Big Brother Database.
-Letters no longer insert themselves. Numbers are prefix arguments.
-You can move around using the usual cursor motion commands.
-\\<bbdb-mode-map>
-\\[bbdb-add-or-remove-mail-alias]\t Add new mail alias to visible records or \
-remove it.
-\\[bbdb-edit-current-field]\t Edit the field on the current line.
-\\[bbdb-record-edit-notes]\t Edit the `notes' field for the current record.
-\\[bbdb-delete-current-field-or-record]\t Delete the field on the \
-current line. If the current line is the\n\t first line of a record, then \
-delete the entire record.
-\\[bbdb-insert-new-field]\t Insert a new field into the current record. \
-Note that this\n\t will let you add new fields of your own as well.
-\\[bbdb-transpose-fields]\t Swap the field on the current line with the \
-previous field.
-\\[bbdb-dial]\t Dial the current phone field.
-\\[bbdb-next-record], \\[bbdb-prev-record]\t Move to the next or the previous \
-displayed record, respectively.
-\\[bbdb-create]\t Create a new record.
-\\[bbdb-toggle-records-display-layout]\t Toggle whether the current record is displayed in a \
-one-line\n\t listing, or a full multi-line listing.
-\\[bbdb-apply-next-command-to-all-records]\\[bbdb-toggle-records-display-layout]\t Do that \
-for all displayed records.
-\\[bbdb-refile-record]\t Merge the contents of the current record with \
-some other, and then\n\t delete the current record. See this command's \
-documentation.
-\\[bbdb-omit-record]\t Remove the current record from the display without \
-deleting it from\n\t the database. This is often a useful thing to do \
-before using one\n\t of the `*' commands.
-\\[bbdb]\t Search for records in the database (on all fields).
-\\[bbdb-net]\t Search for records by net address.
-\\[bbdb-company]\t Search for records by company.
-\\[bbdb-notes]\t Search for records by note.
-\\[bbdb-name]\t Search for records by name.
-\\[bbdb-changed]\t Display records that have changed since the database \
-was saved.
-\\[bbdb-send-mail]\t Compose mail to the person represented by the \
-current record.
-\\[bbdb-apply-next-command-to-all-records]\\[bbdb-send-mail]\t Compose mail \
-to everyone whose record is displayed.
-\\[bbdb-finger]\t Finger the net address of the current record.
-\\[bbdb-ftp]\t FTP to the curent records's `ftp' field.
-\\[bbdb-apply-next-command-to-all-records]\\[bbdb-finger]\t Finger the \
-net address of all displayed records.
-\\[bbdb-save-db]\t Save the BBDB file to disk.
-\\[bbdb-print]\t Create a TeX file containing a pretty-printed version \
-of all the\n\t records in the database.
-\\[bbdb-apply-next-command-to-all-records]\\[bbdb-print]\t Do that for the \
-displayed records only.
-\\[other-window]\t Move to another window.
-\\[bbdb-info]\t Read the Info documentation for BBDB.
-\\[bbdb-help]\t Display a one line command summary in the echo area.
-\\[bbdb-www]\t Visit Web sites listed in the `www' field(s) of the current \
-record.
-\\[bbdb-whois]\t run whois on the current record.
-
-For address completion using the names and net addresses in the database:
-\t in Sendmail mode, type \\<mail-mode-map>\\[bbdb-complete-name].
-\t in Message mode, type \\<message-mode-map>\\[bbdb-complete-name].
-
-Variables of note:
-\t bbdb-always-add-addresses
-\t bbdb-auto-revert-p
-\t bbdb-canonicalize-redundant-nets-p
-\t bbdb-case-fold-search
-\t bbdb-completion-type
-\t bbdb-default-area-code
-\t bbdb-default-domain
-\t bbdb-electric-p
-\t bbdb-display-layout
-\t bbdb-file
-\t bbdb-message-caching-enabled
-\t bbdb-new-nets-always-primary
-\t bbdb-north-american-phone-numbers-p
-\t bbdb-notice-auto-save-file
-\t bbdb-offer-save
-\t bbdb-pop-up-display-layout
-\t bbdb-pop-up-target-lines
-\t bbdb-quiet-about-name-mismatches
-\t bbdb-readonly-p
-\t bbdb-use-alternate-names
-\t bbdb-use-pop-up
-\t bbdb-user-mail-names
-\t bbdb/mail-auto-create-p
-\t bbdb/news-auto-create-p
-
-There are numerous hooks. M-x apropos ^bbdb.*hook RET
-
-The keybindings, more precisely:
-\\{bbdb-mode-map}"
- (setq major-mode 'bbdb-mode)
- (setq mode-name "BBDB")
- (use-local-map bbdb-mode-map)
- (run-hooks 'bbdb-mode-hook))
-
-;;; these should be in bbdb-com.el but they're so simple, why load it all.
-
-(defun bbdb-next-record (p)
- "Move the cursor to the first line of the next BBDB record."
- (interactive "p")
- (if (< p 0)
- (bbdb-prev-record (- p))
- (forward-char)
- (while (> p 0)
- (or (re-search-forward "^[^ \t\n]" nil t)
- (progn (beginning-of-line)
- (error "no next record")))
- (setq p (1- p)))
- (beginning-of-line)))
-
-(defun bbdb-prev-record (p)
- "Move the cursor to the first line of the previous BBDB record."
- (interactive "p")
- (if (< p 0)
- (bbdb-next-record (- p))
- (while (> p 0)
- (or (re-search-backward "^[^ \t\n]" nil t)
- (error "no previous record"))
- (setq p (1- p)))))
-
-
-(defun bbdb-maybe-update-display (bbdb-record)
- (save-excursion
- (save-window-excursion
- (let ((w (get-buffer-window bbdb-buffer-name))
- (b (current-buffer)))
- (if w
- (unwind-protect
- (progn (set-buffer bbdb-buffer-name)
- (save-restriction
- (if (assq bbdb-record bbdb-records)
- (bbdb-redisplay-records))))
- (set-buffer b)))))))
-
-(defcustom bbdb-notes-default-separator ", "
- "*The default separator inserted by `bbdb-annotate-notes'.
-This is used for notes which do not have `field-separator' property set.
-E.g., if you want URLs to be separated by newlines, you can put
- (put 'www 'field-separator \"\\n\")
-into your .emacs."
- :group 'bbdb-noticing-records
- :type 'string)
-
-(defun bbdb-annotate-notes (bbdb-record annotation &optional fieldname replace)
- "Add an annotation to a record.
-Adds (or replaces, when the fourth argument REPLACE is non-nil)
-an ANNOTATION to the note FIELDNAME in BBDB-RECORD.
-Called by `bbdb-auto-notes-hook'."
- (or bbdb-record (error "unperson"))
- (setq annotation (bbdb-string-trim annotation))
- (if (memq fieldname '(name address addresses phone phones net aka AKA))
- (error "bbdb: cannot annotate the %s field this way" fieldname))
- (or fieldname (setq fieldname 'notes))
- (or (memq fieldname '(notes company))
- (assoc (symbol-name fieldname) (bbdb-propnames))
- (bbdb-set-propnames (append (bbdb-propnames)
- (list (list (symbol-name fieldname))))))
- (let ((notes (bbdb-string-trim
- (or (bbdb-record-getprop bbdb-record fieldname) ""))))
- (unless (or (string= "" annotation)
- (string-match (regexp-quote annotation) notes))
- (bbdb-record-putprop bbdb-record fieldname
- (if (or replace (string= notes ""))
- annotation
- (concat notes
- (or (get fieldname 'field-separator)
- bbdb-notes-default-separator)
- annotation)))
- (bbdb-maybe-update-display bbdb-record))))
-
-(defun bbdb-offer-save ()
- "Offer to save the Insidious Big Brother Database if it is modified."
- (if bbdb-offer-save
- (bbdb-save-db (eq bbdb-offer-save t))))
-
-(defcustom bbdb-save-db-timeout nil
- "*If non-nil, then when `bbdb-save-db' is asking you whether to save the db,
-it will time out to `yes' after this many seconds. This only works if the
-function `y-or-n-p-with-timeout' is defined."
- :group 'bbdb-save
- :type '(choice (const :tag "Don't time out" nil)
- (integer :tag "Time out after this many seconds" 5)))
-
-(defun bbdb-save-db (&optional prompt-first mention-if-not-saved)
- "Save the DB if it is modified."
- (interactive (list nil t))
- (bbdb-with-db-buffer
- (if (and (buffer-modified-p)
- (or (null prompt-first)
- (if bbdb-readonly-p
- (bbdb-y-or-n-p
- "Save the BBDB, even though it's supposedly read-only? ")
- (if (and bbdb-save-db-timeout
- (fboundp 'y-or-n-p-with-timeout))
- (y-or-n-p-with-timeout
- "Save the BBDB now? " bbdb-save-db-timeout t)
- (bbdb-y-or-n-p "Save the BBDB now? ")))))
- (save-buffer)
- (if mention-if-not-saved (message "BBDB not saved")))))
-
-
-;;; mail and news interface
-
-(defun bbdb-clean-username (string)
- "Strips garbage from the user full name string."
- ;; This function is called a lot, and should be fast. But I'm loathe to
- ;; remove any of the functionality in it.
- (if (string-match "[@%!]" string) ; ain't no user name! It's an address!
- (bbdb-string-trim string)
- (let ((case-fold-search t))
- ;; Take off leading and trailing non-alpha chars \(quotes, parens,
- ;; digits, etc) and things which look like phone extensions \(like
- ;; "x1234" and "ext. 1234". \)
- ;; This doesn't work all the time because some of our friends in
- ;; northern europe have brackets in their names...
- (if (string-match (if bbdb-have-re-char-classes
- "\\`[^[:alpha:]]+"
- "\\`[^a-z]+")
- string)
- (setq string (substring string (match-end 0))))
- (while (string-match
- "\\(\\W+\\([Xx]\\|[Ee]xt\\.?\\)\\W*[-0-9]+\\|[^a-z]+\\)\\'"
- string)
- (setq string (substring string 0 (match-beginning 0))))
- ;; replace tabs, multiple spaces, dots, and underscores with a single
- ;; space, but don't replace ". " with " " because that could be an
- ;; initial.
- (while (string-match "\\(\t\\| +\\|\\(\\.\\)[^ \t_]\\|_+\\)" string)
- (setq string (concat (substring string 0
- (or (match-beginning 2)
- (match-beginning 1)))
- " "
- (substring string (or (match-end 2)
- (match-end 1))))))
- ;; If the string contains trailing parenthesized comments, nuke 'em.
- (if (string-match "[^ \t]\\([ \t]*\\((\\| -\\| #\\)\\)" string)
- (progn
- (setq string (substring string 0 (match-beginning 1)))
- ;; lose rubbish this may have exposed.
- (while
- (string-match
- "\\(\\W+\\([Xx]\\|[Ee]xt\\.?\\)\\W*[-0-9]+\\|[^a-z]+\\)\\'"
- string)
- (setq string (substring string 0 (match-beginning 0))))))
- string)))
-
-;;; message-caching, to speed up the the mail interfaces
-
-(defvar bbdb-buffers-with-message-caches '()
- "A list of all the buffers which have stuff on their `bbdb-message-cache'
-local variable. When we re-parse the `bbdb-file', we need to flush all of
-these caches.")
-
-(defun notice-buffer-with-cache (buffer)
- (or (memq buffer bbdb-buffers-with-message-caches)
- (progn
- ;; First remove any deleted buffers which may have accumulated.
- ;; This happens only when a buffer is added to the list, so it
- ;; ought not happen that frequently (each time you read mail, say.)
- (let ((rest bbdb-buffers-with-message-caches))
- (while rest
- (if (null (buffer-name (car rest)))
- (setq bbdb-buffers-with-message-caches
- (delq (car rest) bbdb-buffers-with-message-caches)))
- (setq rest (cdr rest))))
- ;; now add this buffer.
- (setq bbdb-buffers-with-message-caches
- (cons buffer bbdb-buffers-with-message-caches)))))
-
-(defvar bbdb-message-cache nil
- "alist of (MESSAGE-KEY BBDB-RECORDS) cached in order to avoid updating
-messages each time they are visited. This is used by all MUAs, while the
-MESSAGE-KEY is specific to the MUA and the cache is local for each MUA or MUA
-folder.")
-
-(make-variable-buffer-local 'bbdb-message-cache)
-
-(defun bbdb-message-cache-lookup (message-key)
- "Return cached BBDB records for MESSAGE-KEY.
-If not present or when the records have been modified return nil."
- (bbdb-records)
- (if bbdb-message-caching-enabled
- (let ((records (assq message-key bbdb-message-cache))
- (invalid nil))
- (when records
- (setq records (cdr records))
- (bbdb-mapc (lambda (record)
- (if (bbdb-record-deleted-p record)
- (setq invalid t)))
- records))
- (if invalid nil records))))
-
-(defun bbdb-encache-message (message-key bbdb-records)
- "Cache the BBDB-RECORDS for a message identified by MESSAGE-KEY and
-return them."
- (and bbdb-message-caching-enabled
- (car bbdb-records)
- (add-to-list 'bbdb-message-cache (cons message-key bbdb-records))
- (notice-buffer-with-cache (current-buffer)))
- bbdb-records)
-
-(defun bbdb-decache-message (message-key)
- "Remove an element form the cache."
- (and bbdb-message-caching-enabled
- (delq (assoc message-key bbdb-message-cache) bbdb-message-cache)))
-
-(defun bbdb-flush-all-caches ()
- (bbdb-debug
- (and bbdb-buffers-with-message-caches
- (message "Flushing BBDB caches")))
- (save-excursion
- (while bbdb-buffers-with-message-caches
- (if (buffer-name (car bbdb-buffers-with-message-caches))
- (progn
- (set-buffer (car bbdb-buffers-with-message-caches))
- (setq bbdb-message-cache nil)))
- (setq bbdb-buffers-with-message-caches
- (cdr bbdb-buffers-with-message-caches)))))
-
-
-(defconst bbdb-name-gubbish
- (concat "[-,. \t/\\]+\\("
- "[JjSs]r\\.?"
- "\\|V?\\(I\\.?\\)+V?"
- (concat "\\|"
- (regexp-opt bbdb-lastname-prefixes))
- "\\)\\W*\\'"))
-
-(defun bbdb-divide-name (string)
- "divide the string into a first name and a last name, cleverly."
- ;; ## This shouldn't be here.
- (if (string-match "\\W+\\([Xx]\\|[Ee]xt\\.?\\)\\W*[-0-9]+\\'" string)
- (setq string (substring string 0 (match-beginning 0))))
- (let* ((case-fold-search nil)
- (str string)
- (gubbish (string-match bbdb-name-gubbish string)))
- (if gubbish
- (setq gubbish (substring str gubbish)
- str (substring string 0 (match-beginning 0))))
- (if (string-match
- (concat " +\\("
- ;; start recognize some prefixes to lastnames
- (if bbdb-lastname-prefixes
- (concat "\\("
- (regexp-opt bbdb-lastname-prefixes t)
- "[ ]+\\)?"))
- ;; end recognize some prefixes to lastnames
- "\\([^ ]+ *- *\\)?[^ ]+\\)\\'") str)
- (list (substring str 0 (match-beginning 0))
- (concat
- (substring str (match-beginning 1))
- (or gubbish "")))
- (list string ""))))
-
-(defun bbdb-check-alternate-name (possible-name record)
- (let (aka)
- (if (setq aka (bbdb-record-aka record))
- (let ((down-name (downcase possible-name))
- match)
- (while aka
- (if (equal down-name (downcase (car aka)))
- (setq match (car aka)
- aka nil)
- (setq aka (cdr aka))))
- match))))
-
-
-(defun bbdb-canonicalize-address (net)
- ;; call the bbdb-canonicalize-net-hook repeatedly until it returns a
- ;; value eq to the value passed in. This implies that it can't
- ;; destructively modify the string.
-
- ;; Hysterical Raisins: This is a function, not a hook. In order to
- ;; make this hook a hook, we'll quietly convert a single function
- ;; into a hook list. We should really warn the user that we're
- ;; doing this, and advise them to update their configuration
- ;; accordingly. For the release, maybe.
- (if (functionp bbdb-canonicalize-net-hook)
- (setq bbdb-canonicalize-net-hook (list bbdb-canonicalize-net-hook)))
-
- ;; Now, do the hook run. Note, if you mess up, it's possible that
- ;; BBDB will get stuck here oscillating between various definitions
- ;; of the canonical address.
- (while (not (equal net (setq net (run-hook-with-args
- 'bbdb-canonicalize-net-hook net)))))
-
- net)
-
-;; Mostly written by Rod Whitby.
-(defun bbdb-net-redundant-p (net old-nets)
- "Returns non-nil if NET represents a sub-domain of one of the OLD-NETS.
-The returned value is the address which makes this one redundant.
-For example, \"foo@bar.baz.com\" is redundant w.r.t. \"foo@baz.com\",
-and \"foo@quux.bar.baz.com\" is redundant w.r.t. \"foo@bar.baz.com\"."
- (let ((redundant-addr nil))
- (while (and (not redundant-addr) old-nets)
- ;; Calculate a host-regexp for each address in OLD-NETS
- (let* ((old (car old-nets))
- (host-index (string-match "@" old))
- (name (and host-index (substring old 0 host-index)))
- (host (and host-index (substring old (1+ host-index))))
- ;; host-regexp is "^<name>@.*\.<host>$"
- (host-regexp (and name host
- (concat "\\`" (regexp-quote name)
- "@.*\\." (regexp-quote host)
- "\\'"))))
- ;; If NET matches host-regexp, then it is redundant
- (if (and host-regexp net
- (string-match host-regexp net))
- (setq redundant-addr old)))
- (setq old-nets (cdr old-nets)))
- redundant-addr))
-
-(defun bbdb-name-normalize (name)
- "Return normalized NAME.
-NAME is converted to lower case and in a MULE enabled Emacs it is converted to
-UTF-8 or unibyte to unify the overlapping ISO-8859-* encodings.
-
-You may advice this function to allow more sophisticated normalizations."
- (when name
- (setq name (downcase name))
- (cond ((functionp 'encode-coding-string)
- (funcall 'encode-coding-string name 'utf-8))
- ((functionp 'string-make-unibyte)
- (funcall 'string-make-unibyte name))
- (t
- name))))
-
-(defun bbdb-name= (a b)
- "Return t if the two names A and B are equal.
-Before comparing A and B they are normalized by calling the function
-`bbdb-name-normalize'."
- (string= (bbdb-name-normalize a) (bbdb-name-normalize b)))
-
-
-(defun bbdb-annotate-message-sender (from &optional loudly create-p
- prompt-to-create-p)
- "Fills the record corresponding to the sender with as much info as possible.
-A record may be created by this; a record or nil is returned.
-If `bbdb-readonly-p' is true, then a record will never be created.
-If CREATE-P is true, then a record may be created, otherwise it won't.
-If PROMPT-TO-CREATE-P is true, then the user will be asked for confirmation
-before the record is created, otherwise it is created without confirmation
-\(assuming that CREATE-P is true\). "
- (let* ((data (if (consp from)
- from ; if from is a cons, it's pre-parsed (hack hack)
- (mail-extract-address-components from)))
- (name (car data))
- (net (car (cdr data))))
- (if (equal name net) (setq name nil))
- (bbdb-debug
- (if (equal name "") (error "mail-extr returned \"\" as name"))
- (if (equal net "") (error "mail-extr returned \"\" as net")))
-
- (if (and net bbdb-canonicalize-net-hook)
- (setq net (bbdb-canonicalize-address net)))
-
- (let ((change-p nil)
- (record (or (bbdb-search-simple nil net)
- (bbdb-search-simple name nil)))
- (created-p nil)
- (fname name)
- (lname nil)
- old-name
- ignore-name-mismatch
- bogon-mode)
- (and record (setq old-name (bbdb-record-name record)))
-
- ;; This is to prevent having losers like "John <blat@foop>" match
- ;; against existing records like "Someone Else <john>".
- ;;
- ;; The solution implemented here is to never create or show records
- ;; corresponding to a person who has a real-name which is the same
- ;; as the network-address of someone in the db already. This is not
- ;; a good solution.
- (let (old-net)
- (if (and record name (not (bbdb-name= name old-name)))
- (progn
- (setq old-net (bbdb-record-net record))
- (while old-net
- (if (bbdb-name= name (car old-net))
- (progn
- (setq bogon-mode t
- old-net nil)
- (message
- "Ignoring bogon %s's name \"%s\" to avoid name-clash with \"%s\""
- net name old-name)
- (sit-for 2))
- (setq old-net (cdr old-net)))))))
-
- (if (or record
- bbdb-readonly-p
- (not create-p)
- (not (or name net))
- bogon-mode)
- ;; no further action required
- nil
- ;; otherwise, the db is writable, and we may create a record.
- ;; first try to get a reasonable default name if not given
- ;; often I get things like <firstname>.<surname>@ ...
- (if (or (null name) (and (stringp name) (string= "" name)))
- (if (string-match "^[^@]+" net)
- (setq name (bbdb-clean-username (match-string 0 net)))))
- (setq record (if (or (null prompt-to-create-p)
- (if (functionp prompt-to-create-p)
- (bbdb-invoke-hook-for-value
- prompt-to-create-p)
- (bbdb-y-or-n-p
- (format "%s is not in the db. Add? "
- (or name net)))))
- (make-vector bbdb-record-length nil))
- created-p (not (null record)))
- (if record
- (bbdb-record-set-cache record (make-vector bbdb-cache-length nil)))
- )
- (if (or bogon-mode (null record))
- nil
- (bbdb-debug (if (bbdb-record-deleted-p record)
- (error "nasty nasty deleted record nasty.")))
- (if (and name
- (not (bbdb-name= name old-name))
- (or (null bbdb-use-alternate-names)
- (not (bbdb-check-alternate-name name record)))
- (let ((fullname (bbdb-divide-name name))
- tmp)
- (setq fname (car fullname)
- lname (nth 1 fullname))
- (not (and (equal (downcase fname)
- (and (setq tmp
- (bbdb-record-firstname record))
- (downcase tmp)))
- (equal (downcase lname)
- (and (setq tmp
- (bbdb-record-lastname record))
- (downcase tmp)))))))
-
- ;; have a message-name, not the same as old name.
- (cond (bbdb-readonly-p nil);; skip if readonly
-
- ;; ignore name mismatches?
- ;; NB 'quiet' means 'don't ask', not 'don't mention'
- ((and old-name
- (setq ignore-name-mismatch bbdb-quiet-about-name-mismatches
- ignore-name-mismatch
- (cond
- ((eq nil ignore-name-mismatch)
- nil)
- ((eq t ignore-name-mismatch)
- 2)
- ((numberp ignore-name-mismatch)
- ignore-name-mismatch)
- ((functionp ignore-name-mismatch)
- (funcall ignore-name-mismatch record name))
- (t
- (eval ignore-name-mismatch)))))
- (if (or bbdb-silent-running (eq t ignore-name-mismatch))
- nil
- (message "name mismatch: \"%s\" changed to \"%s\""
- (bbdb-record-name record) name)
- (if (numberp ignore-name-mismatch)
- (sit-for ignore-name-mismatch))))
- ((or created-p
- (if bbdb-silent-running t
- (if (null old-name)
- (bbdb-y-or-n-p
- (format "Assign name \"%s\" to address \"%s\"? "
- name (car (bbdb-record-net record))))
- (bbdb-y-or-n-p
- (format "Change name \"%s\" to \"%s\"? "
- old-name name)))))
- (setq change-p 'sort)
-
- ;; Keep old name?
- (and old-name bbdb-use-alternate-names
- (not (member old-name (bbdb-record-aka record)))
- ;; Silent mode: just add it.
- (if bbdb-silent-running
- (bbdb-record-set-aka record
- (cons old-name
- (bbdb-record-aka
- record)))
- ;; prompt user otherwise.
- (if (bbdb-y-or-n-p
- (format "Keep name \"%s\" as an AKA? "
- old-name))
- (bbdb-record-set-aka record
- (cons old-name
- (bbdb-record-aka
- record)))
- (bbdb-remhash (downcase old-name) record))))
-
- (bbdb-record-set-namecache record nil)
- (bbdb-record-set-firstname record fname)
- (bbdb-record-set-lastname record lname)
- (bbdb-debug (or fname lname
- (error "bbdb: should have a name by now")))
- (bbdb-puthash (downcase (bbdb-record-name record)) record))
-
- ;; not quiet about mismatches
- ((and old-name bbdb-use-alternate-names
- ;; dedupe
- (not (member old-name (bbdb-record-aka record)))
- (if (not bbdb-silent-running)
- (bbdb-y-or-n-p
- (format "Make \"%s\" an alternate for \"%s\"? "
- name old-name))))
- (setq change-p 'sort)
- (bbdb-record-set-aka
- record (cons name (bbdb-record-aka record)))
- (bbdb-puthash (downcase name) record))))
-
- ;; It's kind of a kludge that the "redundancy" concept is built in.
- ;; Maybe I should just add a new hook here... The problem is that the
- ;; canonicalize-net-hook is run before database lookup, and thus can't
- ;; refer to the database to determine whether a net is redundant.
- (if bbdb-canonicalize-redundant-nets-p
- (setq net (or (bbdb-net-redundant-p net (bbdb-record-net record))
- net)))
-
- (if (and net (not bbdb-readonly-p))
- (if (null (bbdb-record-net record))
- ;; names are always a sure match, so don't bother prompting
- ;; here.
- (progn (bbdb-record-set-net record (list net))
- (bbdb-puthash (downcase net) record) ; important!
- (or change-p (setq change-p t)))
- ;; new address; ask before adding.
- (if (let ((rest-net (bbdb-record-net record))
- (new (downcase net))
- (match nil))
- (while (and rest-net (null match))
- (setq match (string= new (downcase (car rest-net)))
- rest-net (cdr rest-net)))
- match)
- nil
- (if (let ((bbdb-always-add-addresses
- bbdb-always-add-addresses))
- (if (functionp bbdb-always-add-addresses)
- (setq bbdb-always-add-addresses
- (funcall bbdb-always-add-addresses)))
- (cond
- ;; add it automatically
- ((eq bbdb-always-add-addresses t)
- t)
- ;; do not add it
- ((null bbdb-always-add-addresses)
- nil)
- ;; ask the user if it should be added
- (t
- (and
- (not (equal net "???"))
- (let ((the-first-bit
- (format "Add address \"%s\" to \"" net))
- ;; this groveling is to prevent the "(y or n)"
- ;; from falling off the right edge of the
- ;; screen.
- (the-next-bit (mapconcat 'identity
- (bbdb-record-net
- record)
- ", "))
- (w (window-width (minibuffer-window))))
- (if (> (+ (length the-first-bit)
- (length the-next-bit) 15) w)
- (setq the-next-bit
- (concat
- (substring
- the-next-bit
- 0 (max 0 (- w (length the-first-bit)
- 20)))
- "...")))
- (bbdb-display-records (list record))
- (if (bbdb-y-or-n-p (concat the-first-bit
- the-next-bit
- "\"? "))
- ;; then add the new net
- t
- ;; else add a new record with the same name
- (if (and create-p
- (or (null prompt-to-create-p)
- (if (functionp prompt-to-create-p)
- (bbdb-invoke-hook-for-value
- prompt-to-create-p)
- (bbdb-y-or-n-p
- (format
- "Create a new record for %s? "
- (bbdb-record-name record))))))
- (setq record
- (bbdb-create-internal name nil net
- nil nil nil)))
- nil))))))
- ;; then modify an existing record
- (let ((front-p (cond ((null bbdb-new-nets-always-primary)
- (bbdb-y-or-n-p
- (format
- "Make \"%s\" the primary address? "
- net)))
- ((eq bbdb-new-nets-always-primary t)
- t)
- (t nil))))
- (bbdb-record-set-net record
- (if front-p
- (cons net (bbdb-record-net
- record))
- (nconc (bbdb-record-net record)
- (list net))))
- (bbdb-puthash (downcase net) record) ; important!
- (or change-p (setq change-p t)))))))
-
- (bbdb-debug
- (if (and change-p bbdb-readonly-p)
- (error
- "doubleplus ungood: how did we change anything in readonly mode?"
- )))
- (if (and loudly change-p (not bbdb-silent-running))
- (if (eq change-p 'sort)
- (message "noticed \"%s\"" (bbdb-record-name record))
- (if (bbdb-record-name record)
- (message "noticed %s's address \"%s\""
- (bbdb-record-name record) net)
- (message "noticed naked address \"%s\"" net))))
-
- (if created-p
- (bbdb-invoke-hook 'bbdb-create-hook record))
-
- (if change-p
- (bbdb-change-record record (eq change-p 'sort)))
-
- ;; only invoke bbdb-notice-hook if we actually noticed something
- (if record
- (let ((inside-bbdb-notice-hook t))
- (bbdb-invoke-hook 'bbdb-notice-hook record)))
-
- record))))
-
-
-;;; window configuration hackery
-(defun bbdb-multiple-buffers-default ()
- "Default function for guessing a better name for new *BBDB* buffers."
- (cond ((memq major-mode '(vm-mode vm-summary-mode
- vm-presentation-mode
- vm-virtual-mode))
- (vm-select-folder-buffer)
- (buffer-name))
- ((memq major-mode '(gnus-summary-mode gnus-group-mode))
- (set-buffer gnus-article-buffer)
- (buffer-name))
- ((memq major-mode '(mail-mode vm-mail-mode message-mode))
- "message composition")))
-
-(defun bbdb-multiple-buffers-set-name (&optional buffer-list new-name)
- (setq new-name (or new-name
- (concat " *BBDB " (funcall bbdb-multiple-buffers) "*"))
- buffer-list (append (list (current-buffer)
- (get-buffer-create new-name))
- buffer-list))
-
- (save-excursion
- (while buffer-list
- (set-buffer (car buffer-list))
- (make-local-variable 'bbdb-buffer-name)
- (setq bbdb-buffer-name new-name)
- (setq buffer-list (cdr buffer-list)))))
-
-(defun bbdb-pop-up-bbdb-buffer (&optional predicate)
- "Find the largest window on the screen, and split it, displaying the
-*BBDB* buffer in the bottom 'bbdb-pop-up-target-lines' lines (unless
-the *BBDB* buffer is already visible, in which case do nothing.)
-
-PREDICATE can be a function to select the right window for the split.
-
-`bbdb-use-pop-up' controls how to split the selected window and how many lines
-resp. columns it will get. If it is 'vertical a vertical split is done otherwise
-a horizontal.
-
-If `bbdb-multiple-buffers' is set we create a new BBDB buffer when not
-already within one. The new buffer-name starts with a space, i.e. it does
-not clutter the buffer-list."
-
- (let ((current-window (selected-window))
- (current-buffer (current-buffer))
- new-bbdb-buffer-name
- window)
-
- ;; create new BBDB buffer if multiple buffers are desired.
- (when (and bbdb-multiple-buffers (not (eq major-mode 'bbdb-mode)))
- (bbdb-multiple-buffers-set-name (list current-buffer)))
- (setq new-bbdb-buffer-name bbdb-buffer-name)
-
-
- ;; now get the pop-up
- (if (or (not bbdb-use-pop-up) (get-buffer-window new-bbdb-buffer-name))
- ;; just create the buffer if necessary
- (progn
- (get-buffer-create new-bbdb-buffer-name)
- (display-buffer new-bbdb-buffer-name))
-
- ;; else find a window to split
- (when predicate
- (setq window current-window)
- (while (and (not (funcall predicate window))
- (not (eq current-window
- (setq window (next-window window)))))))
-
- ;; find the tallest window if none has been selected so far
- (when (null window)
- (let ((tallest-window current-window))
- (while (not (eq current-window (setq window (next-window window))))
- (if (> (window-height window) (window-height tallest-window))
- (setq tallest-window window)))
- (setq window tallest-window)))
-
- ;; select it and split it...
- (select-window window)
- (cond ((eq bbdb-use-pop-up 'vertical)
- (split-window-horizontally (- bbdb-pop-up-target-columns)))
- (t
- (let ((size (min
- (- (window-height window) window-min-height 1)
- (- (window-height window)
- (max window-min-height
- (1+ bbdb-pop-up-target-lines))))))
- (setq size (if (> size 0) size window-min-height))
- (split-window window size))))
-
- ;; make gnus happy...
- (if (memq major-mode
- '(gnus-Group-mode gnus-Subject-mode gnus-Article-mode))
- (goto-char (point-min)))
-
- ;; goto the next window, the one created by the split and
- ;; make it display the BBDB buffer
- (select-window (next-window))
- (let ((pop-up-windows nil))
- (switch-to-buffer (get-buffer-create new-bbdb-buffer-name)))
-
- ;; select the original window we were in
- (select-window current-window)
- ;; and make sure the original buffer is selected
- (set-buffer current-buffer))))
-
-(defun bbdb-version (&optional arg)
- "Return string describing the version of the BBDB that is running.
-When called interactively with a prefix argument, insert string at point."
- (interactive "P")
- (let ((version-string (format "BBDB version %s" bbdb-version)))
- (cond
- (arg
- (insert (message version-string)))
- ((interactive-p)
- (message version-string))
- (t version-string))))
-
- ;;; resorting, which really shouldn't be necesary...
-
-(defun bbdb-record-lessp-fn (record1 record2) ; for use as a funarg
- (bbdb-record-lessp record1 record2))
-
-(defun bbdb-resort-database ()
- "*Resort BBDB database as a last resort.
-This is not be needed when using BBDB itself. It might be necessary
-after having used inferior software to add entries to the BBDB, however."
- (interactive)
- (let* ((records (copy-sequence (bbdb-records))))
- (bbdb-with-db-buffer
- (setq bbdb-records (sort bbdb-records 'bbdb-record-lessp-fn))
- (if (equal records bbdb-records)
- nil
- (message "DANGER! BBDB was mis-sorted; it's being fixed...")
- (goto-char (point-min))
- (cond ((eq (following-char) ?\[) nil)
- ((search-forward "\n[" nil 0) (forward-char -1)))
- (delete-region (point) bbdb-end-marker)
- (let ((print-escape-newlines t)
- (standard-output (current-buffer))
- (inhibit-quit t) ; really, don't fuck with this
- record cache)
- (setq records bbdb-records)
- (while records
- (setq record (car records)
- cache (bbdb-record-cache record))
- (bbdb-record-set-cache record nil)
- (bbdb-prin1 (car records))
- (bbdb-record-set-cache record cache)
- (insert ?\n)
- (setq records (cdr records))))
- (kill-all-local-variables)
- (error "the BBDB was mis-sorted: it has been repaired.")))))
-
-(defvar bbdb-init-forms
- '((gnus ; gnus 3.15 or newer
- (add-hook 'gnus-startup-hook 'bbdb-insinuate-gnus))
- (mh-e ; MH-E
- (add-hook 'mh-folder-mode-hook 'bbdb-insinuate-mh))
- (rmail ; RMAIL
- (add-hook 'rmail-mode-hook 'bbdb-insinuate-rmail))
- (sendmail ; the standard mail user agent
- (add-hook 'mail-setup-hook 'bbdb-insinuate-sendmail))
- (vm-old ; the alternative mail reader
- (add-hook 'vm-load-hook 'bbdb-insinuate-vm))
- (vm ; newer versions don't have vm-load-hook
- (progn (eval-after-load "vm" '(bbdb-insinuate-vm))))
- (message ; the gnus mail user agent
- (add-hook 'message-setup-hook 'bbdb-insinuate-message))
- (reportmail ; mail notification
- (add-hook 'reportmail-load-hook 'bbdb-insinuate-reportmail))
- (sc ; message citation
- (add-hook 'sc-load-hook 'bbdb-insinuate-sc))
- (supercite ; same
- (add-hook 'sc-load-hook 'bbdb-insinuate-sc))
- (w3 ; WWW browser
- (add-hook 'w3-load-hook 'bbdb-insinuate-w3)))
- "The alist which maps features to insinuation forms.")
-
-;;;###autoload
-(defun bbdb-initialize (&rest to-insinuate)
-"*Initialize the BBDB. One or more of the following symbols can be
-passed as arguments to initiate the appropriate insinuations.
-
- Initialization of mail/news readers:
-
- gnus Initialize BBDB support for the gnus mail/news reader
- version 3.15 or newer. If you pass the `gnus' symbol,
- you should probably also pass the `message' symbol.
- mh-e Initialize BBDB support for the MH-E mail reader.
- rmail Initialize BBDB support for the RMAIL mail reader.
- sendmail Initialize BBDB support for sendmail (M-x mail).
- vm Initialize BBDB support for the VM mail reader.
- NOTE: For the VM insinuation to work properly, you must
- either call `bbdb-initialize' with the `vm' symbol from
- within your VM initialization file (\"~/.vm\") or you
- must call `bbdb-insinuate-vm' manually from within your
- VM initialization file.
-
- Initialization of miscellaneous package:
-
- message Initialize BBDB support for Message mode.
- reportmail Initialize BBDB support for the Reportmail mail
- notification package.
- sc or Initialize BBDB support for the Supercite message
- supercite citation package.
- w3 Initialize BBDB support for Web browsers."
-
- (defalias 'advertized-bbdb-delete-current-field-or-record
- 'bbdb-delete-current-field-or-record)
-
- (require 'bbdb-autoloads)
-
- (while to-insinuate
- (let* ((feature (car to-insinuate))
- (init (assq feature bbdb-init-forms)))
- (setq to-insinuate (cdr to-insinuate))
- (if init
- (if (or (featurep feature) (locate-library (symbol-name feature)))
- (eval (cadr init))
- (bbdb-warn "cannot locate feature `%s'" feature))
- (bbdb-warn "don't know how to insinuate `%s'" feature))))
-
- ;; RMAIL, MHE, and VM interfaces might need these.
- (autoload 'mail-strip-quoted-names "mail-utils")
- (autoload 'mail-fetch-field "mail-utils")
- ;; All of the interfaces need this.
- (autoload 'mail-extract-address-components "mail-extr")
-
- (run-hooks 'bbdb-initialize-hook))
-
-;; Initialize keymaps
-(unless bbdb-mode-search-map
- (define-prefix-command 'bbdb-mode-search-map)
- (if (fboundp 'set-keymap-prompt)
- (set-keymap-prompt
- bbdb-mode-search-map
- "(Search [n]ame, [c]ompany, net [a]ddress, n[o]tes)?"))
-
- (define-key bbdb-mode-search-map [(n)] 'bbdb-name)
- (define-key bbdb-mode-search-map [(c)] 'bbdb-company)
- (define-key bbdb-mode-search-map [(a)] 'bbdb-net)
- (define-key bbdb-mode-search-map [(o)] 'bbdb-notes))
-
-(unless bbdb-mode-map
- (setq bbdb-mode-map (make-keymap))
- (suppress-keymap bbdb-mode-map)
-
- (define-key bbdb-mode-map [(S)] 'bbdb-mode-search-map)
-
- (define-key bbdb-mode-map [(*)] 'bbdb-apply-next-command-to-all-records)
- (define-key bbdb-mode-map [(+)] 'bbdb-append-records)
- (define-key bbdb-mode-map [(!)] 'bbdb-search-invert-set)
- (define-key bbdb-mode-map [(a)] 'bbdb-add-or-remove-mail-alias)
- (define-key bbdb-mode-map [(e)] 'bbdb-edit-current-field)
- (define-key bbdb-mode-map [(n)] 'bbdb-next-record)
- (define-key bbdb-mode-map [(p)] 'bbdb-prev-record)
- (define-key bbdb-mode-map [(d)] 'bbdb-delete-current-field-or-record)
- (define-key bbdb-mode-map [(control k)] 'bbdb-delete-current-field-or-record)
- (define-key bbdb-mode-map [(control o)] 'bbdb-insert-new-field)
- (define-key bbdb-mode-map [(s)] 'bbdb-save-db)
- (define-key bbdb-mode-map [(control x) (control s)]
- 'bbdb-save-db)
- (define-key bbdb-mode-map [(r)] 'bbdb-refile-record)
- (define-key bbdb-mode-map [(t)] 'bbdb-toggle-records-display-layout)
- (define-key bbdb-mode-map [(T)] 'bbdb-display-record-completely)
- (define-key bbdb-mode-map [(o)] 'bbdb-omit-record)
- (define-key bbdb-mode-map [(?\;)] 'bbdb-record-edit-notes)
- (define-key bbdb-mode-map [(m)] 'bbdb-send-mail)
- (define-key bbdb-mode-map "\M-d" 'bbdb-dial)
- (define-key bbdb-mode-map [(f)] 'bbdb-finger)
- (define-key bbdb-mode-map [(F)] 'bbdb-ftp)
- (define-key bbdb-mode-map [(i)] 'bbdb-info)
- (define-key bbdb-mode-map [(??)] 'bbdb-help)
- (define-key bbdb-mode-map [(q)] 'bbdb-bury-buffer)
- (define-key bbdb-mode-map [(control x) (control t)]
- 'bbdb-transpose-fields)
- (define-key bbdb-mode-map [(w)] 'bbdb-www)
- (define-key bbdb-mode-map [(W)] 'bbdb-whois)
- (define-key bbdb-mode-map [(P)] 'bbdb-print)
- (define-key bbdb-mode-map [(h)] 'other-window)
- (define-key bbdb-mode-map [(=)] 'delete-other-windows)
- (define-key bbdb-mode-map [(c)] 'bbdb-create)
- (define-key bbdb-mode-map [(C)] 'bbdb-changed)
- (define-key bbdb-mode-map [(b)] 'bbdb)
-
- (define-key bbdb-mode-map [delete] 'scroll-down)
- (define-key bbdb-mode-map " " 'scroll-up)
- )
-
-
-;;; Support for the various Emacsen. This is for features that the
-;;; BBDB adds to itself for different Emacsen. For definitions of
-;;; functions that aren't present in various Emacsen (for example,
-;;; cadr for Emacs 19.34), see below
-(when (string-match "XEmacs\\|Lucid" emacs-version)
- ;; Above
- (fset 'bbdb-warn 'warn)
-
- ;; bbdb-com.el
- (fset 'bbdb-display-completion-list 'bbdb-xemacs-display-completion-list))
-
-(defun bbdb-insinuate-sendmail ()
- "Call this function to hook BBDB into sendmail (that is, M-x mail)."
- (define-key mail-mode-map "\M-\t" 'bbdb-complete-name))
-
-;;;###autoload
-(defun bbdb-insinuate-message ()
- "Call this function to hook BBDB into `message-mode'."
- (define-key message-mode-map "\M-\t" 'bbdb-complete-name))
-
-;;; Erm. says here that (require...) can take a noerror flag; why do
-;;; we have this function?
-(defmacro safe-require (thing)
- (list 'condition-case nil (list 'require thing) '(file-error nil)))
-
-;; Wrappers for things that change for different Emacsen. Note: This
-;; is for things that get redefined that don't belong elsewhere. Some
-;; functions that get redefined live elsewhere in the source because
-;; it makes sense to put them there.
-
-(defun bbdb-warn (&rest args)
- (beep 1)
- (apply 'message args))
-
-
-(provide 'bbdb) ; provide before loading things which might require
-
-(run-hooks 'bbdb-load-hook)