mailalias.el 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597
  1. ;;; mailalias.el --- expand and complete mailing address aliases -*- lexical-binding: t -*-
  2. ;; Copyright (C) 1985, 1987, 1995-1997, 2001-2017 Free Software
  3. ;; Foundation, Inc.
  4. ;; Maintainer: emacs-devel@gnu.org
  5. ;; Keywords: mail
  6. ;; This file is part of GNU Emacs.
  7. ;; GNU Emacs is free software: you can redistribute it and/or modify
  8. ;; it under the terms of the GNU General Public License as published by
  9. ;; the Free Software Foundation, either version 3 of the License, or
  10. ;; (at your option) any later version.
  11. ;; GNU Emacs is distributed in the hope that it will be useful,
  12. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. ;; GNU General Public License for more details.
  15. ;; You should have received a copy of the GNU General Public License
  16. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  17. ;;; Commentary:
  18. ;; Basic functions for defining and expanding mail aliases.
  19. ;; These seal off the interface to the alias-definition parts of a
  20. ;; .mailrc file formatted for BSD's Mail or USL's mailx.
  21. ;;; Code:
  22. (require 'sendmail)
  23. (defgroup mailalias nil
  24. "Expanding mail aliases."
  25. :group 'mail)
  26. (defcustom mail-passwd-files '("/etc/passwd")
  27. "List of files from which to determine valid user names."
  28. :type '(repeat string)
  29. :group 'mailalias)
  30. (defcustom mail-passwd-command nil
  31. "Shell command to retrieve text to add to `/etc/passwd', or nil."
  32. :type '(choice string (const nil))
  33. :group 'mailalias)
  34. (defvar mail-directory-names t
  35. "Alist of mail address directory entries.
  36. When t this still needs to be initialized.")
  37. (defvar mail-address-field-regexp
  38. "^\\(Resent-\\)?\\(To\\|From\\|CC\\|BCC\\|Reply-to\\):")
  39. (defvar pattern)
  40. (defcustom mail-complete-alist
  41. ;; Don't refer to mail-address-field-regexp here;
  42. ;; that confuses some things such as cus-dep.el.
  43. '(("^\\(Resent-\\)?\\(To\\|From\\|CC\\|BCC\\|Reply-to\\):"
  44. . (mail-get-names pattern))
  45. ("Newsgroups:" . (if (boundp 'gnus-active-hashtb)
  46. gnus-active-hashtb
  47. (if (boundp news-group-article-assoc)
  48. news-group-article-assoc)))
  49. ("Followup-To:" . (mail-sentto-newsgroups))
  50. ;;("Distribution:" ???)
  51. )
  52. "Alist of header field and expression to return alist for completion.
  53. The expression may reference the variable `pattern'
  54. which will hold the string being completed.
  55. If not on matching header, `mail-complete-function' gets called instead."
  56. :type 'alist
  57. :group 'mailalias)
  58. (put 'mail-complete-alist 'risky-local-variable t)
  59. ;;;###autoload
  60. (defcustom mail-complete-style 'angles
  61. "Specifies how \\[mail-complete] formats the full name when it completes.
  62. If nil, they contain just the return address like:
  63. king@grassland.com
  64. If `parens', they look like:
  65. king@grassland.com (Elvis Parsley)
  66. If `angles', they look like:
  67. Elvis Parsley <king@grassland.com>"
  68. :type '(choice (const angles) (const parens) (const nil))
  69. :group 'mailalias)
  70. (defcustom mail-complete-function 'ispell-complete-word
  71. "Function to call when completing outside `mail-complete-alist'-header."
  72. :type '(choice function (const nil))
  73. :group 'mailalias)
  74. (make-obsolete-variable 'mail-complete-function
  75. 'completion-at-point-functions "24.1")
  76. (defcustom mail-directory-function nil
  77. "Function to get completions from directory service or nil for none.
  78. See `mail-directory-requery'."
  79. :type '(choice function (const nil))
  80. :group 'mailalias)
  81. ;; This is for when the directory is huge, or changes frequently.
  82. (defcustom mail-directory-requery nil
  83. "When non-nil call `mail-directory-function' for each completion.
  84. In that case, one argument gets passed to the function, the partial string
  85. entered so far."
  86. :type 'boolean
  87. :group 'mailalias)
  88. (defcustom mail-directory-process nil
  89. "Shell command to get the list of names from a mail directory.
  90. This value is used when the value of `mail-directory-function'
  91. is `mail-directory-process'. The value should be a list
  92. of the form (COMMAND ARG ...), where each of the list elements
  93. is evaluated. COMMAND should evaluate to a string. When
  94. `mail-directory-requery' is non-nil, during evaluation of these
  95. elements, the variable `pattern' contains the partial input being
  96. completed. `pattern' is nil when `mail-directory-requery' is nil.
  97. The value might look like this:
  98. (remote-shell-program \"HOST\" \"-nl\" \"USER\" \"COMMAND\")
  99. or like this:
  100. (remote-shell-program \"HOST\" \"-n\" \"COMMAND \\='^\" pattern \"\\='\")"
  101. :type 'sexp
  102. :group 'mailalias)
  103. (put 'mail-directory-process 'risky-local-variable t)
  104. (defcustom mail-directory-stream nil
  105. "List of (HOST SERVICE) for stream connection to mail directory."
  106. :type '(choice (const nil)
  107. (list (string :tag "Host name or ip address")
  108. (choice (integer :tag "Service port number")
  109. (string :tag "Service name"))
  110. (plist :inline t
  111. :tag "Additional open-network-stream parameters")))
  112. :group 'mailalias)
  113. (put 'mail-directory-stream 'risky-local-variable t)
  114. (defcustom mail-directory-parser nil
  115. "How to interpret the output of `mail-directory-function'.
  116. Three types of values are possible:
  117. - nil means to gather each line as one name
  118. - regexp means first \\(grouping\\) in successive matches is name
  119. - function called at beginning of buffer that returns an alist of names"
  120. :type '(choice (const nil) regexp function)
  121. :group 'mailalias)
  122. (put 'mail-directory-parser 'risky-local-variable t)
  123. ;; Internal variables.
  124. (defvar mail-names t
  125. "Alist of local users, aliases and directory entries as available.
  126. Elements have the form (MAILNAME) or (MAILNAME . FULLNAME).
  127. If the value means t, it means the real value should be calculated
  128. for the next use. This is used in `mail-complete'.")
  129. (defvar mail-local-names t
  130. "Alist of local users.
  131. When t this still needs to be initialized.")
  132. ;; Called from sendmail-send-it, or similar functions,
  133. ;; only if some mail aliases are defined.
  134. ;;;###autoload
  135. (defun expand-mail-aliases (beg end &optional exclude)
  136. "Expand all mail aliases in suitable header fields found between BEG and END.
  137. If interactive, expand in header fields.
  138. Suitable header fields are `To', `From', `CC' and `BCC', `Reply-to', and
  139. their `Resent-' variants.
  140. Optional second arg EXCLUDE may be a regular expression defining text to be
  141. removed from alias expansions."
  142. (interactive
  143. (save-excursion
  144. (list (goto-char (point-min))
  145. (mail-header-end))))
  146. (sendmail-sync-aliases)
  147. (when (eq mail-aliases t)
  148. (setq mail-aliases nil)
  149. (build-mail-aliases))
  150. (save-excursion
  151. (goto-char beg)
  152. (setq end (set-marker (make-marker) end))
  153. (let ((case-fold-search nil))
  154. (while (let ((case-fold-search t))
  155. (re-search-forward mail-address-field-regexp end t))
  156. (skip-chars-forward " \t")
  157. (let ((beg1 (point))
  158. end1 pos epos seplen
  159. ;; DISABLED-ALIASES records aliases temporarily disabled
  160. ;; while we scan text that resulted from expanding those aliases.
  161. ;; Each element is (ALIAS . TILL-WHEN), where TILL-WHEN
  162. ;; is where to reenable the alias (expressed as number of chars
  163. ;; counting from END1).
  164. (disabled-aliases nil))
  165. (re-search-forward "^[^ \t]" end 'move)
  166. (beginning-of-line)
  167. (skip-chars-backward " \t\n")
  168. (setq end1 (point-marker))
  169. (goto-char beg1)
  170. (while (< (point) end1)
  171. (setq pos (point))
  172. ;; Reenable any aliases which were disabled for ranges
  173. ;; that we have passed out of.
  174. (while (and disabled-aliases
  175. (> pos (- end1 (cdr (car disabled-aliases)))))
  176. (setq disabled-aliases (cdr disabled-aliases)))
  177. ;; EPOS gets position of end of next name;
  178. ;; SEPLEN gets length of whitespace&separator that follows it.
  179. (if (re-search-forward "[ \t]*[\n,][ \t]*" end1 t)
  180. (setq epos (match-beginning 0)
  181. seplen (- (point) epos))
  182. ;; Handle the last name in this header field.
  183. ;; We already moved END1 back across whitespace after it.
  184. (setq epos (marker-position end1) seplen 0))
  185. (let ((string (buffer-substring-no-properties pos epos))
  186. translation)
  187. (if (and (not (assoc string disabled-aliases))
  188. (setq translation (cdr (assoc string mail-aliases))))
  189. (progn
  190. ;; This name is an alias. Disable it.
  191. (setq disabled-aliases (cons (cons string (- end1 epos))
  192. disabled-aliases))
  193. ;; Replace the alias with its expansion
  194. ;; then rescan the expansion for more aliases.
  195. (goto-char pos)
  196. (insert translation)
  197. (when exclude
  198. (let ((regexp (concat "\\b\\(" exclude "\\)\\b"))
  199. (end (point-marker)))
  200. (goto-char pos)
  201. (while (re-search-forward regexp end t)
  202. (replace-match ""))
  203. (goto-char end)))
  204. (delete-region (point) (+ (point) (- epos pos)))
  205. (goto-char pos))
  206. ;; Name is not an alias. Skip to start of next name.
  207. (goto-char epos)
  208. (forward-char seplen))))
  209. (set-marker end1 nil)))
  210. (set-marker end nil))))
  211. ;; Called by mail-setup, or similar functions, only if the file specified
  212. ;; by mail-personal-alias-file (usually `~/.mailrc') exists.
  213. (defun build-mail-aliases (&optional file)
  214. "Read mail aliases from personal aliases file and set `mail-aliases'.
  215. By default, this is the file specified by `mail-personal-alias-file'."
  216. (interactive
  217. (list
  218. (read-file-name (format "Read mail alias file (default %s): "
  219. mail-personal-alias-file)
  220. nil mail-personal-alias-file t)))
  221. (setq file (expand-file-name (or file mail-personal-alias-file)))
  222. ;; In case mail-aliases is t, make sure define-mail-alias
  223. ;; does not recursively call build-mail-aliases.
  224. (setq mail-aliases nil)
  225. (with-temp-buffer
  226. (while file
  227. (cond ((get-file-buffer file)
  228. (insert (with-current-buffer (get-file-buffer file)
  229. (buffer-substring-no-properties
  230. (point-min) (point-max)))))
  231. ((file-exists-p file) (insert-file-contents file))
  232. ((file-exists-p (setq file (expand-file-name file "~/")))
  233. (insert-file-contents file))
  234. (t (setq file nil)))
  235. (goto-char (point-min))
  236. ;; Delete comments from the contents.
  237. (while (search-forward "# " nil t)
  238. (let ((p (- (point) 2)))
  239. (end-of-line)
  240. (delete-region p (point))))
  241. ;; Don't lose if no final newline.
  242. (goto-char (point-max))
  243. (or (eq (preceding-char) ?\n) (newline))
  244. (goto-char (point-min))
  245. ;; handle "\\\n" continuation lines
  246. (while (not (eobp))
  247. (end-of-line)
  248. (if (= (preceding-char) ?\\)
  249. (progn (delete-char -1) (delete-char 1) (insert ?\ ))
  250. (forward-char 1)))
  251. (goto-char (point-min))
  252. ;; handle `source' directives -- Eddy/1994/May/25
  253. (cond ((re-search-forward "^source[ \t]+" nil t)
  254. (re-search-forward "\\S-+")
  255. (setq file (buffer-substring-no-properties
  256. (match-beginning 0) (match-end 0)))
  257. (beginning-of-line)
  258. (insert "# ") ; to ensure we don't re-process this file
  259. (beginning-of-line))
  260. (t (setq file nil))))
  261. (goto-char (point-min))
  262. (while (re-search-forward
  263. "^\\(a\\|alias\\|g\\|group\\)[ \t]+\\([^ \t\n]+\\)" nil t)
  264. (let* ((name (match-string 2))
  265. (start (progn (skip-chars-forward " \t") (point)))
  266. value)
  267. (end-of-line)
  268. (setq value (buffer-substring-no-properties start (point)))
  269. (unless (equal value "")
  270. (define-mail-alias name value t))))
  271. mail-aliases))
  272. ;; Always autoloadable in case the user wants to define aliases
  273. ;; interactively or in .emacs.
  274. ;; define-mail-abbrev in mailabbrev.el duplicates much of this code.
  275. ;;;###autoload
  276. (defun define-mail-alias (name definition &optional from-mailrc-file)
  277. "Define NAME as a mail alias that translates to DEFINITION.
  278. This means that sending a message to NAME will actually send to DEFINITION.
  279. Normally, the addresses in DEFINITION must be separated by commas.
  280. If FROM-MAILRC-FILE is non-nil, then addresses in DEFINITION
  281. can be separated by spaces; an address can contain spaces
  282. if it is quoted with double-quotes."
  283. (interactive "sDefine mail alias: \nsDefine %s as mail alias for: ")
  284. ;; Read the defaults first, if we have not done so.
  285. ;; But not if we are doing that already right now.
  286. (unless from-mailrc-file
  287. (sendmail-sync-aliases))
  288. (if (eq mail-aliases t)
  289. (progn
  290. (setq mail-aliases nil)
  291. (if (file-exists-p mail-personal-alias-file)
  292. (build-mail-aliases))))
  293. ;; strip garbage from front and end
  294. (if (string-match "\\`[ \t\n,]+" definition)
  295. (setq definition (substring definition (match-end 0))))
  296. (if (string-match "[ \t\n,]+\\'" definition)
  297. (setq definition (substring definition 0 (match-beginning 0))))
  298. (let* ((L (length definition))
  299. (start (if (> L 0) 0))
  300. end this-entry result tem)
  301. (while start
  302. (cond
  303. (from-mailrc-file
  304. ;; If we're reading from the mailrc file, addresses are
  305. ;; delimited by spaces, and addresses with embedded spaces are
  306. ;; surrounded by non-escaped double-quotes.
  307. (if (eq ?\" (aref definition start))
  308. (setq start (1+ start)
  309. end (and (string-match
  310. "[^\\]\\(\\([\\][\\]\\)*\\)\"[ \t,]*"
  311. definition start)
  312. (match-end 1)))
  313. (setq end (string-match "[ \t,]+" definition start)))
  314. ;; Extract the address and advance the loop past it.
  315. (setq this-entry (substring definition start end)
  316. start (and end (/= (match-end 0) L) (match-end 0)))
  317. ;; If the full name contains a problem character, quote it.
  318. (and (string-match "\\(.+?\\)[ \t]*\\(<.*>\\)" this-entry)
  319. (string-match "[^- !#$%&'*+/0-9=?A-Za-z^_`{|}~]"
  320. (match-string 1 this-entry))
  321. (setq this-entry (replace-regexp-in-string
  322. "\\(.+?\\)[ \t]*\\(<.*>\\)"
  323. "\"\\1\" \\2"
  324. this-entry))))
  325. ;; When we are not reading from .mailrc, addresses are
  326. ;; separated by commas. Try to accept a rfc822-like syntax.
  327. ;; (Todo: extend rfc822.el to do the work for us.)
  328. ((equal (string-match
  329. "[ \t,]*\\(\"\\(?:[^\"]\\|[^\\]\\(?:[\\][\\]\\)*\"\\)*\"[ \t]*\
  330. <[-.!#$%&'*+/0-9=?A-Za-z^_`{|}~@]+>\\)[ \t,]*"
  331. definition start)
  332. start)
  333. ;; If an entry has a valid [ "foo bar" <foo@example.com> ]
  334. ;; form, use it literally . This also allows commas in the
  335. ;; quoted string, e.g. [ "foo bar, jr" <foo@example.com> ]
  336. (setq this-entry (match-string 1 definition)
  337. start (and (/= (match-end 0) L) (match-end 0))))
  338. (t
  339. ;; Otherwise, read the next address by looking for a comma.
  340. (setq end (string-match "[ \t\n,]*,[ \t\n]*" definition start))
  341. (setq this-entry (substring definition start end))
  342. ;; Advance the loop past this address.
  343. (setq start (and end (/= (match-end 0) L) (match-end 0)))
  344. ;; If the full name contains a problem character, quote it.
  345. (and (string-match "\\(.+?\\)[ \t]*\\(<.*>\\)" this-entry)
  346. (string-match "[^- !#$%&'*+/0-9=?A-Za-z^_`{|}~]"
  347. (match-string 1 this-entry))
  348. (setq this-entry (replace-regexp-in-string
  349. "\\(.+?\\)[ \t]*\\(<.*>\\)" "\"\\1\" \\2"
  350. this-entry)))))
  351. (push this-entry result))
  352. (setq definition (mapconcat (function identity)
  353. (nreverse result) ", "))
  354. (setq tem (assoc name mail-aliases))
  355. (if tem
  356. (rplacd tem definition)
  357. (setq mail-aliases (cons (cons name definition) mail-aliases)
  358. mail-names t))))
  359. ;;;###autoload
  360. (defun mail-completion-at-point-function ()
  361. "Compute completion data for mail aliases.
  362. For use on `completion-at-point-functions'."
  363. ;; Read the defaults first, if we have not done so.
  364. (sendmail-sync-aliases)
  365. (if (eq mail-aliases t)
  366. (progn
  367. (setq mail-aliases nil)
  368. (if (file-exists-p mail-personal-alias-file)
  369. (build-mail-aliases))))
  370. (let ((list mail-complete-alist)
  371. (list-exp nil))
  372. (if (and (< 0 (mail-header-end))
  373. (save-excursion
  374. (if (re-search-backward "^[^\t ]" nil t)
  375. (while list
  376. (if (looking-at (car (car list)))
  377. (setq list-exp (cdr (car list))
  378. list ())
  379. (setq list (cdr list)))))
  380. list-exp))
  381. (let* ((end (point))
  382. (beg (save-excursion
  383. (skip-chars-backward "^ \t<,:")
  384. (point)))
  385. (table (completion-table-dynamic
  386. (lambda (prefix)
  387. (let ((pattern prefix)) (eval list-exp))))))
  388. (list beg end table)))))
  389. ;;;###autoload
  390. (defun mail-complete (arg)
  391. "Perform completion on header field or word preceding point.
  392. Completable headers are according to `mail-complete-alist'. If none matches
  393. current header, calls `mail-complete-function' and passes prefix ARG if any."
  394. (declare (obsolete mail-completion-at-point-function "24.1"))
  395. (interactive "P")
  396. ;; Read the defaults first, if we have not done so.
  397. (sendmail-sync-aliases)
  398. (if (eq mail-aliases t)
  399. (progn
  400. (setq mail-aliases nil)
  401. (if (file-exists-p mail-personal-alias-file)
  402. (build-mail-aliases))))
  403. (let ((data (mail-completion-at-point-function)))
  404. (if data
  405. (apply #'completion-in-region data)
  406. (funcall mail-complete-function arg))))
  407. (defun mail-completion-expand (table)
  408. "Build new completion table that expands aliases.
  409. Completes like TABLE except that if the completion is a valid alias,
  410. it expands it to its full `mail-complete-style' form."
  411. (lambda (string pred action)
  412. (cond
  413. ((eq action nil)
  414. (let* ((comp (try-completion string table pred))
  415. (name (and (listp table) comp
  416. (assoc (if (stringp comp) comp string) table))))
  417. (cond
  418. ((null name) comp)
  419. ((eq mail-complete-style 'parens)
  420. (concat (car name) " (" (cdr name) ")"))
  421. ((eq mail-complete-style 'angles)
  422. (concat (cdr name) " <" (car name) ">"))
  423. (t comp))))
  424. (t
  425. (complete-with-action action table string pred)))))
  426. (defun mail-get-names (prefix)
  427. "Fetch local users and global mail addresses for completion.
  428. Consults `/etc/passwd' and a directory service if one is set up via
  429. `mail-directory-function'.
  430. PREFIX is the string we want to complete."
  431. (if (eq mail-local-names t)
  432. (with-current-buffer (generate-new-buffer " passwd")
  433. (let ((files mail-passwd-files))
  434. (while files
  435. (insert-file-contents (car files) nil nil nil t)
  436. (setq files (cdr files))))
  437. (if mail-passwd-command
  438. (call-process shell-file-name nil t nil
  439. shell-command-switch mail-passwd-command))
  440. (goto-char (point-min))
  441. (setq mail-local-names nil)
  442. (while (not (eobp))
  443. ;;Recognize lines like
  444. ;; nobody:*:65534:65534::/:
  445. ;; +demo::::::/bin/csh
  446. ;; +ethanb
  447. ;;while skipping
  448. ;; +@SOFTWARE
  449. ;; The second \(...\) matches the user id.
  450. (if (looking-at "\\+?\\([^:@\n+]+\\):[^:\n]*:\\([^\n:]*\\):")
  451. (add-to-list 'mail-local-names
  452. (cons (match-string 1)
  453. (user-full-name
  454. (string-to-number (match-string 2))))))
  455. (beginning-of-line 2))
  456. (kill-buffer (current-buffer))))
  457. (if (or (eq mail-names t)
  458. (eq mail-directory-names t))
  459. (let (directory)
  460. (and mail-directory-function
  461. (eq mail-directory-names t)
  462. (setq directory
  463. (mail-directory (if mail-directory-requery prefix))))
  464. (or mail-directory-requery
  465. (setq mail-directory-names directory))
  466. (if (or directory
  467. (eq mail-names t))
  468. (setq mail-names
  469. (sort (append (if (consp mail-aliases)
  470. (mapcar
  471. (function (lambda (a) (list (car a))))
  472. mail-aliases))
  473. (if (consp mail-local-names)
  474. mail-local-names)
  475. (or directory
  476. (when (consp mail-directory-names)
  477. mail-directory-names)))
  478. (lambda (a b)
  479. ;; Should cache downcased strings.
  480. (string< (downcase (car a))
  481. (downcase (car b)))))))))
  482. (mail-completion-expand mail-names))
  483. (defun mail-directory (prefix)
  484. "Use mail-directory facility to get user names matching PREFIX.
  485. If PREFIX is nil, get all the defined user names.
  486. This function calls `mail-directory-function' to query the directory,
  487. then uses `mail-directory-parser' to parse the output it returns."
  488. (message "Querying directory...")
  489. (with-current-buffer (generate-new-buffer " *mail-directory*")
  490. (funcall mail-directory-function prefix)
  491. (goto-char (point-min))
  492. (let (directory)
  493. (if (stringp mail-directory-parser)
  494. (while (re-search-forward mail-directory-parser nil t)
  495. (push (match-string 1) directory))
  496. (if mail-directory-parser
  497. (setq directory (funcall mail-directory-parser))
  498. (while (not (eobp))
  499. (push (buffer-substring (point)
  500. (progn
  501. (forward-line)
  502. (if (bolp)
  503. (1- (point))
  504. (point))))
  505. directory))))
  506. (kill-buffer (current-buffer))
  507. (message "Querying directory...done")
  508. directory)))
  509. (defvar mailalias-done)
  510. (defun mail-directory-process (prefix)
  511. "Run a shell command to output names in directory.
  512. See `mail-directory-process'."
  513. (when (consp mail-directory-process)
  514. (let ((pattern prefix)) ;Dynbind!
  515. (apply 'call-process (eval (car mail-directory-process)) nil t nil
  516. (mapcar 'eval (cdr mail-directory-process))))))
  517. ;; This should handle a dialog. Currently expects port to spit out names.
  518. (defun mail-directory-stream (prefix)
  519. "Open a stream to retrieve names in directory.
  520. See `mail-directory-stream'."
  521. (let ((mailalias-done nil)
  522. (pattern prefix)) ;Dynbind!
  523. (set-process-sentinel
  524. (apply 'open-network-stream "mailalias" (current-buffer)
  525. mail-directory-stream)
  526. (lambda (_x _y)
  527. (setq mailalias-done t)))
  528. (while (not mailalias-done)
  529. (sit-for .1))))
  530. (defun mail-sentto-newsgroups ()
  531. "Return all entries from Newsgroups: header as completion alist."
  532. (save-excursion
  533. (if (mail-position-on-field "newsgroups" t)
  534. (let ((point (point))
  535. list)
  536. (while (< (skip-chars-backward "^:, \t\n") 0)
  537. (setq list `((,(buffer-substring (point) point))
  538. ,@list))
  539. (skip-chars-backward ", \t\n")
  540. (setq point (point)))
  541. list))))
  542. (provide 'mailalias)
  543. ;;; mailalias.el ends here