mailalias.el 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590
  1. ;;; mailalias.el --- expand and complete mailing address aliases -*- lexical-binding: t -*-
  2. ;; Copyright (C) 1985, 1987, 1995-1997, 2001-2012
  3. ;; Free Software Foundation, Inc.
  4. ;; Maintainer: FSF
  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 'sexp
  107. :group 'mailalias)
  108. (put 'mail-directory-stream 'risky-local-variable t)
  109. (defcustom mail-directory-parser nil
  110. "How to interpret the output of `mail-directory-function'.
  111. Three types of values are possible:
  112. - nil means to gather each line as one name
  113. - regexp means first \\(grouping\\) in successive matches is name
  114. - function called at beginning of buffer that returns an alist of names"
  115. :type '(choice (const nil) regexp function)
  116. :group 'mailalias)
  117. (put 'mail-directory-parser 'risky-local-variable t)
  118. ;; Internal variables.
  119. (defvar mail-names t
  120. "Alist of local users, aliases and directory entries as available.
  121. Elements have the form (MAILNAME) or (MAILNAME . FULLNAME).
  122. If the value means t, it means the real value should be calculated
  123. for the next use. This is used in `mail-complete'.")
  124. (defvar mail-local-names t
  125. "Alist of local users.
  126. When t this still needs to be initialized.")
  127. ;; Called from sendmail-send-it, or similar functions,
  128. ;; only if some mail aliases are defined.
  129. ;;;###autoload
  130. (defun expand-mail-aliases (beg end &optional exclude)
  131. "Expand all mail aliases in suitable header fields found between BEG and END.
  132. If interactive, expand in header fields.
  133. Suitable header fields are `To', `From', `CC' and `BCC', `Reply-to', and
  134. their `Resent-' variants.
  135. Optional second arg EXCLUDE may be a regular expression defining text to be
  136. removed from alias expansions."
  137. (interactive
  138. (save-excursion
  139. (list (goto-char (point-min))
  140. (mail-header-end))))
  141. (sendmail-sync-aliases)
  142. (when (eq mail-aliases t)
  143. (setq mail-aliases nil)
  144. (build-mail-aliases))
  145. (save-excursion
  146. (goto-char beg)
  147. (setq end (set-marker (make-marker) end))
  148. (let ((case-fold-search nil))
  149. (while (let ((case-fold-search t))
  150. (re-search-forward mail-address-field-regexp end t))
  151. (skip-chars-forward " \t")
  152. (let ((beg1 (point))
  153. end1 pos epos seplen
  154. ;; DISABLED-ALIASES records aliases temporarily disabled
  155. ;; while we scan text that resulted from expanding those aliases.
  156. ;; Each element is (ALIAS . TILL-WHEN), where TILL-WHEN
  157. ;; is where to reenable the alias (expressed as number of chars
  158. ;; counting from END1).
  159. (disabled-aliases nil))
  160. (re-search-forward "^[^ \t]" end 'move)
  161. (beginning-of-line)
  162. (skip-chars-backward " \t\n")
  163. (setq end1 (point-marker))
  164. (goto-char beg1)
  165. (while (< (point) end1)
  166. (setq pos (point))
  167. ;; Reenable any aliases which were disabled for ranges
  168. ;; that we have passed out of.
  169. (while (and disabled-aliases
  170. (> pos (- end1 (cdr (car disabled-aliases)))))
  171. (setq disabled-aliases (cdr disabled-aliases)))
  172. ;; EPOS gets position of end of next name;
  173. ;; SEPLEN gets length of whitespace&separator that follows it.
  174. (if (re-search-forward "[ \t]*[\n,][ \t]*" end1 t)
  175. (setq epos (match-beginning 0)
  176. seplen (- (point) epos))
  177. (setq epos (marker-position end1) seplen 0))
  178. (let ((string (buffer-substring-no-properties pos epos))
  179. translation)
  180. (if (and (not (assoc string disabled-aliases))
  181. (setq translation (cdr (assoc string mail-aliases))))
  182. (progn
  183. ;; This name is an alias. Disable it.
  184. (setq disabled-aliases (cons (cons string (- end1 epos))
  185. disabled-aliases))
  186. ;; Replace the alias with its expansion
  187. ;; then rescan the expansion for more aliases.
  188. (goto-char pos)
  189. (insert translation)
  190. (when exclude
  191. (let ((regexp (concat "\\b\\(" exclude "\\)\\b"))
  192. (end (point-marker)))
  193. (goto-char pos)
  194. (while (re-search-forward regexp end t)
  195. (replace-match ""))
  196. (goto-char end)))
  197. (delete-region (point) (+ (point) (- epos pos)))
  198. (goto-char pos))
  199. ;; Name is not an alias. Skip to start of next name.
  200. (goto-char epos)
  201. (forward-char seplen))))
  202. (set-marker end1 nil)))
  203. (set-marker end nil))))
  204. ;; Called by mail-setup, or similar functions, only if the file specified
  205. ;; by mail-personal-alias-file (usually `~/.mailrc') exists.
  206. (defun build-mail-aliases (&optional file)
  207. "Read mail aliases from personal aliases file and set `mail-aliases'.
  208. By default, this is the file specified by `mail-personal-alias-file'."
  209. (interactive
  210. (list
  211. (read-file-name (format "Read mail alias file (default %s): "
  212. mail-personal-alias-file)
  213. nil mail-personal-alias-file t)))
  214. (setq file (expand-file-name (or file mail-personal-alias-file)))
  215. ;; In case mail-aliases is t, make sure define-mail-alias
  216. ;; does not recursively call build-mail-aliases.
  217. (setq mail-aliases nil)
  218. (with-temp-buffer
  219. (while file
  220. (cond ((get-file-buffer file)
  221. (insert (with-current-buffer (get-file-buffer file)
  222. (buffer-substring-no-properties
  223. (point-min) (point-max)))))
  224. ((file-exists-p file) (insert-file-contents file))
  225. ((file-exists-p (setq file (expand-file-name file "~/")))
  226. (insert-file-contents file))
  227. (t (setq file nil)))
  228. (goto-char (point-min))
  229. ;; Delete comments from the contents.
  230. (while (search-forward "# " nil t)
  231. (let ((p (- (point) 2)))
  232. (end-of-line)
  233. (delete-region p (point))))
  234. ;; Don't lose if no final newline.
  235. (goto-char (point-max))
  236. (or (eq (preceding-char) ?\n) (newline))
  237. (goto-char (point-min))
  238. ;; handle "\\\n" continuation lines
  239. (while (not (eobp))
  240. (end-of-line)
  241. (if (= (preceding-char) ?\\)
  242. (progn (delete-char -1) (delete-char 1) (insert ?\ ))
  243. (forward-char 1)))
  244. (goto-char (point-min))
  245. ;; handle `source' directives -- Eddy/1994/May/25
  246. (cond ((re-search-forward "^source[ \t]+" nil t)
  247. (re-search-forward "\\S-+")
  248. (setq file (buffer-substring-no-properties
  249. (match-beginning 0) (match-end 0)))
  250. (beginning-of-line)
  251. (insert "# ") ; to ensure we don't re-process this file
  252. (beginning-of-line))
  253. (t (setq file nil))))
  254. (goto-char (point-min))
  255. (while (re-search-forward
  256. "^\\(a\\|alias\\|g\\|group\\)[ \t]+\\([^ \t\n]+\\)" nil t)
  257. (let* ((name (match-string 2))
  258. (start (progn (skip-chars-forward " \t") (point)))
  259. value)
  260. (end-of-line)
  261. (setq value (buffer-substring-no-properties start (point)))
  262. (unless (equal value "")
  263. (define-mail-alias name value t))))
  264. mail-aliases))
  265. ;; Always autoloadable in case the user wants to define aliases
  266. ;; interactively or in .emacs.
  267. ;; define-mail-abbrev in mailabbrev.el duplicates much of this code.
  268. ;;;###autoload
  269. (defun define-mail-alias (name definition &optional from-mailrc-file)
  270. "Define NAME as a mail alias that translates to DEFINITION.
  271. This means that sending a message to NAME will actually send to DEFINITION.
  272. Normally, the addresses in DEFINITION must be separated by commas.
  273. If FROM-MAILRC-FILE is non-nil, then addresses in DEFINITION
  274. can be separated by spaces; an address can contain spaces
  275. if it is quoted with double-quotes."
  276. (interactive "sDefine mail alias: \nsDefine %s as mail alias for: ")
  277. ;; Read the defaults first, if we have not done so.
  278. ;; But not if we are doing that already right now.
  279. (unless from-mailrc-file
  280. (sendmail-sync-aliases))
  281. (if (eq mail-aliases t)
  282. (progn
  283. (setq mail-aliases nil)
  284. (if (file-exists-p mail-personal-alias-file)
  285. (build-mail-aliases))))
  286. ;; strip garbage from front and end
  287. (if (string-match "\\`[ \t\n,]+" definition)
  288. (setq definition (substring definition (match-end 0))))
  289. (if (string-match "[ \t\n,]+\\'" definition)
  290. (setq definition (substring definition 0 (match-beginning 0))))
  291. (let* ((L (length definition))
  292. (start (if (> L 0) 0))
  293. end this-entry result tem)
  294. (while start
  295. (cond
  296. (from-mailrc-file
  297. ;; If we're reading from the mailrc file, addresses are
  298. ;; delimited by spaces, and addresses with embedded spaces are
  299. ;; surrounded by non-escaped double-quotes.
  300. (if (eq ?\" (aref definition start))
  301. (setq start (1+ start)
  302. end (and (string-match
  303. "[^\\]\\(\\([\\][\\]\\)*\\)\"[ \t,]*"
  304. definition start)
  305. (match-end 1)))
  306. (setq end (string-match "[ \t,]+" definition start)))
  307. ;; Extract the address and advance the loop past it.
  308. (setq this-entry (substring definition start end)
  309. start (and end (/= (match-end 0) L) (match-end 0)))
  310. ;; If the full name contains a problem character, quote it.
  311. (and (string-match "\\(.+?\\)[ \t]*\\(<.*>\\)" this-entry)
  312. (string-match "[^- !#$%&'*+/0-9=?A-Za-z^_`{|}~]"
  313. (match-string 1 this-entry))
  314. (setq this-entry (replace-regexp-in-string
  315. "\\(.+?\\)[ \t]*\\(<.*>\\)"
  316. "\"\\1\" \\2"
  317. this-entry))))
  318. ;; When we are not reading from .mailrc, addresses are
  319. ;; separated by commas. Try to accept a rfc822-like syntax.
  320. ;; (Todo: extend rfc822.el to do the work for us.)
  321. ((equal (string-match
  322. "[ \t,]*\\(\"\\(?:[^\"]\\|[^\\]\\(?:[\\][\\]\\)*\"\\)*\"[ \t]*\
  323. <[-.!#$%&'*+/0-9=?A-Za-z^_`{|}~@]+>\\)[ \t,]*"
  324. definition start)
  325. start)
  326. ;; If an entry has a valid [ "foo bar" <foo@example.com> ]
  327. ;; form, use it literally . This also allows commas in the
  328. ;; quoted string, e.g. [ "foo bar, jr" <foo@example.com> ]
  329. (setq this-entry (match-string 1 definition)
  330. start (and (/= (match-end 0) L) (match-end 0))))
  331. (t
  332. ;; Otherwise, read the next address by looking for a comma.
  333. (setq end (string-match "[ \t\n,]*,[ \t\n]*" definition start))
  334. (setq this-entry (substring definition start end))
  335. ;; Advance the loop past this address.
  336. (setq start (and end (/= (match-end 0) L) (match-end 0)))
  337. ;; If the full name contains a problem character, quote it.
  338. (and (string-match "\\(.+?\\)[ \t]*\\(<.*>\\)" this-entry)
  339. (string-match "[^- !#$%&'*+/0-9=?A-Za-z^_`{|}~]"
  340. (match-string 1 this-entry))
  341. (setq this-entry (replace-regexp-in-string
  342. "\\(.+?\\)[ \t]*\\(<.*>\\)" "\"\\1\" \\2"
  343. this-entry)))))
  344. (push this-entry result))
  345. (setq definition (mapconcat (function identity)
  346. (nreverse result) ", "))
  347. (setq tem (assoc name mail-aliases))
  348. (if tem
  349. (rplacd tem definition)
  350. (setq mail-aliases (cons (cons name definition) mail-aliases)
  351. mail-names t))))
  352. ;;;###autoload
  353. (defun mail-completion-at-point-function ()
  354. "Compute completion data for mail aliases.
  355. For use on `completion-at-point-functions'."
  356. ;; Read the defaults first, if we have not done so.
  357. (sendmail-sync-aliases)
  358. (if (eq mail-aliases t)
  359. (progn
  360. (setq mail-aliases nil)
  361. (if (file-exists-p mail-personal-alias-file)
  362. (build-mail-aliases))))
  363. (let ((list mail-complete-alist)
  364. (list-exp nil))
  365. (if (and (< 0 (mail-header-end))
  366. (save-excursion
  367. (if (re-search-backward "^[^\t ]" nil t)
  368. (while list
  369. (if (looking-at (car (car list)))
  370. (setq list-exp (cdr (car list))
  371. list ())
  372. (setq list (cdr list)))))
  373. list-exp))
  374. (let* ((end (point))
  375. (beg (save-excursion
  376. (skip-chars-backward "^ \t<,:")
  377. (point)))
  378. (table (completion-table-dynamic
  379. (lambda (prefix)
  380. (let ((pattern prefix)) (eval list-exp))))))
  381. (list beg end table)))))
  382. ;;;###autoload
  383. (defun mail-complete (arg)
  384. "Perform completion on header field or word preceding point.
  385. Completable headers are according to `mail-complete-alist'. If none matches
  386. current header, calls `mail-complete-function' and passes prefix ARG if any."
  387. (interactive "P")
  388. ;; Read the defaults first, if we have not done so.
  389. (sendmail-sync-aliases)
  390. (if (eq mail-aliases t)
  391. (progn
  392. (setq mail-aliases nil)
  393. (if (file-exists-p mail-personal-alias-file)
  394. (build-mail-aliases))))
  395. (let ((data (mail-completion-at-point-function)))
  396. (if data
  397. (apply #'completion-in-region data)
  398. (funcall mail-complete-function arg))))
  399. (make-obsolete 'mail-complete 'mail-completion-at-point-function "24.1")
  400. (defun mail-completion-expand (table)
  401. "Build new completion table that expands aliases.
  402. Completes like TABLE except that if the completion is a valid alias,
  403. it expands it to its full `mail-complete-style' form."
  404. (lambda (string pred action)
  405. (cond
  406. ((eq action nil)
  407. (let* ((comp (try-completion string table pred))
  408. (name (and (listp table) comp
  409. (assoc (if (stringp comp) comp string) table))))
  410. (cond
  411. ((null name) comp)
  412. ((eq mail-complete-style 'parens)
  413. (concat (car name) " (" (cdr name) ")"))
  414. ((eq mail-complete-style 'angles)
  415. (concat (cdr name) " <" (car name) ">"))
  416. (t comp))))
  417. (t
  418. (complete-with-action action table string pred)))))
  419. (defun mail-get-names (prefix)
  420. "Fetch local users and global mail addresses for completion.
  421. Consults `/etc/passwd' and a directory service if one is set up via
  422. `mail-directory-function'.
  423. PREFIX is the string we want to complete."
  424. (if (eq mail-local-names t)
  425. (with-current-buffer (generate-new-buffer " passwd")
  426. (let ((files mail-passwd-files))
  427. (while files
  428. (insert-file-contents (car files) nil nil nil t)
  429. (setq files (cdr files))))
  430. (if mail-passwd-command
  431. (call-process shell-file-name nil t nil
  432. shell-command-switch mail-passwd-command))
  433. (goto-char (point-min))
  434. (setq mail-local-names nil)
  435. (while (not (eobp))
  436. ;;Recognize lines like
  437. ;; nobody:*:65534:65534::/:
  438. ;; +demo::::::/bin/csh
  439. ;; +ethanb
  440. ;;while skipping
  441. ;; +@SOFTWARE
  442. ;; The second \(...\) matches the user id.
  443. (if (looking-at "\\+?\\([^:@\n+]+\\):[^:\n]*:\\([^\n:]*\\):")
  444. (add-to-list 'mail-local-names
  445. (cons (match-string 1)
  446. (user-full-name
  447. (string-to-number (match-string 2))))))
  448. (beginning-of-line 2))
  449. (kill-buffer (current-buffer))))
  450. (if (or (eq mail-names t)
  451. (eq mail-directory-names t))
  452. (let (directory)
  453. (and mail-directory-function
  454. (eq mail-directory-names t)
  455. (setq directory
  456. (mail-directory (if mail-directory-requery prefix))))
  457. (or mail-directory-requery
  458. (setq mail-directory-names directory))
  459. (if (or directory
  460. (eq mail-names t))
  461. (setq mail-names
  462. (sort (append (if (consp mail-aliases)
  463. (mapcar
  464. (function (lambda (a) (list (car a))))
  465. mail-aliases))
  466. (if (consp mail-local-names)
  467. mail-local-names)
  468. (or directory
  469. (when (consp mail-directory-names)
  470. mail-directory-names)))
  471. (lambda (a b)
  472. ;; Should cache downcased strings.
  473. (string< (downcase (car a))
  474. (downcase (car b)))))))))
  475. (mail-completion-expand mail-names))
  476. (defun mail-directory (prefix)
  477. "Use mail-directory facility to get user names matching PREFIX.
  478. If PREFIX is nil, get all the defined user names.
  479. This function calls `mail-directory-function' to query the directory,
  480. then uses `mail-directory-parser' to parse the output it returns."
  481. (message "Querying directory...")
  482. (with-current-buffer (generate-new-buffer " *mail-directory*")
  483. (funcall mail-directory-function prefix)
  484. (goto-char (point-min))
  485. (let (directory)
  486. (if (stringp mail-directory-parser)
  487. (while (re-search-forward mail-directory-parser nil t)
  488. (push (match-string 1) directory))
  489. (if mail-directory-parser
  490. (setq directory (funcall mail-directory-parser))
  491. (while (not (eobp))
  492. (push (buffer-substring (point)
  493. (progn
  494. (forward-line)
  495. (if (bolp)
  496. (1- (point))
  497. (point))))
  498. directory))))
  499. (kill-buffer (current-buffer))
  500. (message "Querying directory...done")
  501. directory)))
  502. (defvar mailalias-done)
  503. (defun mail-directory-process (prefix)
  504. "Run a shell command to output names in directory.
  505. See `mail-directory-process'."
  506. (when (consp mail-directory-process)
  507. (let ((pattern prefix)) ;Dynbind!
  508. (apply 'call-process (eval (car mail-directory-process)) nil t nil
  509. (mapcar 'eval (cdr mail-directory-process))))))
  510. ;; This should handle a dialog. Currently expects port to spit out names.
  511. (defun mail-directory-stream (prefix)
  512. "Open a stream to retrieve names in directory.
  513. See `mail-directory-stream'."
  514. (let ((mailalias-done nil)
  515. (pattern prefix)) ;Dynbind!
  516. (set-process-sentinel
  517. (apply 'open-network-stream "mailalias" (current-buffer)
  518. mail-directory-stream)
  519. (lambda (_x _y)
  520. (setq mailalias-done t)))
  521. (while (not mailalias-done)
  522. (sit-for .1))))
  523. (defun mail-sentto-newsgroups ()
  524. "Return all entries from Newsgroups: header as completion alist."
  525. (save-excursion
  526. (if (mail-position-on-field "newsgroups" t)
  527. (let ((point (point))
  528. list)
  529. (while (< (skip-chars-backward "^:, \t\n") 0)
  530. (setq list `((,(buffer-substring (point) point))
  531. ,@list))
  532. (skip-chars-backward ", \t\n")
  533. (setq point (point)))
  534. list))))
  535. (provide 'mailalias)
  536. ;;; mailalias.el ends here