From 3304290c446e3fd89151ebe599b3c0cea8522329 Mon Sep 17 00:00:00 2001 From: ueno Date: Sat, 4 Dec 1999 16:51:56 +0000 Subject: [PATCH] Importing Gnus v5.8.2. --- lisp/ChangeLog | 181 +++++++++++++ lisp/dgnushack.el | 1 + lisp/gnus-art.el | 12 +- lisp/gnus-cache.el | 1 + lisp/gnus-int.el | 15 +- lisp/gnus-msg.el | 14 +- lisp/gnus-start.el | 7 +- lisp/gnus-sum.el | 50 +++- lisp/gnus-uu.el | 2 +- lisp/gnus.el | 6 +- lisp/mail-source.el | 84 ++++-- lisp/message.el | 90 +++++-- lisp/mm-bodies.el | 35 ++- lisp/mm-util.el | 18 ++ lisp/mm-view.el | 8 +- lisp/mml.el | 27 +- lisp/nnfolder.el | 10 +- lisp/nnmh.el | 2 +- lisp/nnslashdot.el | 147 ++++++----- lisp/nntp.el | 7 + lisp/nnultimate.el | 18 +- lisp/nnweb.el | 79 +++--- lisp/rfc2047.el | 9 +- texi/ChangeLog | 16 ++ texi/gnus.texi | 137 ++++++++-- texi/gnusmail.texi | 708 +++++++++++++++++++++++++++++++++++++++++++++++++++ texi/message.texi | 4 +- 27 files changed, 1451 insertions(+), 237 deletions(-) create mode 100644 texi/gnusmail.texi diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 46c0f9c..16f290a 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,3 +1,184 @@ +Fri Dec 3 20:34:11 1999 Lars Magne Ingebrigtsen + + * gnus.el: Pterodactyl Gnus v5.8.2 is released. + +Fri Dec 3 20:09:41 1999 Lars Magne Ingebrigtsen + + * gnus.el: Pterodactyl Gnus v5.8.1 is released. + +1999-11-11 Hrvoje Niksic + + * mml.el (mml-insert-tag): Don't close the tag. + (mml-insert-empty-tag): New function. + (mml-attach-file): Use mml-insert-empty-tag instead of + mml-insert-tag. + (mml-attach-buffer): Ditto. + (mml-attach-external): Ditto. + (mml-insert-multipart): Ditto. + +1999-12-03 08:49:53 Shenghuo ZHU + + * nnfolder.el (nnfolder-request-article): Return -1 if not find + the article number. + +1999-12-03 01:12:41 Shenghuo ZHU + + * gnus.el (gnus-find-method-for-group): The method of a new group + is not the native one. + +1999-12-03 01:26:55 Lars Magne Ingebrigtsen + + * gnus-art.el (gnus-button-embedded-url): Always call browse-url. + +1999-12-02 18:00:15 Lars Magne Ingebrigtsen + + * nnultimate.el (nnultimate-retrieve-headers): Use + mm-with-unibyte-current-buffer. + (nnultimate-request-article): Ditto. + +1999-12-02 14:57:46 Shenghuo ZHU + + * nntp.el (nntp-retrieve-groups): Set to process buffer. + +1999-12-02 11:14:50 Shenghuo ZHU + + * mm-util.el (mm-with-unibyte-current-buffer): New macro. + * nnweb.el (nnweb-retrieve-headers): Use it. + (nnweb-request-article): Use it. + + * nnweb.el (nnweb-dejanews-create-mapping): Set a default date in + case matching failed. + +1999-12-02 John Wiegley + + * mail-source.el (mail-source-keyword-map): Add backslash to + Delete-flag. + +1999-12-02 07:24:35 Lars Magne Ingebrigtsen + + * gnus-sum.el (gnus-group-charset-alist): Default nnweb groups to + Latin-1. + (gnus-group-charset-alist): No, don't. + + * nnweb.el (nnweb-init): Make the buffer unibyte. + +1999-12-01 23:02:48 Shenghuo ZHU + + * mail-source.el (mail-source-set-common-1): Fix to get the + default value. + +1999-12-02 00:27:46 Lars Magne Ingebrigtsen + + * nnslashdot.el (nnslashdot-read-groups): Unibyte. + + * nnultimate.el (nnultimate-request-list): Use unibyte. + + * gnus-uu.el (gnus-uu-grab-articles): Bind + gnus-display-mime-function to nil. + + * message.el (message-send-mail-with-sendmail): Use the + user-mail-address variable. + + * gnus-art.el (gnus-ignored-headers): More headers. + + * message.el (message-shorten-1): Use list. + +1999-12-01 21:59:36 Lars Magne Ingebrigtsen + + * gnus-msg.el (gnus-configure-posting-styles): Ignore nil + signatures. + + * nnweb.el (nnweb-dejanews-create-mapping): Get the data. + (nnweb-dejanews-create-mapping): Do the properish date. + +1999-12-01 17:41:21 Shenghuo ZHU + + * mail-source.el (mail-source-common-keyword-map): New variable. + (mail-source-bind-common): New macro. + (mail-source-fetch): Support plugged mail source. + * gnus-int.el (gnus-request-scan): Use them. + +1999-12-01 21:59:36 Lars Magne Ingebrigtsen + + * mm-view.el (mm-inline-message): Check whether charset is a + string. + + * nnslashdot.el (nnslashdot-request-post): Insert

