weemba@garnet.berkeley.edu (Matthew P Wiener) (04/01/88)
I'll be gone for about 2 months this trip east; and then I should be
here all summer long.
----------------------------------------------------------------------
I'd appreciate getting e-mail about experiences with Gnews: everything
from why you find it the greatest thing since sliced bread to how it's
been completely incomprehensible and a waste of your time; comments on
the documentation, installation, ELisp code, etc are appreciated also.
----------------------------------------------------------------------
Hal Peterson of CRI has gotten the gnews-spool code to work under Gnews
1.3! I enclose the contents of their site's gnews-init.el file; by
this I mean that this assumes (autoload 'gnews "gnews-init" nil t) is
what users put in their .emacs (along with any load-path fixing that
needs to be done). Two of the changes, in article-get-slow and arti-
cle-get-msg-id, should be installed by everyone, although this is minor.
----------------------------------------------------------------------
On the down side of the above, I must retract my claim earlier that FSF
is adopting Gnews as soon as the spool code is working; I misunderstood
e-mail that only said that working spool code was a necessary prerequi-
site. It's not clear that FSF will adopt Gnews; I plan to keep support-
ing Gnews as long as I can.
----------------------------------------------------------------------
One strange bug, which no one off of garnet has reported to me, is that
Gnews sometimes returns to the *gnews*nntp*index* buffer on exit. I do
not understand what causes this, except that the gnews-buffer-return
variable gets mis-set on Gnews start up. I replaced the setq of g-b-r
to (buffer-name) with a setq to (buffer-name (car (buffer-list))), and
the problem went away. I suspect that I've run into an obscure Emacs
bug; this behavior is incomprehensible.
----------------------------------------------------------------------
A bug was noticed by Hal Peterson that effects people who had an options
line in their .newsrc. The corrected gnews-hook-write is below.
----------------------------------------------------------------------
When I return, finishing the infotex manual and rewriting innards from
the GC-minimization angle are the next goals.
----------------------------------------------------------------------
ucbvax!garnet!weemba Matthew P Wiener/Brahms Gang/Berkeley CA 94720
----------------------------------------------------------------------
Here's the spool code that worked with 1.3:
;; local (Cray) initialization for running gnews on the local machine.
(load "gnews" nil t)
(load-library "gnewspool")
(setq gnews-start-hook 'gnews:start:hook)
(defun gnews:start:hook ()
(fset 'nntp-start 'gnews-spool-start)
(fset 'nntp-exec 'gnews-spool-exec)
(fset 'article-get 'article-get-slow)
(fset 'nntp-index-start '(lambda () t))
(fset 'nntp-run-p '(lambda () t))
(setq nntp-index-fast nil) )
;; Some fixes from weemba. 30 Mar 88.
(defun article-get-msg-id (msg-id)
"Display article with Message-ID MSG-ID. The enclosing angle brackets
are optional."
(interactive "smessage-id: ")
(if (string= (substring msg-id 0 1) "<") nil
(setq msg-id (concat "<" msg-id)))
(if (string= (substring msg-id -1) ">") nil
(setq msg-id (concat msg-id ">")))
(if (nntp-exec t t "article" msg-id)
(let (lines)
(if (< 0 article-current)
(setq article-message-id msg-id
article-trace article-current
article-current 0)) ; NNTP can't return #/gp
(set-buffer nntp-buffer)
(article-header-clean t)
(setq lines (article-effective-init-display))
(if lines (forward-line lines))
(setq article-grab-point (if lines (point)))
(if (cdr article-field-list)
(article-display-init t)
(message "Message-ID %s: no such article" msg-id (ding))))
(message "Message-ID %s: no such article" msg-id (ding)))
(gnews-flush))
(defun article-get-slow (number &optional hook interact)
"Display article NUMBER of the current newsgroup.\n
In ELisp code, optional argument HOOK is a list of per-hooks to apply, and
non-nil INTERACT means to pretend this function was called interactively.\n
\(This is the original version of article-get. The group-pattern-* functions
fail mysteriously on the new version. I don't understand why.\)"
(interactive "narticle #: ")
(if (< article-final number)
(group-last)
(if (nntp-exec t t "article" number)
(let ((b (current-buffer))
lines)
(set-buffer nntp-buffer)
(if (or (interactive-p) interact)
(article-current-set number))
(article-header-clean t)
(setq lines (article-effective-init-display))
(let ((article-current number)
(gnews-hook-continue t))
(while (and hook gnews-hook-continue)
(if (gnews-hook-do (car hook) t)
(progn
(gnews-hook-junk-message number hook)
(set-buffer b) ; I have to doooo this?
(throw 'article-nil t))) ; article KILLed; try again
(setq hook (cdr hook))))
(if lines (forward-line lines))
(setq article-grab-point (if lines (point)))
(article-display-init t)
(if (setq article-junkable (article-done)) (article-quit)))
(throw 'article-nil t))) ; article not found--give it up
(gnews-flush))
;; My fixes to gnewspool.el. hrp, 31 March 1988.
(defun gnews-spool-regexp (msg-id)
"Return the regexp that matches MSG-ID in the history file, and also
brackets off the newsgroup and article number as match #1 and match #2."
(concat "^" (regexp-quote msg-id) ; Message-ID
"[ \t]../../..[ \t]..:..[ \t]" ; date/time. If this doesn't work,
; add a + to the [ \t] expressions(?)
"\\([^/]*\\)/\\([0-9]*\\) ")) ; group/###
(defun gnews-spool-start ()
"Initialize gnews-spool buffers."
(setq gnews-spool-active (find-file-noselect gnews-spool-active-file)
gnews-spool-history (find-file-noselect gnews-spool-history-file))
(gnews-spool-info (if n-reply-allowed "200 " "201 ") news-path))
(defun gnews-spool-exec (clear finish comm &rest args)
"NNTP commands interpreted directly off a news spool."
(interactive (list (not current-prefix-arg)
(read-from-minibuffer "NNTP command: ")
nil))
(if (interactive-p)
(setq args (progn
(string-match "\\<[^ ]*\\>" comm)
(if (/= (length comm) (match-end 0))
(list (substring comm (1+ (match-end 0))))))
comm (substring comm (match-beginning 0) (match-end 0)))
(if (stringp clear) (error "Uh, you forgot the clear flag, eh?")))
(if clear (nntp-clear nntp-buffer))
(let* ((b (current-buffer))
(a1 (car args))
(a2 (cadr args))
(art (or a1 article-current)))
(prog2
(set-buffer nntp-buffer)
(cond ((string= comm "group")
(gnews-spool-exec-group a1))
((string= comm "article")
(gnews-spool-exec-art art 'art))
((string= comm "head")
(gnews-spool-exec-art art 'head))
((string= comm "body")
(gnews-spool-exec-art art 'body)
(goto-char (point-min))
(insert nntp-info "\n"))
((string= comm "stat")
(gnews-spool-exec-art art 'stat))
((string= comm "next")
(gnews-spool-exec-motion t))
((string= comm "last")
(gnews-spool-exec-motion nil))
((string= comm "list")
(gnews-spool-exec-list))
((string= comm "newgroups")
(gnews-spool-exec-newgroups a1 a2))
((string= comm "help")
(gnews-spool-exec-help))
((string= comm "quit")
(gnews-spool-exec-quit)))
(setq nntp-eot 'spool)
(set-buffer b))))
(defun gnews-spool-exec-group (gp)
"Fake an NNTP group command."
(let ((dir (gnews-spool-dir gp)) c f l)
(if (and (file-readable-p dir)
(car (file-attributes dir)))
(gnews-string-as-buffer "" nil
(call-process "ls" nil t nil dir)
(goto-char 1)
(insert "(setq gnews-spool-group-list (gnews-spool-preen '(")
(goto-char (point-max))
(insert ")))")
(eval-current-buffer)
(if (null gnews-spool-group-list)
(gnews-spool-info "211 0 0 0" gp) ; nothing there
(sort gnews-spool-group-list '<)
(setq gnews-spool-group-tsil (reverse gnews-spool-group-list))
(setq c (length gnews-spool-group-list))
(setq f (car gnews-spool-group-list))
(setq l (car gnews-spool-group-tsil))
(gnews-spool-info "211" c f l gp)
t))
(gnews-spool-info "411 Invalid group name.")
nil)))
(defun gnews-spool-preen (grouplist)
"Remove all subgroups from GROUPLIST, a list of articles in a group."
(let ((preened-list nil))
(while (not (null grouplist))
(if (integerp (car grouplist))
(setq preened-list (append preened-list
(list (car grouplist)))))
(setq grouplist (cdr grouplist)))
preened-list))
(defun gnews-spool-exec-art (art-id part)
"Fake an NNTP article/head/body/stat command."
(let (file msg-id (art-no (if (integerp art-id)
(int-to-string art-id)
art-id)))
(if (and (cond ((string-match "^[0-9]+$" art-no)
(setq file (gnews-spool-art group-current art-no))
(if (let ((attributes (file-attributes file)))
(and attributes
(> (nth 7 attributes) 0)))
(if (memq part '(body stat))
;; Set the Message-ID by hand
(setq msg-id (gnews-string-as-buffer "" nil
(call-process "sed" file t t
"/^Message-ID:/q")
(forward-line -1)
(forward-char 12)
(buffer-substring
(point) (gnews-eol))))
t)))
((string-match "^<.*>$" art-no)
(setq msg-id art-no art-no "0")
(set-buffer gnews-spool-history)
(setq file (if (re-search-forward
(gnews-spool-regexp msg-id) nil t)
(gnews-spool-art
(buffer-substring
(match-beginning 1) (match-end 1))
(buffer-substring
(match-beginning 2) (match-end 2)))
"/meese/sucks/raw/eggs/film/at/11"))
(set-buffer nntp-buffer)))
(file-readable-p file))
(progn
(cond ((eq part 'art)
(insert-file file))
((eq part 'head)
(call-process "sed" file t t "/^$/q")
(goto-char (point-max))
(delete-char -1))
((eq part 'body)
(call-process "sed" file t t "1,/^$/d")))
(gnews-spool-info (cond ((eq part 'art) "220")
((eq part 'head) "221")
((eq part 'body) "222")
((eq part 'stat) "223"))
art-no
msg-id
"Article retrieved;"
(cond ((eq part 'art) "head and body follow.")
((eq part 'head) "head follows.")
((eq part 'stat) "request text separately.")
((eq part 'body) "body follows.")))
t)
(gnews-spool-info "423 Invalid article number:" art)
nil)))
(defun gnews-spool-exec-newgroups (ymd hms &optional gmt)
"Fake an NNTP newgroups command. So what if we're off half a day?"
;; fake it. There is no groupdates file.
t)
;; fixes to gnewsutils.el. 31 Mar 88, hrp.
(defun gnews-hook-write (file)
"Write out gnews-hook-list and gnews-abbrev to FILE."
(setq gnews-hook-dangle)
(gnews-string-as-buffer "" nil
(insert "(setq\t\t\t\t; -*- Emacs-Lisp -*-\n gnews-hook-list\n '(nil")
(mapcar '(lambda (g)
(let ((gh (cdr (assoc g gnews-hook-list))))
(if (car gh)
(progn
(insert "\n (" (prin1-to-string g))
(mapcar '(lambda (x)
(insert "\n " (prin1-to-string x)))
gh)
(insert "\n )")))))
(mapcar 'car (cdr gnews-hook-list)))
(insert "\n ))\n\f\n(setq\n gnews-abbrev\n '(")
(mapcar '(lambda (x) (insert "\n " (prin1-to-string x))) gnews-abbrev)
(insert "\n ))\n")
(if (boundp 'gnews-rn-options)
(insert "\f\n(setq gnews-rn-options \"" gnews-rn-options "\")\n"))
(write-region 1 (point-max) file nil 0)))
(defun gnews-load (file &optional mok nom nos)
"Safely load FILE, with optional arguments as in load, which see.
On error, abort NNTP and throw the user into FILE. [[On quit, abort
NNTP and get out of Gnews.]]"
(condition-case q
(load file mok nom nos)
(error (if nntp (delete-process nntp))
(bury-buffer news-buffer)
(find-file file)
(signal (car q) (cdr q)))
;;; (quit (nntp-exec t t "quit") ; doesn't work?
;;; (bury-buffer news-buffer)) ; so why not?
))