1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2026-04-05 22:02:01 -07:00
emacs/lisp/net/eudc-export.el
Stefan Monnier 08e109d45a Use the new error API functions
* lisp/epa-file.el (epa-file--find-file-not-found-function):
Use `error-slot-value` and `error-data`.
(epa-file-insert-file-contents): Use `error-has-type-p`,
`error-slot-value`, and `error-data`.

* lisp/jka-compr.el (jka-compr-insert-file-contents):
Use `error-has-type-p` and `error-slot-value` as well as new
re-signaling form of `signal`.

* lisp/simple.el (minibuffer-error-function): Use `error-has-type-p`.

* lisp/startup.el (startup--load-user-init-file):
Use `error-message-string`.
(command-line): Use `error-has-type-p` and `error-message-string`.

* lisp/type-break.el (type-break-demo-life):
Use `error-message-string`.

* lisp/emacs-lisp/bytecomp.el (batch-byte-compile-file):
Use `error-message-string` and `error-has-type-p`.

* lisp/emacs-lisp/edebug.el (edebug-safe-eval, edebug-report-error)
(edebug-eval-expression):
* lisp/emacs-lisp/debug.el (debugger-eval-expression):
Use `error-message-string`.

* lisp/emacs-lisp/ert.el (ert--should-error-handle-error):
Use `error-has-type-p` and `error-type`.

* lisp/net/sasl.el (sasl-error): Use `define-error`.

* lisp/net/tramp-compat.el (tramp-error-type-p): New function.
(tramp-permission-denied, tramp-compat-permission-denied): Use it.

* lisp/progmodes/elisp-mode.el (elisp-completion-at-point):
Use `error-type-p`.

* lisp/xt-mouse.el (turn-on-xterm-mouse-tracking-on-terminal)
(turn-off-xterm-mouse-tracking-on-terminal): Use `error-slot-value`.

* lisp/simple.el (next-line, previous-line): Remove useless
`condition-case` handler, and hence the whole `condition-case`, and
then simplify.

* lisp/gnus/nnrss.el (nnrss-insert): Use `with-demoted-errors`.

* lisp/gnus/nnmaildir.el (nnmaildir--emlink-p, nnmaildir--enoent-p)
(nnmaildir--eexist-p): Use `error-has-type-p`.
(nnmaildir--new-number, nnmaildir-request-set-mark): Use single-arg
`signal`.

* lisp/ffap.el (ffap-machine-p): Use `error-slot-value`.

* lisp/emacs-lisp/comp.el (comp--native-compile):
Use `error-has-type-p` as well as single-arg `signal`.

* lisp/net/ange-ftp.el (ange-ftp-hook-function): Use single-arg
`signal`.

* lisp/ebuff-menu.el (electric-buffer-menu-looper): Use `error-has-type-p`.
* lisp/progmodes/ebrowse.el (ebrowse-electric-list-looper):
Use `error-has-type-p`.
(ebrowse-electric-position-looper): Make it an alias of
`ebrowse-electric-list-looper`.

* lisp/ibuffer.el (ibuffer-confirm-operation-on):
* lisp/ls-lisp.el (ls-lisp--insert-directory):
* lisp/gnus/gnus-search.el (gnus-search-run-query):
* lisp/mail/mail-extr.el (mail-extr-safe-move-sexp):
* lisp/net/dbus.el (dbus-set-property):
* lisp/net/eudc-export.el (eudc-bbdbify-phone):
* lisp/net/imap.el (imap-fetch-safe):
* lisp/vc/vc.el (vc-root-dir): Use `error-slot-value` and
single-arg `signal` to re-signal.
2026-03-10 10:48:21 -04:00

238 lines
8.5 KiB
EmacsLisp

