Path: utzoo!attcan!uunet!kddlab!titcca!fgw!flab!umerin From: umerin@flab.flab.fujitsu.JUNET (Masanobu UMEDA) Newsgroups: comp.emacs,fj.editor.emacs Subject: GNUS: nntp-based news reader (again) (3 of 3) Message-ID: <3682@flab.flab.fujitsu.JUNET> Date: 16 Jun 88 05:52:42 GMT References: <3680@flab.flab.fujitsu.JUNET> <3681@flab.flab.fujitsu.JUNET> Reply-To: umerin@flab.flab.fujitsu.JUNET (Masanobu UMEDA) Organization: Fujitsu Laboratories Ltd., Kawasaki, Japan Lines: 713 ---- Cut Here and unpack ---- #!/bin/sh # this is part 3 of a multipart archive # do not concatenate these parts, unpack them in order with /bin/sh # file nntp.el continued # CurArch=3 if test ! -r s2_seq_.tmp then echo "Please unpack part 1 first!" exit 1; fi ( read Scheck if test "$Scheck" != $CurArch then echo "Please unpack part $Scheck next!" exit 1; else exit 0; fi ) < s2_seq_.tmp || exit 1 echo "x - Continuing file nntp.el" sed 's/^X//' << 'SHAR_EOF' >> nntp.el X (goto-char (point-min)) X (delete-non-matching-lines X "^Subject:[ \t]\\|^Xref:[ \t]\\|^From:[ \t]\\|^Lines:[ \t]\\|^[23]") X (if (> number 100) X (message "NNTP: Parsing headers...")) X ;; Then examines replies. X (while (not (eobp)) X (cond ((looking-at "^[23].*[ \t]+\\([0-9]+\\)[ \t]+") ;Article exists. X (setq article (string-to-int X (buffer-substring (match-beginning 1) X (match-end 1)))) X (forward-line 1) X (setq subject nil) X (setq xref nil) X (setq from nil) X (setq lines 0) X ;; It is better to extract From:, Subject:, Lines: and X ;; Xref: field values in this order. X (while (looking-at "^[^23]") X (if (looking-at "^From:[ \t]\\(.*\\)\r$") X (progn X (setq from (buffer-substring (match-beginning 1) X (match-end 1))) X (forward-line 1))) X (if (looking-at "^Subject:[ \t]\\(.*\\)\r$") X (progn X (setq subject (buffer-substring (match-beginning 1) X (match-end 1))) X (forward-line 1))) X (if (looking-at "^Lines:[ \t]\\(.*\\)\r$") X (progn X (setq lines (string-to-int X (buffer-substring (match-beginning 1) X (match-end 1)))) X (forward-line 1))) X (if (looking-at "^Xref:[ \t]\\(.*\\)\r$") X (progn X (setq xref (buffer-substring (match-beginning 1) X (match-end 1))) X (forward-line 1))) X ) X (if (null subject) X (setq subject "(None)")) X (if (null from) X (setq from "Unknown User")) X (setq headers X (cons (list article subject from xref lines) headers)) X ) X (t X (error "NNTP: Receive error on line: %s" X (buffer-substring X (point) X (save-excursion (end-of-line) (point)))) X ))) X (nreverse headers) X ))) X X X;;; X;;; Raw Interface to Network News Transfer Protocol (RFC977). X;;; X X(defun nntp-open-server (host &optional service) X "Open news server on HOST. XIf HOST is nil, use value of environment variable `NNTPSERVER'. XIf optional argument SERVICE is non-nil, open by the service name." X (let ((host (or host X (getenv "NNTPSERVER") X (error "NNTP: no server host is specified.")))) X (if (nntp-open-server-internal host service) X (progn X ;; Do check unexpected close of connection. X ;; Suggested by feldmark@hanako.stars.flab.fujitsu.junet. X (set-process-sentinel nntp-server-process 'nntp-default-sentinel) X (nntp-wait-for-response "^[23].*\r$") X )) X )) X X(defun nntp-close-server () X "Close news server." X (unwind-protect X (progn X ;; Un-set default sentinel function before closing connection. X (if (eq 'nntp-default-sentinel X (process-sentinel nntp-server-process)) X (set-process-sentinel nntp-server-process nil)) X ;; We cannot send QUIT command unless the process is running. X (if (nntp-server-active-p) X (nntp-send-command nil "QUIT")) X ) X (nntp-close-server-internal) X )) X X(fset 'nntp-request-quit (symbol-function 'nntp-close-server)) X X(defun nntp-server-active-p () X "Return server process status, T or NIL. XIf the stream is opened, return T, otherwise return NIL." X (and nntp-server-process X (eq (process-status nntp-server-process) 'open))) X X(defun nntp-status-message () X "Return server status response as string." X (if (and nntp-status-message-string X ;; NNN MESSAGE X (string-match "[0-9][0-9][0-9][ \t]+\\([^\r]*\\).*$" X nntp-status-message-string)) X (substring nntp-status-message-string (match-beginning 1) (match-end 1)) X ;; Empty message if nothing. X "" X )) X X(defun nntp-request-article (id) X "Select article by message ID (or number)." X (prog1 X (nntp-send-command "^\\.\r$" "ARTICLE" id) X (nntp-decode-text) X )) X X(defun nntp-request-body (id) X "Select article body by message ID (or number)." X (prog1 X (nntp-send-command "^\\.\r$" "BODY" id) X (nntp-decode-text) X )) X X(defun nntp-request-head (id) X "Select article head by message ID (or number)." X (prog1 X (nntp-send-command "^\\.\r$" "HEAD" id) X (nntp-decode-text) X )) X X(defun nntp-request-stat (id) X "Select article by message ID (or number)." X (nntp-send-command "^[23].*\r$" "STAT" id)) X X(defun nntp-request-group (group) X "Select news GROUP." X ;; 1.2a NNTP's group command is buggy. "^M" (\r) is not appended to X ;; end of the status message. X (nntp-send-command "^[23].*$" "GROUP" group)) X X(defun nntp-request-list () X "List valid newsgoups." X (prog1 X (nntp-send-command "^\\.\r$" "LIST") X (nntp-decode-text) X )) X X(defun nntp-request-last () X "Set current article pointer to the previous article Xin the current news group." X (nntp-send-command "^[23].*\r$" "LAST")) X X(defun nntp-request-next () X "Advance current article pointer." X (nntp-send-command "^[23].*\r$" "NEXT")) X X(defun nntp-request-post () X "Post a new news in current buffer." X (if (nntp-send-command "^[23].*\r$" "POST") X (progn X (nntp-encode-text) X (nntp-send-region-to-server (point-min) (point-max)) X ;; 1.2a NNTP's post command is buggy. "^M" (\r) is not X ;; appended to end of the status message. X (nntp-wait-for-response "^[23].*$") X ))) X X(defun nntp-default-sentinel (proc status) X "Default sentinel function for NNTP server process." X (if (and nntp-server-process X (not (nntp-server-active-p))) X (error "NNTP: Connection closed.") X )) X X;; Encoding and decoding of NNTP text. X X(defun nntp-decode-text () X "Decode text transmitted by NNTP. X0. Delete status line. X1. Delete `^M' at end of line. X2. Delete `.' at end of buffer (end of text mark). X3. Delete `.' at beginning of line." X (save-excursion X (set-buffer nntp-server-buffer) X ;; Insert newline at end of buffer. X (goto-char (point-max)) X (if (not (bolp)) X (insert "\n")) X ;; Delete status line. X (goto-char (point-min)) X (kill-line 1) X ;; Delete `^M' at end of line. X ;; (replace-regexp "\r$" "") X (while (not (eobp)) X (end-of-line) X (forward-char -1) X (if (looking-at "\r$") X (delete-char 1)) X (forward-line 1) X ) X ;; Delete `.' at end of buffer (end of text mark). X (goto-char (point-max)) X (forward-line -1) X (beginning-of-line) X (if (looking-at "^\\.$") X (kill-line 1)) X ;; Replace `..' at beginning of line with `.'. X (goto-char (point-min)) X ;; (replace-regexp "^\\.\\." ".") X (while (not (eobp)) X (if (looking-at "^\\.\\.") X (delete-char 1)) X (forward-line 1) X (beginning-of-line)) X )) X X(defun nntp-encode-text () X "Encode text in current buffer for NNTP transmission. X1. Insert `.' at beginning of line. X2. Insert `.' at end of buffer (end of text mark)." X (save-excursion X ;; Insert newline at end of buffer. X (goto-char (point-max)) X (if (not (bolp)) X (insert "\n")) X ;; Replace `.' ad beginning of line with `..'. X (goto-char (point-min)) X ;; (replace-regexp "^\\." "..") X (while (not (eobp)) X (if (looking-at "^\\.") X (insert ".")) X (forward-line 1) X (beginning-of-line)) X ;; Insert `.' at end of buffer (end of text mark). X (goto-char (point-max)) X (insert ".\n") X )) X X X;;; X;;; Synchronous Communication with NNTP Server. X;;; X X(defun nntp-send-command (response cmd &rest args) X "Wailt for server RESPONSE after sending CMD and optional ARGS to Xnews server." X (save-excursion X ;; Clear communication buffer. X (set-buffer nntp-server-buffer) X (erase-buffer) X (apply 'nntp-send-strings-to-server cmd args) X (if response X (nntp-wait-for-response response) X t) X )) X X(defun nntp-wait-for-response (regexp) X "Wait for server response which matches REGEXP." X (save-excursion X (let ((status t) X (wait t)) X (set-buffer nntp-server-buffer) X ;; Wait for status response (RFC977). X ;; 1xx - Informative message. X ;; 2xx - Command ok. X ;; 3xx - Command ok so far, send the rest of it. X ;; 4xx - Command was correct, but couldn't be performed for some X ;; reason. X ;; 5xx - Command unimplemented, or incorrect, or a serious X ;; program error occurred. X (nntp-accept-response) X (while wait X (goto-char (point-min)) X (cond ((looking-at "[23]") X (setq wait nil)) X ((looking-at "[45]") X (setq status nil) X (setq wait nil)) X (t (nntp-accept-response)) X )) X ;; Save status message. X (end-of-line) X (setq nntp-status-message-string X (buffer-substring (point-min) (point))) X (if status X (progn X (setq wait t) X (while wait X (goto-char (point-max)) X (forward-line -1) X (beginning-of-line) X ;;(message (buffer-substring X ;; (point) X ;; (save-excursion (end-of-line) (point)))) X (if (looking-at regexp) X (setq wait nil) X (message "NNTP: Reading...") X (nntp-accept-response) X (message "") X )) X ;; Successfully received server response. X t X )) X ))) X X X;;; X;;; Low-Level Interface to NNTP Server. X;;; X X(defun nntp-send-strings-to-server (&rest strings) X "Send list of STRINGS to news server as command and its arguments." X (let ((cmd (car strings)) X (strings (cdr strings))) X ;; Command and each argument must be separeted by one or more spaces. X (while strings X (setq cmd (concat cmd " " (car strings))) X (setq strings (cdr strings))) X ;; Command line must be terminated by a CR-LF. X (process-send-string nntp-server-process (concat cmd "\n")) X )) X X(defun nntp-send-region-to-server (begin end) X "Send current buffer region (from BEGIN to END) to news server." X (save-excursion X ;; We have to work in the buffer associated with NNTP server X ;; process because of NEmacs hack. X (copy-to-buffer nntp-server-buffer begin end) X (set-buffer nntp-server-buffer) X (setq begin (point-min)) X (setq end (point-max)) X ;; `process-send-region' does not work if text to be sent is very X ;; large. I don't know maximum size of text sent correctly. X (let ((last nil) X (size 100)) ;Size of text sent at once. X (save-restriction X (narrow-to-region begin end) X (goto-char begin) X (while (not (eobp)) X ;;(setq last (min end (+ (point) size))) X ;; NEmacs gets confused if character at `last' is Kanji. X (setq last (save-excursion X (goto-char (min end (+ (point) size))) X (or (eobp) (forward-char 1)) ;Adjust point X (point))) X (process-send-region nntp-server-process (point) last) X ;; I don't know whether the next codes solve the known X ;; problem of communication error of GNU Emacs. X (accept-process-output) X ;;(sit-for 0) X (goto-char last) X ))) X ;; We cannot erase buffer, because reply may be received. X (delete-region begin end) X )) X X(defun nntp-open-server-internal (host &optional service) X "Open connection to news server on HOST by SERVICE (default is nntp)." X (save-excursion X ;; Initialize communication buffer. X (setq nntp-server-buffer (get-buffer-create " *nntpd*")) X (set-buffer nntp-server-buffer) X (kill-all-local-variables) X (erase-buffer) X (setq nntp-server-process X (open-network-stream "nntpd" (current-buffer) X host (or service "nntp"))) X ;; You can change kanji-fileio-code in hooks. X (run-hooks 'nntp-server-hook) X ;; Return the server process. X nntp-server-process X )) X X(defun nntp-close-server-internal () X "Close connection to news server." X (delete-process nntp-server-process) X (kill-buffer nntp-server-buffer) X (setq nntp-server-buffer nil) X (setq nntp-server-process nil)) X X(defun nntp-accept-response () X "Read response of server. XIt is known that communication speed will be improved much by defining Xthis function as macro." X (if nntp-buggy-select X (progn X ;; We cannot use `accept-process-output'. X ;; Fujitsu UTS requires messages during sleep-for. I don't know why. X (message "NNTP: Reading...") X (sleep-for 1) X (message "")) X ;; To deal with server process exiting before X ;; accept-process-output is called. X ;; Suggested by Jason Venner . X (condition-case () X (accept-process-output nntp-server-process) X (error nil)) X )) SHAR_EOF echo "File nntp.el is complete" chmod 0444 nntp.el || echo "restore of nntp.el fails" set `wc -c nntp.el`;Sum=$1 if test "$Sum" != "17741" then echo original size 17741, current size $Sum;fi echo "x - extracting nnspool.el (Text)" sed 's/^X//' << 'SHAR_EOF' > nnspool.el && X;;; Spool patches to NNTP package for GNU Emacs X;; Copyright (C) 1988 Fujitsu Laboratoris LTD. X;; Copyright (C) 1988 Masanobu UMEDA (umerin@flab.flab.Fujitsu.JUNET) X;; $Header: nnspool.el,v 1.3 88/06/16 09:58:46 umerin Exp $ X X;; This file is part of GNU Emacs. X X;; GNU Emacs is distributed in the hope that it will be useful, X;; but WITHOUT ANY WARRANTY. No author or distributor X;; accepts responsibility to anyone for the consequences of using it X;; or for whether it serves any particular purpose or works at all, X;; unless he says so in writing. Refer to the GNU Emacs General Public X;; License for full details. X X;; Everyone is granted permission to copy, modify and redistribute X;; GNU Emacs, but only under the conditions described in the X;; GNU Emacs General Public License. A copy of this license is X;; supposed to have been given to you along with GNU Emacs so you X;; can know your rights and responsibilities. It should be in a X;; file named COPYING. Among other things, the copyright notice X;; and this notice must be preserved on all copies. X X(provide 'nnspool) X(require 'nntp) X X(defvar nnspool-inews-program news-inews-program X "Program to post news.") X X(defvar nnspool-spool-directory news-path X "Local news spool directory.") X X(defvar nnspool-active-file "/usr/lib/news/active" X "Local news active file.") X X(defvar nnspool-current-directory nil X "Current news group directory.") X X;;; X;;; Replacement of Extended Command for retrieving many headers. X;;; X X(defun nntp-retrieve-headers (sequence) X "Return list of article headers specified by SEQUENCE of article id. XThe format of list is `((NUMBER SUBJECT FROM XREF LINES) ...)'. XAccess macros to contents of the list is defined as `nntp-headers-FIELD'. XNews group must be selected before calling me." X (save-excursion X (set-buffer nntp-server-buffer) X ;;(erase-buffer) X (let ((file nil) X (number (length sequence)) X (count 0) X (headers nil) ;Result list. X (article 0) X (subject nil) X (from nil) X (xref nil) X (lines 0)) X (while sequence X ;;(nntp-send-strings-to-server "HEAD" (car sequence)) X (setq article (car sequence)) X (setq file (concat nnspool-current-directory X (prin1-to-string article))) X (if (and (file-exists-p file) X (not (file-directory-p file))) X (progn X (erase-buffer) X (insert-file-contents file) X (goto-char (point-min)) X (search-forward "\n\n" nil 'move) X (narrow-to-region (point-min) (point)) X ;; Extract From: X (goto-char (point-min)) X (if (search-forward "\nFrom: " nil t) X (setq from (buffer-substring X (point) X (save-excursion (end-of-line) (point)))) X (setq from "Unknown User")) X ;; Extract Subject: X (goto-char (point-min)) X (if (search-forward "\nSubject: " nil t) X (setq subject (buffer-substring X (point) X (save-excursion (end-of-line) (point)))) X (setq subject "(None)")) X ;; Extract Lines: X (goto-char (point-min)) X (if (search-forward "\nLines: " nil t) X (setq lines (string-to-int X (buffer-substring X (point) X (save-excursion (end-of-line) (point))))) X (setq lines 0)) X ;; Extract Xref: X (goto-char (point-min)) X (if (search-forward "\nXref: " nil t) X (setq xref (buffer-substring X (point) X (save-excursion (end-of-line) (point)))) X (setq xref nil)) X (setq headers X (cons (list article subject from xref lines) headers)) X )) X (setq sequence (cdr sequence)) X (setq count (1+ count)) X (if (and (> number 100) X (zerop (% count 20))) X (message "NNSPOOL: %d%% of headers received." X (/ (* count 100) number))) X ) X (if (> number 100) X (message "NNSPOOL: 100%% of headers received.")) X (nreverse headers) X ))) X X X;;; X;;; Replacement of NNTP Raw Interface. X;;; X X(defun nntp-open-server (host &optional service) X "Open news server on HOST. XIf HOST is nil, use value of environment variable `NNTPSERVER'. XIf optional argument SERVICE is non-nil, open by the service name." X (let ((host (or host X (getenv "NNTPSERVER") X (error "NNSPOOL: no server host is specified.")))) X (if (and (file-directory-p nnspool-spool-directory) X (file-exists-p nnspool-active-file)) X (nntp-open-server-internal host service)) X )) X X(defun nntp-close-server () X "Close news server." X (nntp-close-server-internal)) X X(fset 'nntp-request-quit (symbol-function 'nntp-close-server)) X X(defun nntp-server-active-p () X "Return server process status, T or NIL. XIf the stream is opened, return T, otherwise return NIL." X (and nntp-server-buffer X (get-buffer nntp-server-buffer))) X X(defun nntp-status-message () X "Return server status response as string." X nntp-status-message-string X ) X X(defun nntp-request-article (id) X "Select article by message ID (or number)." X (let ((file (concat nnspool-current-directory (prin1-to-string id)))) X (if (and (file-exists-p file) X (not (file-directory-p file))) X (save-excursion X (nnspool-find-file file))) X )) X X(defun nntp-request-body (id) X "Select article body by message ID (or number)." X (if (nntp-request-article id) X (save-excursion X (set-buffer nntp-server-buffer) X (goto-char (point-min)) X (if (search-forward "\n\n" nil t) X (delete-region (point-min) (point))) X t X ) X )) X X(defun nntp-request-head (id) X "Select article head by message ID (or number)." X (if (nntp-request-article id) X (save-excursion X (set-buffer nntp-server-buffer) X (goto-char (point-min)) X (if (search-forward "\n\n" nil t) X (delete-region (1- (point)) (point-max))) X t X ) X )) X X(defun nntp-request-stat (id) X "Select article by message ID (or number)." X (error "NNSPOOL: STAT is not implemented.")) X X(defun nntp-request-group (group) X "Select news GROUP." X (let ((pathname (nnspool-article-pathname group))) X (if (file-directory-p pathname) X (setq nnspool-current-directory pathname)) X )) X X(defun nntp-request-list () X "List valid newsgoups." X (save-excursion X (nnspool-find-file nnspool-active-file))) X X(defun nntp-request-last () X "Set current article pointer to the previous article Xin the current news group." X (error "NNSPOOL: LAST is not implemented.")) X X(defun nntp-request-next () X "Advance current article pointer." X (error "NNSPOOL: NEXT is not implemented.")) X X(defun nntp-request-post () X "Post a new news in current buffer." X (save-excursion X ;; We have to work in the server buffer because of NEmacs hack. X (copy-to-buffer nntp-server-buffer (point-min) (point-max)) X (set-buffer nntp-server-buffer) X (call-process-region (point-min) (point-max) X nnspool-inews-program 'delete t nil X "-h") X (prog1 X ;; If inews returns some strings, it must be error message. X (zerop (buffer-size)) X ;; Make status message by unfolding lines. X (subst-char-in-region (point-min) (point-max) ?\n ?\\ 'noundo) X (setq nntp-status-message-string X (buffer-substring (point-min) (point-max))) X (erase-buffer)) X )) X X X;;; X;;; Replacement of Low-Level Interface to NNTP Server. X;;; X X(defun nntp-open-server-internal (host &optional service) X "Open connection to news server on HOST by SERVICE (default is nntp)." X (save-excursion X (if (not (string-equal host (system-name))) X (error "NNSPOOL: Load `nntp' again if you'd like to talk to %s." host)) X ;; Initialize communication buffer. X (setq nntp-server-buffer (get-buffer-create " *nntpd*")) X (set-buffer nntp-server-buffer) X (kill-all-local-variables) X (erase-buffer) X (setq nntp-server-process nil) X ;; You can change kanji-fileio-code in hooks. X (run-hooks 'nntp-server-hook) X t X )) X X(defun nntp-close-server-internal () X "Close connection to news server." X (kill-buffer nntp-server-buffer) X (setq nntp-server-buffer nil) X (setq nntp-server-process nil)) X X(defun nnspool-find-file (file) X "Insert FILE in server buffer safely." X (set-buffer nntp-server-buffer) X (erase-buffer) X (condition-case () X (progn (insert-file-contents file) t) X (file-error nil) X )) X X(defun nnspool-article-pathname (group) X "Make pathname to news GROUP." X (let ((group (substring group 0)) ;Copy string. X (len (length group)) X (idx 0)) X ;; Replace all occurence of `.' with `/'. X (while (< idx len) X (if (eq (aref group idx) ?.) X (aset group idx ?/)) X (setq idx (1+ idx))) X (concat nnspool-spool-directory group "/") X )) SHAR_EOF chmod 0444 nnspool.el || echo "restore of nnspool.el fails" set `wc -c nnspool.el`;Sum=$1 if test "$Sum" != "8417" then echo original size 8417, current size $Sum;fi rm -f s2_seq_.tmp echo "You have unpacked the last part" exit 0 -- Masanobu UMEDA umerin@flab.flab.Fujitsu.JUNET umerin%flab.flab.Fujitsu.JUNET@uunet.uu.NET