*** A new command for reading collections of documents
(nndoc with nnvirtual on top) has been added -- `M-C-d'.
+*** Process mark sets can be pushed and popped.
+
+*** A new mail-to-news backend makes it possible to post
+even when the NNTP server doesn't allow posting.
+Sat Aug 17 12:58:28 1996 Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
+
+ * gnus-win.el (gnus-window-configuration-element): New function.
+ (gnus-windows-old-to-new): Use it.
+ (gnus-windows-old-to-new): Produced bogus results.
+
+ * message.el (message-cancel-message): New variable.
+
+ * gnus-srvr.el (gnus-server-mode-map): Buggy keymap.
+
+ * gnus-group.el (gnus-group-get-new-news-this-group): Illegal
+ gnus-error value.
+
+Fri Aug 16 21:22:12 1996 Lars Magne Ingebrigtsen <lars@eyesore.no>
+
+ * nnmail.el (nnmail-replace-status, nnmail-decode-status,
+ nnmail-encode-status): New variables.
+
+ * nnml.el (nnml-article-to-file): New function.
+
+Fri Aug 16 20:26:12 1996 Kurt Swanson <kurt@dna.lth.se>
+
+ * nnfolder.el (nnfolder-generate-active-file): Test the right
+ files.
+
+Fri Aug 16 19:30:57 1996 Lars Magne Ingebrigtsen <lars@eyesore.no>
+
+ * gnus-salt.el (gnus-possibly-generate-tree): Would bug out on
+ unthreaded buffers.
+
+ * gnus-xmas.el (gnus-xmas-modeline-right-extent): Disabled.
+ (gnus-xmas-modeline-left-extent): Ditto.
+
+ * gnus-group.el (gnus-group-make-menu-bar): Bugged out on
+ undefined variable.
+
+ * gnus.el (gnus-read-method): Return the virtual server name if
+ possible.
+
+Thu Aug 15 18:15:58 1996 Lars Magne Ingebrigtsen <lars@eyesore.no>
+
+ * nngateway.el: New file.
+
+ * nnoo.el (nnoo-define-skeleton): New macro.
+ (nnoo-define-skeleton-1): New function.
+
+ * gnus-start.el (gnus-strip-killed-list): New function.
+ (gnus-gnus-to-quick-newsrc-format): Use it.
+
+ * gnus-sum.el (gnus-summary-process-mark-set): New function.
+ (gnus-summary-yank-process-mark, gnus-summary-kill-process-mark,
+ gnus-summary-save-process-mark): New commands and keystrokes.
+
+ * nnml.el (nnml-generate-nov-file): Set modes.
+
+ * nnmail.el (nnmail-default-file-modes): New variable.
+ (nnmail-write-region): New function.
+
+ * gnus-score.el (gnus-score-score-files-1): Bind case-fold-search
+ to nil.
+
+Wed Aug 14 21:20:07 1996 Lars Magne Ingebrigtsen <lars@eyesore.no>
+
+ * gnus-soup.el (gnus-soup-send-packet): Disable syntax checks.
+
+Wed Aug 14 20:28:09 1996 Fred Johansen <Fred.Johansen@ifi.ntnu.no>
+
+ * gnus-logic.el (gnus-advanced-score-rule): `and' rules were
+ treated improperly.
+
+Wed Aug 14 15:29:39 1996 Lars Magne Ingebrigtsen <lars@eyesore.no>
+
+ * gnus-salt.el (gnus-mouse-pick-article): New command.
+
+ * gnus-art.el (gnus-button-url): Call with one argument.
+
+ * gnus-start.el (gnus-set-default-directory): New function.
+
+ * gnus-load.el (gnus-default-directory): New variable.
+
+Wed Aug 14 15:03:01 1996 Sudish Joseph <sudish@mindspring.com>
+
+ * gnus-score.el (gnus-home-score-file): Changed syntax.
+
+Tue Aug 13 22:07:11 1996 Jan Vroonhof <vroonhof@math.ethz.ch>
+
+ * nndoc.el (nndoc-dissect-buffer): Went into infinite loop if end
+ of file token wasn't properly detected.
+ (nndoc-type-alist): Better end-of-header regexp for
+ lanl.gov preprints
+ (nndoc-article-type): Updated doc string
+
+Mon Aug 12 21:01:25 1996 Sudish Joseph <sudish@mindspring.com>
+
+ * nntp.el (nntp-request-newgroups): Switch to nntp-server-buffer
+ first.
+
+Tue Aug 13 09:44:46 1996 Lars Magne Ingebrigtsen <lars@eyesore.no>
+
+ * gnus-group.el (gnus-group-sort-by-real-name): New function.
+
+ * gnus-sum.el (gnus-summary-save-article): Pass on number of
+ articles to be saved.
+
+ * gnus-art.el (gnus-article-edit-article): Remove all text props.
+ (gnus-read-save-file-name): Take an optional defaultish parameter.
+
+ * nntp.el (nntp-retrieve-groups): Saved.
+
+ * message.el (message-forward): Didn't work well with multi-line
+ separators.
+
+ * gnus-msg.el (gnus-summary-mail-crosspost-complaint): Check
+ wheteher followup-to was restricted.
+
+ * nnsoup.el (nnsoup-store-reply): Would insert double courtesy
+ headers.
+
+ * gnus-group.el (gnus-group-highlight-line): New `total' number.
+
+Mon Aug 12 06:25:00 1996 Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
+
+ * gnus.el: Red Gnus v0.11 is released.
+
Mon Aug 12 03:51:57 1996 Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
* gnus-async.el (gnus-make-async-article-function): New function.
(nconc '((?w (gnus-article-wash-status) ?s))
gnus-summary-mode-line-format-alist))
+(defvar gnus-number-of-articles-to-be-saved nil)
+
;;; Provide a mapping from `gnus-*' commands to Article commands.
(eval-and-compile
;;; Saving functions.
-(defun gnus-article-save (save-buffer file)
+(defun gnus-article-save (save-buffer file &optional num)
"Save the currently selected article."
(unless gnus-save-all-headers
;; Remove headers accoring to `gnus-saved-headers'.
;; `gnus-original-article-buffer' (or so they think),
;; but we bind that variable to our save-buffer.
(set-buffer gnus-article-buffer)
- (let ((gnus-original-article-buffer save-buffer))
+ (let* ((gnus-original-article-buffer save-buffer)
+ (filename
+ (cond
+ ((not gnus-prompt-before-saving)
+ 'default)
+ ((eq gnus-prompt-before-saving 'always)
+ nil)
+ (t file)))
+ (gnus-number-of-articles-to-be-saved
+ (when (stringp filename) num))) ; Magic
(set-buffer gnus-summary-buffer)
- (funcall
- gnus-default-article-saver
- (cond
- ((not gnus-prompt-before-saving)
- 'default)
- ((eq gnus-prompt-before-saving 'always)
- nil)
- (t file)))))))
-
-(defun gnus-read-save-file-name (prompt default-name)
- (let* ((split-name (gnus-get-split-value gnus-split-methods))
- (file
- ;; Let the split methods have their say.
- (cond
- ;; No split name was found.
- ((null split-name)
- (read-file-name
- (concat prompt " (default "
- (file-name-nondirectory default-name) ") ")
- (file-name-directory default-name)
- default-name))
- ;; A single split name was found
- ((= 1 (length split-name))
- (let* ((name (car split-name))
- (dir (cond ((file-directory-p name)
- (file-name-as-directory name))
- ((file-exists-p name) name)
- (t gnus-article-save-directory))))
- (read-file-name
- (concat prompt " (default " name ") ")
- dir name)))
- ;; A list of splits was found.
- (t
- (setq split-name (nreverse split-name))
- (let (result)
- (let ((file-name-history (nconc split-name file-name-history)))
- (setq result
- (read-file-name
- (concat prompt " (`M-p' for defaults) ")
- gnus-article-save-directory
- (car split-name))))
- (car (push result file-name-history)))))))
- ;; Create the directory.
- (unless (equal (directory-file-name file) file)
- (make-directory (file-name-directory file) t))
- ;; If we have read a directory, we append the default file name.
- (when (file-directory-p file)
- (setq file (concat (file-name-as-directory file)
- (file-name-nondirectory default-name))))
- ;; Possibly translate some characters.
- (nnheader-translate-file-chars file)))
+ (funcall gnus-default-article-saver filename)))))
+
+(defun gnus-read-save-file-name (prompt default-name &optional filename)
+ (cond
+ ((eq filename 'default)
+ default-name)
+ (filename filename)
+ (t
+ (let* ((split-name (gnus-get-split-value gnus-split-methods))
+ (prompt
+ (format prompt (if (and gnus-number-of-articles-to-be-saved
+ (> gnus-number-of-articles-to-be-saved 1))
+ (format "these %d articles"
+ gnus-number-of-articles-to-be-saved)
+ "this article")))
+ (file
+ ;; Let the split methods have their say.
+ (cond
+ ;; No split name was found.
+ ((null split-name)
+ (read-file-name
+ (concat prompt " (default "
+ (file-name-nondirectory default-name) ") ")
+ (file-name-directory default-name)
+ default-name))
+ ;; A single split name was found
+ ((= 1 (length split-name))
+ (let* ((name (car split-name))
+ (dir (cond ((file-directory-p name)
+ (file-name-as-directory name))
+ ((file-exists-p name) name)
+ (t gnus-article-save-directory))))
+ (read-file-name
+ (concat prompt " (default " name ") ")
+ dir name)))
+ ;; A list of splits was found.
+ (t
+ (setq split-name (nreverse split-name))
+ (let (result)
+ (let ((file-name-history (nconc split-name file-name-history)))
+ (setq result
+ (read-file-name
+ (concat prompt " (`M-p' for defaults) ")
+ gnus-article-save-directory
+ (car split-name))))
+ (car (push result file-name-history)))))))
+ ;; Create the directory.
+ (unless (equal (directory-file-name file) file)
+ (make-directory (file-name-directory file) t))
+ ;; If we have read a directory, we append the default file name.
+ (when (file-directory-p file)
+ (setq file (concat (file-name-as-directory file)
+ (file-name-nondirectory default-name))))
+ ;; Possibly translate some characters.
+ (nnheader-translate-file-chars file)))))
(defun gnus-article-archive-name (group)
"Return the first instance of an \"Archive-name\" in the current buffer."
(let ((default-name
(funcall gnus-rmail-save-name gnus-newsgroup-name
gnus-current-headers gnus-newsgroup-last-rmail)))
- (setq filename
- (cond ((eq filename 'default)
- default-name)
- (filename filename)
- (t (gnus-read-save-file-name
- "Save in rmail file:" default-name))))
+ (setq filename (gnus-read-save-file-name
+ "Save %s in rmail file:" default-name filename))
(make-directory (file-name-directory filename) t)
(gnus-eval-in-buffer-window gnus-original-article-buffer
(save-excursion
(let ((default-name
(funcall gnus-mail-save-name gnus-newsgroup-name
gnus-current-headers gnus-newsgroup-last-mail)))
- (setq filename
- (cond ((eq filename 'default)
- default-name)
- (filename filename)
- (t (gnus-read-save-file-name
- "Save in Unix mail file:" default-name))))
+ (setq filename (gnus-read-save-file-name
+ "Save %s in Unix mail file:" default-name filename))
(setq filename
(expand-file-name filename
(and default-name
(let ((default-name
(funcall gnus-file-save-name gnus-newsgroup-name
gnus-current-headers gnus-newsgroup-last-file)))
- (setq filename
- (cond ((eq filename 'default)
- default-name)
- (filename filename)
- (t (gnus-read-save-file-name
- "Save in file:" default-name))))
+ (setq filename (gnus-read-save-file-name
+ "Save %s in file:" default-name filename))
(make-directory (file-name-directory filename) t)
(gnus-eval-in-buffer-window gnus-original-article-buffer
(save-excursion
(let ((default-name
(funcall gnus-file-save-name gnus-newsgroup-name
gnus-current-headers gnus-newsgroup-last-file)))
- (setq filename
- (cond ((eq filename 'default)
- default-name)
- (filename filename)
- (t (gnus-read-save-file-name
- "Save body in file:" default-name))))
+ (setq filename (gnus-read-save-file-name
+ "Save %s body in file:" default-name filename))
(make-directory (file-name-directory filename) t)
(gnus-eval-in-buffer-window gnus-original-article-buffer
(save-excursion
(cons (list 'gnus-show-mime " MIME") minor-mode-alist)))
(use-local-map gnus-article-mode-map)
(gnus-update-format-specifications nil 'article-mode)
- (make-local-variable 'page-delimiter)
- (setq page-delimiter gnus-page-delimiter)
+ (set (make-local-variable 'page-delimiter) gnus-page-delimiter)
+ (gnus-set-default-directory)
(buffer-disable-undo (current-buffer))
(setq buffer-read-only t) ;Disable modification
(run-hooks 'gnus-article-mode-hook))
(let ((winconf (current-window-configuration)))
(set-buffer gnus-article-buffer)
(gnus-article-edit-mode)
+ (set-text-properties (point-min) (point-max) nil)
(gnus-configure-windows 'edit-article)
(setq gnus-article-edit-done-function exit-func)
(setq gnus-prev-winconf winconf)
(window-start (window-start)))
(erase-buffer)
(insert buf)
- (let ((winconf gnus-prev-winconf))
- (gnus-article-mode)
- ;; The cache and backlog have to be flushed somewhat.
- (when gnus-use-cache
- (gnus-cache-update-article
- (car gnus-article-current) (cdr gnus-article-current)))
- (when gnus-keep-backlog
- (gnus-backlog-remove-article
- (car gnus-article-current) (cdr gnus-article-current)))
- ;; Flush original article as well.
- (save-excursion
- (when (get-buffer gnus-original-article-buffer)
- (set-buffer gnus-original-article-buffer)
- (setq gnus-original-article nil)))
- (set-window-configuration winconf)
- ;; Tippy-toe some to make sure that point remains where it was.
- (let ((buf (current-buffer)))
- (set-buffer curbuf)
- (set-window-start (get-buffer-window (current-buffer)) window-start)
- (goto-char p)
- (set-buffer buf)))))
+ (let ((winconf gnus-prev-winconf))
+ (gnus-article-mode)
+ ;; The cache and backlog have to be flushed somewhat.
+ (when gnus-use-cache
+ (gnus-cache-update-article
+ (car gnus-article-current) (cdr gnus-article-current)))
+ (when gnus-keep-backlog
+ (gnus-backlog-remove-article
+ (car gnus-article-current) (cdr gnus-article-current)))
+ ;; Flush original article as well.
+ (save-excursion
+ (when (get-buffer gnus-original-article-buffer)
+ (set-buffer gnus-original-article-buffer)
+ (setq gnus-original-article nil)))
+ (set-window-configuration winconf)
+ ;; Tippy-toe some to make sure that point remains where it was.
+ (let ((buf (current-buffer)))
+ (set-buffer curbuf)
+ (set-window-start (get-buffer-window (current-buffer)) window-start)
+ (goto-char p)
+ (set-buffer buf)))))
(defun gnus-article-edit-full-stops ()
"Interactively repair spacing at end of sentences."
(defun gnus-button-url (address)
"Browse ADDRESS."
- (funcall browse-url-browser-function address browse-url-new-window-p))
+ (funcall browse-url-browser-function address))
;;; Next/prev buttons in the article buffer.
"*Function used for sorting the group buffer.
This function will be called with group info entries as the arguments
for the groups to be sorted. Pre-made functions include
-`gnus-group-sort-by-alphabet', `gnus-group-sort-by-unread',
-`gnus-group-sort-by-level', `gnus-group-sort-by-score',
-`gnus-group-sort-by-method', and `gnus-group-sort-by-rank'.
+`gnus-group-sort-by-alphabet', `gnus-group-sort-by-real-name',
+`gnus-group-sort-by-unread', `gnus-group-sort-by-level',
+`gnus-group-sort-by-score', `gnus-group-sort-by-method', and
+`gnus-group-sort-by-rank'.
This variable can also be a list of sorting functions. In that case,
the most significant sort function should be the last function in the
(gnus-group-group-name)]
["Info" gnus-group-edit-group (gnus-group-group-name)])
("Score file"
- ["Flush cache" gnus-score-flush-cache
- (or gnus-score-cache gnus-short-name-score-file-cache)])
+ ["Flush cache" gnus-score-flush-cache t])
("Move"
["Next" gnus-group-next-group t]
["Previous" gnus-group-prev-group t]
(buffer-disable-undo (current-buffer))
(setq truncate-lines t)
(setq buffer-read-only t)
+ (gnus-set-default-directory)
(gnus-update-format-specifications nil 'group 'group-mode)
(gnus-update-group-mark-positions)
(gnus-make-local-hook 'post-command-hook)
(group (gnus-group-group-name))
(entry (gnus-group-entry group))
(unread (if (numberp (car entry)) (car entry) 0))
+ (active (gnus-active group))
+ (total (if active (1+ (- (cdr active) (car active))) 0))
(info (nth 2 entry))
(method (gnus-server-get-method group (gnus-info-method info)))
(marked (gnus-info-marks info))
"Sort alphabetically."
(string< (gnus-info-group info1) (gnus-info-group info2)))
+(defun gnus-group-sort-by-real-name (info1 info2)
+ "Sort alphabetically on real (unprefixed) names."
+ (string< (gnus-group-real-name (gnus-info-group info1))
+ (gnus-group-real-name (gnus-info-group info2))))
+
(defun gnus-group-sort-by-unread (info1 info2)
"Sort by number of unread articles."
(let ((n1 (car (gnus-gethash (gnus-info-group info1) gnus-newsrc-hashtb)))
(gnus-group-update-group group))
(if (eq (gnus-server-status (gnus-find-method-for-group group))
'denied)
- (gnus-error "Server denied access")
+ (gnus-error 3 "Server denied access")
(gnus-error 3 "%s error: %s" group (gnus-status-message group)))))
(when beg (goto-char beg))
(when gnus-goto-next-group-when-activating
(defvar gnus-directory (or (getenv "SAVEDIR") "~/News/")
"*Directory variable from which all other Gnus file variables are derived.")
+(defvar gnus-default-directory nil
+ "*Default directory for all Gnus buffers.")
+
;; Site dependent variables. These variables should be defined in
;; paths.el.
("nnkiboze" post virtual)
("nnsoup" post-mail address)
("nndraft" post-mail)
- ("nnfolder" mail respool address))
+ ("nnfolder" mail respool address)
+ ("nngateway" none address prompt-address))
"An alist of valid select methods.
The first element of each list lists should be a string with the name
of the select method. The other elements may be the category of
(if (not rule)
t ; Empty rule is true.
(while (and rule
- (gnus-advanced-score-rule (pop rule))))
+ (gnus-advanced-score-rule (car rule)))
+ (pop rule))
;; If all the rules were true, then `rule' should be nil.
(not rule)))
;; "Or" rule.
(defvar gnus-sent-message-ids-length 1000
"The number of sent Message-IDs to save.")
+(defvar gnus-crosspost-complaint
+ "Hi,
+
+You posted the article below with the following Newsgroups header:
+
+Newsgroups: %s
+
+The %s group, at least, was an inappropriate recipient
+of this message. Please trim your Newsgroups header to exclude this
+group before posting in the future.
+
+Thank you.
+"
+ "Format string to be inserted when complaining about crossposts.
+The first %s will be replaced by the Newsgroups header;
+the second with the current group name.")
+
;;; Internal variables.
(defvar gnus-message-buffer "*Mail Gnus*")
(insert (format gnus-nastygram-message group))
(message-send-and-exit))))
-(defvar gnus-crosspost-complaint
- "Hi,
-
-You posted the article below with the following Newsgroups header:
-
-Newsgroups: %s
-
-The %s group, at least, was an inappropriate recipient
-of this message. Please trim your Newsgroups header to exclude this
-group before posting in the future.
-
-Thank you.
-"
- "Format string to be inserted when complaining about crossposts.
-The first %s will be replaced by the Newsgroups header;
-the second with the current group name.")
-
(defun gnus-summary-mail-crosspost-complaint (n)
"Send a complaint about crossposting to the current article(s)."
(interactive "P")
(set-buffer gnus-summary-buffer)
(gnus-summary-goto-subject article)
(let ((group (gnus-group-real-name gnus-newsgroup-name))
- newsgroups)
+ newsgroups followup-to)
(gnus-summary-select-article)
(set-buffer gnus-original-article-buffer)
- (if (<= (length (message-tokenize-header
- (setq newsgroups (mail-fetch-field "newsgroups"))
- ", "))
- 1)
- (gnus-message 1 "Not a crossposted article")
+ (if (and (<= (length (message-tokenize-header
+ (setq newsgroups (mail-fetch-field "newsgroups"))
+ ", "))
+ 1)
+ (or (not (setq followup-to (mail-fetch-field "followup-to")))
+ (not (member group (message-tokenize-header
+ followup-to ", ")))))
+ (if followup-to
+ (gnus-message 1 "Followup-to restricted")
+ (gnus-message 1 "Not a crossposted article"))
(set-buffer gnus-summary-buffer)
(gnus-summary-reply-with-original 1)
(set-buffer gnus-message-buffer)
"b" gnus-uu-mark-buffer
"B" gnus-uu-unmark-buffer
"." gnus-pick-article
+ gnus-mouse-2 gnus-pick-pick-article
"\r" gnus-pick-start-reading))
(defun gnus-pick-make-menu-bar ()
(goto-char pos))))
(gnus-summary-mark-as-processable 1))
+(defun gnus-mouse-pick-article (e)
+ (interactive "e")
+ (mouse-set-point e)
+ (save-excursion
+ (gnus-summary-mark-as-processable 1)))
+
(defun gnus-pick-next-page ()
"Go to the next page. If at the end of the buffer, start reading articles."
(interactive)
(when (save-excursion
(set-buffer gnus-summary-buffer)
(and gnus-use-trees
+ gnus-show-threads
(vectorp (gnus-summary-article-header article))))
(save-excursion
(let ((top (save-excursion
* A list
The elements in this list can be:
- * `(regexp . file-name)'
- If the `regexp' matches the group name, the `file-name' will
- will be used as the home score file.
+ * `(regexp file-name ...)'
+ If the `regexp' matches the group name, the first `file-name' will
+ will be used as the home score file. (Multiple filenames are
+ allowed so that one may use gnus-score-file-single-match-alist to
+ set this variable.)
* A function.
If the function returns non-nil, the result will be used
(delq (assoc file gnus-score-cache) gnus-score-cache)))
(defun gnus-score-load-score-alist (file)
+ "Read score FILE."
(let (alist)
(if (not (file-readable-p file))
+ ;; Couldn't read file.
(setq gnus-score-alist nil)
+ ;; Read file.
(save-excursion
(gnus-set-work-buffer)
(insert-file-contents file)
(condition-case ()
(read (current-buffer))
(error
- (progn
- (gnus-message 3 "Problem with score file %s" file)
- (ding)
- (sit-for 2)
- nil))))))
+ (gnus-error 3.2 "Problem with score file %s" file))))))
(if (eq (car alist) 'setq)
;; This is an old-style score file.
(setq gnus-score-alist (gnus-score-transform-old-to-new alist))
"Return all possible score files under DIR."
(let ((files (directory-files (expand-file-name dir) t nil t))
(regexp (gnus-score-file-regexp))
+ (case-fold-search nil)
out file)
(while (setq file (pop files))
(cond
;; Regexp-file cons
((consp elem)
(when (string-match (car elem) group)
- (cdr elem))))))
+ (cadr elem))))))
(when found
(nnheader-concat gnus-kill-files-directory found))))
(gnus-message 5 "Sending news message to %s..."
(mail-fetch-field "newsgroups"))
(sit-for 1)
- (funcall message-send-news-function))
+ (let ((message-syntax-checks
+ 'dont-check-for-anything-just-trust-me))
+ (funcall message-send-news-function)))
((string= (gnus-soup-reply-kind (car replies)) "mail")
(gnus-message 5 "Sending mail to %s..."
(mail-fetch-field "to"))
"e" gnus-server-edit-server
"O" gnus-server-open-server
- "M-o" gnus-server-open-all-servers
+ "\M-o" gnus-server-open-all-servers
"C" gnus-server-close-server
- "M-c" gnus-server-close-all-servers
+ "\M-c" gnus-server-close-all-servers
"D" gnus-server-deny-server
"R" gnus-server-remove-denials
(gnus-simplify-mode-line)
(setq major-mode 'gnus-server-mode)
(setq mode-name "Server")
- ; (gnus-group-set-mode-line)
+ (gnus-set-default-directory)
(setq mode-line-process nil)
(use-local-map gnus-server-mode-map)
(buffer-disable-undo (current-buffer))
(use-local-map gnus-browse-mode-map)
(buffer-disable-undo (current-buffer))
(setq truncate-lines t)
+ (gnus-set-default-directory)
(setq buffer-read-only t)
(run-hooks 'gnus-browse-mode-hook))
the automatic new newsgroups subscription methods become meaningless.
You should always set `gnus-check-new-newsgroups' to `ask-server' or
-nil if you set this variable to nil.")
+nil if you set this variable to nil.
+
+This variable can also be a regexp. In that case, all groups that do
+not match this regexp will be removed before saving the list.")
(defvar gnus-ignored-newsgroups
(purecopy (mapconcat 'identity
(insert ";; to read .newsrc.\n")
(insert "(setq gnus-newsrc-file-version "
(prin1-to-string gnus-version) ")\n")
- (let ((variables
- (if gnus-save-killed-list gnus-variable-list
- ;; Remove the `gnus-killed-list' from the list of variables
- ;; to be saved, if required.
- (delq 'gnus-killed-list (copy-sequence gnus-variable-list))))
- ;; Peel off the "dummy" group.
- (gnus-newsrc-alist (cdr gnus-newsrc-alist))
- variable)
+ (let* ((gnus-killed-list
+ (if (and gnus-save-killed-list
+ (stringp gnus-save-killed-list))
+ (gnus-strip-killed-list)
+ gnus-killed-list))
+ (variables
+ (if gnus-save-killed-list gnus-variable-list
+ ;; Remove the `gnus-killed-list' from the list of variables
+ ;; to be saved, if required.
+ (delq 'gnus-killed-list (copy-sequence gnus-variable-list))))
+ ;; Peel off the "dummy" group.
+ (gnus-newsrc-alist (cdr gnus-newsrc-alist))
+ variable)
;; Insert the variables into the file.
(while variables
(when (and (boundp (setq variable (pop variables)))
(prin1 (symbol-value variable) (current-buffer))
(insert ")\n")))))
+(defun gnus-strip-killed-list ()
+ "Return the killed list minus the groups that match `gnus-save-killed-list'."
+ (let ((list gnus-killed-list)
+ olist)
+ (while list
+ (when (string-match gnus-save-killed-list)
+ (push (car list) olist))
+ (pop list))
+ (nreverse olist)))
+
(defun gnus-gnus-to-newsrc-format ()
;; Generate and save the .newsrc file.
(save-excursion
(nconc gnus-valid-select-methods
(list (apply 'list name abilities)))))
+(defun gnus-set-default-directory ()
+ "Set the default directory in the current buffer to `gnus-default-directory'.
+If this variable is nil, don't do anything."
+ (setq default-directory (or gnus-default-directory default-directory)))
+
(provide 'gnus-start)
;;; gnus-start.el ends here
(defvar gnus-original-article nil)
(defvar gnus-article-internal-prepare-hook nil)
+(defvar gnus-newsgroup-process-stack nil)
(defvar gnus-thread-indent-array nil)
(defvar gnus-thread-indent-array-level gnus-thread-indent-level)
(gnus-summary-mark-below . global)
gnus-newsgroup-active gnus-scores-exclude-files
gnus-newsgroup-history gnus-newsgroup-ancient
- gnus-newsgroup-sparse
+ gnus-newsgroup-sparse gnus-newsgroup-process-stack
(gnus-newsgroup-adaptive . gnus-use-adaptive-scoring)
gnus-newsgroup-adaptive-score-file (gnus-reffed-article-number . -1)
(gnus-newsgroup-expunged-tally . 0)
["Mark buffer" gnus-uu-mark-buffer t]
["Mark sparse" gnus-uu-mark-sparse t]
["Mark thread" gnus-uu-mark-thread t]
- ["Unmark thread" gnus-uu-unmark-thread t]))
+ ["Unmark thread" gnus-uu-unmark-thread t]
+ ("Process Mark Sets"
+ ["Kill" gnus-summary-kill-process-mark t]
+ ["Yank" gnus-summary-yank-process-mark
+ gnus-newsgroup-process-stack]
+ ["Save" gnus-summary-save-process-mark t])))
("Scroll article"
["Page forward" gnus-summary-next-page t]
["Page backward" gnus-summary-prev-page t]
(setq selective-display t)
(setq selective-display-ellipses t) ;Display `...'
(setq buffer-display-table gnus-summary-display-table)
+ (gnus-set-default-directory)
(setq gnus-newsgroup-name group)
(make-local-variable 'gnus-summary-line-format)
(make-local-variable 'gnus-summary-line-format-spec)
;; Report back a success?
(and header (mail-header-number header))))
+;;; Process/prefix in the summary buffer
+
(defun gnus-summary-work-articles (n)
"Return a list of articles to be worked upon. The prefix argument,
the list of process marked articles, and the current article will be
(nreverse articles))))
(gnus-newsgroup-processable
;; There are process-marked articles present.
+ ;; Save current state.
+ (gnus-summary-save-process-mark)
+ ;; Return the list.
(reverse gnus-newsgroup-processable))
(t
;; Just return the current article.
(list (gnus-summary-article-number)))))
+(defun gnus-summary-save-process-mark ()
+ "Push the current set of process marked articles on the stack."
+ (interactive)
+ (push (copy-sequence gnus-newsgroup-processable)
+ gnus-newsgroup-process-stack))
+
+(defun gnus-summary-kill-process-mark ()
+ "Push the current set of process marked articles on the stack and unmark."
+ (interactive)
+ (gnus-summary-save-process-mark)
+ (gnus-summary-unmark-all-processable))
+
+(defun gnus-summary-yank-process-mark ()
+ "Pop the last process mark state off the stack and restore it."
+ (interactive)
+ (unless gnus-newsgroup-process-stack
+ (error "Empty mark stack"))
+ (gnus-summary-process-mark-set (pop gnus-newsgroup-process-stack)))
+
+(defun gnus-summary-process-mark-set (set)
+ "Make SET into the current process marked articles."
+ (gnus-summary-unmark-all-processable)
+ (while set
+ (gnus-summary-set-process-mark (pop set))))
+
+;;; Searching and stuff
+
(defun gnus-summary-search-group (&optional backward use-level)
"Search for next unread newsgroup.
If optional argument BACKWARD is non-nil, search backward instead."
The variable `gnus-default-article-saver' specifies the saver function."
(interactive "P")
(gnus-set-global-variables)
- (let ((articles (gnus-summary-work-articles n))
- (save-buffer (save-excursion
- (nnheader-set-temp-buffer " *Gnus Save*")))
- header article file)
+ (let* ((articles (gnus-summary-work-articles n))
+ (save-buffer (save-excursion
+ (nnheader-set-temp-buffer " *Gnus Save*")))
+ (num (length articles))
+ header article file)
(while articles
(setq header (gnus-summary-article-header
(setq article (pop articles))))
(set-buffer save-buffer)
(erase-buffer)
(insert-buffer-substring gnus-original-article-buffer))
- (setq file (gnus-article-save save-buffer file))
+ (setq file (gnus-article-save save-buffer file num))
(gnus-summary-remove-process-mark article)
(unless not-saved
(gnus-summary-set-saved-mark article))))
"T" gnus-uu-unmark-thread
"a" gnus-uu-mark-all
"b" gnus-uu-mark-buffer
- "S" gnus-uu-mark-sparse)
+ "S" gnus-uu-mark-sparse
+ "k" gnus-summary-kill-process-mark
+ "y" gnus-summary-yank-process-mark
+ "w" gnus-summary-save-process-mark)
(gnus-define-keys
(gnus-uu-extract-map "X" gnus-summary-mode-map)
(cond ((eq folder 'default) default-name)
(folder folder)
(t (gnus-read-save-file-name
- "Save article in VM folder:" default-name))))
+ "Save %s in VM folder:" default-name))))
(make-directory (file-name-directory folder) t)
(set-buffer gnus-original-article-buffer)
(save-excursion
(error nil)))
(pop gnus-created-frames)))
+(defun gnus-window-configuration-element (list)
+ (while (and list
+ (not (assq (car list) gnus-window-configuration)))
+ (pop list))
+ (cadr (assq (car list) gnus-window-configuration)))
+
(defun gnus-windows-old-to-new (setting)
;; First we take care of the really, really old Gnus 3 actions.
(when (symbolp setting)
(setq setting
;; Take care of ooold GNUS 3.x values.
(cond ((eq setting 'SelectArticle) 'article)
- ((memq setting '(SelectSubject ExpandSubject)) 'summary)
- ((memq setting '(SelectNewsgroup ExitNewsgroup)) 'group)
+ ((memq setting '(SelectNewsgroup SelectSubject ExpandSubject))
+ 'summary)
+ ((memq setting '(ExitNewsgroup)) 'group)
(t setting))))
(if (or (listp setting)
(not (and gnus-window-configuration
(memq setting '(group summary article)))))
setting
- (let* ((setting (if (eq setting 'group)
- (if (assq 'newsgroup gnus-window-configuration)
- 'newsgroup
- 'newsgroups) setting))
- (elem (cadr (assq setting gnus-window-configuration)))
+ (let* ((elem
+ (cond
+ ((eq setting 'group)
+ (gnus-window-configuration-element
+ '(group newsgroups ExitNewsgroup)))
+ ((eq setting 'summary)
+ (gnus-window-configuration-element
+ '(summary SelectNewsgroup SelectSubject ExpandSubject)))
+ ((eq setting 'article)
+ (gnus-window-configuration-element
+ '(article SelectArticle)))))
(total (apply '+ elem))
(types '(group summary article))
(pbuf (if (eq setting 'newsgroups) 'group 'summary))
(i 0)
- perc
- out)
+ perc out)
(while (< i 3)
(or (not (numberp (nth i elem)))
(zerop (nth i elem))
(progn
(setq perc (if (= i 2)
1.0
- (/ (float (nth 0 elem)) total)))
- (setq out (cons (if (eq pbuf (nth i types))
- (list (nth i types) perc 'point)
- (list (nth i types) perc))
- out))))
- (setq i (1+ i)))
+ (/ (float (nth i elem)) total)))
+ (push (if (eq pbuf (nth i types))
+ (list (nth i types) perc 'point)
+ (list (nth i types) perc))
+ out)))
+ (incf i))
`(vertical 1.0 ,@(nreverse out)))))
;;;###autoload
(defvar gnus-xmas-modeline-left-extent
(let ((ext (copy-extent modeline-buffer-id-left-extent)))
- (set-extent-property ext 'pointer gnus-xmas-pointer-glyph)
+ ;(set-extent-property ext 'pointer gnus-xmas-pointer-glyph)
ext))
(defvar gnus-xmas-modeline-right-extent
(let ((ext (copy-extent modeline-buffer-id-right-extent)))
- (set-extent-property ext 'pointer gnus-xmas-pointer-glyph)
+ ;(set-extent-property ext 'pointer gnus-xmas-pointer-glyph)
ext))
(defvar gnus-xmas-modeline-glyph
(eval '(run-hooks 'gnus-load-hook))
-(defconst gnus-version-number "0.11"
+(defconst gnus-version-number "0.12"
"Version number for this version of Gnus.")
(defconst gnus-version (format "Red Gnus v%s" gnus-version-number)
(read-string "Address: ")
"")))
((assoc method gnus-server-alist)
- (cdr (assoc method gnus-server-alist)))
+ method)
(t
(list (intern method) "")))))
(defvar message-ignored-cited-headers "."
"Delete these headers from the messages you yank.")
+(defvar message-cancel-message "I am canceling my own article."
+ "Message to be inserted in the cancel message.")
+
;; Useful to set in site-init.el
;;;###autoload
(defvar message-send-mail-function 'message-send-mail-with-sendmail
(let ((regexp (format "[%s]+" (or separator ",")))
(beg 1)
(first t)
- quoted elems)
+ quoted elems paren)
(save-excursion
(message-set-work-buffer)
(insert header)
(cond ((and (> (point) beg)
(or (eobp)
(and (looking-at regexp)
- (not quoted))))
+ (not quoted)
+ (not paren))))
(push (buffer-substring beg (point)) elems)
(setq beg (match-end 0)))
((= (following-char) ?\")
- (setq quoted (not quoted)))))
+ (setq quoted (not quoted)))
+ ((and (= (following-char) ?\()
+ (not quoted))
+ (setq paren t))
+ ((and (= (following-char) ?\))
+ (not quoted))
+ (setq paren nil))))
(nreverse elems))))
(defun message-fetch-field (header)
(concat "Distribution: " distribution "\n")
"")
mail-header-separator "\n"
- "This is a cancel message from " from ".\n")
+ message-cancel-message)
(message "Canceling your article...")
(let ((message-syntax-checks 'dont-check-for-anything-just-trust-me))
(funcall message-send-news-function))
Optional NEWS will use news to forward instead of mail."
(interactive "P")
(let ((cur (current-buffer))
- (subject (message-make-forward-subject)))
+ (subject (message-make-forward-subject))
+ art-beg)
(if news (message-news nil subject) (message-mail nil subject))
;; Put point where we want it before inserting the forwarded
;; message.
(narrow-to-region (point) (point))
;; Insert the separators and the forwarded buffer.
(insert message-forward-start-separator)
+ (setq art-beg (point))
(insert-buffer-substring cur)
(goto-char (point-max))
(insert message-forward-end-separator)
(set-text-properties (point-min) (point-max) nil)
;; Remove all unwanted headers.
- (goto-char (point-min))
- (forward-line 1)
+ (goto-char art-beg)
(narrow-to-region (point) (if (search-forward "\n\n" nil t)
(1- (point))
(point)))
(create-file-buffer nnbabyl-mbox-file)))
(setq buffer-file-name nnbabyl-mbox-file)
(insert "BABYL OPTIONS:\n\n\^_")
- (write-region (point-min) (point-max) nnbabyl-mbox-file t 'nomesg)))
+ (nnmail-write-region
+ (point-min) (point-max) nnbabyl-mbox-file t 'nomesg)))
(if (and nnbabyl-mbox-buffer
(buffer-name nnbabyl-mbox-buffer)
(lanl-gov-announce
(article-begin . "^\\\\\\\\\n")
(head-begin . "^Paper.*:")
- (head-end . "^\\\\\\\\\n")
+ (head-end . "\\(^\\\\\\\\.*\n\\|-----------------\\)")
(body-begin . "")
(body-end . "-------------------------------------------------")
(file-end . "^Title: Recent Seminal")
(setq subject (concat (match-string 1) subject))
(setq from (concat (match-string 2) " <" e-mail ">"))))
))
- (while (string-match "(\[^)\]*)" from)
+ (while (and from (string-match "(\[^)\]*)" from))
(setq from (replace-match "" t t from)))
(insert "From: " (or from "unknown")
"\nSubject: " (or subject "(no subject)") "\n")))
(funcall nndoc-head-begin-function))
(nndoc-head-begin
(nndoc-search nndoc-head-begin)))
- (if (and nndoc-file-end
- (looking-at nndoc-file-end))
+ (if (or (>= (point) (point-max)) (and nndoc-file-end
+ (looking-at nndoc-file-end)))
(goto-char (point-max))
(setq head-begin (point))
(nndoc-search (or nndoc-head-end "^$"))
(setq nnfolder-buffer-alist (delq inf nnfolder-buffer-alist))
(setq inf nil)))
- (if inf
- ()
+ (unless inf
(save-excursion
(setq file (nnfolder-group-pathname group))
- (if (file-directory-p (file-truename file))
- ()
+ (unless (file-directory-p (file-truename file))
(unless (file-exists-p file)
(unless (file-exists-p (file-name-directory file))
(make-directory (file-name-directory file) t))
- (write-region 1 1 file t 'nomesg))
+ (nnmail-write-region 1 1 file t 'nomesg))
(setq nnfolder-current-buffer
(nnfolder-read-folder file scanning))
- (if nnfolder-current-buffer
- (progn
- (set-buffer nnfolder-current-buffer)
- (setq nnfolder-buffer-alist
- (cons (list group nnfolder-current-buffer)
- nnfolder-buffer-alist)))))))))
+ (when nnfolder-current-buffer
+ (set-buffer nnfolder-current-buffer)
+ (setq nnfolder-buffer-alist
+ (cons (list group nnfolder-current-buffer)
+ nnfolder-buffer-alist))))))))
(setq nnfolder-current-group group)))
(defun nnfolder-save-mail (&optional group)
(interactive)
(nnmail-activate 'nnfolder)
(let ((files (directory-files nnfolder-directory))
- file)
+ file)
(while (setq file (pop files))
(when (and (not (backup-file-name-p file))
- (nnheader-mail-file-mbox-p file))
- (nnheader-message 5 "Adding group %s..." file)
- (push (list file (cons 1 0)) nnfolder-group-alist)
- (nnfolder-possibly-change-group file)
-;; (nnfolder-read-folder file)
- (nnfolder-close-group file))
+ (nnheader-mail-file-mbox-p
+ (concat nnfolder-directory file)))
+ (nnheader-message 5 "Adding group %s..." file)
+ (push (list file (cons 1 0)) nnfolder-group-alist)
+ (nnfolder-possibly-change-group file)
+ (nnfolder-close-group file))
(message ""))))
(defun nnfolder-group-pathname (group)
--- /dev/null
+;;; nngateway.el --- posting news via mail gateways
+;; Copyright (C) 1996 Free Software Foundation, Inc.
+
+;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
+;; Keywords: news, mail
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs; see the file COPYING. If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;;; Commentary:
+
+;;; Code:
+
+(require 'nnoo)
+(require 'message)
+
+(nnoo-declare nngateway)
+
+(defvoo nngateway-address nil
+ "Address of the mail-to-news gateway.")
+
+(defvoo nngateway-header-transformation 'nngateway-simple-header-transformation
+ "Function to be called to rewrite the news headers into mail headers.
+It is called narrowed to the headers to be transformed with one
+parameter -- the gateway address.")
+
+;;; Interface functions
+
+(nnoo-define-basics nngateway)
+(nnoo-define-skeleton nngateway)
+
+(deffoo nngateway-open-server (server &optional defs)
+ (if (nngateway-server-opened server)
+ t
+ (unless (assq 'nngateway-address defs)
+ (setq defs (append defs (list (list 'nntp-address server)))))
+ (nnoo-change-server 'nntp server defs)))
+
+(deffoo nngateway-request-post (&optional server)
+ (when (or (nngateway-server-opened server)
+ (nngateway-open-server server))
+ ;; Rewrite the header.
+ (let ((buf (current-buffer)))
+ (nnheader-temp-write nil
+ (insert-buffer-substring buf)
+ (message-narrow-to-head)
+ (funcall nngateway-header-transformation nngateway-address)
+ (widen)
+ (let (message-required-mail-headers)
+ (message-send-mail))))))
+
+;;; Internal functions
+
+(defun nngateway-simple-header-transformation (gateway)
+ "Transform the headers to use GATEWAY."
+ (let ((newsgroups (mail-fetch-field "newsgroups")))
+ (message-remove-header "to")
+ (message-remove-header "cc")
+ (goto-char (point-min))
+ (insert "To: " (nnheader-replace-chars-in-string newsgroups ?. ?-)
+ "@" gateway "\n")))
+
+(provide 'nngateway)
+
+;;; nngateway.el ends here
nil)
(defun nnheader-get-report (backend)
- (message "%s" (symbol-value (intern (format "%s-status-string" backend)))))
+ "Get the most recent report from BACKEND."
+ (condition-case ()
+ (message "%s" (symbol-value (intern (format "%s-status-string"
+ backend))))
+ (error (message ""))))
(defun nnheader-insert (format &rest args)
"Clear the communicaton buffer and insert FORMAT and ARGS into the buffer.
If nil, groups like \"mail.misc\" will end up in directories like
\"mail/misc/\".")
+(defvar nnmail-default-file-modes 384
+ "Set the mode bits of all new mail files to this integer.")
+
(defvar nnmail-expiry-wait 7
"*Expirable articles that are older than this will be expired.
This variable can either be a number (which will be interpreted as a
(caadr group))))
(unless (file-exists-p (file-name-directory file-name))
(make-directory (file-name-directory file-name) t))
- (write-region 1 (point-max) (expand-file-name file-name) nil 'nomesg)
+ (nnmail-write-region
+ 1 (point-max) (expand-file-name file-name) nil 'nomesg)
(kill-buffer (current-buffer))))))
(defun nnmail-get-split-group (file group)
(or (file-exists-p (file-name-directory nnmail-message-id-cache-file))
(make-directory (file-name-directory nnmail-message-id-cache-file)
t))
- (write-region (point-min) (point-max)
- nnmail-message-id-cache-file nil 'silent)
+ (nnmail-write-region (point-min) (point-max)
+ nnmail-message-id-cache-file nil 'silent)
(set-buffer-modified-p nil)
(setq nnmail-cache-buffer nil)
;;(kill-buffer (current-buffer))
(unless (re-search-forward "^Message-Id:" nil t)
(insert "Message-ID: " (nnmail-message-id) "\n")))))
+(defun nnmail-write-region (start end filename &optional append visit lockname)
+ "Do a `write-region', and then set the file modes."
+ (write-region start end filename append visit lockname)
+ (set-file-modes filename nnmail-default-file-modes))
+
+;;;
+;;; Status functions
+;;;
+
+(defun nnmail-replace-status (name value)
+ "Make status NAME and VALUE part of the current status line."
+ (save-restriction
+ (message-narrow-to-head)
+ (let ((status (nnmail-decode-status)))
+ (setq status (delq (member name status) status))
+ (when value
+ (push (cons name value) status))
+ (message-remove-header "status")
+ (goto-char (point-max))
+ (insert "Status: " (nnmail-encode-status status) "\n"))))
+
+(defun nnmail-decode-status ()
+ "Return a status-value alist from STATUS."
+ (goto-char (point-min))
+ (when (re-search-forward "^Status: " nil t)
+ (let (name value status)
+ (save-restriction
+ ;; Narrow to the status.
+ (narrow-to-region
+ (point)
+ (if (re-search-forward "^[^ \t]" nil t)
+ (1- (point))
+ (point-max)))
+ ;; Go through all elements and add them to the list.
+ (goto-char (point-min))
+ (while (re-search-forward "[^ \t=]+" nil t)
+ (setq name (match-string 0))
+ (if (not (= (following-char) ?=))
+ ;; Implied "yes".
+ (setq value "yes")
+ (forward-char 1)
+ (if (not (= (following-char) ?\"))
+ (if (not (looking-at "[^ \t]"))
+ ;; Implied "no".
+ (setq value "no")
+ ;; Unquoted value.
+ (setq value (match-string 0))
+ (goto-char (match-end 0)))
+ ;; Quoted value.
+ (setq value (read (current-buffer)))))
+ (push (cons name value) status)))
+ status)))
+
+(defun nnmail-encode-status (status)
+ "Return a status string from STATUS."
+ (mapconcat
+ (lambda (elem)
+ (concat
+ (car elem) "="
+ (if (string-match "[ \t]" (cdr elem))
+ (prin1-to-string (cdr elem))
+ (cdr elem))))
+ status " "))
+
(run-hooks 'nnmail-load-hook)
(provide 'nnmail)
(defun nnmbox-read-mbox ()
(nnmail-activate 'nnmbox)
(if (not (file-exists-p nnmbox-mbox-file))
- (write-region 1 1 nnmbox-mbox-file t 'nomesg))
+ (nnmail-write-region 1 1 nnmbox-mbox-file t 'nomesg))
(if (and nnmbox-mbox-buffer
(buffer-name nnmbox-mbox-buffer)
(save-excursion
(nnmh-possibly-create-directory group)
(condition-case ()
(progn
- (write-region
+ (nnmail-write-region
(point-min) (point-max)
(concat nnmh-current-directory (int-to-string article))
nil (if (nnheader-be-verbose 5) nil 'nomesg))
;; It was already saved, so we just make a hard link.
(funcall nnmail-crosspost-link-function first file t)
;; Save the article.
- (write-region (point-min) (point-max) file nil nil)
+ (nnmail-write-region (point-min) (point-max) file nil nil)
(setq first file)))
(setq ga (cdr ga))))
group-art))
(insert ";; Gnus article active file for " group "\n\n")
(insert "(setq nnmh-newsgroup-articles '")
(insert (prin1-to-string articles) ")\n")
- (write-region (point-min) (point-max) nnmh-file nil 'nomesg)
+ (nnmail-write-region (point-min) (point-max) nnmh-file nil 'nomesg)
(kill-buffer (current-buffer)))))
(defun nnmh-deletable-article-p (group article)
(car group-num)
nnml-directory)))))))
(setq path (concat gpath (int-to-string (cdr group-num)))))
- (unless nnml-article-file-alist
- (setq nnml-article-file-alist
- (nnheader-article-to-file-alist nnml-current-directory)))
- (when (setq file (cdr (assq id nnml-article-file-alist)))
- (setq path (concat nnml-current-directory file))))
+ (setq path (nnml-article-to-file id)))
(cond
((not path)
(nnheader-report 'nnml "No such article: %s" id))
(nnml-possibly-create-directory group)
(let ((chars (nnmail-insert-lines))
(art (concat (int-to-string article) "\t"))
- headers)
+ headers file)
(when (condition-case ()
(progn
- (write-region
+ (nnmail-write-region
(point-min) (point-max)
- (concat nnml-current-directory (int-to-string article))
+ (setq file (concat nnml-current-directory
+ (int-to-string article)))
nil (if (nnheader-be-verbose 5) nil 'nomesg))
t)
(error nil))
(nnmail-save-active nnml-group-alist nnml-active-file)
t))))
+(deffoo nnml-set-status (article name value &optional group server)
+ (nnml-possibly-change-directory group server)
+ (let ((file (nnml-article-to-file article)))
+ (cond
+ ((not (file-exists-p file))
+ (nnheader-report 'nnml "File %s does not exist" file))
+ (t
+ (nnheader-temp-write file
+ (nnheader-insert-file-contents-literally file)
+ (nnmail-replace-status name value))
+ t))))
+
\f
;;; Internal functions.
+(defun nnml-article-to-file (article)
+ (unless nnml-article-file-alist
+ (setq nnml-article-file-alist
+ (nnheader-article-to-file-alist nnml-current-directory)))
+ (let (file)
+ (when (setq file (cdr (assq article nnml-article-file-alist)))
+ (concat nnml-current-directory file))))
+
(defun nnml-deletable-article-p (group article)
"Say whether ARTICLE in GROUP can be deleted."
(let (file path)
;; It was already saved, so we just make a hard link.
(funcall nnmail-crosspost-link-function first file t)
;; Save the article.
- (write-region (point-min) (point-max) file nil
- (if (nnheader-be-verbose 5) nil 'nomesg))
+ (nnmail-write-region (point-min) (point-max) file nil
+ (if (nnheader-be-verbose 5) nil 'nomesg))
(setq first file)))
(setq ga (cdr ga))))
;; Generate a nov line for this article. We generate the nov
(while nnml-nov-buffer-alist
(when (buffer-name (cdar nnml-nov-buffer-alist))
(set-buffer (cdar nnml-nov-buffer-alist))
- (and (buffer-modified-p)
- (write-region
- 1 (point-max) (buffer-file-name) nil 'nomesg))
+ (when (buffer-modified-p)
+ (nnmail-write-region 1 (point-max) (buffer-file-name) nil 'nomesg))
(set-buffer-modified-p nil)
(kill-buffer (current-buffer)))
(setq nnml-nov-buffer-alist (cdr nnml-nov-buffer-alist)))))
(setq files (cdr files)))
(save-excursion
(set-buffer nov-buffer)
- (write-region 1 (point-max) (expand-file-name nov) nil
- 'nomesg)
+ (nnmail-write-region 1 (point-max) nov nil 'nomesg)
(kill-buffer (current-buffer))))))
(defun nnml-nov-delete-article (group article)
;;; Code:
(require 'nnheader)
-(eval-when-compile (require 'cl))
+(require 'cl)
(defvar nnoo-definition-alist nil)
(defvar nnoo-state-alist nil)
(buffer-name nntp-server-buffer)))
(defmacro nnoo-define-basics (backend)
+ "Define `close-server', `server-opened' and `status-message'."
`(eval-and-compile
(nnoo-define-basics-1 ',backend)))
(server &optional defs)
(nnoo-change-server ',backend server defs))))
+(defmacro nnoo-define-skeleton (backend)
+ "Define all required backend functions for BACKEND.
+All functions will return nil and report an error."
+ `(eval-and-compile
+ (nnoo-define-skeleton-1 ',backend)))
+
+(defun nnoo-define-skeleton-1 (backend)
+ (let ((functions '(retrieve-headers
+ request-close server-opened request-article
+ open-group request-group close-group
+ request-list request-post))
+ function)
+ (while (setq function (pop functions))
+ (eval `(deffoo ,(nnoo-symbol backend function)
+ (&optional server)
+ (nnheader-report ',backend ,(format "%s-%s not implemented"
+ backend function)))))))
(provide 'nnoo)
;;; nnoo.el ends here.
;; require one newline at the end.
(or (= (preceding-char) ?\n)
(insert ?\n))
- (when (and news
- (equal kind "mail")
- (or (mail-fetch-field "cc")
- (mail-fetch-field "to")))
- (message-insert-courtesy-copy))
(let ((case-fold-search t))
;; Change header-delimiter to be what sendmail expects.
(goto-char (point-min))
(copy-to-buffer nntp-server-buffer (point-min) (point-max))
'headers))))
+(deffoo nntp-retrieve-groups (groups &optional server)
+ "Retrieve group info on GROUPS."
+ (nntp-possibly-change-group nil server)
+ (save-excursion
+ (set-buffer nntp-server-buffer)
+ ;; The first time this is run, this variable is `try'. So we
+ ;; try.
+ (when (eq nntp-server-list-active-group 'try)
+ (nntp-try-list-active (car groups)))
+ (erase-buffer)
+ (let ((count 0)
+ (received 0)
+ (last-point (point-min))
+ (command (if nntp-server-list-active-group "LIST ACTIVE" "GROUP")))
+ (while groups
+ ;; Send the command to the server.
+ (nntp-send-command nil command (car groups))
+ (setq groups (cdr groups))
+ (setq count (1+ count))
+ ;; Every 400 requests we have to read the stream in
+ ;; order to avoid deadlocks.
+ (when (or (null groups) ;All requests have been sent.
+ (zerop (% count nntp-maximum-request)))
+ (nntp-accept-response)
+ (while (progn
+ (goto-char last-point)
+ ;; Count replies.
+ (while (re-search-forward "^[0-9]" nil t)
+ (setq received (1+ received)))
+ (setq last-point (point))
+ (< received count))
+ (nntp-accept-response))))
+
+ ;; Wait for the reply from the final command.
+ (when nntp-server-list-active-group
+ (goto-char (point-max))
+ (re-search-backward "^[0-9]" nil t)
+ (when (looking-at "^[23]")
+ (while (progn
+ (goto-char (- (point-max) 3))
+ (not (looking-at "^\\.\r?\n")))
+ (nntp-accept-response))))
+
+ ;; Now all replies are received. We remove CRs.
+ (goto-char (point-min))
+ (while (search-forward "\r" nil t)
+ (replace-match "" t t))
+
+ (if (not nntp-server-list-active-group)
+ 'group
+ ;; We have read active entries, so we just delete the
+ ;; superfluos gunk.
+ (goto-char (point-min))
+ (while (re-search-forward "^[.2-5]" nil t)
+ (delete-region (match-beginning 0)
+ (progn (forward-line 1) (point))))
+ 'active))))
+
+ (defun nntp-try-list-active (group)
+ (nntp-list-active-group group)
+ (save-excursion
+ (set-buffer nntp-server-buffer)
+ (goto-char (point-min))
+ (cond ((looking-at "5[0-9]+")
+ (setq nntp-server-list-active-group nil))
+ (t
+ (setq nntp-server-list-active-group t)))))
+
+(deffoo nntp-list-active-group (group &optional server)
+ "Return the active info on GROUP (which can be a regexp."
+ (nntp-possibly-change-group group server)
+ (nntp-send-command "^.*\r?\n" "LIST ACTIVE" group))
+
(deffoo nntp-request-article (article &optional group server buffer command)
(nntp-possibly-change-group group server)
(when (nntp-send-command-and-decode
(deffoo nntp-request-newgroups (date &optional server)
(nntp-possibly-change-group nil server)
- (let* ((date (timezone-parse-date date))
- (time-string
- (format "%s%02d%02d %s%s%s"
- (substring (aref date 0) 2) (string-to-int (aref date 1))
- (string-to-int (aref date 2)) (substring (aref date 3) 0 2)
- (substring
- (aref date 3) 3 5) (substring (aref date 3) 6 8))))
- (prog1
- (nntp-send-command "^\\.\r?\n" "NEWGROUPS" time-string)
- (nntp-decode-text))))
+ (save-excursion
+ (set-buffer nntp-server-buffer)
+ (let* ((date (timezone-parse-date date))
+ (time-string
+ (format "%s%02d%02d %s%s%s"
+ (substring (aref date 0) 2) (string-to-int (aref date 1))
+ (string-to-int (aref date 2)) (substring (aref date 3) 0 2)
+ (substring
+ (aref date 3) 3 5) (substring (aref date 3) 6 8))))
+ (prog1
+ (nntp-send-command "^\\.\r?\n" "NEWGROUPS" time-string)
+ (nntp-decode-text)))))
(deffoo nntp-request-post (&optional server)
(nntp-possibly-change-group nil server)
+Thu Aug 15 17:59:12 1996 Lars Magne Ingebrigtsen <lars@eyesore.no>
+
+ * gnus.texi (Followups To Yourself): Addition.
+ (Setting Process Marks): Addition.
+ (Process/Prefix): Addition.
+ (Startup Files): Addition.
+ (Mail-To-News Gateways): New.
+
+Wed Aug 14 15:02:14 1996 Lars Magne Ingebrigtsen <lars@eyesore.no>
+
+ * gnus.texi (Home Score File): Fix.
+ (Various Various): New.
+
+Tue Aug 13 10:38:47 1996 Lars Magne Ingebrigtsen <lars@eyesore.no>
+
+ * gnus.texi (Error Messaging): New.
+ (Mail Backend Variables): Fix.
+ (Foreign Groups): Added references.
+ (Sorting Groups): Addition.
+
Sun Aug 11 02:52:37 1996 Lars Magne Ingebrigtsen <lars@eyesore.no>
* gnus.texi (User-Defined Specs): Correction.
@end menu
+
@node Starting Up
@chapter Starting Gnus
@cindex starting up
so the automatic new groups subscription methods become meaningless.
You should always set @code{gnus-check-new-newsgroups} to @code{nil} or
@code{ask-server} if you set this variable to @code{nil} (@pxref{New
-Groups}).
+Groups}). This variable can also be a regular expression. If that's
+the case, remove all groups that do not match this regexp before
+saving. This can be useful in certain obscure situations that involve
+several servers where not all servers support @code{ask-server}.
@vindex gnus-startup-file
The @code{gnus-startup-file} variable says where the startup files are.
The score of the group.
@item ticked
The number of ticked articles in the group.
+@item total
+The total number of articles in the group. Or rather, MAX-NUMBER minus
+MIN-NUMBER.
@item topic
When using the topic minor mode, this variable is bound to the current
topic being inserted.
@item G d
@kindex G d (Group)
@findex gnus-group-make-directory-group
-Make a directory group. You will be prompted for a directory name
-(@code{gnus-group-make-directory-group}).
+Make a directory group (@pxref{Directory Groups}). You will be prompted
+for a directory name (@code{gnus-group-make-directory-group}).
@item G h
@kindex G h (Group)
@findex gnus-group-enter-directory
Read an arbitrary directory as if with were a newsgroup with the
@code{nneething} backend (@code{gnus-group-enter-directory}).
+@xref{Anything Groups}.
@item G f
@kindex G f (Group)
Currently supported types are @code{babyl}, @code{mbox}, @code{digest},
@code{mmdf}, @code{news}, @code{rnews}, @code{clari-briefs}, and
@code{forward}. If you run this command without a prefix, Gnus will
-guess at the file type.
+guess at the file type. @xref{Document Groups}.
@item G DEL
@kindex G DEL (Group)
@kindex G V (Group)
@findex gnus-group-make-empty-virtual
Make a new, fresh, empty @code{nnvirtual} group
-(@code{gnus-group-make-empty-virtual}).
+(@code{gnus-group-make-empty-virtual}). @xref{Virtual Groups}.
@item G v
@kindex G v (Group)
@findex gnus-group-sort-by-alphabet
Sort the group names alphabetically. This is the default.
+@item gnus-group-sort-by-real-name
+@findex gnus-group-sort-by-real-name
+Sort the group alphabetically on the real (unprefixed) group names.
+
@item gnus-group-sort-by-level
@findex gnus-group-sort-by-level
Sort by group level.
@findex gnus-uu-mark-buffer
Mark all articles in the buffer in the order they appear
(@code{gnus-uu-mark-buffer}).
+
+@item M P k
+@kindex M P k (Summary)
+@findex gnus-summary-kill-process-mark
+Push the current process mark set onto the stack and unmark all articles
+(@code{gnus-summary-kill-process-mark}).
+
+@item M P y
+@kindex M P y (Summary)
+@findex gnus-summary-yank-process-mark
+Pop the previous process mark set from the stack and restore it
+(@code{gnus-summary-yank-process-mark}).
+
+@item M P w
+@kindex M P w (Summary)
+@findex gnus-summary-save-process-mark
+Push the current process mark set onto the stack
+(@code{gnus-summary-save-process-mark}).
+
@end table
so. It's certainly preferrable to reading the same articles more than
once.
+Duplicate suppression is not a very subtle instrument. It's more like a
+sledge hammer than anything else. It works in a very simple
+fashion---if you have marked an article as read, it adds this Message-ID
+to a cache. The next time it sees this Message-ID, it will mark the
+article as read the the @samp{M} mark. It doesn't care what group it
+saw the article in.
+
@table @code
@item gnus-suppress-duplicates
@vindex gnus-suppress-duplicates
@cindex incoming mail files
@cindex deleting incoming files
If non-@code{nil}, the mail backends will delete the temporary incoming
-file after splitting mail into the proper groups. This is @code{t} by
+file after splitting mail into the proper groups. This is @code{nil} by
default for reasons of security.
+Since Red Gnus is an alpha release, it is to be expected to lose mail.
+(No Gnus release since (ding) Gnus 0.10 (or something like that) have
+lost mail, I think, but that's not the point.) By not deleting the
+Incoming* files, one can be sure to not lose mail -- if Gnus totally
+whacks out, one can always recover what was lost.
+
+Delete the @file{Incoming*} files at will.
+
@item nnmail-use-long-file-names
@vindex nnmail-use-long-file-names
If non-@code{nil}, the mail backends will use long file and directory
newsgroups.
@menu
-* Directory Groups:: You can read a directory as if it was a newsgroup.
-* Anything Groups:: Dired? Who needs dired?
-* Document Groups:: Single files can be the basis of a group.
-* SOUP:: Reading @sc{SOUP} packets ``offline''.
+* Directory Groups:: You can read a directory as if it was a newsgroup.
+* Anything Groups:: Dired? Who needs dired?
+* Document Groups:: Single files can be the basis of a group.
+* SOUP:: Reading @sc{SOUP} packets ``offline''.
+* Mail-To-News Gateways:: Posting articles via mail-to-news gateways.
@end menu
new & spiffy Gnus mail backend, @code{nndoc} can probably help you with
that. Say you have an old @file{RMAIL} file with mail that you now want
to split into your new @code{nnml} groups. You look at that file using
-@code{nndoc}, set the process mark on all the articles in the buffer
-(@kbd{M P b}, for instance), and then re-spool (@kbd{B r}) using
-@code{nnml}. If all goes well, all the mail in the @file{RMAIL} file is
-now also stored in lots of @code{nnml} directories, and you can delete
-that pesky @file{RMAIL} file. If you have the guts!
+@code{nndoc} (using the @kbd{G f} command in the group buffer
+(@pxref{Foreign Groups})), set the process mark on all the articles in
+the buffer (@kbd{M P b}, for instance), and then re-spool (@kbd{B r})
+using @code{nnml}. If all goes well, all the mail in the @file{RMAIL}
+file is now also stored in lots of @code{nnml} directories, and you can
+delete that pesky @file{RMAIL} file. If you have the guts!
Virtual server variables:
@sc{soup}ed you use the second.
+@node Mail-To-News Gateways
+@subsection Mail-To-News Gateways
+@cindex mail-to-news gateways
+@cindex gateways
+
+If your local @code{nntp} server doesn't allow posting, for some reason
+or other, you can post using one of the numerous mail-to-news gateways.
+The @code{nngateway} backend provides the interface.
+
+Note that you can't read anything from this backend---it can only be
+used to post with.
+
+Server variables:
+
+@table @code
+@item nngateway-address
+@vindex nngateway-address
+This is the address of the mail-to-news gateway.
+
+@item nngateway-header-transformation
+@vindex nngateway-header-transformation
+News headers have often have to be transformed in some odd way or other
+for the mail-to-news gateway to accept it. This variable says what
+transformation should be called, and defaults to
+@code{nngateway-simple-header-transformation}. The function is called
+narrowed to the headers to be transformed and with one parameter---the
+gateway address.
+
+This default function just inserts a new @code{To} header based on the
+@code{Newsgroups} header and the gateway address---an article with this
+@code{Newsgroups} header:
+
+@example
+Newsgroups: alt.religion.emacs
+@end example
+
+will get this @code{From} header inserted:
+
+@example
+To: alt-religion-emacs@@GATEWAY
+@end example
+
+@end table
+
+So, to use this, simply say something like:
+
+@lisp
+(setq gnus-post-method '(nngateway "GATEWAY.ADDRESS"))
+@end lisp
+
+
@node Combined Groups
@section Combined Groups
@enumerate
@item
-@var{(regexp . file-name)}. If the @var{regexp} matches the group name,
+@var{(regexp file-name)}. If the @var{regexp} matches the group name,
the @var{file-name} will will be used as the home score file.
@item
@lisp
(setq gnus-home-score-file
;; All groups that match the regexp "\\.emacs"
- '("\\.emacs" . "emacs.SCORE")
+ '("\\.emacs" "emacs.SCORE")
;; All the comp groups in one score file
- ("^comp" . "comp.SCORE"))
+ ("^comp" "comp.SCORE"))
@end lisp
@vindex gnus-home-adapt-file
These two functions are both primarily meant to be used in hooks like
@code{message-sent-hook}.
+If you look closely at your own @code{Message-ID}, you'll notice that
+the first two or three characters are always the same. Here's two of
+mine:
+
+@example
+<x6u3u47icf.fsf@@eyesore.no>
+<x6sp9o7ibw.fsf@@eyesore.no>
+@end example
+
+So ``my'' ident on this machine is @samp{x6}. This can be
+exploited---the following rule will raise the score on all followups to
+myself:
+
+@lisp
+("references"
+ "<x6[0-9a-z]+\\.fsf@.*eyesore.no>" 1000 nil r)
+@end lisp
+
+Whether it's the first two or first three characters that are ``yours''
+is system-dependent.
+
+
@node Scoring Tips
@section Scoring Tips
@cindex scoring tips
Quite simple, really, but it needs to be made clear so that surprises
are avoided.
+Commands that react to the process mark will push the current list of
+process marked articles onto a stack and will then clear all process
+marked articles. You can restore the previous configuration with the
+@kbd{M P y} command (@pxref{Setting Process Marks}).
+
@vindex gnus-summary-goto-unread
One thing that seems to shock & horrify lots of people is that, for
instance, @kbd{3 d} does exactly the same as @kbd{d} @kbd{d} @kbd{d}.
@table @code
+@item gnus-directory
+@vindex gnus-directory
+All Gnus directories will be initialized from this variable, which
+defaults to the @samp{SAVEDIR} environment variable, or @file{~/News/}
+if that variable isn't set.
+
+@item gnus-default-directory
+@vindex gnus-default-directory
+Not related to the above variable at all---this variable says what the
+default directory of all Gnus buffers should be. If you issue commands
+like @kbd{C-x C-f}, the prompt you'll get starts in the current buffer's
+default directory. If this variable is @code{nil} (which is the
+default), the default directory will be the default directory of the
+buffer you were in when you started Gnus.
+
@item gnus-verbose
@vindex gnus-verbose
This variable is an integer between zero and ten. The higher the value,
@menu
* Required Backend Functions:: Functions that must be implemented.
* Optional Backend Functions:: Functions that need not be implemented.
+* Error Messaging:: How to get messages and report errors.
* Writing New Backends:: Extending old backends.
* Hooking New Backends Into Gnus:: What has to be done on the Gnus end.
@end menu
@end table
+@node Error Messaging
+@subsubsection Error Messaging
+
+@findex nnheader-report
+@findex nnheader-get-report
+The backends should use the function @code{nnheader-report} to report
+error conditions---they should not raise errors when they aren't able to
+perform a request. The first argument to this function is the backend
+symbol, and the rest are interpreted as arguments to @code{format} if
+there are many of them, or just a string if there is one of them.
+This function always returns @code{nil}.
+
+@lisp
+(nnheader-report 'nnchoke "You did something totally bogus")
+
+(nnheader-report 'nnchoke "Could not request group %s" group)
+@end lisp
+
+Gnus, in turn, will call @code{nnheader-get-report} when it gets a
+@code{nil} back from a server, and this function returns the most
+recently reported message for the backend in question. This function
+takes one argument---the server symbol.
+
+Internally, these function access @var{backend}@code{-status-string}, so
+the @code{nnchoke} backend will have its error message stored in
+@code{nnchoke-status-string}.
+
+
@node Writing New Backends
@subsubsection Writing New Backends
-The various backends share many similarities. @code{nnml} is just like
+Many backends are quite similar. @code{nnml} is just like
@code{nnspool}, but it allows you to edit the articles on the server.
@code{nnmh} is just like @code{nnml}, but it doesn't use an active file,
and it doesn't maintain overview databases. @code{nndir} is just like