;;; eudc-export.el --- functions to export EUDC query results -*- lexical-binding: t; -*-
;; Copyright (C) 1998-2026 Free Software Foundation, Inc.
;; Author: Oscar Figueiredo <oscar@cpe.fr>
;; Pavel Janík <Pavel@Janik.cz>
;; Maintainer: Thomas Fitzsimmons <fitzsim@fitzsim.org>
;; Keywords: comm
;; Package: eudc
;; This file is part of GNU Emacs.
;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; GNU Emacs 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. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;;; Usage:
;; See the corresponding info file
;;; Code:
(require 'eudc)
;; NOERROR is so we can compile it.
(require 'bbdb nil t)
(require 'bbdb-com nil t)
(require 'cl-lib)
(defun eudc-create-bbdb-record (record &optional silent)
"Create a BBDB record using the RECORD alist.
RECORD is an alist of (KEY . VALUE) where KEY is a directory attribute name
symbol and VALUE is the corresponding value for the record.
If SILENT is non-nil then the created BBDB record is not displayed."
(require 'bbdb)
(declare-function bbdb-create-internal "bbdb-com" (&rest spec))
(declare-function bbdb-display-records "bbdb"
(records &optional layout append))
;; This function runs in a special context where lisp symbols corresponding
;; to field names in record are bound to the corresponding values
(cl-progv (mapcar #'car record) (mapcar #'cdr record)
(let* (bbdb-name
bbdb-company
bbdb-net
bbdb-address
bbdb-phones
bbdb-notes
spec
bbdb-record
value
(conversion-alist (symbol-value eudc-bbdb-conversion-alist)))
;; BBDB standard fields
(setq bbdb-name (eudc-parse-spec (cdr (assq 'name conversion-alist)) record nil)
bbdb-company (eudc-parse-spec (cdr (assq 'company conversion-alist)) record nil)
bbdb-net (eudc-parse-spec (cdr (assq 'net conversion-alist)) record nil)
bbdb-notes (eudc-parse-spec (cdr (assq 'notes conversion-alist)) record nil))
(setq spec (cdr (assq 'address conversion-alist)))
(setq bbdb-address (delq nil (eudc-parse-spec (if (listp (car spec))
spec
(list spec))
record t)))
(setq spec (cdr (assq 'phone conversion-alist)))
(setq bbdb-phones (delq nil (eudc-parse-spec (if (listp (car spec))
spec
(list spec))
record t)))
;; BBDB custom fields
(setq bbdb-notes (append (list (and bbdb-notes (cons 'notes bbdb-notes)))
(mapcar (lambda (mapping)
(if (and (not (memq (car mapping)
'(name company net address phone notes)))
(setq value (eudc-parse-spec (cdr mapping) record nil)))
(cons (car mapping) value)))
conversion-alist)))
(setq bbdb-notes (delq nil bbdb-notes))
(setq bbdb-record
(apply #'bbdb-create-internal
`(,bbdb-name
,@(when (eudc--using-bbdb-3-or-newer-p)
'(nil
nil))
,bbdb-company
,bbdb-net
,@(if (eudc--using-bbdb-3-or-newer-p)
(list bbdb-phones
bbdb-address)
(list bbdb-address
bbdb-phones))
,bbdb-notes)))
(or silent
(bbdb-display-records (list bbdb-record))))))
(defun eudc-parse-spec (spec record recurse)
"Parse the conversion SPEC using RECORD.
If RECURSE is non-nil then SPEC may be a list of atomic specs."
(cond
((or (stringp spec)
(symbolp spec)
(and (listp spec)
(symbolp (car spec))
(fboundp (car spec))))
(condition-case nil
(eval spec t)
(void-variable nil)))
((and recurse
(listp spec))
(mapcar (lambda (spec-elem)
(eudc-parse-spec spec-elem record nil))
spec))
(t
(error "Invalid specification for `%s' in `eudc-bbdb-conversion-alist'" spec))))
(defun eudc-bbdbify-address (addr location)
"Parse ADDR into a vector compatible with BBDB.
ADDR should be an address string of no more than four lines or a
list of lines.
The last two lines are searched for the zip code, city and state name.
LOCATION is used as the address location for bbdb."
(let* ((addr-components (if (listp addr)
(reverse addr)
(reverse (split-string addr "\n"))))
(last1 (pop addr-components))
(last2 (pop addr-components))
zip city state)
(setq addr-components (nreverse addr-components))
;; If not containing the zip code the last line is supposed to contain a
;; country name and the address is supposed to be in european style
(if (not (string-match "[0-9][0-9][0-9]" last1))
(progn
(setq state last1)
(if (string-match "\\([0-9]+\\)[ \t]+\\(.*\\)" last2)
(setq city (match-string 2 last2)
zip (string-to-number (match-string 1 last2)))
(error "Cannot parse the address")))
(cond
;; American style
((string-match "\\(\\w+\\)\\W*\\([A-Z][A-Z]\\)\\W*\\([0-9]+\\)" last1)
(setq city (match-string 1 last1)
state (match-string 2 last1)
zip (string-to-number (match-string 3 last1))))
;; European style
((string-match "\\([0-9]+\\)[ \t]+\\(.*\\)" last1)
(setq city (match-string 2 last1)
zip (string-to-number (match-string 1 last1))))
(t
(error "Cannot parse the address"))))
(vector location
(or (nth 0 addr-components) "")
(or (nth 1 addr-components) "")
(or (nth 2 addr-components) "")
(or city "")
(or state "")
zip)))
;; External.
(declare-function bbdb-parse-phone-number "ext:bbdb-com"
(string &optional number-type))
(declare-function bbdb-parse-phone "ext:bbdb-com" (string &optional style))
(declare-function bbdb-string-trim "ext:bbdb" (string))
(defun eudc-bbdbify-company (&rest organizations)
"Return ORGANIZATIONS as a list compatible with BBDB."
organizations)
(defun eudc-bbdbify-phone (phone location)
"Parse PHONE into a vector compatible with BBDB.
PHONE is either a string supposedly containing a phone number or
a list of such strings which are concatenated.
LOCATION is used as the phone location for BBDB."
(require 'bbdb)
(cond
((stringp phone)
(let (phone-list)
(condition-case err
(setq phone-list (if (eudc--using-bbdb-3-or-newer-p)
(bbdb-parse-phone phone)
(bbdb-parse-phone-number phone)))
(error
(if (equal "phone number unparsable." (error-slot-value err 1))
(if (not (y-or-n-p (format "BBDB claims %S to be unparsable--insert anyway? " phone)))
(error "Phone number unparsable")
(setq phone-list (list (bbdb-string-trim phone))))
(signal err))))
(if (= 3 (length phone-list))
(setq phone-list (append phone-list '(nil))))
(apply #'vector location phone-list)))
((listp phone)
(vector location (mapconcat #'identity phone ", ")))
(t
(error "Invalid phone specification"))))
(defun eudc-batch-export-records-to-bbdb ()
"Insert all the records returned by a directory query into BBDB."
(interactive)
(require 'bbdb)
(goto-char (point-min))
(let ((nbrec 0)
record)
(while (eudc-move-to-next-record)
(and (overlays-at (point))
(setq record (overlay-get (car (overlays-at (point))) 'eudc-record))
(setq nbrec (1+ nbrec))
(eudc-create-bbdb-record record t)))
(message "%d records imported into BBDB" nbrec)))
;;;###autoload
(defun eudc-insert-record-at-point-into-bbdb ()
"Insert record at point into the BBDB database.
This function can only be called from a directory query result buffer."
(interactive)
(require 'bbdb)
(let ((record (and (overlays-at (point))
(overlay-get (car (overlays-at (point))) 'eudc-record))))
(if (null record)
(error "Point is not over a record")
(eudc-create-bbdb-record record))))
;;;###autoload
(defun eudc-try-bbdb-insert ()
"Call `eudc-insert-record-at-point-into-bbdb' if on a record."
(interactive)
(require 'bbdb)
(and (overlays-at (point))
(overlay-get (car (overlays-at (point))) 'eudc-record)
(eudc-insert-record-at-point-into-bbdb)))
;;; eudc-export.el ends here