eudc-export.el 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. ;;; eudc-export.el --- functions to export EUDC query results
  2. ;; Copyright (C) 1998-2017 Free Software Foundation, Inc.
  3. ;; Author: Oscar Figueiredo <oscar@cpe.fr>
  4. ;; Pavel Janík <Pavel@Janik.cz>
  5. ;; Maintainer: Thomas Fitzsimmons <fitzsim@fitzsim.org>
  6. ;; Keywords: comm
  7. ;; Package: eudc
  8. ;; This file is part of GNU Emacs.
  9. ;; GNU Emacs is free software: you can redistribute it and/or modify
  10. ;; it under the terms of the GNU General Public License as published by
  11. ;; the Free Software Foundation, either version 3 of the License, or
  12. ;; (at your option) any later version.
  13. ;; GNU Emacs is distributed in the hope that it will be useful,
  14. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. ;; GNU General Public License for more details.
  17. ;; You should have received a copy of the GNU General Public License
  18. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  19. ;;; Commentary:
  20. ;;; Usage:
  21. ;; See the corresponding info file
  22. ;;; Code:
  23. (require 'eudc)
  24. ;; NOERROR is so we can compile it.
  25. (require 'bbdb nil t)
  26. (require 'bbdb-com nil t)
  27. (defun eudc-create-bbdb-record (record &optional silent)
  28. "Create a BBDB record using the RECORD alist.
  29. RECORD is an alist of (KEY . VALUE) where KEY is a directory attribute name
  30. symbol and VALUE is the corresponding value for the record.
  31. If SILENT is non-nil then the created BBDB record is not displayed."
  32. (require 'bbdb)
  33. ;; This function runs in a special context where lisp symbols corresponding
  34. ;; to field names in record are bound to the corresponding values
  35. (eval
  36. `(let* (,@(mapcar (lambda (c)
  37. (list (car c) (if (listp (cdr c))
  38. (list 'quote (cdr c))
  39. (cdr c))))
  40. record)
  41. bbdb-name
  42. bbdb-company
  43. bbdb-net
  44. bbdb-address
  45. bbdb-phones
  46. bbdb-notes
  47. spec
  48. bbdb-record
  49. value
  50. (conversion-alist (symbol-value eudc-bbdb-conversion-alist)))
  51. ;; BBDB standard fields
  52. (setq bbdb-name (eudc-parse-spec (cdr (assq 'name conversion-alist)) record nil)
  53. bbdb-company (eudc-parse-spec (cdr (assq 'company conversion-alist)) record nil)
  54. bbdb-net (eudc-parse-spec (cdr (assq 'net conversion-alist)) record nil)
  55. bbdb-notes (eudc-parse-spec (cdr (assq 'notes conversion-alist)) record nil))
  56. (setq spec (cdr (assq 'address conversion-alist)))
  57. (setq bbdb-address (delq nil (eudc-parse-spec (if (listp (car spec))
  58. spec
  59. (list spec))
  60. record t)))
  61. (setq spec (cdr (assq 'phone conversion-alist)))
  62. (setq bbdb-phones (delq nil (eudc-parse-spec (if (listp (car spec))
  63. spec
  64. (list spec))
  65. record t)))
  66. ;; BBDB custom fields
  67. (setq bbdb-notes (append (list (and bbdb-notes (cons 'notes bbdb-notes)))
  68. (mapcar (function
  69. (lambda (mapping)
  70. (if (and (not (memq (car mapping)
  71. '(name company net address phone notes)))
  72. (setq value (eudc-parse-spec (cdr mapping) record nil)))
  73. (cons (car mapping) value))))
  74. conversion-alist)))
  75. (setq bbdb-notes (delq nil bbdb-notes))
  76. (setq bbdb-record (bbdb-create-internal
  77. bbdb-name
  78. ,@(when (eudc--using-bbdb-3-or-newer-p)
  79. '(nil
  80. nil))
  81. bbdb-company
  82. bbdb-net
  83. ,@(if (eudc--using-bbdb-3-or-newer-p)
  84. '(bbdb-phones
  85. bbdb-address)
  86. '(bbdb-address
  87. bbdb-phones))
  88. bbdb-notes))
  89. (or silent
  90. (bbdb-display-records (list bbdb-record))))))
  91. (defun eudc-parse-spec (spec record recurse)
  92. "Parse the conversion SPEC using RECORD.
  93. If RECURSE is non-nil then SPEC may be a list of atomic specs."
  94. (cond
  95. ((or (stringp spec)
  96. (symbolp spec)
  97. (and (listp spec)
  98. (symbolp (car spec))
  99. (fboundp (car spec))))
  100. (condition-case nil
  101. (eval spec)
  102. (void-variable nil)))
  103. ((and recurse
  104. (listp spec))
  105. (mapcar (lambda (spec-elem)
  106. (eudc-parse-spec spec-elem record nil))
  107. spec))
  108. (t
  109. (error "Invalid specification for `%s' in `eudc-bbdb-conversion-alist'" spec))))
  110. (defun eudc-bbdbify-address (addr location)
  111. "Parse ADDR into a vector compatible with BBDB.
  112. ADDR should be an address string of no more than four lines or a
  113. list of lines.
  114. The last two lines are searched for the zip code, city and state name.
  115. LOCATION is used as the address location for bbdb."
  116. (let* ((addr-components (if (listp addr)
  117. (reverse addr)
  118. (reverse (split-string addr "\n"))))
  119. (last1 (pop addr-components))
  120. (last2 (pop addr-components))
  121. zip city state)
  122. (setq addr-components (nreverse addr-components))
  123. ;; If not containing the zip code the last line is supposed to contain a
  124. ;; country name and the address is supposed to be in european style
  125. (if (not (string-match "[0-9][0-9][0-9]" last1))
  126. (progn
  127. (setq state last1)
  128. (if (string-match "\\([0-9]+\\)[ \t]+\\(.*\\)" last2)
  129. (setq city (match-string 2 last2)
  130. zip (string-to-number (match-string 1 last2)))
  131. (error "Cannot parse the address")))
  132. (cond
  133. ;; American style
  134. ((string-match "\\(\\w+\\)\\W*\\([A-Z][A-Z]\\)\\W*\\([0-9]+\\)" last1)
  135. (setq city (match-string 1 last1)
  136. state (match-string 2 last1)
  137. zip (string-to-number (match-string 3 last1))))
  138. ;; European style
  139. ((string-match "\\([0-9]+\\)[ \t]+\\(.*\\)" last1)
  140. (setq city (match-string 2 last1)
  141. zip (string-to-number (match-string 1 last1))))
  142. (t
  143. (error "Cannot parse the address"))))
  144. (vector location
  145. (or (nth 0 addr-components) "")
  146. (or (nth 1 addr-components) "")
  147. (or (nth 2 addr-components) "")
  148. (or city "")
  149. (or state "")
  150. zip)))
  151. ;; External.
  152. (declare-function bbdb-parse-phone-number "ext:bbdb-com"
  153. (string &optional number-type))
  154. (declare-function bbdb-parse-phone "ext:bbdb-com" (string &optional style))
  155. (declare-function bbdb-string-trim "ext:bbdb" (string))
  156. (defun eudc-bbdbify-company (&rest organizations)
  157. "Return ORGANIZATIONS as a list compatible with BBDB."
  158. organizations)
  159. (defun eudc-bbdbify-phone (phone location)
  160. "Parse PHONE into a vector compatible with BBDB.
  161. PHONE is either a string supposedly containing a phone number or
  162. a list of such strings which are concatenated.
  163. LOCATION is used as the phone location for BBDB."
  164. (require 'bbdb)
  165. (cond
  166. ((stringp phone)
  167. (let (phone-list)
  168. (condition-case err
  169. (setq phone-list (if (eudc--using-bbdb-3-or-newer-p)
  170. (bbdb-parse-phone phone)
  171. (bbdb-parse-phone-number phone)))
  172. (error
  173. (if (string= "phone number unparsable." (cadr err))
  174. (if (not (y-or-n-p (format "BBDB claims %S to be unparsable--insert anyway? " phone)))
  175. (error "Phone number unparsable")
  176. (setq phone-list (list (bbdb-string-trim phone))))
  177. (signal (car err) (cdr err)))))
  178. (if (= 3 (length phone-list))
  179. (setq phone-list (append phone-list '(nil))))
  180. (apply 'vector location phone-list)))
  181. ((listp phone)
  182. (vector location (mapconcat 'identity phone ", ")))
  183. (t
  184. (error "Invalid phone specification"))))
  185. (defun eudc-batch-export-records-to-bbdb ()
  186. "Insert all the records returned by a directory query into BBDB."
  187. (interactive)
  188. (require 'bbdb)
  189. (goto-char (point-min))
  190. (let ((nbrec 0)
  191. record)
  192. (while (eudc-move-to-next-record)
  193. (and (overlays-at (point))
  194. (setq record (overlay-get (car (overlays-at (point))) 'eudc-record))
  195. (1+ nbrec)
  196. (eudc-create-bbdb-record record t)))
  197. (message "%d records imported into BBDB" nbrec)))
  198. ;;;###autoload
  199. (defun eudc-insert-record-at-point-into-bbdb ()
  200. "Insert record at point into the BBDB database.
  201. This function can only be called from a directory query result buffer."
  202. (interactive)
  203. (require 'bbdb)
  204. (let ((record (and (overlays-at (point))
  205. (overlay-get (car (overlays-at (point))) 'eudc-record))))
  206. (if (null record)
  207. (error "Point is not over a record")
  208. (eudc-create-bbdb-record record))))
  209. ;;;###autoload
  210. (defun eudc-try-bbdb-insert ()
  211. "Call `eudc-insert-record-at-point-into-bbdb' if on a record."
  212. (interactive)
  213. (require 'bbdb)
  214. (and (overlays-at (point))
  215. (overlay-get (car (overlays-at (point))) 'eudc-record)
  216. (eudc-insert-record-at-point-into-bbdb)))
  217. ;;; eudc-export.el ends here