's. + + * message.el (message-mode-map): Changed keystroke for + message-yank-buffer. + +1999-11-26 Hrvoje Niksic + + * message.el (message-shorten-references): Cut references to 31 + elements, then either fold them or shorten them to 988 characters. + (message-shorten-1): New function. + (message-cater-to-broken-inn): New variable. + +1999-12-01 21:47:10 Eric Marsden + + * nnslashdot.el (nnslashdot-lose): New function. + +1999-12-01 21:08:48 Lars Magne Ingebrigtsen + + * mm-view.el (mm-inline-message): Not the right type of charset is + being fetched here. Let the group charset rule. + (mm-inline-message): Ignore us-ascii. + +1999-11-24 Carsten Leonhardt + + * mail-source.el (mail-source-fetch-maildir): work around the + ommitted "file-regular-p" in efs/ange-ftp + +1999-12-01 19:59:25 Lars Magne Ingebrigtsen + + * mml.el (mml-generate-mime-1): Don't insert extra empty line. + (mml-generate-mime-1): Use the encoding param. + + * gnus-sum.el (gnus-summary-show-article): Don't bind gnus-visual. + + * gnus-cache.el (gnus-cache-possibly-enter-article): Require + gnus-art before binding its variables. + + * gnus-art.el (gnus-article-prepare-display): Run the prepare + after the MIME. + +1999-12-01 19:48:14 Rupa Schomaker + + * message.el (message-clone-locals): Use it. + + * gnus-msg.el (gnus-configure-posting-styles): Make + user-mail-address local. + +1999-11-20 Simon Josefsson + + * gnus-start.el (gnus-get-unread-articles): Scan each method only + once. + +1999-12-01 17:37:18 Lars Magne Ingebrigtsen + + * message.el (message-generate-new-buffer-clone-locals): Use varstr. + (message-clone-locals): Ditto. + + * gnus-sum.el (gnus-summary-enter-digest-group): Have the digest + group inherit reply-to or from. + +1999-12-01 13:04:09 Shenghuo ZHU + + * gnus-sum.el (gnus-summary-show-article): Support numbered ARG + for charset. + (gnus-summary-show-article-charset-alist): New variable. + + * mm-bodies.el (mm-decode-string): Support gnus-all and + gnus-unknown. + (mm-decode-body): Ditto. + * rfc2047.el (rfc2047-decode): Ditto. + +1999-12-01 17:37:18 Lars Magne Ingebrigtsen + + * mail-source.el (mail-source-delete-incoming): Change default to + t. + Wed Dec 1 16:31:31 1999 Lars Magne Ingebrigtsen * gnus.el: Pterodactyl Gnus v0.99 is released. diff --git a/lisp/dgnushack.el b/lisp/dgnushack.el index 5af2e61..76b8c88 100644 --- a/lisp/dgnushack.el +++ b/lisp/dgnushack.el @@ -53,6 +53,7 @@ (defvar srcdir (or (getenv "srcdir") ".")) (push srcdir load-path) +;(push "/usr/share/emacs/site-lisp" load-path) (load (expand-file-name "lpath.el" srcdir) nil t) (defalias 'device-sound-enabled-p 'ignore) diff --git a/lisp/gnus-art.el b/lisp/gnus-art.el index 809169d..0c01793 100644 --- a/lisp/gnus-art.el +++ b/lisp/gnus-art.el @@ -127,7 +127,8 @@ "^X-Sun-Charset:" "^X-Accept-Language:" "^X-Envelope-Sender:" "^List-[A-Za-z]+:" "^X-Listprocessor-Version:" "^X-Received:" "^X-Distribute:" "^X-Sequence:" "^X-Juno-Line-Breaks:" - "^X-Notes-Item:" "^X-MS-TNEF-Correlator:" "^x-uunet-gateway:") + "^X-Notes-Item:" "^X-MS-TNEF-Correlator:" "^x-uunet-gateway:" + "^X-Received:" "^Content-length:" "X-precedence:") "*All headers that start with this regexp will be hidden. This variable can also be a list of regexps of headers to be ignored. If `gnus-visible-headers' is non-nil, this variable will be ignored." @@ -2724,9 +2725,9 @@ If ALL-HEADERS is non-nil, no headers are hidden." (setq buffer-read-only nil gnus-article-wash-types nil) (gnus-run-hooks 'gnus-tmp-internal-hook) - (gnus-run-hooks 'gnus-article-prepare-hook) (when gnus-display-mime-function - (funcall gnus-display-mime-function)))) + (funcall gnus-display-mime-function)) + (gnus-run-hooks 'gnus-article-prepare-hook))) ;;; ;;; Gnus MIME viewing functions @@ -4390,10 +4391,7 @@ forbidden in URL encoding." (defun gnus-button-embedded-url (address) "Browse ADDRESS." - ;; In Emacs 20, `browse-url-browser-function' may be an alist. - (if (listp browse-url-browser-function) - (browse-url (gnus-strip-whitespace address)) - (funcall browse-url-browser-function (gnus-strip-whitespace address)))) + (browse-url (gnus-strip-whitespace address))) ;;; Next/prev buttons in the article buffer. diff --git a/lisp/gnus-cache.el b/lisp/gnus-cache.el index fcb8bb5..0cc99b5 100644 --- a/lisp/gnus-cache.el +++ b/lisp/gnus-cache.el @@ -175,6 +175,7 @@ it's not cached." t ; The article already is saved. (save-excursion (set-buffer nntp-server-buffer) + (require 'gnus-art) (let ((gnus-use-cache nil) (gnus-article-decode-hook nil)) (gnus-request-article-this-buffer number group)) diff --git a/lisp/gnus-int.el b/lisp/gnus-int.el index bc94a5c..5b6264d 100644 --- a/lisp/gnus-int.el +++ b/lisp/gnus-int.el @@ -413,13 +413,14 @@ If BUFFER, insert the article in that group." (defun gnus-request-scan (group gnus-command-method) "Request a SCAN being performed in GROUP from GNUS-COMMAND-METHOD. If GROUP is nil, all groups on GNUS-COMMAND-METHOD are scanned." - (when gnus-plugged - (let ((gnus-command-method - (if group (gnus-find-method-for-group group) gnus-command-method)) - (gnus-inhibit-demon t)) - (funcall (gnus-get-function gnus-command-method 'request-scan) - (and group (gnus-group-real-name group)) - (nth 1 gnus-command-method))))) + (let ((gnus-command-method + (if group (gnus-find-method-for-group group) gnus-command-method)) + (gnus-inhibit-demon t) + (mail-source-plugged gnus-plugged)) + (if (or gnus-plugged (not (gnus-agent-method-p gnus-command-method))) + (funcall (gnus-get-function gnus-command-method 'request-scan) + (and group (gnus-group-real-name group)) + (nth 1 gnus-command-method))))) (defsubst gnus-request-update-info (info gnus-command-method) "Request that GNUS-COMMAND-METHOD update INFO." diff --git a/lisp/gnus-msg.el b/lisp/gnus-msg.el index 4bd55a6..55fdbbf 100644 --- a/lisp/gnus-msg.el +++ b/lisp/gnus-msg.el @@ -1198,11 +1198,13 @@ this is a reply." ((eq 'signature (car result)) (set (make-local-variable 'message-signature) nil) (set (make-local-variable 'message-signature-file) nil) - `(lambda () - (save-excursion - (let ((message-signature ,(cdr result))) - (when message-signature - (message-insert-signature)))))) + (if (not (cdr result)) + 'ignore + `(lambda () + (save-excursion + (let ((message-signature ,(cdr result))) + (when message-signature + (message-insert-signature))))))) (t (let ((header (if (symbolp (car result)) @@ -1216,6 +1218,8 @@ this is a reply." (when (or name address) (add-hook 'message-setup-hook `(lambda () + (set (make-local-variable 'user-mail-address) + ,(or (cdr address) user-mail-address)) (let ((user-full-name ,(or (cdr name) (user-full-name))) (user-mail-address ,(or (cdr address) user-mail-address))) diff --git a/lisp/gnus-start.el b/lisp/gnus-start.el index ed5cd3a..7358bd6 100644 --- a/lisp/gnus-start.el +++ b/lisp/gnus-start.el @@ -1498,7 +1498,7 @@ newsgroup." gnus-activate-foreign-newsgroups) (t 0)) level)) - info group active method retrievegroups) + scanned-methods info group active method retrievegroups) (gnus-message 5 "Checking new news...") (while newsrc @@ -1542,7 +1542,10 @@ newsgroup." (setcdr (assoc method retrievegroups) (cons group (cdr (assoc method retrievegroups)))) (push (list method group) retrievegroups)) - (setq active (gnus-activate-group group 'scan)) + (if (member method scanned-methods) + (setq active (gnus-activate-group group)) + (setq active (gnus-activate-group group 'scan)) + (push method scanned-methods)) (inline (gnus-close-group group)))))) ;; Get the number of unread articles in the group. diff --git a/lisp/gnus-sum.el b/lisp/gnus-sum.el index dfc7dd9..2e9f183 100644 --- a/lisp/gnus-sum.el +++ b/lisp/gnus-sum.el @@ -849,6 +849,17 @@ This variable uses the same syntax as `gnus-emphasis-alist'." gnus-emphasis-highlight-words))))) :group 'gnus-summary-visual) +(defcustom gnus-summary-show-article-charset-alist + nil + "Alist of number and charset. +The article will be shown with the charset corresponding to the +numbered argument. +For example: ((1 . cn-gb-2312) (2 . big5))." + :type '(repeat (cons (number :tag "Argument" 1) + (symbol :tag "Charset"))) + :group 'gnus-charset) + + ;;; Internal variables (defvar gnus-article-mime-handles nil) @@ -6801,8 +6812,14 @@ to guess what the document format is." (list (cons 'save-article-group ogroup)))) (case-fold-search t) (buf (current-buffer)) - dig) + dig to-address) (save-excursion + (set-buffer gnus-original-article-buffer) + ;; Have the digest group inherit the main mail address of + ;; the parent article. + (when (setq to-address (or (message-fetch-field "reply-to") + (message-fetch-field "from"))) + (setq params (append (list (cons 'to-address to-address))))) (setq dig (nnheader-set-temp-buffer " *gnus digest buffer*")) (insert-buffer-substring gnus-original-article-buffer) ;; Remove lines that may lead nndoc to misinterpret the @@ -7110,12 +7127,23 @@ to save in." (defun gnus-summary-show-article (&optional arg) "Force re-fetching of the current article. -If ARG (the prefix) is non-nil, show the raw article without any -article massaging functions being run." +If ARG (the prefix) is a number, show the article with the charset +defined in `gnus-summary-show-article-charset-alist', or the charset +inputed. +If ARG (the prefix) is non-nil and not a number, show the raw article +without any article massaging functions being run." (interactive "P") - (if (not arg) - ;; Select the article the normal way. - (gnus-summary-select-article nil 'force) + (cond + ((numberp arg) + (let ((gnus-newsgroup-charset + (or (cdr (assq arg gnus-summary-show-article-charset-alist)) + (read-coding-system "Charset: "))) + (gnus-newsgroup-ignored-charsets 'gnus-all)) + (gnus-summary-select-article nil 'force))) + ((not arg) + ;; Select the article the normal way. + (gnus-summary-select-article nil 'force)) + (t ;; We have to require this here to make sure that the following ;; dynamic binding isn't shadowed by autoloading. (require 'gnus-async) @@ -7125,14 +7153,13 @@ article massaging functions being run." gnus-article-prepare-hook gnus-article-decode-hook gnus-display-mime-function - gnus-break-pages - gnus-visual) + gnus-break-pages) ;; Destroy any MIME parts. (when (gnus-buffer-live-p gnus-article-buffer) (save-excursion (set-buffer gnus-article-buffer) (mm-destroy-parts gnus-article-mime-handles))) - (gnus-summary-select-article nil 'force))) + (gnus-summary-select-article nil 'force)))) (gnus-summary-goto-subject gnus-current-article) (gnus-summary-position-point)) @@ -9375,10 +9402,9 @@ If REVERSE, save parts that do not match TYPE." (setq gnus-newsgroup-charset (or gnus-newsgroup-ephemeral-charset (and gnus-newsgroup-name - (or (gnus-group-find-parameter gnus-newsgroup-name - 'charset) + (or (gnus-group-find-parameter gnus-newsgroup-name 'charset) (let ((alist gnus-group-charset-alist) - elem (charset nil)) + elem charset) (while (setq elem (pop alist)) (when (and name (string-match (car elem) name)) diff --git a/lisp/gnus-uu.el b/lisp/gnus-uu.el index b68af25..72845ef 100644 --- a/lisp/gnus-uu.el +++ b/lisp/gnus-uu.el @@ -1210,7 +1210,7 @@ didn't work, and overwrite existing files. Otherwise, ask each time." (gnus-inhibit-treatment t) has-been-begin article result-file result-files process-state gnus-summary-display-article-function - gnus-article-prepare-hook + gnus-article-prepare-hook gnus-display-mime-function article-series files) (while (and articles diff --git a/lisp/gnus.el b/lisp/gnus.el index 057b9b0..c9d9050 100644 --- a/lisp/gnus.el +++ b/lisp/gnus.el @@ -260,10 +260,10 @@ is restarted, and sometimes reloaded." :link '(custom-manual "(gnus)Exiting Gnus") :group 'gnus) -(defconst gnus-version-number "0.99" +(defconst gnus-version-number "5.8.2" "Version number for this version of Gnus.") -(defconst gnus-version (format "Pterodactyl Gnus v%s" gnus-version-number) +(defconst gnus-version (format "Gnus v%s" gnus-version-number) "Version string for this version of Gnus.") (defcustom gnus-inhibit-startup-message nil @@ -2743,6 +2743,8 @@ If NEWSGROUP is nil, return the global kill file name instead." (or gnus-override-method (and (not group) gnus-select-method) + (and (not (gnus-group-entry group)) ;; a new group + (gnus-group-name-to-method group)) (let ((info (or info (gnus-get-info group))) method) (if (or (not info) diff --git a/lisp/mail-source.el b/lisp/mail-source.el index 96cbc14..8d73bc6 100644 --- a/lisp/mail-source.el +++ b/lisp/mail-source.el @@ -55,7 +55,7 @@ This variable is a list of mail source specifiers." :group 'mail-source :type 'integer) -(defcustom mail-source-delete-incoming nil +(defcustom mail-source-delete-incoming t "*If non-nil, delete incoming files after handling." :group 'mail-source :type 'boolean) @@ -66,6 +66,11 @@ This variable is a list of mail source specifiers." "A dynamically bound string that says what the current mail source is.") (eval-and-compile + (defvar mail-source-common-keyword-map + '((:plugged)) + "Mapping from keywords to default values. +Common keywords should be listed here.") + (defvar mail-source-keyword-map '((file (:prescript) @@ -100,7 +105,7 @@ This variable is a list of mail source specifiers." (:password) (:mailbox "INBOX") (:predicate "UNSEEN UNDELETED") - (:fetchflag "\Deleted") + (:fetchflag "\\Deleted") (:dontexpunge)) (webmail (:subtype hotmail) @@ -121,6 +126,8 @@ All keywords that can be used must be listed here.")) (defvar mail-source-password-cache nil) +(defvar mail-source-plugged t) + ;;; Functions (eval-and-compile @@ -168,6 +175,39 @@ the `mail-source-keyword-map' variable." (mail-source-value value) (mail-source-value (cadr default))))))) +(eval-and-compile + (defun mail-source-bind-common-1 () + (let* ((defaults mail-source-common-keyword-map) + default bind) + (while (setq default (pop defaults)) + (push (list (mail-source-strip-keyword (car default)) + nil) + bind)) + bind))) + +(defun mail-source-set-common-1 (source) + (let* ((type (pop source)) + (defaults mail-source-common-keyword-map) + (defaults-1 (cdr (assq type mail-source-keyword-map))) + default value keyword) + (while (setq default (pop defaults)) + (set (mail-source-strip-keyword (setq keyword (car default))) + (if (setq value (plist-get source keyword)) + (mail-source-value value) + (if (setq value (assq keyword defaults-1)) + (mail-source-value (cadr value)) + (mail-source-value (cadr default)))))))) + +(defmacro mail-source-bind-common (source &rest body) + "Return a `let' form that binds all common variables. +See `mail-source-bind'." + `(let ,(mail-source-bind-common-1) + (mail-source-set-common-1 source) + ,@body)) + +(put 'mail-source-bind-common 'lisp-indent-function 1) +(put 'mail-source-bind-common 'edebug-form-spec '(form body)) + (defun mail-source-value (value) "Return the value of VALUE." (cond @@ -187,24 +227,26 @@ the `mail-source-keyword-map' variable." CALLBACK will be called with the name of the file where (some of) the mail from SOURCE is put. Return the number of files that were found." - (save-excursion - (let ((function (cadr (assq (car source) mail-source-fetcher-alist))) - (found 0)) - (unless function - (error "%S is an invalid mail source specification" source)) - ;; If there's anything in the crash box, we do it first. - (when (file-exists-p mail-source-crash-box) - (message "Processing mail from %s..." mail-source-crash-box) - (setq found (mail-source-callback - callback mail-source-crash-box))) - (+ found - (condition-case err - (funcall function source callback) - (error - (unless (yes-or-no-p - (format "Mail source error (%s). Continue? " err)) - (error "Cannot get new mail.")) - 0)))))) + (mail-source-bind-common source + (if (or mail-source-plugged plugged) + (save-excursion + (let ((function (cadr (assq (car source) mail-source-fetcher-alist))) + (found 0)) + (unless function + (error "%S is an invalid mail source specification" source)) + ;; If there's anything in the crash box, we do it first. + (when (file-exists-p mail-source-crash-box) + (message "Processing mail from %s..." mail-source-crash-box) + (setq found (mail-source-callback + callback mail-source-crash-box))) + (+ found + (condition-case err + (funcall function source callback) + (error + (unless (yes-or-no-p + (format "Mail source error (%s). Continue? " err)) + (error "Cannot get new mail.")) + 0)))))))) (defun mail-source-make-complex-temp-name (prefix) (let ((newname (make-temp-name prefix)) @@ -439,7 +481,7 @@ If ARGS, PROMPT is used as an argument to `format'." (let ((found 0) (mail-source-string (format "maildir:%s" path))) (dolist (file (directory-files path t)) - (when (and (file-regular-p file) + (when (and (not (file-directory-p file)) (not (if function (funcall function file mail-source-crash-box) (rename-file file mail-source-crash-box)))) diff --git a/lisp/message.el b/lisp/message.el index 4929c84..a0642c1 100644 --- a/lisp/message.el +++ b/lisp/message.el @@ -384,10 +384,9 @@ always query the user whether to use the value. If it is the symbol (const use) (const ask))) -;; stuff relating to broken sendmail in MMDF (defcustom message-sendmail-f-is-evil nil - "*Non-nil means that \"-f username\" should not be added to the sendmail -command line, because it is even more evil than leaving it out." + "*Non-nil means that \"-f username\" should not be added to the sendmail command line. +Doing so would be even more evil than leaving it out." :group 'message-sending :type 'boolean) @@ -407,6 +406,11 @@ might set this variable to '(\"-f\" \"you@some.where\")." :group 'message-sending :type '(repeat string)) +(defvar message-cater-to-broken-inn t + "Non-nil means Gnus should not fold the `References' header. +Folding `References' makes ancient versions of INN create incorrect +NOV lines.") + (defvar gnus-post-method) (defvar gnus-select-method) (defcustom message-post-method @@ -1300,7 +1304,7 @@ Point is left at the beginning of the narrowed-to region." (define-key message-mode-map "\C-c\C-n" 'message-insert-newsgroups) (define-key message-mode-map "\C-c\C-y" 'message-yank-original) - (define-key message-mode-map "\C-c\C-Y" 'message-yank-buffer) + (define-key message-mode-map "\C-c\M-\C-y" 'message-yank-buffer) (define-key message-mode-map "\C-c\C-q" 'message-fill-yanked-message) (define-key message-mode-map "\C-c\C-w" 'message-insert-signature) (define-key message-mode-map "\C-c\M-h" 'message-insert-headers) @@ -2173,7 +2177,7 @@ the user from the mailer." (defun message-send-mail-with-sendmail () "Send off the prepared buffer with sendmail." (let ((errbuf (if message-interactive - (generate-new-buffer " sendmail errors") + (message-generate-new-buffer-clone-locals " sendmail errors") 0)) resend-to-addresses delimline) (let ((case-fold-search t)) @@ -2210,7 +2214,10 @@ the user from the mailer." ;; But some systems are more broken with -f, so ;; we'll let users override this. (if (null message-sendmail-f-is-evil) - (list "-f" (user-login-name))) + (list "-f" + (if (null user-mail-address) + (user-login-name) + user-mail-address))) ;; These mean "report errors by mail" ;; and "deliver in background". (if (null message-interactive) '("-oem" "-odb")) @@ -3175,7 +3182,7 @@ Headers already prepared in the buffer are not modified." (defun message-fill-header (header value) (let ((begin (point)) - (fill-column 990) + (fill-column 78) (fill-prefix "\t")) (insert (capitalize (symbol-name header)) ": " @@ -3194,23 +3201,60 @@ Headers already prepared in the buffer are not modified." (replace-match " " t t)) (goto-char (point-max))))) +(defun message-shorten-1 (list cut surplus) + ;; Cut SURPLUS elements out of LIST, beginning with CUTth one. + (setcdr (nthcdr (- cut 2) list) + (nthcdr (+ (- cut 2) surplus 1) list))) + (defun message-shorten-references (header references) - "Limit REFERENCES to be shorter than 988 characters." - (let ((max 988) - (cut 4) + "Trim REFERENCES to be less than 31 Message-ID long, and fold them. +If folding is disallowed, also check that the REFERENCES are less +than 988 characters long, and if they are not, trim them until they are." + (let ((maxcount 31) + (count 0) + (cut 6) refs) (with-temp-buffer (insert references) (goto-char (point-min)) + ;; Cons a list of valid references. (while (re-search-forward "<[^>]+>" nil t) (push (match-string 0) refs)) - (setq refs (nreverse refs)) - (while (> (length (mapconcat 'identity refs " ")) max) - (when (< (length refs) (1+ cut)) - (decf cut)) - (setcdr (nthcdr cut refs) (cddr (nthcdr cut refs))))) - (insert (capitalize (symbol-name header)) ": " - (mapconcat 'identity refs " ") "\n"))) + (setq refs (nreverse refs) + count (length refs))) + + ;; If the list has more than MAXCOUNT elements, trim it by + ;; removing the CUTth element and the required number of + ;; elements that follow. + (when (> count maxcount) + (let ((surplus (- count maxcount))) + (message-shorten-1 refs cut surplus) + (decf count surplus))) + + ;; If folding is disallowed, make sure the total length (including + ;; the spaces between) will be less than MAXSIZE characters. + (when message-cater-to-broken-inn + (let ((maxsize 988) + (totalsize (+ (apply #'+ (mapcar #'length refs)) + (1- count))) + (surplus 0) + (ptr (nthcdr (1- cut) refs))) + ;; Decide how many elements to cut off... + (while (> totalsize maxsize) + (decf totalsize (1+ (length (car ptr)))) + (incf surplus) + (setq ptr (cdr ptr))) + ;; ...and do it. + (when (> surplus 0) + (message-shorten-1 refs cut surplus)))) + + ;; Finally, collect the references back into a string and insert + ;; it into the buffer. + (let ((refstring (mapconcat #'identity refs " "))) + (if message-cater-to-broken-inn + (insert (capitalize (symbol-name header)) ": " + refstring "\n") + (message-fill-header header refstring))))) (defun message-position-point () "Move point to where the user probably wants to find it." @@ -4138,20 +4182,22 @@ regexp varstr." (let ((oldbuf (current-buffer))) (save-excursion (set-buffer (generate-new-buffer name)) - (message-clone-locals oldbuf) + (message-clone-locals oldbuf varstr) (current-buffer)))) -(defun message-clone-locals (buffer) +(defun message-clone-locals (buffer &optional varstr) "Clone the local variables from BUFFER to the current buffer." (let ((locals (save-excursion (set-buffer buffer) (buffer-local-variables))) - (regexp "^gnus\\|^nn\\|^message")) + (regexp "^gnus\\|^nn\\|^message\\|^user-mail-address")) (mapcar (lambda (local) (when (and (consp local) (car local) - (string-match regexp (symbol-name (car local)))) + (string-match regexp (symbol-name (car local))) + (or (null varstr) + (string-match varstr (symbol-name (car local))))) (ignore-errors (set (make-local-variable (car local)) (cdr local))))) @@ -4197,7 +4243,7 @@ regexp varstr." (delete-char 1) (search-forward "\n\n") (setq lines (buffer-substring (point-min) (1- (point)))) - (delete-region (point-min) (point)))))) + (delete-region (point-min) (point)))))) (save-restriction (message-narrow-to-headers-or-head) (message-remove-header "Mime-Version") diff --git a/lisp/mm-bodies.el b/lisp/mm-bodies.el index ad7bbeb..64bcac3 100644 --- a/lisp/mm-bodies.el +++ b/lisp/mm-bodies.el @@ -95,7 +95,7 @@ If no encoding was done, nil is returned." (setq start nil))) charset))))))) -(defun mm-body-encoding (charset) +(defun mm-body-encoding (charset &optional encoding) "Do Content-Transfer-Encoding and return the encoding of the current buffer." (let ((bits (mm-body-7-or-8))) (cond @@ -104,7 +104,8 @@ If no encoding was done, nil is returned." ((eq charset mail-parse-charset) bits) (t - (let ((encoding (or (cdr (assq charset mm-body-charset-encoding-alist)) + (let ((encoding (or encoding + (cdr (assq charset mm-body-charset-encoding-alist)) (mm-qp-or-base64)))) (mm-encode-content-transfer-encoding encoding "text/plain") encoding))))) @@ -179,15 +180,22 @@ If no encoding was done, nil is returned." The characters in CHARSET should then be decoded." (if (stringp charset) (setq charset (intern (downcase charset)))) - (if (or (not charset) (memq charset mail-parse-ignored-charsets)) + (if (or (not charset) + (eq 'gnus-all mail-parse-ignored-charsets) + (memq 'gnus-all mail-parse-ignored-charsets) + (memq charset mail-parse-ignored-charsets)) (setq charset mail-parse-charset)) (save-excursion (when encoding (mm-decode-content-transfer-encoding encoding type)) (when (featurep 'mule) - (let (mule-charset) - (when (and charset - (setq mule-charset (mm-charset-to-coding-system charset)) + (let ((mule-charset (mm-charset-to-coding-system charset))) + (if (and (not mule-charset) + (listp mail-parse-ignored-charsets) + (memq 'gnus-unknown mail-parse-ignored-charsets)) + (setq mule-charset + (mm-charset-to-coding-system mail-parse-charset))) + (when (and charset mule-charset ;; buffer-file-coding-system ;;Article buffer is nil coding system ;;in XEmacs @@ -201,13 +209,20 @@ The characters in CHARSET should then be decoded." "Decode STRING with CHARSET." (if (stringp charset) (setq charset (intern (downcase charset)))) - (if (or (not charset) (memq charset mail-parse-ignored-charsets)) + (if (or (not charset) + (eq 'gnus-all mail-parse-ignored-charsets) + (memq 'gnus-all mail-parse-ignored-charsets) + (memq charset mail-parse-ignored-charsets)) (setq charset mail-parse-charset)) (or (when (featurep 'mule) - (let (mule-charset) - (when (and charset - (setq mule-charset (mm-charset-to-coding-system charset)) + (let ((mule-charset (mm-charset-to-coding-system charset))) + (if (and (not mule-charset) + (listp mail-parse-ignored-charsets) + (memq 'gnus-unknown mail-parse-ignored-charsets)) + (setq mule-charset + (mm-charset-to-coding-system mail-parse-charset))) + (when (and charset mule-charset (mm-multibyte-p) (or (not (eq mule-charset 'ascii)) (setq mule-charset mail-parse-charset))) diff --git a/lisp/mm-util.el b/lisp/mm-util.el index a23a7f6..8006fec 100644 --- a/lisp/mm-util.el +++ b/lisp/mm-util.el @@ -271,6 +271,24 @@ See also `with-temp-file' and `with-output-to-string'." (put 'mm-with-unibyte-buffer 'lisp-indent-function 0) (put 'mm-with-unibyte-buffer 'edebug-form-spec '(body)) +(defmacro mm-with-unibyte-current-buffer (&rest forms) + "Evaluate FORMS there like `progn' in current buffer." + (let ((multibyte (make-symbol "multibyte"))) + `(if (or (string-match "XEmacs\\|Lucid" emacs-version) + (not (fboundp 'set-buffer-multibyte))) + (progn + ,@forms) + (let ((,multibyte (default-value 'enable-multibyte-characters))) + (unwind-protect + (let ((buffer-file-coding-system mm-binary-coding-system) + (coding-system-for-read mm-binary-coding-system) + (coding-system-for-write mm-binary-coding-system)) + (set-buffer-multibyte nil) + ,@forms) + (set-buffer-multibyte ,multibyte)))))) +(put 'mm-with-unibyte-current-buffer 'lisp-indent-function 0) +(put 'mm-with-unibyte-current-buffer 'edebug-form-spec '(body)) + (defun mm-find-charset-region (b e) "Return a list of charsets in the region." (cond diff --git a/lisp/mm-view.el b/lisp/mm-view.el index 25c6773..908fa8a 100644 --- a/lisp/mm-view.el +++ b/lisp/mm-view.el @@ -187,12 +187,18 @@ (charset (mail-content-type-get (mm-handle-type handle) 'charset)) gnus-displaying-mime handles) + (when (and charset + (stringp charset)) + (setq charset (intern (downcase charset))) + (when (eq charset 'us-ascii) + (setq charset nil))) (save-excursion (save-restriction (narrow-to-region b b) (mm-insert-part handle) (let (gnus-article-mime-handles - (gnus-newsgroup-charset (or charset gnus-newsgroup-charset))) + (gnus-newsgroup-charset + (or charset gnus-newsgroup-charset))) (run-hooks 'gnus-article-decode-hook) (gnus-article-prepare-display) (setq handles gnus-article-mime-handles)) diff --git a/lisp/mml.el b/lisp/mml.el index e84e955..9203465 100644 --- a/lisp/mml.el +++ b/lisp/mml.el @@ -241,7 +241,8 @@ called for this message.") (delete-region (+ (match-beginning 0) 2) (+ (match-beginning 0) 3)))))) (setq charset (mm-encode-body)) - (setq encoding (mm-body-encoding charset)) + (setq encoding (mm-body-encoding charset + (cdr (assq 'encoding cont)))) (setq coded (buffer-string))) (mm-with-unibyte-buffer (cond @@ -300,7 +301,6 @@ called for this message.") (let ((mml-boundary (mml-compute-boundary cont))) (insert (format "Content-Type: multipart/%s; boundary=\"%s\"\n" type mml-boundary)) - (insert "\n") (setq cont (cddr cont)) (while cont (insert "\n--" mml-boundary "\n") @@ -654,7 +654,14 @@ called for this message.") (when (string-match "[\"\\~/* \t\n]" value) (setq value (prin1-to-string value))) (insert (format " %s=%s" key value))))) - (insert ">\n<#/" name ">\n")) + (insert ">\n")) + +(defun mml-insert-empty-tag (name &rest plist) + "Insert an empty MML tag described by NAME and PLIST." + (when (symbolp name) + (setq name (symbol-name name))) + (apply #'mml-insert-tag name plist) + (insert "<#/" name ">\n")) ;;; Attachment functions. @@ -671,8 +678,8 @@ description of the attachment." (type (mml-minibuffer-read-type file)) (description (mml-minibuffer-read-description))) (list file type description))) - (mml-insert-tag 'part 'type type 'filename file 'disposition "attachment" - 'description description)) + (mml-insert-empty-tag 'part 'type type 'filename file + 'disposition "attachment" 'description description)) (defun mml-attach-buffer (buffer &optional type description) "Attach a buffer to the outgoing MIME message. @@ -682,8 +689,8 @@ See `mml-attach-file' for details of operation." (type (mml-minibuffer-read-type buffer "text/plain")) (description (mml-minibuffer-read-description))) (list buffer type description))) - (mml-insert-tag 'part 'type type 'buffer buffer 'disposition "attachment" - 'description description)) + (mml-insert-empty-tag 'part 'type type 'buffer buffer + 'disposition "attachment" 'description description)) (defun mml-attach-external (file &optional type description) "Attach an external file into the buffer. @@ -694,8 +701,8 @@ TYPE is the MIME type to use." (type (mml-minibuffer-read-type file)) (description (mml-minibuffer-read-description))) (list file type description))) - (mml-insert-tag 'external 'type type 'name file 'disposition "attachment" - 'description description)) + (mml-insert-empty-tag 'external 'type type 'name file + 'disposition "attachment" 'description description)) (defun mml-insert-multipart (&optional type) (interactive (list (completing-read "Multipart type (default mixed): " @@ -704,7 +711,7 @@ TYPE is the MIME type to use." nil nil "mixed"))) (or type (setq type "mixed")) - (mml-insert-tag "multipart" 'type type) + (mml-insert-empty-tag "multipart" 'type type) (forward-line -1)) (defun mml-preview (&optional raw) diff --git a/lisp/nnfolder.el b/lisp/nnfolder.el index 783d8d0..c1bbb10 100644 --- a/lisp/nnfolder.el +++ b/lisp/nnfolder.el @@ -184,11 +184,13 @@ If NIL, NNFOLDER-FILE-CODING-SYSTEM is used.") (if (numberp article) (cons nnfolder-current-group article) (goto-char (point-min)) - (search-forward (concat "\n" nnfolder-article-marker)) (cons nnfolder-current-group - (string-to-int - (buffer-substring - (point) (progn (end-of-line) (point))))))))))) + (if (search-forward (concat "\n" nnfolder-article-marker) + nil t) + (string-to-int + (buffer-substring + (point) (progn (end-of-line) (point)))) + -1)))))))) (deffoo nnfolder-request-group (group &optional server dont-check) (nnfolder-possibly-change-group group server t) diff --git a/lisp/nnmh.el b/lisp/nnmh.el index 0224709..0adde1f 100644 --- a/lisp/nnmh.el +++ b/lisp/nnmh.el @@ -60,7 +60,7 @@ (defvoo nnmh-status-string "") (defvoo nnmh-group-alist nil) -(defvoo nnmh-allow-delete-final nil) +(defvar nnmh-allow-delete-final nil) diff --git a/lisp/nnslashdot.el b/lisp/nnslashdot.el index c28e35c..fbf1509 100644 --- a/lisp/nnslashdot.el +++ b/lisp/nnslashdot.el @@ -90,10 +90,12 @@ (deffoo nnslashdot-retrieve-headers (articles &optional group server fetch-old) (nnslashdot-possibly-change-server group server) - (unless gnus-nov-is-evil - (if nnslashdot-threaded - (nnslashdot-threaded-retrieve-headers articles group) - (nnslashdot-sane-retrieve-headers articles group)))) + (condition-case why + (unless gnus-nov-is-evil + (if nnslashdot-threaded + (nnslashdot-threaded-retrieve-headers articles group) + (nnslashdot-sane-retrieve-headers articles group))) + (search-failed (nnslashdot-lose why)))) (deffoo nnslashdot-threaded-retrieve-headers (articles group) (let ((last (car (last articles))) @@ -310,30 +312,33 @@ (deffoo nnslashdot-request-article (article &optional group server buffer) (nnslashdot-possibly-change-server group server) (let (contents) - (save-excursion - (set-buffer nnslashdot-buffer) - (let ((case-fold-search t)) - (goto-char (point-min)) - (when (and (stringp article) - (string-match "%\\([0-9]+\\)@" article)) - (setq article (string-to-number (match-string 1 article)))) - (when (numberp article) - (if (= article 1) - (progn - (re-search-forward "Posted by .* on ") - (forward-line 1) + (condition-case why + (save-excursion + (set-buffer nnslashdot-buffer) + (let ((case-fold-search t)) + (goto-char (point-min)) + (when (and (stringp article) + (string-match "%\\([0-9]+\\)@" article)) + (setq article (string-to-number (match-string 1 article)))) + (when (numberp article) + (if (= article 1) + (progn + (re-search-forward "Posted by .* on ") + (forward-line 1) + (setq contents + (buffer-substring + (point) + (progn + (re-search-forward + "

.*A href=http://slashdot.org/article.pl") + (match-beginning 0))))) + (search-forward (format "" (1- article))) (setq contents (buffer-substring - (point) - (progn - (re-search-forward - "

.*A href=http://slashdot.org/article.pl") - (match-beginning 0))))) - (search-forward (format "" (1- article))) - (setq contents - (buffer-substring - (re-search-forward "]+>") - (search-forward ""))))))) + (re-search-forward "]+>") + (search-forward ""))))))) + (search-failed (nnslashdot-lose why))) + (when contents (save-excursion (set-buffer (or buffer nntp-server-buffer)) @@ -363,49 +368,51 @@ (nnslashdot-possibly-change-server nil server) (let ((number 0) sid elem description articles gname) - ;; First we do the Ultramode to get info on all the latest groups. - (with-temp-buffer - (nnweb-insert "http://slashdot.org/slashdot.xml") - (goto-char (point-min)) - (while (search-forward "" nil t) - (narrow-to-region (point) (search-forward "")) - (goto-char (point-min)) - (re-search-forward "\\([^<]+\\)") - (setq description (match-string 1)) - (re-search-forward "\\([^<]+\\)") - (setq sid (match-string 1)) - (string-match "/\\([0-9/]+\\).shtml" sid) - (setq sid (match-string 1 sid)) - (re-search-forward "\\([^<]+\\)") - (setq articles (string-to-number (match-string 1))) - (setq gname (concat description " (" sid ")")) - (if (setq elem (assoc gname nnslashdot-groups)) - (setcar (cdr elem) articles) - (push (list gname articles sid) nnslashdot-groups)) - (goto-char (point-max)) - (widen))) - ;; Then do the older groups. - (while (> (- nnslashdot-group-number number) 0) - (with-temp-buffer - (let ((case-fold-search t)) - (nnweb-insert (format nnslashdot-active-url number)) - (goto-char (point-min)) - (while (re-search-forward - "article.pl\\?sid=\\([^&]+\\).*\\([^<]+\\)" nil t) - (setq sid (match-string 1) - description (match-string 2)) - (forward-line 1) - (when (re-search-forward "\\([0-9]+\\)" nil t) - (setq articles (string-to-number (match-string 1)))) - (setq gname (concat description " (" sid ")")) - (if (setq elem (assoc gname nnslashdot-groups)) - (setcar (cdr elem) articles) - (push (list gname articles sid) nnslashdot-groups))))) - (incf number 30)) + (condition-case why + ;; First we do the Ultramode to get info on all the latest groups. + (mm-with-unibyte-buffer + (nnweb-insert "http://slashdot.org/slashdot.xml") + (goto-char (point-min)) + (while (search-forward "" nil t) + (narrow-to-region (point) (search-forward "")) + (goto-char (point-min)) + (re-search-forward "\\([^<]+\\)") + (setq description (match-string 1)) + (re-search-forward "\\([^<]+\\)") + (setq sid (match-string 1)) + (string-match "/\\([0-9/]+\\).shtml" sid) + (setq sid (match-string 1 sid)) + (re-search-forward "\\([^<]+\\)") + (setq articles (string-to-number (match-string 1))) + (setq gname (concat description " (" sid ")")) + (if (setq elem (assoc gname nnslashdot-groups)) + (setcar (cdr elem) articles) + (push (list gname articles sid) nnslashdot-groups)) + (goto-char (point-max)) + (widen))) + ;; Then do the older groups. + (while (> (- nnslashdot-group-number number) 0) + (mm-with-unibyte-buffer + (let ((case-fold-search t)) + (nnweb-insert (format nnslashdot-active-url number)) + (goto-char (point-min)) + (while (re-search-forward + "article.pl\\?sid=\\([^&]+\\).*\\([^<]+\\)" nil t) + (setq sid (match-string 1) + description (match-string 2)) + (forward-line 1) + (when (re-search-forward "\\([0-9]+\\)" nil t) + (setq articles (string-to-number (match-string 1)))) + (setq gname (concat description " (" sid ")")) + (if (setq elem (assoc gname nnslashdot-groups)) + (setcar (cdr elem) articles) + (push (list gname articles sid) nnslashdot-groups))))) + (incf number 30)) + (search-failed (nnslashdot-lose why))) (nnslashdot-write-groups) (nnslashdot-generate-active) t)) - + (deffoo nnslashdot-request-newgroups (date &optional server) (nnslashdot-possibly-change-server nil server) (nnslashdot-generate-active) @@ -434,6 +441,9 @@ (insert "\n") (setq quoted nil))) (forward-line 1)) + (goto-char (point-min)) + (while (re-search-forward "^ *\n" nil t) + (replace-match "

\n")) (widen) (when (message-goto-signature) (forward-line -1) @@ -472,7 +482,7 @@ (defun nnslashdot-read-groups () (let ((file (expand-file-name "groups" nnslashdot-directory))) (when (file-exists-p file) - (with-temp-buffer + (mm-with-unibyte-buffer (insert-file-contents file) (goto-char (point-min)) (setq nnslashdot-groups (read (current-buffer))))))) @@ -508,6 +518,9 @@ (insert (prin1-to-string (car elem)) " " (number-to-string (cadr elem)) " 1 y\n")))) +(defun nnslashdot-lose (why) + (error "Slashdot HTML has changed; please get a new version of nnslashdot")) + (provide 'nnslashdot) ;;; nnslashdot.el ends here diff --git a/lisp/nntp.el b/lisp/nntp.el index 39c3dc5..b3ed322 100644 --- a/lisp/nntp.el +++ b/lisp/nntp.el @@ -495,6 +495,7 @@ noticing asynchronous data.") (received 0) (last-point (point-min)) (nntp-inhibit-erase t) + (buf (nntp-find-connection-buffer nntp-server-buffer)) (command (if nntp-server-list-active-group "LIST ACTIVE" "GROUP"))) (while groups ;; Send the command to the server. @@ -506,6 +507,9 @@ noticing asynchronous data.") (zerop (% count nntp-maximum-request))) (nntp-accept-response) (while (progn + ;; Search `blue moon' in this file for the + ;; reason why set-buffer here. + (set-buffer buf) (goto-char last-point) ;; Count replies. (while (re-search-forward "^[0-9]" nil t) @@ -515,10 +519,12 @@ noticing asynchronous data.") (nntp-accept-response)))) ;; Wait for the reply from the final command. + (set-buffer buf) (goto-char (point-max)) (re-search-backward "^[0-9]" nil t) (when (looking-at "^[23]") (while (progn + (set-buffer buf) (goto-char (point-max)) (if (not nntp-server-list-active-group) (not (re-search-backward "\r?\n" (- (point) 3) t)) @@ -526,6 +532,7 @@ noticing asynchronous data.") (nntp-accept-response))) ;; Now all replies are received. We remove CRs. + (set-buffer buf) (goto-char (point-min)) (while (search-forward "\r" nil t) (replace-match "" t t)) diff --git a/lisp/nnultimate.el b/lisp/nnultimate.el index b2ff2bf..b1962ac 100644 --- a/lisp/nnultimate.el +++ b/lisp/nnultimate.el @@ -114,7 +114,7 @@ (set-buffer nntp-server-buffer) (erase-buffer)) (setq nnultimate-articles nil) - (with-temp-buffer + (mm-with-unibyte-buffer (dolist (elem fetchers) (setq pages 1 current-page 1 @@ -197,9 +197,10 @@ (setq nnultimate-headers (sort headers 'car-less-than-car)) (save-excursion (set-buffer nntp-server-buffer) - (erase-buffer) - (dolist (header nnultimate-headers) - (nnheader-insert-nov (cdr header))))) + (mm-with-unibyte-current-buffer + (erase-buffer) + (dolist (header nnultimate-headers) + (nnheader-insert-nov (cdr header)))))) 'nov))) (deffoo nnultimate-request-group (group &optional server dont-check) @@ -230,13 +231,14 @@ (goto-char (point-min)) (insert "Content-Type: text/html\nMIME-Version: 1.0\n") (let ((header (cdr (assq article nnultimate-headers)))) - (nnheader-insert-header header)) + (mm-with-unibyte-current-buffer + (nnheader-insert-header header))) (nnheader-report 'nnultimate "Fetched article %s" article) (cons group article))))) (deffoo nnultimate-request-list (&optional server) (nnultimate-possibly-change-server nil server) - (with-temp-buffer + (mm-with-unibyte-buffer (nnweb-insert (if (string-match "/$" nnultimate-address) (concat nnultimate-address "Ultimate.cgi") @@ -299,7 +301,7 @@ (furls (list (concat nnultimate-address (format furl sid)))) contents forum-contents furl-fetched a subject href garticles topic tinfo old-max inc parse) - (with-temp-buffer + (mm-with-unibyte-buffer (while furls (erase-buffer) (nnweb-insert (pop furls)) @@ -387,7 +389,7 @@ (setq nnultimate-groups-alist nil) (let ((file (expand-file-name "groups" nnultimate-directory))) (when (file-exists-p file) - (with-temp-buffer + (mm-with-unibyte-buffer (insert-file-contents file) (goto-char (point-min)) (setq nnultimate-groups-alist (read (current-buffer))))))) diff --git a/lisp/nnweb.el b/lisp/nnweb.el index 9d855f9..875853a 100644 --- a/lisp/nnweb.el +++ b/lisp/nnweb.el @@ -113,9 +113,10 @@ and `altavista'.") (set-buffer nntp-server-buffer) (erase-buffer) (let (article header) - (while (setq article (pop articles)) - (when (setq header (cadr (assq article nnweb-articles))) - (nnheader-insert-nov header))) + (mm-with-unibyte-current-buffer + (while (setq article (pop articles)) + (when (setq header (cadr (assq article nnweb-articles))) + (nnheader-insert-nov header)))) 'nov))) (deffoo nnweb-request-scan (&optional group server) @@ -169,7 +170,8 @@ and `altavista'.") (let* ((header (cadr (assq article nnweb-articles))) (url (and header (mail-header-xref header)))) (when (or (and url - (nnweb-fetch-url url)) + (mm-with-unibyte-current-buffer + (nnweb-fetch-url url))) (and (stringp article) (nnweb-definition 'id t) (let ((fetch (nnweb-definition 'id)) @@ -178,8 +180,9 @@ and `altavista'.") (setq art (match-string 1 article))) (and fetch art - (nnweb-fetch-url - (format fetch article)))))) + (mm-with-unibyte-current-buffer + (nnweb-fetch-url + (format fetch article))))))) (unless nnheader-callback-function (funcall (nnweb-definition 'article)) (nnweb-decode-entities)) @@ -231,7 +234,7 @@ and `altavista'.") (defun nnweb-read-overview (group) "Read the overview of GROUP and build the map." (when (file-exists-p (nnweb-overview-file group)) - (with-temp-buffer + (mm-with-unibyte-buffer (nnheader-insert-file-contents (nnweb-overview-file group)) (goto-char (point-min)) (let (header) @@ -299,22 +302,34 @@ and `altavista'.") (unless (gnus-buffer-live-p nnweb-buffer) (setq nnweb-buffer (save-excursion - (nnheader-set-temp-buffer - (format " *nnweb %s %s %s*" nnweb-type nnweb-search server)))))) + (let ((multibyte (default-value 'enable-multibyte-characters))) + (unwind-protect + (progn + (setq-default enable-multibyte-characters nil) + (nnheader-set-temp-buffer + (format " *nnweb %s %s %s*" + nnweb-type nnweb-search server))) + (setq-default enable-multibyte-characters multibyte)) + (current-buffer)))))) (defun nnweb-fetch-url (url) - (save-excursion - (if (not nnheader-callback-function) - (let ((buf (current-buffer))) - (save-excursion - (set-buffer nnweb-buffer) + (let (buf) + (save-excursion + (if (not nnheader-callback-function) + (progn + (with-temp-buffer + (mm-enable-multibyte) + (let ((coding-system-for-read 'binary) + (coding-system-for-write 'binary) + (default-process-coding-system 'binary)) + (nnweb-insert url)) + (setq buf (buffer-string))) (erase-buffer) - (url-insert-file-contents url) - (copy-to-buffer buf (point-min) (point-max)) - t)) - (nnweb-url-retrieve-asynch - url 'nnweb-callback (current-buffer) nnheader-callback-function) - t))) + (insert buf) + t) + (nnweb-url-retrieve-asynch + url 'nnweb-callback (current-buffer) nnheader-callback-function) + t)))) (defun nnweb-callback (buffer callback) (when (gnus-buffer-live-p url-working-buffer) @@ -368,18 +383,20 @@ and `altavista'.") (dolist (row (nth 2 (car (nth 2 table)))) (setq a (nnweb-parse-find 'a row) url (cdr (assq 'href (nth 1 a))) - text (nnweb-text row)) + text (nreverse (nnweb-text row))) (when a - (setq subject (nth 2 text) - group (nth 4 text) - date (nth 5 text) - from (nth 6 text)) - (string-match "\\([0-9]+\\)/\\([0-9]+\\)/\\([0-9]+\\)" date) - (setq date (format "%s %s %s" - (car (rassq (string-to-number - (match-string 2 date)) - parse-time-months)) - (match-string 3 date) (match-string 1 date))) + (setq subject (nth 4 text) + group (nth 2 text) + date (nth 1 text) + from (nth 0 text)) + (if (string-match "\\([0-9]+\\)/\\([0-9]+\\)/\\([0-9]+\\)" date) + (setq date (format "%s %s 00:00:00 %s" + (car (rassq (string-to-number + (match-string 2 date)) + parse-time-months)) + (match-string 3 date) + (match-string 1 date))) + (setq date "Jan 1 00:00:00 0000")) (incf i) (setq url (concat url "&fmt=text")) (unless (nnweb-get-hashtb url) diff --git a/lisp/rfc2047.el b/lisp/rfc2047.el index 1a08b3c..3344753 100644 --- a/lisp/rfc2047.el +++ b/lisp/rfc2047.el @@ -338,9 +338,16 @@ Valid ENCODINGs are \"B\" and \"Q\". If your Emacs implementation can't decode CHARSET, it returns nil." (if (stringp charset) (setq charset (intern (downcase charset)))) - (if (or (not charset) (memq charset mail-parse-ignored-charsets)) + (if (or (not charset) + (eq 'gnus-all mail-parse-ignored-charsets) + (memq 'gnus-all mail-parse-ignored-charsets) + (memq charset mail-parse-ignored-charsets)) (setq charset mail-parse-charset)) (let ((cs (mm-charset-to-coding-system charset))) + (if (and (not cs) charset + (listp mail-parse-ignored-charsets) + (memq 'gnus-unknown mail-parse-ignored-charsets)) + (setq cs (mm-charset-to-coding-system mail-parse-charset))) (when cs (when (and (eq cs 'ascii) mail-parse-charset) diff --git a/texi/ChangeLog b/texi/ChangeLog index 54e7f9b..dd82a2b 100644 --- a/texi/ChangeLog +++ b/texi/ChangeLog @@ -1,8 +1,24 @@ +1999-12-03 00:02:11 Lars Magne Ingebrigtsen + + * gnus.texi (Other Gnus Versions): New. + (Gnus Versions): Made into own node. + +1999-12-02 00:00:00 Lars Magne Ingebrigtsen + + * gnus.texi (Paging the Article): Addition. + (History): Addition. + +1999-11-24 Carsten Leonhardt + + * gnus.texi (Mail Source Specifiers): Mention maildir in the + overview and the possibility to use remote maildirs. + 1999-12-01 14:21:19 Lars Magne Ingebrigtsen * gnus.texi (Topic Parameters): Addition. (Summary Message Commands): New. (Canceling and Superseding): Made into subsection. + (Charsets): Addition. 1999-11-30 10:54:31 Shenghuo ZHU diff --git a/texi/gnus.texi b/texi/gnus.texi index f77798e..bf60769 100644 --- a/texi/gnus.texi +++ b/texi/gnus.texi @@ -1,7 +1,7 @@ \input texinfo @c -*-texinfo-*- -*- coding: iso-latin-1 -*- @setfilename gnus -@settitle Pterodactyl Gnus Manual +@settitle Gnus Manual @synindex fn cp @synindex vr cp @synindex pg cp @@ -319,7 +319,7 @@ into another language, under the above conditions for modified versions. @tex @titlepage -@title Pterodactyl Gnus Manual +@title Gnus Manual @author by Lars Magne Ingebrigtsen @page @@ -355,7 +355,7 @@ can be gotten by any nefarious means you can think of---@sc{nntp}, local spool or your mbox file. All at the same time, if you want to push your luck. -This manual corresponds to Pterodactyl Gnus . +This manual corresponds to Gnus 5.8.2. @end ifinfo @@ -3910,11 +3910,24 @@ Scroll the current article one line backward @kindex A g (Summary) @kindex g (Summary) @findex gnus-summary-show-article +@vindex gnus-summary-show-article-charset-alist (Re)fetch the current article (@code{gnus-summary-show-article}). If given a prefix, fetch the current article, but don't run any of the article treatment functions. This will give you a ``raw'' article, just the way it came from the server. +If given a numerical prefix, you can do semi-manual charset stuff. +@kbd{C-u 0 g cn-gb-2312 RET} will decode the message as if it were +encoded in the @code{cn-gb-2312} charset. If you have + +@lisp +(setq gnus-summary-show-article-charset-alist + '((1 . cn-gb-2312) + (2 . big5))) +@end lisp + +then you can say @kbd{C-u 1 g} to get the same effect. + @item A < @itemx < @kindex < (Summary) @@ -7159,9 +7172,9 @@ instance, @kbd{3 b} means ``view the third @sc{mime} part''. @table @kbd @item b -@itemx K b +@itemx K v @kindex b (Summary) -@kindex K b (Summary) +@kindex K v (Summary) View the @sc{mime} part. @item K o @@ -7312,6 +7325,42 @@ on a group-by-group basis using the group parameters (@pxref{Group Parameters}). The default value is @code{(unknown-8bit)}, which is something some agents insist on having in there. +@cindex Russina +@cindex koi8-r +@cindex koi8-u +@cindex iso-8859-5 +@cindex coding system aliases +@cindex preferred charset + +Other charset tricks that may be useful, although not Gnus-specific: + +If there are several @sc{mime} charsets that encode the same Emacs +charset, you can choose what charset to use by saying the following: + +@lisp +(put-charset-property 'cyrillic-iso8859-5 + 'preferred-coding-system 'koi8-r) +@end lisp + +This means that Russian will be encoded using @code{koi8-r} instead of +the default @code{iso-8859-5} @sc{mime} charset. + +If you want to read messages in @code{koi8-u}, you can cheat and say + +@lisp +(define-coding-system-alias 'koi8-u 'koi8-r) +@end lisp + +This will almost do the right thing. + +And finally, to read charsets like @code{windows-1251}, you can say +something like + +@lisp +(codepage-setup 1251) +(define-coding-system-alias 'windows-1251 'cp1251) +@end lisp + @node Article Commands @section Article Commands @@ -10275,7 +10324,8 @@ month's rent money. @subsection Mail Sources Mail can be gotten from many different sources---the mail spool, from a -POP mail server, or from a procmail directory, for instance. +POP mail server, from a procmail directory, or from a maildir, for +instance. @menu * Mail Source Specifiers:: How to specify what a mail source is. @@ -10489,9 +10539,9 @@ Use @samp{movemail} to move the mail: @end lisp @item maildir -Get mail from a maildir. This is a type of mailbox currently only -supported by qmail, where each file in a special directory contains -exactly one mail. +Get mail from a maildir. This is a type of mailbox that is supported by +at least qmail and postfix, where each file in a special directory +contains exactly one mail. Keywords: @@ -10502,16 +10552,24 @@ The path of the directory where the mails are stored. The default is If you sometimes look at your mail through a pop3 daemon before fetching them with Gnus, you may also have to fetch your mails from the -@code{cur} directory inside the maildir, like in the following example. +@code{cur} directory inside the maildir, like in the first example +below. + +You can also get mails from remote hosts (because maildirs don't suffer +from locking problems). @end table -An example maildir mail source: +Two example maildir mail sources: @lisp (maildir :path "/home/user-name/Maildir/cur") @end lisp +@lisp +(maildir :path "/user@@remotehost.org:~/Maildir/new") +@end lisp + @item imap Get mail from a @sc{imap} server. If you don't want to use @sc{imap} as intended, as a network mail reading protocol (ie with nnimap), for some reason or @@ -17497,6 +17555,27 @@ spunky name, we decided that the name was @emph{too} spunky, so we renamed it back again to ``Gnus''. But in mixed case. ``Gnus'' vs. ``@sc{gnus}''. New vs. old. +@menu +* Gnus Versions:: What Gnus versions have been released. +* Other Gnus Versions:: Other Gnus versions that also have been released. +* Why?:: What's the point of Gnus? +* Compatibility:: Just how compatible is Gnus with @sc{gnus}? +* Conformity:: Gnus tries to conform to all standards. +* Emacsen:: Gnus can be run on a few modern Emacsen. +* Gnus Development:: How Gnus is developed. +* Contributors:: Oodles of people. +* New Features:: Pointers to some of the new stuff in Gnus. +* Newest Features:: Features so new that they haven't been written yet. +@end menu + + +@node Gnus Versions +@subsection Gnus Versions +@cindex Pterodactyl Gnus +@cindex ding Gnus +@cindex September Gnus +@cindex Quassia Gnus + The first ``proper'' release of Gnus 5 was done in November 1995 when it was included in the Emacs 19.30 distribution (132 (ding) Gnus releases plus 15 Gnus 5.0 releases). @@ -17507,8 +17586,12 @@ releases)) was released under the name ``Gnus 5.2'' (40 releases). On July 28th 1996 work on Red Gnus was begun, and it was released on January 25th 1997 (after 84 releases) as ``Gnus 5.4'' (67 releases). -On September 13th 1997, Quassia Gnus was started and lasted 37 -releases. If was released as ``Gnus 5.6 on March 8th 1998. +On September 13th 1997, Quassia Gnus was started and lasted 37 releases. +If was released as ``Gnus 5.6'' on March 8th 1998 (46 releases). + +Gnus 5.6 begat Pterodactyl Gnus on August 29th 1998 and was released as +``Gnus 5.8'' (after 99 releases and a CVS repository) on December 3rd +1999. If you happen upon a version of Gnus that has a prefixed name -- ``(ding) Gnus'', ``September Gnus'', ``Red Gnus'', ``Quassia Gnus'' -- @@ -17517,16 +17600,21 @@ Slowly. Whatever you do, don't run. Walk away, calmly, until you're out of its reach. Find a proper released version of Gnus and snuggle up to that instead. -@menu -* Why?:: What's the point of Gnus? -* Compatibility:: Just how compatible is Gnus with @sc{gnus}? -* Conformity:: Gnus tries to conform to all standards. -* Emacsen:: Gnus can be run on a few modern Emacsen. -* Gnus Development:: How Gnus is developed. -* Contributors:: Oodles of people. -* New Features:: Pointers to some of the new stuff in Gnus. -* Newest Features:: Features so new that they haven't been written yet. -@end menu + +@node Other Gnus Versions +@subsection Other Gnus Versions +@cindex Semi-gnus + +In addition to the versions of Gnus which have had their releases +coordinated by Lars, one major development has been Semi-gnus from +Japan. It's based on a library called @sc{semi}, which provides +@sc{mime} capabilities. + +These Gnusae are based mainly on Gnus 5.6 and Pterodactyl Gnus. +Collectively, they are called ``Semi-gnus'', and different strains are +called T-gnus, ET-gnus, Nana-gnus and Chaos. These provide powerful +@sc{mime} and multilingualization things, especially important for +Japanese users. @node Why? @@ -19989,6 +20077,9 @@ fetching. Be able to forward groups of messages as MIME digests. @item +nnweb should include the "get whole article" article when getting articles. + +@item Solve the halting problem. @c TODO diff --git a/texi/gnusmail.texi b/texi/gnusmail.texi new file mode 100644 index 0000000..7c8c66b --- /dev/null +++ b/texi/gnusmail.texi @@ -0,0 +1,708 @@ +@node Mail with Gnus, , Key bindings, Top +@comment node-name, next, previous, up +@chapter Mail with Gnus + +Quite a few people wish to read mail with Gnus but stumble across a few +issues which make this a bit difficult. This comes from the fact that +Gnus is really a newsreader, and thus it treats mail in a newsreaderly +fashion. It is not desirable to change this, because it is a wonderful +thing and this is what distinguishes Gnus from other mail readers. In +this little tutorial, I'll try to explain what ``newsreaderly fashion'' +means and how exactly Gnus treats mail. + +Specific pieces of behavior can always be changed, but if you desire to +make Gnus behave like a conventional mail reader, think again. It will +be an uphill battle. Maybe a different mail reader is for you? But +also, read on. Maybe you'll find the right behavior in the description +below. + +@c ------------------------------------------------------------ +@section Gnus Terminology +@c ------------------------------------------------------------ + +First, let's talk about a few terms which I'm going to use which might +be unfamiliar to you. + +@table @dfn +@item Posting, Article, Message, Mail +These are all related terms. A news message might also be called a +posting or an article, whereas a mail message is known as a mail. Since +Gnus treats both news and mail, the distinction isn't as clear. In the +following, I'll use the term ``message'' for the generic case and ``news +message'' and ``mail message'' for the specific cases. But sometimes, I +might use ``posting'' or ``article'', too, both synonymous with +``message''. + +@item Backend +Gnus can read messages from various sources. On the one hand, there is +news, and on the other hand, there is mail. News can be read from a +news server (an NNTP server), or from a so-called spool directory. Mail +can be read in various formats. + +Generally speaking, a backend describes the way Gnus accesses a set of +messages. For a news server, this is the Network News Transfer +Protocol, NNTP, and thus there is a backend ``nntp''. For mail stored +in the same format that the MH message handling system used, there is +the backend ``nnmh''. And so on. See below for a list of backends. + +@item Server +Whereas a backend describes the mechanism used by Gnus to access the +messages, a server is a specific instance of that mechanism. You might +create a specific server for accessing the host @file{news.frob.org} +using NNTP, say. Or you might create a server for accessing the +MH-style directory @file{~/OldMail}. + +If you are a programmer, think of a backend as the class and of a server +as an object (instance) of that class. If you are a cook, think of a +backend as an apple pie recipe (say), and think of a server as an actual +apple pie. (Yummy!) If you live in a huge city, think of a backend as +a bus (or tram, or underground) line (the Circle Line comes to mind), +and think of a server as a specific bus (or tram train, or underground +train). The one at 8:15 last Monday morning, for example. If you drive +a car, think of a backend as the model and make and of a server as a +specific car. + +Obviously, there can be two servers using the same backend. (Two +instances of the same class, two apple pies baked according to the same +recipe, two busses going the same route, two cars of the same model.) + +@item (Select) method +Just another term for server. + +@item Native server +This is the primary server, so to speak. Most people let their news +server be the native server, hence: +@lisp +(setq gnus-select-method '(nntp "news.frob.org")) +@end lisp +Groups from the native server are also known as native groups. + +@item Secondary select methods +This is a list of other servers which one also wishes to use. Many +people are only going to have two servers, one native (for news) and one +secondary (for mail). Thus: +@lisp +(setq gnus-secondary-select-methods '((nnml ""))) +@end lisp +Note that there is one more pair of parentheses in order to be able to +mention more than one seconary select method. + +Groups from a secondary server are also known as secondary groups. + +In order to be able to distinguish native groups from secondary groups, +each server is identified with a (unique) name and that name is used as +a prefix for the secondary groups. Thus, you might have a group +@file{gnu.emacs.help} (which is native) and another group +@file{nnml:mail.misc} (which is secondary). A plus character is used if +the name of a server is not the empty string. For example, given the +following in your @file{.gnus} file +@lisp +(setq gnus-secondary-select-methods + '((nnml "work" (nnml-directory "~/Mail.work/")) + (nnml "play" (nnml-directory "~/Mail.play/")))) +@end lisp +you might have the groups @file{nnml+work:boss} and +@file{nnml+play:so}@footnote{``SO'' standing for ``significant other'', +i.e.@ girlfriend or boyfriend}. + +@item Group +Well, if you've read news before, you know about different news groups. +One of my favorites is @file{gnu.emacs.gnus}, and I wish I would read +@file{alt.fan.pratchett}. Since Gnus treats mail in a newsreaderly +fashion, it is obvious that it uses groups rather than ``folders'' like +other mail readers do. So with Gnus there are news groups and mail +groups, where mail groups are known as mail folders to other programs. + +Each group belongs to a certain server, and each server uses a certain +backend. + +@item Expiry +News servers offer news groups which contain news postings. New news +postings come in, so the news postings accumulate, and pretty soon the +new hard disk is full. This is not good news at all. + +Thus, a news server does what is known as @dfn{expiry}: it deletes old +messages. Of course, on a news server, expiry happens with no regard of +whether people have already seen the message in question; instead, the +news server admin chooses expiry times based on available disk space and +maybe on the normal amount of traffic in a news group. + +But mail messages should be under the users' control, so there better be +no server which deletes messages regardless of users having seen them! +Instead, Gnus adopts a scheme where users say which messages may be +deleted, and Gnus takes care of deleting them after a while. (They are +not deleted immediately in case you made a mistake, or in case you wish +to refer back to an old article.) + +@item Article marks +Gnus distinguishes between a number of article marks, which indicate +whether they have been looked at, or are considered important, or the +like. Marks are represented by a character. + +If that character is a space, it looks as if the message isn't marked at +all. These messages are called @dfn{unmarked}, the mark character used +is a space, and marking a message with space is considered to be the +same as removing all marks---after all, such messages are unmarked. You +can type @kbd{M-u} to remove all marks and make an article unmarked. + +Articles that are considered important or where you wish to indicate +that you have to deal with them later can be @dfn{ticked}. The mark +character used for ticked messages is the exclamation mark, and you can +use @kbd{u} or @kbd{!} to tick messages. Ticked messages are always +shown when you enter a group. + +There is the @dfn{dormant} mark which is similar to the ticked mark but +does not imply importance or urgency; thus, dormant messages aren't +shown by default. The mark character used is the question mark, and you +can mark messages as dormant using the @kbd{?} key. + +So far, each kind of mark was associated with one character (as was the +absence of any mark). But articles which you have read are a bit +different, since lots of different characters are used here. The +important thing to realize is that these messages are treated in the +same way by Gnus; the different characters are only used as an +information for the user. + +Articles which are marked as read because you have actually read them +(the normal case, one would think) are marked with the `R' character. +(Type @kbd{@key{SPC}} or @kbd{g} to read a message, or click on it using +the middle mouse button, @kbd{@key{mouse-2}}.) You can also mark a +message as read without actually reading it, this is indicated with the +`r' character and can be achieved with @kbd{d} or @kbd{M r}. + +After exiting a group and then entering it again (some time later), the +messages that had been marked as read before appear with the `O' +character. + +To reiterate: the difference between `r', `R' and `O' is only an +information for the user. + +@end table + + + +@c ------------------------------------------------------------ +@section Choosing a mail backend +@c ------------------------------------------------------------ + +The Gnus manual lists quite a few backends. Of these, the news backends +pose no problem: you use the @code{nntp} backend if you access a news +server and the @code{nnspool} backend if you have a local news spool +directory. (Leafnode users should use @code{nntp} so that the leafnode +program can see what you are doing and choose the groups to download +accordingly.) But the mail backends are more difficult. There are many +of them, and it is not obvious which is the best choice. In quite a few +cases, this is because there is no single best choice; or maybe what's +the best choice depends on the group or changes over time. + +Below, I'll give a list of mail backends. While I say something about +how messages are stored, I'll try to emphasize what that means for you +as a user. + +Let's try to structure the discussion a bit. We have servers, which +contain groups, which in turn contain messages. How could we store this +on disk? After some thought, you'll quickly come up with the following +alternatives: You could store all messages from a server in one file. +The second alternative is to store all messages from one group in one +file, different groups are stored in different files. A third +alternative is to store each message in one file; in this case, one +could use a directory per group. A very interesting fourth alternative +is not to store the messages at all but instead to use the Oracle of +Delphi (say) to predict what the messages will be; this saves a lot of +disk space. I won't talk about the fourth alternative in the following. + + +@subsection Backends with one file per server + +Many people use just two servers, the native server for news and a +secondary server for mail. Thus, this alternative would mean that you +store all your mail in one file. Since Emacs has no fancy mechanisms to +access parts of files, this means that Gnus loads that file into main +memory at startup, and all your mails are kept in main memory all the +time. (Of course, copies are written to disk every now and then, for +safekeeping!) + +I think you can pretty much figure out the consequences on your own, +now: +@itemize @bullet +@item +Handling large amounts of mail will be a problem. (Emacs has a maximum +file size of 128 MB.) + +@item +Some operations on mails will be fast, since they are in-memory +operations. (Saving everything to disk will be slow, though.) + +@item +Some operations on mails will be slow, since they have to search through +the whole file. + +@item +It is convenient to have all mail stored in one file: you can easily +transfer it to another computer using FTP, say, or store it on a floppy +or Zip disk or a tape. + +@end itemize + +Conclusion: If you don't have a lot of mail to deal with and like the +convenience of storing it all in one file, one of these backends might +be for you. However, since Gnus really shines when dealing with lots of +mails, most Gnus users can be expected to deal with quite a large volume +of mail. Thus, I don't think many Gnus users choose one of these +backends. + +@table @code +@item nnmbox +This backend uses the well-known ``mbox'' format for storing mails. In +this format, a message begins with the five characters @code{From_} (the +last character is a space) at the beginning of a line, and ends with an +empty line. + +@item nnbabyl +This backend uses the lesser known ``babyl'' format for storing mails. +This uses delimiters for the beginning and the end of a message which +are less likely to occur in a message. + +@quotation +CCC Are they guaranteed to never occur? +@end quotation + +One advantage of a babyl file over an mbox file is that it is possible +to insert information about a message in the babyl file, without having +to change the message itself. In an mbox file, the only place to put +such information is the message header, which is part of the message. +Thus, adding information about a message to an mbox file means that one +has to change the message. + +I think Gnus doesn't make use of this advantage, though. Gnus stores +information about messages in an extra file, @file{~/.newsrc.eld}. + +@end table + +@quotation +CCC Can somebody provide me with some more arguments in favor of one of +the formats? + +CCC Is it possible to just use an existing babyl file for Gnus, by +creating a new nnmbox server and pointing it at the file? What about +mbox? +@end quotation + + +@subsection Backends with one file per group + +Storing all messages in a group in one file provides a nice middle +ground between the one file per server type of backend and the one file +per message type of backend. Using lots of little files wastes disk +space; since this approach uses a moderate number of files, less disk +space is wasted. + +@quotation +CCC Which operations are fast using this kind of backend? Which are +slow? +@end quotation + +@table @code +@item nnfolder +This backend uses the same file format as @code{nnmbox}, but uses the +one file per group approach. + +@end table + +There is no ``nnbabylfolder'' backend which uses babyl format. + + +@subsection Backends with one file per message + +If the number of messages grows so large that even the size of a single +group exceeds the limit which can be handled by the file-per-group +backends, you need to think about using one of the backends mentioned +here. + +This category also includes @code{nnml}, the backend which is fastest if +you have lots of messages. + +@table @code +@item nnmh +This backend uses the same file format (and directory structure) as MH, +i.e.@ a group is a directory, and each message is stored in a file, and +the file names are numbers. + +Since @code{nnml} is so similar to @code{nnmh} but a lot faster, only +unusual situations could warrant using this backend. You may want to +use @code{nnmh} if you wish to use Gnus in parallel to your old MH based +reader. + +Normally, you should not let two programs write the same Gnus directory +(not even two instances of Gnus!), but if you really must, you may wish +to use @code{nnmh}, since there the probability of things breaking is +smaller than with the other backends. + +@item nnml +This backend is like @code{nnmh} but also includes an extra file +@file{.overview} in each directory (group) which contains some headers +from each message. Thus, where @code{nnmh} needs to open every file in +a group to examine its headers, @code{nnml} (normally) needs to only +read the @file{.overview} file, which is a lot faster. +@end table + + +@subsection Other mail backends + +There is one other mail backend, for keeping messages on an IMAP server. + +@table @code +@item nnimap +This backend transforms Gnus into an IMAP client. The general idea of +IMAP is to store and manipulate the mails on a server (similar to NNTP +for news). + +@code{nnimap} only works with the current development version of Gnus, +though. See @url{http://www.extundo.com/nnimap/} for @code{nnimap} and +see @url{http://www.gnus.org/} for Gnus. Don't forget to subscribe to +both the Gnus and the nnimap mailing lists since you are using alpha +grade software which can be exptected to have bugs. Be prepared to +submit meaningful bug reports if you encounter bugs. + +Rumor has it that @code{nnimap} will be integrated with the next version +of Gnus (version 5.8, presumably), when that comes out. + +@end table + + +@subsection Summary + +If you must talk to an IMAP server, the choice is clear. But if you +keep mails on your local disk, the choice isn't as clear-cut. I think +that @code{nnml} is generally the best choice unless you have real great +disk space trouble. Then, you should be thinking about @code{nnfolder}. + +I'm not sure if there is a situation where @code{nnmbox} or +@code{nnbabyl} is desirable. + +@quotation +CCC Tell me about it if you know more! +@end quotation + + +@c ------------------------------------------------------------ +@section Auto-expire versus total-expire +@c ------------------------------------------------------------ + +Every now and then, people ask about auto-expire and total-expire. +Since both of these features are means to the same end, and since they +are similar and dissimilar at the same time, great confusion can result +in the unsuspecting new Gnus user. I'll try to explain how each works +and which one to use. However, be prepared that there will be no clear +recommendation: both work well, so for many situations both are +appropriate. So it is more a matter of taste which one to choose. And +I can't help you with that! + + +@subsection What is expiry? + +Gnus treats mail in a newsreaderly fashion, so it is useful to examine +the situation for news. Your news server periodically contacts other +news servers and exchanges messages with the other server. The two news +servers exchange lists of messages, and messages present in one server +but not in the other are sent to the other server. This works in both +directions. Many connections between news servers exist, and this is +the way how postings travel from you into the world: when you post a +message, your news server stores it and offers it to the other servers +in the message list exchanging phase. Since the other servers aren't +going to have the posting you just wrote, it gets transferred and +finally can be seen all over the world. + +You can quickly see that new messages will be arriving at your news +server, which stores them on disk. So something has got to happen else +the disk will fill up real fast. That ``something'' is expiry: the +oldest messages are deleted to make room for the new ones. Normally, a +news server can be configured on a per-group basis which messages should +be deleted. In some groups, messages might be deleted which are older +than a day, in other groups, messages might be kept for a month. + +This means if you go on vacation then come back later to read news, you +are likely to miss some postings if the expiration time for the groups +you read is shorter than the time you were on vacation. + +How does that apply to mail? + +Well, mail should stay more under the control of the user than news is. +When you come back from a vacation, you expect to see all messages +arrived during that time, not only the recent ones! + +Because of this, Gnus offers you the @kbd{E} key. This marks a message +as expirable. No mail is ever deleted from disk, unless it is +expirable. Every once in a while (by default, whenever you quit a group +by hitting @kbd{q} in the Summary buffer), the expiry process is run, +which goes through all expirable messages (of a group) and expires it if +old enough. By default, messages older than seven days are ``old +enough''. Seven days, that is, since it was marked as expirable. + +@quotation +CCC Last sentence correct? +@end quotation + +``But when I read a message, exit the group, then enter it again, the +message is gone!'' + +Right. By default, Gnus hides messages which have already been read. +If you are the keyboard type, you can hit @kbd{C-u @key{RET}} or +@kbd{C-u @key{SPC}} to enter the group or @kbd{C-u M-g} when in the +group to look at the read messages. If you are the mousey type, you may +wish to use the ``See old articles'' entry in the ``Group'' menu. + +@quotation +CCC How does one code menu entries in TeXinfo? +@end quotation + + +@subsection Why auto-expire and total-expire? + +When reading mail, by default Gnus marks as read each message that you +read. If you want to mark it as expirable, you have to hit @kbd{E}. +Many people are subscribed to mailing lists and they configure Gnus to +put mails from a mailing list into their own group. Most messages in +such groups should be expirable, once read. But hitting @kbd{E} all the +gets old real quick. Hence, auto-expire and total-expire were invented. + + +@subsection Auto-expire vs.@ total-expire + +Auto-expire and total-expire both aim at the same goal: articles which +are read should normally be expired, only if one does something special +should these articles be saved on disk. But what happens when a message +is being read by you, the user? Well, the message is marked as read +(with the `R' mark). So, what can be done to make these messages +expire? Well, two approaches come to mind: one idea is to change the +mark that is attached to messages that you read, and the other idea is +to make the `R' articles expirable. These are @emph{exactly} the things +that are done in Gnus: auto-expire means to change the mark that is +attached to a message that is being read, and total-expire means to +change the meaning of the `R' mark to mean expirable. + +A more precise description of auto-expire might be the following: If an +article is unmarked and then selected for reading,@footnote{Using +@kbd{g}, or @kbd{@key{mouse-2}}, or by moving to it with @kbd{n} or +@kbd{p}, or by one of the many other methods provided by Gnus.} it is +marked with `E', as if the user had hit @kbd{E}. + +It is important to realize that auto-expire has @emph{no other} +consequences. Selecting a message for reading which wasn't unmarked +doesn't do anything special, and hitting @kbd{d} on a message also +doesn't do anything special. (It therefore marks the message as read, +not as expirable!) + +Now, forget about auto-expire, empty your mind and prepare to learn +about total-expire. Like I said, total-expire changes what it means for +an article to be marked as read. + +A more precise description of total-expire might be the following: When +the expire process is run (for example, when you leave a group with +@kbd{q}), all messages marked as read are considered to be expirable, as +if they had been marked with `E'. Recall that there are several ways to +mark a message as read: by reading it, by hitting @kbd{d} on it, and in +a few other ways which I haven't mentioned so far. Recall that, in +addition to the messages marked with `R', also those marked with `r' or +`O' are considered to be marked as read. + +Can auto-expire and total-expire be used together? Well, in principle +they can, but that doesn't make sense. Just using total-expire alone +achieves the same effect. + +So, how do auto-expire and total-expire compare? Well, for once thing, +hitting @kbd{d} on a message means it is expirable if total-expire is +on (since it is marked as read and all messages marked as read are +considered expirable when total-expire is on), but it is not expirable +if auto-expire is on (since it is marked as read and only articles +marked expirable (`E') are considered to be expirable). If you want to +mark a message as expirable when total-expire is off, use @kbd{E}. + +One way of comparing auto-expire and total-expire is to compare the +message marks that are available, and what they mean. Since auto-expire +does not change the meaning of marks, its behavior is the same as in the +default case. It's only important whether total-expire is on or off. +Thus, there are only two cases: the default case and the total-expire +case. + +@subsubsection Article marks with and without total-expire + +The following are the default article marks and behavior: + +@table @dfn +@item unmarked +All new messages are unmarked. This means you haven't seen them. They +are always shown and won't be deleted. + +@item read +Messages marked as read are not shown by default but kept on disk till +hell freezes over. You can show them with @kbd{C-u M-g} from the +summary buffer, or with @kbd{C-u @key{SPC}} or with the `Group' menu +item `See old articles' from the group buffer. + +Depending on the setting of @var{gnus-fetch-old-headers}, a message +marked as read might be shown if there is a followup or reply to it. + +@item dormant +Dormant messages aren't shown by default but kept on disk till hell +freezes over. You can show them with @kbd{/ D} from the summary buffer. +If there is a reply or followup to a dormant message, the dormant +message is also shown. + +@item ticked +Ticked messages are always shown and kept on disk till hell freezes +over. + +@item expirable +Expirable messages will be deleted in due time. They are not shown by +default, but you can make them appear with @kbd{C-u M-g} and so on, +similar to the read ones. + +@end table + +Please note that the behavior for ticked messages is similar to the +unread ones, and that the behavior of dormant messages is similar to the +read ones. Especially the second fact will become important when we +look at + +The behavior of the article marks with total-expire: + +@table @dfn +@item unmarked +Same as in the default case. + +@item expirable +Same as in the default case. + +@item read +Same as expirable. + +@item dormant +Same as in the default case. + +@item ticked +Same as in the default case. + +@end table + +As you can see, only the behavior of the read messages is different, and +you can use the dormant mark if you want to achieve behavior similar to +the behavior of read messages in the default case. + + +@subsubsection Speed issues + +Total-expire may be slow when expiry happens. Why is that? Well, Gnus +keeps an explicit list of all expirable messages (the ones marked `E' +without taking total-expire into account), as well as a list of dormant +messages, and a list of ticked messages. Normally, when expiration time +comes, Gnus goes through all articles in the expire list and looks if +they are old enough to be expired. + +However, for read messages the situation is different. Here, Gnus just +keeps a list of ranges of article numbers to be able to distinguish read +messages from unmarked ones. The assumption is that a message is to be +considered marked as read if it falls in one of the ranges and isn't +mentioned in any of the expirable, dormant or ticked lists. + +When total-expire is turned on, Gnus needs to go through all messages in +the read range in order to look if it's in one of the lists. If the +message is not in the ticked or dormant list, it is expirable and thus +Gnus looks to see if it is old enough. + +Obviously, going through all the articles in the read ranges takes more +time than going through just the list of expirable articles. + +Something can be done about the speed issue, though. Normally, the +expiry process is started whenever you leave a group. I suggest that +you disable this and instead run expiry just once per day, for example +while you are going for lunch. This means that expiry still takes a +long time, but you don't see it and thus it doesn't bother you. + +Here's how to do that: You disable the expire-on-group-exit thing with +the following line in your @file{~/.gnus} file: +@lisp +(remove-hook 'gnus-summary-prepare-exit-hook + 'gnus-summary-expire-articles) +@end lisp +And before you leave for lunch, you hit @kbd{C-M-x}, or @kbd{M-x +gnus-group-expire-all-groups @key{RET}}. + + +@subsubsection Functionality + +Adaptive scoring doesn't work with auto-expire. (But normal scoring +still works fine.) Adaptive scoring works fine with total-expire. + + +@subsubsection A summary + +Well, it is difficult to sum up the whole discussion. I used to use +total-expire but have switched to auto-expire a long time ago. I liked +the fact that I could use one more kind of article mark. I also liked +the fact that marking a message as read works the same in auto-expirable +groups and in normal mail groups: you hit @kbd{E}. (With total-expire, +you normally hit @kbd{d} but must remember to use @kbd{E} for those +groups where total-expire is off.) And I liked that auto-expire is +faster. + +On the other hand, adaptive scoring is surely a very useful feature (I'm +just beginning to use it), so many people might prefer total-expire. + +And on a third hand, maybe the key binding issue isn't so important +after all. You see, in mail groups the @kbd{d} key means `keep this +message for archival purposes', whereas in many other modes (dired, CCC +others?) it stands for `delete'. I propose to make it mean delete in +mail groups, too, with the following line in +@file{~/.gnus}:@footnote{See the chapter on key bindings; maybe you need +a `require' statement.} +@lisp +(define-key gnus-summary-mode-map "d" 'gnus-summary-mark-as-expirable) +@end lisp +Marking messages as expirable (rather than read) in news groups does no +harm, nor does it harm to do so in total-expirable mail groups. The old +`keep this message' semantics can still be had by marking a message as +dormant or by using @kbd{M r} (in non-total-expirable groups only). + + +@c ------------------------------------------------------------ +@section Migrating old mail +@c ------------------------------------------------------------ + +Probably, you've been reading mail in pre-Gnus times, right? And surely +you wish to carry this over to Gnus. Well, I haven't found a real good +way to do it, but I can offer a few suggestions for doing it at least +semi-automatically. + +One way of getting at your old mail is to type @kbd{G f}, and to then +enter the name of your old mail file. This provides read-only access to +your mails. For some people, this might be sufficient. (With @kbd{G +f}, you have created an @code{nndoc} group.) + +Some people might want to have their mails available in their normal +mail groups hierarchy. That's simple, just create an @code{nndoc} group +for your mail file, then mark all messages in it with @kbd{M P a}, then +copy all of them over to a normal mail group, with @kbd{B c}. + +This is good for people who wish to keep their old arrangement of +folders, and who have a one-to-one correspondence between old mail files +and new Gnus groups. But some people might wish to split up their mails +differently. For them, it might be useful to set up +@var{nnmail-split-methods} correctly and to use @kbd{B r} instead of +@kbd{B c}. This goes through all process-marked messages and subjects +them to the same splitting process that newly arriving messages go +through. (Whee! What a run-on sentence!) + + +@section TODO + +@table @bullet +@item +Say something about the cache. Though this belongs in the news reading +tips, right? Hm. +@end table + + +@c Local Variables: +@c TeX-master: "tutorials.texi" +@c End: diff --git a/texi/message.texi b/texi/message.texi index f910c06..4f3060f 100644 --- a/texi/message.texi +++ b/texi/message.texi @@ -421,8 +421,8 @@ Move to the signature of the message (@code{message-goto-signature}). Yank the message that's being replied to into the message buffer (@code{message-yank-original}). -@item C-c C-Y -@kindex C-c C-Y +@item C-c M-C-y +@kindex C-c M-C-y @findex message-yank-buffer Prompt for a buffer name and yank the contents of that buffer into the message buffer (@code{message-yank-buffer}). -- 1.7.10.4