|
|
1.1 root 1: ;;; Netnews reader for gnu emacs
2: ;; Copyright (C) 1985 Free Software Foundation
3:
4: ;; This file is part of GNU Emacs.
5:
6: ;; GNU Emacs is distributed in the hope that it will be useful,
7: ;; but WITHOUT ANY WARRANTY. No author or distributor
8: ;; accepts responsibility to anyone for the consequences of using it
9: ;; or for whether it serves any particular purpose or works at all,
10: ;; unless he says so in writing. Refer to the GNU Emacs General Public
11: ;; License for full details.
12:
13: ;; Everyone is granted permission to copy, modify and redistribute
14: ;; GNU Emacs, but only under the conditions described in the
15: ;; GNU Emacs General Public License. A copy of this license is
16: ;; supposed to have been given to you along with GNU Emacs so you
17: ;; can know your rights and responsibilities. It should be in a
18: ;; file named COPYING. Among other things, the copyright notice
19: ;; and this notice must be preserved on all copies.
20:
21:
22: ;;; Created Sun Mar 10,1985 at 21:35:01 ads and sundar
23: ;;; Should do the point pdl stuff sometime
24: ;;; finito except pdl.... Sat Mar 16,1985 at 06:43:44
25: ;;; lets keep the summary stuff out until we get it working ..
26: ;;; sundar Wed Apr 10,1985 at 16:32:06
27: ;;; hack slash maim. mly Thu 18 Apr, 1985 06:11:14
28: ;;; news-add-news-group / 'stead of . bug tower Mon Mar 3 15:39:44 EST 1986
29: ;;; news-mail-reply from anywhere in buffer tower Wed Mar 12 11:15:03 EST 1986
30: ;;; modified to correct reentrance bug, to not bother with groups that
31: ;;; received no new traffic since last read completely, to find out
32: ;;; what traffic a group has available much more quickly when
33: ;;; possible, to do some completing reads for group names - should
34: ;;; be much faster...
35: ;;; KING@KESTREL, Thu Mar 13 09:03:28 1986
36: ;;; fixed doc error tower Sun Mar 16 14:25:43 EST 1986
37: (require 'mail-utils)
38:
39: ;Now in paths.el.
40: ;(defvar news-path "/usr/spool/news/"
41: ; "The root directory below which all news files are stored.")
42: ;(defvar news-inews-program "inews"
43: ; "Function to post news.")
44:
45: (defvar news-startup-file "$HOME/.newsrc" "Contains ~/.newsrc")
46: (defvar news-certification-file "$HOME/.news-dates" "Contains ~/.news-dates")
47:
48: ;;; random headers that we decide to ignore.
49: (defvar news-ignored-headers
50: "^Path:\\|^Posting-Version:\\|^Article-I.D.:\\|^Followup-To:\\|^Expires:\\|^Date-Received:\\|^Organization:\\|^References:\\|^Control:\\|^Xref:\\|^Lines:\\|^Posted:\\|^Relay-Version:\\|^Message-ID:\\|^Nf-ID:"
51: "All random fields within the header of a message.")
52:
53: (defvar news-mode-map nil)
54: (defvar news-read-first-time-p t)
55: ;; Contains the (dotified) news groups of which you are a member.
56: (defvar news-user-group-list nil)
57:
58: (defvar news-current-news-group nil)
59: (defvar news-current-group-begin nil)
60: (defvar news-current-group-end nil)
61: (defvar news-current-certifications nil
62: "An assoc list of a group name and the time at which it is
63: known that the grop had no new traffic")
64: (defvar news-current-certifiable nil
65: "The time when the directory we are now working on was written")
66:
67:
68: (defvar news-message-filter nil
69: "User specifiable filter function that will be called during
70: formatting of the news file")
71:
72: ;(defvar news-mode-group-string "Starting-Up"
73: ; "Mode line group name info is held in this variable")
74: (defvar news-list-of-files nil
75: "Global variable in which we store the list of files
76: associated with the current newsgroup")
77: (defvar news-list-of-files-possibly-bogus nil
78: "variable indicating we only are guessing at which files are available.
79: Not currently used.")
80:
81: ;; association list in which we store lists of the form
82: ;; (pointified-group-name (first last old-last))
83: (defvar news-group-article-assoc nil)
84:
85: (defvar news-current-message-number 0 "Displayed Article Number")
86: (defvar news-total-current-group 0 "Total no of messages in group")
87:
88: (defvar news-unsubscribe-groups ())
89: (defvar news-point-pdl () "List of visited news messages.")
90: (defvar news-no-jumps-p t)
91: (defvar news-buffer () "Buffer into which news files are read.")
92:
93: (defmacro caar (x) (list 'car (list 'car x)))
94: (defmacro cadr (x) (list 'car (list 'cdr x)))
95: (defmacro cdar (x) (list 'cdr (list 'car x)))
96: (defmacro caddr (x) (list 'car (list 'cdr (list 'cdr x))))
97: (defmacro cadar (x) (list 'car (list 'cdr (list 'car x))))
98: (defmacro caadr (x) (list 'car (list 'car (list 'cdr x))))
99: (defmacro cdadr (x) (list 'cdr (list 'car (list 'cdr x))))
100:
101: (defmacro news-wins (pfx index)
102: (` (file-exists-p (concat (, pfx) "/" (int-to-string (, index))))))
103:
104: (defvar news-max-plausible-gap 2
105: "* In an rnews directory, the maximum possible gap size.
106: A gap is a sequence of missing messages between two messages that exist.
107: An empty file does not contribute to a gap -- it ends one.")
108:
109: (defun news-find-first-and-last (prefix base)
110: (and (news-wins prefix base)
111: (cons (news-find-first-or-last prefix base -1)
112: (news-find-first-or-last prefix base 1))))
113:
114: (defmacro // (a1 a2)
115: ;;; a form of / that guarantees that (/ -1 2) = 0
116: (if (zerop (/ -1 2))
117: (` (/ (, a1) (, a2)))
118: (` (if (< (, a1) 0)
119: (- (/ (- (, a1)) (, a2)))
120: (/ (, a1) (, a2))))))
121:
122: (defun news-find-first-or-last (pfx base dirn)
123: ;; first use powers of two to find a plausible cieling
124: (let ((original-dir dirn))
125: (while (news-wins pfx (+ base dirn))
126: (setq dirn (* dirn 2)))
127: (setq dirn (// dirn 2))
128: ;;; Then use a binary search to find the high water mark
129: (let ((offset (// dirn 2)))
130: (while (/= offset 0)
131: (if (news-wins pfx (+ base dirn offset))
132: (setq dirn (+ dirn offset)))
133: (setq offset (// offset 2))))
134: ;;; If this high-water mark is bogus, recurse.
135: (let ((offset (* news-max-plausible-gap original-dir)))
136: (while (and (/= offset 0) (not (news-wins pfx (+ base dirn offset))))
137: (setq offset (- offset original-dir)))
138: (if (= offset 0)
139: (+ base dirn)
140: (news-find-first-or-last pfx (+ base dirn offset) original-dir)))))
141:
142: (defun rnews ()
143: "Read netnews for groups for which you are a member and add or delete groups.
144: You can reply to articles posted and send articles to any group.
145: Type Help m once reading news to get a list of rnews commands."
146: (interactive)
147: (let ((last-buffer (buffer-name)))
148: (switch-to-buffer (setq news-buffer (get-buffer-create "*news*")))
149: (news-mode)
150: (setq news-buffer-save last-buffer)
151: (setq buffer-read-only nil)
152: (erase-buffer)
153: (setq buffer-read-only t)
154: (set-buffer-modified-p t)
155: (sit-for 0)
156: (message "Getting new net news...")
157: (news-set-mode-line)
158: (news-get-certifications)
159: (news-get-new-news)))
160:
161: (defun news-group-certification (group)
162: (cdr-safe (assoc group news-current-certifications)))
163:
164:
165: (defun news-set-current-certifiable ()
166: ;;; Record the date that corresponds to the directory you are about to check
167: (let ((file (concat news-path
168: (string-subst-char ?/ ?. news-current-news-group))))
169: (setq news-current-certifiable
170: (nth 5 (file-attributes
171: (or (file-symlink-p file) file))))))
172:
173: (defun news-get-certifications ()
174: ;;; Read the certified-read file from last session
175: (save-excursion
176: (save-window-excursion
177: (setq news-current-certifications
178: (car-safe
179: (condition-case var
180: (let*
181: ((file (substitute-in-file-name news-certification-file))
182: (buf (find-file-noselect file)))
183: (and (file-exists-p file)
184: (progn
185: (switch-to-buffer buf 'norecord)
186: (unwind-protect
187: (read-from-string (buffer-string))
188: (kill-buffer buf)))))
189: (error nil)))))))
190:
191: (defun news-write-certifications ()
192: ;;; Write a certification file. This is an assoc list of group names with
193: ;;;doubletons that represent mod times of the directory when group is read
194: ;;;completely.
195: (save-excursion
196: (save-window-excursion
197: (with-output-to-temp-buffer
198: "*CeRtIfIcAtIoNs*"
199: (print news-current-certifications))
200: (let ((buf (get-buffer "*CeRtIfIcAtIoNs*")))
201: (switch-to-buffer buf)
202: (write-file (substitute-in-file-name news-certification-file))
203: (kill-buffer buf)))))
204:
205: (defun news-set-current-group-certification ()
206: (let ((cgc (assoc news-current-news-group news-current-certifications)))
207: (if cgc (setcdr cgc news-current-certifiable)
208: (push (cons news-current-news-group news-current-certifiable)
209: news-current-certifications))))
210:
211: (defun news-set-minor-modes ()
212: "Creates a minor mode list that has group name, total articles,
213: and attribute for current article."
214: (setq minor-modes (list (cons 'foo
215: (concat news-current-message-number
216: "/"
217: news-total-current-group
218: (news-get-attribute-string))))))
219:
220: (defun news-set-message-counters ()
221: "Scan through current news-groups filelist to figure out how many messages
222: are there. Set counters for use with minor mode display."
223: (if (null news-list-of-files)
224: (setq news-current-message-number 0)))
225:
226: (if news-mode-map
227: nil
228: (setq news-mode-map (make-keymap))
229: (suppress-keymap news-mode-map)
230: (define-key news-mode-map "." 'beginning-of-buffer)
231: (define-key news-mode-map " " 'scroll-up)
232: (define-key news-mode-map "\177" 'scroll-down)
233: (define-key news-mode-map "n" 'news-next-message)
234: (define-key news-mode-map "c" 'news-make-link-to-message)
235: (define-key news-mode-map "p" 'news-previous-message)
236: (define-key news-mode-map "j" 'news-goto-message)
237: (define-key news-mode-map "q" 'news-exit)
238: (define-key news-mode-map "e" 'news-exit)
239: (define-key news-mode-map "\ej" 'news-goto-news-group)
240: (define-key news-mode-map "\en" 'news-next-group)
241: (define-key news-mode-map "\ep" 'news-previous-group)
242: (define-key news-mode-map "l" 'news-list-news-groups)
243: (define-key news-mode-map "?" 'describe-mode)
244: (define-key news-mode-map "g" 'news-get-new-news)
245: (define-key news-mode-map "f" 'news-reply)
246: (define-key news-mode-map "m" 'news-mail-other-window)
247: (define-key news-mode-map "a" 'news-post-news)
248: (define-key news-mode-map "r" 'news-mail-reply)
249: (define-key news-mode-map "o" 'news-save-item-in-file)
250: (define-key news-mode-map "t" 'news-show-all-headers)
251: (define-key news-mode-map "x" 'news-force-update)
252: (define-key news-mode-map "A" 'news-add-news-group)
253: (define-key news-mode-map "u" 'news-unsubscribe-current-group)
254: (define-key news-mode-map "U" 'news-unsubscribe-group))
255:
256: (defun news-mode ()
257: "News Mode is used by M-x rnews for editing News files.
258: All normal editing commands are turned off.
259: Instead, these commands are available:
260:
261: . move point to front of this news article (same as Meta-<).
262: Space scroll to next screen of this news article.
263: Delete scroll down previous page of this news article.
264: n move to next news article, possibly next group.
265: p move to previous news article, possibly previous group.
266: j jump to news article specified by numeric position.
267: M-j jump to news group.
268: M-n goto next news group.
269: M-p goto previous news group.
270: l list all the news groups with current status.
271: ? print this help message.
272: g get new net news.
273: f post follow-up article to the net.
274: a post a news article.
275: A add a newsgroup.
276: o save the current article in the named file (append if file exists).
277: c \"copy\" (actually link) current or prefix-arg msg to file.
278: warning: target directory and message file must be on same device
279: (UNIX magic)
280: t show all the headers this news article originally had.
281: q quit reading news after updating .newsrc file.
282: e exit updating .newsrc file.
283: m mail a news article. Same as C-x 4 m.
284: x update last message seen to be the current message.
285: r reply to this news article. Like m but initializes some fields.
286: u unsubscribe from current newsgroup.
287: U unsubscribe from specified newsgroup."
288: (interactive)
289: (kill-all-local-variables)
290: (make-local-variable 'news-read-first-time-p)
291: (setq news-read-first-time-p t)
292: (make-local-variable 'news-current-news-group)
293: ; (setq news-current-news-group "??")
294: (make-local-variable 'news-current-group-begin)
295: (setq news-current-group-begin 0)
296: (make-local-variable 'news-current-message-number)
297: (setq news-current-message-number 0)
298: (make-local-variable 'news-total-current-group)
299: (make-local-variable 'news-buffer-save)
300: (make-local-variable 'version-control)
301: (setq version-control 'never)
302: (make-local-variable 'news-point-pdl)
303: ; This breaks it. I don't have time to figure out why. -- RMS
304: ; (make-local-variable 'news-group-article-assoc)
305: (setq major-mode 'news-mode)
306: (setq mode-name "NEWS")
307: (news-set-mode-line)
308: (set-syntax-table text-mode-syntax-table)
309: (use-local-map news-mode-map)
310: (setq local-abbrev-table text-mode-abbrev-table)
311: (run-hooks 'news-mode-hook))
312:
313: (defun string-subst-char (new old string)
314: (let (index)
315: (setq old (regexp-quote (char-to-string old))
316: string (substring string 0))
317: (while (setq index (string-match old string))
318: (aset string index new)))
319: string)
320:
321: ;;; update read message number
322: (defmacro news-update-message-read (ngroup nno)
323: (list 'setcar
324: (list 'cdadr
325: (list 'assoc ngroup 'news-group-article-assoc))
326: nno))
327:
328: (defun news-parse-range (number-string)
329: "Parse string representing range of numbers of he form <a>-<b>
330: to a list (a . b)"
331: (let ((n (string-match "-" number-string)))
332: (if n
333: (cons (string-to-int (substring number-string 0 n))
334: (string-to-int (substring number-string (1+ n))))
335: (setq n (string-to-int number-string))
336: (cons n n))))
337:
338: ;(defun is-in (elt lis)
339: ; (catch 'foo
340: ; (while lis
341: ; (if (equal (car lis) elt)
342: ; (throw 'foo t)
343: ; (setq lis (cdr lis))))))
344:
345:
346: (defun news-get-new-news ()
347: "Get new netnews if there is any for the current user."
348: (interactive)
349: (if (not (null news-user-group-list))
350: (news-update-newsrc-file))
351: (setq news-group-article-assoc ())
352: (setq news-user-group-list ())
353: (message "Looking up .newsrc file...")
354: (let ((file (substitute-in-file-name news-startup-file))
355: (temp-user-groups ()))
356: (save-excursion
357: (let ((newsrcbuf (find-file-noselect file))
358: start end endofline tem)
359: (set-buffer newsrcbuf)
360: (goto-char 0)
361: (while (search-forward ": " nil t)
362: (setq end (point))
363: (beginning-of-line)
364: (setq start (point))
365: (end-of-line)
366: (setq endofline (point))
367: (setq tem (buffer-substring start (- end 2)))
368: (let ((range (news-parse-range
369: (buffer-substring end endofline))))
370:
371: ; (if (is-in tem temp-user-groups)
372: ; (progn
373: ; (setq temp-user-groups (delq tem temp-user-groups))
374: ; (setq news-group-article-assoc
375: ; (delq (assoc tem news-group-article-assoc)
376: ; news-group-article-assoc))
377: ; (message "Subscribed to the same group twice?")))
378:
379: (setq temp-user-groups (cons tem temp-user-groups)
380: news-group-article-assoc
381: (cons (list tem (list (car range)
382: (cdr range)
383: (cdr range)))
384: news-group-article-assoc))))
385: (kill-buffer newsrcbuf)))
386: (setq temp-user-groups (nreverse temp-user-groups))
387: (message "Prefrobnicating...")
388: (switch-to-buffer news-buffer)
389: (setq news-user-group-list temp-user-groups)
390: (while (and temp-user-groups
391: (not (news-read-files-into-buffer
392: (car temp-user-groups) nil)))
393: (setq temp-user-groups (cdr temp-user-groups)))
394: (if (null temp-user-groups)
395: (message "No news is good news.")
396: (message ""))))
397:
398: (defun news-list-news-groups ()
399: "Display all the news groups to which you belong."
400: (interactive)
401: (if (null news-user-group-list)
402: (message "No user groups read yet!")
403: (let ((buffer-read-only ()))
404: (setq mode-line-format "--%%--[q: to goback, space: scroll-forward, delete:scroll-backward] %M --%--")
405: (local-set-key " " 'scroll-up)
406: (local-set-key "\177" 'scroll-down)
407: (local-set-key "q" 'news-get-back)
408: (goto-char 0)
409: (save-excursion
410: (erase-buffer)
411: (insert
412: "News Group Msg No. News Group Msg No.\n")
413: (insert
414: "------------------------- -------------------------\n")
415: (let ((temp news-user-group-list)
416: (flag nil))
417: (while temp
418: (let ((item (assoc (car temp) news-group-article-assoc)))
419: (insert (car item))
420: (indent-to (if flag 52 20))
421: (insert (int-to-string (cadr (cadr item))))
422: (if flag
423: (insert "\n")
424: (indent-to 33))
425: (setq temp (cdr temp) flag (not flag)))))))))
426:
427: (defun news-get-back ()
428: "Called when you quit from seeing the news groups list."
429: (interactive)
430: (let ((buffer-read-only ()))
431: (erase-buffer)
432: (local-set-key "q" 'news-exit)
433: (news-set-mode-line)
434: (news-read-in-file
435: (concat news-path
436: (string-subst-char ?/ ?. news-current-news-group)
437: "/" (int-to-string news-current-message-number)))))
438:
439: (defun strcpyn (str1 str2 len)
440: (if (string= str2 "")
441: str1
442: (while (>= len 0)
443: (aset str1 len (aref str2 len))
444: (setq len (1- len)))
445: str1))
446:
447: ;; Mode line hack
448: (defun news-set-mode-line ()
449: "Set mode line string to something useful."
450: (let ((tem (1- (length news-current-news-group)))
451: (idx 0)
452: (buffer-modified-p ()))
453: (setq mode-line-format
454: (concat "--%1*%1*-NEWS: "
455: (if (> tem 15)
456: news-current-news-group
457: (let ((string (make-string 16 ? )))
458: (setq idx 0)
459: (while (<= idx tem)
460: (aset string idx (aref news-current-news-group idx))
461: (setq idx (1+ idx)))
462: string))
463: " ["
464: (if (integerp news-current-message-number)
465: (int-to-string news-current-message-number)
466: "??")
467: "/"
468: (if (integerp news-current-group-end)
469: (int-to-string news-current-group-end)
470: news-current-group-end)
471: "] %M ----%3p-%-"))
472: (set-buffer-modified-p t)
473: (sit-for 0)))
474:
475: (defun news-goto-news-group (gp)
476: "Takes a string and goes to that news group."
477: (interactive (list (completing-read "NewsGroup: "
478: news-group-article-assoc)))
479: (message "Jumping to news group %s..." gp)
480: (news-select-news-group gp)
481: (message "Jumping to news group %s... done." gp))
482:
483: (defun news-select-news-group (gp)
484: (let ((grp (assoc gp news-group-article-assoc)))
485: (if (null grp)
486: (error "No more news groups")
487: (progn
488: (news-update-message-read news-current-news-group
489: (cdar news-point-pdl))
490: (news-read-files-into-buffer (car grp) nil)
491: (news-set-mode-line)))))
492:
493: (defun news-goto-message (arg)
494: "Goes to the article ARG in current newsgroup."
495: (interactive "p")
496: (if (null current-prefix-arg)
497: (setq arg (read-no-blanks-input "Go to article: " "")))
498: (news-select-message arg))
499:
500: (defun news-select-message (arg)
501: (if (stringp arg) (setq arg (string-to-int arg)))
502: (let ((file (concat news-path
503: (string-subst-char ?/ ?. news-current-news-group)
504: "/" arg)))
505: (if (file-exists-p file)
506: (let ((buffer-read-only ()))
507: (if (= arg
508: (or (cadr (memq (cdar news-point-pdl) news-list-of-files))
509: 0))
510: (setcdr (car news-point-pdl) arg))
511: (setq news-current-message-number arg)
512: (news-read-in-file file)
513: (news-set-mode-line))
514: (error "Article %d nonexistent" arg))))
515:
516: (defun news-force-update ()
517: "updates the position of last article read in the current news group"
518: (interactive)
519: (setcdr (car news-point-pdl) news-current-message-number)
520: (message "Updated to %d" news-current-message-number))
521:
522: (defun news-next-message (arg)
523: "Move ARG messages forward within one newsgroup.
524: Negative ARG moves backward.
525: If ARG is 1 or -1, moves to next or previous newsgroup if at end."
526: (interactive "p")
527: (let ((no (+ arg news-current-message-number)))
528: (if (or (< no news-current-group-begin)
529: (> no news-current-group-end))
530: (cond ((= arg 1)
531: (news-set-current-group-certification)
532: (news-next-group)
533: (while (null news-list-of-files)
534: (news-next-group)))
535: ((= arg -1)
536: (news-previous-group)
537: (while (null news-list-of-files)
538: (news-previous-group)))
539: (t (error "Article out of range")))
540: (let ((plist (news-get-motion-lists
541: news-current-message-number
542: news-list-of-files)))
543: (if (< arg 0)
544: (news-select-message (nth (1- (- arg)) (car (cdr plist))))
545: (news-select-message (nth (1- arg) (car plist))))))))
546:
547: (defun news-previous-message (arg)
548: "Move ARG messages backward in current newsgroup.
549: With no arg or arg of 1, move one message
550: and move to previous newsgroup if at beginning.
551: A negative ARG means move forward."
552: (interactive "p")
553: (news-next-message (- arg)))
554:
555: (defun news-move-to-group (arg)
556: "Given arg move forward or backward to a new newsgroup."
557: (let ((cg news-current-news-group))
558: (let ((plist (news-get-motion-lists cg news-user-group-list))
559: ngrp)
560: (if (< arg 0)
561: (or (setq ngrp (nth (1- (- arg)) (cadr plist)))
562: (error "No more news groups"))
563: (or (setq ngrp (nth arg (car plist)))
564: (error "No previous news groups")))
565: (news-select-news-group ngrp))))
566:
567: (defun news-next-group ()
568: "Moves to the next user group."
569: (interactive)
570: ; (message "Moving to next group...")
571: (news-move-to-group 0))
572: ; (message "Moving to next group... done.")
573:
574: (defun news-previous-group ()
575: "Moves to the previous user group."
576: (interactive)
577: ; (message "Moving to previous group...")
578: (news-move-to-group -1))
579: ; (message "Moving to previous group... done.")
580:
581: (defun news-get-motion-lists (arg listy)
582: "Given a msgnumber/group this will return a list of two lists;
583: one for moving forward and one for moving backward."
584: (let ((temp listy)
585: (result ()))
586: (catch 'out
587: (while temp
588: (if (equal (car temp) arg)
589: (throw 'out (cons (cdr temp) (list result)))
590: (setq result (nconc (list (car temp)) result))
591: (setq temp (cdr temp)))))))
592:
593: ;; miscellaneous io routines
594: (defun news-read-in-file (filename)
595: (erase-buffer)
596: (let ((start (point)))
597: (insert-file-contents filename)
598: (news-convert-format)
599: (goto-char start)
600: (forward-line 1)
601: (if (eobp)
602: (message "(Empty file?)")
603: (goto-char start))))
604:
605: (defun news-convert-format ()
606: (save-excursion
607: (save-restriction
608: (let* ((start (point))
609: (end (condition-case ()
610: (progn (search-forward "\n\n") (point))
611: (error nil)))
612: has-from has-date)
613: (cond (end
614: (narrow-to-region start end)
615: (goto-char start)
616: (setq has-from (search-forward "\nFrom:" nil t))
617: (cond ((and (not has-from) has-date)
618: (goto-char start)
619: (search-forward "\nDate:")
620: (beginning-of-line)
621: (kill-line) (kill-line)))
622: (news-delete-headers start)
623: (goto-char start)))))))
624:
625: (defun news-show-all-headers ()
626: "Redisplay current news item with all original headers"
627: (interactive)
628: (let (news-ignored-headers)
629: (news-get-back)))
630:
631: (defun news-delete-headers (pos)
632: (goto-char pos)
633: (and (stringp news-ignored-headers)
634: (while (re-search-forward news-ignored-headers nil t)
635: (beginning-of-line)
636: (delete-region (point)
637: (progn (re-search-forward "\n[^ \t]")
638: (forward-char -1)
639: (point))))))
640:
641: (defun news-exit ()
642: "Quit news reading session and update the newsrc file."
643: (interactive)
644: (if (y-or-n-p "Do you really wanna quit reading news ? ")
645: (progn (message "Updating .newsrc...")
646: (news-update-newsrc-file)
647: (news-write-certifications)
648: (message "Updating .newsrc... done")
649: (message "Now do some real work")
650: (and (fboundp 'bury-buffer) (bury-buffer (current-buffer)))
651: (switch-to-buffer news-buffer-save)
652: (setq news-user-group-list ()))
653: (message "")))
654:
655: (defun news-update-newsrc-file ()
656: "Updates the newsrc file in the users home dir."
657: (let ((newsrcbuf (find-file-noselect
658: (substitute-in-file-name news-startup-file)))
659: (tem news-user-group-list)
660: group)
661: (save-excursion
662: (if (not (null news-current-news-group))
663: (news-update-message-read news-current-news-group
664: (cdar news-point-pdl)))
665: (switch-to-buffer newsrcbuf)
666: (while tem
667: (setq group (assoc (car tem)
668: news-group-article-assoc))
669: (if (= (cadr (cadr group)) (caddr (cadr group)))
670: nil
671: (goto-char 0)
672: (if (search-forward (concat (car group) ": ") nil t)
673: (kill-line nil)
674: (insert (car group) ": \n") (backward-char 1))
675: (insert (int-to-string (car (cadr group))) "-"
676: (int-to-string (cadr (cadr group)))))
677: (setq tem (cdr tem)))
678: (while news-unsubscribe-groups
679: (setq group (assoc (car news-unsubscribe-groups)
680: news-group-article-assoc))
681: (goto-char 0)
682: (if (search-forward (concat (car group) ": ") nil t)
683: (progn
684: (backward-char 2)
685: (kill-line nil)
686: (insert "! " (int-to-string (car (cadr group)))
687: "-" (int-to-string (cadr (cadr group))))))
688: (setq news-unsubscribe-groups (cdr news-unsubscribe-groups)))
689: (save-buffer)
690: (kill-buffer (current-buffer)))))
691:
692:
693: (defun news-unsubscribe-group (group)
694: "Removes you from newgroup GROUP."
695: (interactive (list (completing-read "Unsubscribe from group: "
696: news-group-article-assoc)))
697: (news-unsubscribe-internal group))
698:
699: (defun news-unsubscribe-current-group ()
700: "Removes you from the newsgroup you are now reading."
701: (interactive)
702: (if (y-or-n-p "Do you really want to unsubscribe from this group ? ")
703: (news-unsubscribe-internal news-current-news-group)))
704:
705: (defun news-unsubscribe-internal (group)
706: (let ((tem (assoc group news-group-article-assoc)))
707: (if tem
708: (progn
709: (setq news-unsubscribe-groups (cons group news-unsubscribe-groups))
710: (news-update-message-read group (cdar news-point-pdl))
711: (if (equal group news-current-news-group)
712: (news-next-group))
713: (message "Member-p of %s ==> nil" group))
714: (error "No such group: %s" group))))
715:
716: (defun news-save-item-in-file (file)
717: "Save the current article that is being read by appending to a file."
718: (interactive "FSave item in file: ")
719: (append-to-file (point-min) (point-max) file))
720:
721: (defun news-get-pruned-list-of-files (gp-list end-file-no)
722: "Given a news group it does an ls to give all files in the news group.
723: The arg must be in slashified format."
724: (let
725: ((answer
726: (and
727: (not (and end-file-no
728: (equal (news-set-current-certifiable)
729: (news-group-certification gp-list))
730: (setq news-list-of-files nil
731: news-list-of-files-possibly-bogus t)))
732: (let* ((file-directory (concat news-path
733: (string-subst-char ?/ ?. gp-list)))
734: tem
735: (last-winner
736: (and end-file-no
737: (news-wins file-directory end-file-no)
738: (news-find-first-or-last file-directory end-file-no 1))))
739: (setq news-list-of-files-possibly-bogus t news-list-of-files nil)
740: (if last-winner
741: (progn
742: (setq news-list-of-files-possibly-bogus t
743: news-current-group-end last-winner)
744: (while (> last-winner end-file-no)
745: (push last-winner news-list-of-files)
746: (setq last-winner (1- last-winner)))
747: news-list-of-files)
748: (if (not (file-directory-p file-directory))
749: nil
750: (setq news-list-of-files
751: (setq tem (directory-files file-directory)))
752: (while tem
753: (if (or (not (string-match "^[0-9]*$" (car tem)))
754: ; dont get confused by directories that look like numbers
755: (file-directory-p
756: (concat file-directory "/" (car tem)))
757: (<= (string-to-int (car tem)) end-file-no))
758: (setq news-list-of-files
759: (delq (car tem) news-list-of-files)))
760: (setq tem (cdr tem)))
761: (if (null news-list-of-files)
762: (progn (setq news-current-group-end 0)
763: nil)
764: (setq news-list-of-files
765: (mapcar 'string-to-int news-list-of-files))
766: (setq news-list-of-files (sort news-list-of-files '<))
767: (setq news-current-group-end
768: (elt news-list-of-files
769: (1- (length news-list-of-files))))
770: news-list-of-files)))))))
771: (or answer (progn (news-set-current-group-certification) nil))))
772:
773: (defun news-read-files-into-buffer (group reversep)
774: (let* ((files-start-end (cadr (assoc group news-group-article-assoc)))
775: (start-file-no (car files-start-end))
776: (end-file-no (cadr files-start-end))
777: (buffer-read-only nil))
778:
779: (setq news-current-news-group group)
780: (setq news-current-message-number nil)
781: (setq news-current-group-end nil)
782: (news-set-mode-line)
783: (news-get-pruned-list-of-files group end-file-no)
784: (news-set-mode-line)
785: ;; should be a lot smarter than this if we have to move
786: ;; around correctly.
787: (setq news-point-pdl (list (cons (car files-start-end)
788: (cadr files-start-end))))
789: (if (null news-list-of-files)
790: (progn (erase-buffer)
791: (setq news-current-group-end end-file-no)
792: (setq news-current-group-begin end-file-no)
793: (setq news-current-message-number end-file-no)
794: (news-set-mode-line)
795: ; (message "No new articles in " group " group.")
796: nil)
797: (setq news-current-group-begin (car news-list-of-files))
798: (if reversep
799: (setq news-current-message-number news-current-group-end)
800: (if (> (car news-list-of-files) end-file-no)
801: (setcdr (car news-point-pdl) (car news-list-of-files)))
802: (setq news-current-message-number news-current-group-begin))
803: (news-set-message-counters)
804: (news-set-mode-line)
805: (news-read-in-file (concat news-path
806: (string-subst-char ?/ ?. group)
807: "/"
808: (int-to-string
809: news-current-message-number)))
810: (news-set-message-counters)
811: (news-set-mode-line)
812: t)))
813:
814:
815: ;;; Replying and posting news items are done by these functions.
816: ;;; imported from rmail and modified to work with rnews ...
817: ;;; Mon Mar 25,1985 at 03:07:04 ads@mit-hermes.
818: ;;; this is done so that rnews can operate independently from rmail.el and sendmail and
819: ;;; dosen't have to autoload these functions.
820:
821: ;;;>> Nuked by Mly to autoload those functions again, as the duplication of
822: ;;;>> code was making maintenance too difficult.
823:
824: (defvar news-reply-mode-map () "Mode map used by news-reply.")
825:
826: (or news-reply-mode-map
827: (progn (setq news-reply-mode-map (make-keymap))
828: (define-key news-reply-mode-map "\C-c?" 'describe-mode)
829: (define-key news-reply-mode-map "\C-ct" 'mail-to)
830: (define-key news-reply-mode-map "\C-cb" 'mail-bcc)
831: (define-key news-reply-mode-map "\C-cc" 'mail-cc)
832: (define-key news-reply-mode-map "\C-cs" 'mail-subject)
833: (define-key news-reply-mode-map "\C-cy" 'mail-yank-original)
834: (define-key news-reply-mode-map "\C-c\C-c" 'news-inews)
835: (define-key news-reply-mode-map "\C-c\C-s" 'news-inews)))
836:
837: (defun news-reply-mode ()
838: "Major mode for editing news to be posted on netnews.
839: Like Text Mode but with these additional commands:
840: \\{news-reply-mode-map}"
841: (interactive)
842: ;; require...
843: (or (fboundp 'mail-setup) (load "sendmail"))
844: (kill-all-local-variables)
845: (make-local-variable 'mail-reply-buffer)
846: (setq mail-reply-buffer nil)
847: (set-syntax-table text-mode-syntax-table)
848: (use-local-map news-reply-mode-map)
849: (setq local-abbrev-table text-mode-abbrev-table)
850: (setq major-mode 'news-reply-mode)
851: (setq mode-name "News")
852: (make-local-variable 'paragraph-separate)
853: (make-local-variable 'paragraph-start)
854: (setq paragraph-start (concat "^" mail-header-separator "$\\|"
855: paragraph-start))
856: (setq paragraph-separate (concat "^" mail-header-separator "$\\|"
857: paragraph-separate))
858: (run-hooks 'text-mode-hook 'news-reply-mode-hook))
859:
860: (defun news-setup (to subject in-reply-to newsgroups replybuffer)
861: (setq mail-reply-buffer replybuffer)
862: (let ((mail-setup-hook nil))
863: (if (null to)
864: ;; this hack is needed so that inews wont be confused by
865: ;; the fcc: and bcc: fields
866: (let ((mail-self-blind nil)
867: (mail-archive-file-name nil))
868: (mail-setup to subject in-reply-to nil replybuffer)
869: (beginning-of-line)
870: (kill-line 1)
871: (goto-char (point-max)))
872: (mail-setup to subject in-reply-to nil replybuffer))
873: (goto-char (point-max))
874: (if (let ((case-fold-search t))
875: (re-search-backward "^Subject:" (point-min) t))
876: (progn (beginning-of-line)
877: (insert "Newsgroups: " (or newsgroups "") "\n")
878: (if (not newsgroups)
879: (backward-char 1)
880: (goto-char (point-max)))))
881: (run-hooks 'news-setup-hook)))
882:
883: (defun news-inews ()
884: "Send a news message using inews."
885: (interactive)
886: (let* (newsgroups subject
887: (case-fold-search nil))
888: (save-restriction
889: (goto-char (point-min))
890: (search-forward (concat "\n" mail-header-separator "\n"))
891: (narrow-to-region (point-min) (point))
892: (setq newsgroups (mail-fetch-field "newsgroups")
893: subject (mail-fetch-field "subject")))
894: (widen)
895: (goto-char (point-min))
896: (search-forward (concat "\n" mail-header-separator "\n"))
897: (message "Posting to the net...")
898: (call-process-region (point) (point-max)
899: news-inews-program nil 0 nil
900: "-t" subject
901: "-n" newsgroups)
902: (message "Posting to the net... done")
903: (set-buffer-modified-p nil)
904: (delete-windows-on (current-buffer))
905: (and (fboundp 'bury-buffer) (bury-buffer (current-buffer)))))
906:
907: (defun news-mail-reply ()
908: "Mail a reply to the author of the current article.
909: While composing the reply, use \\[mail-yank-original] to yank the original message into it."
910: (interactive)
911: (let (from cc subject date to reply-to
912: (buffer (current-buffer)))
913: (save-restriction
914: (narrow-to-region (point-min) (progn (goto-line (point-min))
915: (search-forward "\n\n")
916: (- (point) 2)))
917: (setq from (mail-fetch-field "from")
918: subject (mail-fetch-field "subject")
919: reply-to (mail-fetch-field "reply-to")
920: date (mail-fetch-field "date"))
921: (setq to from)
922: (pop-to-buffer "*mail*")
923: (mail nil
924: (if reply-to reply-to to)
925: subject
926: (let ((stop-pos (string-match " *at \\| *@ \\| *(\\| *<" from)))
927: (concat (if stop-pos (substring from 0 stop-pos) from)
928: "'s message of "
929: date))
930: nil
931: buffer))))
932:
933: (defun news-reply ()
934: "Compose and send a reply to the current article to the net.
935: While composing the reply, use \\[mail-yank-original] to yank the original message into it."
936: (interactive)
937: (if (y-or-n-p "Are you sure you want to reply to the net? ")
938: (let (from cc subject date to newsgroups
939: (buffer (current-buffer)))
940: (save-restriction
941: (narrow-to-region (point-min) (progn (search-forward "\n\n")
942: (- (point) 2)))
943: (setq from (mail-fetch-field "from")
944: subject (mail-fetch-field "subject")
945: date (mail-fetch-field "date")
946: newsgroups (mail-fetch-field "newsgroups"))
947: (pop-to-buffer "*post-news*")
948: (news-reply-mode)
949: (erase-buffer)
950: (news-setup
951: nil
952: subject
953: (let ((stop-pos (string-match " *at \\| *@ \\| *(\\| *<" from)))
954: (concat (if stop-pos (substring from 0 stop-pos) from)
955: "'s message of "
956: date))
957: newsgroups
958: buffer)))
959: (message "")))
960:
961: (defun news-post-news ()
962: "Begin editing a news article to be posted."
963: (interactive)
964: (pop-to-buffer "*post-news*")
965: (news-reply-mode)
966: (erase-buffer)
967: (news-setup () () () () ()))
968:
969: (defun news-add-news-group (gp)
970: "Add you to news group named GROUP (a string)."
971: ; (completing-read ...)
972: (interactive "sAdd news group: ")
973: (let ((file-dir (concat news-path (string-subst-char ?/ ?. gp))))
974: (save-excursion
975: (if (null (assoc gp news-group-article-assoc))
976: (let ((newsrcbuf (find-file-noselect
977: (substitute-in-file-name news-startup-file))))
978: (if (file-directory-p file-dir)
979: (progn
980: (switch-to-buffer newsrcbuf)
981: (end-of-buffer)
982: (insert (string-subst-char ?. ?\ gp) ": 1-1\n")
983: (save-buffer)
984: (kill-buffer (current-buffer))
985: (message "Added %s to your current list of newsgroups." gp))
986: (message "Newsgroup %s doesn't exist." gp)))
987: (message "Already subscribed to group %s." gp)))))
988:
989: (defun news-mail-other-window ()
990: "Send mail in another window.
991: While composing the message, use \\[mail-yank-original] to yank the
992: original message into it."
993: (interactive)
994: (mail-other-window nil nil nil nil nil (current-buffer)))
995:
996: (defun news-make-link-to-message (number newname)
997: "Forges a link to an rnews message numbered number (current if no arg)
998: Good for hanging on to a message that might or might not be
999: automatically deleted."
1000: (interactive "P
1001: FName to link to message: ")
1002: (add-name-to-file
1003: (concat news-path
1004: (string-subst-char ?/ ?. news-current-news-group)
1005: "/" (if number
1006: (prefix-numeric-value number)
1007: news-current-message-number))
1008: newname))
1009:
1010:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.