home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume8 / pcmail / part07 < prev    next >
Lisp/Scheme  |  1989-11-03  |  51KB  |  1,290 lines

  1. Newsgroups: comp.sources.misc
  2. subject: v08i115: pcmail part 07 of 08
  3. From: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc)
  4. Reply-To: markl@oracle.com (Croaker the Physician)
  5.  
  6. Posting-number: Volume 8, Issue 115
  7. Submitted-by: markl@oracle.com (Croaker the Physician)
  8. Archive-name: pcmail/part07
  9.  
  10. #--------------------------------CUT HERE-------------------------------------
  11. #! /bin/sh
  12. #
  13. # This is a shell archive.  Save this into a file, edit it
  14. # and delete all lines above this comment.  Then give this
  15. # file to sh by executing the command "sh file".  The files
  16. # will be extracted into the current directory owned by
  17. # you with default permissions.
  18. #
  19. # The files contained herein are:
  20. #
  21. # -rw-rw-r--  1 markl       17622 Oct 30 16:57 pcmailsub.el
  22. # -rw-rw-r--  1 markl       30775 Oct 30 15:47 pcmailsysdep.el
  23. #
  24. echo 'x - pcmailsub.el'
  25. if test -f pcmailsub.el; then echo 'shar: not overwriting pcmailsub.el'; else
  26. sed 's/^X//' << '________This_Is_The_END________' > pcmailsub.el
  27. X;;;; GNU-EMACS PCMAIL mail reader
  28. X
  29. X;;  Written by Mark L. Lambert
  30. X;;  Architecture Group, Network Products Division
  31. X;;  Oracle Corporation
  32. X;;  20 Davis Dr,
  33. X;;  Belmont CA, 94002
  34. X;;
  35. X;;  internet: markl@oracle.com or markl%oracle.com@apple.com
  36. X;;  UUCP:     {hplabs,uunet,apple}!oracle!markl
  37. X
  38. X;; Copyright (C) 1989 Mark L. Lambert
  39. X
  40. X;; This file is not officially part of GNU Emacs, but is being
  41. X;; donated to the Free Software Foundation.  As such, it is
  42. X;; subject to the standard GNU-Emacs General Public License,
  43. X;; referred to below.
  44. X
  45. X;; GNU Emacs is distributed in the hope that it will be useful,
  46. X;; but WITHOUT ANY WARRANTY.  No author or distributor
  47. X;; accepts responsibility to anyone for the consequences of using it
  48. X;; or for whether it serves any particular purpose or works at all,
  49. X;; unless he says so in writing.  Refer to the GNU Emacs General Public
  50. X;; License for full details.
  51. X
  52. X;; Everyone is granted permission to copy, modify and redistribute
  53. X;; GNU Emacs, but only under the conditions described in the
  54. X;; GNU Emacs General Public License.   A copy of this license is
  55. X;; supposed to have been given to you along with GNU Emacs so you
  56. X;; can know your rights and responsibilities.  It should be in a
  57. X;; file named COPYING.  Among other things, the copyright notice
  58. X;; and this notice must be preserved on all copies.
  59. X
  60. X;;;; global variables
  61. X
  62. X;;; system-defined globals
  63. X
  64. X(defvar pcmail-key-alist
  65. X  '(("date" pcmail-date-less-than-p) 
  66. X    ("priority" pcmail-priority-less-than-p)
  67. X    ("from" pcmail-from-field-less-than-p)
  68. X    ("to" pcmail-to-field-less-than-p))
  69. X  "Completion list of sort types.")
  70. X
  71. X(defvar pcmail-filter-alist
  72. X  '(("string" (pcmail-contains-string-p pcmail-last-search)
  73. X          (setq pcmail-last-search
  74. X            (pcmail-read-string-default "Search string (regexp): "
  75. X                        pcmail-last-search)))
  76. X    ("address" (pcmail-has-address-p pcmail-last-addresses)
  77. X           (setq pcmail-last-addresses
  78. X             (pcmail-read-string-default "Addresses: " 
  79. X                         pcmail-last-addresses)))
  80. X    ("attribute" (pcmail-contains-attribute-p pcmail-last-filter-attr)
  81. X         (setq pcmail-last-filter-attr
  82. X               (pcmail-read-attr "Attribute: ")))
  83. X    ("numeric-range" (pcmail-within-numeric-range-p pcmail-last-numeric-range)
  84. X             (setq pcmail-last-numeric-range 
  85. X               (pcmail-read-numeric-range)))
  86. X    ("date-range" (pcmail-within-date-range-p pcmail-last-date-range)
  87. X          (setq pcmail-last-date-range (pcmail-read-date-range)))
  88. X    ("interesting" (pcmail-subset-interesting-message-p))
  89. X    ("unseen" (pcmail-contains-attribute-p "unseen"))
  90. X    ("unanswered" (not (pcmail-contains-attribute-p "answered")))
  91. X    ("todays" (pcmail-within-date-range-p (list pcmail-today pcmail-today))
  92. X          (setq pcmail-today (pcmail-string-to-date-triple)))
  93. X    ("all" t))
  94. X  "List of filter names, expressions, and setup functions.  When using
  95. Xa particular filter, the setup function (if non-NIL) is run through
  96. Xeval to set up any arguments needed by the filter.  Then each message
  97. Xin the folder is applied to the filter expression.  The variable
  98. Xpcmail-current-tested-message is bound to the absolute number of the
  99. Xmessage being tested before the function is called.  This allows the
  100. Xfunction to access the current absolute message number without
  101. Xrequiring that it do so.  The filter expression is run through eval
  102. Xbecause the expression arguments must be evaluated at filter time and
  103. Xcan change on successive applications of the filter.  For each message
  104. Xrun through the filter, if the filter expression evaluates non-NIL,
  105. Xthe message is included in the resulting subset.")
  106. X
  107. X;;; defaults
  108. X
  109. X(defvar pcmail-current-filter-description t
  110. X  "Current filter expression.")
  111. X
  112. X(defvar pcmail-last-search nil
  113. X  "The last regular expression given to a search command.")
  114. X
  115. X(defvar pcmail-last-addresses nil
  116. X  "The last comma-separated list of addresses given to an address command.")
  117. X
  118. X(defvar pcmail-last-numeric-range nil
  119. X  "The last numeric range given to a numeric range command.  A numeric range
  120. Xis a list of two numbers, low end and high end.")
  121. X
  122. X(defvar pcmail-last-date-range nil
  123. X  "The last date range given to a date range command.  A date range is a pair
  124. Xof triples (day month year), low end and high end.")
  125. X
  126. X(defvar pcmail-last-filter-name nil
  127. X  "The last filter name given to a filter command.")
  128. X
  129. X(defvar pcmail-last-key nil
  130. X  "The last key name given a sort command.")
  131. X
  132. X;;;; subset maintenance commands and utilities
  133. X
  134. X;;; subset commands
  135. X
  136. X(defun pcmail-filter-folder (filter-name)
  137. X  "Run the current folder through a specified filter.
  138. XArgs: (filter-name)
  139. X  Get a filter name and associated arguments from the minibuffer.  Completion 
  140. Xof input is permitted; input defaults to last filter name requested.  Apply
  141. Xthe filter's predicate to each message in the current folder.  Messages
  142. Xwhich pass through the filter comprise the current subset and are the only
  143. Xaccessible messages in the current folder.  If the desired subset is
  144. Xempty, do nothing.  User-defined filters are defined in your emacs
  145. Xstartup file using the pcmail-define-filter function."
  146. X  (interactive (list (pcmail-read-filter-name)))
  147. X  (cond ((pcmail-build-subset-membership (pcmail-get-filter filter-name))
  148. X     (pcmail-goto-message 1)
  149. X     (pcmail-maybe-resummarize-folder))
  150. X    (t
  151. X     (error "Desired subset is empty."))))
  152. X
  153. X(defun pcmail-expand-subset ()
  154. X  "Expand the current subset to include all messages in the current folder.
  155. XArgs: none"
  156. X  (interactive)
  157. X  (let ((n (pcmail-make-absolute pcmail-current-subset-message)))
  158. X    (pcmail-build-subset-membership t)
  159. X    (pcmail-goto-message n)
  160. X    (pcmail-maybe-resummarize-folder)))
  161. X
  162. X(defun pcmail-sort-folder (key-name)
  163. X  "Sort the current subset by one of several keys.
  164. XArgs: (key-name)
  165. X  Sort the current subset by one of several keys.  If called interactively,
  166. Xspecifiy a key in the minibuffer.  Completion on input is permitted; input
  167. Xdefaults to last key given this command."
  168. X  (interactive
  169. X   (list (pcmail-completing-read "Key name: " pcmail-key-alist
  170. X                 pcmail-last-key)))
  171. X  (let ((key-entry (pcmail-search-entry-list key-name pcmail-key-alist))
  172. X    (subset) (i 0))
  173. X    (or key-entry
  174. X    (error "Unknown sort key."))
  175. X    (setq pcmail-last-key key-name)
  176. X    (message "Sorting %s by %s..." pcmail-folder-name key-name)
  177. X
  178. X    ;; convert subset vector to a list since sort works only on lists
  179. X    (while (< i (length pcmail-current-subset-vector))
  180. X      (setq subset (cons (aref pcmail-current-subset-vector i) subset))
  181. X      (setq i (1+ i)))
  182. X    (setq pcmail-current-subset-vector
  183. X      (apply 'vector (sort (nreverse subset) (nth 1 key-entry))))
  184. X    (pcmail-maybe-resummarize-folder)
  185. X    (message "Sorting %s by %s...done" pcmail-folder-name key-name)))
  186. X
  187. X(defun pcmail-define-filter (name sexp input-fn)
  188. X  "Install a user-defined filter.
  189. XArgs: (name sexp input-fn)
  190. X  Create a filter entry named NAME with description SEXP and argument-input
  191. Xfunction INPUT-FN, and install it in the assoc list pcmail-filter-alist.  
  192. XIf a filter by that name already exists, ask for overwrite permission unless 
  193. Xthe name is the special filter named \"all\", in which case overwriting is not
  194. Xpermitted."
  195. X  (and (string= name "all")
  196. X       (error "Cannot overwrite the \"all\" filter"))
  197. X  (let ((ent))
  198. X    (and (setq ent (pcmail-filter-exists-p name))
  199. X     (if (y-or-n-p "Filter exists; overwrite? ")
  200. X         (setq pcmail-filter-alist (delq ent pcmail-filter-alist))
  201. X       (error "Aborted.")))
  202. X    (setq pcmail-filter-alist
  203. X      (cons (list name sexp input-fn) pcmail-filter-alist))))
  204. X
  205. X;;; subset utility routines
  206. X
  207. X(defun pcmail-build-subset-membership (pred &optional start)
  208. X  "Create a subset of messages that satisfy PRED
  209. XArgs: (pred &optional start)
  210. X  Using filter description PRED, build a vector of messages that 
  211. Xsatisfy that description.  If START is NIL, begin at message 1, replacing 
  212. Xthe current subset with the subset generated by this function (unless it is
  213. Xof zero length).  If START is non-NIL, begin membership testing at message 
  214. XSTART, appending any new members to the current subset."
  215. X  (condition-case nil
  216. X      (let ((pcmail-current-tested-message (or start 1))
  217. X        (subset))
  218. X    (while (<= pcmail-current-tested-message pcmail-total-messages)
  219. X      (and (eval pred)
  220. X           (setq subset (cons pcmail-current-tested-message subset)))
  221. X      (and (zerop (% (- (setq pcmail-current-tested-message 
  222. X                  (1+ pcmail-current-tested-message))
  223. X                (or start 1))
  224. X             pcmail-progress-interval))
  225. X           (message "Checking filter membership...%d" 
  226. X            pcmail-current-tested-message)))
  227. X    (and (>= (- pcmail-current-tested-message (or start 1))
  228. X         pcmail-progress-interval)
  229. X         (message "Checking filter membership...done (%d message%s)"
  230. X              (length subset) (pcmail-s-ending (length subset))))
  231. X    (and (or subset (eq pred t))
  232. X         (setq pcmail-current-filter-description pred
  233. X           pcmail-current-subset-vector 
  234. X           (vconcat (if start
  235. X                pcmail-current-subset-vector
  236. X                  (make-vector 1 0))
  237. X                (apply 'vector (nreverse subset)))))
  238. X    subset)
  239. X    (quit
  240. X      nil)))
  241. X
  242. X(defun pcmail-fix-expunged-subset (map)
  243. X  "Remove expunged messages from the current subset
  244. XArgs: (map)
  245. X  MAP is a vector pcmail-total-messages long, with entries that are either
  246. Xa message's post-expunge message number, or NIL if the message was expunged.
  247. XThis function updates the current subset vector's message numbers to their
  248. Xpost-expunged values."
  249. X  (let ((new-subset)
  250. X    (map-ent)
  251. X    (i 0))
  252. X    (unwind-protect
  253. X    (while (< i (length pcmail-current-subset-vector))
  254. X      (setq map-ent (aref map (aref pcmail-current-subset-vector i)))
  255. X      (and map-ent
  256. X           (setq new-subset (cons map-ent new-subset)))
  257. X      (setq i (1+ i)))
  258. X      (setq pcmail-current-subset-vector 
  259. X        (apply 'vector (nreverse new-subset))))))
  260. X
  261. X(defun pcmail-make-absolute (n)
  262. X  "Return Nth subset message's absolute message number
  263. XArgs: (n)
  264. X  Convert relative message number N into an absolute number by indexing into
  265. Xthe current subset membership vector.  If N is larger than the current
  266. Xsubset length, return last subset message's absolute number.  If no absolute 
  267. Xexists, return 0."
  268. X  (setq n (min n (pcmail-current-subset-length)))
  269. X  (or (aref pcmail-current-subset-vector n) 0))
  270. X
  271. X(defun pcmail-filter-description (name)
  272. X  "Return named filter's description.  Signal an error if filter not found
  273. XArgs: (name)"
  274. X  (let ((ent (pcmail-filter-exists-p name)))
  275. X    (or ent
  276. X    (error "No filter named %s" name))
  277. X    (nth 1 ent)))
  278. X
  279. X(defun pcmail-filter-exists-p (name)
  280. X  "If NAME is a valid filter, return its assoc list entry, else NIL.
  281. XArgs: (name)"
  282. X  (pcmail-search-entry-list name pcmail-filter-alist))
  283. X    
  284. X(defun pcmail-current-subset-length ()
  285. X  "Return the number of messages in the current subset.
  286. XArgs: none"
  287. X  (1- (length pcmail-current-subset-vector)))
  288. X
  289. X(defun pcmail-read-filter-name (&optional pr)
  290. X  "Read a filter name from the minibuffer.
  291. XArgs: (&optional PROMPT)
  292. XRead a filter name from the minibuffer.  Completion is permitted; input
  293. Xdefaults to pcmail-last-filter-name.  Signal an error if supplied filter
  294. Xname is invalid."
  295. X  (let ((s (pcmail-completing-read (or pr "Filter name: ") pcmail-filter-alist
  296. X                   pcmail-last-filter-name)))
  297. X    (or (pcmail-filter-exists-p s)
  298. X    (error "No filter named %s." s))
  299. X    (setq pcmail-last-filter-name s)))
  300. X
  301. X(defun pcmail-get-filter (filter-name)
  302. X  "Read filter arguments and return filter predicate.
  303. XArgs: (filter-name)
  304. X  If FILTER-NAME is a valid filter, get its required arguments from the 
  305. Xminibuffer and return the filter predicate."
  306. X  (let ((ent))
  307. X    (setq ent (pcmail-filter-exists-p filter-name))
  308. X    (and (nth 2 ent) (eval (nth 2 ent)))
  309. X    (nth 1 ent)))
  310. X
  311. X;;; predicates for subset creation.  Each predicate is applied to a 
  312. X;;; message with number pcmail-current-tested-message.  This variable 
  313. X;;; is a free variable. 
  314. X
  315. X(defun pcmail-has-address-p (recipients)
  316. X  "Return non-NIL if current message contains the supplied address regexp .
  317. XArgs: (recipients)
  318. X  Convert comma-separated list of recipients RECIPIENTS into a regular
  319. Xexpression.  Return non-NIL if message pcmail-current-tested-message
  320. X(free variable) contains this regular expression, NIL else."
  321. X  (setq recipients (mail-comma-list-regexp recipients))
  322. X  (save-excursion
  323. X    (save-restriction
  324. X      (pcmail-narrow-to-unpruned-header pcmail-current-tested-message)
  325. X      (or (string-match recipients (or (mail-fetch-field "to") ""))
  326. X      (string-match recipients (or (mail-fetch-field "resent-to") ""))
  327. X      (string-match recipients (or (mail-fetch-field "from") ""))
  328. X      (string-match recipients (or (mail-fetch-field "resent-from") ""))
  329. X      (string-match recipients (or (mail-fetch-field "cc") ""))
  330. X      (string-match recipients (or (mail-fetch-field "resent-cc") ""))))))
  331. X
  332. X(defun pcmail-contains-attribute-p (attr)
  333. X  "Return non-NIL if current message has attribute, NIL else.
  334. XArgs: (attr)
  335. X  Return non-NIL if pcmail-current-tested-message (free variable) has
  336. XATTR set, NIL else."
  337. X  (pcmail-has-attribute-p pcmail-current-tested-message attr))
  338. X
  339. X(defun pcmail-subset-interesting-message-p ()
  340. X  "Return non-NIL is current message is interesting, NIL else.
  341. XArgs: none"
  342. X  (pcmail-interesting-p pcmail-current-tested-message))
  343. X
  344. X(defun pcmail-within-numeric-range-p (range)
  345. X  "Return non-NIL if current message is within a numeric range, NIL else.
  346. XArgs: (range)
  347. X  Return non-NIL if pcmail-current-tested-message (free variable) is
  348. Xwithin the range of absolute message numbers specified by the list RANGE, 
  349. XNIL else."
  350. X  (and (>= pcmail-current-tested-message (nth 0 range))
  351. X       (<= pcmail-current-tested-message (nth 1 range))))
  352. X
  353. X(defun pcmail-within-date-range-p (range)
  354. X  "Return non-NIL if the current message's date is within date range, NIL else.
  355. XArgs: (range)
  356. X  Return non-NIL if pcmail-current-tested-message (free variable)
  357. Xhas its date within the range of dates specified by the list RANGE, NIL else.
  358. XDates are triples (day month year); RANGE is a pair of such triples."
  359. X  (let ((lo (pcmail-date-triple-to-ndays (nth 0 range)))
  360. X    (hi (pcmail-date-triple-to-ndays (nth 1 range)))
  361. X    (date (pcmail-message-date pcmail-current-tested-message)))
  362. X    (and date
  363. X     (setq date (pcmail-date-triple-to-ndays date))
  364. X     (<= date hi)
  365. X     (>= date lo))))
  366. X
  367. X(defun pcmail-contains-string-p (regexp)
  368. X  "Return non-NIL if the current message contains a specified regexp, NIL else.
  369. XArgs: (regexp)"
  370. X  (save-excursion
  371. X    (save-restriction
  372. X      (let ((case-fold-search t))
  373. X    (pcmail-narrow-to-message pcmail-current-tested-message)
  374. X    (re-search-forward regexp nil t)))))
  375. X
  376. X;;; read ranges from keyboard
  377. X
  378. X(defun pcmail-read-date-range ()
  379. X  "Read a date range from the minibuffer
  380. XArgs: none
  381. X  Read a pair of dates from the minibuffer.  Dates must be input in the
  382. Xform dd-mmm-yy.  Default range is pcmail-last-date-range, which is a pair of 
  383. Xdate triples, low and high.  If no default has been specified, use low value 
  384. Xas default for high value.  If the string \"begin\" is input at the low value 
  385. Xprompt, range includes all messages below high-value.  If the string \"now\" 
  386. Xis input at the high value prompt, range includes all messages above 
  387. Xlow-value.  Input becomes new value of pcmail-last-date-range."
  388. X
  389. X  ; our date input parser is stupid, so temporarily bind the date format to
  390. X  ; the date input format so default input works correctly
  391. X  (let ((lo) (hi) (pcmail-date-format "%d-%m-%y"))
  392. X    (setq lo
  393. X      (pcmail-read-string-default 
  394. X        "First date in range: " 
  395. X        (and (nth 0 pcmail-last-date-range)
  396. X         (pcmail-date-triple-to-string (nth 0 pcmail-last-date-range)))
  397. X        t))
  398. X    (cond ((string= lo "begin")
  399. X       (setq lo '(1 1 0)))
  400. X      ((not (setq lo (pcmail-string-to-date-triple lo)))
  401. X       (error "Date not dd-mmm-yy or \"begin\".")))
  402. X    (setq hi
  403. X      (pcmail-read-string-default 
  404. X        "Last date in range: " 
  405. X        (if (nth 1 pcmail-last-date-range)
  406. X        (pcmail-date-triple-to-string (nth 1 pcmail-last-date-range))
  407. X            (pcmail-date-triple-to-string lo))
  408. X        t))
  409. X    (cond ((string= hi "now")
  410. X       (setq hi (pcmail-string-to-date-triple)))
  411. X      ((not (setq hi (pcmail-string-to-date-triple hi)))
  412. X       (error "Date not dd-mmm-yy or \"now\".")))
  413. X    (if (> (pcmail-date-triple-to-ndays lo) (pcmail-date-triple-to-ndays hi))
  414. X    (list hi lo)
  415. X      (list lo hi))))
  416. X
  417. X(defun pcmail-read-numeric-range ()
  418. X  "Read a numeric range from the minibuffer.
  419. XArgs: none
  420. X  Read a pair of absolute message numbers from the minibuffer.  Default 
  421. Xrange is value of variable pcmail-last-numeric-range, which is a pair of 
  422. Xnumbers, low and high.  If no default has been specified, use low value as 
  423. Xdefault for high value.  If the string \"first\" is input at the low
  424. Xvalue prompt, range includes all messages below high-value.  If the string
  425. X\"last\" is input at the high value prompt, range includes all messages above
  426. Xlow-value.  Input becomes new value of pcmail-last-numeric-range."
  427. X  (let ((lo)
  428. X    (hi))
  429. X    (setq lo
  430. X      (pcmail-read-string-default 
  431. X        "First message in range: " 
  432. X        (and (nth 0 pcmail-last-numeric-range)
  433. X         (int-to-string (nth 0 pcmail-last-numeric-range)))
  434. X        t))
  435. X    (cond ((string= lo "first")
  436. X       (setq lo 1))
  437. X      ((or (not (setq lo (string-to-int lo)))
  438. X           (< lo 1)
  439. X           (> lo pcmail-total-messages))
  440. X       (error "Range endpoint not 1 - %d or \"first\"."
  441. X          pcmail-total-messages)))
  442. X    (setq hi
  443. X      (pcmail-read-string-default 
  444. X        "Last message in range: " 
  445. X        (if (nth 1 pcmail-last-numeric-range)
  446. X        (int-to-string (nth 1 pcmail-last-numeric-range))
  447. X          (int-to-string lo))
  448. X        t))
  449. X    (cond ((string= hi "last")
  450. X       (setq hi pcmail-total-messages))
  451. X      ((or (not (setq hi (string-to-int hi)))
  452. X           (< hi 1)
  453. X           (> hi pcmail-total-messages))
  454. X       (error "Range endpoint not 1 - %d or \"last\"."
  455. X          pcmail-total-messages)))
  456. X    (list (min lo hi) (max lo hi))))
  457. X
  458. X(provide 'pcmailsub)
  459. ________This_Is_The_END________
  460. if test `wc -c < pcmailsub.el` -ne 17622; then
  461.     echo 'shar: pcmailsub.el was damaged during transit (should have been 17622 bytes)'
  462. fi
  463. fi        ; : end of overwriting check
  464. echo 'x - pcmailsysdep.el'
  465. if test -f pcmailsysdep.el; then echo 'shar: not overwriting pcmailsysdep.el'; else
  466. sed 's/^X//' << '________This_Is_The_END________' > pcmailsysdep.el
  467. X;;;; GNU-EMACS PCMAIL mail reader
  468. X
  469. X;;  Written by Mark L. Lambert
  470. X;;  Architecture Group, Network Products Division
  471. X;;  Oracle Corporation
  472. X;;  20 Davis Dr,
  473. X;;  Belmont CA, 94002
  474. X;;
  475. X;;  internet: markl@oracle.com or markl%oracle.com@apple.com
  476. X;;  UUCP:     {hplabs,uunet,apple}!oracle!markl
  477. X
  478. X;; Copyright (C) 1989 Mark L. Lambert
  479. X
  480. X;; This file is not officially part of GNU Emacs, but is being
  481. X;; donated to the Free Software Foundation.  As such, it is
  482. X;; subject to the standard GNU-Emacs General Public License,
  483. X;; referred to below.
  484. X
  485. X;; GNU Emacs is distributed in the hope that it will be useful,
  486. X;; but WITHOUT ANY WARRANTY.  No author or distributor
  487. X;; accepts responsibility to anyone for the consequences of using it
  488. X;; or for whether it serves any particular purpose or works at all,
  489. X;; unless he says so in writing.  Refer to the GNU Emacs General Public
  490. X;; License for full details.
  491. X
  492. X;; Everyone is granted permission to copy, modify and redistribute
  493. X;; GNU Emacs, but only under the conditions described in the
  494. X;; GNU Emacs General Public License.   A copy of this license is
  495. X;; supposed to have been given to you along with GNU Emacs so you
  496. X;; can know your rights and responsibilities.  It should be in a
  497. X;; file named COPYING.  Among other things, the copyright notice
  498. X;; and this notice must be preserved on all copies.
  499. X
  500. X;;;; system-dependent things
  501. X
  502. X;;; mail environment.  For new environments, simply add a system-type switch
  503. X;;; to the cond and put whatever properties you desire into the cond clause
  504. X;;; examples for VMS and UNIX follow.  Currently all UNIXes are treated the
  505. X;;; same.  This can change as required.  A fair amount of this code is
  506. X;;; VMS-specific.  If you need to save space and you don't use VMS,
  507. X;;; cut where indicated and throw the remainder of the file away.
  508. X
  509. X(cond ((eq system-type 'vax-vms)
  510. X
  511. X       ;; VMS system mail drop
  512. X
  513. X       (put 'vms-default-mail-drop 'conversion-function 
  514. X        'pcmail-convert-vms-message)
  515. X       (put 'vms-default-mail-drop 'msg-start-regexp "^\^L\nFrom:[ \t]+")
  516. X       (put 'vms-default-mail-drop 'insert-function 'pcmail-do-vms-movemail)
  517. X
  518. X       ;; VMS file mail drop, used to perform an initial import (extract
  519. X       ;; your messages into a file and use this mail drop to import the 
  520. X       ;; file
  521. X
  522. X       (put 'vms-file-mail-drop 'conversion-function 
  523. X        'pcmail-convert-vms-message)
  524. X       (put 'vms-file-mail-drop 'msg-start-regexp "^\^L\nFrom:[ \t]+")
  525. X       (put 'vms-file-mail-drop 'insert-function 'pcmail-rename-mail-drop)
  526. X       (put 'vms-file-mail-drop 'name-input-func
  527. X        '(lambda () (pcmail-narrow-read-file-name "maildrop.log")))
  528. X
  529. X       ;; other environment stuff
  530. X
  531. X       (put 'pcmail-mail-environment 'printer "SYS$PRINT")
  532. X       (put 'pcmail-mail-environment 'print-function 'pcmail-vms-print-message)
  533. X       (put 'pcmail-mail-environment 'mail-directory
  534. X        (concat (substring (getenv "HOME") 0 -1) ".pcmail]"))
  535. X       (put 'pcmail-mail-environment 'time-zone "PST")
  536. X       (put 'pcmail-mail-environment 'legal-folder-regexp 
  537. X        "[0-9A-Za-z][0-9A-Za-z---_$+.]+")
  538. X       (put 'pcmail-mail-environment 'send-mail-function 'pcmail-vms-send-mail)
  539. X       (put 'pcmail-mail-environment 'create-mail-directory-fn
  540. X        'pcmail-vms-create-mail-directory)
  541. X       (put 'pcmail-mail-environment 'folder-to-file-function 
  542. X        'pcmail-vms-folder-name-to-file)
  543. X       (put 'pcmail-mail-environment 'default-mail-drop-list 
  544. X        '(vms-default-mail-drop)))
  545. X
  546. X      ;;; UNIX systems
  547. X
  548. X      (t
  549. X
  550. X       ;; NNTP mail drop
  551. X       
  552. X       (put 'nntp-mail-drop 'conversion-function 'pcmail-convert-nntp-message)
  553. X       (put 'nntp-mail-drop 'msg-start-regexp 
  554. X        "^\^L\n\\(Path\\|From\\|Xref\\):")
  555. X       (put 'nntp-mail-drop 'insert-function 'pcmail-load-nntp-mail)
  556. X       (put 'nntp-mail-drop 'display-errors-p t)
  557. X       (put 'nntp-mail-drop 'folder-delete-hook 'pcmail-delete-nntp-folder)
  558. X       
  559. X       ;; NNTP file mail drop -- this is a file of NNTP messages that have been
  560. X       ;; assembled by the nntp-slave program.  An indirect variant of the 
  561. X       ;; above
  562. X       
  563. X       (put 'nntp-file-mail-drop 'conversion-function 
  564. X        'pcmail-convert-nntp-message)
  565. X       (put 'nntp-file-mail-drop 'msg-start-regexp "^\^L\n\\(Path\\|From\\):")
  566. X       (put 'nntp-file-mail-drop 'insert-function 'pcmail-rename-mail-drop)
  567. X       (put 'nntp-file-mail-drop 'folder-delete-hook 
  568. X        'pcmail-delete-nntp-folder)
  569. X       (put 'nntp-file-mail-drop 'name-input-func
  570. X        '(lambda () (pcmail-narrow-read-file-name "mail-log")))
  571. X       
  572. X       ;; Berkeley-mail mail drops
  573. X       (put 'spool-mail-drop 'conversion-function 'pcmail-convert-unix-message)
  574. X       (put 'spool-mail-drop 'insert-function 'pcmail-do-unix-movemail)
  575. X       (put 'spool-mail-drop 'msg-start-regexp "^From ")
  576. X       
  577. X       (put 'berkeley-mail-drop 'conversion-function 
  578. X        'pcmail-convert-unix-message)
  579. X       (put 'berkeley-mail-drop 'msg-start-regexp "^From ")
  580. X       (put 'berkeley-mail-drop 'insert-function 'pcmail-rename-mail-drop)
  581. X       (put 'berkeley-mail-drop 'name-input-func
  582. X        '(lambda () (pcmail-narrow-read-file-name "~/mbox")))
  583. X       
  584. X       ;; MH "mail drop"
  585. X       
  586. X       (put 'mh-mail-drop 'conversion-function 'pcmail-convert-mh-message)
  587. X       (put 'mh-mail-drop 'msg-start-regexp "^\^Lbegin-message\^L\n")
  588. X       (put 'mh-mail-drop 'insert-function 'pcmail-do-mh-movemail)
  589. X       (put 'mh-mail-drop 'display-errors-p t)
  590. X
  591. X       ;; other environment stuff
  592. X
  593. X       (put 'pcmail-mail-environment 'time-zone "PST")
  594. X       (put 'pcmail-mail-environment 'legal-folder-regexp
  595. X        "[0-9A-Za-z---_$.+%#&!]+")
  596. X       (put 'pcmail-mail-environment 'printer (or (getenv "PRINTER") "lp"))
  597. X       (put 'pcmail-mail-environment 'print-function 'pcmail-unix-lpr-message)
  598. X       (put 'pcmail-mail-environment 'mail-directory "~/.pcmail/")
  599. X       (put 'pcmail-mail-environment 'create-mail-directory-fn
  600. X        'pcmail-unix-create-mail-directory)
  601. X       (put 'pcmail-mail-environment 'folder-to-file-function 'identity)
  602. X       (put 'pcmail-mail-environment 'default-mail-drop-list 
  603. X        '(spool-mail-drop))))
  604. X
  605. X;;;; UNIX functions
  606. X
  607. X;;; message print function
  608. X
  609. X(defun pcmail-unix-lpr-message (printer-name folder-name)
  610. X  "Send current message to printer
  611. XArgs: (printer-name folder-name)
  612. X  Send the current message to the printer using LPR.  Call-process-region
  613. Xon the current region.  Add job/title arguments so burst page looks nice."
  614. X  (call-process-region (point-min) (point-max) "lpr"
  615. X               nil nil nil
  616. X               (concat "-P" printer-name)
  617. X               (format "-J\"Msg %s/%d\"" folder-name
  618. X                   (pcmail-make-absolute 
  619. X                pcmail-current-subset-message))
  620. X               (format "-T\"Msg %s/%d\"" folder-name
  621. X                   (pcmail-make-absolute 
  622. X                pcmail-current-subset-message))))
  623. X
  624. X;;; maildrop to folder move routines
  625. X
  626. X;; UNIX mail-drop transfer routine
  627. X
  628. X(defun pcmail-do-unix-movemail (mail-drop)
  629. X  "UNIX mail-drop transfer function.
  630. XArgs: (mail-drop)
  631. XCall the emacs movemail utility to transfer <spool-directory>/foo to a 
  632. Xtemporary file, returning the temporary file's name to the caller.  If
  633. XMAIL-DROP has a 'display-errors-p property, signal any errors from movemail 
  634. Xby formatting the movemail output in process output buffer."
  635. X  (let ((fromfile (substitute-in-file-name 
  636. X           (concat (if (boundp 'rmail-spool-directory)
  637. X                   rmail-spool-directory
  638. X                 "/usr/spool/mail/")
  639. X                 "$USER")))
  640. X    (errors (get mail-drop 'display-errors-p))
  641. X    (tofile (expand-file-name "~/.newmail")))
  642. X
  643. X    ;; On some systems, <spool-directory>/foo is a directory
  644. X    ;; and the actual mail drop is <spool-directory>/foo/foo.
  645. X    (and (file-directory-p fromfile)
  646. X     (setq fromfile
  647. X           (substitute-in-file-name (expand-file-name "$USER" fromfile))))
  648. X    (cond ((file-exists-p fromfile)
  649. X       (pcmail-generic-unix-movemail "movemail" exec-directory errors
  650. X                     fromfile tofile)
  651. X       tofile))))
  652. X
  653. X;; UNIX NNTP mail transfer routine
  654. X
  655. X(defun pcmail-load-nntp-mail (mail-drop)
  656. X  "UNIX NNTP mail-drop transfer function.
  657. XArgs: (mail-drop)
  658. XCall the nntp_slave program to transfer netnews messages from a newgroup
  659. Xwith the same name as the current folder to a temporary file. If MAIL-DROP 
  660. Xhas a 'display-errors-p property, signal any errors from movemail by 
  661. Xformatting the movemail output in process output buffer."
  662. X  (let ((errors (get mail-drop 'display-errors-p))
  663. X    (tofile (expand-file-name (concat pcmail-folder-name ".newnews")))
  664. X    (controlfile (concat pcmail-folder-name ".ctl")))
  665. X    (pcmail-generic-unix-movemail "nntp_slave" exec-directory errors
  666. X                  pcmail-nntp-host-name pcmail-folder-name
  667. X                  tofile controlfile)
  668. X    tofile))
  669. X
  670. X(defun pcmail-delete-nntp-folder (folder-name)
  671. X  "NNTP-mail-drop-specific folder delete processing
  672. XArgs: (foler_name)
  673. X  Run on delete of FOLDER_NAME with an attached nntp mail drop.  Deletes the
  674. Xnntp_slave news control file associated with FOLDER_NAME."
  675. X  (condition-case nil
  676. X      (delete-file (expand-file-name (concat folder-name ".ctl") 
  677. X                     pcmail-directory))
  678. X    (file-error nil)))
  679. X
  680. X;; Unix MH load
  681. X
  682. X(defun pcmail-do-mh-movemail (mail-drop)
  683. X  "UNIX MH mail-drop transfer function.
  684. XArgs: (mail-drop)
  685. X  Read an MH folder name from the minibuffer and use an export utility to
  686. Xmove all messages in the MH folder into a temporary file, returning
  687. Xthe temporary file's name to the caller.  If MAIL-DROP has a 
  688. X'display-errors-p property, signal any errors from the shell script by 
  689. Xformatting the shell script output in the process output buffer."
  690. X  (let* ((folder (pcmail-mh-read-folder-name))
  691. X     (errors (get mail-drop 'display-errors-p))
  692. X     (tofile (expand-file-name (concat "~/Mail/" folder "/" folder 
  693. X                       ".mhexport"))))
  694. X    (pcmail-generic-unix-movemail "mh-to-pcmail-export" exec-directory
  695. X                  errors folder tofile)
  696. X    tofile))
  697. X
  698. X(defun pcmail-mh-read-folder-name ()
  699. X  "Read a folder name from the minibuffer, using completion.
  700. XArgs: none
  701. X  Use pcmail-completing-read to read an MH folder name from the minibuffer.
  702. XCompletion directory is the standard MH mail directory ~/Mail/.  
  703. XPcmail-completing-read takes an alist, so we need to convert the output of
  704. Xfile-name-all-completions to alist form.  In the process, remove trailing
  705. Xslashes from any directory names in the completion set.  Completion set
  706. Xis filtered through a lambda expression that passes only directories and
  707. Xeliminates the special directories \".\" and \"..\"."
  708. X  (let ((mhdir (expand-file-name "~/Mail/")))
  709. X    (or (file-directory-p mhdir) 
  710. X    (error "Default MH mail directory \"%s\" does not exist." mhdir))
  711. X    (pcmail-completing-read 
  712. X     "Folder name: " 
  713. X     (mapcar '(lambda (s) (list (if (string-match ".*/$" s)
  714. X                    (substring s 0 -1)
  715. X                  s)))
  716. X         (file-name-all-completions "" mhdir))
  717. X     nil
  718. X     '(lambda (s) (and (file-directory-p (expand-file-name (car s) mhdir))
  719. X               (not (string= (car s) ".."))
  720. X               (not (string= (car s) ".")))))))
  721. X
  722. X
  723. X;; generic call-process and error-handling part of the above three routines
  724. X
  725. X(defun pcmail-generic-unix-movemail (progname dir errorbuf &rest args)
  726. X  "Generic mail mover.  Calls a program, formatting and signalling errors.
  727. XArgs: (progname dir tofile fromfile errorbuf &rest args)
  728. X  If ERRORBUF is non-nil, generate an error buffer.  Call PROGNAME in
  729. Xdirectory DIR, passing it arguments ARGS, and routing output to ERRORBUF
  730. Xif present.  If errors occur, format the output in ERRORBUF and use it as
  731. Xan argument to a file-error signal."
  732. X  (and errors 
  733. X       (setq errors (generate-new-buffer (concat " *" progname " lossage*"))))
  734. X  (unwind-protect
  735. X      (save-excursion
  736. X    (and errors (buffer-flush-undo errors))
  737. X    (apply 'call-process (expand-file-name progname dir) nil errors nil 
  738. X           args)
  739. X    (cond ((and errors (buffer-modified-p errors))
  740. X           (set-buffer errors)
  741. X           (subst-char-in-region (point-min) (point-max) ?\n ?\  )
  742. X           (goto-char (point-max))
  743. X           (skip-chars-backward " \t")
  744. X           (delete-region (point) (point-max))
  745. X           (goto-char (point-min))
  746. X           (and (looking-at (concat progname ": "))
  747. X            (delete-region (point-min) (match-end 0)))
  748. X           (signal 'file-error
  749. X               (list progname
  750. X                 (buffer-substring (point-min) (point-max)))))))
  751. X    (and errors (kill-buffer errors))))
  752. X
  753. X;;; Generic mail drop insert function
  754. X
  755. X(defun pcmail-rename-mail-drop (mail-drop)
  756. X  "A generic mail drop insert function
  757. XArgs: (mail-drop)
  758. X  Read a source mail drop name from the minibuffer and rename it to a
  759. Xtemporary file, returning the name of the temporary file to the caller."
  760. X  (or (get mail-drop 'name-input-func)
  761. X      (error "Missing mail drop name input property in mail drop %s"
  762. X         mail-drop))
  763. X  (let ((tofile) 
  764. X    (fromfile (funcall (get mail-drop 'name-input-func))))
  765. X    (cond ((file-exists-p fromfile)
  766. X       (setq tofile 
  767. X         (concat (file-name-directory fromfile) "new-" 
  768. X             (file-name-nondirectory fromfile)))
  769. X       (rename-file fromfile tofile nil)
  770. X       tofile))))
  771. X
  772. X;;; message conversion routines.  These functions look from point
  773. X;;; forward for a message-begin regexp (end of current message, beginning of 
  774. X;;; next message).  They narrow to that region and reformat the message,
  775. X;;; putting it in Babyl format and converting any non-conformant headers
  776. X
  777. X;;; default conversion routine
  778. X
  779. X(defun pcmail-convert-unknown-message ()
  780. X  "Convert a message of unknown type to Babyl format.
  781. X  Args: none
  782. XThis routine is called when there is no match for a mail drop message-begin
  783. Xregular expression.  Assumes the buffer is narrowed from point to end of 
  784. Xbuffer."
  785. X    (insert pcmail-babyl-header)
  786. X    (pcmail-add-babyl-attr nil "badheader")
  787. X    (insert "Date: " (pcmail-todays-date) "\n")
  788. X    (insert "From: \"The Mail Reader\" <pcmail>\n")
  789. X    (insert "To: " pcmail-primary-folder-name "\n")
  790. X    (insert "Subject: Could not convert this message to Babyl format")
  791. X    (insert pcmail-header-delim)
  792. X    (goto-char (point-min))
  793. X    (while (search-forward pcmail-babyl-end nil t)
  794. X      (replace-match (concat "\n" pcmail-babyl-exploded-end)))
  795. X    (goto-char (point-max))
  796. X    (insert pcmail-babyl-end))
  797. X
  798. X;;; Babyl conversion routine
  799. X
  800. X(defun pcmail-convert-babyl-message ()
  801. X  "Convert a Babyl message to Babyl format
  802. XArgs: (none)
  803. X  Convert a Babyl message to Babyl format.  If looking at Babyl header, nuke
  804. Xit.  If looking at Babyl message, remove summary-line field if present.  
  805. XAssume the current buffer is narrowed from point to end-of-buffer."
  806. X  (cond ((looking-at "BABYL OPTIONS:")
  807. X     (setq newmsgs (1- newmsgs)) ;not a real message
  808. X     (re-search-forward pcmail-babyl-end nil 'move)
  809. X     (delete-region (point-min) (point)))
  810. X    ((looking-at pcmail-babyl-begin)
  811. X     (let ((end) (case-fold-search t))
  812. X       (cond ((re-search-forward pcmail-babyl-end nil 'move)
  813. X          (delete-region (point)
  814. X                 (progn (skip-chars-forward " \t\n")
  815. X                    (point))))
  816. X         (t
  817. X          (insert pcmail-babyl-end)))
  818. X       (save-excursion
  819. X         (goto-char (point-min))
  820. X         (cond ((search-forward pcmail-header-delim nil t)
  821. X            (setq end (point))
  822. X            (goto-char (point-min))
  823. X            (and (re-search-forward 
  824. X              "^summary-line:.*\n\\([ \t]+.*\n\\)*" end t)
  825. X             (replace-match "")))))))))
  826. X
  827. X;;; MH-export conversion routine
  828. X
  829. X(defun pcmail-convert-mh-message ()
  830. X  "Convert an exported MH message to Babyl format.  
  831. XArgs: none
  832. X  See pcmail-convert-unix-message."
  833. X  (let ((start (point))
  834. X    (msgseparator (get 'mh-mail-drop 'msg-start-regexp)))
  835. X    ;point must be at this regexp; see convert-region-to-babyl-format
  836. X    (re-search-forward msgseparator nil t)
  837. X    (replace-match "")
  838. X    (insert pcmail-babyl-header)
  839. X    (cond ((re-search-forward (concat "\\(" msgseparator "\\)") nil t)
  840. X       (goto-char (match-beginning 1)))
  841. X      (t
  842. X       (goto-char (point-max))))
  843. X    (narrow-to-region start (point))
  844. X    (goto-char (point-min))
  845. X    (pcmail-bash-unix-header)
  846. X    (goto-char (point-min))
  847. X    (while (search-forward pcmail-babyl-end nil t)
  848. X      (replace-match (concat "\n" pcmail-babyl-exploded-end)))
  849. X    (goto-char (point-max))
  850. X    (widen)
  851. X    (insert pcmail-babyl-end)))
  852. X
  853. X;;; Berkeley MAIL conversion routine
  854. X
  855. X(defun pcmail-convert-unix-message ()
  856. X  "Convert a Berkeley Mail message to Babyl format.
  857. XArgs: none
  858. X  Convert a UNIX-style Mail message to Babyl format.  Regexps snarfed from 
  859. XRMAIL.  Assumes the current buffer is narrowed from point to end of buffer."
  860. X  (let ((start (point)))
  861. X    (insert pcmail-babyl-header)
  862. X    (forward-line 1)                   ;over first line
  863. X    (if (re-search-forward               ; UNIX header regexp...
  864. X     (concat "^\\("
  865. X         "From [^ \n]*\\(\\|\".*\"[^ \n]*\\)  ?[^ \n]* [^ \n]* *"
  866. X         "[0-9]* [0-9:]* "           ; time of day
  867. X         "\\([A-Z]?[A-Z][A-Z]T \\|"        ; 3-char time zone
  868. X         "[-+][0-9][0-9][0-9][0-9] \\|\\)" ; numeric offset time zone
  869. X         "19[0-9]*$\\)") nil t)
  870. X    (goto-char (match-beginning 1))
  871. X      (goto-char (point-max)))
  872. X    (narrow-to-region start (point))
  873. X    (goto-char (point-min))
  874. X    (pcmail-bash-unix-header)
  875. X    (goto-char (point-min))
  876. X    (while (search-forward pcmail-babyl-end nil t)
  877. X      (replace-match (concat "\n" pcmail-babyl-exploded-end)))
  878. X    (goto-char (point-max))
  879. X    (widen)
  880. X    (insert pcmail-babyl-end)))
  881. X
  882. X(defun pcmail-bash-unix-header ()
  883. X  "Turn a Berkeley Mail header into an RFC822 header
  884. XArgs: none"
  885. X  (let ((hdrend (progn
  886. X          (or (re-search-forward pcmail-header-delim nil 'move)
  887. X              (insert pcmail-header-delim))
  888. X          (point)))
  889. X    (case-fold-search t))
  890. X    (save-excursion
  891. X      (save-restriction
  892. X    (narrow-to-region (point-min) hdrend)
  893. X    (goto-char (point-min))
  894. X    (pcmail-maybe-gronk-unix-header)))))
  895. X
  896. X(defun pcmail-maybe-gronk-unix-header ()
  897. X  "Transform unix mail header.
  898. XArgs: none
  899. X  If there is a righteous from or date field, nuke the non-standard Berkeley
  900. Xfrom field, otherwise extract from and date field info from it and create
  901. Xrighteous fields before nuking the Berkeley from field.  Assume buffer is
  902. Xnarrowed to the message header."
  903. X  (let ((case-fold-search t) (has-from) (has-date))
  904. X    (goto-char (point-min))
  905. X    (and (re-search-forward "^Date:[ \t]+.*\n\\([\t ]+.*\n\\)*" nil t)
  906. X     (setq has-date t))
  907. X    (goto-char (point-min))
  908. X    (and (re-search-forward "^From:[ \t]+.*\n\\([\t ]+.*\n\\)*" nil t)
  909. X     (setq has-from t))
  910. X    (goto-char (point-min))
  911. X
  912. X    ; if the header has neither a from nor a date field, create them using
  913. X    ; the Berkeley from field
  914. X    (let ((case-fold-search nil))
  915. X      (and (re-search-forward    ;The Pinhead Header
  916. X        "^From \\([^ ]*\\(\\|\".*\"[^ ]*\\)\\)  ?\\([^ ]*\\) \\([^ ]*\\) *\\([0-9]*\\) \\([0-9:]*\\)\\( [A-Z]?[A-Z][A-Z]T\\|[-+][0-9][0-9][0-9][0-9]\\|\\) 19\\([0-9]*\\)\n" nil t)
  917. X       (replace-match
  918. X        (concat
  919. X         (cond (has-date
  920. X            "")
  921. X           ((= (match-beginning 7) (match-end 7))
  922. X            (concat "Date: \\3, \\5 \\4 \\8 \\6 " pcmail-time-zone
  923. X                "\n"))
  924. X           (t
  925. X            "Date: \\3, \\5 \\4 \\8 \\6\\7\n"))
  926. X         (cond (has-from
  927. X            "")
  928. X           (t
  929. X            "From: \\1\n"))))))))
  930. X
  931. X;;; NNTP message conversion routine
  932. X
  933. X(defun pcmail-convert-nntp-message ()
  934. X  "Convert an NNTP slave message to Babyl format.  
  935. XArgs: none
  936. X  See pcmail-convert-unix-message."
  937. X  (let ((start (point)))
  938. X    ;point must be at this regexp; see convert-region-to-babyl-format
  939. X    (re-search-forward "^\^L\n" nil t)
  940. X    (replace-match pcmail-babyl-header)
  941. X    (cond ((re-search-forward "\\(^\^L\n\\)\\(Path\\|From\\|Xref\\):[ \t]+" 
  942. X                  nil t)
  943. X       (goto-char (match-beginning 1)))
  944. X      (t
  945. X       (goto-char (point-max))))
  946. X    (narrow-to-region start (point))
  947. X    (goto-char (point-min))
  948. X    (pcmail-bash-nntp-header)
  949. X    (goto-char (point-min))
  950. X    (while (search-forward pcmail-babyl-end nil t)
  951. X      (replace-match (concat "\n" pcmail-babyl-exploded-end)))
  952. X    (goto-char (point-max))
  953. X    (widen)
  954. X    (insert pcmail-babyl-end)))
  955. X
  956. X(defun pcmail-bash-nntp-header ()
  957. X  "Turn an NNTP message into a mail message.
  958. XArgs: none
  959. XSimple routine to change the NNTP Newsgroups: field into a To: field so
  960. Xthat the mail reader will be happy (mail messages need To: fields)."
  961. X  (let ((hdrend (progn
  962. X          (or (re-search-forward pcmail-header-delim nil 'move)
  963. X              (insert pcmail-header-delim))
  964. X          (point)))
  965. X    (case-fold-search t))
  966. X    (save-excursion
  967. X      (save-restriction
  968. X    (narrow-to-region (point-min) hdrend)
  969. X    (and (re-search-backward "^Newsgroups:" nil t)
  970. X         (replace-match "To:"))))))
  971. X
  972. X
  973. X;;; initial mail directory create
  974. X
  975. X(defun pcmail-unix-create-mail-directory ()
  976. X  "Create UNIX local mail directory.
  977. XArgs: none"
  978. X  (call-process "mkdir" nil nil nil 
  979. X        (directory-file-name (expand-file-name pcmail-directory))))
  980. X
  981. X
  982. X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  983. X;;;;
  984. X;;;; VMS SYSTEM-SPECIFIC FUNCTIONS.  IF YOU DON'T RUN VMS AND WANT TO SAVE 
  985. X;;;; SPACE, CUT HERE, BEGING CAREFUL TO PRESERVE THE (PROVIDE 'PCMAILSYSDEP)
  986. X;;;; FORM ON THE LAST LINE OF THE FILE
  987. X;;;;
  988. X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  989. X
  990. X;;; message print function
  991. X
  992. X(defun pcmail-vms-print-message (printer-name &ignore)
  993. X  "Send the current message to printer queue.
  994. XArgs: (printer-name &ignore)
  995. X  Send the current message to printer queue PRINTER-NAME using a canned
  996. XCOM file.  First write message to a file.  COM file prints the file and may 
  997. Xdelete it."
  998. X  (let ((temp-file (expand-file-name "pcmail-msg.txt" pcmail-directory))
  999. X    (com-file (expand-file-name "vms-doprint.com" exec-directory))
  1000. X    (bname (buffer-name)))
  1001. X    (write-region (point-min) (point-max) temp-file)
  1002. X    (pcmail-vms-command (format "@%s %s %s" com-file printer-name 
  1003. X                temp-file "delete"))
  1004. X    (set-buffer bname)))        ;pcmail-vms-command lossage
  1005. X
  1006. X;;; directory create function
  1007. X
  1008. X(defun pcmail-vms-create-mail-directory ()
  1009. X  "Create VMS local mail directory.
  1010. XArgs: none"
  1011. X  (pcmail-vms-command (concat "create/dir " pcmail-directory))
  1012. X  (while (not (file-directory-p pcmail-directory))))
  1013. X
  1014. X
  1015. X;;; mail-drop move function
  1016. X
  1017. X(defun pcmail-do-vms-movemail (mail-drop)
  1018. X  "VMS mail-drop transfer function.
  1019. XArgs: (mail-drop)
  1020. X  Call a COM file to transfer a file named newmail into a temporary file
  1021. Xnamed newmail.  Return the file name to the caller.  Assumes existence of 
  1022. Xa function called pcmail-vms-command which does a non-blocking exexute of a 
  1023. XDCL command in an kept inferior process."
  1024. X  (let ((bname (buffer-name))
  1025. X    (fromfile "newmail")
  1026. X    (tofile "mail.temp"))
  1027. X    (condition-case nil
  1028. X    (delete-file tofile)        ;in case of previous lossage
  1029. X      (file-error nil))
  1030. X    (pcmail-vms-command (concat "@" 
  1031. X             (expand-file-name "vms-movemail.com" exec-directory)
  1032. X             " "
  1033. X             fromfile
  1034. X             " "
  1035. X             tofile
  1036. X             " "
  1037. X             (file-name-directory (buffer-file-name))))
  1038. X    (set-buffer bname)                        ;pcmail-vms-command lossage
  1039. X    (while (not (file-exists-p tofile)))         ;gag, choke, nonblocking call
  1040. X    tofile))
  1041. X
  1042. X
  1043. X;;; convert a VMS VAX-MAIL message to a Babyl message.  This is pretty 
  1044. X;;; horrific, but works well enough.
  1045. X
  1046. X(defun pcmail-convert-vms-message ()
  1047. X  "Convert a VMS-style message to Babyl format.  
  1048. XArgs: none
  1049. X  See pcmail-convert-unix-message."
  1050. X  (let ((start (point))
  1051. X    (msg-start-regexp "^\^L\nFrom:[ \t]+"))
  1052. X    ;point must be at this regexp; see convert-region-to-babyl-format
  1053. X    (re-search-forward "^\^L\n" nil t)
  1054. X    (replace-match pcmail-babyl-header)
  1055. X    (cond ((re-search-forward
  1056. X        (concat "\\(^\^L\n\\)From:[ \t]+.+[0-9]+-[a-zA-Z]+-"
  1057. X            "19[0-9]+[ \t]*[0-9]+:[0-9]+")
  1058. X        nil t)
  1059. X       (goto-char (match-beginning 1)))
  1060. X      (t
  1061. X       (goto-char (point-max))))
  1062. X    (narrow-to-region start (point))
  1063. X    (goto-char (point-min))
  1064. X    (pcmail-bash-vms-header)
  1065. X    (goto-char (point-min))
  1066. X    (while (search-forward pcmail-babyl-end nil t)
  1067. X      (replace-match (concat "\n" pcmail-babyl-exploded-end)))
  1068. X    (goto-char (point-max))
  1069. X    (widen)
  1070. X    (insert pcmail-babyl-end)))
  1071. X
  1072. X(defun pcmail-bash-vms-header ()
  1073. X  "Convert a VMS message header to at least minimally resemble an RFC822 header
  1074. XArgs: none
  1075. X  Assume the region is narrowed to the current message."
  1076. X  (let ((hdrend (progn
  1077. X          (or (re-search-forward pcmail-header-delim nil 'move)
  1078. X              (insert pcmail-header-delim))
  1079. X          (point)))
  1080. X    (case-fold-search t))
  1081. X    (save-excursion
  1082. X      (save-restriction
  1083. X    (narrow-to-region (point-min) hdrend)
  1084. X    (pcmail-maybe-gronk-vms-header)))))
  1085. X
  1086. X(defun pcmail-maybe-gronk-vms-header ()
  1087. X  "Reformat or nuke VMS fields as necessary.  Not too bad.
  1088. XArgs: none"
  1089. X  (goto-char (point-min))
  1090. X  (cond ((re-search-forward
  1091. X      (concat "^From:\t"            ;Anatomy of a VMS From: field
  1092. X          "\\(\\(\\w+::\\)?"            ;optional host name
  1093. X          "\\(\\w+\\)"                    ;user name
  1094. X          "[ \t]*\\(\".*\"\\)?[ \t]*\\)";comment
  1095. X          "\\([0-9]+\\)-"         ;day
  1096. X          "\\([a-zA-Z]+\\)-"        ;month
  1097. X          "19\\([0-9]+\\)[ \t]*"    ;year
  1098. X          "\\([0-9]+:[0-9]+\\)\\(:[0-9]+\\)?.*\n")      ;time
  1099. X      nil t)
  1100. X     (replace-match 
  1101. X      (concat "Date: \\5 "
  1102. X          (capitalize 
  1103. X           (buffer-substring (match-beginning 6) (match-end 6)))
  1104. X          " \\7 \\8 " pcmail-time-zone "\n"
  1105. X          "From: \\1\n")
  1106. X      t)))
  1107. X
  1108. X  ; find subject and reformat if it exists, punt if blank
  1109. X  (goto-char (point-min))
  1110. X  (let ((no-subject) (has-cc) (has-to))
  1111. X    (setq no-subject 
  1112. X      (let ((temp-subj (mail-fetch-field "subj")))
  1113. X        (zerop (length temp-subj))))
  1114. X    (and (re-search-forward "^Subj:\t\\(.*\n\\([ \t]+.*\n\\)?\\)" nil t)
  1115. X     (replace-match (if no-subject "" "Subject: \\1") t))
  1116. X  
  1117. X    ; find CC and reformat if it exists, punt if blank
  1118. X    (goto-char (point-min))
  1119. X    (setq no-cc 
  1120. X      (let ((temp-cc (mail-fetch-field "cc")))
  1121. X        (zerop (length temp-cc))))
  1122. X    (and (re-search-forward "^CC:\t\\(.*\n\\([ \t]+.*\n\\)?\\)" nil t)
  1123. X     (if no-cc
  1124. X         (replace-match "")
  1125. X       (replace-match "Cc: \\1" t)))
  1126. X
  1127. X    ; find to: field and reformat if no other to fields exists
  1128. X    (goto-char (point-min))
  1129. X    (and (re-search-forward "^To:\t" nil t)
  1130. X     (replace-match "To: "))))
  1131. X
  1132. X;;; folder-to-file translate function
  1133. X
  1134. X(defun pcmail-vms-folder-name-to-file (folder-name)
  1135. X  "Return a copy of FOLDER-NAME that has been translated into a valid VMS
  1136. Xfile name.  The translation converts \".\" characters into \"_\" characters
  1137. Xand \"+\" characters into \"$\" characters."
  1138. X  (let ((i 0) (outbox (copy-sequence folder-name)))
  1139. X    (while (< i (length outbox))
  1140. X      (and (= (aref outbox i) ?.) (aset outbox i ?_))
  1141. X      (and (= (aref outbox i) ?+) (aset outbox i ?$))
  1142. X      (setq i (1+ i)))
  1143. X    outbox))
  1144. X
  1145. X;;; mail transmission function
  1146. X
  1147. X(defconst pcmail-vms-mailcopy "SYS$SCRATCH:MAIL.CPY"
  1148. X  "File used to store body of message when using VMS mail utility.
  1149. XDeleted on mail transmit.")
  1150. X
  1151. X(defun pcmail-vms-send-mail ()
  1152. X  "Send a message using VMS Mail.
  1153. XArgs: none
  1154. X  Copy message body to a file.  Using message header, create TO and SUBJECT
  1155. Xarguments after converting addresses from RFC822 format to VMS format.  
  1156. XCall VMS MAIL with to, subject, and body file arguments.  Note that this is
  1157. Xa hack, and may break down from time to time."
  1158. X  (let ((tembuf) (case-fold-search t) (to) (cc) (subj) (delimline)
  1159. X    (mailbuf (current-buffer)))
  1160. X    (and (file-exists-p pcmail-vms-mailcopy) (delete-file pcmail-vms-mailcopy))
  1161. X    (find-file pcmail-vms-mailcopy)
  1162. X    (unwind-protect
  1163. X    (save-excursion
  1164. X      (save-restriction
  1165. X        (setq tembuf (current-buffer))
  1166. X        (erase-buffer)
  1167. X        (insert-buffer-substring mailbuf)
  1168. X        ;; Find end of header and narrow to it.
  1169. X        (goto-char (point-min))
  1170. X        (or (re-search-forward
  1171. X         (concat "^" (regexp-quote mail-header-separator)))
  1172. X        (error "Improperly formatted mail buffer."))
  1173. X        (setq delimline (point-marker))
  1174. X        (replace-match "")
  1175. X        (narrow-to-region (point-min) delimline)
  1176. X        
  1177. X        ;; Find and handle any aliases.
  1178. X        (and mail-aliases 
  1179. X         (expand-mail-aliases (point-min) delimline))
  1180. X
  1181. X        ;; Remove any blank lines in the header.
  1182. X        (goto-char (point-min))
  1183. X        (while (re-search-forward "^[ \t\n]*\n" delimline t)
  1184. X          (replace-match ""))
  1185. X
  1186. X        ;; Find and handle any FCC fields.
  1187. X        (goto-char (point-min))
  1188. X        (and (re-search-forward "^FCC:" delimline t)
  1189. X         (mail-do-fcc delimline))
  1190. X
  1191. X        ;; don't send out a blank subject line
  1192. X        (goto-char (point-min))
  1193. X        (and (re-search-forward "^Subject:[ \t]*\n" delimline t)
  1194. X         (replace-match ""))
  1195. X
  1196. X        (goto-char (point-min))
  1197. X        (pcmail-vms-mail-convert-text-field "subject")
  1198. X        (setq to (or (mail-fetch-field "to" nil t)
  1199. X             (error "Message must have a to: recipient.")))
  1200. X        (setq cc (mail-fetch-field "cc" nil t))
  1201. X        (and cc (setq to (concat to "," cc)))
  1202. X        (setq to (pcmail-vms-mail-nl-to-space to))
  1203. X        (if (setq subj (mail-fetch-field "subject" t))
  1204. X        (setq subj (pcmail-vms-mail-nl-to-space
  1205. X                (concat "/SUBJECT=\"" subj "\"")))
  1206. X          (setq subj "")))
  1207. X      (delete-region (point-min) delimline)
  1208. X      (write-file (buffer-file-name))
  1209. X      ;; Make call to VMS Mail.
  1210. X      (pcmail-vms-command (concat "MAIL" subj " -"))
  1211. X      (pcmail-vms-command (concat pcmail-vms-mailcopy " -"))
  1212. X      (pcmail-vms-command (concat "\"" to "\""))
  1213. X      (pcmail-vms-command " "))    ; to clear out any prompts due to errors
  1214. X      (set-buffer tembuf)
  1215. X      (set-buffer-modified-p nil)
  1216. X      (kill-buffer tembuf))))
  1217. X
  1218. X(defun pcmail-vms-mail-convert-text-field (field)
  1219. X  "Convert RFC822 text fields to VMS format.
  1220. XArgs: (field)"
  1221. X  (let ((start)
  1222. X    (case-fold-search t))
  1223. X    (save-excursion
  1224. X      (save-restriction
  1225. X    (goto-char (point-min))
  1226. X    (cond ((re-search-forward (concat "^" (regexp-quote field) ":[ \t]*")
  1227. X                  nil t)
  1228. X           (setq start (point))
  1229. X           (while (progn (forward-line 1)
  1230. X                 (looking-at "[ \t]")))
  1231. X           (narrow-to-region start (point))
  1232. X           (goto-char start)
  1233. X           (while (re-search-forward "\"" nil t)
  1234. X         (replace-match "\"\""))))))))
  1235. X  
  1236. X(defun pcmail-vms-mail-nl-to-space (s)
  1237. X  "Convert all whitespace in S to spaces and return the result.  Modifies S.
  1238. XArgs: (s)"
  1239. X  (let ((i 0))
  1240. X    (while (< i (length s))
  1241. X      (and (or (= (aref s i) ?\n)
  1242. X           (= (aref s i) ?\t))
  1243. X       (aset s i ? ))
  1244. X      (setq i (1+ i))))
  1245. X  s)
  1246. X
  1247. X
  1248. X;;; subprocess capability.  NOTE THAT THIS IS A HACK.  A GRUNGY HACK.
  1249. X
  1250. X(defvar pcmail-vms-process-id nil
  1251. X  "Process ID of inferior VMS process used by pcmail-vms-command.")
  1252. X
  1253. X(defvar pcmail-vms-process-buffer "*DCL Output*"
  1254. X  "Name of buffer where output from VMS process goes.")
  1255. X
  1256. X(defun pcmail-vms-command (s)
  1257. X  "Send a string S to a kept inferior VMS process.
  1258. XArgs: (s)
  1259. X  If variable PCMAIL-VMS-PROCESS-ID is unbound, spawn a process using the
  1260. XSPAWN-PROCESS function.  Then send S to the process using the
  1261. XSEND-COMMAND-TO-SUBPROCESS function."
  1262. X  (cond ((not pcmail-vms-process-id)
  1263. X     (setq pcmail-vms-process-id (random))
  1264. X     (spawn-subprocess pcmail-vms-process-id 'pcmail-vms-process-input)))
  1265. X  (send-command-to-subprocess pcmail-vms-process-id s))
  1266. X
  1267. X(defun pcmail-vms-process-input (id s)
  1268. X  "Called when input string S arrives from VMS process with handle ID
  1269. XArgs: (id s)
  1270. X  Place input in buffer PCMAIL-VMS-PROCESS-BUFFER and display that buffer in
  1271. Xanother window."
  1272. X  (pop-to-buffer pcmail-vms-process-buffer)
  1273. X  (goto-char (point-max))
  1274. X  (insert s))
  1275. X
  1276. X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1277. X;;;;
  1278. X;;;; STOP CUTTING HERE
  1279. X;;;;
  1280. X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1281. X
  1282. X(provide 'pcmailsysdep)
  1283. ________This_Is_The_END________
  1284. if test `wc -c < pcmailsysdep.el` -ne 30775; then
  1285.     echo 'shar: pcmailsysdep.el was damaged during transit (should have been 30775 bytes)'
  1286. fi
  1287. fi        ; : end of overwriting check
  1288. exit 0
  1289.  
  1290.