+Sun Sep 10 00:39:41 1995 Lars Ingebrigtsen <lars@eyesore.no>
+
+ * gnus-msg.el (gnus-post-news): Set the name of the newsgroup for
+ later use.
+
+ * gnus.el (gnus-group-unsubscribe-group): Don't accept empty group
+ names.
+
+ * nnbabyl.el (nnbabyl-get-new-mail): If moving is unsuccessful,
+ don't pretend it went ok.
+ * nnmbox.el (nnmbox-get-new-mail): Ditto.
+ * nnfolder.el (nnfolder-get-new-mail): Ditto.
+ * nnmh.el (nnmh-get-new-mail): Ditto.
+ * nnml.el (nnml-get-new-mail): Ditto.
+
+ * gnus-vis.el (gnus-group-menu-hook, gnus-summary-menu-hook,
+ gnus-article-menu-hook, gnus-server-menu-hook,
+ gnus-browse-menu-hook): New hooks.
+
+Fri Sep 8 19:08:29 1995 Per Abrahamsen <abraham@dina.kvl.dk>
+
+ * nnmail.el (nnmail-request-post-buffer): Newer send to `sender',
+ don't put everything in the `To' header, filter the `CC' header
+ through `rmail-dont-reply-to'.
+
+Fri Sep 8 20:42:27 1995 Lars Ingebrigtsen <lars@eyesore.no>
+
+ * gnus.el (gnus-group-make-archive-group): Wrong interactive spec.
+ (gnus-select-method): Take `gnus-nntp-service' into account for
+ backwards compatability.
+
+Thu Sep 7 22:17:33 1995 Lars Ingebrigtsen <lars@eyesore.no>
+
+ * gnus.el (gnus-setup-news): Find new groups before finding
+ numbers of unread articles in the groups.
+
+ * gnus-score.el (gnus-summary-score-entry): Fuzzy wrong match
+ value.
+
+ * gnus.el ('gnus-load-hook): Run this hook while loading.
+
+ * gnus-vis.el (gnus-summary-make-menu-bar): Would re-generate the
+ summary buffers on each group entry.
+
+ * gnus-score.el (gnus-score-save): Don't try to write a score file
+ unless one can.
+
+Wed Sep 6 20:38:43 1995 Per Abrahamsen <abraham@dina.kvl.dk>
+
+ * gnus-cite.el (gnus-cite-parse-max-size): New variable.
+ (gnus-cite-parse-maybe): Use it.
+ (gnus-cite-parse): Move parser initialization to
+ `gnus-cite-parse-maybe' and reformat.
+ * gnus-vis.el (gnus-article-add-buttons): Force citation parsing
+ if called interactively.
+ * gnus-cite.el (gnus-article-highlight-citation): Ditto.
+ (gnus-article-hide-citation): Ditto.
+ (gnus-article-hide-citation-maybe): Ditto.
+ (gnus-article-highlight): Ditto.
+ (gnus-article-highlight-some): Ditto.
+ (gnus-article-hide): Ditto.
+
+Thu Sep 7 00:52:36 1995 Lars Ingebrigtsen <lars@eyesore.no>
+
+ * gnus.el (gnus-summary-show-thread): Expand hidden subthreads as
+ well.
+
+Wed Sep 6 20:38:43 1995 Per Abrahamsen <abraham@dina.kvl.dk>
+
+ * gnus-msg.el (gnus-use-followup-to): New value `ask' will make
+ Gnus always ask before obeying the followup-to header. Changed
+ default to t to confirm with the Seal.
+ (gnus-summary-followup): Support `ask' value of
+ `gnus-use-followup-to'.
+ * nntp.el (nntp-request-post-buffer): Support `ask' value of
+ `gnus-use-followup-to'.
+ * gnus.texi (Post): Document `ask' value of
+ `gnus-use-followup-to'.
+
+Thu Sep 7 00:20:44 1995 Lars Ingebrigtsen <lars@eyesore.no>
+
+ * gnus.el (gnus-select-newsgroup): Checked the server twice.
+ (gnus-select-newsgroup): Wouldn't respond properly to unwell
+ groups.
+
+Wed Sep 6 00:11:00 1995 Lars Ingebrigtsen <lars@eyesore.no>
+
+ * gnus.el (gnus-read-old-newsrc-el-file): Didn't parse options
+ lines from the .el file.
+ (gnus-summary-prepare-threads): When using empty make-false-root,
+ and the subject changed, that wouldn't be reflected in the summary
+ buffer.
+
+ * nnfolder.el (nnfolder-read-folder): Make absofuckinutely sure
+ that active numbers never, ever decrease.
+
+ * nnbabyl.el (nnbabyl-request-expire-articles): Remove all text
+ props.
+ (nnbabyl-read-mbox): If an rmail buffer is in rmail mode, make it
+ ordinary.
+
+ * gnus.el (gnus-summary-kill-thread): Did not kill hidden
+ threads.
+
+ * gnus-uu.el (gnus-uu-save-article): Didn't remove text props.
+
+ * gnus.el (gnus-group-make-articles-read): Would not do
+ crosspostings in one obscure instance.
+ (gnus-summary-update-mark): Would compute score-markedness even
+ when setting the process mark.
+
+Tue Sep 5 21:50:33 1995 Lars Ingebrigtsen <lars@eyesore.no>
+
+ * gnus-msg.el (gnus-inews-news): Would choke on trailing
+ commands. Also used `replace-regexp'.
+ (gnus-inews-news): Would, for some reason, fold all lines
+ sometimes.
+
+ * gnus-ems.el: Force our definition of `set-text-properties'. So
+ there!
+
+ * gnus.el (gnus-summary-sort-by-subject): Sorted oddly for (1/2)
+ stuff.
+ (gnus-request-body): Had gone missing for some reason.
+ (gnus-group-exit): Would quit out of an empty group buffer without
+ confirmation.
+
+Mon Sep 4 00:44:38 1995 Per Abrahamsen <abraham@dina.kvl.dk>
+
+ * custom.el (custom-field-face): Check that the face is defined.
+ (custom-face-tag): New function.
+ (custom-group-accept): Use it.
+ (custom-group-insert): Ditto.
+ (custom-type-properties): Give `face->other' a default value.
+ (custom-facep): New function.
+ (custom-face-lookup): Use it.
+
+Sun Sep 3 19:36:29 1995 Per Abrahamsen <abraham@dina.kvl.dk>
+
+ * custom.el (custom-local-type-properties): Added extra line of
+ documentation.
+ (custom-valid, custom-const-valid): Changed legal to valid in doc
+ string.
+ (custom-match): More documentation.
+ (custom-field-update): Doc. clarification.
+ (custom-field-accept): Ditto.
+ (custom-type-properties): More documentation.
+
+Fri Sep 1 15:39:56 1995 Per Abrahamsen <abraham@dina.kvl.dk>
+
+ * custom.el (menu-bar): Added XEmacs and Emacs 19.28 support.
+
+Fri Sep 1 15:39:56 1995 Per Abrahamsen <abraham@dina.kvl.dk>
+
+ * custom.el (plist-put): Fixed bogus definition.
+
+Thu Aug 31 15:21:23 1995 Per Abrahamsen <abraham@dina.kvl.dk>
+
+ * gnus-cus.el: Removed dead code.
+
Thu Aug 31 10:45:26 1995 Lars Magne Ingebrigtsen <lingebri@sunsci4.cern.ch>
+ * gnus.el: 5.0 is released.
+
* gnus-cus.el (gnus-face-dark-name-list): Use dark blue instead of
sky blue.
;;; Todo:
;;
;; - Toggle documentation in three states `none', `one-line', `full'.
-;; - Function to generate a XEmacs menu from a CUSTOM.
+;; - Function to generate an XEmacs menu from a CUSTOM.
;; - Write TeXinfo documentation.
;; - Make it possible to hide sections by clicking at the level.
;; - Declare AUC TeX variables.
;; - XEmacs port.
;; - Allow `URL', `info', and internal hypertext buttons.
;; - Support meta-variables and goal directed customization.
+;; - Make it easy to declare custom types independently.
+;; - Make it possible to declare default value and type for a single
+;; variable, storing the data in a symbol property.
+;; - Syntactic sugar for CUSTOM declarations.
+;; - Use W3 for variable documenation.
;;; Code:
otherwise the new PROP VAL pair is added. The new plist is returned;
use `(setq x (plist-put x prop val))' to be sure to use the new value.
The PLIST is modified by side effects."
- (while plist
- (cond ((eq (car plist) prop)
- (setcar (cdr plist) val)
- (setq plist nil))
- ((null (cdr (cdr plist)))
- (setcdr (cdr plist) (list prop val))
- (setq plist nil))
- (t
- (setq plist (cdr (cdr plist))))))))
+ (if (null plist)
+ (list prop val)
+ (let ((current plist))
+ (while current
+ (cond ((eq (car current) prop)
+ (setcar (cdr current) val)
+ (setq current nil))
+ ((null (cdr (cdr current)))
+ (setcdr (cdr current) (list prop val))
+ (setq current nil))
+ (t
+ (setq current (cdr (cdr current)))))))
+ plist)))
(or (fboundp 'match-string)
;; Introduced in Emacs 19.29.
(assq x (and (boundp 'global-face-data) global-face-data))))
t)))
-(if (facep 'underline)
+;; XEmacs and Emacs 19.29 facep does different things.
+(if (fboundp 'find-face)
+ (fset 'custom-facep 'find-face)
+ (fset 'custom-facep 'facep))
+
+(if (custom-facep 'underline)
()
;; No underline face in XEmacs 19.12.
(and (fboundp 'make-face)
;; Put it in the Help menu, if possible.
(if (string-match "XEmacs" emacs-version)
- nil
- ;; This will not work under XEmacs.
- (condition-case nil
- (global-set-key [ menu-bar help-menu customize ]
- '("Customize..." . customize))
- (error nil)))
+ ;; XEmacs (disabled because it doesn't work)
+ (add-menu-item '("Help") "Customize..." 'customize nil)
+ ;; Emacs 19.28 and earlier
+ (global-set-key [ menu-bar help customize ]
+ '("Customize..." . customize))
+ ;; Emacs 19.29 and later
+ (global-set-key [ menu-bar help-menu customize ]
+ '("Customize..." . customize)))
+
+;; XEmacs popup-menu stolen from w3.el.
+(defun custom-x-really-popup-menu (pos title menudesc)
+ "My hacked up function to do a blocking popup menu..."
+ (let ((echo-keystrokes 0)
+ event menu)
+ (while menudesc
+ (setq menu (cons (vector (car (car menudesc))
+ (list (car (car menudesc))) t) menu)
+ menudesc (cdr menudesc)))
+ (setq menu (cons title menu))
+ (popup-menu menu)
+ (catch 'popup-done
+ (while t
+ (setq event (next-command-event event))
+ (cond ((and (misc-user-event-p event) (stringp (car-safe (event-object event))))
+ (throw 'popup-done (event-object event)))
+ ((and (misc-user-event-p event)
+ (or (eq (event-object event) 'abort)
+ (eq (event-object event) 'menu-no-selection-hook)))
+ nil)
+ ((not (popup-menu-up-p))
+ (throw 'popup-done nil))
+ ((button-release-event-p event);; don't beep twice
+ nil)
+ (t
+ (beep)
+ (message "please make a choice from the menu.")))))))
;;; Categories:
;;
(defconst custom-type-properties
'((repeat (type . default)
+ ;; See `custom-match'.
(import . custom-repeat-import)
(eval . custom-repeat-eval)
(quote . custom-repeat-quote)
(del-tag . "[DEL]")
(add-tag . "[INS]"))
(pair (type . group)
+ ;; A cons-cell.
(accept . custom-pair-accept)
(eval . custom-pair-eval)
(import . custom-pair-import)
(valid . (lambda (c d) (consp d)))
(extract . custom-pair-extract))
(list (type . group)
+ ;; A lisp list.
(quote . custom-list-quote)
- (valid . (lambda (c d) (listp d)))
+ (valid . (lambda (c d)
+ (and (listp d) (not (eq custom-nil(car d))))))
(extract . custom-list-extract))
(group (type . default)
+ ;; See `custom-match'.
(face-tag . nil)
(eval . custom-group-eval)
(import . custom-group-import)
(insert . custom-group-insert)
(find . custom-group-find))
(toggle (type . choice)
+ ;; Booleans.
(data ((type . const)
(tag . "On ")
(default . t))
(tag . "Off")
(default . nil))))
(choice (type . default)
+ ;; See `custom-match'.
(query . custom-choice-query)
(accept . custom-choice-accept)
(extract . custom-choice-extract)
(default . __uninitialized__)
(type . const)))
(const (type . default)
+ ;; A `const' only matches a single lisp value.
(extract . (lambda (c f) (list (custom-default c))))
(validate . (lambda (c f) nil))
(valid . custom-const-valid)
(update . custom-const-update)
(insert . custom-const-insert))
(face-doc (type . doc)
+ ;; A variable containing a face.
(doc . "\
You can customize the look of Emacs by deciding which faces should be
used when. If you push one of the face buttons below, you will be
(type . list))
((prompt . "Other")
(face . custom-field-value)
+ (default . __uninitialized__)
(type . symbol))))
(file (type . string)
+ ;; A string containing a file or directory name.
(directory . nil)
(default-file . nil)
(query . custom-file-query))
(sexp (type . default)
+ ;; Any lisp expression.
(width . 40)
(default . (__uninitialized__ . "Uninitialized"))
(read . custom-sexp-read)
(write . custom-sexp-write))
(symbol (type . sexp)
+ ;; A lisp symbol.
(width . 40)
(valid . (lambda (c d) (symbolp d))))
(integer (type . sexp)
+ ;; A lisp integer.
(width . 10)
(valid . (lambda (c d) (integerp d))))
(string (type . default)
+ ;; A lisp string.
(width . 40)
(valid . (lambda (c d) (stringp d)))
(read . custom-string-read)
(write . custom-string-write))
(button (type . default)
+ ;; Push me.
(accept . ignore)
(extract . nil)
(validate . ignore)
(insert . custom-button-insert))
(doc (type . default)
+ ;; A documentation only entry with no value.
(header . nil)
(reset . ignore)
(extract . nil)
The format is `((SYMBOL (PROPERTY . VALUE)... )... )'.")
(defconst custom-local-type-properties nil
- "Local type properties.")
+ "Local type properties.
+Entries in this list take precedence over `custom-type-properties'.")
+
(make-variable-buffer-local 'custom-local-type-properties)
(defconst custom-nil '__uninitialized__
"Extract `tag' from CUSTOM."
(custom-property custom 'tag))
+(defun custom-face-tag (custom)
+ "Extract `face-tag' from CUSTOM."
+ (custom-property custom 'face-tag))
+
(defun custom-prompt (custom)
"Extract `prompt' from CUSTOM.
If none exist, default to `tag' or, failing that, `type'."
(custom-property custom 'padding))
(defun custom-valid (custom value)
- "Non-nil if CUSTOM may legally be set to VALUE."
+ "Non-nil if CUSTOM may validly be set to VALUE."
(and (not (and (listp value) (eq custom-invalid (car value))))
(funcall (custom-property custom 'valid) custom value)))
(defun custom-import (custom value)
- "Import CUSTOM VALUE from external variable."
+ "Import CUSTOM VALUE from external variable.
+
+This function change VALUE into a form that makes it easier to edit
+internally. What the internal form is exactly depends on CUSTOM.
+The internal form is returned."
(if (eq custom-nil value)
(list custom-nil)
(funcall (custom-property custom 'import) custom value)))
(funcall (custom-property custom 'write) custom value))))
(defun custom-read (custom string)
- "Convert CUSTOM field content STRING into external form."
+ "Convert CUSTOM field content STRING into lisp."
(condition-case nil
(funcall (custom-property custom 'read) custom string)
(error (cons custom-invalid string))))
(defun custom-match (custom values)
"Match CUSTOM with a list of VALUES.
+
Return a cons-cell where the car is the sublist of VALUES matching CUSTOM,
-and the cdr is the remaining VALUES."
+and the cdr is the remaining VALUES.
+
+A CUSTOM is actually a regular expression over the alphabet of lisp
+types. Most CUSTOM types are just doing a literal match, e.g. the
+`symbol' type matches any lisp symbol. The exceptions are:
+
+group: which corresponds to a `(' and `)' group in a regular expression.
+choice: which corresponds to a group of `|' in a regular expression.
+repeat: which corresponds to a `*' in a regular expression.
+optional: which corresponds to a `?', and isn't implemented yet."
(if (memq values (list custom-nil nil))
+ ;; Nothing matches the uninitialized or empty list.
(cons custom-nil nil)
(funcall (custom-property custom 'match) custom values)))
(funcall (custom-property (custom-field-custom field) 'query) field))
(defun custom-field-accept (field value &optional original)
- "Accept FIELD VALUE.
+ "Store a new value into field FIELD, taking it from VALUE.
If optional ORIGINAL is non-nil, concider VALUE for the original value."
(let ((inhibit-point-motion-hooks t))
(funcall (custom-property (custom-field-custom field) 'accept)
(let ((custom (custom-field-custom field)))
(if (stringp custom)
nil
- (funcall (custom-property custom 'face) field))))
+ (let ((face (funcall (custom-property custom 'face) field)))
+ (if (custom-facep face) face nil)))))
(defun custom-field-update (field)
- "Update content of FIELD."
+ "Update the screen appearance of FIELD to correspond with the field's value."
(let ((custom (custom-field-custom field)))
(if (stringp custom)
nil
value))))
(defun custom-repeat-accept (field value &optional original)
- "Enter content of editing FIELD."
+ "Store a new value into field FIELD, taking it from VALUE."
(let ((values (copy-sequence (custom-field-value field)))
(all (custom-field-value field))
(start (custom-field-start field))
result))
(defun custom-pair-accept (field value &optional original)
- "Enter content of editing FIELD with VALUE."
+ "Store a new value into field FIELD, taking it from VALUE."
(custom-group-accept field (list (car value) (cdr value)) original))
(defun custom-pair-eval (custom value)
(setq data (cdr data))))))
(defun custom-group-accept (field value &optional original)
- "Enter content of editing FIELD with VALUE."
+ "Store a new value into field FIELD, taking it from VALUE."
(let* ((values (custom-field-value field))
(custom (custom-field-custom field))
(from (custom-field-start field))
- (face-tag (custom-property custom 'face-tag))
+ (face-tag (custom-face-tag custom))
current)
(if face-tag
(put-text-property from (+ from (length (custom-tag custom)))
(from (point))
(compact (custom-compact custom))
(tag (custom-tag custom))
- (face-tag (custom-property custom 'face-tag)))
+ (face-tag (custom-face-tag custom)))
(cond (face-tag (custom-text-insert tag))
(tag (custom-tag-insert tag field)))
(or compact (custom-documentation-insert custom))
field))
(defun custom-choice-accept (field value &optional original)
- "Reset content of editing FIELD."
+ "Store a new value into field FIELD, taking it from VALUE."
(let ((custom (custom-field-custom field))
(start (custom-field-start field))
(end (custom-field-end field))
(setq current (car data)
data (cdr data))
(setq alist (cons (cons (custom-prompt current) current) alist)))
- (let ((answer (if (listp last-input-event)
- (x-popup-menu last-input-event
- (list tag (cons "" (reverse alist))))
- (let ((choice (completing-read (concat tag " (default "
- default "): ")
- alist nil t)))
- (if (or (null choice) (string-equal choice ""))
- (setq choice default))
- (cdr (assoc choice alist))))))
+ (let ((answer (cond ((and (fboundp 'button-press-event-p)
+ (fboundp 'popup-menu)
+ (button-press-event-p last-input-event))
+ (cdr (assoc (car (custom-x-really-popup-menu
+ last-input-event tag
+ (reverse alist)))
+ alist)))
+ ((listp last-input-event)
+ (x-popup-menu last-input-event
+ (list tag (cons "" (reverse alist)))))
+ (t
+ (let ((choice (completing-read (concat tag
+ " (default "
+ default
+ "): ")
+ alist nil t)))
+ (if (or (null choice) (string-equal choice ""))
+ (setq choice default))
+ (cdr (assoc choice alist)))))))
(if answer
(custom-field-accept field (custom-default answer)))))))
(or bg "default")
(or stipple "default")
bold italic underline))))
- (if (and (facep name)
+ (if (and (custom-facep name)
(fboundp 'make-face))
()
(make-face name)
(defun custom-face-hack (field value)
"Face that should be used for highlighting FIELD containing VALUE."
- (let ((custom (custom-field-custom field)))
- (eval (funcall (custom-property custom 'export) custom value))))
+ (let* ((custom (custom-field-custom field))
+ (face (eval (funcall (custom-property custom 'export)
+ custom value))))
+ (if (custom-facep face) face nil)))
(defun custom-const-insert (custom level)
"Insert field for CUSTOM at nesting LEVEL in customization buffer."
'face (custom-field-face field))))
(defun custom-const-valid (custom value)
- "Non-nil if CUSTOM can legally have the value VALUE."
+ "Non-nil if CUSTOM can validly have the value VALUE."
(equal (custom-default custom) value))
(defun custom-const-face (field)
(defun custom-default-export (custom value)
;; Convert CUSTOM's VALUE to external representation.
+ ;; See `custom-import'.
(if (custom-eval custom value)
(eval (car (custom-quote custom value)))
value))
field))
(defun custom-default-accept (field value &optional original)
- "Enter into FIELD the value VALUE."
+ "Store a new value into field FIELD, taking it from VALUE."
(if original
(custom-field-original-set field value))
(custom-field-value-set field value)
from (point)
(list 'custom-field field
'custom-tag field
- 'face (let ((face (custom-field-face field)))
- (if (facep face) face nil))
+ 'face (custom-field-face field)
front-sticky t))))
(defun custom-field-read (field)
(buffer-substring-no-properties (custom-field-start field)
(custom-field-end field))))
+;; Fields are shown in a special `active' face when point is inside
+;; it. You activate the field by moving point inside (entering) it
+;; and deactivate the field by moving point outside (leaving) it.
+
(defun custom-field-leave (field)
;; Deactivate FIELD.
(let ((before-change-functions nil)
(size (- end begin)))
(cond ((< size width)
(goto-char end)
- (condition-case nil
+ (if (fboundp 'insert-before-markers-and-inherit)
+ ;; Emacs 19.
(insert-before-markers-and-inherit
(make-string (- width size) padding))
- (error (insert-before-markers
- (make-string (- width size) padding))))
+ ;; XEmacs: BUG: Doesn't work!
+ (insert-before-markers (make-string (- width size) padding)))
(goto-char pos))
((> size width)
(let ((start (if (and (< (+ begin width) pos) (<= pos end))
(goto-char pos))))))
(defvar custom-field-changed nil)
-;; List of fields changed on the screen.
+;; List of fields changed on the screen but whose VALUE attribute has
+;; not yet been updated to reflect the new screen content.
(make-variable-buffer-local 'custom-field-changed)
(defun custom-field-parse (field)
;;; Customization:
+(defvar gnus-cite-parse-max-size 25000
+ "Maximum article size (in bytes) where parsing citations is allowed.
+Set it to nil to parse all articles.")
+
(defvar gnus-cite-prefix-regexp
"^[]>|:}+ ]*[]>|:}+]\\(.*>\\)?\\|^.*>"
"Regexp matching the longest possible citation prefix on a line.")
;;; Commands:
-(defun gnus-article-highlight-citation ()
+(defun gnus-article-highlight-citation (&optional force)
"Highlight cited text.
Each citation in the article will be highlighted with a different face.
The faces are taken from `gnus-cite-face-list'.
Lines matching `gnus-cite-attribution-postfix' and perhaps
`gnus-cite-attribution-prefix' are considered attribution lines."
- (interactive)
+ (interactive (list 'force))
;; Create dark or light faces if necessary.
(cond ((eq gnus-cite-face-list 'light)
(setq gnus-cite-face-list
(mapcar 'gnus-make-face gnus-face-dark-name-list))))
(save-excursion
(set-buffer gnus-article-buffer)
- (gnus-cite-parse-maybe)
+ (gnus-cite-parse-maybe force)
(let ((buffer-read-only nil)
(alist gnus-cite-prefix-alist)
(faces gnus-cite-face-list)
skip (gnus-cite-find-prefix number))
(gnus-cite-add-face number skip gnus-cite-attribution-face)))))
-(defun gnus-article-hide-citation ()
+(defun gnus-article-hide-citation (&optional force)
"Hide all cited text except attribution lines.
See the documentation for `gnus-article-highlight-citation'."
- (interactive)
+ (interactive (list 'force))
(save-excursion
(set-buffer gnus-article-buffer)
- (gnus-cite-parse-maybe)
+ (gnus-cite-parse-maybe force)
(let ((buffer-read-only nil)
(alist gnus-cite-prefix-alist)
(inhibit-point-motion-hooks t)
(interactive (list 'force))
(save-excursion
(set-buffer gnus-article-buffer)
- (gnus-cite-parse-maybe)
+ (gnus-cite-parse-maybe force)
(goto-char (point-min))
(search-forward "\n\n")
(let ((start (point))
;;; Internal functions:
-(defun gnus-cite-parse-maybe ()
+(defun gnus-cite-parse-maybe (&optional force)
;; Parse if the buffer has changes since last time.
(if (eq gnus-article-length (- (point-max) (point-min)))
()
- (setq gnus-article-length (- (point-max) (point-min)))
- (gnus-cite-parse)))
+ ;;Reset parser information.
+ (setq gnus-cite-prefix-alist nil
+ gnus-cite-attribution-alist nil
+ gnus-cite-loose-prefix-alist nil
+ gnus-cite-loose-attribution-alist nil)
+ ;; Parse if not too large.
+ (if (and (not force)
+ gnus-cite-parse-max-size
+ (> (buffer-size) gnus-cite-parse-max-size))
+ ()
+ (setq gnus-article-length (- (point-max) (point-min)))
+ (gnus-cite-parse))))
(defun gnus-cite-parse ()
;; Parse and connect citation prefixes and attribution lines.
- (setq gnus-cite-prefix-alist nil
- gnus-cite-attribution-alist nil
- gnus-cite-loose-prefix-alist nil
- gnus-cite-loose-attribution-alist nil)
+
;; Parse current buffer searching for citation prefixes.
(goto-char (point-min))
(or (search-forward "\n\n" nil t)
(setq gnus-cite-prefix-alist
(cons entry gnus-cite-prefix-alist)))
(t
- (setq gnus-cite-prefix-alist (cons entry gnus-cite-prefix-alist))
+ (setq gnus-cite-prefix-alist (cons entry
+ gnus-cite-prefix-alist))
;; Remove articles from other prefixes.
(let ((loop alist)
current)
(gnus-cite-match-attributions 'small nil
(lambda (prefix tag)
(if tag
- (concat "\\`" (regexp-quote prefix) "[ \t]*"
+ (concat "\\`"
+ (regexp-quote prefix) "[ \t]*"
(regexp-quote tag) ">"))))
;; Find loose supercite citations after attributions.
(gnus-cite-match-attributions 'small t
(lambda (prefix tag)
- (if tag (concat "\\<" (regexp-quote tag) "\\>"))))
+ (if tag (concat "\\<"
+ (regexp-quote tag)
+ "\\>"))))
;; Find loose supercite citations anywhere.
(gnus-cite-match-attributions 'small nil
(lambda (prefix tag)
- (if tag (concat "\\<" (regexp-quote tag) "\\>"))))
+ (if tag (concat "\\<"
+ (regexp-quote tag)
+ "\\>"))))
;; Find nested citations after attributions.
(gnus-cite-match-attributions 'small-if-unique t
(lambda (prefix tag)
"turquoise"))
(defvar gnus-face-dark-name-list
- '("dark blue" "firebrick"
- "dark green" "dark orange" "dark khaki" "dark violet"
- "dark turquoise"))
+ '("RoyalBlue" "firebrick"
+ "dark green" "OrangeRed" "dark khaki" "dark violet"
+ "SteelBlue4"))
+; CornflowerBlue SeaGreen OrangeRed SteelBlue4 DeepPink3
+; DarkOlviveGreen4
(custom-declare '()
'((tag . "GNUS")
You can choose between one of the predefined browsers, or `Other'.")
(name . gnus-button-url)
- (default . w3-fetch)
(calculate . (cond ((boundp 'browse-url-browser-function)
browse-url-browser-function)
((fboundp 'w3-fetch)
;; portable!
(or (face-differs-from-default-p 'underline)
(funcall 'set-face-underline-p 'underline t))
- (or (fboundp 'set-text-properties)
+ ; (or (fboundp 'set-text-properties) Fuckit!
(defun set-text-properties (start end props &optional buffer)
(if (or (null buffer) (bufferp buffer))
(if props
(put-text-property
start end (car props) (nth 1 props) buffer)
(setq props (nthcdr 2 props)))
- (remove-text-properties start end ())))))
+ (remove-text-properties start end ()))))
(defalias 'gnus-make-overlay 'make-extent)
(defalias 'gnus-overlay-put 'set-extent-property)
Optional argument YANK means yank original article.
The command \\[mh-yank-cur-msg] yank the original message into current buffer."
(let (from cc subject date to reply-to to-userid orig-to
+ references message-id
(config (current-window-configuration))
buffer)
(pop-to-buffer gnus-article-buffer)
reply-to (gnus-fetch-field "reply-to")
cc (gnus-fetch-field "cc")
orig-to (or (gnus-fetch-field "to") "")
- date (gnus-fetch-field "date"))
+ date (gnus-fetch-field "date")
+ references (gnus-fetch-field "references")
+ message-id (gnus-fetch-field "message-id"))
(setq to (or reply-to from))
(setq to-userid (mail-strip-quoted-names orig-to))
(if (or (string-match "," orig-to)
"In-reply-to:"
(concat
(substring from 0 (string-match " *at \\| *@ \\| *(\\| *<" from))
- "'s message of " date)))
+ "'s message of " date))
+ (nnheader-insert-references references message-id))
;; need this for mh-yank-cur-msg
(setq mh-sent-from-folder buffer)
If you want to insert the signature, you might put
`gnus-inews-insert-signature' in this hook.")
-(defvar gnus-use-followup-to 'use
+(defvar gnus-use-followup-to t
"*Specifies what to do with Followup-To header.
If nil, ignore the header. If it is t, use its value, but ignore
-`poster'. If it is neither nil nor t, which is the default, always use
-the value.")
+`poster'. If it is the symbol `ask', query the user before posting.
+If it is the symbol `use', always use the value.")
(defvar gnus-followup-to-function nil
"*A variable that contains a function that returns a followup address.
(set-buffer gnus-article-buffer)
(if (and gnus-use-followup-to
(string-equal "poster" (gnus-fetch-field "followup-to"))
- (or (not (eq gnus-use-followup-to t))
+ (or (not (memq gnus-use-followup-to '(t ask)))
(not (gnus-y-or-n-p
"Do you want to ignore `Followup-To: poster'? "))))
;; Mail to the poster.
(not gnus-expert-user)
post (not group)
(progn
- (setq group
- (completing-read "Group: " gnus-active-hashtb))
+ (setq gnus-newsgroup-name
+ (setq group
+ (completing-read "Group: " gnus-active-hashtb)))
(or subject
(setq subject (read-string "Subject: ")))))
(setq mail-reply-buffer gnus-article-copy)
;; Correct newsgroups field: change sequence of spaces to comma and
;; eliminate spaces around commas. Eliminate imbedded line breaks.
(goto-char (point-min))
- (if (search-forward-regexp "^Newsgroups: +" nil t)
+ (if (re-search-forward "^Newsgroups: +" nil t)
(save-restriction
(narrow-to-region
(point)
- (if (re-search-forward "^[^ \t]" nil 'end)
+ (if (re-search-forward "^[^ \t]" nil t)
(match-beginning 0)
- (point-max)))
+ (forward-line 1)
+ (point)))
(goto-char (point-min))
- (replace-regexp "\n[ \t]+" " ") ;No line breaks (too confusing)
+ (while (re-search-forward "\n[ \t]+" nil t)
+ (replace-match " " t t)) ;No line breaks (too confusing)
(goto-char (point-min))
- (replace-regexp "[ \t\n]*,[ \t\n]*\\|[ \t]+" ",")))
+ (while (re-search-forward "[ \t\n]*,[ \t\n]*\\|[ \t]+" nil t)
+ (replace-match "," t t))
+ (goto-char (point-min))
+ ;; Remove a trailing comma.
+ (if (re-search-forward ",$" nil t)
+ (replace-match "" t t))))
;; Added by Per Abrahamsen <abraham@iesd.auc.dk>.
;; Help save the the world!
(let ((newsgroups (mail-fetch-field "newsgroups"))
(followup-to (mail-fetch-field "followup-to"))
groups to)
- (if (and (string-match "," newsgroups) (not followup-to))
+ (if (and newsgroups
+ (string-match "," newsgroups) (not followup-to))
(progn
(while (string-match "," newsgroups)
(setq groups
(while follow-to
(insert (car (car follow-to)) ": " (cdr (car follow-to)) "\n")
(setq follow-to (cdr follow-to)))))
- ;; Fold long references line to follow RFC1036.
- (mail-position-on-field "References")
- (let ((begin (- (point) (length "References: ")))
- (fill-column 78)
- (fill-prefix "\t"))
- (if references (insert references))
- (if (and references message-id) (insert " "))
- (if message-id (insert message-id))
- ;; The region must end with a newline to fill the region
- ;; without inserting extra newline.
- (fill-region-as-paragraph begin (1+ (point))))
+ (nnheader-insert-references references message-id)
(goto-char (point-min))
(re-search-forward
(concat "^" (regexp-quote mail-header-separator) "$"))
;; Regexp is the default type.
(if (eq type t) (setq type 'r))
;; Simplify matches...
- (if (or (eq type 'r) (eq type 's) (eq type nil))
- (setq match (gnus-simplify-subject-re match)))
+ (cond ((or (eq type 'r) (eq type 's) (eq type nil))
+ (setq match (if match (gnus-simplify-subject-re match) "")))
+ ((eq type 'f)
+ (setq match (gnus-simplify-subject-fuzzy match))))
(let ((score (gnus-score-default score))
(header (downcase header)))
(and prompt (setq match (read-string
(and (= score gnus-score-interactive-default-score)
(setq score nil))
(let ((new (cond
- ((eq type 'f)
- (list (gnus-simplify-subject-fuzzy match)
- score (and date (gnus-day-number date)) type))
(type
(list match score (and date (gnus-day-number date)) type))
(date
(if (zerop (buffer-size))
(delete-file file)
;; There are scores, so we write the file.
- (write-region (point-min) (point-max) file nil 'silent))))))
+ (and (file-writable-p file)
+ (write-region (point-min) (point-max)
+ file nil 'silent)))))))
(kill-buffer (current-buffer)))))
(defun gnus-score-headers (score-files &optional trace)
(save-excursion
(save-restriction
(set-buffer buffer)
+ (set-text-properties (point-min) (point-max) nil)
+ ;; These two are necessary for XEmacs 19.12 fascism.
+ (put-text-property (point-min) (point-max) 'invisible nil)
+ (put-text-property (point-min) (point-max) 'intangible nil)
(goto-char (point-min))
(re-search-forward "\n\n")
(setq body (buffer-substring (1- (point)) (point-max)))
(require 'gnus-ems)
(require gnus-easymenu)
(require 'custom)
+
+(defvar gnus-group-menu-hook nil
+ "*Hook run after the creation of the group mode menu.")
+
+(defvar gnus-summary-menu-hook nil
+ "*Hook run after the creation of the summary mode menu.")
+
+(defvar gnus-article-menu-hook nil
+ "*Hook run after the creation of the article mode menu.")
+
+(defvar gnus-server-menu-hook nil
+ "*Hook run after the creation of the server mode menu.")
+
+(defvar gnus-browse-menu-hook nil
+ "*Hook run after the creation of the browse mode menu.")
;;; Summary highlights.
["Send a bug report" gnus-bug t]
["Send a mail" gnus-group-mail t]
["Post an article" gnus-group-post-news t]
- ["Customize score file" gnus-score-customize (not (string-match "XEmacs" emacs-version)) ]
+ ["Customize score file" gnus-score-customize
+ (not (string-match "XEmacs" emacs-version)) ]
["Check for new news" gnus-group-get-new-news t]
["Delete bogus groups" gnus-group-check-bogus-groups t]
["Find new newsgroups" gnus-find-new-newsgroups t]
["Edit global kill file" gnus-group-edit-global-kill t]
["Sort group buffer" gnus-group-sort-groups t]
))
+ (run-hooks 'gnus-group-menu-hook)
)))
;; Server mode
(gnus-visual-turn-off-edit-menu 'server)
(or
(boundp 'gnus-server-menu)
- (easy-menu-define
- gnus-server-menu
- gnus-server-mode-map
- ""
- '("Server"
- ["Add" gnus-server-add-server t]
- ["Browse" gnus-server-read-server t]
- ["List" gnus-server-list-servers t]
- ["Kill" gnus-server-kill-server t]
- ["Yank" gnus-server-yank-server t]
- ["Copy" gnus-server-copy-server t]
- ["Edit" gnus-server-edit-server t]
- ["Exit" gnus-server-exit t]
- ))))
+ (progn
+ (easy-menu-define
+ gnus-server-menu
+ gnus-server-mode-map
+ ""
+ '("Server"
+ ["Add" gnus-server-add-server t]
+ ["Browse" gnus-server-read-server t]
+ ["List" gnus-server-list-servers t]
+ ["Kill" gnus-server-kill-server t]
+ ["Yank" gnus-server-yank-server t]
+ ["Copy" gnus-server-copy-server t]
+ ["Edit" gnus-server-edit-server t]
+ ["Exit" gnus-server-exit t]
+ ))
+ (run-hooks 'gnus-server-menu-hook)
+ )))
;; Browse mode
(defun gnus-browse-make-menu-bar ()
(gnus-visual-turn-off-edit-menu 'browse)
(or
(boundp 'gnus-browse-menu)
- (easy-menu-define
- gnus-browse-menu
- gnus-browse-mode-map
- ""
- '("Browse"
- ["Subscribe" gnus-browse-unsubscribe-current-group t]
- ["Read" gnus-group-read-group t]
- ["Exit" gnus-browse-exit t]
- ))))
+ (progn
+ (easy-menu-define
+ gnus-browse-menu
+ gnus-browse-mode-map
+ ""
+ '("Browse"
+ ["Subscribe" gnus-browse-unsubscribe-current-group t]
+ ["Read" gnus-group-read-group t]
+ ["Exit" gnus-browse-exit t]
+ ))
+ (run-hooks 'gnus-browse-menu-hook)
+ )))
+
;; Summary buffer
(defun gnus-summary-make-menu-bar ()
(gnus-visual-turn-off-edit-menu 'summary)
(or
- (boundp 'gnus-summary-mark-menu)
+ (boundp 'gnus-summary-misc-menu)
(progn
(easy-menu-define
["Fetch article with id..." gnus-summary-refer-article t]
["Redisplay" gnus-summary-show-article t]))
+
(easy-menu-define
gnus-summary-thread-menu
["Reply & followup and yank" gnus-summary-followup-and-reply-with-original t]
["Uuencode and post" gnus-uu-post-news t]
))
-
+ (run-hooks 'gnus-summary-menu-hook)
)))
(defun gnus-score-set-default (var value)
["Remove carriage return" gnus-article-remove-cr t]
["Remove quoted-unreadable" gnus-article-de-quoted-unreadable t]
))
+ (run-hooks 'gnus-article-menu-hook)
)))
;;;
(goto-char pos)
(error "No buttons found")))))
-(defun gnus-article-highlight ()
+(defun gnus-article-highlight (&optional force)
"Highlight current article.
This function calls `gnus-article-highlight-headers',
`gnus-article-highlight-citation',
`gnus-article-highlight-signature', and `gnus-article-add-buttons' to
do the highlighting. See the documentation for those functions."
- (interactive)
+ (interactive (list 'force))
(gnus-article-highlight-headers)
- (gnus-article-highlight-citation)
+ (gnus-article-highlight-citation force)
(gnus-article-highlight-signature)
- (gnus-article-add-buttons))
+ (gnus-article-add-buttons force))
-(defun gnus-article-highlight-some ()
+(defun gnus-article-highlight-some (&optional force)
"Highlight current article.
This function calls `gnus-article-highlight-headers',
`gnus-article-highlight-signature', and `gnus-article-add-buttons' to
do the highlighting. See the documentation for those functions."
- (interactive)
+ (interactive (list 'force))
(gnus-article-highlight-headers)
(gnus-article-highlight-signature)
(gnus-article-add-buttons))
-(defun gnus-article-hide ()
+(defun gnus-article-hide (&optional force)
"Hide current article.
This function calls `gnus-article-hide-headers',
`gnus-article-hide-citation-maybe', and `gnus-article-hide-signature'
to do the hiding. See the documentation for those functions."
- (interactive)
+ (interactive (list 'force))
(gnus-article-hide-headers)
- (gnus-article-hide-citation-maybe)
+ (gnus-article-hide-citation-maybe force)
(gnus-article-hide-signature))
(defun gnus-article-highlight-headers ()
(add-text-properties (match-end 0) (point-max)
gnus-hidden-properties)))))
-(defun gnus-article-add-buttons ()
+(defun gnus-article-add-buttons (&optional force)
"Find external references in article and make them to buttons.
External references are things like message-ids and URLs, as specified by
`gnus-button-alist'."
- (interactive)
+ (interactive (list 'force))
(if (eq gnus-button-last gnus-button-alist)
()
(setq gnus-button-regexp (mapconcat 'car gnus-button-alist "\\|")
gnus-button-last gnus-button-alist))
(save-excursion
(set-buffer gnus-article-buffer)
- (gnus-cite-parse-maybe)
+ (gnus-cite-parse-maybe force)
(let ((buffer-read-only nil)
(inhibit-point-motion-hooks t)
(case-fold-search t))
;;; Code:
+(eval '(run-hooks 'gnus-load-hook))
+
(require 'mail-utils)
(require 'timezone)
(require 'nnheader)
;; Customization variables
+;; Don't touch this variable.
+(defvar gnus-nntp-service "nntp"
+ "*NNTP service name (\"nntp\" or 119).
+This is an obsolete variable, which is scarcely used. If you use an
+nntp server for your newsgroup and want to change the port number
+used to 899, you would say something along these lines:
+
+ (setq gnus-select-method '(nntp \"my.nntp.server\" (nntp-port-number 899)))")
+
(defvar gnus-select-method
- (list 'nntp (or (getenv "NNTPSERVER")
- (if (and gnus-default-nntp-server
- (not (string= gnus-default-nntp-server "")))
- gnus-default-nntp-server)
- (system-name)))
+ (nconc
+ (list 'nntp (or (getenv "NNTPSERVER")
+ (if (and gnus-default-nntp-server
+ (not (string= gnus-default-nntp-server "")))
+ gnus-default-nntp-server)
+ (system-name)))
+ (if (equal gnus-nntp-service "nntp") nil (list gnus-nntp-service)))
"*Default method for selecting a newsgroup.
This variable should be a list, where the first element is how the
news is to be fetched, the second is the address.
This variable is semi-obsolete. Use the `gnus-select-method'
variable instead.")
-(defvar gnus-nntp-service "nntp"
- "*NNTP service name (\"nntp\" or 119).
-This is an obsolete variable, which is scarcely used. If you use an
-nntp server for your newsgroup and want to change the port number
-used to 899, you would say something along these lines:
-
- (setq gnus-select-method '(nntp \"my.nntp.server\" (nntp-port-number 899)))")
-
(defvar gnus-startup-file "~/.newsrc"
"*Your `.newsrc' file.
`.newsrc-SERVER' will be used instead if that exists.")
(defvar gnus-open-server-hook nil
"*A hook called just before opening connection to the news server.")
+(defvar gnus-load-hook nil
+ "*A hook run while Gnus is loaded.")
+
(defvar gnus-startup-hook nil
"*A hook called at startup.
This hook is called after Gnus is connected to the NNTP server.")
"gnus-bug@ifi.uio.no (The Gnus Bugfixing Girls + Boys)"
"The mail address of the Gnus maintainers.")
-(defconst gnus-version "Gnus v5.0"
+(defconst gnus-version "Gnus v5.0.1"
"Version number for this version of Gnus.")
(defvar gnus-info-nodes
(define-key gnus-group-mode-map "\177" 'gnus-group-prev-unread-group)
(define-key gnus-group-mode-map "N" 'gnus-group-next-group)
(define-key gnus-group-mode-map "P" 'gnus-group-prev-group)
- (define-key gnus-group-mode-map "\M-n" 'gnus-group-next-unread-group-same-level)
- (define-key gnus-group-mode-map "\M-p" 'gnus-group-prev-unread-group-same-level)
+ (define-key gnus-group-mode-map
+ "\M-n" 'gnus-group-next-unread-group-same-level)
+ (define-key gnus-group-mode-map
+ "\M-p" 'gnus-group-prev-unread-group-same-level)
(define-key gnus-group-mode-map "," 'gnus-group-best-unread-group)
(define-key gnus-group-mode-map "." 'gnus-group-first-unread-group)
(define-key gnus-group-mode-map "u" 'gnus-group-unsubscribe-current-group)
(gnus-group-make-group
(gnus-group-real-name name)
(list 'nndoc name
- (list 'nndoc-address (concat (file-name-as-directory (car path)) "doc.txt"))
+ (list 'nndoc-address
+ (concat (file-name-as-directory (car path)) "doc.txt"))
(list 'nndoc-article-type 'mbox))))
(gnus-group-position-cursor))
(defun gnus-group-make-archive-group (&optional all)
"Create the (ding) Gnus archive group of the most recent articles.
Given a prefix, create a full group."
- (interactive)
+ (interactive "P")
(let ((group (gnus-group-prefixed-name
(if all "ding.archives" "ding.recent") '(nndir ""))))
(and (gnus-gethash group gnus-newsrc-hashtb)
"Group: " gnus-active-hashtb nil
(memq gnus-select-method gnus-have-read-active-file))))
(let ((newsrc (gnus-gethash group gnus-newsrc-hashtb)))
- (cond (newsrc
- ;; Toggle subscription flag.
- (gnus-group-change-level
- newsrc (if level level (if (<= (nth 1 (nth 2 newsrc))
- gnus-level-subscribed)
- (1+ gnus-level-subscribed)
- gnus-level-default-subscribed)))
- (gnus-group-update-group group))
- ((and (stringp group)
- (or (not (memq gnus-select-method gnus-have-read-active-file))
- (gnus-gethash group gnus-active-hashtb)))
- ;; Add new newsgroup.
- (gnus-group-change-level
- group
- (if level level gnus-level-default-subscribed)
- (or (and (member group gnus-zombie-list)
- gnus-level-zombie)
- gnus-level-killed)
- (and (gnus-group-group-name)
- (gnus-gethash (gnus-group-group-name) gnus-newsrc-hashtb)))
- (gnus-group-update-group group))
- (t (error "No such newsgroup: %s" group)))
+ (cond
+ ((string-match "^[ \t]$" group)
+ (error "Empty group name"))
+ (newsrc
+ ;; Toggle subscription flag.
+ (gnus-group-change-level
+ newsrc (if level level (if (<= (nth 1 (nth 2 newsrc))
+ gnus-level-subscribed)
+ (1+ gnus-level-subscribed)
+ gnus-level-default-subscribed)))
+ (gnus-group-update-group group))
+ ((and (stringp group)
+ (or (not (memq gnus-select-method gnus-have-read-active-file))
+ (gnus-gethash group gnus-active-hashtb)))
+ ;; Add new newsgroup.
+ (gnus-group-change-level
+ group
+ (if level level gnus-level-default-subscribed)
+ (or (and (member group gnus-zombie-list)
+ gnus-level-zombie)
+ gnus-level-killed)
+ (and (gnus-group-group-name)
+ (gnus-gethash (gnus-group-group-name) gnus-newsrc-hashtb)))
+ (gnus-group-update-group group))
+ (t (error "No such newsgroup: %s" group)))
(gnus-group-position-cursor)))
(defun gnus-group-transpose-groups (n)
(interactive "P")
(setq gnus-current-kill-article article)
(gnus-kill-file-edit-file group)
- (gnus-message 6
- (substitute-command-keys
- "Editing a global kill file (Type \\[gnus-kill-file-exit] to exit)")))
+ (gnus-message
+ 6
+ (substitute-command-keys
+ "Editing a global kill file (Type \\[gnus-kill-file-exit] to exit)")))
(defun gnus-group-edit-local-kill (article group)
"Edit a local kill file."
The hook `gnus-exit-gnus-hook' is called before actually exiting."
(interactive)
(if (or noninteractive ;For gnus-batch-kill
- (zerop (buffer-size)) ;No news is good news.
(not (gnus-server-opened gnus-select-method)) ;NNTP connection closed
(not gnus-interactive-exit) ;Without confirmation
gnus-expert-user
(define-key gnus-summary-mode-map "\M-p" 'gnus-summary-prev-unread-subject)
(define-key gnus-summary-mode-map "." 'gnus-summary-first-unread-article)
(define-key gnus-summary-mode-map "," 'gnus-summary-best-unread-article)
- (define-key gnus-summary-mode-map "\M-s" 'gnus-summary-search-article-forward)
- (define-key gnus-summary-mode-map "\M-r" 'gnus-summary-search-article-backward)
+ (define-key gnus-summary-mode-map
+ "\M-s" 'gnus-summary-search-article-forward)
+ (define-key gnus-summary-mode-map
+ "\M-r" 'gnus-summary-search-article-backward)
(define-key gnus-summary-mode-map "<" 'gnus-summary-beginning-of-article)
(define-key gnus-summary-mode-map ">" 'gnus-summary-end-of-article)
(define-key gnus-summary-mode-map "j" 'gnus-summary-goto-subject)
(define-key gnus-summary-mode-map "E" 'gnus-summary-mark-as-expirable)
(define-key gnus-summary-mode-map "\M-u" 'gnus-summary-clear-mark-forward)
(define-key gnus-summary-mode-map "\M-U" 'gnus-summary-clear-mark-backward)
- (define-key gnus-summary-mode-map "k" 'gnus-summary-kill-same-subject-and-select)
+ (define-key gnus-summary-mode-map
+ "k" 'gnus-summary-kill-same-subject-and-select)
(define-key gnus-summary-mode-map "\C-k" 'gnus-summary-kill-same-subject)
(define-key gnus-summary-mode-map "\M-\C-k" 'gnus-summary-kill-thread)
(define-key gnus-summary-mode-map "\M-\C-l" 'gnus-summary-lower-thread)
(define-key gnus-summary-mode-map "\C-w" 'gnus-summary-mark-region-as-read)
(define-key gnus-summary-mode-map "\C-t" 'gnus-summary-toggle-truncation)
(define-key gnus-summary-mode-map "?" 'gnus-summary-mark-as-dormant)
- (define-key gnus-summary-mode-map "\C-c\M-\C-s" 'gnus-summary-show-all-expunged)
- (define-key gnus-summary-mode-map "\C-c\C-s\C-n" 'gnus-summary-sort-by-number)
- (define-key gnus-summary-mode-map "\C-c\C-s\C-a" 'gnus-summary-sort-by-author)
- (define-key gnus-summary-mode-map "\C-c\C-s\C-s" 'gnus-summary-sort-by-subject)
+ (define-key gnus-summary-mode-map
+ "\C-c\M-\C-s" 'gnus-summary-show-all-expunged)
+ (define-key gnus-summary-mode-map
+ "\C-c\C-s\C-n" 'gnus-summary-sort-by-number)
+ (define-key gnus-summary-mode-map
+ "\C-c\C-s\C-a" 'gnus-summary-sort-by-author)
+ (define-key gnus-summary-mode-map
+ "\C-c\C-s\C-s" 'gnus-summary-sort-by-subject)
(define-key gnus-summary-mode-map "\C-c\C-s\C-d" 'gnus-summary-sort-by-date)
(define-key gnus-summary-mode-map "\C-c\C-s\C-i" 'gnus-summary-sort-by-score)
(define-key gnus-summary-mode-map "=" 'gnus-summary-expand-window)
- (define-key gnus-summary-mode-map "\C-x\C-s" 'gnus-summary-reselect-current-group)
+ (define-key gnus-summary-mode-map
+ "\C-x\C-s" 'gnus-summary-reselect-current-group)
(define-key gnus-summary-mode-map "\M-g" 'gnus-summary-rescan-group)
(define-key gnus-summary-mode-map "w" 'gnus-summary-stop-page-breaking)
(define-key gnus-summary-mode-map "\C-c\C-r" 'gnus-summary-caesar-message)
(define-key gnus-summary-mode-map gnus-mouse-2 'gnus-mouse-pick-article)
(define-key gnus-summary-mode-map "m" 'gnus-summary-mail-other-window)
(define-key gnus-summary-mode-map "a" 'gnus-summary-post-news)
- (define-key gnus-summary-mode-map "x" 'gnus-summary-remove-lines-marked-as-read)
+ (define-key gnus-summary-mode-map
+ "x" 'gnus-summary-remove-lines-marked-as-read)
; (define-key gnus-summary-mode-map "X" 'gnus-summary-remove-lines-marked-with)
(define-key gnus-summary-mode-map "s" 'gnus-summary-isearch-article)
(define-key gnus-summary-mode-map "t" 'gnus-summary-toggle-header)
(define-key gnus-summary-mark-map "B" 'gnus-summary-remove-bookmark)
(define-key gnus-summary-mark-map "#" 'gnus-summary-mark-as-processable)
(define-key gnus-summary-mark-map "\M-#" 'gnus-summary-unmark-as-processable)
- (define-key gnus-summary-mark-map "\M-r" 'gnus-summary-remove-lines-marked-as-read)
- (define-key gnus-summary-mark-map "\M-\C-r" 'gnus-summary-remove-lines-marked-with)
+ (define-key gnus-summary-mark-map
+ "\M-r" 'gnus-summary-remove-lines-marked-as-read)
+ (define-key gnus-summary-mark-map
+ "\M-\C-r" 'gnus-summary-remove-lines-marked-with)
(define-key gnus-summary-mark-map "D" 'gnus-summary-show-all-dormant)
(define-key gnus-summary-mark-map "\M-D" 'gnus-summary-hide-all-dormant)
(define-key gnus-summary-mark-map "S" 'gnus-summary-show-all-expunged)
(define-key gnus-summary-mark-map "C" 'gnus-summary-catchup)
(define-key gnus-summary-mark-map "H" 'gnus-summary-catchup-to-here)
(define-key gnus-summary-mark-map "\C-c" 'gnus-summary-catchup-all)
- (define-key gnus-summary-mark-map "k" 'gnus-summary-kill-same-subject-and-select)
+ (define-key gnus-summary-mark-map
+ "k" 'gnus-summary-kill-same-subject-and-select)
(define-key gnus-summary-mark-map "K" 'gnus-summary-kill-same-subject)
(define-prefix-command 'gnus-summary-mscore-map)
(define-key gnus-summary-exit-map "E" 'gnus-summary-exit-no-update)
(define-key gnus-summary-exit-map "Q" 'gnus-summary-exit)
(define-key gnus-summary-exit-map "Z" 'gnus-summary-exit)
- (define-key gnus-summary-exit-map "n" 'gnus-summary-catchup-and-goto-next-group)
+ (define-key gnus-summary-exit-map
+ "n" 'gnus-summary-catchup-and-goto-next-group)
(define-key gnus-summary-exit-map "R" 'gnus-summary-reselect-current-group)
(define-key gnus-summary-exit-map "G" 'gnus-summary-rescan-group)
(define-key gnus-summary-exit-map "N" 'gnus-summary-next-group)
(define-key gnus-summary-wash-hide-map "h" 'gnus-article-hide-headers)
(define-key gnus-summary-wash-hide-map "s" 'gnus-article-hide-signature)
(define-key gnus-summary-wash-hide-map "c" 'gnus-article-hide-citation)
- (define-key gnus-summary-wash-hide-map "\C-c" 'gnus-article-hide-citation-maybe)
+ (define-key gnus-summary-wash-hide-map
+ "\C-c" 'gnus-article-hide-citation-maybe)
(define-prefix-command 'gnus-summary-wash-highlight-map)
(define-key gnus-summary-wash-map "H" 'gnus-summary-wash-highlight-map)
(define-key gnus-summary-wash-highlight-map "a" 'gnus-article-highlight)
- (define-key gnus-summary-wash-highlight-map "h" 'gnus-article-highlight-headers)
- (define-key gnus-summary-wash-highlight-map "c" 'gnus-article-highlight-citation)
- (define-key gnus-summary-wash-highlight-map "s" 'gnus-article-highlight-signature)
+ (define-key gnus-summary-wash-highlight-map
+ "h" 'gnus-article-highlight-headers)
+ (define-key gnus-summary-wash-highlight-map
+ "c" 'gnus-article-highlight-citation)
+ (define-key gnus-summary-wash-highlight-map
+ "s" 'gnus-article-highlight-signature)
(define-prefix-command 'gnus-summary-wash-time-map)
(define-key gnus-summary-wash-map "T" 'gnus-summary-wash-time-map)
(gnus-simplify-subject-re
(mail-header-subject (car headers)))))
(progn
- (if (not (< (or (cdr (assq (mail-header-number (car headers))
+ (if (not (< (or (cdr (assq (mail-header-number
+ (car headers))
gnus-newsgroup-scored))
default) below))
(setq new-roots (cons (car headers) new-roots))
((eq gnus-summary-make-false-root 'dummy)
;; We output a dummy root.
(gnus-summary-insert-dummy-line
- nil header (mail-header-number (car (car (cdr (car thread)))))))
+ nil header (mail-header-number
+ (car (car (cdr (car thread)))))))
(t
;; We do not make a root for the gathered
;; sub-threads at all.
gnus-ancient-mark)))
(memq number gnus-newsgroup-replied)
(memq number gnus-newsgroup-expirable)
- (if (and (eq gnus-summary-make-false-root 'empty)
- (memq number gnus-tmp-gathered))
- gnus-summary-same-subject
- (if (or (zerop level)
- (and gnus-thread-ignore-subject
- (not (string=
- (gnus-simplify-subject-re
- gnus-tmp-prev-subject)
- (gnus-simplify-subject-re
- subject)))))
- subject
- gnus-summary-same-subject))
+ (cond
+ ((and gnus-thread-ignore-subject
+ (not (string=
+ (gnus-simplify-subject-re gnus-tmp-prev-subject)
+ (gnus-simplify-subject-re subject))))
+ subject)
+ ((zerop level)
+ (if (and (eq gnus-summary-make-false-root 'empty)
+ (memq number gnus-tmp-gathered))
+ gnus-summary-same-subject
+ subject))
+ (t gnus-summary-same-subject))
(and (eq gnus-summary-make-false-root 'adopt)
(memq number gnus-tmp-gathered))
(cdr (assq number gnus-newsgroup-scored)))
;; Do the async thing, if that is required.
(if gnus-newsgroup-async
(setq gnus-newsgroup-threads
- (mapcar (lambda (h) (cons (mail-header-number h) (mail-header-lines h)))
+ (mapcar (lambda (h)
+ (cons (mail-header-number h) (mail-header-lines h)))
headers)))
(while headers
(let* ((entry (gnus-gethash group gnus-newsrc-hashtb))
(info (nth 2 entry))
articles)
- (gnus-check-server
- (setq gnus-current-select-method (gnus-find-method-for-group group)))
- (or (gnus-check-server gnus-current-select-method)
+ (or (gnus-check-server
+ (setq gnus-current-select-method (gnus-find-method-for-group group)))
(error "Couldn't open server"))
- (or (and (null entry)
- (gnus-activate-group group))
- (and (eq (car entry) t)
- (gnus-activate-group (car info)))
- (gnus-request-group group t)
- (progn
+ (or (and entry (not (eq (car entry) t))) ; Either it's active...
+ (gnus-activate-group group) ; Or we can activate it...
+ (progn ; Or we bug out.
(kill-buffer (current-buffer))
(error "Couldn't request group %s: %s"
group (gnus-status-message group))))
(setq gnus-newsgroup-begin
(mail-header-number (car gnus-newsgroup-headers)))
(setq gnus-newsgroup-end
- (mail-header-number (gnus-last-element gnus-newsgroup-headers))))
+ (mail-header-number
+ (gnus-last-element gnus-newsgroup-headers))))
(setq gnus-reffed-article-number -1)
;; GROUP is successfully selected.
(or gnus-newsgroup-headers t)))))
(let ((ids articles)
(ticked (cdr (assq 'tick (nth 3 info))))
(dormant (cdr (assq 'dormant (nth 3 info))))
- id)
+ id first)
(setq exps nil)
(while ids
(setq id (car ids))
+ (if (and first (> id (cdr active)))
+ (progn
+ ;; We'll end up in this situation in one particular
+ ;; obscure situation. If you re-scan a group and get
+ ;; a new article that is cross-posted to a different
+ ;; group that has not been re-scanned, you might get
+ ;; crossposted article that has a higher number than
+ ;; Gnus believes possible. So we re-activate this
+ ;; group as well. This might mean doing the
+ ;; crossposting thingie will *increase* the number
+ ;; of articles in some groups. Tsk, tsk.
+ (setq active (or (gnus-activate-group group) active))))
(if (or (> id (cdr active))
(< id (car active))
(memq id ticked)
(progn
(mail-header-set-xref
(car (symbol-value dep))
- (concat (or (mail-header-xref (car (symbol-value dep))) "")
+ (concat (or (mail-header-xref
+ (car (symbol-value dep))) "")
(or (mail-header-xref header) "")))
(setq header nil))
(setcar (symbol-value dep) header))
(mode major-mode)
(buf (current-buffer)))
(run-hooks 'gnus-summary-prepare-exit-hook)
- (gnus-summary-update-info) ; Make all changes in this group permanent.
+ ;; Make all changes in this group permanent.
+ (gnus-summary-update-info)
(set-buffer buf)
(and gnus-use-cache (gnus-cache-possibly-remove-articles))
;; Make sure where I was, and go to next newsgroup.
(string-to-int
(completing-read
"Article number: "
- (mapcar (lambda (headers) (list (int-to-string (mail-header-number headers))))
+ (mapcar (lambda (headers)
+ (list (int-to-string (mail-header-number headers))))
gnus-newsgroup-headers)
nil 'require-match))))
(prog1
(gnus-summary-select-article)
(gnus-message 9 "Searching article: %d..." gnus-current-article)
(setq last gnus-current-article)
- (gnus-eval-in-buffer-window gnus-article-buffer
- (save-restriction
- (widen)
- ;; Begin search from current point.
- (setq found (funcall re-search regexp nil t))))
+ (gnus-eval-in-buffer-window
+ gnus-article-buffer
+ (save-restriction
+ (widen)
+ ;; Begin search from current point.
+ (setq found (funcall re-search regexp nil t))))
;; Then search next articles.
(while (and (not found)
(gnus-summary-display-article
(gnus-summary-search-subject backward nil nil)))
(gnus-message 9 "Searching article: %d..." gnus-current-article)
- (gnus-eval-in-buffer-window gnus-article-buffer
- (save-restriction
- (widen)
- (goto-char (if backward (point-max) (point-min)))
- (setq found (funcall re-search regexp nil t)))))
+ (gnus-eval-in-buffer-window
+ gnus-article-buffer
+ (save-restriction
+ (widen)
+ (goto-char (if backward (point-max) (point-min)))
+ (setq found (funcall re-search regexp nil t)))))
(message "")
;; Adjust article pointer.
(or (eq last gnus-current-article)
(or total (setq gnus-newsgroup-expirable es))
;; We go through the old list of expirable, and mark all
;; really expired articles as non-existant.
- (or (eq es expirable) ; If nothing was expired, we don't mark.
+ (or (eq es expirable) ;If nothing was expired, we don't mark.
(let ((gnus-use-cache nil))
(while expirable
(or (memq (car expirable) es)
(while (and
(> n 0)
(if unmark
- (gnus-summary-remove-process-mark (gnus-summary-article-number))
+ (gnus-summary-remove-process-mark
+ (gnus-summary-article-number))
(gnus-summary-set-process-mark (gnus-summary-article-number)))
(zerop (gnus-summary-next-subject (if backward -1 1) nil t)))
(setq n (1- n)))
(insert mark)
(and plist (add-text-properties (1- (point)) (point) plist))
(and (eq type 'unread)
- (add-text-properties (1- (point)) (point) (list 'gnus-mark mark)))
- (gnus-summary-update-line (eq mark gnus-unread-mark)))))
+ (progn
+ (add-text-properties (1- (point)) (point) (list 'gnus-mark mark))
+ (gnus-summary-update-line (eq mark gnus-unread-mark)))))))
(defun gnus-mark-article-as-read (article &optional mark)
"Enter ARTICLE in the pertinent lists and remove it from others."
(interactive)
(gnus-set-global-variables)
(let ((buffer-read-only nil)
- (orig (point))
+ (orig (prog1 (point) (gnus-summary-hide-thread)))
;; first goto end then to beg, to have point at beg after let
(end (progn (end-of-line) (point)))
(beg (progn (beginning-of-line) (point))))
(goto-char end)
(search-backward "\n" start t))
(subst-char-in-region start end ?\n ?\^M t)
- (forward-line -1)))))
+ (forward-line -1)
+ (gnus-summary-position-cursor)))))
(defun gnus-summary-go-to-next-thread (&optional previous)
"Go to the same level (or less) next thread.
(let ((killing t)
(level (gnus-summary-thread-level)))
(save-excursion
+ ;; Expand the thread.
+ (gnus-summary-show-thread)
(while killing
;; Mark the article...
(cond ((null unmark) (gnus-summary-mark-article-as-read
gnus-extract-address-components
(mail-header-from header))))
(concat
- (downcase (gnus-simplify-subject (gnus-summary-subject-string)))
+ (downcase (gnus-simplify-subject (gnus-summary-subject-string) t))
"\r" (or (car extract) (cdr extract)))))
'gnus-thread-sort-by-subject)
reverse))
(define-key gnus-article-mode-map "h" 'gnus-article-show-summary)
(define-key gnus-article-mode-map "s" 'gnus-article-show-summary)
(define-key gnus-article-mode-map "\C-c\C-m" 'gnus-article-mail)
- (define-key gnus-article-mode-map "\C-c\C-M" 'gnus-article-mail-with-original)
+ (define-key gnus-article-mode-map
+ "\C-c\C-M" 'gnus-article-mail-with-original)
(define-key gnus-article-mode-map "?" 'gnus-article-describe-briefly)
(define-key gnus-article-mode-map gnus-mouse-2 'gnus-article-push-button)
(define-key gnus-article-mode-map "\r" 'gnus-article-press-button)
;;; Server Communication
;;;
-;; All the Gnus backends have the same interface, and should return
-;; data in a similar format. Below is an overview of what functions
-;; these packages must supply and what results they should return.
-;;
-;; Variables:
-;;
-;; `nntp-server-buffer' - All data should be returned to Gnus in this
-;; buffer.
-;;
-;; Functions for the imaginary backend `choke':
-;;
-;; `choke-retrieve-headers ARTICLES &optional GROUP SERVER'
-;; Should return all headers for all ARTICLES, or return NOV lines for
-;; the same.
-;;
-;; `choke-request-group GROUP &optional SERVER DISCARD'
-;; Switch to GROUP. If DISCARD is nil, active information on the group
-;; must be returned.
-;;
-;; `choke-close-group GROUP &optional SERVER'
-;; Close group. Most backends won't have to do anything with this
-;; call, but it is an opportunity to clean up, if that is needed. It
-;; is called when Gnus exits a group.
-;;
-;; `choke-request-article ARTICLE &optional GROUP SERVER'
-;; Return ARTICLE, which is either an article number or
-;; message-id. Note that not all backends can return articles based on
-;; message-id.
-;;
-;; `choke-request-list SERVER'
-;; Return a list of all newsgroups on SERVER.
-;;
-;; `choke-request-list-newsgroups SERVER'
-;; Return a list of descriptions of all newsgroups on SERVER.
-;;
-;; `choke-request-newgroups DATE &optional SERVER'
-;; Return a list of all groups that have arrived after DATE on
-;; SERVER. Note that the date doesn't have to be respected - Gnus will
-;; always check whether the groups are old or not. Backends that do
-;; not store date information may just return the entire list of
-;; groups, although this might not be a good idea in general.
-;;
-;; `choke-request-post-buffer METHOD HEADER ARTICLE-BUFFER GROUP INFO'
-;; Should return a buffer that is suitable for "posting". nnspool and
-;; nntp return a `*post-buffer*', and nnmail return a `*mail*'
-;; buffer. This function should fill out the appropriate headers.
-;;
-;; `choke-request-post &optional SERVER'
-;; Function that will be called from a buffer to be posted.
-;;
-;; `choke-open-server SERVER &optional ARGUMENT'
-;; Open a connection to SERVER.
-;;
-;; `choke-close-server &optional SERVER'
-;; Close the connection to SERVER.
-;;
-;; `choke-server-opened &optional SERVER'
-;; Whether the conenction to SERVER is opened or not.
-;;
-;; `choke-server-status &optional SERVER'
-;; Should return a status string (not in the nntp buffer, but as the
-;; result of the function).
-;;
-;; `choke-retrieve-groups GROUPS &optional SERVER'
-;; Optional function for retrieving active file info on all groups in
-;; GROUPS. Two return formats are supported: The normal active file
-;; format, and a list of GROUP lines. This function should return (as
-;; a function value) either `active' or `group', depending on what
-;; format it returns.
-;;
-;; The following functions are optional and apply only to backends
-;; that are able to control the contents of their groups totally
-;; (ie. mail backends.) Backends that aren't able to do that
-;; shouldn't define these functions at all. Gnus will check for their
-;; presence before attempting to call them.
-;;
-;; `choke-request-expire-articles ARTICLES &optional NEWSGROUP SERVER'
-;; Should expire (according to some aging scheme) all ARTICLES. Most
-;; backends will not be able to expire articles. Should return a list
-;; of all articles that were not expired.
-;;
-;; `choke-request-move-article ARTICLE GROUP SERVER ACCEPT-FORM &optional LAST'
-;; Should move ARTICLE from GROUP on SERVER by using ACCEPT-FORM.
-;; Removes any information it has added to the article (extra headers,
-;; whatever - make it as clean as possible), and then passes the
-;; article on by evaling ACCEPT-FORM, which is normally a call to the
-;; function described below. If the ACCEPT-FORM returns a non-nil
-;; value, the article should then be deleted. If LAST is nil, that
-;; means that there will be further calls to this function. This might
-;; be taken as an advice not to save buffers/internal variables just
-;; yet, but wait until the last call to speed things up.
-;;
-;; `choke-request-accept-article GROUP &optional LAST'
-;; The contents of the current buffer will be put into GROUP. There
-;; should, of course, be an article in the current buffer. This
-;; function is normally only called by the function described above,
-;; and LAST works the same way as in that function.
-;;
-;; `choke-request-replace-article ARTICLE GROUP BUFFER'
-;; Replace ARTICLE in GROUP with the contents of BUFFER.
-;; This provides an easy interface for allowing editing of
-;; articles. Note that even headers may be edited, so the backend has
-;; to update any tables (nov buffers, etc) that it maintains after
-;; replacing the article.
-;;
-;; `choke-request-create-group GROUP &optional SERVER'
-;; Create GROUP on SERVER. This might be a new, empty group, or it
-;; might be a group that already exists, but hasn't been registered
-;; yet.
-;;
-;; All these functions must return nil if they couldn't service the
-;; request. If the optional arguments are not supplied, some "current"
-;; or "default" values should be used. In short, one should emulate an
-;; NNTP server, in a way.
-;;
-;; If you want to write a new backend, you just have to supply the
-;; functions listed above. In addition, you must enter the new backend
-;; into the list of valid select methods:
-;; (setq gnus-valid-select-methods
-;; (cons '("choke" mail) gnus-valid-select-methods))
-;; The first element in this list is the name of the backend. Other
-;; elemnets may be `mail' (for mail groups), `post' (for news
-;; groups), `none' (neither), `respool' (for groups that can control
-;; their contents).
-
(defun gnus-start-news-server (&optional confirm)
"Open a method for getting news.
If CONFIRM is non-nil, the user will be asked for an NNTP server."
(defun gnus-request-group (group &optional dont-check)
(let ((method (gnus-find-method-for-group group)))
- ; (and t (message "%s GROUP %s" (car method) group))
(funcall (gnus-get-function method 'request-group)
(gnus-group-real-name group) (nth 1 method) dont-check)))
(funcall (gnus-get-function method 'request-head)
article (gnus-group-real-name group) (nth 1 method))))
+(defun gnus-request-body (article group)
+ (let ((method (gnus-find-method-for-group group)))
+ (funcall (gnus-get-function method 'request-body)
+ article (gnus-group-real-name group) (nth 1 method))))
+
;; Fix by Sudish Joseph <joseph@cis.ohio-state.edu>.
(defun gnus-request-post-buffer (post group subject header artbuf
info follow-to respect-poster)
(gnus-update-format-specifications)
- ;; Find the number of unread articles in each non-dead group.
- (let ((gnus-read-active-file (and (not level) gnus-read-active-file)))
- (gnus-get-unread-articles (or level (1+ gnus-level-subscribed))))
;; Find new newsgroups and treat them.
(if (and init gnus-check-new-newsgroups gnus-read-active-file (not level)
(gnus-server-opened gnus-select-method))
(gnus-find-new-newsgroups))
+
+ ;; Find the number of unread articles in each non-dead group.
+ (let ((gnus-read-active-file (and (not level) gnus-read-active-file)))
+ (gnus-get-unread-articles (or level (1+ gnus-level-subscribed))))
+
(if (and init gnus-check-bogus-newsgroups
gnus-read-active-file (not level)
(gnus-server-opened gnus-select-method))
(gnus-ask-server-for-new-groups)
(let ((groups 0)
group new-newsgroups)
- (gnus-message 5 "Checking for new newsgroups...")
+ (gnus-message 5 "Looking for new newsgroups...")
(or gnus-have-read-active-file (gnus-read-active-file))
(setq gnus-newsrc-last-checked-date (current-time-string))
(if (not gnus-killed-hashtb) (gnus-make-hashtable-from-killed))
(let ((method (gnus-find-method-for-group group))
active)
(and (gnus-check-server method)
- ;; We escape all bugs and quits here to make it possible to
+ ;; We escape all bugs and quit here to make it possible to
;; continue if a group is so out-there that it reports bugs
;; and stuff.
(condition-case ()
(setq newsrc (cdr newsrc)))
(gnus-check-server method)
(setq list-type (gnus-retrieve-groups groups method))
- (cond ((not list-type)
- (gnus-message
- 1 "Cannot read partial active file from %s server."
- (car method))
- (ding)
- (sit-for 2))
- ((eq list-type 'active)
- (gnus-active-to-gnus-format method gnus-active-hashtb))
- (t
- (gnus-groups-to-gnus-format method gnus-active-hashtb)))))
+ (cond
+ ((not list-type)
+ (gnus-message
+ 1 "Cannot read partial active file from %s server."
+ (car method))
+ (ding)
+ (sit-for 2))
+ ((eq list-type 'active)
+ (gnus-active-to-gnus-format method gnus-active-hashtb))
+ (t
+ (gnus-groups-to-gnus-format method gnus-active-hashtb)))))
(t
(if (not (gnus-request-list method))
(progn
(cdr (cdr group))))
gnus-newsrc-alist)))
(if (setq m (assoc (car group) marked))
- (setcdr (cdr (cdr info)) (cons (list (cons 'tick (cdr m))) nil))))
+ (setcdr (cdr (cdr info))
+ (cons (list (cons 'tick (cdr m))) nil))))
(setq newsrc (cdr newsrc)))
(setq newsrc killed)
(while newsrc
(and (not (string-match "^ *options" gnus-newsrc-options))
(setq gnus-newsrc-options (concat "options " gnus-newsrc-options)))
(and (not (string-match "\n$" gnus-newsrc-options))
- (setq gnus-newsrc-options (concat gnus-newsrc-options "\n")))))
+ (setq gnus-newsrc-options (concat gnus-newsrc-options "\n")))
+ ;; Finally, if we read some options lines, we parse them.
+ (or (string= gnus-newsrc-options "")
+ (gnus-newsrc-parse-options gnus-newsrc-options))))
+
(setq gnus-newsrc-alist (nreverse gnus-newsrc-alist))
(gnus-make-hashtable-from-newsrc-alist)))
(setq reads (cons num1 reads))
(setq reads
(cons
- (cons num1 (progn
- (narrow-to-region (match-beginning 0)
- (match-end 0))
- (read buf)))
+ (cons num1
+ (progn
+ (narrow-to-region (match-beginning 0)
+ (match-end 0))
+ (read buf)))
reads))
(widen)))
;; It was just a simple number, so we add it to the
(t
;; Not numbers and not eol, so this might be a buggy
;; line...
- (or (eobp) ; If it was eob instead of ?\n, we allow it.
+ (or (eobp)
+ ;; If it was eob instead of ?\n, we allow it.
(progn
;; The line was buggy.
(setq group nil)
score-files)
;; if this group has been seen before, return the cached entry
(if (setq score-files (assoc group gnus-score-file-alist-cache))
- (cdr score-files) ; ensures caching of groups with no matches
+ (cdr score-files) ;ensures caching groups with no matches
;; handle the multiple match alist
(while alist
(and (string-match (car (car alist)) group)
(save-excursion
(set-buffer nnbabyl-mbox-buffer)
+ (set-text-properties (point-min) (point-max) nil)
(while (and articles is-old)
(goto-char (point-min))
(if (search-forward (nnbabyl-article-string (car articles)) nil t)
(nnheader-find-file-noselect
nnbabyl-mbox-file nil 'raw)))
(buffer-disable-undo (current-buffer))
+ (widen)
+ (setq buffer-read-only nil)
+ (fundamental-mode)
(goto-char (point-min))
(re-search-forward delim nil t)
(progn
(and gnus-verbose-backends
(message "nnbabyl: Reading incoming mail..."))
- (setq incoming
- (nnmail-move-inbox
- (car spools) (concat nnbabyl-mbox-file "-Incoming")))
- (setq incomings (cons incoming incomings))
- (save-excursion
- (setq group (nnmail-get-split-group (car spools) group-in))
- (let* ((nnmail-prepare-incoming-hook
- (cons 'nnbabyl-remove-incoming-delims
- nnmail-prepare-incoming-hook))
- in-buf)
- (setq in-buf (nnmail-split-incoming
- incoming 'nnbabyl-save-mail t group))
- (set-buffer in-buf)
- (goto-char (point-min))
- (while (search-forward "\n\^_\n" nil t)
- (delete-char -1))
- (set-buffer nnbabyl-mbox-buffer)
- (goto-char (point-max))
- (search-backward "\n\^_" nil t)
- (goto-char (match-end 0))
- (insert-buffer-substring in-buf)
- (kill-buffer in-buf)))))
+ (if (not (setq incoming
+ (nnmail-move-inbox
+ (car spools)
+ (concat nnbabyl-mbox-file "-Incoming"))))
+ ()
+ (setq incomings (cons incoming incomings))
+ (save-excursion
+ (setq group (nnmail-get-split-group (car spools) group-in))
+ (let* ((nnmail-prepare-incoming-hook
+ (cons 'nnbabyl-remove-incoming-delims
+ nnmail-prepare-incoming-hook))
+ in-buf)
+ (setq in-buf (nnmail-split-incoming
+ incoming 'nnbabyl-save-mail t group))
+ (set-buffer in-buf)
+ (goto-char (point-min))
+ (while (search-forward "\n\^_\n" nil t)
+ (delete-char -1))
+ (set-buffer nnbabyl-mbox-buffer)
+ (goto-char (point-max))
+ (search-backward "\n\^_" nil t)
+ (goto-char (match-end 0))
+ (insert-buffer-substring in-buf)
+ (kill-buffer in-buf))))))
(setq spools (cdr spools)))
;; If we did indeed read any incoming spools, we save all info.
(and (buffer-modified-p nnbabyl-mbox-buffer)
(setq activenumber (max activenumber newnum))
(setq activemin (min activemin newnum))))
(setcar active (min activemin activenumber))
- (setcdr active activenumber)
+ (setcdr active (max activenumber (cdr active)))
(goto-char (point-min))))
;; Keep track of the active number on our own, and insert it back into
(progn
(narrow-to-region start end)
(nnmail-insert-lines)
- (setq activenumber (1+ activenumber))
- (nnfolder-insert-newsgroup-line (cons nil activenumber))
+ (nnfolder-insert-newsgroup-line
+ (cons nil (nnfolder-active-number nnfolder-current-group)))
(widen))))
;; Make absolutely sure that the active list reflects reality!
- (setcdr active activenumber)
(nnmail-save-active nnfolder-group-alist nnfolder-active-file)
(current-buffer))))
(progn
(and gnus-verbose-backends
(message "nnfolder: Reading incoming mail..."))
- (setq incoming
- (nnmail-move-inbox
- (car spools)
- (concat (file-name-as-directory nnfolder-directory)
- "Incoming")))
- (setq incomings (cons incoming incomings))
- (setq group (nnmail-get-split-group (car spools) group-in))
- (nnmail-split-incoming incoming 'nnfolder-save-mail nil group)))
+ (if (not (setq incoming
+ (nnmail-move-inbox
+ (car spools)
+ (concat (file-name-as-directory nnfolder-directory)
+ "Incoming"))))
+ ()
+ (setq incomings (cons incoming incomings))
+ (setq group (nnmail-get-split-group (car spools) group-in))
+ (nnmail-split-incoming incoming 'nnfolder-save-mail nil group))))
(setq spools (cdr spools)))
;; If we did indeed read any incoming spools, we save all info.
(if incoming
"Set article author of HEADER to FROM."
(` (aset (, header) 2 (, from))))
-(defalias 'nntp-header-xref 'mail-header-xref)
-(defmacro mail-header-xref (header)
- "Return xref string in HEADER."
- (` (aref (, header) 8)))
-
-(defalias 'nntp-set-header-xref 'mail-header-set-xref)
-(defmacro mail-header-set-xref (header xref)
- "Set article xref of HEADER to xref."
- (` (aset (, header) 8 (, xref))))
-
-(defalias 'nntp-header-lines 'mail-header-lines)
-(defmacro mail-header-lines (header)
- "Return lines in HEADER."
- (` (aref (, header) 7)))
-
-(defalias 'nntp-set-header-lines 'mail-header-set-lines)
-(defmacro mail-header-set-lines (header lines)
- "Set article lines of HEADER to LINES."
- (` (aset (, header) 7 (, lines))))
-
(defalias 'nntp-header-date 'mail-header-date)
(defmacro mail-header-date (header)
"Return date in HEADER."
"Set number of chars in article of HEADER to CHARS."
(` (aset (, header) 6 (, chars))))
+(defalias 'nntp-header-lines 'mail-header-lines)
+(defmacro mail-header-lines (header)
+ "Return lines in HEADER."
+ (` (aref (, header) 7)))
+
+(defalias 'nntp-set-header-lines 'mail-header-set-lines)
+(defmacro mail-header-set-lines (header lines)
+ "Set article lines of HEADER to LINES."
+ (` (aset (, header) 7 (, lines))))
+
+(defalias 'nntp-header-xref 'mail-header-xref)
+(defmacro mail-header-xref (header)
+ "Return xref string in HEADER."
+ (` (aref (, header) 8)))
+
+(defalias 'nntp-set-header-xref 'mail-header-set-xref)
+(defmacro mail-header-set-xref (header xref)
+ "Set article xref of HEADER to xref."
+ (` (aset (, header) 8 (, xref))))
+
+
;; Various cruft the backends and Gnus need to communicate.
(defvar nntp-server-buffer nil)
(after-find-file error (not nowarn)))))
buf)))
+(defun nnheader-insert-references (references message-id)
+ ;; Fold long references line to follow RFC1036.
+ (mail-position-on-field "References")
+ (let ((begin (gnus-point-at-bol))
+ (fill-column 78)
+ (fill-prefix "\t"))
+ (if references (insert references))
+ (if (and references message-id) (insert " "))
+ (if message-id (insert message-id))
+ ;; The region must end with a newline to fill the region
+ ;; without inserting extra newline.
+ (fill-region-as-paragraph begin (1+ (point)))))
(provide 'nnheader)
the argument. It should return a non-nil value if it thinks that the
mail belongs in that group.
-The last element should always have \"\" as the regexp.")
+The last element should always have \"\" as the regexp.
+
+This variable can also have a function as its value.")
;; Suggested by Erik Selberg <speed@cs.washington.edu>.
(defvar nnmail-crosspost t
info follow-to respect-poster)
(let ((method-address (cdr (assq 'to-address (nth 5 info))))
from date to reply-to message-of
- references message-id sender cc sendto elt)
+ references message-id cc new-cc sendto elt)
(setq method-address
(if (and (stringp method-address)
(string= method-address ""))
(setq message-of
(concat (if stop-pos (substring from 0 stop-pos) from)
"'s message of " date))))
- (setq sender (mail-fetch-field "sender"))
- (setq cc (mail-fetch-field "cc"))
- (setq to (mail-fetch-field "to"))
+ (setq cc (mail-strip-quoted-names (mail-fetch-field "cc")))
+ (setq to (mail-strip-quoted-names (mail-fetch-field "to")))
+ (setq new-cc (rmail-dont-reply-to
+ (concat (or to "")
+ (if cc (concat (if to ", " "") cc) ""))))
(setq subject (mail-header-subject header))
(or (string-match "^[Rr][Ee]:" subject)
(setq subject (concat "Re: " subject)))
(while (setq elt (assoc "To" follow-to))
(setq sendto (concat sendto (and sendto ", ") (cdr elt)))
(setq follow-to (delq elt follow-to))))
- (mail-setup (if (and follow-to (listp follow-to)) sendto
- (or method-address
- (concat (or sender reply-to from "")
- (if to (concat ", " to) "")
- (if cc (concat ", " cc) ""))))
- subject message-of nil article-buffer nil)
+ (mail-setup (if (and follow-to (listp follow-to))
+ sendto
+ (or method-address reply-to from ""))
+ subject message-of
+ (if (zerop (length new-cc)) nil new-cc)
+ article-buffer nil)
(auto-save-mode auto-save-default)
;; Note that "To" elements should already be in the message.
(if (and follow-to (listp follow-to))
(insert
(car (car follow-to)) ": " (cdr (car follow-to)) "\n")
(setq follow-to (cdr follow-to)))))
- ;; Fold long references line to follow RFC1036.
- (mail-position-on-field "References")
- (let ((begin (- (point) (length "References: ")))
- (fill-column 78)
- (fill-prefix "\t"))
- (if references (insert references))
- (if (and references message-id) (insert " "))
- (if message-id (insert message-id))
- ;; The region must end with a newline to fill the region
- ;; without inserting extra newline.
- (fill-region-as-paragraph begin (1+ (point))))))
+ (nnheader-insert-references references message-id)))
(current-buffer))))
(defun nnmail-find-file (file)
(progn
(and gnus-verbose-backends
(message "nnmbox: Reading incoming mail..."))
- (setq incoming
- (nnmail-move-inbox
- (car spools) (concat nnmbox-mbox-file "-Incoming")))
- (setq incomings (cons incoming incomings))
- (save-excursion
- (setq group (nnmail-get-split-group (car spools) group-in))
- (let ((in-buf (nnmail-split-incoming
- incoming 'nnmbox-save-mail t group)))
- (set-buffer nnmbox-mbox-buffer)
- (goto-char (point-max))
- (insert-buffer-substring in-buf)
- (kill-buffer in-buf)))))
+ (if (not (setq incoming
+ (nnmail-move-inbox
+ (car spools)
+ (concat nnmbox-mbox-file "-Incoming"))))
+ ()
+ (setq incomings (cons incoming incomings))
+ (save-excursion
+ (setq group (nnmail-get-split-group (car spools) group-in))
+ (let ((in-buf (nnmail-split-incoming
+ incoming 'nnmbox-save-mail t group)))
+ (set-buffer nnmbox-mbox-buffer)
+ (goto-char (point-max))
+ (insert-buffer-substring in-buf)
+ (kill-buffer in-buf))))))
(setq spools (cdr spools)))
;; If we did indeed read any incoming spools, we save all info.
(and (buffer-modified-p nnmbox-mbox-buffer)
(progn
(and gnus-verbose-backends
(message "nnmh: Reading incoming mail..."))
- (setq incoming
- (nnmail-move-inbox
- (car spools) (concat (file-name-as-directory nnmh-directory)
- "Incoming")))
- (setq incomings (cons incoming incomings))
- (setq group (nnmail-get-split-group (car spools) group-in))
- (nnmail-split-incoming incoming 'nnmh-save-mail nil group)))
+ (if (not (setq incoming
+ (nnmail-move-inbox
+ (car spools)
+ (concat (file-name-as-directory nnmh-directory)
+ "Incoming"))))
+ ()
+ (setq incomings (cons incoming incomings))
+ (setq group (nnmail-get-split-group (car spools) group-in))
+ (nnmail-split-incoming incoming 'nnmh-save-mail nil group))))
(setq spools (cdr spools)))
;; If we did indeed read any incoming spools, we save all info.
(if incoming
(progn
(and gnus-verbose-backends
(message "nnml: Reading incoming mail..."))
- (setq incoming
- (nnmail-move-inbox
- (car spools) (concat nnml-directory "Incoming")))
- (setq group (nnmail-get-split-group (car spools) group-in))
- (nnmail-split-incoming incoming 'nnml-save-mail nil group)
- (setq incomings (cons incoming incomings))))
+ (if (not (setq incoming
+ (nnmail-move-inbox
+ (car spools) (concat nnml-directory "Incoming"))))
+ ()
+ (setq group (nnmail-get-split-group (car spools) group-in))
+ (nnmail-split-incoming incoming 'nnml-save-mail nil group)
+ (setq incomings (cons incoming incomings)))))
(setq spools (cdr spools)))
;; If we did indeed read any incoming spools, we save all info.
(if incoming
(setq followup-to (mail-fetch-field "followup-to"))
(if (or (null respect-poster) ;Ignore followup-to: field.
(string-equal "" followup-to) ;Bogus header.
- (string-equal "poster" followup-to)) ;Poster
+ (string-equal "poster" followup-to);Poster
+ (and (eq respect-poster 'ask)
+ followup-to
+ (y-or-n-p (concat "Followup to "
+ followup-to "? "))))
(setq followup-to nil))
(setq newsgroups
(or follow-to followup-to (mail-fetch-field "newsgroups")))
(insert (car (car newsgroups)) ": "
(cdr (car newsgroups)) "\n")
(setq newsgroups (cdr newsgroups)))))
- ;; Fold long references line to follow RFC1036.
- (mail-position-on-field "References")
- (let ((begin (- (point) (length "References: ")))
- (fill-column 79)
- (fill-prefix "\t"))
- (if references (insert references))
- (if (and references message-id) (insert " "))
- (if message-id (insert message-id))
- ;; The region must end with a newline to fill the region
- ;; without inserting extra newline.
- (fill-region-as-paragraph begin (1+ (point))))
+ (nnheader-insert-references references message-id)
(if distribution
(progn
(mail-position-on-field "Distribution")
TEXI2DVI=texi2dvi
EMACS=emacs
MAKEINFO=$(EMACS) -batch -q -no-site-file gnus.texi -l texinfmt -f texinfo-every-node-update -f texinfo-format-buffer -f save-buffer
-# MAKEINFO=makeinfo -o gnus gnus.texi
+# MAKEINFO=makeinfo -o gnus.info gnus.texi
LATEX=latex
all: gnus.info refcard.dvi
* Various:: General purpose settings.
* Customization:: Tailoring Gnus to your needs.
* Troubleshooting:: What you might try if things do not work.
-* The End:: Farewell, and goodbye.
+* The End:: Farewell and goodbye.
+* Appendix:: Technical stuff for technical people.
* Index:: Variable, function and concept index.
* Key Index:: Key Index.
@end menu
distribution point for the new and spiffy versions of Gnus, also know as
The Site That Destroys Newsrcs And Drives People Mad.
-@dfn{(ding)}, is, of course, short for @dfn{ding is not Gnus}, which is
-a total and utter lie, but who cares? (Besides, the "Gnus" in this
-abbreviation should probably be pronounced "news" as UMEDA intended,
-which makes it a more appropriate name, don't you think?)
+During the first extended alpha period of develpment, the new Gnus was
+called "(ding) Gnus". @dfn{(ding)}, is, of course, short for @dfn{ding
+is not Gnus}, which is a total and utter lie, but who cares? (Besides,
+the "Gnus" in this abbreviation should probably be pronounced "news" as
+UMEDA intended, which makes it a more appropriate name, don't you
+think?)
In any case, after spending all that energy with coming up with a new
and spiffy name, we decided that the name was @emph{too} spiffy, so we
@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.
* Contributors:: Oodles of people.
-* New Features:: A short description of all the new stuff in Gnus.
+* 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
anyway, so you might have to wait longer if you mail XEmacs questions to
me.
+
+@node Conformity
+@section Conformity
+
+No rebels without a clue here, ma'am. We conform to all standards known
+to man. Except, of course, where we disagree with the standards and/or
+conventions.
+
+@table @strong
+
+@item RFC 822
+There are no known breaches to this standard.
+
+@item RFC 1036
+There are no known breaches to this standard, either.
+
+@item Usenet Seal of Approval
+Gnus hasn't been formally through the Seal process, but I have read
+through the Seal text, and I think that Gnus would pass.
+
+@item Son-of-RFC 1036
+We do have some breaches to this one.
+
+@table @emph
+@item MIME
+Gnus does no MIME handling, and this standard-to-be seems to think that
+MIME is the bees' knees, so we have major breakage here.
+@item X-Newsreader
+This is considered to be a "vanity header", while I consider it to be
+consumer information. After seeing so many badly formatted articles
+coming from @code{tin} and @code{Netscape} I know not to use either of
+those for posting articles. I would not have known that if it wasn't
+for the @code{X-Newsreader} header.
+@item References
+Gnus does line breaking on this header. I infer from RFC1036 that being
+conservative in what you output is not creating 5000-character lines, so
+it seems like a good idea to me. However, this standard-to-be says that
+whitespace in the @code{References} header is to be preserved, so... It
+doesn't matter one way or the other to Gnus, so if somebody tells me
+what The Way is, I'll change it. Or not.
+@end table
+
+@end table
+
+If you ever see Gnus act noncompliantly to the texts mentioned above,
+don't hesitate to drop a note to Gnus Towers and let us know.
+
+
@node Contributors
@section Contributors
@cindex contributors
@section New Features
@cindex new features
-The look of all buffers can be changed by setting format-like variables.
+@itemize @bullet
+
+@item
+The look of all buffers can be changed by setting format-like variables
+(@pxref{Group Buffer Format} and @pxref{Summary Buffer Format}).
-Local spool and several @sc{nntp} servers can be used at once. Virtual
-groups and private mail groups are featured.
+@item
+Local spool and several @sc{nntp} servers can be used at once
+(@pxref{Foreign Groups}).
+
+@item
+You can combine groups into virtual groups (@pxref{nnvirtual}).
+
+@item
+You can read a number of different mail formats (@pxref{Reading Mail}).
+All the mail backends implement a convenient mail expiry scheme
+(@code{Expiring Old Mail Articles}).
+@item
Gnus can use various strategies for gathering threads that have lost
their roots (thereby gathering loose sub-threads in one thread) or it
-can go back and retrieve enough headers to build a complete thread.
+can go back and retrieve enough headers to build a complete thread
+(@pxref{Customizing Threading}).
+@item
Killed groups can be displayed in the group buffer, and you can read
them as well.
+@item
Gnus can do partial group updates - you do not have to retrieve the
-entire active file just to check for new articles in a few groups.
+entire active file just to check for new articles in a few groups
+(@pxref{The Active File}).
+
+@item
+Gnus implements a sliding scale of subscribedness to groups
+(@pxref{Group Levels}).
+
+@item
+You can score articles according to any number of criteria (@pxref{Score
+Files}). You can even get Gnus to score articles for you
+(@pxref{Adaptive Scoring}).
+
+@item
+Gnus maintains a dribble buffer that is auto-saved the normal Emacs
+manner, so it should be difficult to lose much data on what you have
+read if your machine should go down (@pxref{Auto Save}).
+
+@item
+Gnus now has its own startup file to avoid cluttering up the
+@file{.emacs} file.
+
+@item
+You can set the process mark on both groups and articles and perform
+operations on all the marked items (@pxref{Process/Prefix}).
+
+@item
+You can grep through a subset of groups and create a group from the
+results (@pxref{nnkiboze}).
+
+@item
+You can list subsets of groups according to, well, anything
+(@pxref{Listing Groups}).
+
+@item
+You can browse foreign servers and subscribe to groups from those
+servers (@pxref{Browse Foreign Server}).
+
+@item
+Gnus can fetch articles asynchronously on a second connection to the
+server (@pxref{Asynchronous Fetching}).
-Gnus implements a sliding scale of subscribedness to groups.
+@item
+You can cache articles locally (@pxref{Article Caching}).
+
+@item
+The uudecode functions have been expanded and generalized
+(@pxref{Decoding Articles}).
+
+@item
+You can still post uuencoded articles, which was a little-known feature
+of @sc{gnus} past (@pxref{Uuencoding & Posting}).
+
+@item
+Fetching parents (and other articles) now actually works without
+glitches (@pxref{Finding the Parent}).
+
+@item
+Gnus can fetch FAQs to and descriptions of groups (@pxref{Group
+Information}).
+
+@item
+Digests (and other files) can be used as the basis for groups
+(@pxref{nndoc}).
+
+@item
+Articles can be highlighted and customized (@pxref{Customizing
+Articles}).
+
+@item
+All Gnus buffers can be customized in a difficult fashion
+(@pxref{Windows Configuration}).
+
+@item
+You can click on buttons instead of using the keyboard
+(@pxref{Buttons}).
+
+@end itemize
+
+This is, of course, just a @emph{short} overview of the @emph{most}
+important new features. No, really. There are tons more. Yes, we have
+feeping creaturism in full effect, but nothing too gratuitous, I would
+hope.
-The approach to killing has been changed. Instead of simply killing or
-not, you can score articles for easier reading.
@node Newest Features
@section Newest Features
@cindex todo
Also known as the @dfn{todo list}. Sure to be implemented before the
-next millennium.
+next millennium.
+
+Be afraid. Be very afraid.
@itemize @bullet
@item
I could steal code from @code{Mew}, the @sc{mime} mail reader for Emacs,
but I'm not quite sure what the status of that project is. Gnus might
support @sc{mime} quite soon, and it might not.
+
+@item
+@code{trn}-like trees.
+@item
+@code{nn}-like pick-and-read summary interface.
+@item
+NoCeM support.
+@item
+Frame configuration.
+@item
+Re-sending bounced mail and rejected articles.
+@item
+Floating point group levels and group bubbling.
+@item
+@file{/etc/nntpserver} usage.
+@item
+Automatic re-scan of incoming mail.
+@item
+Buttonize more stuff in the article buffer.
+@item
+A better and simpler method for specifying mail composing methods.
+@item
+Marks for saved, forwarded, etc articles.
+@item
+Speed up caching and adaptive scoring.
+@item
+Gather thread by filling in missing Message-IDs.
+@item
+Slave Gnusii to enable several Gnusii to run at once.
+@item
+PGP support.
+@item
+Allow posting through mail-to-news gateways.
+@item
+Allow renaming mail groups in a simple fashion.
+@item
+Speed up massive group massacres.
+@item
+@code{jka-compr} isn't fully supported.
+@item
+Create better digests.
+@item
+Do better word-wrap on cited text.
+@item
+Better X-Face support with X-Face databases and stuff.
+@item
+Support SELF-DISCIPLINE pins.
@item
-When the user references the parent of an article, some sort of
-re-threading should be done to build a proper tree. The same goes for
-article expunging. However, as it stands, it's not a trivial issue to
-re-generate parts of the summary buffer. Generating the entire buffer
-is very easy, but slow.
+Really do unbinhexing.
+@item
+Fetching by Message-ID should work in mail groups.
+@item
+Listing of all active groups.
+@item
+XEmacs toolbar.
+@item
+Do the X-Receipt-To thing.
+@item
+Hierarchal group buffers.
+@item
+Don't kill summary buffers upon exit from the groups.
+@item
+Allow adaption on secondary marks.
@end itemize
+And much, much, much more. There is more to come than has already been
+implemented. (But that's always true, isn't it?)
+
+You can probably sneak a look at the actual up-to-the-second todo list
+by snooping @code{<URL:http://www.ifi.uio.no/~larsi/sgnus/todo>}.
+
@node Terminology
@chapter Terminology
@section Startup Variables
@table @code
+@item gnus-load-hook
+@vindex gnus-load-hook
+A hook that is run while Gnus is being loaded. Note that this hook will
+normally be run just once in a single Emacs session, no matter how many
+times you start Gnus.
+
+@item gnus-startup-hook
+@vindex gnus-startup-hook
+A hook that is run after starting up Gnus successfully.
+
@item gnus-check-bogus-newsgroups
@vindex gnus-check-bogus-newsgroups
If non-@code{nil}, Gnus will check for and delete all bogus groups at
bogus groups isn't very quick, so to save time and resources, it's best
to leave this option off, and instead do the checking for bogus groups
once in a while from the group buffer (@pxref{Group Maintenance}).
+
@item gnus-inhibit-startup-message
@vindex gnus-inhibit-startup-message
If non-@code{nil}, the startup message won't be displayed. That way,
your boss might not notice that you are reading news instead of doing
your job.
+
@item gnus-no-groups-message
@vindex gnus-no-groups-message
Message displayed by Gnus when no groups are available.
Reading mail with a newsreader - isn't that just plain WeIrD? But of
course.
+Gnus will read the mail spool when you activate a mail group. The mail
+file is first copied to your home directory. What happens after that
+depends on what format you want to store your mail in.
+
@menu
* Creating Mail Groups:: How to create mail groups.
* Fancy Mail Splitting:: Gnus can do hairy splitting of incoming mail.
* Mail & Procmail:: Reading mail groups that procmail create.
* Expiring Old Mail Articles:: Getting rid of unwanted mail.
* Not Reading Mail:: Using mail backends for reading other files.
-@end menu
-
-Gnus will read the mail spool when you activate a mail group. The mail
-file is first copied to your home directory. What happens after that
-depends on what format you want to store your mail in.
-
-@menu
-* nnmbox:: Using the (quite) standard Un*x mbox.
-* nnbabyl:: Many Emacs programs use the rmail babyl format.
-* nnml:: Store your mail in a private spool?
-* nnmh:: An mhspool-like backend useful for procmail people.
-* nnfolder:: Having one file for each group.
+* nnmbox:: Using the (quite) standard Un*x mbox.
+* nnbabyl:: Emacs programs use the rmail babyl format.
+* nnml:: Store your mail in a private spool?
+* nnmh:: An mhspool-like backend.
+* nnfolder:: Having one file for each group.
@end menu
@vindex nnmail-read-incoming-hook
@item r
@kindex r (Group)
@findex gnus-group-read-init-file
+@vindex gnus-init-file
Read the init file (@code{gnus-init-file}, which defaults to
@file{~/.gnus}) (@code{gnus-group-read-init-file}).
@item s
If @code{nil}, always ignore the Followup-To header. If it is @code{t},
use its value, but ignore the special value @samp{poster}, which will
send the followup as a reply mail to the person you are responding to.
-If it is neither @code{nil} nor @code{t}, always use the Followup-To
-value.
+If it is the symbol @code{ask}, query the user before posting.
+If it is the symbol @code{use}, always use the value.
@item gnus-followup-to-function
@vindex gnus-followup-to-function
* Uuencoded Articles:: Uudecode articles.
* Shared Articles:: Unshar articles.
* PostScript Files:: Split PostScript.
+* Decoding Variables:: Variables for a happy decoding.
+* Viewing Files:: You want to look at the result of the decoding?
@end menu
All these functions use the process/prefix convention
series}, will not be properly recognized by any of the automatic viewing
commands, and you have to mark the articles manually with @key{#}.
-@menu
-* Decoding Variables:: Variables for a happy decoding.
-* Viewing Files:: You want to look at the result of the decoding?
-@end menu
-
@node Uuencoded Articles
@subsection Uuencoded Articles
@cindex uudecode
alists should probably be placed before the "real" score file functions,
to ensure that the last score file returned is the local score file.
Phu.
-@item gnus-kill-expiry-days
-@vindex gnus-kill-expiry-days
+@item gnus-score-expiry-days
+@vindex gnus-score-expiry-days
This variable says how many days should pass before an unused score file
entry is expired. The default is 7.
@end table
@node Article Keymap
@section Article Keymap
-Most of the keystrokes in the summary buffer can also be used in the
-article buffer. They should behave as if you typed them in the summary
-buffer, which means that you don't actually have to have a summary
-buffer displayed while reading. You can do it all from the article
-buffer.
+@c Most of the keystrokes in the summary buffer can also be used in the
+@c article buffer. They should behave as if you typed them in the summary
+@c buffer, which means that you don't actually have to have a summary
+@c buffer displayed while reading. You can do it all from the article
+@c buffer.
A few additional keystrokes are available:
@node Servers & Methods
@section Servers & Methods
+Wherever you would normally use a select method
+(eg. @code{gnus-secondary-select-method}, in the group select method,
+when browsing a foreign server) you can use a virtual server name
+instead. This could potentially save lots of typing. And it's nice all
+over.
@node Various
but at the common table.@*
@end quotation
+@node Appendix
+@chapter A Programmer's Guide to Gnus
+
+It is my hope that other people will figure out smart stuff that Gnus
+can do, and that other people will write those smart things as well. To
+facilitate that I thought it would be a good idea to describe the inner
+workings of Gnus. And some of the not-so-inner workings, while I'm at
+it.
+
+You can never expect the internals of a program not to change, but I
+will be defining (in some details) the interface between Gnus and its
+backends (this is written in stone), the format of the score files
+(ditto), data structures (some are less likely to change than others)
+and general method of operations.
+
+@menu
+* Backend Interface:: How Gnus communicates with the servers.
+* Score File Syntax:: A BNF definition of the score file standard.
+* Headers:: How Gnus stores headers internally.
+* Ranges:: A handy format for storing mucho numbers.
+* Group Info:: The group info format.
+@end menu
+
+
+@node Backend Interface
+@section Backend Interface
+
+Gnus doesn't know anything about nntp, spools, mail or virtual groups.
+It only knows how to talk to @dfn{virtual servers}. A virtual server is
+a @dfn{backend} and some @dfn{backend variables}. As examples of the
+first, we have @code{nntp}, @code{nnspool} and @code{nnmbox}. As
+examples of the latter we have @code{nntp-port-number} and
+@code{nnmbox-directory}.
+
+When Gnus asks for information from a backend -- say @code{nntp} -- on
+something, it will normally include a virtual server name in the
+function parameters. (If not, the backend should use the "current"
+virtual server.) For instance, @code{nntp-request-list} takes a virtual
+server as its only (optional) parameter. If this virtual server hasn't
+been opened, the function should fail.
+
+Note that a virtual server name has no relation to some physical server
+name. Take this example:
+
+@lisp
+(nntp "odd-one"
+ (nntp-address "ifi.uio.no")
+ (nntp-port-number 4324))
+@end lisp
+
+Here the virtual server name is @samp{"odd-one"} while the name of
+the physical server is @samp{"ifi.uio.no"}.
+
+The backends should be able to switch between several virtual servers.
+The standard backends implement this by keeping an alist of virtual
+server environments that it pulls down/pushes up when needed.
+
+There are two groups of interface functions: @dfn{required functions},
+which must be present, and @dfn{optional functions}, which Gnus will
+always check whether are present before attempting to call.
+
+All these functions are expected to return data in the buffer
+@code{nntp-server-buffer} (@samp{" *nntpd*"}), which is somewhat
+unfortunately named, but we'll have to live with it. When I talk about
+"resulting data", I always refer to the data in that buffer. When I
+talk about "return value", I talk about the function value returned by
+the function call.
+
+Some backends could be said to be @dfn{server-forming} backends, and
+some might be said to not be. The latter are backends that generally
+only operate on one group at a time, and have no concept of "server" --
+they have a group, and they deliver info on that group and nothing more.
+
+In the examples and definitions I will refer to the imaginary backend
+@code{nnchoke}.
+
+@menu
+* Required Backend Functions:: Functions that must be implemented.
+* Optional Backend Functions:: Functions that need not be implemented.
+@end menu
+
+
+@node Required Backend Functions
+@subsection Required Backend Functions
+
+@table @code
+
+@item (nnchoke-retrieve-headers ARTICLES &optional GROUP SERVER)
+
+@var{articles} is either a range of article numbers or a list of
+@code{Message-ID}s. Current backends do not fully support either - only
+sequences (lists) of article numbers, and most backends do not support
+retrieval of @code{Message-ID}s. But they should try for both.
+
+The result data should either be HEADs or NOV lines, and the result
+value should either be @code{headers} or @code{nov} to reflect this.
+This might later be expanded to @code{various}, which will be a mixture
+of HEADs and NOV lines, but this is currently not supported by Gnus.
+
+Here's an example HEAD:
+
+@example
+221 1056 Article retrieved.
+Path: ifi.uio.no!sturles
+From: sturles@@ifi.uio.no (Sturle Sunde)
+Newsgroups: ifi.discussion
+Subject: Re: Something very droll
+Date: 27 Oct 1994 14:02:57 +0100
+Organization: Dept. of Informatics, University of Oslo, Norway
+Lines: 26
+Message-ID: <38o8e1$a0o@@holmenkollen.ifi.uio.no>
+References: <38jdmq$4qu@@visbur.ifi.uio.no>
+NNTP-Posting-Host: holmenkollen.ifi.uio.no
+.
+@end example
+
+So a @code{headers} return value would imply that there's a number of
+these in the data buffer.
+
+Here's a BNF definition of such a buffer:
+
+@example
+headers = *head
+head = error / valid-head
+error-message = [ "4" / "5" ] 2number " " <error message> eol
+valid-head = valid-message *header "." eol
+valid-message = "221 " <number> " Article retrieved." eol
+header = <text> eol
+@end example
+
+If the return value is @code{nov}, the data buffer should contain
+@dfn{network overview database} lines. These are basically fields
+separated by tabs.
+
+@example
+nov-buffer = *nov-line
+nov-line = 8*9 [ field <TAB> ] eol
+field = <text except TAB>
+@end example
+
+For a closer explanation what should be in those fields,
+@xref{Headers}.
+
+
+@item (nnchoke-open-server SERVER &optional DEFINITIONS)
+
+@var{server} is here the virtual server name. @var{definitions} is a
+list of @code{(VARIABLE VALUE)} pairs that defines this virtual server.
+
+If the server can't be opened, no error should be signaled. The backend
+may then choose to refuse further attempts at connecting to this
+server. In fact, it should do so.
+
+If the server is opened already, this function should return a
+non-@code{nil} value. There should be no data returned.
+
+
+@item (nnchoke-close-server &optional SERVER)
+
+Close connection to @var{server} and free all resources connected
+to it.
+
+There should be no data returned.
+
+
+@item (nnchoke-request-close)
+
+Close connection to all servers and free all resources that the backend
+have reserved. All buffers that have been created by that backend
+should be killed. (Not the @code{nntp-server-buffer}, though.)
+
+There should be no data returned.
+
+
+@item (nnchoke-server-opened &optional SERVER)
+
+This function should return whether @var{server} is opened, and that the
+connection to it is still alive. This function should under no
+circumstances attempt to reconnect to a server that is has lost
+connection to.
+
+There should be no data returned.
+
+
+@item (nnchoke-status-message &optional SERVER)
+
+This function should return the last error message from @var{server}.
+
+There should be no data returned.
+
+
+@item (nnchoke-request-article ARTICLE &optional GROUP SERVER TO-BUFFER)
+
+The result data from this function should be the article specified by
+@var{article}. This might either be a @code{Message-ID} or a number.
+It is optional whether to implement retrieval by @code{Message-ID}, but
+it would be nice if that were possible.
+
+If @var{to-buffer} is non-@code{nil}, the result data should be returned
+in this buffer instead of the normal data buffer. This is to make it
+possible to avoid copying large amounts of data from one buffer to
+another, and Gnus mainly request articles to be inserted directly into
+its article buffer.
+
+
+@item (nnchoke-open-group GROUP &optional SERVER)
+
+Make @var{group} the current group.
+
+There should be no data returned by this function.
+
+
+@item (nnchoke-request-group GROUP &optional SERVER)
+
+Get data on @var{group}. This function also has the side effect of
+making @var{group} the current group.
+
+Here's an example of some result data and a definition of the same:
+
+@example
+211 56 1000 1059 ifi.discussion
+@end example
+
+The first number is the status, which should be @samp{211}. Next is the
+total number of articles in the group, the lowest article number, the
+highest article number, and finally the group name. Note that the total
+number of articles may be less than one might think while just
+considering the highest and lowest article numbers, but some articles
+may have been cancelled. Gnus just discards the total-number, so
+whether one should take the bother to generate it properly (if that is a
+problem) is left as an excercise to the reader.
+
+@example
+group-status = [ error / info ] eol
+error = [ "4" / "5" ] 2<number> " " <Error message>
+info = "211 " 3* [ <number> " " ] <string>
+@end example
+
+
+@item (nnchoke-close-group GROUP &optional SERVER)
+
+Close @var{group} and free any resources connected to it. This will be
+a no-op on most backends.
+
+There should be no data returned.
+
+
+@item (nnchoke-request-list &optional SERVER)
+
+Return a list of all groups available on @var{server}. And that means
+@emph{all}.
+
+Here's an example from a server that only carries two groups:
+
+@example
+ifi.test 0000002200 0000002000 y
+ifi.discussion 3324 3300 n
+@end example
+
+On each line we have a group name, then the highest article number in
+that group, the lowest article number, and finally a flag.
+
+@example
+active-file = *active-line
+active-line = name " " <number> " " <number> " " flags eol
+name = <string>
+flags = "n" / "y" / "m" / "x" / "j" / "=" name
+@end example
+
+The flag says whether the group is read-only (@samp{n}), is moderated
+(@samp{m}), is dead (@samp{x}), is aliased to some other group
+(@samp{=other-group} or none of the above (@samp{y}).
+
+
+@item (nnchoke-request-post &optional SERVER)
+
+This function should post the current buffer. It might return whether
+the posting was successful or not, but that's not required. If, for
+instance, the posting is done asynchronously, it has generally not been
+completed by the time this function concludes. In that case, this
+function should set up some kind of sentinel to beep the user loud and
+clear if the posting could not be completed.
+
+There should be no result data from this function.
+
+
+@item (nnchoke-request-post-buffer POST GROUP SUBJECT HEADER ARTICLE-BUFFER INFO FOLLOW-TO RESPECT-POSTER)
+
+This function should return a buffer suitable for composing an article
+to be posted by @code{nnchoke-request-post}. If @var{post} is
+non-@code{nil}, this is not a followup, but a totally new article.
+@var{group} is the name of the group to be posted to. @var{subject} is
+the subject of the message. @var{article-buffer} is the buffer being
+followed up, if that is the case. @var{info} is the group info.
+@var{follow-to} is the group that one is supposed to re-direct the
+article to. If @var{respect-poster} is non-@code{nil}, the special
+@samp{"poster"} value of a @code{Followup-To} header is to be respected.
+
+There should be no result data returned.
+
+@end table
+
+@node Optional Backend Functions
+@subsection Optional Backend Functions
+
+@table @code
+
+@item (nnchoke-retrieve-groups GROUPS &optional SERVER)
+
+@var{groups} is a list of groups, and this function should request data
+on all those groups. How it does it is of no concern to Gnus, but it
+should attempt to do this in a speedy fashion.
+
+The return value of this function can be either @code{active} or
+@code{group}, which says what the format of the result data is. The
+former is in the same format as the data from
+@code{nnchoke-request-list}, while the latter is a buffer full of lines
+in the same format as @code{nnchoke-request-group} gives.
+
+@example
+group-buffer = *active-line / *group-status
+@end example
+
+
+@item (nnchoke-request-update-info GROUP INFO &optional SERVER)
+
+A Gnus group info (@pxref{Group Info}) is handed to the backend for
+alterations. This comes in handy if the backend really carries all the
+information (as is the case with virtual an imap groups). This function
+may alter the info in any manner it sees fit, and should return the
+(altered) group info. This function may alter the group info
+destructively, so no copying is needed before boogying.
+
+There should be no result data from this function.
+
+
+@item (nnchoke-request-scan &optional GROUP SERVER)
+
+This function may be called at any time (by Gnus or anything else) to
+request that the backend check for incoming articles, in one way or
+another. A mail backend will typically read the spool file or query the
+POP server when this function is invoked. The @var{group} doesn't have
+to be heeded -- if the backend decides that it is too much work just
+scanning for a single group, it may do a total scan of all groups. It
+would be nice, however, to keep things local if that's practical.
+
+There should be no result data from this function.
+
+
+@item (nnchoke-request-asynchronous GROUP &optional SERVER ARTICLES)
+
+This is a request to fetch articles asynchronously later.
+@var{articles} is an alist of @var{(article-number line-number)}. One
+would generally expect that if one later fetches article number 4, for
+instance, some sort of asynchronous fetching of the articles after 4
+(which might be 5, 6, 7 or 11, 3, 909 depending on the order in that
+alist) would be fetched asynchronouly, but that is left up to the
+backend. Gnus doesn't care.
+
+There should be no result data from this function.
+
+
+@item (nnchoke-request-group-description GROUP &optional SERVER)
+
+The result data from this function should be a description of
+@var{group}.
+
+@example
+description-line = name <TAB> description eol
+name = <string>
+description = <text>
+@end example
+
+@item (nnchoke-request-list-newsgroups &optional SERVER)
+
+The result data from this function should be the description of all
+groups available on the server.
+
+@example
+description-buffer = *description-line
+@end example
+
+
+@item (nnchoke-request-newgroups DATE &optional SERVER)
+
+The result data from this function should be all groups that were
+created after @samp{date}, which is in normal human-readable date
+format. The data should be in the active buffer format.
+
+
+@item (nnchoke-request-create-groups GROUP &optional SERVER)
+
+This function should create an empty group with name @var{group}.
+
+There should be no return data.
+
+
+@item (nnchoke-request-expire-articles ARTICLES &optional GROUP SERVER FORCE)
+
+This function should run the expiry process on all articles in the
+@var{articles} range (which is currently a simple list of article
+numbers.) It is left up to the backend to decide how old articles
+should be before they are removed by this function. If @var{force} is
+non-@code{nil}, all @var{articles} should be deleted, no matter how new
+they are.
+
+This function should return a list of articles that it did not/was not
+able to delete.
+
+There should be no result data returned.
+
+
+@item (nnchoke-request-move-article ARTICLE GROUP SERVER ACCEPT-FORM
+&optional LAST)
+
+This function should move @var{article} (which is a number) from
+@var{group} by calling @var{accept-form}.
+
+This function should ready the article in question for moving by
+removing any header lines it has added to the article, and generally
+should "tidy up" the article. Then it should @code{eval}
+@var{accept-form} in the buffer where the "tidy" article is. This will
+do the actual copying. If this @code{eval} returns a non-@code{nil}
+value, the article should be removed.
+
+If @var{last} is @code{nil}, that means that there is a high likelihood
+that there will be more requests issued shortly, so that allows some
+optimizations.
+
+There should be no data returned.
+
+
+@item (nnchoke-request-accept-article GROUP &optional LAST)
+
+This function takes the current buffer and inserts it into @var{group}.
+If @var{last} in @code{nil}, that means that there will be more calls to
+this function in short order.
+
+There should be no data returned.
+
+
+@item (nnchoke-request-replace-article ARTICLE GROUP BUFFER)
+
+This function should remove @var{article} (which is a number) from
+@var{group} and insert @var{buffer} there instead.
+
+There should be no data returned.
+
+@end table
+
+
+@node Score File Syntax
+@section Score File Syntax
+
+Score files are meant to be easily parsable, but yet extremely
+mallable. It was decided that something that had the same read syntax
+as an Emacs Lisp list would fit that spec.
+
+Here's a typical score file:
+
+@lisp
+(("summary"
+ ("win95" -10000 nil s)
+ ("Gnus"))
+ ("from"
+ ("Lars" -1000))
+ (mark -100))
+@end lisp
+
+BNF definition of a score file:
+
+@example
+score-file = "" / "(" *element ")"
+element = rule / atom
+rule = string-rule / number-rule / date-rule
+string-rule = "(" quote string-header quote space *string-match ")"
+number-rule = "(" quote number-header quote space *number-match ")"
+date-rule = "(" quote date-header quote space *date-match ")"
+quote = <ascii 34>
+string-header = "subject" / "from" / "references" / "message-id" /
+ "xref" / "body" / "head" / "all" / "followup"
+number-header = "lines" / "chars"
+date-header = "date"
+string-match = "(" quote <string> quote [ "" / [ space score [ "" /
+ space date [ "" / [ space string-match-t ] ] ] ] ] ")"
+score = "nil" / <integer>
+date = "nil" / <natural number>
+string-match-t = "nil" / "s" / "substring" / "S" / "Substring" /
+ "r" / "regex" / "R" / "Regex" /
+ "e" / "exact" / "E" / "Exact" /
+ "f" / "fuzzy" / "F" / "Fuzzy"
+number-match = "(" <integer> [ "" / [ space score [ "" /
+ space date [ "" / [ space number-match-t ] ] ] ] ] ")"
+number-match-t = "nil" / "=" / "<" / ">" / ">=" / "<="
+date-match = "(" quote <string> quote [ "" / [ space score [ "" /
+ space date [ "" / [ space date-match-t ] ] ] ] ")"
+date-match-t = "nil" / "at" / "before" / "after"
+atom = "(" [ required-atom / optional-atom ] ")"
+required-atom = mark / expunge / mark-and-expunge / files /
+ exclude-files / read-only / touched
+optional-atom = adapt / local / eval
+mark = "mark" space nil-or-number
+nil-or-t = "nil" / <integer>
+expunge = "expunge" space nil-or-number
+mark-and-expunge = "mark-and-expunge" space nil-or-number
+files = "files" *[ space <string> ]
+exclude-files = "exclude-files" *[ space <string> ]
+read-only = "read-only" [ space "nil" / space "t" ]
+adapt = "adapt" [ space "nil" / space "t" / space adapt-rule ]
+adapt-rule = "(" *[ <string> *[ "(" <string> <integer> ")" ] ")"
+local = "local" *[ space "(" <string> space <form> ")" ]
+eval = "eval" space <form>
+space = *[ " " / <TAB> / <NEWLINE> ]
+@end example
+
+Any unrecognized elements in a score file should be ignored, but not
+discarded.
+
+As you can see, white space is needed, but the type and amount of white
+space is irrelevant. This means that formatting of the score file is
+left up to the programmer -- if it's simpler to just spew it all out on
+one looong line, then that's ok.
+
+The meaning of the various atoms are explained elsewhere in this
+manual.
+
+@node Headers
+@section Headers
+
+Gnus uses internally a format for storing article headers that
+corresponds to the @sc{nov} format in a mysterious fashion. One could
+almost suspect that the author looked at the @sc{nov} specification and
+just shamelessly @emph{stole} the entire thing, and one would be right.
+
+@dfn{Header} is a severly overloaded term. "Header" is used in RFC1036
+to talk about lines in the head of an article (eg., @code{From}). It is
+used by many people as a synonym for "head" -- "the header and the
+body". (That should be avoided, in my opinion.) And Gnus uses a format
+interanally that it calls "header", which is what I'm talking about
+here. This is a 9-element vector, basically, with each header (ouch)
+having one slot.
+
+These slots are, in order: @code{number}, @code{subject}, @code{from},
+@code{date}, @code{id}, @code{references}, @code{chars}, @code{lines},
+@code{xref}. There are macros for accessing and setting these slots --
+they all have predicatable names beginning with @code{mail-header-} and
+@code{mail-header-set-}, respectively.
+
+The @code{xref} slot is really a @code{misc} slot. Any extra info will
+be put in there.
+
+@node Ranges
+@section Ranges
+
+@sc{gnus} introduced a concept that I found so useful that I've started
+using it a lot and have elaborated on it greatly.
+
+The question is simple: If you have a large amount of objects that are
+identified by numbers (say, articles, to take a @emph{wild} example)
+that you want to callify as being "included", a normal sequence isn't
+very useful. (A 200,000 length sequence is a bit long-winded.)
+
+The solution is as simple as the question: You just collapse the
+sequence.
+
+@example
+(1 2 3 4 5 6 10 11 12)
+@end example
+
+is transformed into
+
+@example
+((1 . 6) (10 . 12))
+@end example
+
+To avoid having those nasty @samp{(13 . 13)} elements to denote a
+lonesome object, a @samp{13} is a valid element:
+
+@example
+((1 . 6) 7 (10 . 12))
+@end example
+
+This means that comparing two ranges to find out whether they are equal
+is slightly tricky:
+
+@example
+((1 . 6) 7 8 (10 . 12))
+@end example
+
+and
+
+@example
+((1 . 5) (7 . 8) (10 . 12))
+@end example
+
+are equal. In fact, any non-descending list is a range:
+
+@example
+(1 2 3 4 5)
+@end example
+
+is a perfectly valid range, although a pretty longwinded one. This is
+also legal:
+
+@example
+(1 . 5)
+@end example
+
+and is equal to the previous range.
+
+Here's a BNF definition of ranges. Of course, one must remember the
+semantic requirement that the numbers are non-descending. (Any number
+of repetition of the same number is allowed, but apt to disappear in
+range handling.)
+
+@example
+range = simple-range / normal-range
+simple-range = "(" number " . " number ")"
+normal-range = "(" start-contents ")"
+contents = "" / simple-range *[ " " contents ] /
+ number *[ " " contents ]
+@end example
+
+Gnus currently uses ranges to keep track of read articles and article
+marks. I plan on implementing a number of range operators in C if The
+Powers That Be are willing to let me. (I haven't asked yet, because I
+need to do some more thinking on what operators I need to make life
+totally range-based without ever having to convert back to normal
+sequences.)
+
+
+@node Group Info
+@section Group Info
+
+Gnus stores all permanent info on groups in a @dfn{group info} list.
+This list is from three to six elements (or more) long and exhaustively
+describes the group.
+
+Here are two example group infos; one is a very simple group while the
+second is a more complex one:
+
+@example
+("no.group" 5 (1 . 54324))
+
+("nnml:my.mail" 3 ((1 . 5) 9 (20 . 55))
+ ((tick (15 . 19)) (replied 3 6 (19 . 23)))
+ (nnml "")
+ (auto-expire (to-address "ding@@ifi.uio.no")))
+@end example
+
+The first element is the group name as Gnus knows the group; the second
+is the group level; the third is the read articles in range format; the
+fourth is a list of article marks lists; the fifth is the select method;
+and the sixth contains the group parameters.
+
+Here's a BNF definition of the group info format:
+
+@example
+info = "(" group space level space read
+ [ "" / [ space marks-list [ "" / [ space method [ "" /
+ space parameters ] ] ] ] ] ")"
+group = quote <string> quote
+level = <integer in the range of 1 to inf>
+read = range
+marks-lists = nil / "(" *marks ")"
+marks = "(" <string> range ")"
+method = "(" <string> *elisp-forms ")"
+parameters = "(" *elisp-forms ")"
+@end example
+
+Actually that @samp{marks} rule is a fib. A @samp{marks} is a
+@samp{<string>} consed on to a @samp{range}, but that's a bitch to say
+in pseudo-BNF.
+
+
@node Index
@chapter Index
@printindex cp