Date: 25 Apr 1983 10:29-PDT (Monday) From: Jim Rees Subject: rnews.ml Message-Id: <83/04/25 1029.783@uw-beaver> Emacs Readnews Distribution This is an mlisp package, rnews.ml, for reading the news in Gosling Emacs. It is similar to rmail.ml, and in fact depends on some of the functions in rmail.ml. Our rmail.ml is modified, but I think this will also work with the unmodified rmail.ml. This version is for use with news 2.10. There is a different version for use with news 2.9. To install rnews, install rnews.ml in your emacs/maclib directory. In your profile, put an autoload for rnews.ml: (autoload "rnews" "rnews.ml") Then enter emacs and do ESC-X-rnews. The rnews directory is kept in your ~/Messages directory, so if you don't use rmail you might have to make this directory before you can use rnews. There is an info page, written by grkermit!chris. To install it, change to your Emacs databases directory and run "dbadd info emacs:rnews" with the info page as standard input. To be complete you should change all the info nodes pointing to rnews, for example "up", "previous", and "next", but it will work if you don't change them. If you have any suggestions or criticisms, or want your name added to the distribution list for new releases and updates, send mail to emacs-rnews-request at uw-beaver via uucp or Arpanet. You can send mail to the list by mailing to emacs-rnews at uw-beaver. Here are the info page and rnews.ml: File: emacs Node: rnews Up: Packages Unix Emacs readnews facility. "rnews" is used for reading news. Executing it places your news directory into a window and enters a special command interpretation loop. The commands that it understands are: p move to the previous article. n move to the next article. f move forward in the current article. b and move backward in the current article. d delete the current article. (the one indicated by '>') u undelete the current article. D delete the article at the cursor. U undelete the article at the cursor. r reply to the current article. m enter smail, to send mail. a append the current article to a file. F Post a followup to the current article. P Post an article. q quit out of Rnews, saving undeleted articles. ? display the info page for rnews < go to the first article > go to the last article s asks for a number, skips down that number of articles g asks for a number, goes to the article that many lines from the top ` decrypts a net.jokes "rot 13" article H show the entire article header, including "junk" lines ^X-U unsubscribe to the current group : execute an emacs extended command and make the article at the cursor be the current article When in Post mode: ^Xd positions you in the Distribution: field of the message, creating it if necessary. While in rnews, the current article is indicated with an ">" on the left margin. You can move the cursor around with C-N, C-P, C-V, arrow keys, searches etc. If you move the cursor, and want to see an article far from the current article, or makes the article at the cursor current. If you provide an argument to the rnews command (usually with ^U) you will be put into rnews, but the directory will not be updated to include new articles. This is much faster and is useful, for example, if you just want to post a new article or re-read an old one. ------------------------------ Rnews.ml---------------------------------- (if (! (is-bound rmail-default-log)) (load "rmail.ml")) (message "Loading the news system, please wait...") (sit-for 0) ; Unix Emacs readnews facility. ; "rnews" is used for reading news. Executing it places your news ; directory into a window and enters a special command interpretation loop. ; The commands that it understands are: ; p move to the previous message. ; n move to the next message. ; f move forward in the current message. ; b move backward in the current message. ; d delete the current message. ; u undelete the last deleted message. ; D delete the article at the cursor. ; U undelete the article at the cursor. ; r reply to the current message. ; m enter smail, to send mail. ; a append the current message to a file. ; F Post a followup to the current message. ; P Post a message. ; q quit out of RMail, appending all undeleted messages to mbox. ; ? display the info page for rnews ; < go to the first article ; > go to the last article ; s asks for a number, skips down that number of articles ; g asks for a number, goes to the article that many lines from the top ; ` decrypts a net.jokes "rot 13" article ; H show the entire article header, including "junk" lines ; ^X-U unsubscribe to the current group ; : execute an emacs extended command ; and ; make the article at the cursor be the current article ; When in Post mode, ; ^Xd positions you in the Distribution: field of the message, ; creating it if necessary. ; If you want replies sent back along the path by which they arrived, ; change the 1 in (setq internet-replies 1) to 0. You will need to do ; this if your mail program doesn't do automatic uucp routing. ; "smail" is used for sending mail. It places you in a buffer for ; constructing the message and locally defines a few commands: ; ^X^S send the mail -- if all went well the window will disappear, ; otherwise a message indicating which addresses failed will appear ; at the bottom of the acreen. Unfortunatly, the way the mailers on ; Unix work, the message will have been sent to those addresses which ; succeded and not to the others, so you have to delete some ; addresses and fix up the others before you resend the message. ; ^Xt positions you in the To: field of the message. ; ^Xc positions you in the Cc: field of the message, creating it if it ; doesn't already exist. ; The abbrev facility is used for mail address expansion, ; the file /usr/local/lib/emacs/RMailAbbrevs should contain ; abbrev definitions to expand login names to their ; proper mail address. This gets used at CMU since we have ; 7 VAXen, 4 10's and countless 11's; remembering where a ; person usually logs in is nearly impossible. ; ^Xs positions you in the Subject: field of the message. ; ^Xa positions you to the end of the body of the message, ready to ; append more text. (defun (rnews nbx internet-replies this-from this-id this-length (setq internet-replies 1) (setq nbx (concat (getenv "HOME") "/Messages/Newsbox")) (message "Please wait while I read the news...") (sit-for 0) (save-window-excursion (pop-to-buffer "rnews-directory") (setq mode-line-format (concat " News from message file " (substr nbx 1 -1) " %M %[%p%]")) (setq mode-string "RNews") (erase-buffer) (set-mark) (if (! prefix-argument-provided) (filter-region (concat "readnews -e >> " nbx))) (read-file nbx) (end-of-file) (setq case-fold-search 0) (if (error-occured (re-search-reverse "^[>N ]")) (beginning-of-file) (next-line) ) (error-occured (re-replace-string "^" "N ")) (setq case-fold-search 1) (save-excursion (temp-use-buffer "Full Header") (setq needs-checkpointing 0) ) (rnews-position) (rnews-mark) (sit-for 0) (message "Type ^C to exit rnews; ? for help") (recursive-edit) (pop-to-buffer "rnews-directory") (rnews-erase-messages) (if buffer-is-modified (write-current-file)) (temp-use-buffer "current-message") (setq buffer-is-modified 0) ) (novalue) ) ) (defun (rnews-position (beginning-of-line) (if (! (looking-at "^>")) (progn (beginning-of-file) (if (error-occured (re-search-forward "^>")) (if (error-occured (re-search-forward "^N")) (progn (end-of-file) (previous-line) ) ) ) (beginning-of-line) ) ) ) ) (defun (rnews-pickup rnews-file (beginning-of-line) (save-excursion (provide-prefix-argument 2 (forward-character)) (set-mark) (search-forward " ") (backward-character) (copy-region-to-buffer "Scratch Stuff") (temp-use-buffer "Scratch Stuff") (setq needs-checkpointing 0) (beginning-of-file) (set-mark) (error-occured (replace-string "." "/")) (end-of-line) (setq rnews-file (region-to-string)) (pop-to-buffer "current-message") (setq needs-checkpointing 0) (setq this-from "") (setq this-length "") (setq this-id "") (if (error-occured (read-file (concat "/usr/spool/news/" rnews-file))) (progn (erase-buffer) (setq mode-line-format (concat "Expired article " rnews-file))) (progn (setq case-fold-search 1) (beginning-of-file) (if (error-occured (search-forward "\n\n")) (message "Garbled header") (rnews-fix-header)) (set-rnews-mode-line-format)) ) ) ) (rnews-fix-header (set-mark) (beginning-of-file) (copy-region-to-buffer "Full Header") (narrow-region) (convert-head "Posted:" "Date:") (convert-head "Title:" "Subject:") (convert-head "Article-I.D.:" "Message-ID:") (error-occured (re-search-forward "^From:[ \t]") (end-of-line) (set-mark) (re-search-reverse "[!:@]") (re-search-reverse "[!: ]") (forward-character) (setq this-from (region-to-string)) (beginning-of-file) ) (error-occured (re-search-forward "^Lines:[ \t]*\\(.*\\)") (region-around-match 1) (setq this-length (region-to-string)) (beginning-of-file) (re-replace-string "^Lines:.*\n" "") (beginning-of-file) ) (error-occured (re-search-forward "^Message-ID:[ \t]*\\(.*\\)") (region-around-match 1) (setq this-id (region-to-string)) (beginning-of-file) (re-replace-string "^Message-ID:.*\n" "") (beginning-of-file) ) (error-occured (re-replace-string "^.*-version:.*\n" "")) (error-occured (re-replace-string "^Path:.*\n" "")) (error-occured (re-replace-string "^Sender:.*\n" "")) (error-occured (re-replace-string "^.*received:.*\n" "")) (widen-region) (if (= (length this-length) 0) ; The constant 38 comes from the average ; line length of 356 articles in net.* (setq this-length (concat "~" (/ (buffer-size) 38)))) ) (convert-head re1 re2 (setq re1 (concat "^" (arg 1))) (setq re2 (concat "^" (arg 2))) (if (error-occured (re-search-forward re2)) (error-occured (re-replace-string re1 (arg 2))) (beginning-of-file)) (error-occured (re-replace-string (concat re1 ".*\n") "")) ) (rnews-show-full-header (save-window-excursion (temp-use-buffer "current-message") (beginning-of-file) (set-mark) (error-occured (search-forward "\n\n") (erase-region) (yank-buffer "Full Header") ) ) ) (rnews-next-page (save-excursion (pop-to-buffer "current-message") (next-page) (set-rnews-mode-line-format) ) ) (rnews-previous-page (save-excursion (pop-to-buffer "current-message") (previous-page) (set-rnews-mode-line-format) ) ) (set-rnews-mode-line-format from (save-excursion (temp-use-buffer "Scratch Stuff") (erase-buffer) (insert-string this-from) (set-mark) (beginning-of-line) (error-occured (replace-string "%" "%%")) (setq from (region-to-string)) (use-old-buffer "current-message") (end-of-file) (setq mode-line-format (concat "From: " from (if (!= (length this-length) 0) (concat " Lines: " this-length) "") " %[%p%]" (if (dot-is-visible) "" " --More--") ))) ) ) (defun (rnews-erase-messages (save-excursion (pop-to-buffer "rnews-directory") (beginning-of-file) (error-occured (while 1 (re-search-forward "^.D") (beginning-of-line) (set-mark) (end-of-line) (forward-character) (erase-region) ) ) ) ) ) (defun (rnews-com (argc) (rnews) (exit-emacs) ) (rnews-next-message (rnews-position) (delete-next-character) (insert-character ' ') (beginning-of-line) (next-line) (if (eobp) (progn (previous-line) (message "You're at the last message already"))) (delete-next-character) (insert-character '>') (rnews-pickup) ) (rnews-previous-message (rnews-position) (delete-next-character) (insert-character ' ') (previous-line) (beginning-of-line) (delete-next-character) (insert-character '>') (rnews-pickup) ) (rnews-delete-message (rnews-position) (forward-character) (delete-next-character) (insert-character 'D') (beginning-of-line) ) (rnews-undelete-message (rnews-position) (forward-character) (delete-next-character) (insert-character ' ') (beginning-of-line) ) (rnews-delete-message-at-cursor (beginning-of-line) (forward-character) (delete-next-character) (insert-character 'D') (beginning-of-line) ) (rnews-undelete-message-at-cursor (beginning-of-line) (forward-character) (delete-next-character) (insert-character ' ') (beginning-of-line) ) (rnews-goto-message n (setq n (get-tty-string "Goto message number: ")) (rnews-unmark) (beginning-of-file) (provide-prefix-argument n (next-line)) (rnews-mark) ) ) (defun (rnews-help (&info "emacs" "rnews"))) (defun (rnews-reply subject dest excess refs (setq subject "") (setq dest "") (setq excess "") (save-window-excursion (pop-to-buffer "current-message") (setq case-fold-search 1) (beginning-of-file) (search-forward "\n\n") (set-mark) (beginning-of-file) (narrow-region) (error-occured (re-search-forward "^Subject:[ \t]*\\(.*\\)") (region-around-match 1) (setq subject (region-to-string)) (if (!= (substr subject 1 3) "Re:") (setq subject (concat "Re: " subject)) ) ) (save-excursion (temp-use-buffer "Full Header") (beginning-of-file) (if internet-replies (if (error-occured (re-search-forward "^Reply-To:[ \t]*\\(.*\\)")) (setq dest this-from) (progn (region-around-match 1) (setq dest (region-to-string))) ) (progn (if (error-occured (re-search-forward "^Path:[ \t]*[^ \t!]*!\\(.*\\)")) (re-search-forward "^From:[ \t]*[^ \t!]*!\\(.*\\)")) (region-around-match 1) (setq dest (region-to-string)))) ) (beginning-of-file) (error-occured edest (save-excursion (temp-use-buffer "Scratch Stuff") (setq needs-checkpointing 0) (erase-buffer) (insert-string dest) (set-mark) (beginning-of-file) (if (! (error-occured (re-search-forward " (\\(.*\\))"))) (progn (region-around-match 1) (setq dest (region-to-string)) (beginning-of-file) (insert-string (concat dest " <")) (re-replace-string " *(.*" ">") (end-of-line) (set-mark) (beginning-of-line) (setq dest (region-to-string)) ) ) (error-occured (re-replace-string " *at *[^,\n]*\\| *@ *[^,\n]*\\| *([^)\n]*)\\| *<[^>\n]*>" "")) (error-occured (re-replace-string ".*!" "")) (setq edest (region-to-string)) ) (error-occured (re-search-forward "^Date:[ \t]*\\(.*\\)") (region-around-match 1) ) (setq excess (concat "In-Reply-To: " edest "'s message of " (region-to-string) "\n")) (beginning-of-file) (if (error-occured (re-search-forward "^References:[ \t]*\\(.*\\)") (region-around-match 1) (setq refs (concat (region-to-string) " " this-id))) (setq refs this-id)) ) (widen-region) (pop-to-buffer "send-mail") (setq needs-checkpointing 0) (setq case-fold-search 1) (erase-buffer) (insert-string subject) (newline) (insert-string dest) (newline) (insert-string excess) (insert-string (concat "References: " refs "\n")) (do-mail-setup) ) (rnews-position) (if (looking-at "^>") (progn (forward-character) (delete-next-character) (insert-character 'A') (beginning-of-line))) ) ) (defun (rnews-followup newsgroups subject refs (setq newsgroups "") (setq subject "") (setq refs "") (save-window-excursion (pop-to-buffer "current-message") (beginning-of-file) (search-forward "\n\n") (set-mark) (beginning-of-file) (narrow-region) (error-occured (re-search-forward "^Newsgroups:[ \t]*\\(.*\\)") (region-around-match 1) (setq newsgroups (region-to-string)) ) (save-excursion (temp-use-buffer "Scratch Stuff") (setq needs-checkpointing 0) (erase-buffer) (insert-string newsgroups) (beginning-of-file) (error-occured (replace-string "general" "followup")) (set-mark) (end-of-file) (setq newsgroups (region-to-string)) ) (beginning-of-file) (error-occured (re-search-forward "^Subject:[ \t]*\\(.*\\)") (region-around-match 1) (setq subject (region-to-string)) (if (!= (substr subject 1 3) "Re:") (setq subject (concat "Re: " subject)) ) ) (beginning-of-file) (if (error-occured (re-search-forward "^References:[ \t]*\\(.*\\)") (region-around-match 1) (setq refs (concat (region-to-string) " " this-id))) (setq refs this-id)) (beginning-of-file) (widen-region) (pop-to-buffer "send-mail") (setq needs-checkpointing 0) (setq case-fold-search 1) (erase-buffer) (insert-string (concat "Newsgroups: " newsgroups)) (newline) (insert-string (concat "Subject: " subject)) (newline) (insert-string (concat "References: " refs)) (newline) (newline) (rnews-do-post) ) (rnews-position) (if (looking-at "^>") (progn (forward-character) (delete-next-character) (insert-character 'F') (beginning-of-line))) ) (rnews-post (save-window-excursion (pop-to-buffer "send-mail") (setq needs-checkpointing 0) (setq case-fold-search 1) (erase-buffer) (insert-string "Newsgroups: \nSubject: \n\n") (beginning-of-file) (end-of-line) (rnews-do-post) ) ) (rnews-do-post rnews-do-send (setq rnews-do-send 1) (setq right-margin 72) (local-bind-to-key "exit-emacs" "\^X\^S") (local-bind-to-key "exit-emacs" "\^X\^F") (local-bind-to-key "rnews-abort-send" "\^X\^A") (local-bind-to-key "justify-paragraph" "\ej") (local-bind-to-key "rnews-post-goto-dist" "\^Xd") (while (= rnews-do-send 1) (progn (recursive-edit) (if (= rnews-do-send 1) (rnews-call-inews)))) ) (rnews-post-goto-dist ; Move to the "Distribution:" field (beginning-of-file) (if (error-occured (re-search-forward "^Distribution:.*")) (progn (re-search-forward "\n\n\\|^Subject:.*\n.") (backward-character) (backward-character) (insert-string "\nDistribution: "))) ) (rnews-call-inews inews-errors (save-excursion (beginning-of-file) (set-mark) (end-of-file) (copy-region-to-buffer "Delivery-errors") ) (message "Sending...") (sit-for 0) (save-window-excursion (temp-use-buffer "Delivery-errors") (setq needs-checkpointing 0) (beginning-of-file) (set-mark) (end-of-file) (filter-region "inews -h") (beginning-of-file) (set-mark) (error-occured (re-replace-string "\n\n* *" "; ")) (end-of-line) (setq inews-errors (region-to-string)) (if (= (length inews-errors) 0) (progn (setq rnews-do-send 0) (message "Posted") ) (message inews-errors) ) ) ) (rnews-abort-send (if (!= "y" (substr (get-tty-string "Do you really want to abort the message? ") 1 1)) (error-message "Turkey!")) (setq rnews-do-send 0) (exit-emacs) ) ) (defun (rnews-unmark (error-occured (rnews-position) (delete-next-character) (insert-character ' ') (beginning-of-line))) (rnews-mark (beginning-of-line) (if (error-occured (if (eobp) (re-search-reverse "^.") (progn (re-search-forward "^.") (beginning-of-line))) ) (message "No messages") (progn (delete-next-character) (insert-character '>') (rnews-pickup)) ) ) (rnews-first-message (rnews-unmark) (beginning-of-file) (rnews-mark) ) (rnews-last-message (rnews-unmark) (end-of-file) (rnews-mark) ) (rnews-skip n (setq n (get-tty-string "Skip messages: ")) (rnews-unmark) (provide-prefix-argument n (next-line)) (rnews-mark) ) (rnews-this-message save-dot (setq save-dot (dot)) (rnews-unmark) (goto-character save-dot) (rnews-mark) ) (rnews-decrypt-joke (save-window-excursion (temp-use-buffer "current-message") (beginning-of-file) (search-forward "\n\n") (set-mark) (end-of-file) (filter-region "tr a-zA-Z n-za-mN-ZA-M") ) ) (rnews-unsubscribe group newsrc (beginning-of-line) (save-excursion (provide-prefix-argument 2 (forward-character)) (set-mark) (search-forward " ") (search-reverse "/") (setq group (region-to-string)) (temp-use-buffer ".newsrc") (if (error-occured (setq newsrc (getenv "NEWSRC"))) (setq newsrc (concat (getenv "HOME") "/.newsrc"))) (read-file newsrc) (if (error-occured (search-forward (concat group ":"))) (message "Already unsubscribed") (progn (delete-previous-character) (insert-character '!') (write-current-file) (message (concat "Unsubscribed from " group)))) ) ) ) (save-excursion i (temp-use-buffer "rnews-directory") (setq i ' ') (while (< i 128) (local-bind-to-key "illegal-operation" i) (setq i (+ i 1))) (local-bind-to-key "rnews-next-page" 'f') (local-bind-to-key "rnews-previous-page" 'b') (local-bind-to-key "rnews-previous-page" '^H') (local-bind-to-key "rnews-next-message" 'n') (local-bind-to-key "rnews-previous-message" 'p') (local-bind-to-key "rnews-delete-message" 'd') (local-bind-to-key "rnews-undelete-message" 'u') (local-bind-to-key "rnews-delete-message-at-cursor" 'D') (local-bind-to-key "rnews-undelete-message-at-cursor" 'U') (local-bind-to-key "rnews-help" '?') (local-bind-to-key "exit-emacs" 'q') (local-bind-to-key "rnews-reply" 'r') (local-bind-to-key "rnews-followup" 'F') (local-bind-to-key "rnews-post" 'P') (local-bind-to-key "smail" 'm') (local-bind-to-key "rnews-goto-message" 'g') (local-bind-to-key "rnews-first-message" '<') (local-bind-to-key "rnews-last-message" '>') (local-bind-to-key "rnews-skip" 's') (local-bind-to-key "rmail-append" 'a') (local-bind-to-key "rmail-shell" '!') (local-bind-to-key "execute-extended-command" ':') (local-bind-to-key "rnews-this-message" ' ') (local-bind-to-key "rnews-this-message" '\r') (local-bind-to-key "rnews-decrypt-joke" '`') (local-bind-to-key "rnews-show-full-header" 'H') (local-bind-to-key "rnews-unsubscribe" "\^Xu") )