branch: externals/bbdb commit 4b20ac7d3ab3f17904509bc4e2b00bd5117cdc33 Author: Stefan Monnier <monn...@iro.umontreal.ca> Commit: Roland Winkler <wink...@gnu.org>
Use cl-defstruct to define BBDB record type --- bbdb-com.el | 114 +++++++++++++++-------------- bbdb-migrate.el | 123 +++++++++++++++----------------- bbdb-snarf.el | 91 +++++++++++------------ bbdb.el | 218 +++++++++++++++++++++++++++++++------------------------- 4 files changed, 285 insertions(+), 261 deletions(-) diff --git a/bbdb-com.el b/bbdb-com.el index d4cd581..3d13c14 100644 --- a/bbdb-com.el +++ b/bbdb-com.el @@ -1,6 +1,6 @@ ;;; bbdb-com.el --- user-level commands of BBDB -*- lexical-binding: t -*- -;; Copyright (C) 2010-2019 Free Software Foundation, Inc. +;; Copyright (C) 2010-2020 Free Software Foundation, Inc. ;; This file is part of the Insidious Big Brother Database (aka BBDB), @@ -59,8 +59,8 @@ If RECORDS is a single record turn it into a list. If FULL is non-nil, assume that RECORDS include display information." (if records (if full - (if (vectorp (car records)) (list records) records) - (if (vectorp records) (list records) records)))) + (if (bbdb-record-p (car records)) (list records) records) + (if (bbdb-record-p records) (list records) records)))) ;; Note about BBDB prefix commands: ;; `bbdb-do-all-records', `bbdb-append-display' and `bbdb-search-invert' @@ -761,7 +761,7 @@ Return a list containing four numbers or one string." ;; Is this always correct? What about an extension zero? ;; Should we use nil instead of zeros? (unless style (setq style bbdb-phone-style)) - (let ((area-regexp (concat "(?[ \t]*\\+?1?[ \t]*[-\(]?[ \t]*[-\(]?[ \t]*" + (let ((area-regexp (concat "(?[ \t]*\\+?1?[ \t]*[-(]?[ \t]*[-(]?[ \t]*" "\\([2-9][0-9][0-9]\\)[ \t]*)?[-./ \t]*")) (main-regexp (concat "\\([1-9][0-9][0-9]\\)[ \t]*[-.]?[ \t]*" "\\([0-9][0-9][0-9][0-9]\\)[ \t]*")) @@ -827,18 +827,18 @@ but does ensure that there will not be name collisions." (bbdb-error-retry (setq name (bbdb-read-name first-and-last)) (bbdb-check-name name)) - (bbdb-record-set-firstname record (car name)) - (bbdb-record-set-lastname record (cdr name))) + (setf (bbdb-record-firstname record) (car name)) + (setf (bbdb-record-lastname record) (cdr name))) ;; organization - (bbdb-record-set-organization record (bbdb-read-organization)) + (setf (bbdb-record-organization record) (bbdb-read-organization)) ;; mail (let (mail) (bbdb-error-retry (setq mail (bbdb-split 'mail (bbdb-read-string "E-Mail Addresses: "))) (bbdb-check-mail mail)) - (bbdb-record-set-mail record mail)) + (setf (bbdb-record-mail record) mail)) ;; address (let (addresses label) @@ -849,7 +849,7 @@ but does ensure that there will not be name collisions." nil bbdb-address-label-list)))) ;; Here we could also already update the completion lists. Bother? (push (bbdb-record-edit-address nil label) addresses)) - (bbdb-record-set-address record (nreverse addresses))) + (setf (bbdb-record-address record) (nreverse addresses))) ;; phones (let (phones label) @@ -859,13 +859,13 @@ but does ensure that there will not be name collisions." "Phone Label [RET when done]: " nil bbdb-phone-label-list)))) (push (bbdb-record-edit-phone nil label) phones)) - (bbdb-record-set-phone record (nreverse phones))) + (setf (bbdb-record-phone record) (nreverse phones))) ;; `bbdb-default-xfield' (let ((xfield (bbdb-read-xfield bbdb-default-xfield))) (unless (string= "" xfield) - (bbdb-record-set-xfields - record (list (cons bbdb-default-xfield xfield))))) + (setf (bbdb-record-xfields record) + (list (cons bbdb-default-xfield xfield))))) record)) @@ -966,7 +966,8 @@ The following keywords are supported in SPEC: (setq spec (apply #'append newspec)))) (let ((record (bbdb-empty-record)) - (record-type (cdr bbdb-record-type)) + ;; FIXME: Use something like `bbdb-record--make' i.s.o `vector'. + (record-type (apply #'vector (cdr bbdb-record-type))) (check (prog1 (memq :check spec) (setq spec (delq :check spec)))) keyw) @@ -983,57 +984,57 @@ The following keywords are supported in SPEC: (cons string string)) t))) (bbdb-check-name name) ; check for duplicates - (bbdb-record-set-firstname record (car name)) - (bbdb-record-set-lastname record (cdr name)))) + (setf (bbdb-record-firstname record) (car name)) + (setf (bbdb-record-lastname record) (cdr name)))) (`:affix (let ((affix (bbdb-split-maybe 'affix (pop spec)))) (if check (bbdb-check-type affix (bbdb-record-affix record-type) t)) - (bbdb-record-set-affix record affix))) + (setf (bbdb-record-affix record) affix))) (`:organization (let ((organization (bbdb-split-maybe 'organization (pop spec)))) (if check (bbdb-check-type organization (bbdb-record-organization record-type) t)) - (bbdb-record-set-organization record organization))) + (setf (bbdb-record-organization record) organization))) (`:aka (let ((aka (bbdb-split-maybe 'aka (pop spec)))) (if check (bbdb-check-type aka (bbdb-record-aka record-type) t)) (bbdb-check-name aka) - (bbdb-record-set-aka record aka))) + (setf (bbdb-record-aka record) aka))) (`:mail (let ((mail (bbdb-split-maybe 'mail (pop spec)))) (if check (bbdb-check-type mail (bbdb-record-mail record-type) t)) (bbdb-check-mail mail) - (bbdb-record-set-mail record mail))) + (setf (bbdb-record-mail record) mail))) (`:phone (let ((phone (pop spec))) (if check (bbdb-check-type phone (bbdb-record-phone record-type) t)) - (bbdb-record-set-phone record phone))) + (setf (bbdb-record-phone record) phone))) (`:address (let ((address (pop spec))) (if check (bbdb-check-type address (bbdb-record-address record-type) t)) - (bbdb-record-set-address record address))) + (setf (bbdb-record-address record) address))) (`:xfields (let ((xfields (pop spec))) (if check (bbdb-check-type xfields (bbdb-record-xfields record-type) t)) - (bbdb-record-set-xfields record xfields))) + (setf (bbdb-record-xfields record) xfields))) (`:uuid (let ((uuid (pop spec))) (if check (bbdb-check-type uuid (bbdb-record-uuid record-type) t)) - (bbdb-record-set-uuid record uuid))) + (setf (bbdb-record-uuid record) uuid))) (`:creation-date (let ((creation-date (pop spec))) (if check (bbdb-check-type creation-date (bbdb-record-creation-date record-type) t)) - (bbdb-record-set-creation-date record creation-date))) + (setf (bbdb-record-creation-date record) creation-date))) (_ (error "Keyword `%s' undefined" keyw)))) @@ -1362,7 +1363,7 @@ If the country field of ADDRESS is nonempty and IGNORE-COUNTRY is nil, use the rule from `bbdb-address-format-list' matching this country. Otherwise, use the default rule according to `bbdb-address-format-list'." (unless address - (setq address (make-vector bbdb-address-length nil))) + (setq address (bbdb-address--make))) (unless label (setq label (bbdb-read-string "Label: " (bbdb-address-label address) @@ -1410,19 +1411,20 @@ Otherwise, use the default rule according to `bbdb-address-format-list'." "Country: " (or (bbdb-address-country address) bbdb-default-country) bbdb-country-list)))))) - (bbdb-address-set-label address label) - (bbdb-address-set-streets address (elt new-addr 0)) - (bbdb-address-set-city address (elt new-addr 1)) - (bbdb-address-set-state address (elt new-addr 2)) - (bbdb-address-set-postcode address (elt new-addr 3)) - (if (string= "" (bbdb-concat "" (elt new-addr 0) (elt new-addr 1) - (elt new-addr 2) (elt new-addr 3) - (elt new-addr 4))) - ;; User did not enter anything. this causes a display bug. - ;; The following is a temporary fix. Ideally, we would simply discard - ;; the entire address, but that requires bigger hacking. - (bbdb-address-set-country address "Emacs") - (bbdb-address-set-country address (elt new-addr 4))) + (setf (bbdb-address-label address) label) + (setf (bbdb-address-streets address) (elt new-addr 0)) + (setf (bbdb-address-city address) (elt new-addr 1)) + (setf (bbdb-address-state address) (elt new-addr 2)) + (setf (bbdb-address-postcode address) (elt new-addr 3)) + (setf (bbdb-address-country address) + (if (string= "" (bbdb-concat "" (elt new-addr 0) (elt new-addr 1) + (elt new-addr 2) (elt new-addr 3) + (elt new-addr 4))) + ;; User did not enter anything. this causes a display bug. + ;; The following is a temporary fix. Ideally, we would simply discard + ;; the entire address, but that requires bigger hacking. + "Emacs" + (elt new-addr 4))) address)) (defun bbdb-edit-address-street (streets) @@ -1469,7 +1471,7 @@ If LABEL is nil, edit the label sub-field of PHONE as well." ;; two or four elements. We do not know whether after editing PHONE ;; we still have a number requiring the same format as PHONE. ;; So we throw away the argument PHONE and return a new vector. - (apply #'vector + (apply #'bbdb-phone--make (or label (bbdb-read-string "Label: " (and phone (bbdb-phone-label phone)) @@ -1857,9 +1859,11 @@ in `bbdb-change-hook')." (interactive (list (bbdb-do-records) t)) (bbdb-editable) (dolist (record (bbdb-record-list records)) - (bbdb-record-set-address - record (sort (bbdb-record-address record) - (lambda (xx yy) (string< (aref xx 0) (aref yy 0))))) + (setf (bbdb-record-address record) + (sort (bbdb-record-address record) + ;; FIXME: Should these `aref's be replaced by + ;; `bbdb-address-label'? + (lambda (xx yy) (string< (aref xx 0) (aref yy 0))))) (if update (bbdb-change-record record)))) @@ -1874,9 +1878,11 @@ in `bbdb-change-hook')." (interactive (list (bbdb-do-records) t)) (bbdb-editable) (dolist (record (bbdb-record-list records)) - (bbdb-record-set-phone - record (sort (bbdb-record-phone record) - (lambda (xx yy) (string< (aref xx 0) (aref yy 0))))) + (setf (bbdb-record-phone record) + (sort (bbdb-record-phone record) + ;; FIXME: Should these `aref's be replaced by + ;; `bbdb-phone-label'? + (lambda (xx yy) (string< (aref xx 0) (aref yy 0))))) (if update (bbdb-change-record record)))) @@ -1891,11 +1897,11 @@ in `bbdb-change-hook')." (interactive (list (bbdb-do-records) t)) (bbdb-editable) (dolist (record (bbdb-record-list records)) - (bbdb-record-set-xfields - record (sort (bbdb-record-xfields record) - (lambda (a b) - (< (or (cdr (assq (car a) bbdb-xfields-sort-order)) 100) - (or (cdr (assq (car b) bbdb-xfields-sort-order)) 100))))) + (setf (bbdb-record-xfields record) + (sort (bbdb-record-xfields record) + (lambda (a b) + (< (or (cdr (assq (car a) bbdb-xfields-sort-order)) 100) + (or (cdr (assq (car b) bbdb-xfields-sort-order)) 100))))) (if update (bbdb-change-record record)))) (define-obsolete-function-alias 'bbdb-sort-notes #'bbdb-sort-xfields "3.0") @@ -2108,7 +2114,7 @@ Completion is done according to `bbdb-completion-list'. If the user just hits return, nil is returned. Otherwise, a valid response is forced." (let* ((completion-ignore-case t) (string (completing-read prompt bbdb-hashtable - 'bbdb-completion-predicate t))) + #'bbdb-completion-predicate t))) (unless (string= "" string) (let (records) (dolist (record (gethash string bbdb-hashtable)) @@ -2224,7 +2230,7 @@ as part of the MUA insinuation." (completion-ignore-case t) (completion (and orig (try-completion orig bbdb-hashtable - 'bbdb-completion-predicate))) + #'bbdb-completion-predicate))) all-completions dwim-completions one-record) (unless done @@ -2243,7 +2249,7 @@ as part of the MUA insinuation." (setq completion (substring completion 0 (match-beginning 0)))) (setq all-completions (all-completions orig bbdb-hashtable - 'bbdb-completion-predicate)) + #'bbdb-completion-predicate)) ;; Resolve the records matching ORIG: ;; Multiple completions may match the same record (let ((records (delete-dups @@ -2716,7 +2722,7 @@ is non-nil. Do not dial the extension." (setq phone (car (bbdb-record-phone (bbdb-current-record))))) (if (eq (car-safe phone) 'phone) (setq phone (car (cdr phone)))) - (or (vectorp phone) (error "Not on a phone field")) + (or (bbdb-phone-p phone) (error "Not on a phone field")) (let ((number (bbdb-phone-string phone)) shortnumber) diff --git a/bbdb-migrate.el b/bbdb-migrate.el index 7bce4da..0270c72 100644 --- a/bbdb-migrate.el +++ b/bbdb-migrate.el @@ -1,6 +1,6 @@ -;;; bbdb-migrate.el --- migration functions for BBDB -*- lexical-binding: t -*- +;;; bbdb-migrate.el --- migration functions for BBDB -*- lexical-binding:t -*- -;; Copyright (C) 2010-2017 Free Software Foundation, Inc. +;; Copyright (C) 2010-2020 Free Software Foundation, Inc. ;; This file is part of the Insidious Big Brother Database (aka BBDB), @@ -97,22 +97,16 @@ records) (defconst bbdb-migrate-alist - '((3 (bbdb-record-xfields bbdb-record-set-xfields - bbdb-migrate-dates)) - (4 (bbdb-record-address bbdb-record-set-address - bbdb-migrate-add-country)) - (5 (bbdb-record-address bbdb-record-set-address - bbdb-migrate-streets-to-list)) - (6 (bbdb-record-address bbdb-record-set-address - bbdb-migrate-postcode-to-string)) - (7 (bbdb-record-xfields bbdb-record-set-xfields - bbdb-migrate-xfields-to-list) - (bbdb-record-organization bbdb-record-set-organization - bbdb-migrate-organization-to-list))) + '((3 (bbdb-record-xfields bbdb-migrate-dates)) + (4 (bbdb-record-address bbdb-migrate-add-country)) + (5 (bbdb-record-address bbdb-migrate-streets-to-list)) + (6 (bbdb-record-address bbdb-migrate-postcode-to-string)) + (7 (bbdb-record-xfields bbdb-migrate-xfields-to-list) + (bbdb-record-organization bbdb-migrate-organization-to-list))) ;; Formats 8 and 9: do nothing "Alist (VERSION . CHANGES). -CHANGES is a list with elements (GET SET FUNCTION) that expands -to action (SET record (FUNCTION (GET record))).") +CHANGES is a list with elements (GETTER FUNCTION) that expands +to action (setf (GETTER record) (FUNCTION (GETTER record))).") (defun bbdb-migrate-lambda (old) "Return the function to migrate from OLD to `bbdb-file-format'. @@ -121,60 +115,61 @@ The manipulations are defined by `bbdb-migrate-alist'." (while (<= old bbdb-file-format) (setq spec (append spec (cdr (assoc old bbdb-migrate-alist))) old (1+ old))) - `(lambda (record) - ,@(mapcar (lambda (change) - ;; (SET record (FUNCTION (GET record))) - `(,(nth 1 change) record ; SET - (,(nth 2 change) ; FUNCTION - (,(nth 0 change) record)))) ; GET - spec) - record))) + (eval + `(lambda (record) + ,@(mapcar (lambda (change) + (pcase-let ((`(,getter ,migrate-function) change)) + ;; (SET record (FUNCTION (GET record))) + `(cl-callf ,migrate-function (,getter record)))) + spec) + record) + 'lexical))) (defun bbdb-migrate-postcode-to-string (addresses) "Make all postcodes plain strings. This uses the code that used to be in `bbdb-address-postcode'." ;; apply the function to all addresses in the list and return a ;; modified list of addresses - (mapcar (lambda (address) - (let ((postcode (bbdb-address-postcode address))) - (bbdb-address-set-postcode - address - (cond ((stringp postcode) - postcode) - ;; nil or zero - ((or (zerop postcode) - (null postcode)) - "") - ;; a number - ((numberp postcode) - (format "%d" postcode)) - ;; list with two strings - ((and (stringp (nth 0 postcode)) - (stringp (nth 1 postcode))) - ;; the second string starts with 4 digits - (if (string-match "^[0-9][0-9][0-9][0-9]" - (nth 1 postcode)) - (format "%s-%s" (nth 0 postcode) (nth 1 postcode)) - ;; ("abc" "efg") - (format "%s %s" (nth 0 postcode) (nth 1 postcode)))) - ;; list with two numbers - ((and (integerp (nth 0 postcode)) - (integerp (nth 1 postcode))) - (format "%05d-%04d" (nth 0 postcode) (nth 1 postcode))) - ;; list with a string and a number - ((and (stringp (nth 0 postcode)) - (integerp (nth 1 postcode))) - (format "%s-%d" (nth 0 postcode) (nth 1 postcode))) - ;; ("SE" (123 45)) - ((and (stringp (nth 0 postcode)) - (integerp (nth 0 (nth 1 postcode))) - (integerp (nth 1 (nth 1 postcode)))) - (format "%s-%d %d" (nth 0 postcode) (nth 0 (nth 1 postcode)) - (nth 1 (nth 1 postcode)))) - ;; last possibility - (t (format "%s" postcode))))) - address) - addresses)) + (mapcar + (lambda (address) + (let ((postcode (bbdb-address-postcode address))) + (setf (bbdb-address-postcode address) + (cond ((stringp postcode) + postcode) + ;; nil or zero + ((or (zerop postcode) + (null postcode)) + "") + ;; a number + ((numberp postcode) + (format "%d" postcode)) + ;; list with two strings + ((and (stringp (nth 0 postcode)) + (stringp (nth 1 postcode))) + ;; the second string starts with 4 digits + (if (string-match "^[0-9][0-9][0-9][0-9]" + (nth 1 postcode)) + (format "%s-%s" (nth 0 postcode) (nth 1 postcode)) + ;; ("abc" "efg") + (format "%s %s" (nth 0 postcode) (nth 1 postcode)))) + ;; list with two numbers + ((and (integerp (nth 0 postcode)) + (integerp (nth 1 postcode))) + (format "%05d-%04d" (nth 0 postcode) (nth 1 postcode))) + ;; list with a string and a number + ((and (stringp (nth 0 postcode)) + (integerp (nth 1 postcode))) + (format "%s-%d" (nth 0 postcode) (nth 1 postcode))) + ;; ("SE" (123 45)) + ((and (stringp (nth 0 postcode)) + (integerp (nth 0 (nth 1 postcode))) + (integerp (nth 1 (nth 1 postcode)))) + (format "%s-%d %d" (nth 0 postcode) (nth 0 (nth 1 postcode)) + (nth 1 (nth 1 postcode)))) + ;; last possibility + (t (format "%s" postcode))))) + address) + addresses)) (defun bbdb-migrate-dates (xfields) "Change date formats. diff --git a/bbdb-snarf.el b/bbdb-snarf.el index 7649501..f1c84ad 100644 --- a/bbdb-snarf.el +++ b/bbdb-snarf.el @@ -1,6 +1,6 @@ ;;; bbdb-snarf.el --- convert free-form text to BBDB records -*- lexical-binding: t -*- -;; Copyright (C) 2010-2017 Free Software Foundation, Inc. +;; Copyright (C) 2010-2020 Free Software Foundation, Inc. ;; This file is part of the Insidious Big Brother Database (aka BBDB), @@ -189,8 +189,8 @@ The first subexpression becomes the URL." (let ((name (match-string 1))) (replace-match "") (setq name (bbdb-divide-name name)) - (bbdb-record-set-firstname record (car name)) - (bbdb-record-set-lastname record (cdr name))))) + (setf (bbdb-record-firstname record) (car name)) + (setf (bbdb-record-lastname record) (cdr name))))) (defun bbdb-snarf-name-mail (record) "Snarf name from mail address for RECORD." @@ -202,8 +202,8 @@ The first subexpression becomes the URL." (setq name (car (bbdb-extract-address-components (car (bbdb-record-mail record))))) (setq name (bbdb-divide-name name))) - (bbdb-record-set-firstname record (car name)) - (bbdb-record-set-lastname record (cadr name))))) + (setf (bbdb-record-firstname record) (car name)) + (setf (bbdb-record-lastname record) (cadr name))))) (defun bbdb-snarf-mail-address (record) "Snarf name and mail address for RECORD." @@ -212,9 +212,9 @@ The first subexpression becomes the URL." ;; a more complex rule, the buffer should be narrowed appropriately. (let* ((data (bbdb-extract-address-components (buffer-string))) (name (and (car data) (bbdb-divide-name (car data))))) - (bbdb-record-set-firstname record (car name)) - (bbdb-record-set-lastname record (cdr name)) - (bbdb-record-set-mail record (list (cadr data))) + (setf (bbdb-record-firstname record) (car name)) + (setf (bbdb-record-lastname record) (cdr name)) + (setf (bbdb-record-mail record) (list (cadr data))) (delete-region (point-min) (point-max)))) (defun bbdb-snarf-mail (record) @@ -224,7 +224,7 @@ This uses the first subexpresion of `bbdb-snarf-mail-regexp'." (while (re-search-forward bbdb-snarf-mail-regexp nil t) (push (match-string 1) mails) (replace-match "")) - (bbdb-record-set-mail record (nconc (bbdb-record-mail record) mails)))) + (setf (bbdb-record-mail record) (nconc (bbdb-record-mail record) mails)))) (defun bbdb-snarf-label (field) "Extract the label before point, or return default label for FIELD." @@ -250,8 +250,8 @@ This uses the first subexpresion of `bbdb-snarf-phone-nanp-regexp'." (bbdb-parse-phone (match-string 1)))) phones) (replace-match ""))) - (bbdb-record-set-phone record (nconc (bbdb-record-phone record) - (nreverse phones))))) + (setf (bbdb-record-phone record) (nconc (bbdb-record-phone record) + (nreverse phones))))) (defun bbdb-snarf-phone-eu (record &optional phone-regexp) "Snarf European phone numbers for RECORD. @@ -259,41 +259,44 @@ PHONE-REGEXP is the regexp to match a phone number. It defaults to `bbdb-snarf-phone-eu-regexp'." (let ((case-fold-search t) phones) (while (re-search-forward (or phone-regexp - bbdb-snarf-phone-eu-regexp) nil t) + bbdb-snarf-phone-eu-regexp) + nil t) (goto-char (match-beginning 0)) (push (vector (bbdb-snarf-label 'phone) (match-string 1)) phones) (replace-match "")) - (bbdb-record-set-phone record (nconc (bbdb-record-phone record) - (nreverse phones))))) + (setf (bbdb-record-phone record) (nconc (bbdb-record-phone record) + (nreverse phones))))) (defun bbdb-snarf-streets (address) "Snarf streets for ADDRESS. This assumes a narrowed region." - (bbdb-address-set-streets address (bbdb-split "\n" (buffer-string))) + (setf (bbdb-address-streets address) (bbdb-split "\n" (buffer-string))) (delete-region (point-min) (point-max))) (defun bbdb-snarf-address-us (record) "Snarf a US address for RECORD." - (let ((address (make-vector bbdb-address-length nil))) + (let ((address (bbdb-address--make))) (cond ((re-search-forward bbdb-snarf-postcode-us-regexp nil t) ;; Streets, City, State Postcode (save-restriction (narrow-to-region (point-min) (match-end 0)) ;; Postcode (goto-char (match-beginning 0)) - (bbdb-address-set-postcode address - (bbdb-parse-postcode (match-string 1))) + (setf (bbdb-address-postcode address) + (bbdb-parse-postcode (match-string 1))) ;; State (skip-chars-backward " \t") (let ((pos (point))) (skip-chars-backward "^ \t,") - (bbdb-address-set-state address (buffer-substring (point) pos))) + (setf (bbdb-address-state address) + (buffer-substring (point) pos))) ;; City (skip-chars-backward " \t,") (let ((pos (point))) (beginning-of-line) - (bbdb-address-set-city address (buffer-substring (point) pos))) + (setf (bbdb-address-city address) + (buffer-substring (point) pos))) ;; Toss it (forward-char -1) (delete-region (point) (point-max)) @@ -303,8 +306,8 @@ It defaults to `bbdb-snarf-phone-eu-regexp'." ;; Try for just Streets, City, State ((let (case-fold-search) (re-search-forward "^\\(.*\\), \\([A-Z][A-Za-z]\\)$" nil t)) - (bbdb-address-set-city address (match-string 1)) - (bbdb-address-set-state address (match-string 2)) + (setf (bbdb-address-city address) (match-string 1)) + (setf (bbdb-address-state address) (match-string 2)) (replace-match "") (save-restriction (narrow-to-region (point-min) (match-beginning 0)) @@ -312,13 +315,13 @@ It defaults to `bbdb-snarf-phone-eu-regexp'." (bbdb-snarf-streets address)))) (when (bbdb-address-city address) (if bbdb-snarf-address-us-country - (bbdb-address-set-country address bbdb-snarf-address-us-country)) + (setf (bbdb-address-country address) bbdb-snarf-address-us-country)) ;; Fixme: There are no labels anymore. `bbdb-snarf-streets' snarfed ;; everything that was left! - (bbdb-address-set-label address (bbdb-snarf-label 'address)) - (bbdb-record-set-address record - (nconc (bbdb-record-address record) - (list address)))))) + (setf (bbdb-address-label address) (bbdb-snarf-label 'address)) + (setf (bbdb-record-address record) + (nconc (bbdb-record-address record) + (list address)))))) (defun bbdb-snarf-address-eu (record &optional postcode-regexp country) "Snarf a European address for RECORD. @@ -328,27 +331,29 @@ is used in many continental European countries. POSTCODE-REGEXP defaults to `bbdb-snarf-postcode-eu-regexp'. COUNTRY is the country to use. It defaults to `bbdb-snarf-address-eu-country'." (when (re-search-forward (or postcode-regexp - bbdb-snarf-postcode-eu-regexp) nil t) - (let ((address (make-vector bbdb-address-length nil))) + bbdb-snarf-postcode-eu-regexp) + nil t) + (let ((address (bbdb-address--make))) (save-restriction (goto-char (match-end 0)) (narrow-to-region (point-min) (line-end-position)) ;; Postcode - (bbdb-address-set-postcode address (match-string 1)) + (setf (bbdb-address-postcode address) (match-string 1)) ;; City (skip-chars-forward " \t") - (bbdb-address-set-city address (buffer-substring (point) (point-max))) + (setf (bbdb-address-city address) + (buffer-substring (point) (point-max))) ;; Toss it (delete-region (match-beginning 0) (point-max)) ;; Streets (goto-char (point-min)) (bbdb-snarf-streets address)) (unless country (setq country bbdb-snarf-address-eu-country)) - (if country (bbdb-address-set-country address country)) - (bbdb-address-set-label address (bbdb-snarf-label 'address)) - (bbdb-record-set-address record - (nconc (bbdb-record-address record) - (list address)))))) + (if country (setf (bbdb-address-country address) country)) + (setf (bbdb-address-label address) (bbdb-snarf-label 'address)) + (setf (bbdb-record-address record) + (nconc (bbdb-record-address record) + (list address)))))) (defun bbdb-snarf-url (record) "Snarf URL for RECORD. @@ -356,19 +361,17 @@ This uses the first subexpresion of `bbdb-snarf-url-regexp'." (when (and bbdb-snarf-url (let ((case-fold-search t)) (re-search-forward bbdb-snarf-url-regexp nil t))) - (bbdb-record-set-xfields - record - (nconc (bbdb-record-xfields record) - (list (cons bbdb-snarf-url (match-string 1))))) + (setf (bbdb-record-xfields record) + (nconc (bbdb-record-xfields record) + (list (cons bbdb-snarf-url (match-string 1))))) (replace-match ""))) (defun bbdb-snarf-notes (record) "Snarf notes for RECORD." (when (/= (point-min) (point-max)) - (bbdb-record-set-xfields - record - (nconc (bbdb-record-xfields record) - (list (cons bbdb-default-xfield (buffer-string))))) + (setf (bbdb-record-xfields record) + (nconc (bbdb-record-xfields record) + (list (cons bbdb-default-xfield (buffer-string))))) (erase-buffer))) (defsubst bbdb-snarf-rule-interactive () diff --git a/bbdb.el b/bbdb.el index 9030978..05967ec 100644 --- a/bbdb.el +++ b/bbdb.el @@ -1,10 +1,10 @@ ;;; bbdb.el --- core of BBDB -*- lexical-binding: t -*- -;; Copyright (C) 2010-2019 Free Software Foundation, Inc. +;; Copyright (C) 2010-2020 Free Software Foundation, Inc. ;; Maintainer: Roland Winkler <wink...@gnu.org> ;; Version: 3.2 -;; Package-Requires: ((emacs "24")) +;; Package-Requires: ((emacs "24") (cl-lib "0.5")) ;; This file is part of the Insidious Big Brother Database (aka BBDB), @@ -2344,57 +2344,68 @@ This strips garbage from the user full NAME string." (substring-no-properties name)) ;; BBDB data structure -(defmacro bbdb-defstruct (name &rest elts) - ;; FIXME: Use cl-defstruct instead! - "Define two functions to operate on vector NAME for each symbol ELT in ELTS. -The function bbdb-NAME-ELT returns the element ELT in vector NAME. -The function bbdb-NAME-set-ELT sets ELT. -Also define a constant bbdb-NAME-length that holds the number of ELTS -in vector NAME." + +(defmacro bbdb--defun-obsolete-setters (type &optional fields) + "Define obsolete setters that used to be defined by the old `bbdb-defstruct'." (declare (indent 1)) - (let* ((count 0) - (sname (symbol-name name)) - (uname (upcase sname)) - (cname (concat "bbdb-" sname "-")) - body) - (dolist (elt elts) - (let* ((selt (symbol-name elt)) - (setname (intern (concat cname "set-" selt)))) - (push (list 'defsubst (intern (concat cname selt)) `(,name) - (format "For BBDB %s read element %i `%s'." - uname count selt) - ;; Use `elt' instead of `aref' so that these functions - ;; also work for the `bbdb-record-type' pseudo-code. - `(elt ,name ,count)) - body) - (push (list 'defsubst setname `(,name value) - (format "For BBDB %s set element %i `%s' to VALUE. \ -Return VALUE. -Do not call this function directly. Call instead `bbdb-record-set-field' -which ensures the integrity of the database. Also, this makes your code -more robust with respect to possible future changes of BBDB's innermost -internals." - uname count selt) - `(aset ,name ,count value)) - body)) - (setq count (1+ count))) - (push (list 'defconst (intern (concat cname "length")) count - (concat "Length of BBDB `" sname "'.")) - body) - (cons 'progn body))) + (unless fields + ;; This only works in Emacsā„25! + (let* ((class (cl-find-class type)) + (slots (cl--class-slots class))) + (setq fields (mapcar #'cl--slot-descriptor-name slots)))) + `(progn + ,@(mapcar (lambda (field) + (let ((accessor (intern (format "%s-%s" type field)))) + `(defun ,(intern (format "%s-set-%s" type field)) (obj val) + (declare (obsolete + ,(format "use (setf (%s OBJ) VAL)) instead" + accessor) + "BBDB-3.3")) + (setf (,accessor obj) val)))) + fields))) ;; Define RECORD: -(bbdb-defstruct record +(defalias 'bbdb-record-p #'vectorp) +(cl-defstruct (bbdb-record + (:type vector) + (:constructor nil) + (:constructor bbdb-record--make ())) firstname lastname affix aka organization phone address mail xfields uuid creation-date timestamp cache) +(bbdb--defun-obsolete-setters bbdb-record + (firstname lastname affix aka organization phone address mail xfields + uuid creation-date timestamp cache)) ;; Define PHONE: -(bbdb-defstruct phone +;; FIXME: Currently, the PHONE structure can have two internal formats. +;; - It can be a vector with elements (LABEL AREA EXCHANGE SUFFIX EXTENSION), +;; where LABEL is a string and the remaining elements are integer numbers. +;; This scheme refers to the North American Numbering Plan (NANP). +;; - It can be a vector with elements (LABEL STRING) both of which are strings. +;; Does the NANP format have any real benefits for NANP numbers? +;; There should be only one internal format for all phone numbers. Likely, +;; this scheme should represent all phone numbers as strings. +;; Also, it should allow comments as a separate string. +;; Inspired by RFC3966, it could be something like +;; [country code [area code [local number]]] [extension} [comment] +;; However, this will often require sophisticated parsing. Is it +;; worth the effort compared with using a single string? +(defalias 'bbdb-phone-p #'vectorp) +(defalias 'bbdb-phone--make #'vector) +(cl-defstruct (bbdb-phone + (:type vector) + (:constructor nil)) label area exchange suffix extension) +(bbdb--defun-obsolete-setters bbdb-phone (label area exchange suffix extension)) ;; Define ADDRESS: -(bbdb-defstruct address +(cl-defstruct (bbdb-address + (:type vector) + (:constructor nil) + (:constructor bbdb-address--make ())) label streets city state postcode country) +(bbdb--defun-obsolete-setters bbdb-address + (label streets city state postcode country)) ;; Define record CACHE: ;; - fl-name (first and last name of the person referred to by the record), @@ -2403,7 +2414,10 @@ internals." ;; - mail-canon (list of canonical mail addresses) ;; - sortkey (the concatenation of the elements used for sorting the record), ;; - marker (position of beginning of record in `bbdb-file') -(bbdb-defstruct cache +(cl-defstruct (bbdb-cache + (:type vector) + (:constructor nil) + (:constructor bbdb-cache--make ())) fl-name lf-name mail-aka mail-canon sortkey marker) (defsubst bbdb-record-mail-aka (record) @@ -2417,8 +2431,8 @@ internals." (defun bbdb-empty-record () "Return a new empty record structure with a cache. It is the caller's responsibility to make the new record known to BBDB." - (let ((record (make-vector bbdb-record-length nil))) - (bbdb-record-set-cache record (make-vector bbdb-cache-length nil)) + (let ((record (bbdb-record--make))) + (setf (bbdb-record-cache record) (bbdb-cache--make)) record)) ;; `bbdb-hashtable' associates with each KEY a list of matching records. @@ -2510,10 +2524,10 @@ KEY must be a string or nil. Empty strings and nil are ignored." (bbdb-puthash (car address) record)) (push (nth 1 address) mail-canon) (bbdb-puthash (nth 1 address) record)) - (bbdb-cache-set-mail-aka (bbdb-record-cache record) - (nreverse mail-aka)) - (bbdb-cache-set-mail-canon (bbdb-record-cache record) - (nreverse mail-canon)))) + (setf (bbdb-cache-mail-aka (bbdb-record-cache record)) + (nreverse mail-aka)) + (setf (bbdb-cache-mail-canon (bbdb-record-cache record)) + (nreverse mail-canon)))) (defun bbdb-hash-update (record old new) "Update hash for RECORD. Remove OLD, insert NEW. @@ -2601,16 +2615,16 @@ Set full name in cache and hash. Return first-last name." (if lf-name (bbdb-remhash lf-name record))) (if (eq t first) (setq first (bbdb-record-firstname record)) - (bbdb-record-set-firstname record first)) + (setf (bbdb-record-firstname record) first)) (if (eq t last) (setq last (bbdb-record-lastname record)) - (bbdb-record-set-lastname record last)) + (setf (bbdb-record-lastname record) last)) (let ((fl-name (bbdb-concat 'name-first-last first last)) (lf-name (bbdb-concat 'name-last-first last first)) (cache (bbdb-record-cache record))) ;; Set cache of RECORD - (bbdb-cache-set-fl-name cache fl-name) - (bbdb-cache-set-lf-name cache lf-name) + (setf (bbdb-cache-fl-name cache) fl-name) + (setf (bbdb-cache-lf-name cache) lf-name) ;; Set hash. For convenience, the hash contains the full name ;; as first-last and last-fist. (bbdb-puthash fl-name record) @@ -2625,12 +2639,11 @@ Set and store it if necessary." (defun bbdb-record-set-sortkey (record) "Record cache function: Set and return RECORD's sortkey." - (bbdb-cache-set-sortkey - (bbdb-record-cache record) - (downcase - (bbdb-concat "" (bbdb-record-lastname record) - (bbdb-record-firstname record) - (bbdb-record-organization record))))) + (setf (bbdb-cache-sortkey (bbdb-record-cache record)) + (downcase + (bbdb-concat "" (bbdb-record-lastname record) + (bbdb-record-firstname record) + (bbdb-record-organization record))))) (defsubst bbdb-record-marker (record) "Record cache function: Return the marker for RECORD." @@ -2638,7 +2651,7 @@ Set and store it if necessary." (defsubst bbdb-record-set-marker (record marker) "Record cache function: Set and return RECORD's MARKER." - (bbdb-cache-set-marker (bbdb-record-cache record) marker)) + (setf (bbdb-cache-marker (bbdb-record-cache record)) marker)) (defsubst bbdb-record-xfield (record label) "For RECORD return value of xfield LABEL. @@ -2688,16 +2701,17 @@ Return VALUE." (setcdr old-xfield value)) (value ; new xfield (bbdb-pushnewq label bbdb-xfield-label-list) - (bbdb-record-set-xfields record - (append (bbdb-record-xfields record) - (list (cons label value))))) + (setf (bbdb-record-xfields record) + (append (bbdb-record-xfields record) + (list (cons label value))))) (old-xfield ; remove - (bbdb-record-set-xfields record - (delq old-xfield - (bbdb-record-xfields record)))))) + (setf (bbdb-record-xfields record) + (delq old-xfield + (bbdb-record-xfields record)))))) value) (defun bbdb-check-type (object type &optional abort extended) + ;; FIXME: Use `cl-typep'? "Return non-nil if OBJECT is of type TYPE. TYPE is a pseudo-code as in `bbdb-record-type'. If ABORT is non-nil, abort with error message if type checking fails. @@ -2722,7 +2736,8 @@ symbols, numbers, markers, and strings." (setq tmp (= type object)) t) ((stringp type) (setq tmp (and (stringp object) - (string= type object))) t))) + (string= type object))) + t))) tmp) ((not (consp type)) (error "Atomic type `%s' undefined" type)) @@ -2838,6 +2853,7 @@ See also `bbdb-record-set-field'." (define-obsolete-function-alias 'bbdb-record-get-field #'bbdb-record-field "3.0") (defun bbdb-record-set-field (record field value &optional merge check) + ;; FIXME: No caller passes the `check' argument! "For RECORD set FIELD to VALUE. Return VALUE. If MERGE is non-nil, merge VALUE with the current value of FIELD. If CHECK is non-nil, check syntactically whether FIELD may take VALUE. @@ -2867,7 +2883,8 @@ See also `bbdb-record-field'." (bbdb-editable) (if (memq field '(name-lf mail-aka mail-canon aka-all)) (error "`%s' is not allowed as the name of a field" field)) - (let ((record-type (cdr bbdb-record-type))) + ;; FIXME: Use something like `bbdb-record--make' i.s.o `vector'. + (let ((record-type (apply #'vector (cdr bbdb-record-type)))) (cond ((eq field 'firstname) ; First name (if merge (error "Does not merge names")) (if check (bbdb-check-type value (bbdb-record-firstname record-type) t)) @@ -2896,7 +2913,7 @@ See also `bbdb-record-field'." value 'bbdb-string=))) (if check (bbdb-check-type value (bbdb-record-affix record-type) t)) (setq value (bbdb-list-strings value)) - (bbdb-record-set-affix record value)) + (setf (bbdb-record-affix record) value)) ;; Organization ((eq field 'organization) @@ -2907,7 +2924,7 @@ See also `bbdb-record-field'." (bbdb-hash-update record (bbdb-record-organization record) value) (dolist (organization value) (bbdb-pushnew organization bbdb-organization-list)) - (bbdb-record-set-organization record value)) + (setf (bbdb-record-organization record) value)) ;; AKA ((eq field 'aka) @@ -2917,7 +2934,7 @@ See also `bbdb-record-field'." (setq value (bbdb-list-strings value)) (bbdb-check-name value record) (bbdb-hash-update record (bbdb-record-aka record) value) - (bbdb-record-set-aka record value)) + (setf (bbdb-record-aka record) value)) ;; Mail ((eq field 'mail) @@ -2930,7 +2947,7 @@ See also `bbdb-record-field'." (bbdb-remhash aka record)) (dolist (mail (bbdb-record-mail-canon record)) (bbdb-remhash mail record)) - (bbdb-record-set-mail record value) + (setf (bbdb-record-mail record) value) (bbdb-puthash-mail record)) ;; Phone @@ -2940,7 +2957,7 @@ See also `bbdb-record-field'." (if check (bbdb-check-type value (bbdb-record-phone record-type) t)) (dolist (phone value) (bbdb-pushnew (bbdb-phone-label phone) bbdb-phone-label-list)) - (bbdb-record-set-phone record value)) + (setf (bbdb-record-phone record) value)) ;; Address ((eq field 'address) @@ -2955,7 +2972,7 @@ See also `bbdb-record-field'." (bbdb-pushnewt (bbdb-address-state address) bbdb-state-list) (bbdb-pushnewt (bbdb-address-postcode address) bbdb-postcode-list) (bbdb-pushnewt (bbdb-address-country address) bbdb-country-list)) - (bbdb-record-set-address record value)) + (setf (bbdb-record-address record) value)) ;; uuid ((eq field 'uuid) @@ -2964,20 +2981,20 @@ See also `bbdb-record-field'." (let ((old-uuid (bbdb-record-uuid record))) (unless (string= old-uuid value) (remhash old-uuid bbdb-uuid-table) - (bbdb-record-set-uuid record value) + (setf (bbdb-record-uuid record) value) (puthash value record bbdb-uuid-table)))) ;; creation-date ((eq field 'creation-date) ;; MERGE not meaningful (if check (bbdb-check-type value (bbdb-record-creation-date record-type) t)) - (bbdb-record-set-creation-date record value)) + (setf (bbdb-record-creation-date record) value)) ;; timestamp ((eq field 'timestamp) ;; MERGE not meaningful (if check (bbdb-check-type value (bbdb-record-timestamp record-type) t)) - (bbdb-record-set-timestamp record value)) + (setf (bbdb-record-timestamp record) value)) ;; all xfields ((eq field 'xfields) @@ -2997,7 +3014,7 @@ See also `bbdb-record-field'." (when (and (cdr xfield) (not (equal "" (cdr xfield)))) (push xfield new-xfields) (bbdb-pushnewq (car xfield) bbdb-xfield-label-list))) - (bbdb-record-set-xfields record (nreverse new-xfields)))) + (setf (bbdb-record-xfields record) (nreverse new-xfields)))) ;; Single xfield ((symbolp field) @@ -3107,7 +3124,7 @@ Do this only if `bbdb-check-postcode' is non-nil." string)) (defun bbdb-phone-string (phone) - "Massage string PHONE into a standard format." + "Massage vector PHONE into a standard format." ;; Phone numbers should come in two forms: (if (= 2 (length phone)) ;; (1) ["where" "the number"] @@ -3268,7 +3285,10 @@ BBDB is not editable if it is read-only." t) ;;;###autoload -(defsubst bbdb-records () +(defun bbdb-records () + ;; We used to define it as a `defsubst' but those are treated differently + ;; by the ;;;###autoload machinery: calling the function didn't load + ;; bbdb.el, so the call to bbdb-buffer then failed :-( "Return a list of all BBDB records; read in and parse the db if necessary. This function also notices if the corresponding file on disk has been modified." (with-current-buffer (bbdb-buffer) @@ -3405,9 +3425,9 @@ If `bbdb-file' uses an outdated format, migrate to `bbdb-file-format'." (unless (looking-at "\\[") (error "BBDB corrupted: junk between records at %s" (point)))) - (bbdb-cache-set-marker - (bbdb-record-set-cache record (make-vector bbdb-cache-length nil)) - (point-marker)) + (setf (bbdb-cache-marker + (setf (bbdb-record-cache record) (bbdb-cache--make))) + (point-marker)) (forward-line 1) ;; Every record must have a unique uuid in `bbdb-uuid-table'. @@ -3552,12 +3572,12 @@ They are present only for backward compatibility." bbdb-end-marker)))) (let ((cache (bbdb-record-cache record)) (inhibit-quit t)) - (bbdb-record-set-cache record nil) + (setf (bbdb-record-cache record) nil) (prog1 (bbdb-with-print-loadably (prin1-to-string record)) - (bbdb-record-set-cache record cache)))))) - (bbdb-record-set-timestamp - record (format-time-string bbdb-time-stamp-format nil t)) + (setf (bbdb-record-cache record) cache)))))) + (setf (bbdb-record-timestamp record) + (format-time-string bbdb-time-stamp-format nil t)) (run-hook-with-args 'bbdb-change-hook record) (let ((sort (not (equal (bbdb-cache-sortkey (bbdb-record-cache record)) (bbdb-record-set-sortkey record))))) @@ -3575,12 +3595,12 @@ They are present only for backward compatibility." ;; Record is new and not yet in BBDB. (unless (bbdb-record-cache record) - (bbdb-record-set-cache record (make-vector bbdb-cache-length nil))) + (setf (bbdb-record-cache record) (bbdb-cache--make))) (unless (bbdb-record-uuid record) - (bbdb-record-set-uuid record (bbdb-uuid))) + (setf (bbdb-record-uuid record) (bbdb-uuid))) (unless (bbdb-record-creation-date record) - (bbdb-record-set-creation-date - record (format-time-string bbdb-time-stamp-format nil t)) + (setf (bbdb-record-creation-date record) + (format-time-string bbdb-time-stamp-format nil t)) (run-hook-with-args 'bbdb-create-hook record)) (let ((old-record (gethash (bbdb-record-uuid record) bbdb-uuid-table))) @@ -3590,8 +3610,8 @@ They are present only for backward compatibility." record old-record) ;; RECORD is really new. - (bbdb-record-set-timestamp - record (format-time-string bbdb-time-stamp-format nil t)) + (setf (bbdb-record-timestamp record) + (format-time-string bbdb-time-stamp-format nil t)) (run-hook-with-args 'bbdb-change-hook record) (bbdb-register-record record) ; Call this earlier? (bbdb-insert-record-internal record) @@ -3661,11 +3681,11 @@ that calls the hooks, too." (if (and (/= point bbdb-end-marker) (not (looking-at "^\\["))) (error "Not inserting before a record (%s)" point))) - (bbdb-record-set-cache record nil) + (setf (bbdb-record-cache record) nil) (insert-before-markers (bbdb-with-print-loadably (prin1-to-string record)) "\n") (set-marker (bbdb-cache-marker cache) point) - (bbdb-record-set-cache record cache)) + (setf (bbdb-record-cache record) cache)) record)) (defun bbdb-overwrite-record-internal (record) @@ -3687,13 +3707,13 @@ that calls the hooks, too." (not (looking-at "\\["))) (error "Not inserting before a record (%s)" (point)))) - (bbdb-record-set-cache record nil) + (setf (bbdb-record-cache record) nil) (insert (bbdb-with-print-loadably (prin1-to-string record)) "\n") (delete-region (point) (if (cdr tail) (bbdb-record-marker (car (cdr tail))) bbdb-end-marker)) - (bbdb-record-set-cache record cache) + (setf (bbdb-record-cache record) cache) (bbdb-debug (if (<= (if (cdr tail) @@ -4721,9 +4741,9 @@ however, after having used other programs to add records to the BBDB." ;; and update the cache's marker. (setq cache (bbdb-record-cache record)) (set-marker (bbdb-cache-marker cache) (point)) - (bbdb-record-set-cache record nil) + (setf (bbdb-record-cache record) nil) (bbdb-with-print-loadably (prin1 record buf)) - (bbdb-record-set-cache record cache) + (setf (bbdb-record-cache record) cache) (insert ?\n))) (dolist (buffer (buffer-list)) (with-current-buffer buffer