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? ))