mirror of
https://git.savannah.gnu.org/git/emacs.git
synced 2025-01-08 15:35:02 +00:00
New version.
This commit is contained in:
parent
b8c631a53b
commit
231f989be9
220
lisp/custom.el
220
lisp/custom.el
@ -68,15 +68,50 @@
|
||||
|
||||
;;; Code:
|
||||
|
||||
(eval-when-compile
|
||||
(require 'cl))
|
||||
|
||||
;;; Compatibility:
|
||||
|
||||
(or (fboundp 'buffer-substring-no-properties)
|
||||
;; Introduced in Emacs 19.29.
|
||||
(defun buffer-substring-no-properties (beg end)
|
||||
"Return the text from BEG to END, without text properties, as a string."
|
||||
(let ((string (buffer-substring beg end)))
|
||||
(set-text-properties 0 (length string) nil string)
|
||||
string)))
|
||||
(defun custom-xmas-add-text-properties (start end props &optional object)
|
||||
(add-text-properties start end props object)
|
||||
(put-text-property start end 'start-open t object)
|
||||
(put-text-property start end 'end-open t object))
|
||||
|
||||
(defun custom-xmas-put-text-property (start end prop value &optional object)
|
||||
(put-text-property start end prop value object)
|
||||
(put-text-property start end 'start-open t object)
|
||||
(put-text-property start end 'end-open t object))
|
||||
|
||||
(defun custom-xmas-extent-start-open ()
|
||||
(map-extents (lambda (extent arg)
|
||||
(set-extent-property extent 'start-open t))
|
||||
nil (point) (min (1+ (point)) (point-max))))
|
||||
|
||||
(if (string-match "XEmacs\\|Lucid" emacs-version)
|
||||
(progn
|
||||
(fset 'custom-add-text-properties 'custom-xmas-add-text-properties)
|
||||
(fset 'custom-put-text-property 'custom-xmas-put-text-property)
|
||||
(fset 'custom-extent-start-open 'custom-xmas-extent-start-open)
|
||||
(fset 'custom-set-text-properties
|
||||
(if (fboundp 'set-text-properties)
|
||||
'set-text-properties))
|
||||
(fset 'custom-buffer-substring-no-properties
|
||||
(if (fboundp 'buffer-substring-no-properties)
|
||||
'buffer-substring-no-properties
|
||||
'custom-xmas-buffer-substring-no-properties)))
|
||||
(fset 'custom-add-text-properties 'add-text-properties)
|
||||
(fset 'custom-put-text-property 'put-text-property)
|
||||
(fset 'custom-extent-start-open 'ignore)
|
||||
(fset 'custom-set-text-properties 'set-text-properties)
|
||||
(fset 'custom-buffer-substring-no-properties
|
||||
'buffer-substring-no-properties))
|
||||
|
||||
(defun custom-xmas-buffer-substring-no-properties (beg end)
|
||||
"Return the text from BEG to END, without text properties, as a string."
|
||||
(let ((string (buffer-substring beg end)))
|
||||
(custom-set-text-properties 0 (length string) nil string)
|
||||
string))
|
||||
|
||||
(or (fboundp 'add-to-list)
|
||||
;; Introduced in Emacs 19.29.
|
||||
@ -171,16 +206,14 @@ STRING should be given if the last search was by `string-match' on STRING."
|
||||
(and (fboundp 'set-face-underline-p)
|
||||
(funcall 'set-face-underline-p 'underline t))))
|
||||
|
||||
(or (fboundp 'set-text-properties)
|
||||
;; Missing in XEmacs 19.12.
|
||||
(defun set-text-properties (start end props &optional buffer)
|
||||
(if (or (null buffer) (bufferp buffer))
|
||||
(if props
|
||||
(while props
|
||||
(put-text-property
|
||||
start end (car props) (nth 1 props) buffer)
|
||||
(setq props (nthcdr 2 props)))
|
||||
(remove-text-properties start end ())))))
|
||||
(defun custom-xmas-set-text-properties (start end props &optional buffer)
|
||||
(if (null buffer)
|
||||
(if props
|
||||
(while props
|
||||
(custom-put-text-property
|
||||
start end (car props) (nth 1 props) buffer)
|
||||
(setq props (nthcdr 2 props)))
|
||||
(remove-text-properties start end ()))))
|
||||
|
||||
(or (fboundp 'event-point)
|
||||
;; Missing in Emacs 19.29.
|
||||
@ -201,60 +234,6 @@ into the buffer visible in the event's window."
|
||||
(defvar custom-mouse-face nil)
|
||||
(defvar custom-field-active-face nil))
|
||||
|
||||
(or (and (fboundp 'modify-face) (not (featurep 'face-lock)))
|
||||
;; Introduced in Emacs 19.29. Incompatible definition also introduced
|
||||
;; by face-lock.el version 3.00 and above for Emacs 19.28 and below.
|
||||
;; face-lock does not call modify-face, so we can safely redefine it.
|
||||
(defun modify-face (face foreground background stipple
|
||||
bold-p italic-p underline-p)
|
||||
"Change the display attributes for face FACE.
|
||||
FOREGROUND and BACKGROUND should be color strings or nil.
|
||||
STIPPLE should be a stipple pattern name or nil.
|
||||
BOLD-P, ITALIC-P, and UNDERLINE-P specify whether the face should be set bold,
|
||||
in italic, and underlined, respectively. (Yes if non-nil.)
|
||||
If called interactively, prompts for a face and face attributes."
|
||||
(interactive
|
||||
(let* ((completion-ignore-case t)
|
||||
(face (symbol-name (read-face-name "Modify face: ")))
|
||||
(colors (mapcar 'list x-colors))
|
||||
(stipples (mapcar 'list
|
||||
(apply 'nconc
|
||||
(mapcar 'directory-files
|
||||
x-bitmap-file-path))))
|
||||
(foreground (modify-face-read-string
|
||||
face (face-foreground (intern face))
|
||||
"foreground" colors))
|
||||
(background (modify-face-read-string
|
||||
face (face-background (intern face))
|
||||
"background" colors))
|
||||
(stipple (modify-face-read-string
|
||||
face (face-stipple (intern face))
|
||||
"stipple" stipples))
|
||||
(bold-p (y-or-n-p (concat "Set face " face " bold ")))
|
||||
(italic-p (y-or-n-p (concat "Set face " face " italic ")))
|
||||
(underline-p (y-or-n-p (concat "Set face " face " underline "))))
|
||||
(message "Face %s: %s" face
|
||||
(mapconcat 'identity
|
||||
(delq nil
|
||||
(list (and foreground (concat (downcase foreground) " foreground"))
|
||||
(and background (concat (downcase background) " background"))
|
||||
(and stipple (concat (downcase stipple) " stipple"))
|
||||
(and bold-p "bold") (and italic-p "italic")
|
||||
(and underline-p "underline"))) ", "))
|
||||
(list (intern face) foreground background stipple
|
||||
bold-p italic-p underline-p)))
|
||||
(condition-case nil (set-face-foreground face foreground) (error nil))
|
||||
(condition-case nil (set-face-background face background) (error nil))
|
||||
(condition-case nil (set-face-stipple face stipple) (error nil))
|
||||
(if (string-match "XEmacs" emacs-version)
|
||||
(progn
|
||||
(funcall (if bold-p 'make-face-bold 'make-face-unbold) face)
|
||||
(funcall (if italic-p 'make-face-italic 'make-face-unitalic) face))
|
||||
(funcall (if bold-p 'make-face-bold 'make-face-unbold) face nil t)
|
||||
(funcall (if italic-p 'make-face-italic 'make-face-unitalic) face nil t))
|
||||
(set-face-underline-p face underline-p)
|
||||
(and (interactive-p) (redraw-display))))
|
||||
|
||||
;; We can't easily check for a working intangible.
|
||||
(defconst intangible (if (and (boundp 'emacs-minor-version)
|
||||
(or (> emacs-major-version 19)
|
||||
@ -281,9 +260,10 @@ If called interactively, prompts for a face and face attributes."
|
||||
|
||||
;; Put it in the Help menu, if possible.
|
||||
(if (string-match "XEmacs" emacs-version)
|
||||
;; XEmacs (disabled because it doesn't work)
|
||||
(and current-menubar
|
||||
(add-menu-item '("Help") "Customize..." 'customize nil))
|
||||
(if (featurep 'menubar)
|
||||
;; XEmacs (disabled because it doesn't work)
|
||||
(and current-menubar
|
||||
(add-menu-item '("Help") "Customize..." 'customize t)))
|
||||
;; Emacs 19.28 and earlier
|
||||
(global-set-key [ menu-bar help customize ]
|
||||
'("Customize..." . customize))
|
||||
@ -359,7 +339,7 @@ If called interactively, prompts for a face and face attributes."
|
||||
|
||||
(defun custom-category-set (from to category)
|
||||
"Make text between FROM and TWO have category CATEGORY."
|
||||
(put-text-property from to 'category category)))
|
||||
(custom-put-text-property from to 'category category)))
|
||||
|
||||
;;; External Data:
|
||||
;;
|
||||
@ -419,7 +399,7 @@ If called interactively, prompts for a face and face attributes."
|
||||
;; where each CUSTOM is a leaf in the hierarchy defined by the `type'
|
||||
;; property and `custom-type-properties'.
|
||||
|
||||
(defvar custom-file (convert-standard-filename "~/.custom.el")
|
||||
(defvar custom-file "~/.custom.el"
|
||||
"Name of file with customization information.")
|
||||
|
||||
(defconst custom-data
|
||||
@ -1080,6 +1060,7 @@ If optional ORIGINAL is non-nil, consider VALUE for the original value."
|
||||
(end (make-marker))
|
||||
(data (vector repeat nil start end))
|
||||
field)
|
||||
(custom-extent-start-open)
|
||||
(insert-before-markers "\n")
|
||||
(backward-char 1)
|
||||
(set-marker start (point))
|
||||
@ -1309,7 +1290,7 @@ If optional ORIGINAL is non-nil, consider VALUE for the original value."
|
||||
(face-tag (custom-face-tag custom))
|
||||
current)
|
||||
(if face-tag
|
||||
(put-text-property from (+ from (length (custom-tag custom)))
|
||||
(custom-put-text-property from (+ from (length (custom-tag custom)))
|
||||
'face (funcall face-tag field value)))
|
||||
(if original
|
||||
(custom-field-original-set field value))
|
||||
@ -1395,9 +1376,10 @@ If optional ORIGINAL is non-nil, consider VALUE for the original value."
|
||||
()
|
||||
(setq begin (point)
|
||||
found (custom-insert (custom-property custom 'none) nil))
|
||||
(add-text-properties begin (point)
|
||||
(list rear-nonsticky t
|
||||
'face custom-field-uninitialized-face)))
|
||||
(custom-add-text-properties
|
||||
begin (point)
|
||||
(list rear-nonsticky t
|
||||
'face custom-field-uninitialized-face)))
|
||||
(or original
|
||||
(custom-field-original-set found (custom-field-original field)))
|
||||
(custom-field-accept found value original)
|
||||
@ -1483,7 +1465,8 @@ If optional ORIGINAL is non-nil, consider VALUE for the original value."
|
||||
|
||||
(defun custom-face-import (custom value)
|
||||
"Modify CUSTOM's VALUE to match internal expectations."
|
||||
(let ((name (symbol-name value)))
|
||||
(let ((name (or (and (facep value) (symbol-name (face-name value)))
|
||||
(symbol-name value))))
|
||||
(list (if (string-match "\
|
||||
custom-face-\\(.*\\)-\\(.*\\)-\\(.*\\)-\\(.*\\)-\\(.*\\)-\\(.*\\)"
|
||||
name)
|
||||
@ -1496,9 +1479,8 @@ custom-face-\\(.*\\)-\\(.*\\)-\\(.*\\)-\\(.*\\)-\\(.*\\)-\\(.*\\)"
|
||||
(intern (match-string 6 name)))
|
||||
value))))
|
||||
|
||||
(defun custom-face-lookup (fg bg stipple bold italic underline)
|
||||
"Lookup or create a face with specified attributes.
|
||||
FG BG STIPPLE BOLD ITALIC UNDERLINE"
|
||||
(defun custom-face-lookup (&optional fg bg stipple bold italic underline)
|
||||
"Lookup or create a face with specified attributes."
|
||||
(let ((name (intern (format "custom-face-%s-%s-%s-%S-%S-%S"
|
||||
(or fg "default")
|
||||
(or bg "default")
|
||||
@ -1507,12 +1489,37 @@ FG BG STIPPLE BOLD ITALIC UNDERLINE"
|
||||
(if (and (custom-facep name)
|
||||
(fboundp 'make-face))
|
||||
()
|
||||
(make-face name)
|
||||
(modify-face name
|
||||
(if (string-equal fg "default") nil fg)
|
||||
(if (string-equal bg "default") nil bg)
|
||||
(if (string-equal stipple "default") nil stipple)
|
||||
bold italic underline))
|
||||
(copy-face 'default name)
|
||||
(when (and fg
|
||||
(not (string-equal fg "default")))
|
||||
(condition-case ()
|
||||
(set-face-foreground name fg)
|
||||
(error nil)))
|
||||
(when (and bg
|
||||
(not (string-equal bg "default")))
|
||||
(condition-case ()
|
||||
(set-face-background name bg)
|
||||
(error nil)))
|
||||
(when (and stipple
|
||||
(not (string-equal stipple "default"))
|
||||
(not (eq stipple 'custom:asis))
|
||||
(fboundp 'set-face-stipple))
|
||||
(set-face-stipple name stipple))
|
||||
(when (and bold
|
||||
(not (eq bold 'custom:asis)))
|
||||
(condition-case ()
|
||||
(make-face-bold name)
|
||||
(error nil)))
|
||||
(when (and italic
|
||||
(not (eq italic 'custom:asis)))
|
||||
(condition-case ()
|
||||
(make-face-italic name)
|
||||
(error nil)))
|
||||
(when (and underline
|
||||
(not (eq underline 'custom:asis)))
|
||||
(condition-case ()
|
||||
(set-face-underline-p name t)
|
||||
(error nil))))
|
||||
name))
|
||||
|
||||
(defun custom-face-hack (field value)
|
||||
@ -1528,7 +1535,7 @@ FG BG STIPPLE BOLD ITALIC UNDERLINE"
|
||||
(face (custom-field-face field))
|
||||
(from (point)))
|
||||
(custom-text-insert (custom-tag custom))
|
||||
(add-text-properties from (point)
|
||||
(custom-add-text-properties from (point)
|
||||
(list 'face face
|
||||
rear-nonsticky t))
|
||||
(custom-documentation-insert custom)
|
||||
@ -1539,7 +1546,7 @@ FG BG STIPPLE BOLD ITALIC UNDERLINE"
|
||||
"Update face of FIELD."
|
||||
(let ((from (custom-field-start field))
|
||||
(custom (custom-field-custom field)))
|
||||
(put-text-property from (+ from (length (custom-tag custom)))
|
||||
(custom-put-text-property from (+ from (length (custom-tag custom)))
|
||||
'face (custom-field-face field))))
|
||||
|
||||
(defun custom-const-valid (custom value)
|
||||
@ -1828,9 +1835,9 @@ If the optional argument SAVE is non-nil, use that for saving changes."
|
||||
(let ((from (point)))
|
||||
(insert tag)
|
||||
(custom-category-set from (point) 'custom-button-properties)
|
||||
(put-text-property from (point) 'custom-tag field)
|
||||
(custom-put-text-property from (point) 'custom-tag field)
|
||||
(if data
|
||||
(add-text-properties from (point) (list 'custom-data data)))))
|
||||
(custom-add-text-properties from (point) (list 'custom-data data)))))
|
||||
|
||||
(defun custom-documentation-insert (custom &rest ignore)
|
||||
"Insert documentation from CUSTOM in current buffer."
|
||||
@ -1849,11 +1856,13 @@ If the optional argument SAVE is non-nil, use that for saving changes."
|
||||
"Describe how to execute COMMAND."
|
||||
(let ((from (point)))
|
||||
(insert "`" (key-description (where-is-internal command nil t)) "'")
|
||||
(set-text-properties from (point)
|
||||
(list 'face custom-button-face
|
||||
mouse-face custom-mouse-face
|
||||
'custom-jump t ;Make TAB jump over it.
|
||||
'custom-tag command))
|
||||
(custom-set-text-properties from (point)
|
||||
(list 'face custom-button-face
|
||||
mouse-face custom-mouse-face
|
||||
'custom-jump t ;Make TAB jump over it.
|
||||
'custom-tag command
|
||||
'start-open t
|
||||
'end-open t))
|
||||
(custom-category-set from (point) 'custom-documentation-properties))
|
||||
(custom-help-insert ": " (custom-first-line (documentation command)) "\n"))
|
||||
|
||||
@ -2175,17 +2184,18 @@ If the optional argument is non-nil, show text iff the argument is positive."
|
||||
(insert-char (custom-padding custom)
|
||||
(- (custom-width custom) (- (point) from)))
|
||||
(custom-field-move field from (point))
|
||||
(set-text-properties
|
||||
(custom-set-text-properties
|
||||
from (point)
|
||||
(list 'custom-field field
|
||||
'custom-tag field
|
||||
'face (custom-field-face field)
|
||||
front-sticky t))))
|
||||
'start-open t
|
||||
'end-open t))))
|
||||
|
||||
(defun custom-field-read (field)
|
||||
;; Read the screen content of FIELD.
|
||||
(custom-read (custom-field-custom field)
|
||||
(buffer-substring-no-properties (custom-field-start field)
|
||||
(custom-buffer-substring-no-properties (custom-field-start field)
|
||||
(custom-field-end field))))
|
||||
|
||||
;; Fields are shown in a special `active' face when point is inside
|
||||
@ -2196,7 +2206,7 @@ If the optional argument is non-nil, show text iff the argument is positive."
|
||||
;; Deactivate FIELD.
|
||||
(let ((before-change-functions nil)
|
||||
(after-change-functions nil))
|
||||
(put-text-property (custom-field-start field) (custom-field-end field)
|
||||
(custom-put-text-property (custom-field-start field) (custom-field-end field)
|
||||
'face (custom-field-face field))))
|
||||
|
||||
(defun custom-field-enter (field)
|
||||
@ -2214,7 +2224,7 @@ If the optional argument is non-nil, show text iff the argument is positive."
|
||||
(setq pos (1- pos)))
|
||||
(if (< pos (point))
|
||||
(goto-char pos))))
|
||||
(put-text-property start end 'face custom-field-active-face)))
|
||||
(custom-put-text-property start end 'face custom-field-active-face)))
|
||||
|
||||
(defun custom-field-resize (field)
|
||||
;; Resize FIELD after change.
|
||||
@ -2296,7 +2306,7 @@ If the optional argument is non-nil, show text iff the argument is positive."
|
||||
(let ((field custom-field-was))
|
||||
(custom-assert '(prog1 field (setq custom-field-was nil)))
|
||||
;; Prevent mixing fields properties.
|
||||
(put-text-property begin end 'custom-field field)
|
||||
(custom-put-text-property begin end 'custom-field field)
|
||||
;; Update the field after modification.
|
||||
(if (eq (custom-field-property begin) field)
|
||||
(let ((field-end (custom-field-end field)))
|
||||
|
@ -1,6 +1,5 @@
|
||||
;;; gnus-cache.el --- cache interface for Gnus
|
||||
|
||||
;; Copyright (C) 1995 Free Software Foundation, Inc.
|
||||
;; Copyright (C) 1995,96 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
|
||||
;; Keywords: news
|
||||
@ -27,45 +26,55 @@
|
||||
;;; Code:
|
||||
|
||||
(require 'gnus)
|
||||
(eval-when-compile (require 'cl))
|
||||
|
||||
(defvar gnus-cache-directory (concat gnus-article-save-directory "cache/")
|
||||
(defvar gnus-cache-directory
|
||||
(nnheader-concat gnus-directory "cache/")
|
||||
"*The directory where cached articles will be stored.")
|
||||
|
||||
(defvar gnus-cache-active-file
|
||||
(concat (file-name-as-directory gnus-cache-directory) "active")
|
||||
"*The cache active file.")
|
||||
|
||||
(defvar gnus-cache-enter-articles '(ticked dormant)
|
||||
"*Classes of articles to enter into the cache.")
|
||||
|
||||
(defvar gnus-cache-remove-articles '(read)
|
||||
"*Classes of articles to remove from the cache.")
|
||||
|
||||
(defvar gnus-uncacheable-groups nil
|
||||
"*Groups that match this regexp will not be cached.
|
||||
|
||||
If you want to avoid caching your nnml groups, you could set this
|
||||
variable to \"^nnml\".")
|
||||
|
||||
|
||||
|
||||
;;; Internal variables.
|
||||
|
||||
(defvar gnus-cache-buffer nil)
|
||||
(defvar gnus-cache-active-hashtb nil)
|
||||
(defvar gnus-cache-active-altered nil)
|
||||
|
||||
(eval-and-compile
|
||||
(autoload 'nnml-generate-nov-databases-1 "nnml")
|
||||
(autoload 'nnvirtual-find-group-art "nnvirtual"))
|
||||
|
||||
|
||||
|
||||
(defun gnus-cache-change-buffer (group)
|
||||
(and gnus-cache-buffer
|
||||
;; see if the current group's overview cache has been loaded
|
||||
(or (string= group (car gnus-cache-buffer))
|
||||
;; another overview cache is current, save it
|
||||
(gnus-cache-save-buffers)))
|
||||
;; if gnus-cache buffer is nil, create it
|
||||
(or gnus-cache-buffer
|
||||
;; create cache buffer
|
||||
(save-excursion
|
||||
(setq gnus-cache-buffer
|
||||
(cons group
|
||||
(set-buffer (get-buffer-create " *gnus-cache-overview*"))))
|
||||
(buffer-disable-undo (current-buffer))
|
||||
;; insert the contents of this groups cache overview
|
||||
(erase-buffer)
|
||||
(let ((file (gnus-cache-file-name group ".overview")))
|
||||
(and (file-exists-p file)
|
||||
(insert-file-contents file)))
|
||||
;; we have a fresh (empty/just loaded) buffer,
|
||||
;; mark it as unmodified to save a redundant write later.
|
||||
(set-buffer-modified-p nil))))
|
||||
;;; Functions called from Gnus.
|
||||
|
||||
(defun gnus-cache-open ()
|
||||
"Initialize the cache."
|
||||
(gnus-cache-read-active))
|
||||
|
||||
(gnus-add-shutdown 'gnus-cache-close 'gnus)
|
||||
|
||||
(defun gnus-cache-close ()
|
||||
"Shut down the cache."
|
||||
(gnus-cache-write-active)
|
||||
(gnus-cache-save-buffers)
|
||||
(setq gnus-cache-active-hashtb nil))
|
||||
|
||||
(defun gnus-cache-save-buffers ()
|
||||
;; save the overview buffer if it exists and has been modified
|
||||
@ -99,185 +108,318 @@
|
||||
(gnus-kill-buffer buffer)
|
||||
(setq gnus-cache-buffer nil))))
|
||||
|
||||
(defun gnus-cache-possibly-enter-article
|
||||
(group article headers ticked dormant unread &optional force)
|
||||
(when (and (or force (not (eq gnus-use-cache 'passive)))
|
||||
(numberp article)
|
||||
(> article 0)
|
||||
(vectorp headers)) ; This might be a dummy article.
|
||||
;; If this is a virtual group, we find the real group.
|
||||
(when (gnus-virtual-group-p group)
|
||||
(let ((result (nnvirtual-find-group-art
|
||||
(gnus-group-real-name group) article)))
|
||||
(setq group (car result)
|
||||
headers (copy-sequence headers))
|
||||
(mail-header-set-number headers (cdr result))))
|
||||
(let ((number (mail-header-number headers))
|
||||
file dir)
|
||||
(when (and (> number 0) ; Reffed article.
|
||||
(or (not gnus-uncacheable-groups)
|
||||
(not (string-match gnus-uncacheable-groups group)))
|
||||
(or force
|
||||
(gnus-cache-member-of-class
|
||||
gnus-cache-enter-articles ticked dormant unread))
|
||||
(not (file-exists-p (setq file (gnus-cache-file-name
|
||||
group number)))))
|
||||
;; Possibly create the cache directory.
|
||||
(or (file-exists-p (setq dir (file-name-directory file)))
|
||||
(gnus-make-directory dir))
|
||||
;; Save the article in the cache.
|
||||
(if (file-exists-p file)
|
||||
t ; The article already is saved.
|
||||
(save-excursion
|
||||
(set-buffer nntp-server-buffer)
|
||||
(let ((gnus-use-cache nil))
|
||||
(gnus-request-article-this-buffer number group))
|
||||
(when (> (buffer-size) 0)
|
||||
(write-region (point-min) (point-max) file nil 'quiet)
|
||||
(gnus-cache-change-buffer group)
|
||||
(set-buffer (cdr gnus-cache-buffer))
|
||||
(goto-char (point-max))
|
||||
(forward-line -1)
|
||||
(while (condition-case ()
|
||||
(and (not (bobp))
|
||||
(> (read (current-buffer)) number))
|
||||
(error
|
||||
;; The line was malformed, so we just remove it!!
|
||||
(gnus-delete-line)
|
||||
t))
|
||||
(forward-line -1))
|
||||
(if (bobp)
|
||||
(if (not (eobp))
|
||||
(progn
|
||||
(beginning-of-line)
|
||||
(if (< (read (current-buffer)) number)
|
||||
(forward-line 1)))
|
||||
(beginning-of-line))
|
||||
(forward-line 1))
|
||||
(beginning-of-line)
|
||||
;; [number subject from date id references chars lines xref]
|
||||
(insert (format "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t\n"
|
||||
(mail-header-number headers)
|
||||
(mail-header-subject headers)
|
||||
(mail-header-from headers)
|
||||
(mail-header-date headers)
|
||||
(mail-header-id headers)
|
||||
(or (mail-header-references headers) "")
|
||||
(or (mail-header-chars headers) "")
|
||||
(or (mail-header-lines headers) "")
|
||||
(or (mail-header-xref headers) "")))
|
||||
;; Update the active info.
|
||||
(set-buffer gnus-summary-buffer)
|
||||
(gnus-cache-update-active group number)
|
||||
(push article gnus-newsgroup-cached)
|
||||
(gnus-summary-update-secondary-mark article))
|
||||
t))))))
|
||||
|
||||
(defun gnus-cache-enter-remove-article (article)
|
||||
"Mark ARTICLE for later possible removal."
|
||||
(when article
|
||||
(push article gnus-cache-removable-articles)))
|
||||
|
||||
(defun gnus-cache-possibly-remove-articles ()
|
||||
"Possibly remove some of the removable articles."
|
||||
(if (not (gnus-virtual-group-p gnus-newsgroup-name))
|
||||
(gnus-cache-possibly-remove-articles-1)
|
||||
(let ((arts gnus-cache-removable-articles)
|
||||
ga)
|
||||
(while arts
|
||||
(when (setq ga (nnvirtual-find-group-art
|
||||
(gnus-group-real-name gnus-newsgroup-name) (pop arts)))
|
||||
(let ((gnus-cache-removable-articles (list (cdr ga)))
|
||||
(gnus-newsgroup-name (car ga)))
|
||||
(gnus-cache-possibly-remove-articles-1)))))
|
||||
(setq gnus-cache-removable-articles nil)))
|
||||
|
||||
(defun gnus-cache-possibly-remove-articles-1 ()
|
||||
"Possibly remove some of the removable articles."
|
||||
(unless (eq gnus-use-cache 'passive)
|
||||
(let ((articles gnus-cache-removable-articles)
|
||||
(cache-articles gnus-newsgroup-cached)
|
||||
article)
|
||||
(gnus-cache-change-buffer gnus-newsgroup-name)
|
||||
(while articles
|
||||
(if (memq (setq article (pop articles)) cache-articles)
|
||||
;; The article was in the cache, so we see whether we are
|
||||
;; supposed to remove it from the cache.
|
||||
(gnus-cache-possibly-remove-article
|
||||
article (memq article gnus-newsgroup-marked)
|
||||
(memq article gnus-newsgroup-dormant)
|
||||
(or (memq article gnus-newsgroup-unreads)
|
||||
(memq article gnus-newsgroup-unselected))))))
|
||||
;; The overview file might have been modified, save it
|
||||
;; safe because we're only called at group exit anyway.
|
||||
(gnus-cache-save-buffers)))
|
||||
|
||||
(defun gnus-cache-request-article (article group)
|
||||
"Retrieve ARTICLE in GROUP from the cache."
|
||||
(let ((file (gnus-cache-file-name group article))
|
||||
(buffer-read-only nil))
|
||||
(when (file-exists-p file)
|
||||
(erase-buffer)
|
||||
(gnus-kill-all-overlays)
|
||||
(insert-file-contents file)
|
||||
t)))
|
||||
|
||||
(defun gnus-cache-possibly-alter-active (group active)
|
||||
"Alter the ACTIVE info for GROUP to reflect the articles in the cache."
|
||||
(let ((cache-active (gnus-gethash group gnus-cache-active-hashtb)))
|
||||
(and cache-active
|
||||
(< (car cache-active) (car active))
|
||||
(setcar active (car cache-active)))
|
||||
(and cache-active
|
||||
(> (cdr cache-active) (cdr active))
|
||||
(setcdr active (cdr cache-active)))))
|
||||
|
||||
(defun gnus-cache-retrieve-headers (articles group &optional fetch-old)
|
||||
"Retrieve the headers for ARTICLES in GROUP."
|
||||
(let ((cached
|
||||
(setq gnus-newsgroup-cached (gnus-cache-articles-in-group group))))
|
||||
(if (not cached)
|
||||
;; No cached articles here, so we just retrieve them
|
||||
;; the normal way.
|
||||
(let ((gnus-use-cache nil))
|
||||
(gnus-retrieve-headers articles group fetch-old))
|
||||
(let ((uncached-articles (gnus-sorted-intersection
|
||||
(gnus-sorted-complement articles cached)
|
||||
articles))
|
||||
(cache-file (gnus-cache-file-name group ".overview"))
|
||||
type)
|
||||
;; We first retrieve all the headers that we don't have in
|
||||
;; the cache.
|
||||
(let ((gnus-use-cache nil))
|
||||
(when uncached-articles
|
||||
(setq type (and articles
|
||||
(gnus-retrieve-headers
|
||||
uncached-articles group fetch-old)))))
|
||||
(gnus-cache-save-buffers)
|
||||
;; Then we insert the cached headers.
|
||||
(save-excursion
|
||||
(cond
|
||||
((not (file-exists-p cache-file))
|
||||
;; There are no cached headers.
|
||||
type)
|
||||
((null type)
|
||||
;; There were no uncached headers (or retrieval was
|
||||
;; unsuccessful), so we use the cached headers exclusively.
|
||||
(set-buffer nntp-server-buffer)
|
||||
(erase-buffer)
|
||||
(insert-file-contents cache-file)
|
||||
'nov)
|
||||
((eq type 'nov)
|
||||
;; We have both cached and uncached NOV headers, so we
|
||||
;; braid them.
|
||||
(gnus-cache-braid-nov group cached)
|
||||
type)
|
||||
(t
|
||||
;; We braid HEADs.
|
||||
(gnus-cache-braid-heads group (gnus-sorted-intersection
|
||||
cached articles))
|
||||
type)))))))
|
||||
|
||||
(defun gnus-cache-enter-article (&optional n)
|
||||
"Enter the next N articles into the cache.
|
||||
If not given a prefix, use the process marked articles instead.
|
||||
Returns the list of articles entered."
|
||||
(interactive "P")
|
||||
(gnus-set-global-variables)
|
||||
(let ((articles (gnus-summary-work-articles n))
|
||||
article out)
|
||||
(while articles
|
||||
(setq article (pop articles))
|
||||
(when (gnus-cache-possibly-enter-article
|
||||
gnus-newsgroup-name article (gnus-summary-article-header article)
|
||||
nil nil nil t)
|
||||
(push article out))
|
||||
(gnus-summary-remove-process-mark article)
|
||||
(gnus-summary-update-secondary-mark article))
|
||||
(gnus-summary-next-subject 1)
|
||||
(gnus-summary-position-point)
|
||||
(nreverse out)))
|
||||
|
||||
(defun gnus-cache-remove-article (n)
|
||||
"Remove the next N articles from the cache.
|
||||
If not given a prefix, use the process marked articles instead.
|
||||
Returns the list of articles removed."
|
||||
(interactive "P")
|
||||
(gnus-set-global-variables)
|
||||
(gnus-cache-change-buffer gnus-newsgroup-name)
|
||||
(let ((articles (gnus-summary-work-articles n))
|
||||
article out)
|
||||
(while articles
|
||||
(setq article (pop articles))
|
||||
(when (gnus-cache-possibly-remove-article article nil nil nil t)
|
||||
(push article out))
|
||||
(gnus-summary-remove-process-mark article)
|
||||
(gnus-summary-update-secondary-mark article))
|
||||
(gnus-summary-next-subject 1)
|
||||
(gnus-summary-position-point)
|
||||
(nreverse out)))
|
||||
|
||||
(defun gnus-cached-article-p (article)
|
||||
"Say whether ARTICLE is cached in the current group."
|
||||
(memq article gnus-newsgroup-cached))
|
||||
|
||||
;;; Internal functions.
|
||||
|
||||
(defun gnus-cache-change-buffer (group)
|
||||
(and gnus-cache-buffer
|
||||
;; See if the current group's overview cache has been loaded.
|
||||
(or (string= group (car gnus-cache-buffer))
|
||||
;; Another overview cache is current, save it.
|
||||
(gnus-cache-save-buffers)))
|
||||
;; if gnus-cache buffer is nil, create it
|
||||
(or gnus-cache-buffer
|
||||
;; Create cache buffer
|
||||
(save-excursion
|
||||
(setq gnus-cache-buffer
|
||||
(cons group
|
||||
(set-buffer (get-buffer-create " *gnus-cache-overview*"))))
|
||||
(buffer-disable-undo (current-buffer))
|
||||
;; Insert the contents of this group's cache overview.
|
||||
(erase-buffer)
|
||||
(let ((file (gnus-cache-file-name group ".overview")))
|
||||
(and (file-exists-p file)
|
||||
(insert-file-contents file)))
|
||||
;; We have a fresh (empty/just loaded) buffer,
|
||||
;; mark it as unmodified to save a redundant write later.
|
||||
(set-buffer-modified-p nil))))
|
||||
|
||||
;; Return whether an article is a member of a class.
|
||||
(defun gnus-cache-member-of-class (class ticked dormant unread)
|
||||
(or (and ticked (memq 'ticked class))
|
||||
(and dormant (memq 'dormant class))
|
||||
(and unread (memq 'unread class))
|
||||
(and (not unread) (memq 'read class))))
|
||||
(and (not unread) (not ticked) (not dormant) (memq 'read class))))
|
||||
|
||||
(defun gnus-cache-file-name (group article)
|
||||
(concat (file-name-as-directory gnus-cache-directory)
|
||||
(if (gnus-use-long-file-name 'not-cache)
|
||||
group
|
||||
(let ((group (concat group "")))
|
||||
(if (string-match ":" group)
|
||||
(aset group (match-beginning 0) ?/))
|
||||
(gnus-replace-chars-in-string group ?. ?/)))
|
||||
"/" (if (stringp article) article (int-to-string article))))
|
||||
(file-name-as-directory
|
||||
(if (gnus-use-long-file-name 'not-cache)
|
||||
group
|
||||
(let ((group (concat group "")))
|
||||
(if (string-match ":" group)
|
||||
(aset group (match-beginning 0) ?/))
|
||||
(nnheader-replace-chars-in-string group ?. ?/))))
|
||||
(if (stringp article) article (int-to-string article))))
|
||||
|
||||
(defun gnus-cache-possibly-enter-article
|
||||
(group article headers ticked dormant unread)
|
||||
(let ((number (mail-header-number headers))
|
||||
file dir)
|
||||
(if (or (not (vectorp headers)) ; This might be a dummy article.
|
||||
(< number 0) ; Reffed article from other group.
|
||||
(not (gnus-cache-member-of-class
|
||||
gnus-cache-enter-articles ticked dormant unread))
|
||||
(file-exists-p (setq file (gnus-cache-file-name group article))))
|
||||
() ; Do nothing.
|
||||
;; Possibly create the cache directory.
|
||||
(or (file-exists-p (setq dir (file-name-directory file)))
|
||||
(gnus-make-directory dir))
|
||||
;; Save the article in the cache.
|
||||
(if (file-exists-p file)
|
||||
t ; The article already is saved, so we end here.
|
||||
(let ((gnus-use-cache nil))
|
||||
(gnus-summary-select-article))
|
||||
(save-excursion
|
||||
(set-buffer gnus-article-buffer)
|
||||
(save-restriction
|
||||
(widen)
|
||||
(write-region (point-min) (point-max) file nil 'quiet))
|
||||
(gnus-cache-change-buffer group)
|
||||
(set-buffer (cdr gnus-cache-buffer))
|
||||
(goto-char (point-max))
|
||||
(forward-line -1)
|
||||
(while (condition-case ()
|
||||
(and (not (bobp))
|
||||
(> (read (current-buffer)) number))
|
||||
(error
|
||||
;; The line was malformed, so we just remove it!!
|
||||
(gnus-delete-line)
|
||||
t))
|
||||
(forward-line -1))
|
||||
(if (bobp)
|
||||
(if (not (eobp))
|
||||
(progn
|
||||
(beginning-of-line)
|
||||
(if (< (read (current-buffer)) number)
|
||||
(forward-line 1)))
|
||||
(beginning-of-line))
|
||||
(forward-line 1))
|
||||
(beginning-of-line)
|
||||
;; [number subject from date id references chars lines xref]
|
||||
(insert (format "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t\n"
|
||||
(mail-header-number headers)
|
||||
(mail-header-subject headers)
|
||||
(mail-header-from headers)
|
||||
(mail-header-date headers)
|
||||
(mail-header-id headers)
|
||||
(or (mail-header-references headers) "")
|
||||
(or (mail-header-chars headers) "")
|
||||
(or (mail-header-lines headers) "")
|
||||
(or (mail-header-xref headers) ""))))
|
||||
t))))
|
||||
(defun gnus-cache-update-article (group article)
|
||||
"If ARTICLE is in the cache, remove it and re-enter it."
|
||||
(when (gnus-cache-possibly-remove-article article nil nil nil t)
|
||||
(let ((gnus-use-cache nil))
|
||||
(gnus-cache-possibly-enter-article
|
||||
gnus-newsgroup-name article (gnus-summary-article-header article)
|
||||
nil nil nil t))))
|
||||
|
||||
(defun gnus-cache-enter-remove-article (article)
|
||||
(setq gnus-cache-removable-articles
|
||||
(cons article gnus-cache-removable-articles)))
|
||||
|
||||
(defsubst gnus-cache-possibly-remove-article
|
||||
(article ticked dormant unread)
|
||||
(let ((file (gnus-cache-file-name gnus-newsgroup-name article)))
|
||||
(if (or (not (file-exists-p file))
|
||||
(not (gnus-cache-member-of-class
|
||||
gnus-cache-remove-articles ticked dormant unread)))
|
||||
nil
|
||||
(defun gnus-cache-possibly-remove-article (article ticked dormant unread
|
||||
&optional force)
|
||||
"Possibly remove ARTICLE from the cache."
|
||||
(let ((group gnus-newsgroup-name)
|
||||
(number article)
|
||||
file)
|
||||
;; If this is a virtual group, we find the real group.
|
||||
(when (gnus-virtual-group-p group)
|
||||
(let ((result (nnvirtual-find-group-art
|
||||
(gnus-group-real-name group) article)))
|
||||
(setq group (car result)
|
||||
number (cdr result))))
|
||||
(setq file (gnus-cache-file-name group number))
|
||||
(when (and (file-exists-p file)
|
||||
(or force
|
||||
(gnus-cache-member-of-class
|
||||
gnus-cache-remove-articles ticked dormant unread)))
|
||||
(save-excursion
|
||||
(delete-file file)
|
||||
(set-buffer (cdr gnus-cache-buffer))
|
||||
(goto-char (point-min))
|
||||
(if (or (looking-at (concat (int-to-string article) "\t"))
|
||||
(search-forward (concat "\n" (int-to-string article) "\t")
|
||||
(if (or (looking-at (concat (int-to-string number) "\t"))
|
||||
(search-forward (concat "\n" (int-to-string number) "\t")
|
||||
(point-max) t))
|
||||
(delete-region (progn (beginning-of-line) (point))
|
||||
(progn (forward-line 1) (point))))))))
|
||||
|
||||
(defun gnus-cache-possibly-remove-articles ()
|
||||
(let ((articles gnus-cache-removable-articles)
|
||||
(cache-articles (gnus-cache-articles-in-group gnus-newsgroup-name))
|
||||
article)
|
||||
(gnus-cache-change-buffer gnus-newsgroup-name)
|
||||
(while articles
|
||||
(setq article (car articles)
|
||||
articles (cdr articles))
|
||||
(if (memq article cache-articles)
|
||||
;; The article was in the cache, so we see whether we are
|
||||
;; supposed to remove it from the cache.
|
||||
(gnus-cache-possibly-remove-article
|
||||
article (memq article gnus-newsgroup-marked)
|
||||
(memq article gnus-newsgroup-dormant)
|
||||
(or (memq article gnus-newsgroup-unreads)
|
||||
(memq article gnus-newsgroup-unselected))))))
|
||||
;; the overview file might have been modified, save it
|
||||
;; safe because we're only called at group exit anyway
|
||||
(gnus-cache-save-buffers))
|
||||
|
||||
|
||||
(defun gnus-cache-request-article (article group)
|
||||
(let ((file (gnus-cache-file-name group article)))
|
||||
(if (not (file-exists-p file))
|
||||
()
|
||||
(erase-buffer)
|
||||
;; There may be some overlays that we have to kill...
|
||||
(insert "i")
|
||||
(let ((overlays (overlays-at (point-min))))
|
||||
(while overlays
|
||||
(delete-overlay (car overlays))
|
||||
(setq overlays (cdr overlays))))
|
||||
(erase-buffer)
|
||||
(insert-file-contents file)
|
||||
(progn (forward-line 1) (point)))))
|
||||
(setq gnus-newsgroup-cached
|
||||
(delq article gnus-newsgroup-cached))
|
||||
(gnus-summary-update-secondary-mark article)
|
||||
t)))
|
||||
|
||||
(defun gnus-cache-articles-in-group (group)
|
||||
"Return a sorted list of cached articles in GROUP."
|
||||
(let ((dir (file-name-directory (gnus-cache-file-name group 1)))
|
||||
articles)
|
||||
(if (not (file-exists-p dir))
|
||||
nil
|
||||
(setq articles (directory-files dir nil "^[0-9]+$" t))
|
||||
(if (not articles)
|
||||
nil
|
||||
(sort (mapcar (function (lambda (name)
|
||||
(string-to-int name)))
|
||||
articles)
|
||||
'<)))))
|
||||
|
||||
(defun gnus-cache-active-articles (group)
|
||||
(let ((articles (gnus-cache-articles-in-group group)))
|
||||
(and articles
|
||||
(cons (car articles) (gnus-last-element articles)))))
|
||||
|
||||
(defun gnus-cache-possibly-alter-active (group active)
|
||||
(let ((cache-active (gnus-cache-active-articles group)))
|
||||
(and cache-active (< (car cache-active) (car active))
|
||||
(setcar active (car cache-active)))
|
||||
(and cache-active (> (cdr cache-active) (cdr active))
|
||||
(setcdr active (cdr cache-active)))))
|
||||
|
||||
(defun gnus-cache-retrieve-headers (articles group)
|
||||
(let* ((cached (gnus-cache-articles-in-group group))
|
||||
(articles (gnus-sorted-complement articles cached))
|
||||
(cache-file (gnus-cache-file-name group ".overview"))
|
||||
type)
|
||||
(let ((gnus-use-cache nil))
|
||||
(setq type (and articles (gnus-retrieve-headers articles group))))
|
||||
(gnus-cache-save-buffers)
|
||||
(save-excursion
|
||||
(cond ((not (file-exists-p cache-file))
|
||||
type)
|
||||
((null type)
|
||||
(set-buffer nntp-server-buffer)
|
||||
(erase-buffer)
|
||||
(insert-file-contents cache-file)
|
||||
'nov)
|
||||
((eq type 'nov)
|
||||
(gnus-cache-braid-nov group cached)
|
||||
type)
|
||||
(t
|
||||
(gnus-cache-braid-heads group cached)
|
||||
type)))))
|
||||
(when (file-exists-p dir)
|
||||
(sort (mapcar (lambda (name) (string-to-int name))
|
||||
(directory-files dir nil "^[0-9]+$" t))
|
||||
'<))))
|
||||
|
||||
(defun gnus-cache-braid-nov (group cached)
|
||||
(let ((cache-buf (get-buffer-create " *gnus-cache*"))
|
||||
@ -331,7 +473,9 @@
|
||||
(erase-buffer)
|
||||
(insert-file-contents (gnus-cache-file-name group (car cached)))
|
||||
(goto-char (point-min))
|
||||
(insert "220 " (int-to-string (car cached)) " Article retrieved.\n")
|
||||
(insert "220 ")
|
||||
(princ (car cached) (current-buffer))
|
||||
(insert " Article retrieved.\n")
|
||||
(search-forward "\n\n" nil 'move)
|
||||
(delete-region (point) (point-max))
|
||||
(forward-char -1)
|
||||
@ -340,23 +484,132 @@
|
||||
(setq cached (cdr cached)))
|
||||
(kill-buffer cache-buf)))
|
||||
|
||||
;;;###autoload
|
||||
(defun gnus-jog-cache ()
|
||||
"Go through all groups and put the articles into the cache."
|
||||
(interactive)
|
||||
(let ((newsrc (cdr gnus-newsrc-alist))
|
||||
(gnus-cache-enter-articles '(unread))
|
||||
(gnus-mark-article-hook nil)
|
||||
(let ((gnus-mark-article-hook nil)
|
||||
(gnus-expert-user t)
|
||||
(nnmail-spool-file nil)
|
||||
(gnus-use-dribble-file nil)
|
||||
(gnus-novice-user nil)
|
||||
(gnus-large-newsgroup nil))
|
||||
(while newsrc
|
||||
(gnus-summary-read-group (car (car newsrc)))
|
||||
(if (not (eq major-mode 'gnus-summary-mode))
|
||||
()
|
||||
(while gnus-newsgroup-unreads
|
||||
(gnus-summary-select-article t t nil (car gnus-newsgroup-unreads))
|
||||
(setq gnus-newsgroup-unreads (cdr gnus-newsgroup-unreads)))
|
||||
(kill-buffer (current-buffer)))
|
||||
(setq newsrc (cdr newsrc)))))
|
||||
;; Start Gnus.
|
||||
(gnus)
|
||||
;; Go through all groups...
|
||||
(gnus-group-mark-buffer)
|
||||
(gnus-group-universal-argument
|
||||
nil nil
|
||||
(lambda ()
|
||||
(gnus-summary-read-group nil nil t)
|
||||
;; ... and enter the articles into the cache.
|
||||
(when (eq major-mode 'gnus-summary-mode)
|
||||
(gnus-uu-mark-buffer)
|
||||
(gnus-cache-enter-article)
|
||||
(kill-buffer (current-buffer)))))))
|
||||
|
||||
(defun gnus-cache-read-active (&optional force)
|
||||
"Read the cache active file."
|
||||
(unless (file-exists-p gnus-cache-directory)
|
||||
(make-directory gnus-cache-directory t))
|
||||
(if (not (and (file-exists-p gnus-cache-active-file)
|
||||
(or force (not gnus-cache-active-hashtb))))
|
||||
;; There is no active file, so we generate one.
|
||||
(gnus-cache-generate-active)
|
||||
;; We simply read the active file.
|
||||
(save-excursion
|
||||
(gnus-set-work-buffer)
|
||||
(insert-file-contents gnus-cache-active-file)
|
||||
(gnus-active-to-gnus-format
|
||||
nil (setq gnus-cache-active-hashtb
|
||||
(gnus-make-hashtable
|
||||
(count-lines (point-min) (point-max)))))
|
||||
(setq gnus-cache-active-altered nil))))
|
||||
|
||||
(defun gnus-cache-write-active (&optional force)
|
||||
"Write the active hashtb to the active file."
|
||||
(when (or force
|
||||
(and gnus-cache-active-hashtb
|
||||
gnus-cache-active-altered))
|
||||
(save-excursion
|
||||
(gnus-set-work-buffer)
|
||||
(mapatoms
|
||||
(lambda (sym)
|
||||
(when (and sym (boundp sym))
|
||||
(insert (format "%s %d %d y\n"
|
||||
(symbol-name sym) (cdr (symbol-value sym))
|
||||
(car (symbol-value sym))))))
|
||||
gnus-cache-active-hashtb)
|
||||
(gnus-make-directory (file-name-directory gnus-cache-active-file))
|
||||
(write-region
|
||||
(point-min) (point-max) gnus-cache-active-file nil 'silent))
|
||||
;; Mark the active hashtb as unaltered.
|
||||
(setq gnus-cache-active-altered nil)))
|
||||
|
||||
(defun gnus-cache-update-active (group number &optional low)
|
||||
"Update the upper bound of the active info of GROUP to NUMBER.
|
||||
If LOW, update the lower bound instead."
|
||||
(let ((active (gnus-gethash group gnus-cache-active-hashtb)))
|
||||
(if (null active)
|
||||
;; We just create a new active entry for this group.
|
||||
(gnus-sethash group (cons number number) gnus-cache-active-hashtb)
|
||||
;; Update the lower or upper bound.
|
||||
(if low
|
||||
(setcar active number)
|
||||
(setcdr active number))
|
||||
;; Mark the active hashtb as altered.
|
||||
(setq gnus-cache-active-altered t))))
|
||||
|
||||
;;;###autoload
|
||||
(defun gnus-cache-generate-active (&optional directory)
|
||||
"Generate the cache active file."
|
||||
(interactive)
|
||||
(let* ((top (null directory))
|
||||
(directory (expand-file-name (or directory gnus-cache-directory)))
|
||||
(files (directory-files directory 'full))
|
||||
(group
|
||||
(if top
|
||||
""
|
||||
(string-match
|
||||
(concat "^" (file-name-as-directory
|
||||
(expand-file-name gnus-cache-directory)))
|
||||
(directory-file-name directory))
|
||||
(nnheader-replace-chars-in-string
|
||||
(substring (directory-file-name directory) (match-end 0))
|
||||
?/ ?.)))
|
||||
nums alphs)
|
||||
(when top
|
||||
(gnus-message 5 "Generating the cache active file...")
|
||||
(setq gnus-cache-active-hashtb (gnus-make-hashtable 123)))
|
||||
;; Separate articles from all other files and directories.
|
||||
(while files
|
||||
(if (string-match "^[0-9]+$" (file-name-nondirectory (car files)))
|
||||
(push (string-to-int (file-name-nondirectory (pop files))) nums)
|
||||
(push (pop files) alphs)))
|
||||
;; If we have nums, then this is probably a valid group.
|
||||
(when (setq nums (sort nums '<))
|
||||
(gnus-sethash group (cons (car nums) (gnus-last-element nums))
|
||||
gnus-cache-active-hashtb))
|
||||
;; Go through all the other files.
|
||||
(while alphs
|
||||
(when (and (file-directory-p (car alphs))
|
||||
(not (string-match "^\\.\\.?$"
|
||||
(file-name-nondirectory (car alphs)))))
|
||||
;; We descend directories.
|
||||
(gnus-cache-generate-active (car alphs)))
|
||||
(setq alphs (cdr alphs)))
|
||||
;; Write the new active file.
|
||||
(when top
|
||||
(gnus-cache-write-active t)
|
||||
(gnus-message 5 "Generating the cache active file...done"))))
|
||||
|
||||
;;;###autoload
|
||||
(defun gnus-cache-generate-nov-databases (dir)
|
||||
"Generate NOV files recursively starting in DIR."
|
||||
(interactive (list gnus-cache-directory))
|
||||
(gnus-cache-close)
|
||||
(let ((nnml-generate-active-function 'identity))
|
||||
(nnml-generate-nov-databases-1 dir)))
|
||||
|
||||
(provide 'gnus-cache)
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
;;; gnus-cite.el --- parse citations in articles for Gnus
|
||||
|
||||
;; Copyright (C) 1995 Free Software Foundation, Inc.
|
||||
;; Copyright (C) 1995,96 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Per Abrahamsen <abraham@iesd.auc.dk>
|
||||
;; Keywords: news, mail
|
||||
@ -29,13 +28,19 @@
|
||||
(require 'gnus)
|
||||
(require 'gnus-msg)
|
||||
(require 'gnus-ems)
|
||||
(eval-when-compile (require 'cl))
|
||||
|
||||
(eval-and-compile
|
||||
(autoload 'gnus-article-add-button "gnus-vis")
|
||||
)
|
||||
(autoload 'gnus-article-add-button "gnus-vis"))
|
||||
|
||||
;;; Customization:
|
||||
|
||||
(defvar gnus-cited-text-button-line-format "%(%{[...]%}%)\n"
|
||||
"Format of cited text buttons.")
|
||||
|
||||
(defvar gnus-cited-lines-visible nil
|
||||
"The number of lines of hidden cited text to remain visible.")
|
||||
|
||||
(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.")
|
||||
@ -45,20 +50,20 @@ Set it to nil to parse all articles.")
|
||||
"Regexp matching the longest possible citation prefix on a line.")
|
||||
|
||||
(defvar gnus-cite-max-prefix 20
|
||||
"Maximal possible length for a citation prefix.")
|
||||
"Maximum possible length for a citation prefix.")
|
||||
|
||||
(defvar gnus-supercite-regexp
|
||||
(concat "^\\(" gnus-cite-prefix-regexp "\\)? *"
|
||||
">>>>> +\"\\([^\"\n]+\\)\" +==")
|
||||
"Regexp matching normal SuperCite attribution lines.
|
||||
The first regexp group should match a prefix added by another package.")
|
||||
"Regexp matching normal Supercite attribution lines.
|
||||
The first grouping must match prefixes added by other packages.")
|
||||
|
||||
(defvar gnus-supercite-secondary-regexp "^.*\"\\([^\"\n]+\\)\" +=="
|
||||
"Regexp matching mangled SuperCite attribution lines.
|
||||
The first regexp group should match the SuperCite attribution.")
|
||||
"Regexp matching mangled Supercite attribution lines.
|
||||
The first regexp group should match the Supercite attribution.")
|
||||
|
||||
(defvar gnus-cite-minimum-match-count 2
|
||||
"Minimal number of identical prefix'es before we believe it is a citation.")
|
||||
"Minimum number of identical prefixes before we believe it's a citation.")
|
||||
|
||||
;see gnus-cus.el
|
||||
;(defvar gnus-cite-face-list
|
||||
@ -78,7 +83,7 @@ The first regexp group should match the SuperCite attribution.")
|
||||
(defvar gnus-cite-attribution-prefix "in article\\|in <"
|
||||
"Regexp matching the beginning of an attribution line.")
|
||||
|
||||
(defvar gnus-cite-attribution-postfix
|
||||
(defvar gnus-cite-attribution-suffix
|
||||
"\\(wrote\\|writes\\|said\\|says\\):[ \t]*$"
|
||||
"Regexp matching the end of an attribution line.
|
||||
The text matching the first grouping will be used as a button.")
|
||||
@ -112,9 +117,7 @@ The text matching the first grouping will be used as a button.")
|
||||
|
||||
;;; Internal Variables:
|
||||
|
||||
(defvar gnus-article-length nil)
|
||||
;; Length of article last time we parsed it.
|
||||
;; BUG! KLUDGE! UGLY! FIX ME!
|
||||
(defvar gnus-cite-article nil)
|
||||
|
||||
(defvar gnus-cite-prefix-alist nil)
|
||||
;; Alist of citation prefixes.
|
||||
@ -135,7 +138,13 @@ The text matching the first grouping will be used as a button.")
|
||||
;; WROTE: is the attribution line number
|
||||
;; IN: is the line number of the previous line if part of the same attribution,
|
||||
;; PREFIX: Is the citation prefix of the attribution line(s), and
|
||||
;; TAG: Is a SuperCite tag, if any.
|
||||
;; TAG: Is a Supercite tag, if any.
|
||||
|
||||
(defvar gnus-cited-text-button-line-format-alist
|
||||
`((?b beg ?d)
|
||||
(?e end ?d)
|
||||
(?l (- end beg) ?d)))
|
||||
(defvar gnus-cited-text-button-line-format-spec nil)
|
||||
|
||||
;;; Commands:
|
||||
|
||||
@ -149,7 +158,7 @@ corresponding citation merged with `gnus-cite-attribution-face'.
|
||||
Text is considered cited if at least `gnus-cite-minimum-match-count'
|
||||
lines matches `gnus-cite-prefix-regexp' with the same prefix.
|
||||
|
||||
Lines matching `gnus-cite-attribution-postfix' and perhaps
|
||||
Lines matching `gnus-cite-attribution-suffix' and perhaps
|
||||
`gnus-cite-attribution-prefix' are considered attribution lines."
|
||||
(interactive (list 'force))
|
||||
;; Create dark or light faces if necessary.
|
||||
@ -193,7 +202,7 @@ Lines matching `gnus-cite-attribution-postfix' and perhaps
|
||||
face (cdr (assoc prefix face-alist)))
|
||||
;; Add attribution button.
|
||||
(goto-line number)
|
||||
(if (re-search-forward gnus-cite-attribution-postfix
|
||||
(if (re-search-forward gnus-cite-attribution-suffix
|
||||
(save-excursion (end-of-line 1) (point))
|
||||
t)
|
||||
(gnus-article-add-button (match-beginning 1) (match-end 1)
|
||||
@ -210,76 +219,203 @@ Lines matching `gnus-cite-attribution-postfix' and perhaps
|
||||
skip (gnus-cite-find-prefix number))
|
||||
(gnus-cite-add-face number skip gnus-cite-attribution-face)))))
|
||||
|
||||
(defun gnus-article-hide-citation (&optional force)
|
||||
"Hide all cited text except attribution lines.
|
||||
See the documentation for `gnus-article-highlight-citation'."
|
||||
(interactive (list 'force))
|
||||
(defun gnus-dissect-cited-text ()
|
||||
"Dissect the article buffer looking for cited text."
|
||||
(save-excursion
|
||||
(set-buffer gnus-article-buffer)
|
||||
(gnus-cite-parse-maybe force)
|
||||
(let ((buffer-read-only nil)
|
||||
(alist gnus-cite-prefix-alist)
|
||||
(inhibit-point-motion-hooks t)
|
||||
numbers number)
|
||||
(gnus-cite-parse-maybe)
|
||||
(let ((alist gnus-cite-prefix-alist)
|
||||
prefix numbers number marks m)
|
||||
;; Loop through citation prefixes.
|
||||
(while alist
|
||||
(setq numbers (cdr (car alist))
|
||||
alist (cdr alist))
|
||||
(setq numbers (pop alist)
|
||||
prefix (pop numbers))
|
||||
(while numbers
|
||||
(setq number (car numbers)
|
||||
numbers (cdr numbers))
|
||||
(goto-line number)
|
||||
(or (assq number gnus-cite-attribution-alist)
|
||||
(add-text-properties (point) (progn (forward-line 1) (point))
|
||||
gnus-hidden-properties)))))))
|
||||
(setq number (pop numbers))
|
||||
(goto-char (point-min))
|
||||
(forward-line number)
|
||||
(push (cons (point-marker) "") marks)
|
||||
(while (and numbers
|
||||
(= (1- number) (car numbers)))
|
||||
(setq number (pop numbers)))
|
||||
(goto-char (point-min))
|
||||
(forward-line (1- number))
|
||||
(push (cons (point-marker) prefix) marks)))
|
||||
(goto-char (point-min))
|
||||
(search-forward "\n\n" nil t)
|
||||
(push (cons (point-marker) "") marks)
|
||||
(goto-char (point-max))
|
||||
(re-search-backward gnus-signature-separator nil t)
|
||||
(push (cons (point-marker) "") marks)
|
||||
(setq marks (sort marks (lambda (m1 m2) (< (car m1) (car m2)))))
|
||||
(let* ((omarks marks))
|
||||
(setq marks nil)
|
||||
(while (cdr omarks)
|
||||
(if (= (caar omarks) (caadr omarks))
|
||||
(progn
|
||||
(unless (equal (cdar omarks) "")
|
||||
(push (car omarks) marks))
|
||||
(unless (equal (cdadr omarks) "")
|
||||
(push (cadr omarks) marks))
|
||||
(setq omarks (cdr omarks)))
|
||||
(push (car omarks) marks))
|
||||
(setq omarks (cdr omarks)))
|
||||
(when (car omarks)
|
||||
(push (car omarks) marks))
|
||||
(setq marks (setq m (nreverse marks)))
|
||||
(while (cddr m)
|
||||
(if (and (equal (cdadr m) "")
|
||||
(equal (cdar m) (cdaddr m))
|
||||
(goto-char (caadr m))
|
||||
(forward-line 1)
|
||||
(= (point) (caaddr m)))
|
||||
(setcdr m (cdddr m))
|
||||
(setq m (cdr m))))
|
||||
marks))))
|
||||
|
||||
|
||||
(defun gnus-article-hide-citation-maybe (&optional force)
|
||||
"Hide cited text that has an attribution line.
|
||||
(defun gnus-article-fill-cited-article (&optional force)
|
||||
"Do word wrapping in the current article."
|
||||
(interactive (list t))
|
||||
(save-excursion
|
||||
(set-buffer gnus-article-buffer)
|
||||
(let ((buffer-read-only nil)
|
||||
(inhibit-point-motion-hooks t)
|
||||
(marks (gnus-dissect-cited-text))
|
||||
(adaptive-fill-mode nil))
|
||||
(save-restriction
|
||||
(while (cdr marks)
|
||||
(widen)
|
||||
(narrow-to-region (caar marks) (caadr marks))
|
||||
(let ((adaptive-fill-regexp
|
||||
(concat "^" (regexp-quote (cdar marks)) " *"))
|
||||
(fill-prefix (cdar marks)))
|
||||
(fill-region (point-min) (point-max)))
|
||||
(set-marker (caar marks) nil)
|
||||
(setq marks (cdr marks)))
|
||||
(when marks
|
||||
(set-marker (caar marks) nil))))))
|
||||
|
||||
(defun gnus-article-hide-citation (&optional arg force)
|
||||
"Toggle hiding of all cited text except attribution lines.
|
||||
See the documentation for `gnus-article-highlight-citation'.
|
||||
If given a negative prefix, always show; if given a positive prefix,
|
||||
always hide."
|
||||
(interactive (append (gnus-hidden-arg) (list 'force)))
|
||||
(setq gnus-cited-text-button-line-format-spec
|
||||
(gnus-parse-format gnus-cited-text-button-line-format
|
||||
gnus-cited-text-button-line-format-alist t))
|
||||
(unless (gnus-article-check-hidden-text 'cite arg)
|
||||
(save-excursion
|
||||
(set-buffer gnus-article-buffer)
|
||||
(let ((buffer-read-only nil)
|
||||
(marks (gnus-dissect-cited-text))
|
||||
(inhibit-point-motion-hooks t)
|
||||
(props (nconc (list 'gnus-type 'cite)
|
||||
gnus-hidden-properties))
|
||||
beg end)
|
||||
(while marks
|
||||
(setq beg nil
|
||||
end nil)
|
||||
(while (and marks (string= (cdar marks) ""))
|
||||
(setq marks (cdr marks)))
|
||||
(when marks
|
||||
(setq beg (caar marks)))
|
||||
(while (and marks (not (string= (cdar marks) "")))
|
||||
(setq marks (cdr marks)))
|
||||
(when marks
|
||||
(setq end (caar marks)))
|
||||
;; Skip past lines we want to leave visible.
|
||||
(when (and beg end gnus-cited-lines-visible)
|
||||
(goto-char beg)
|
||||
(forward-line gnus-cited-lines-visible)
|
||||
(if (>= (point) end)
|
||||
(setq beg nil)
|
||||
(setq beg (point-marker))))
|
||||
(when (and beg end)
|
||||
(gnus-add-text-properties beg end props)
|
||||
(goto-char beg)
|
||||
(unless (save-excursion (search-backward "\n\n" nil t))
|
||||
(insert "\n"))
|
||||
(gnus-article-add-button
|
||||
(point)
|
||||
(progn (eval gnus-cited-text-button-line-format-spec) (point))
|
||||
`gnus-article-toggle-cited-text (cons beg end))
|
||||
(set-marker beg (point))))))))
|
||||
|
||||
(defun gnus-article-toggle-cited-text (region)
|
||||
"Toggle hiding the text in REGION."
|
||||
(let (buffer-read-only)
|
||||
(funcall
|
||||
(if (text-property-any
|
||||
(car region) (1- (cdr region))
|
||||
(car gnus-hidden-properties) (cadr gnus-hidden-properties))
|
||||
'remove-text-properties 'gnus-add-text-properties)
|
||||
(car region) (cdr region) gnus-hidden-properties)))
|
||||
|
||||
(defun gnus-article-hide-citation-maybe (&optional arg force)
|
||||
"Toggle hiding of cited text that has an attribution line.
|
||||
If given a negative prefix, always show; if given a positive prefix,
|
||||
always hide.
|
||||
This will do nothing unless at least `gnus-cite-hide-percentage'
|
||||
percent and at least `gnus-cite-hide-absolute' lines of the body is
|
||||
cited text with attributions. When called interactively, these two
|
||||
variables are ignored.
|
||||
See also the documentation for `gnus-article-highlight-citation'."
|
||||
(interactive (list 'force))
|
||||
(interactive (append (gnus-hidden-arg) (list 'force)))
|
||||
(unless (gnus-article-check-hidden-text 'cite arg)
|
||||
(save-excursion
|
||||
(set-buffer gnus-article-buffer)
|
||||
(gnus-cite-parse-maybe force)
|
||||
(goto-char (point-min))
|
||||
(search-forward "\n\n" nil t)
|
||||
(let ((start (point))
|
||||
(atts gnus-cite-attribution-alist)
|
||||
(buffer-read-only nil)
|
||||
(inhibit-point-motion-hooks t)
|
||||
(hiden 0)
|
||||
total)
|
||||
(goto-char (point-max))
|
||||
(re-search-backward gnus-signature-separator nil t)
|
||||
(setq total (count-lines start (point)))
|
||||
(while atts
|
||||
(setq hiden (+ hiden (length (cdr (assoc (cdar atts)
|
||||
gnus-cite-prefix-alist))))
|
||||
atts (cdr atts)))
|
||||
(if (or force
|
||||
(and (> (* 100 hiden) (* gnus-cite-hide-percentage total))
|
||||
(> hiden gnus-cite-hide-absolute)))
|
||||
(progn
|
||||
(setq atts gnus-cite-attribution-alist)
|
||||
(while atts
|
||||
(setq total (cdr (assoc (cdar atts) gnus-cite-prefix-alist))
|
||||
atts (cdr atts))
|
||||
(while total
|
||||
(setq hiden (car total)
|
||||
total (cdr total))
|
||||
(goto-line hiden)
|
||||
(or (assq hiden gnus-cite-attribution-alist)
|
||||
(gnus-add-text-properties
|
||||
(point) (progn (forward-line 1) (point))
|
||||
(nconc (list 'gnus-type 'cite)
|
||||
gnus-hidden-properties)))))))))))
|
||||
|
||||
(defun gnus-article-hide-citation-in-followups ()
|
||||
"Hide cited text in non-root articles."
|
||||
(interactive)
|
||||
(save-excursion
|
||||
(set-buffer gnus-article-buffer)
|
||||
(gnus-cite-parse-maybe force)
|
||||
(goto-char (point-min))
|
||||
(search-forward "\n\n" nil t)
|
||||
(let ((start (point))
|
||||
(atts gnus-cite-attribution-alist)
|
||||
(buffer-read-only nil)
|
||||
(inhibit-point-motion-hooks t)
|
||||
(hiden 0)
|
||||
total)
|
||||
(goto-char (point-max))
|
||||
(re-search-backward gnus-signature-separator nil t)
|
||||
(setq total (count-lines start (point)))
|
||||
(while atts
|
||||
(setq hiden (+ hiden (length (cdr (assoc (cdr (car atts))
|
||||
gnus-cite-prefix-alist))))
|
||||
atts (cdr atts)))
|
||||
(if (or force
|
||||
(and (> (* 100 hiden) (* gnus-cite-hide-percentage total))
|
||||
(> hiden gnus-cite-hide-absolute)))
|
||||
(progn
|
||||
(setq atts gnus-cite-attribution-alist)
|
||||
(while atts
|
||||
(setq total (cdr (assoc (cdr (car atts)) gnus-cite-prefix-alist))
|
||||
atts (cdr atts))
|
||||
(while total
|
||||
(setq hiden (car total)
|
||||
total (cdr total))
|
||||
(goto-line hiden)
|
||||
(or (assq hiden gnus-cite-attribution-alist)
|
||||
(add-text-properties (point)
|
||||
(progn (forward-line 1) (point))
|
||||
gnus-hidden-properties)))))))))
|
||||
(let ((article (cdr gnus-article-current)))
|
||||
(unless (save-excursion
|
||||
(set-buffer gnus-summary-buffer)
|
||||
(gnus-article-displayed-root-p article))
|
||||
(gnus-article-hide-citation)))))
|
||||
|
||||
;;; Internal functions:
|
||||
|
||||
(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)))
|
||||
(if (equal gnus-cite-article gnus-article-current)
|
||||
()
|
||||
;;Reset parser information.
|
||||
(setq gnus-cite-prefix-alist nil
|
||||
@ -291,7 +427,8 @@ See also the documentation for `gnus-article-highlight-citation'."
|
||||
gnus-cite-parse-max-size
|
||||
(> (buffer-size) gnus-cite-parse-max-size))
|
||||
()
|
||||
(setq gnus-article-length (- (point-max) (point-min)))
|
||||
(setq gnus-cite-article (cons (car gnus-article-current)
|
||||
(cdr gnus-article-current)))
|
||||
(gnus-cite-parse))))
|
||||
|
||||
(defun gnus-cite-parse ()
|
||||
@ -315,7 +452,7 @@ See also the documentation for `gnus-article-highlight-citation'."
|
||||
end (progn (beginning-of-line 2) (point))
|
||||
start end)
|
||||
(goto-char begin)
|
||||
;; Ignore standard SuperCite attribution prefix.
|
||||
;; Ignore standard Supercite attribution prefix.
|
||||
(if (looking-at gnus-supercite-regexp)
|
||||
(if (match-end 1)
|
||||
(setq end (1+ (match-end 1)))
|
||||
@ -327,7 +464,7 @@ See also the documentation for `gnus-article-highlight-citation'."
|
||||
;; Each prefix.
|
||||
(setq end (match-end 0)
|
||||
prefix (buffer-substring begin end))
|
||||
(set-text-properties 0 (length prefix) nil prefix)
|
||||
(gnus-set-text-properties 0 (length prefix) nil prefix)
|
||||
(setq entry (assoc prefix alist))
|
||||
(if entry
|
||||
(setcdr entry (cons line (cdr entry)))
|
||||
@ -374,7 +511,7 @@ See also the documentation for `gnus-article-highlight-citation'."
|
||||
;; Parse current buffer searching for attribution lines.
|
||||
(goto-char (point-min))
|
||||
(search-forward "\n\n" nil t)
|
||||
(while (re-search-forward gnus-cite-attribution-postfix (point-max) t)
|
||||
(while (re-search-forward gnus-cite-attribution-suffix (point-max) t)
|
||||
(let* ((start (match-beginning 0))
|
||||
(end (match-end 0))
|
||||
(wrote (count-lines (point-min) end))
|
||||
@ -392,7 +529,7 @@ See also the documentation for `gnus-article-highlight-citation'."
|
||||
(beginning-of-line 0)
|
||||
(point))
|
||||
t)
|
||||
(not (re-search-forward gnus-cite-attribution-postfix
|
||||
(not (re-search-forward gnus-cite-attribution-suffix
|
||||
start t))
|
||||
(count-lines (point-min) (1+ (point)))))))
|
||||
(if (eq wrote in)
|
||||
@ -463,7 +600,7 @@ See also the documentation for `gnus-article-highlight-citation'."
|
||||
;;
|
||||
;; WROTE is the attribution line number.
|
||||
;; PREFIX is the attribution line prefix.
|
||||
;; TAG is the SuperCite tag on the attribution line.
|
||||
;; TAG is the Supercite tag on the attribution line.
|
||||
(let ((atts gnus-cite-loose-attribution-alist)
|
||||
(case-fold-search t)
|
||||
att wrote in prefix tag regexp limit smallest best size)
|
||||
@ -536,18 +673,19 @@ See also the documentation for `gnus-article-highlight-citation'."
|
||||
|
||||
(defun gnus-cite-add-face (number prefix face)
|
||||
;; At line NUMBER, ignore PREFIX and add FACE to the rest of the line.
|
||||
(if face
|
||||
(let ((inhibit-point-motion-hooks t)
|
||||
from to)
|
||||
(goto-line number)
|
||||
(when face
|
||||
(let ((inhibit-point-motion-hooks t)
|
||||
from to)
|
||||
(goto-line number)
|
||||
(unless (eobp) ;; Sometimes things become confused.
|
||||
(forward-char (length prefix))
|
||||
(skip-chars-forward " \t")
|
||||
(setq from (point))
|
||||
(end-of-line 1)
|
||||
(skip-chars-backward " \t")
|
||||
(setq to (point))
|
||||
(if (< from to)
|
||||
(gnus-overlay-put (gnus-make-overlay from to) 'face face)))))
|
||||
(when (< from to)
|
||||
(gnus-overlay-put (gnus-make-overlay from to) 'face face))))))
|
||||
|
||||
(defun gnus-cite-toggle (prefix)
|
||||
(save-excursion
|
||||
@ -565,8 +703,10 @@ See also the documentation for `gnus-article-highlight-citation'."
|
||||
gnus-hidden-properties))
|
||||
((assq number gnus-cite-attribution-alist))
|
||||
(t
|
||||
(add-text-properties (point) (progn (forward-line 1) (point))
|
||||
gnus-hidden-properties)))))))
|
||||
(gnus-add-text-properties
|
||||
(point) (progn (forward-line 1) (point))
|
||||
(nconc (list 'gnus-type 'cite)
|
||||
gnus-hidden-properties))))))))
|
||||
|
||||
(defun gnus-cite-find-prefix (line)
|
||||
;; Return citation prefix for LINE.
|
||||
@ -580,6 +720,11 @@ See also the documentation for `gnus-article-highlight-citation'."
|
||||
(setq prefix (car entry))))
|
||||
prefix))
|
||||
|
||||
(gnus-add-shutdown 'gnus-cache-close 'gnus)
|
||||
|
||||
(defun gnus-cache-close ()
|
||||
(setq gnus-cite-prefix-alist nil))
|
||||
|
||||
(gnus-ems-redefine)
|
||||
|
||||
(provide 'gnus-cite)
|
||||
|
830
lisp/gnus-cus.el
830
lisp/gnus-cus.el
@ -1,7 +1,6 @@
|
||||
;;; gnus-cus.el --- User friendly customization of Gnus
|
||||
|
||||
;; Copyright (C) 1995 Free Software Foundation, Inc.
|
||||
|
||||
;; Copyright (C) 1995,96 Free Software Foundation, Inc.
|
||||
;;
|
||||
;; Author: Per Abrahamsen <abraham@iesd.auc.dk>
|
||||
;; Keywords: help, news
|
||||
;; Version: 0.1
|
||||
@ -29,12 +28,14 @@
|
||||
|
||||
(require 'custom)
|
||||
(require 'gnus-ems)
|
||||
(require 'browse-url)
|
||||
(eval-when-compile (require 'cl))
|
||||
|
||||
;; The following is just helper functions and data, not ment to be set
|
||||
;; The following is just helper functions and data, not meant to be set
|
||||
;; by the user.
|
||||
(defun gnus-make-face (color)
|
||||
;; Create entry for face with COLOR.
|
||||
(custom-face-lookup color nil nil 'custom:asis 'custom:asis 'custom:asis))
|
||||
(custom-face-lookup color nil nil nil nil nil))
|
||||
|
||||
(defvar gnus-face-light-name-list
|
||||
'("light blue" "light cyan" "light yellow" "light pink"
|
||||
@ -42,84 +43,107 @@
|
||||
"turquoise"))
|
||||
|
||||
(defvar gnus-face-dark-name-list
|
||||
'("RoyalBlue" "firebrick"
|
||||
"dark green" "OrangeRed" "dark khaki" "dark violet"
|
||||
"SteelBlue4"))
|
||||
'("dark blue" "firebrick" "dark green" "OrangeRed"
|
||||
"dark khaki" "dark violet" "SteelBlue4"))
|
||||
; CornflowerBlue SeaGreen OrangeRed SteelBlue4 DeepPink3
|
||||
; DarkOlviveGreen4
|
||||
|
||||
(custom-declare '()
|
||||
'((tag . "GNUS")
|
||||
'((tag . "Gnus")
|
||||
(doc . "\
|
||||
The coffee-brewing, all singing, all dancing, kitchen sink newsreader.")
|
||||
(type . group)
|
||||
(data ((tag . "Visual")
|
||||
(doc . "\
|
||||
GNUS can be made colorful and fun or grey and dull as you wish.")
|
||||
(type . group)
|
||||
(data ((tag . "Visual")
|
||||
(doc . "Enable visual features.
|
||||
(data
|
||||
((tag . "Visual")
|
||||
(doc . "\
|
||||
Gnus can be made colorful and fun or grey and dull as you wish.")
|
||||
(type . group)
|
||||
(data
|
||||
((tag . "Visual")
|
||||
(doc . "Enable visual features.
|
||||
If `visual' is disabled, there will be no menus and few faces. Most of
|
||||
the visual customization options below will be ignored. GNUS will use
|
||||
the visual customization options below will be ignored. Gnus will use
|
||||
less space and be faster as a result.")
|
||||
(default . t)
|
||||
(name . gnus-visual)
|
||||
(type . toggle))
|
||||
((tag . "WWW Browser")
|
||||
(doc . "\
|
||||
(default .
|
||||
(summary-highlight group-highlight
|
||||
article-highlight
|
||||
mouse-face
|
||||
summary-menu group-menu article-menu
|
||||
tree-highlight menu highlight
|
||||
browse-menu server-menu
|
||||
page-marker tree-menu binary-menu pick-menu
|
||||
grouplens-menu))
|
||||
(name . gnus-visual)
|
||||
(type . sexp))
|
||||
((tag . "WWW Browser")
|
||||
(doc . "\
|
||||
WWW Browser to call when clicking on an URL button in the article buffer.
|
||||
|
||||
You can choose between one of the predefined browsers, or `Other'.")
|
||||
(name . gnus-button-url)
|
||||
(calculate . (cond ((boundp 'browse-url-browser-function)
|
||||
browse-url-browser-function)
|
||||
((fboundp 'w3-fetch)
|
||||
'w3-fetch)
|
||||
((eq window-system 'x)
|
||||
'gnus-netscape-open-url)))
|
||||
(type . choice)
|
||||
(data ((tag . "W3")
|
||||
(type . const)
|
||||
(default . w3-fetch))
|
||||
((tag . "Netscape")
|
||||
(type . const)
|
||||
(default . gnus-netscape-open-url))
|
||||
((prompt . "Other")
|
||||
(doc . "\
|
||||
(name . browse-url-browser-function)
|
||||
(calculate . (cond ((boundp 'browse-url-browser-function)
|
||||
browse-url-browser-function)
|
||||
((fboundp 'w3-fetch)
|
||||
'w3-fetch)
|
||||
((eq window-system 'x)
|
||||
'gnus-netscape-open-url)))
|
||||
(type . choice)
|
||||
(data
|
||||
((tag . "W3")
|
||||
(type . const)
|
||||
(default . w3-fetch))
|
||||
((tag . "Netscape")
|
||||
(type . const)
|
||||
(default . browse-url-netscape))
|
||||
((prompt . "Other")
|
||||
(doc . "\
|
||||
You must specify the name of a Lisp function here. The lisp function
|
||||
should open a WWW browser when called with an URL (a string).
|
||||
")
|
||||
(default . __uninitialized__)
|
||||
(type . symbol))))
|
||||
((tag . "Mouse Face")
|
||||
(doc . "\
|
||||
(default . __uninitialized__)
|
||||
(type . symbol))))
|
||||
((tag . "Mouse Face")
|
||||
(doc . "\
|
||||
Face used for group or summary buffer mouse highlighting.
|
||||
The line beneath the mouse pointer will be highlighted with this
|
||||
face.")
|
||||
(name . gnus-mouse-face)
|
||||
(calculate . (if (boundp 'gnus-mouse-face)
|
||||
gnus-mouse-face
|
||||
'highlight))
|
||||
(type . face))
|
||||
((tag . "Article Display")
|
||||
(doc . "Controls how the article buffer will look.
|
||||
(name . gnus-mouse-face)
|
||||
(calculate . (if (gnus-visual-p 'mouse-face 'highlight)
|
||||
(if (boundp 'gnus-mouse-face)
|
||||
gnus-mouse-face
|
||||
'highlight)
|
||||
'default))
|
||||
(type . face))
|
||||
((tag . "Article Display")
|
||||
(doc . "Controls how the article buffer will look.
|
||||
|
||||
The list below contains various filters you can use to change the look
|
||||
of the article. If you leave the list empty, the article will appear
|
||||
exactly as it is stored on the disk. The list entries will hide or
|
||||
highlight various parts of the article, making it easier to find the
|
||||
information you want.")
|
||||
(name . gnus-article-display-hook)
|
||||
(type . list)
|
||||
(default . (gnus-article-hide-headers-if-wanted
|
||||
gnus-article-treat-overstrike
|
||||
gnus-article-maybe-highlight))
|
||||
(data ((type . repeat)
|
||||
(header . nil)
|
||||
(data (tag . "Filter")
|
||||
(type . choice)
|
||||
(data ((tag . "Treat Overstrike")
|
||||
(doc . "\
|
||||
If you leave the list empty, the article will appear exactly as it is
|
||||
stored on the disk. The list entries will hide or highlight various
|
||||
parts of the article, making it easier to find the information you
|
||||
want.")
|
||||
(name . gnus-article-display-hook)
|
||||
(type . list)
|
||||
(calculate
|
||||
. (if (and (string-match "xemacs" emacs-version)
|
||||
(featurep 'xface))
|
||||
'(gnus-article-hide-headers-if-wanted
|
||||
gnus-article-hide-boring-headers
|
||||
gnus-article-treat-overstrike
|
||||
gnus-article-maybe-highlight
|
||||
gnus-article-display-x-face)
|
||||
'(gnus-article-hide-headers-if-wanted
|
||||
gnus-article-hide-boring-headers
|
||||
gnus-article-treat-overstrike
|
||||
gnus-article-maybe-highlight)))
|
||||
(data
|
||||
((type . repeat)
|
||||
(header . nil)
|
||||
(data
|
||||
(tag . "Filter")
|
||||
(type . choice)
|
||||
(data
|
||||
((tag . "Treat Overstrike")
|
||||
(doc . "\
|
||||
Convert use of overstrike into bold and underline.
|
||||
|
||||
Two identical letters separated by a backspace are displayed as a
|
||||
@ -129,178 +153,178 @@ technique was developed for old line printers (think about it), and is
|
||||
still in use on some newsgroups, in particular the ClariNet
|
||||
hierarchy.
|
||||
")
|
||||
(type . const)
|
||||
(default .
|
||||
gnus-article-treat-overstrike))
|
||||
((tag . "Word Wrap")
|
||||
(doc . "\
|
||||
(type . const)
|
||||
(default .
|
||||
gnus-article-treat-overstrike))
|
||||
((tag . "Word Wrap")
|
||||
(doc . "\
|
||||
Format too long lines.
|
||||
")
|
||||
(type . const)
|
||||
(default . gnus-article-word-wrap))
|
||||
((tag . "Remove CR")
|
||||
(doc . "\
|
||||
(type . const)
|
||||
(default . gnus-article-word-wrap))
|
||||
((tag . "Remove CR")
|
||||
(doc . "\
|
||||
Remove carriage returns from an article.
|
||||
")
|
||||
(type . const)
|
||||
(default . gnus-article-remove-cr))
|
||||
((tag . "Display X-Face")
|
||||
(doc . "\
|
||||
(type . const)
|
||||
(default . gnus-article-remove-cr))
|
||||
((tag . "Display X-Face")
|
||||
(doc . "\
|
||||
Look for an X-Face header and display it if present.
|
||||
|
||||
See also `X Face Command' for a definition of the external command
|
||||
used for decoding and displaying the face.
|
||||
")
|
||||
(type . const)
|
||||
(default . gnus-article-display-x-face))
|
||||
((tag . "Unquote Printable")
|
||||
(doc . "\
|
||||
(type . const)
|
||||
(default . gnus-article-display-x-face))
|
||||
((tag . "Unquote Printable")
|
||||
(doc . "\
|
||||
Transform MIME quoted printable into 8-bit characters.
|
||||
|
||||
Quoted printable is often seen by strings like `=EF' where you would
|
||||
expect a non-English letter.
|
||||
")
|
||||
(type . const)
|
||||
(default .
|
||||
gnus-article-de-quoted-unreadable))
|
||||
((tag . "Universal Time")
|
||||
(doc . "\
|
||||
(type . const)
|
||||
(default .
|
||||
gnus-article-de-quoted-unreadable))
|
||||
((tag . "Universal Time")
|
||||
(doc . "\
|
||||
Convert date header to universal time.
|
||||
")
|
||||
(type . const)
|
||||
(default . gnus-article-date-ut))
|
||||
((tag . "Local Time")
|
||||
(doc . "\
|
||||
(type . const)
|
||||
(default . gnus-article-date-ut))
|
||||
((tag . "Local Time")
|
||||
(doc . "\
|
||||
Convert date header to local timezone.
|
||||
")
|
||||
(type . const)
|
||||
(default . gnus-article-date-local))
|
||||
((tag . "Lapsed Time")
|
||||
(doc . "\
|
||||
(type . const)
|
||||
(default . gnus-article-date-local))
|
||||
((tag . "Lapsed Time")
|
||||
(doc . "\
|
||||
Replace date header with a header showing the articles age.
|
||||
")
|
||||
(type . const)
|
||||
(default . gnus-article-date-lapsed))
|
||||
((tag . "Highlight")
|
||||
(doc . "\
|
||||
(type . const)
|
||||
(default . gnus-article-date-lapsed))
|
||||
((tag . "Highlight")
|
||||
(doc . "\
|
||||
Highlight headers, citations, signature, and buttons.
|
||||
")
|
||||
(type . const)
|
||||
(default . gnus-article-highlight))
|
||||
((tag . "Maybe Highlight")
|
||||
(doc . "\
|
||||
(type . const)
|
||||
(default . gnus-article-highlight))
|
||||
((tag . "Maybe Highlight")
|
||||
(doc . "\
|
||||
Highlight headers, signature, and buttons if `Visual' is turned on.
|
||||
")
|
||||
(type . const)
|
||||
(default .
|
||||
gnus-article-maybe-highlight))
|
||||
((tag . "Highlight Some")
|
||||
(doc . "\
|
||||
(type . const)
|
||||
(default .
|
||||
gnus-article-maybe-highlight))
|
||||
((tag . "Highlight Some")
|
||||
(doc . "\
|
||||
Highlight headers, signature, and buttons.
|
||||
")
|
||||
(type . const)
|
||||
(default . gnus-article-highlight-some))
|
||||
((tag . "Highlight Headers")
|
||||
(doc . "\
|
||||
(type . const)
|
||||
(default . gnus-article-highlight-some))
|
||||
((tag . "Highlight Headers")
|
||||
(doc . "\
|
||||
Highlight headers as specified by `Article Header Highlighting'.
|
||||
")
|
||||
(type . const)
|
||||
(default .
|
||||
gnus-article-highlight-headers))
|
||||
((tag . "Highlight Signature")
|
||||
(doc . "\
|
||||
(type . const)
|
||||
(default .
|
||||
gnus-article-highlight-headers))
|
||||
((tag . "Highlight Signature")
|
||||
(doc . "\
|
||||
Highlight the signature as specified by `Article Signature Face'.
|
||||
")
|
||||
(type . const)
|
||||
(default .
|
||||
gnus-article-highlight-signature))
|
||||
((tag . "Citation")
|
||||
(doc . "\
|
||||
(type . const)
|
||||
(default .
|
||||
gnus-article-highlight-signature))
|
||||
((tag . "Citation")
|
||||
(doc . "\
|
||||
Highlight the citations as specified by `Citation Faces'.
|
||||
")
|
||||
(type . const)
|
||||
(default .
|
||||
gnus-article-highlight-citation))
|
||||
((tag . "Hide")
|
||||
(doc . "\
|
||||
(type . const)
|
||||
(default .
|
||||
gnus-article-highlight-citation))
|
||||
((tag . "Hide")
|
||||
(doc . "\
|
||||
Hide unwanted headers, excess citation, and the signature.
|
||||
")
|
||||
(type . const)
|
||||
(default . gnus-article-hide))
|
||||
((tag . "Hide Headers If Wanted")
|
||||
(doc . "\
|
||||
(type . const)
|
||||
(default . gnus-article-hide))
|
||||
((tag . "Hide Headers If Wanted")
|
||||
(doc . "\
|
||||
Hide headers, but allow user to display them with `t' or `v'.
|
||||
")
|
||||
(type . const)
|
||||
(default .
|
||||
gnus-article-hide-headers-if-wanted))
|
||||
((tag . "Hide Headers")
|
||||
(doc . "\
|
||||
(type . const)
|
||||
(default .
|
||||
gnus-article-hide-headers-if-wanted))
|
||||
((tag . "Hide Headers")
|
||||
(doc . "\
|
||||
Hide unwanted headers and possibly sort them as well.
|
||||
Most likely you want to use `Hide Headers If Wanted' instead.
|
||||
")
|
||||
(type . const)
|
||||
(default . gnus-article-hide-headers))
|
||||
((tag . "Hide Signature")
|
||||
(doc . "\
|
||||
(type . const)
|
||||
(default . gnus-article-hide-headers))
|
||||
((tag . "Hide Signature")
|
||||
(doc . "\
|
||||
Hide the signature.
|
||||
")
|
||||
(type . const)
|
||||
(default . gnus-article-hide-signature))
|
||||
((tag . "Hide Excess Citations")
|
||||
(doc . "\
|
||||
(type . const)
|
||||
(default . gnus-article-hide-signature))
|
||||
((tag . "Hide Excess Citations")
|
||||
(doc . "\
|
||||
Hide excess citation.
|
||||
|
||||
Excess is defined by `Citation Hide Percentage' and `Citation Hide Absolute'.
|
||||
")
|
||||
(type . const)
|
||||
(default .
|
||||
gnus-article-hide-citation-maybe))
|
||||
((tag . "Hide Citations")
|
||||
(doc . "\
|
||||
(type . const)
|
||||
(default .
|
||||
gnus-article-hide-citation-maybe))
|
||||
((tag . "Hide Citations")
|
||||
(doc . "\
|
||||
Hide all cited text.
|
||||
")
|
||||
(type . const)
|
||||
(default . gnus-article-hide-citation))
|
||||
((tag . "Add Buttons")
|
||||
(doc . "\
|
||||
(type . const)
|
||||
(default . gnus-article-hide-citation))
|
||||
((tag . "Add Buttons")
|
||||
(doc . "\
|
||||
Make URL's into clickable buttons.
|
||||
")
|
||||
(type . const)
|
||||
(default . gnus-article-add-buttons))
|
||||
((prompt . "Other")
|
||||
(doc . "\
|
||||
(type . const)
|
||||
(default . gnus-article-add-buttons))
|
||||
((prompt . "Other")
|
||||
(doc . "\
|
||||
Name of Lisp function to call.
|
||||
|
||||
Push the `Filter' button to select one of the predefined filters.
|
||||
")
|
||||
(type . symbol)))))))
|
||||
((tag . "Article Button Face")
|
||||
(doc . "\
|
||||
(type . symbol)))))))
|
||||
((tag . "Article Button Face")
|
||||
(doc . "\
|
||||
Face used for highlighting buttons in the article buffer.
|
||||
|
||||
An article button is a piece of text that you can activate by pressing
|
||||
`RET' or `mouse-2' above it.")
|
||||
(name . gnus-article-button-face)
|
||||
(default . bold)
|
||||
(type . face))
|
||||
((tag . "Article Mouse Face")
|
||||
(doc . "\
|
||||
(name . gnus-article-button-face)
|
||||
(default . bold)
|
||||
(type . face))
|
||||
((tag . "Article Mouse Face")
|
||||
(doc . "\
|
||||
Face used for mouse highlighting in the article buffer.
|
||||
|
||||
Article buttons will be displayed in this face when the cursor is
|
||||
above them.")
|
||||
(name . gnus-article-mouse-face)
|
||||
(default . highlight)
|
||||
(type . face))
|
||||
((tag . "Article Signature Face")
|
||||
(doc . "\
|
||||
(name . gnus-article-mouse-face)
|
||||
(default . highlight)
|
||||
(type . face))
|
||||
((tag . "Article Signature Face")
|
||||
(doc . "\
|
||||
Face used for highlighting a signature in the article buffer.")
|
||||
(name . gnus-signature-face)
|
||||
(default . italic)
|
||||
(type . face))
|
||||
((tag . "Article Header Highlighting")
|
||||
(doc . "\
|
||||
(name . gnus-signature-face)
|
||||
(default . italic)
|
||||
(type . face))
|
||||
((tag . "Article Header Highlighting")
|
||||
(doc . "\
|
||||
Controls highlighting of article header.
|
||||
|
||||
Below is a list of article header names, and the faces used for
|
||||
@ -322,110 +346,106 @@ If you only want to change the display of the name part for a specific
|
||||
header, specify `None' in the `Content' field. Similarly, specify
|
||||
`None' in the `Name' field if you only want to leave the name part
|
||||
alone.")
|
||||
(name . gnus-header-face-alist)
|
||||
(type . list)
|
||||
(calculate . (cond ((not (eq gnus-display-type 'color))
|
||||
'(("" bold italic)))
|
||||
((eq gnus-background-mode 'dark)
|
||||
(list (list "From" nil
|
||||
(custom-face-lookup
|
||||
"dark blue" nil nil t t
|
||||
'custom:asis))
|
||||
(list "Subject" nil
|
||||
(custom-face-lookup
|
||||
"pink" nil nil t t 'custom:asis))
|
||||
(list "Newsgroups:.*," nil
|
||||
(custom-face-lookup
|
||||
"yellow" nil nil t t 'custom:asis))
|
||||
(list ""
|
||||
(custom-face-lookup
|
||||
"cyan" nil nil t 'custom:asis 'custom:asis)
|
||||
(custom-face-lookup
|
||||
"forestgreen"
|
||||
nil nil 'custom:asis t 'custom:asis))))
|
||||
(t
|
||||
(list (list "From" nil
|
||||
(custom-face-lookup
|
||||
"RoyalBlue"
|
||||
nil nil t t 'custom:asis))
|
||||
(list "Subject" nil
|
||||
(custom-face-lookup
|
||||
"firebrick"
|
||||
nil nil t t 'custom:asis))
|
||||
(list "Newsgroups:.*," nil
|
||||
(custom-face-lookup
|
||||
"indianred" nil nil t t 'custom:asis))
|
||||
(list ""
|
||||
(custom-face-lookup
|
||||
"DarkGreen"
|
||||
nil nil t 'custom:asis 'custom:asis)
|
||||
(custom-face-lookup
|
||||
"DarkGreen"
|
||||
nil nil nil t 'custom:asis))))))
|
||||
(data ((type . repeat)
|
||||
(header . nil)
|
||||
(data (type . list)
|
||||
(compact . t)
|
||||
(data ((type . string)
|
||||
(prompt . "Header")
|
||||
(tag . "Header "))
|
||||
"\n "
|
||||
((type . face)
|
||||
(prompt . "Name")
|
||||
(tag . "Name "))
|
||||
"\n "
|
||||
((type . face)
|
||||
(tag . "Content"))
|
||||
"\n")))))
|
||||
((tag . "Attribution Face")
|
||||
(doc . "\
|
||||
(name . gnus-header-face-alist)
|
||||
(type . list)
|
||||
(calculate
|
||||
. (cond
|
||||
((not (eq gnus-display-type 'color))
|
||||
'(("" bold italic)))
|
||||
((eq gnus-background-mode 'dark)
|
||||
(list
|
||||
(list "From" nil
|
||||
(custom-face-lookup "light blue" nil nil t t nil))
|
||||
(list "Subject" nil
|
||||
(custom-face-lookup "pink" nil nil t t nil))
|
||||
(list "Newsgroups:.*," nil
|
||||
(custom-face-lookup "yellow" nil nil t t nil))
|
||||
(list
|
||||
""
|
||||
(custom-face-lookup "cyan" nil nil t nil nil)
|
||||
(custom-face-lookup "forestgreen" nil nil nil t
|
||||
nil))))
|
||||
(t
|
||||
(list
|
||||
(list "From" nil
|
||||
(custom-face-lookup "MidnightBlue" nil nil t t nil))
|
||||
(list "Subject" nil
|
||||
(custom-face-lookup "firebrick" nil nil t t nil))
|
||||
(list "Newsgroups:.*," nil
|
||||
(custom-face-lookup "indianred" nil nil t t nil))
|
||||
(list ""
|
||||
(custom-face-lookup
|
||||
"DarkGreen" nil nil t nil nil)
|
||||
(custom-face-lookup "DarkGreen" nil nil
|
||||
nil t nil))))))
|
||||
(data
|
||||
((type . repeat)
|
||||
(header . nil)
|
||||
(data
|
||||
(type . list)
|
||||
(compact . t)
|
||||
(data
|
||||
((type . string)
|
||||
(prompt . "Header")
|
||||
(tag . "Header "))
|
||||
"\n "
|
||||
((type . face)
|
||||
(prompt . "Name")
|
||||
(tag . "Name "))
|
||||
"\n "
|
||||
((type . face)
|
||||
(tag . "Content"))
|
||||
"\n")))))
|
||||
((tag . "Attribution Face")
|
||||
(doc . "\
|
||||
Face used for attribution lines.
|
||||
It is merged with the face for the cited text belonging to the attribution.")
|
||||
(name . gnus-cite-attribution-face)
|
||||
(default . underline)
|
||||
(type . face))
|
||||
((tag . "Citation Faces")
|
||||
(doc . "\
|
||||
(name . gnus-cite-attribution-face)
|
||||
(default . underline)
|
||||
(type . face))
|
||||
((tag . "Citation Faces")
|
||||
(doc . "\
|
||||
List of faces used for highlighting citations.
|
||||
|
||||
When there are citations from multiple articles in the same message,
|
||||
Gnus will try to give each citation from each article its own face.
|
||||
This should make it easier to see who wrote what.")
|
||||
(name . gnus-cite-face-list)
|
||||
(import . gnus-custom-import-cite-face-list)
|
||||
(type . list)
|
||||
(calculate . (cond ((not (eq gnus-display-type 'color))
|
||||
'(italic))
|
||||
((eq gnus-background-mode 'dark)
|
||||
(mapcar 'gnus-make-face
|
||||
gnus-face-light-name-list))
|
||||
(t
|
||||
(mapcar 'gnus-make-face
|
||||
gnus-face-dark-name-list))))
|
||||
(data ((type . repeat)
|
||||
(header . nil)
|
||||
(data (type . face)
|
||||
(tag . "Face")))))
|
||||
((tag . "Citation Hide Percentage")
|
||||
(doc . "\
|
||||
(name . gnus-cite-face-list)
|
||||
(import . gnus-custom-import-cite-face-list)
|
||||
(type . list)
|
||||
(calculate . (cond ((not (eq gnus-display-type 'color))
|
||||
'(italic))
|
||||
((eq gnus-background-mode 'dark)
|
||||
(mapcar 'gnus-make-face
|
||||
gnus-face-light-name-list))
|
||||
(t
|
||||
(mapcar 'gnus-make-face
|
||||
gnus-face-dark-name-list))))
|
||||
(data
|
||||
((type . repeat)
|
||||
(header . nil)
|
||||
(data (type . face)
|
||||
(tag . "Face")))))
|
||||
((tag . "Citation Hide Percentage")
|
||||
(doc . "\
|
||||
Only hide excess citation if above this percentage of the body.")
|
||||
(name . gnus-cite-hide-percentage)
|
||||
(default . 50)
|
||||
(type . integer))
|
||||
((tag . "Citation Hide Absolute")
|
||||
(doc . "\
|
||||
(name . gnus-cite-hide-percentage)
|
||||
(default . 50)
|
||||
(type . integer))
|
||||
((tag . "Citation Hide Absolute")
|
||||
(doc . "\
|
||||
Only hide excess citation if above this number of lines in the body.")
|
||||
(name . gnus-cite-hide-absolute)
|
||||
(default . 10)
|
||||
(type . integer))
|
||||
((tag . "Summary Selected Face")
|
||||
(doc . "\
|
||||
(name . gnus-cite-hide-absolute)
|
||||
(default . 10)
|
||||
(type . integer))
|
||||
((tag . "Summary Selected Face")
|
||||
(doc . "\
|
||||
Face used for highlighting the current article in the summary buffer.")
|
||||
(name . gnus-summary-selected-face)
|
||||
(default . underline)
|
||||
(type . face))
|
||||
((tag . "Summary Line Highlighting")
|
||||
(doc . "\
|
||||
(name . gnus-summary-selected-face)
|
||||
(default . underline)
|
||||
(type . face))
|
||||
((tag . "Summary Line Highlighting")
|
||||
(doc . "\
|
||||
Controls the highlighting of summary buffer lines.
|
||||
|
||||
Below is a list of `Form'/`Face' pairs. When deciding how a a
|
||||
@ -443,87 +463,199 @@ score: The article's score
|
||||
default: The default article score.
|
||||
below: The score below which articles are automatically marked as read.
|
||||
mark: The article's mark.")
|
||||
(name . gnus-summary-highlight)
|
||||
(type . list)
|
||||
(calculate . (cond ((not (eq gnus-display-type 'color))
|
||||
'(((> score default) . bold)
|
||||
((< score default) . italic)))
|
||||
((eq gnus-background-mode 'dark)
|
||||
(list (cons '(= mark gnus-canceled-mark)
|
||||
(custom-face-lookup "yellow" "black" nil 'custom:asis 'custom:asis 'custom:asis))
|
||||
(cons '(and (> score default)
|
||||
(or (= mark gnus-dormant-mark)
|
||||
(= mark gnus-ticked-mark)))
|
||||
(custom-face-lookup "pink" nil nil t 'custom:asis 'custom:asis))
|
||||
(cons '(and (< score default)
|
||||
(or (= mark gnus-dormant-mark)
|
||||
(= mark gnus-ticked-mark)))
|
||||
(custom-face-lookup "pink" nil nil 'custom:asis t 'custom:asis))
|
||||
(cons '(or (= mark gnus-dormant-mark)
|
||||
(= mark gnus-ticked-mark))
|
||||
(custom-face-lookup "pink" nil nil 'custom:asis 'custom:asis 'custom:asis))
|
||||
(name . gnus-summary-highlight)
|
||||
(type . list)
|
||||
(calculate
|
||||
. (cond
|
||||
((not (eq gnus-display-type 'color))
|
||||
'(((> score default) . bold)
|
||||
((< score default) . italic)))
|
||||
((eq gnus-background-mode 'dark)
|
||||
(list
|
||||
(cons
|
||||
'(= mark gnus-canceled-mark)
|
||||
(custom-face-lookup "yellow" "black" nil
|
||||
nil nil nil))
|
||||
(cons '(and (> score default)
|
||||
(or (= mark gnus-dormant-mark)
|
||||
(= mark gnus-ticked-mark)))
|
||||
(custom-face-lookup
|
||||
"pink" nil nil t nil nil))
|
||||
(cons '(and (< score default)
|
||||
(or (= mark gnus-dormant-mark)
|
||||
(= mark gnus-ticked-mark)))
|
||||
(custom-face-lookup "pink" nil nil
|
||||
nil t nil))
|
||||
(cons '(or (= mark gnus-dormant-mark)
|
||||
(= mark gnus-ticked-mark))
|
||||
(custom-face-lookup
|
||||
"pink" nil nil nil nil nil))
|
||||
|
||||
(cons '(and (> score default) (= mark gnus-ancient-mark))
|
||||
(custom-face-lookup "dark blue" nil nil t 'custom:asis 'custom:asis))
|
||||
(cons '(and (< score default) (= mark gnus-ancient-mark))
|
||||
(custom-face-lookup "SkyBlue" nil nil 'custom:asis t 'custom:asis))
|
||||
(cons '(= mark gnus-ancient-mark)
|
||||
(custom-face-lookup "SkyBlue" nil nil 'custom:asis 'custom:asis 'custom:asis))
|
||||
(cons
|
||||
'(and (> score default) (= mark gnus-ancient-mark))
|
||||
(custom-face-lookup "medium blue" nil nil t
|
||||
nil nil))
|
||||
(cons
|
||||
'(and (< score default) (= mark gnus-ancient-mark))
|
||||
(custom-face-lookup "SkyBlue" nil nil
|
||||
nil t nil))
|
||||
(cons
|
||||
'(= mark gnus-ancient-mark)
|
||||
(custom-face-lookup "SkyBlue" nil nil
|
||||
nil nil nil))
|
||||
(cons '(and (> score default) (= mark gnus-unread-mark))
|
||||
(custom-face-lookup "white" nil nil t
|
||||
nil nil))
|
||||
(cons '(and (< score default) (= mark gnus-unread-mark))
|
||||
(custom-face-lookup "white" nil nil
|
||||
nil t nil))
|
||||
(cons '(= mark gnus-unread-mark)
|
||||
(custom-face-lookup
|
||||
"white" nil nil nil nil nil))
|
||||
|
||||
(cons '(and (> score default) (= mark gnus-unread-mark))
|
||||
(custom-face-lookup "white" nil nil t 'custom:asis 'custom:asis))
|
||||
(cons '(and (< score default) (= mark gnus-unread-mark))
|
||||
(custom-face-lookup "white" nil nil 'custom:asis t 'custom:asis))
|
||||
(cons '(= mark gnus-unread-mark)
|
||||
(custom-face-lookup "white" nil nil 'custom:asis 'custom:asis 'custom:asis))
|
||||
(cons '(> score default) 'bold)
|
||||
(cons '(< score default) 'italic)))
|
||||
(t
|
||||
(list
|
||||
(cons
|
||||
'(= mark gnus-canceled-mark)
|
||||
(custom-face-lookup
|
||||
"yellow" "black" nil nil nil nil))
|
||||
(cons '(and (> score default)
|
||||
(or (= mark gnus-dormant-mark)
|
||||
(= mark gnus-ticked-mark)))
|
||||
(custom-face-lookup "firebrick" nil nil
|
||||
t nil nil))
|
||||
(cons '(and (< score default)
|
||||
(or (= mark gnus-dormant-mark)
|
||||
(= mark gnus-ticked-mark)))
|
||||
(custom-face-lookup "firebrick" nil nil
|
||||
nil t nil))
|
||||
(cons
|
||||
'(or (= mark gnus-dormant-mark)
|
||||
(= mark gnus-ticked-mark))
|
||||
(custom-face-lookup
|
||||
"firebrick" nil nil nil nil nil))
|
||||
|
||||
(cons '(> score default) 'bold)
|
||||
(cons '(< score default) 'italic)))
|
||||
(t
|
||||
(list (cons '(= mark gnus-canceled-mark)
|
||||
(custom-face-lookup "yellow" "black" nil 'custom:asis 'custom:asis 'custom:asis))
|
||||
(cons '(and (> score default)
|
||||
(or (= mark gnus-dormant-mark)
|
||||
(= mark gnus-ticked-mark)))
|
||||
(custom-face-lookup "firebrick" nil nil t 'custom:asis 'custom:asis))
|
||||
(cons '(and (< score default)
|
||||
(or (= mark gnus-dormant-mark)
|
||||
(= mark gnus-ticked-mark)))
|
||||
(custom-face-lookup "firebrick" nil nil 'custom:asis t 'custom:asis))
|
||||
(cons '(or (= mark gnus-dormant-mark)
|
||||
(= mark gnus-ticked-mark))
|
||||
(custom-face-lookup "firebrick" nil nil 'custom:asis 'custom:asis 'custom:asis))
|
||||
(cons '(and (> score default) (= mark gnus-ancient-mark))
|
||||
(custom-face-lookup "RoyalBlue" nil nil
|
||||
t nil nil))
|
||||
(cons '(and (< score default) (= mark gnus-ancient-mark))
|
||||
(custom-face-lookup "RoyalBlue" nil nil
|
||||
nil t nil))
|
||||
(cons
|
||||
'(= mark gnus-ancient-mark)
|
||||
(custom-face-lookup
|
||||
"RoyalBlue" nil nil nil nil nil))
|
||||
|
||||
(cons '(and (> score default) (= mark gnus-ancient-mark))
|
||||
(custom-face-lookup "RoyalBlue" nil nil t 'custom:asis 'custom:asis))
|
||||
(cons '(and (< score default) (= mark gnus-ancient-mark))
|
||||
(custom-face-lookup "RoyalBlue" nil nil 'custom:asis t 'custom:asis))
|
||||
(cons '(= mark gnus-ancient-mark)
|
||||
(custom-face-lookup "RoyalBlue" nil nil 'custom:asis 'custom:asis 'custom:asis))
|
||||
(cons '(and (> score default) (/= mark gnus-unread-mark))
|
||||
(custom-face-lookup "DarkGreen" nil nil
|
||||
t nil nil))
|
||||
(cons '(and (< score default) (/= mark gnus-unread-mark))
|
||||
(custom-face-lookup "DarkGreen" nil nil
|
||||
nil t nil))
|
||||
(cons
|
||||
'(/= mark gnus-unread-mark)
|
||||
(custom-face-lookup "DarkGreen" nil nil
|
||||
nil nil nil))
|
||||
|
||||
(cons '(and (> score default) (/= mark gnus-unread-mark))
|
||||
(custom-face-lookup "DarkGreen" nil nil t 'custom:asis 'custom:asis))
|
||||
(cons '(and (< score default) (/= mark gnus-unread-mark))
|
||||
(custom-face-lookup "DarkGreen" nil nil 'custom:asis t 'custom:asis))
|
||||
(cons '(/= mark gnus-unread-mark)
|
||||
(custom-face-lookup "DarkGreen" nil nil 'custom:asis 'custom:asis 'custom:asis))
|
||||
(cons '(> score default) 'bold)
|
||||
(cons '(< score default) 'italic)))))
|
||||
(data ((type . repeat)
|
||||
(header . nil)
|
||||
(data (type . pair)
|
||||
(compact . t)
|
||||
(data ((type . sexp)
|
||||
(width . 60)
|
||||
(tag . "Form"))
|
||||
"\n "
|
||||
((type . face)
|
||||
(tag . "Face"))
|
||||
"\n")))))
|
||||
;; Do not define `gnus-button-alist' before we have
|
||||
;; some `complexity' attribute so we can hide it from
|
||||
;; beginners.
|
||||
)))))
|
||||
(cons '(> score default) 'bold)
|
||||
(cons '(< score default) 'italic)))))
|
||||
(data
|
||||
((type . repeat)
|
||||
(header . nil)
|
||||
(data (type . pair)
|
||||
(compact . t)
|
||||
(data ((type . sexp)
|
||||
(width . 60)
|
||||
(tag . "Form"))
|
||||
"\n "
|
||||
((type . face)
|
||||
(tag . "Face"))
|
||||
"\n")))))
|
||||
|
||||
((tag . "Group Line Highlighting")
|
||||
(doc . "\
|
||||
Controls the highlighting of group buffer lines.
|
||||
|
||||
Below is a list of `Form'/`Face' pairs. When deciding how a a
|
||||
particular group line should be displayed, each form is
|
||||
evaluated. The content of the face field after the first true form is
|
||||
used. You can change how those group lines are displayed by
|
||||
editing the face field.
|
||||
|
||||
It is also possible to change and add form fields, but currently that
|
||||
requires an understanding of Lisp expressions. Hopefully this will
|
||||
change in a future release. For now, you can use the following
|
||||
variables in the Lisp expression:
|
||||
|
||||
group: The name of the group.
|
||||
unread: The number of unread articles in the group.
|
||||
method: The select method used.
|
||||
mailp: Whether it's a mail group or not.
|
||||
level: The level of the group.
|
||||
score: The score of the group.
|
||||
ticked: The number of ticked articles.")
|
||||
(name . gnus-group-highlight)
|
||||
(type . list)
|
||||
(calculate
|
||||
. (cond
|
||||
((not (eq gnus-display-type 'color))
|
||||
'((mailp . bold)
|
||||
((= unread 0) . italic)))
|
||||
((eq gnus-background-mode 'dark)
|
||||
`(((and (not mailp) (eq level 1)) .
|
||||
,(custom-face-lookup "PaleTurquoise" nil nil t))
|
||||
((and (not mailp) (eq level 2)) .
|
||||
,(custom-face-lookup "turquoise" nil nil t))
|
||||
((and (not mailp) (eq level 3)) .
|
||||
,(custom-face-lookup "MediumTurquoise" nil nil t))
|
||||
((and (not mailp) (>= level 4)) .
|
||||
,(custom-face-lookup "DarkTurquoise" nil nil t))
|
||||
((and mailp (eq level 1)) .
|
||||
,(custom-face-lookup "aquamarine1" nil nil t))
|
||||
((and mailp (eq level 2)) .
|
||||
,(custom-face-lookup "aquamarine2" nil nil t))
|
||||
((and mailp (eq level 3)) .
|
||||
,(custom-face-lookup "aquamarine3" nil nil t))
|
||||
((and mailp (>= level 4)) .
|
||||
,(custom-face-lookup "aquamarine4" nil nil t))
|
||||
))
|
||||
(t
|
||||
`(((and (not mailp) (<= level 3)) .
|
||||
,(custom-face-lookup "ForestGreen" nil nil t))
|
||||
((and (not mailp) (eq level 4)) .
|
||||
,(custom-face-lookup "DarkGreen" nil nil t))
|
||||
((and (not mailp) (eq level 5)) .
|
||||
,(custom-face-lookup "CadetBlue4" nil nil t))
|
||||
((and mailp (eq level 1)) .
|
||||
,(custom-face-lookup "DeepPink3" nil nil t))
|
||||
((and mailp (eq level 2)) .
|
||||
,(custom-face-lookup "HotPink3" nil nil t))
|
||||
((and mailp (eq level 3)) .
|
||||
,(custom-face-lookup "dark magenta" nil nil t))
|
||||
((and mailp (eq level 4)) .
|
||||
,(custom-face-lookup "DeepPink4" nil nil t))
|
||||
((and mailp (> level 4)) .
|
||||
,(custom-face-lookup "DarkOrchid4" nil nil t))
|
||||
))))
|
||||
(data
|
||||
((type . repeat)
|
||||
(header . nil)
|
||||
(data (type . pair)
|
||||
(compact . t)
|
||||
(data ((type . sexp)
|
||||
(width . 60)
|
||||
(tag . "Form"))
|
||||
"\n "
|
||||
((type . face)
|
||||
(tag . "Face"))
|
||||
"\n")))))
|
||||
|
||||
;; Do not define `gnus-button-alist' before we have
|
||||
;; some `complexity' attribute so we can hide it from
|
||||
;; beginners.
|
||||
)))))
|
||||
|
||||
(defun gnus-custom-import-cite-face-list (custom alist)
|
||||
;; Backward compatible grokking of light and dark.
|
||||
@ -533,16 +665,6 @@ mark: The article's mark.")
|
||||
(setq alist (mapcar 'gnus-make-face gnus-face-dark-name-list))))
|
||||
(funcall (custom-super custom 'import) custom alist))
|
||||
|
||||
;(defun gnus-custom-import-swap-alist (custom alist)
|
||||
; ;; Swap key and value in CUSTOM ALIST.
|
||||
; (let ((swap (mapcar (lambda (e) (cons (cdr e) (car e))) alist)))
|
||||
; (funcall (custom-super custom 'import) custom swap)))
|
||||
|
||||
;(defun gnus-custom-export-swap-alist (custom alist)
|
||||
; ;; Swap key and value in CUSTOM ALIST.
|
||||
; (let ((swap (mapcar (lambda (e) (cons (cdr e) (car e))) alist)))
|
||||
; (funcall (custom-super custom 'export) custom swap)))
|
||||
|
||||
(provide 'gnus-cus)
|
||||
|
||||
;;; gnus-cus.el ends here
|
||||
|
@ -1,36 +1,19 @@
|
||||
;;; gnus-edit.el --- Gnus SCORE file editing
|
||||
|
||||
;; Copyright (C) 1995 Free Software Foundation, Inc.
|
||||
|
||||
;; Copyright (C) 1995,96 Free Software Foundation, Inc.
|
||||
;;
|
||||
;; Author: Per Abrahamsen <abraham@iesd.auc.dk>
|
||||
;; Keywords: news, help
|
||||
;; Version: 0.2
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation; either version 2, or (at your option)
|
||||
;; any later version.
|
||||
|
||||
;; GNU Emacs is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with GNU Emacs; see the file COPYING. If not, write to the
|
||||
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
;; Boston, MA 02111-1307, USA.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;;
|
||||
;; Type `M-x gnus-score-customize RET' to invoke.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'custom)
|
||||
(require 'gnus-score)
|
||||
(eval-when-compile (require 'cl))
|
||||
|
||||
(defconst gnus-score-custom-data
|
||||
'((tag . "Score")
|
||||
@ -64,7 +47,7 @@ the score file and the value of the global variable
|
||||
(query . gnus-score-custom-save))
|
||||
((name . file)
|
||||
(tag . "File")
|
||||
(directory . "~/News/")
|
||||
(directory . gnus-kill-files-directory)
|
||||
(default-file . "SCORE")
|
||||
(type . file))))
|
||||
((name . files)
|
||||
@ -79,7 +62,7 @@ delete a score file from the list.")
|
||||
(data ((type . repeat)
|
||||
(header . nil)
|
||||
(data (type . file)
|
||||
(directory . "~/News/")))))
|
||||
(directory . gnus-kill-files-directory)))))
|
||||
((name . exclude-files)
|
||||
(tag . "Exclude Files")
|
||||
(doc . "\
|
||||
@ -94,7 +77,7 @@ delete a score file from the list.")
|
||||
(data ((type . repeat)
|
||||
(header . nil)
|
||||
(data (type . file)
|
||||
(directory . "~/News/")))))
|
||||
(directory . gnus-kill-files-directory)))))
|
||||
((name . mark)
|
||||
(tag . "Mark")
|
||||
(doc . "\
|
||||
@ -554,7 +537,8 @@ groups matched by the current score file.")
|
||||
'gnus-score-custom-get
|
||||
'gnus-score-custom-save))
|
||||
(make-local-variable 'gnus-score-custom-file)
|
||||
(setq gnus-score-custom-file (expand-file-name "SCORE" "~/News"))
|
||||
(setq gnus-score-custom-file
|
||||
(expand-file-name "SCORE" gnus-kill-files-directory))
|
||||
(make-local-variable 'gnus-score-alist)
|
||||
(setq gnus-score-alist nil)
|
||||
(custom-reset-all))
|
||||
@ -566,9 +550,9 @@ groups matched by the current score file.")
|
||||
(if entry
|
||||
(mapcar 'gnus-score-custom-sanify (cdr entry))
|
||||
(setq entry (assoc name gnus-score-alist))
|
||||
(if (or (memq name '(files exclude-files local))
|
||||
(and (eq name 'adapt)
|
||||
(not (symbolp (car (cdr entry))))))
|
||||
(if (or (memq name '(files exclude-files local))
|
||||
(and (eq name 'adapt)
|
||||
(not (symbolp (car (cdr entry))))))
|
||||
(cdr entry)
|
||||
(car (cdr entry)))))))
|
||||
|
||||
@ -618,11 +602,11 @@ groups matched by the current score file.")
|
||||
(let ((file (custom-name-value 'file)))
|
||||
(if (eq file custom-nil)
|
||||
(error "You must specify a file name"))
|
||||
(setq file (expand-file-name file "~/News"))
|
||||
(setq file (expand-file-name file gnus-kill-files-directory))
|
||||
(gnus-score-load file)
|
||||
(setq gnus-score-custom-file file)
|
||||
(custom-reset-all)
|
||||
(message "Loaded")))
|
||||
(gnus-message 4 "Loaded")))
|
||||
|
||||
(defun gnus-score-custom-save ()
|
||||
(interactive)
|
||||
@ -639,7 +623,7 @@ groups matched by the current score file.")
|
||||
(gnus-make-directory (file-name-directory file))
|
||||
(write-region (point-min) (point-max) file nil 'silent)
|
||||
(kill-buffer (current-buffer))))
|
||||
(message "Saved"))
|
||||
(gnus-message 4 "Saved"))
|
||||
|
||||
(provide 'gnus-edit)
|
||||
|
||||
|
743
lisp/gnus-ems.el
743
lisp/gnus-ems.el
@ -1,6 +1,5 @@
|
||||
;;; gnus-ems.el --- functions for making Gnus work under different Emacsen
|
||||
|
||||
;; Copyright (C) 1995 Free Software Foundation, Inc.
|
||||
;; Copyright (C) 1995,96 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
|
||||
;; Keywords: news
|
||||
@ -24,91 +23,89 @@
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;;; This file has been censored by the Communications Decency Act.
|
||||
;;; That law was passed under the guise of a ban on pornography, but
|
||||
;;; it bans far more than that. This file did not contain pornography,
|
||||
;;; but it was censored nonetheless.
|
||||
|
||||
;;; For information on US government censorship of the Internet, and
|
||||
;;; what you can do to bring back freedom of the press, see the web
|
||||
;;; site http://www.vtw.org/
|
||||
|
||||
;;; Code:
|
||||
|
||||
(eval-when-compile (require 'cl))
|
||||
|
||||
(defvar gnus-mouse-2 [mouse-2])
|
||||
(defvar gnus-group-mode-hook ())
|
||||
(defvar gnus-summary-mode-hook ())
|
||||
(defvar gnus-article-mode-hook ())
|
||||
|
||||
(defalias 'gnus-make-overlay 'make-overlay)
|
||||
(defalias 'gnus-overlay-put 'overlay-put)
|
||||
(defalias 'gnus-move-overlay 'move-overlay)
|
||||
(defalias 'gnus-overlay-end 'overlay-end)
|
||||
(defalias 'gnus-extent-detached-p 'ignore)
|
||||
(defalias 'gnus-extent-start-open 'ignore)
|
||||
(defalias 'gnus-set-text-properties 'set-text-properties)
|
||||
(defalias 'gnus-group-remove-excess-properties 'ignore)
|
||||
(defalias 'gnus-topic-remove-excess-properties 'ignore)
|
||||
(defalias 'gnus-appt-select-lowest-window 'appt-select-lowest-window)
|
||||
(defalias 'gnus-mail-strip-quoted-names 'mail-strip-quoted-names)
|
||||
(defalias 'gnus-make-local-hook 'make-local-hook)
|
||||
(defalias 'gnus-add-hook 'add-hook)
|
||||
(defalias 'gnus-character-to-event 'identity)
|
||||
(defalias 'gnus-add-text-properties 'add-text-properties)
|
||||
(defalias 'gnus-put-text-property 'put-text-property)
|
||||
(defalias 'gnus-mode-line-buffer-identification 'identity)
|
||||
|
||||
|
||||
(eval-and-compile
|
||||
(autoload 'gnus-xmas-define "gnus-xmas")
|
||||
(autoload 'gnus-xmas-redefine "gnus-xmas")
|
||||
(autoload 'appt-select-lowest-window "appt.el"))
|
||||
|
||||
(or (fboundp 'mail-file-babyl-p)
|
||||
(fset 'mail-file-babyl-p 'rmail-file-p))
|
||||
|
||||
;; Don't warn about these undefined variables.
|
||||
;defined in gnus.el
|
||||
(defvar gnus-active-hashtb)
|
||||
(defvar gnus-article-buffer)
|
||||
(defvar gnus-auto-center-summary)
|
||||
(defvar gnus-buffer-list)
|
||||
(defvar gnus-current-headers)
|
||||
(defvar gnus-level-killed)
|
||||
(defvar gnus-level-zombie)
|
||||
(defvar gnus-newsgroup-bookmarks)
|
||||
(defvar gnus-newsgroup-dependencies)
|
||||
(defvar gnus-newsgroup-headers-hashtb-by-number)
|
||||
(defvar gnus-newsgroup-selected-overlay)
|
||||
(defvar gnus-newsrc-hashtb)
|
||||
(defvar gnus-read-mark)
|
||||
(defvar gnus-refer-article-method)
|
||||
(defvar gnus-reffed-article-number)
|
||||
(defvar gnus-unread-mark)
|
||||
(defvar gnus-version)
|
||||
(defvar gnus-view-pseudos)
|
||||
(defvar gnus-view-pseudos-separately)
|
||||
(defvar gnus-visual)
|
||||
(defvar gnus-zombie-list)
|
||||
;defined in gnus-msg.el
|
||||
(defvar gnus-article-copy)
|
||||
(defvar gnus-check-before-posting)
|
||||
;defined in gnus-vis.el
|
||||
(defvar gnus-article-button-face)
|
||||
(defvar gnus-article-mouse-face)
|
||||
(defvar gnus-summary-selected-face)
|
||||
;;; Mule functions.
|
||||
|
||||
(defun gnus-mule-cite-add-face (number prefix face)
|
||||
;; At line NUMBER, ignore PREFIX and add FACE to the rest of the line.
|
||||
(if face
|
||||
(let ((inhibit-point-motion-hooks t)
|
||||
from to)
|
||||
(goto-line number)
|
||||
(if (boundp 'MULE)
|
||||
(forward-char (chars-in-string prefix))
|
||||
(forward-char (length prefix)))
|
||||
(skip-chars-forward " \t")
|
||||
(setq from (point))
|
||||
(end-of-line 1)
|
||||
(skip-chars-backward " \t")
|
||||
(setq to (point))
|
||||
(if (< from to)
|
||||
(gnus-overlay-put (gnus-make-overlay from to) 'face face)))))
|
||||
|
||||
;; We do not byte-compile this file, because error messages are such a
|
||||
;; bore.
|
||||
(defun gnus-mule-max-width-function (el max-width)
|
||||
(` (let* ((val (eval (, el)))
|
||||
(valstr (if (numberp val)
|
||||
(int-to-string val) val)))
|
||||
(if (> (length valstr) (, max-width))
|
||||
(truncate-string valstr (, max-width))
|
||||
valstr))))
|
||||
|
||||
(defun gnus-set-text-properties-xemacs (start end props &optional buffer)
|
||||
"You should NEVER use this function. It is ideologically blasphemous.
|
||||
It is provided only to ease porting of broken FSF Emacs programs."
|
||||
(if (and (stringp buffer) (not (setq buffer (get-buffer buffer))))
|
||||
nil
|
||||
(map-extents (lambda (extent ignored)
|
||||
(remove-text-properties
|
||||
start end
|
||||
(list (extent-property extent 'text-prop) nil)
|
||||
buffer))
|
||||
buffer start end nil nil 'text-prop)
|
||||
(add-text-properties start end props buffer)))
|
||||
(eval-and-compile
|
||||
(if (string-match "XEmacs\\|Lucid" emacs-version)
|
||||
()
|
||||
|
||||
(eval
|
||||
'(progn
|
||||
(if (string-match "XEmacs\\|Lucid" emacs-version)
|
||||
()
|
||||
;; Added by Per Abrahamsen <amanda@iesd.auc.dk>.
|
||||
(defvar gnus-display-type
|
||||
(condition-case nil
|
||||
(let ((display-resource (x-get-resource ".displayType" "DisplayType")))
|
||||
(cond (display-resource (intern (downcase display-resource)))
|
||||
((x-display-color-p) 'color)
|
||||
((x-display-grayscale-p) 'grayscale)
|
||||
(t 'mono)))
|
||||
(error 'mono))
|
||||
"A symbol indicating the display Emacs is running under.
|
||||
(defvar gnus-mouse-face-prop 'mouse-face
|
||||
"Property used for highlighting mouse regions.")
|
||||
|
||||
(defvar gnus-article-x-face-command
|
||||
"{ echo '/* Width=48, Height=48 */'; uncompface; } | icontopbm | xv -quit -"
|
||||
"String or function to be executed to display an X-Face header.
|
||||
If it is a string, the command will be executed in a sub-shell
|
||||
asynchronously. The compressed face will be piped to this command.")
|
||||
|
||||
;; Added by Per Abrahamsen <amanda@iesd.auc.dk>.
|
||||
(defvar gnus-display-type
|
||||
(condition-case nil
|
||||
(let ((display-resource (x-get-resource ".displayType" "DisplayType")))
|
||||
(cond (display-resource (intern (downcase display-resource)))
|
||||
((x-display-color-p) 'color)
|
||||
((x-display-grayscale-p) 'grayscale)
|
||||
(t 'mono)))
|
||||
(error 'mono))
|
||||
"A symbol indicating the display Emacs is running under.
|
||||
The symbol should be one of `color', `grayscale' or `mono'. If Emacs
|
||||
guesses this display attribute wrongly, either set this variable in
|
||||
your `~/.emacs' or set the resource `Emacs.displayType' in your
|
||||
@ -118,20 +115,20 @@ This is a meta-variable that will affect what default values other
|
||||
variables get. You would normally not change this variable, but
|
||||
pounce directly on the real variables themselves.")
|
||||
|
||||
(defvar gnus-background-mode
|
||||
(condition-case nil
|
||||
(let ((bg-resource (x-get-resource ".backgroundMode"
|
||||
"BackgroundMode"))
|
||||
(params (frame-parameters)))
|
||||
(cond (bg-resource (intern (downcase bg-resource)))
|
||||
((and (cdr (assq 'background-color params))
|
||||
(< (apply '+ (x-color-values
|
||||
(cdr (assq 'background-color params))))
|
||||
(* (apply '+ (x-color-values "white")) .6)))
|
||||
'dark)
|
||||
(t 'light)))
|
||||
(error 'light))
|
||||
"A symbol indicating the Emacs background brightness.
|
||||
(defvar gnus-background-mode
|
||||
(condition-case nil
|
||||
(let ((bg-resource (x-get-resource ".backgroundMode"
|
||||
"BackgroundMode"))
|
||||
(params (frame-parameters)))
|
||||
(cond (bg-resource (intern (downcase bg-resource)))
|
||||
((and (cdr (assq 'background-color params))
|
||||
(< (apply '+ (x-color-values
|
||||
(cdr (assq 'background-color params))))
|
||||
(* (apply '+ (x-color-values "white")) .6)))
|
||||
'dark)
|
||||
(t 'light)))
|
||||
(error 'light))
|
||||
"A symbol indicating the Emacs background brightness.
|
||||
The symbol should be one of `light' or `dark'.
|
||||
If Emacs guesses this frame attribute wrongly, either set this variable in
|
||||
your `~/.emacs' or set the resource `Emacs.backgroundMode' in your
|
||||
@ -142,95 +139,24 @@ This is a meta-variable that will affect what default values other
|
||||
variables get. You would normally not change this variable, but
|
||||
pounce directly on the real variables themselves."))
|
||||
|
||||
(cond
|
||||
((string-match "XEmacs\\|Lucid" emacs-version)
|
||||
;; XEmacs definitions.
|
||||
(cond
|
||||
((string-match "XEmacs\\|Lucid" emacs-version)
|
||||
(gnus-xmas-define))
|
||||
|
||||
(setq gnus-mouse-2 [button2])
|
||||
|
||||
(or (memq 'underline (list-faces))
|
||||
(and (fboundp 'make-face)
|
||||
(funcall (intern "make-face") 'underline)))
|
||||
;; Must avoid calling set-face-underline-p directly, because it
|
||||
;; is a defsubst in emacs19, and will make the .elc files non
|
||||
;; portable!
|
||||
(or (face-differs-from-default-p 'underline)
|
||||
(funcall 'set-face-underline-p 'underline t))
|
||||
|
||||
(defalias 'gnus-make-overlay 'make-extent)
|
||||
(defalias 'gnus-overlay-put 'set-extent-property)
|
||||
(defun gnus-move-overlay (extent start end &optional buffer)
|
||||
(set-extent-endpoints extent start end))
|
||||
|
||||
(require 'text-props)
|
||||
(fset 'set-text-properties 'gnus-set-text-properties-xemacs)
|
||||
|
||||
(or (boundp 'standard-display-table) (setq standard-display-table nil))
|
||||
(or (boundp 'read-event) (fset 'read-event 'next-command-event))
|
||||
|
||||
;; Fix by "jeff (j.d.) sparkes" <jsparkes@bnr.ca>.
|
||||
(defvar gnus-display-type (device-class)
|
||||
"A symbol indicating the display Emacs is running under.
|
||||
The symbol should be one of `color', `grayscale' or `mono'. If Emacs
|
||||
guesses this display attribute wrongly, either set this variable in
|
||||
your `~/.emacs' or set the resource `Emacs.displayType' in your
|
||||
`~/.Xdefaults'. See also `gnus-background-mode'.
|
||||
|
||||
This is a meta-variable that will affect what default values other
|
||||
variables get. You would normally not change this variable, but
|
||||
pounce directly on the real variables themselves.")
|
||||
|
||||
|
||||
(or (fboundp 'x-color-values)
|
||||
(fset 'x-color-values
|
||||
(lambda (color)
|
||||
(color-instance-rgb-components
|
||||
(make-color-instance color)))))
|
||||
|
||||
(defvar gnus-background-mode
|
||||
(let ((bg-resource
|
||||
(condition-case ()
|
||||
(x-get-resource ".backgroundMode" "BackgroundMode" 'string)
|
||||
(error nil)))
|
||||
(params (frame-parameters)))
|
||||
(cond (bg-resource (intern (downcase bg-resource)))
|
||||
((and (assq 'background-color params)
|
||||
(< (apply '+ (x-color-values
|
||||
(cdr (assq 'background-color params))))
|
||||
(/ (apply '+ (x-color-values "white")) 3)))
|
||||
'dark)
|
||||
(t 'light)))
|
||||
"A symbol indicating the Emacs background brightness.
|
||||
The symbol should be one of `light' or `dark'.
|
||||
If Emacs guesses this frame attribute wrongly, either set this variable in
|
||||
your `~/.emacs' or set the resource `Emacs.backgroundMode' in your
|
||||
`~/.Xdefaults'.
|
||||
See also `gnus-display-type'.
|
||||
|
||||
This is a meta-variable that will affect what default values other
|
||||
variables get. You would normally not change this variable, but
|
||||
pounce directly on the real variables themselves.")
|
||||
|
||||
|
||||
(defun gnus-install-mouse-tracker ()
|
||||
(require 'mode-motion)
|
||||
(setq mode-motion-hook 'mode-motion-highlight-line)))
|
||||
|
||||
((< emacs-minor-version 30)
|
||||
;; Remove the `intangible' prop.
|
||||
(let ((props (and (boundp 'gnus-hidden-properties)
|
||||
gnus-hidden-properties)))
|
||||
(while (and props (not (eq (car (cdr props)) 'intangible)))
|
||||
(setq props (cdr props)))
|
||||
(and props (setcdr props (cdr (cdr (cdr props))))))
|
||||
(or (fboundp 'buffer-substring-no-properties)
|
||||
(defun buffer-substring-no-properties (beg end)
|
||||
(format "%s" (buffer-substring beg end)))))
|
||||
((or (not (boundp 'emacs-minor-version))
|
||||
(< emacs-minor-version 30))
|
||||
;; Remove the `intangible' prop.
|
||||
(let ((props (and (boundp 'gnus-hidden-properties)
|
||||
gnus-hidden-properties)))
|
||||
(while (and props (not (eq (car (cdr props)) 'intangible)))
|
||||
(setq props (cdr props)))
|
||||
(and props (setcdr props (cdr (cdr (cdr props))))))
|
||||
(or (fboundp 'buffer-substring-no-properties)
|
||||
(defun buffer-substring-no-properties (beg end)
|
||||
(format "%s" (buffer-substring beg end)))))
|
||||
|
||||
((boundp 'MULE)
|
||||
(provide 'gnusutil))
|
||||
|
||||
)))
|
||||
((boundp 'MULE)
|
||||
(provide 'gnusutil))))
|
||||
|
||||
(eval-and-compile
|
||||
(cond
|
||||
@ -248,451 +174,64 @@ pounce directly on the real variables themselves.")
|
||||
(not (file-symlink-p file))
|
||||
(file-exists-p file))))
|
||||
(or (fboundp 'face-list)
|
||||
(defun face-list (&rest args)))
|
||||
)
|
||||
(defun face-list (&rest args))))
|
||||
|
||||
(defun gnus-highlight-selected-summary-xemacs ()
|
||||
;; Highlight selected article in summary buffer
|
||||
(if gnus-summary-selected-face
|
||||
(progn
|
||||
(if gnus-newsgroup-selected-overlay
|
||||
(delete-extent gnus-newsgroup-selected-overlay))
|
||||
(setq gnus-newsgroup-selected-overlay
|
||||
(make-extent (gnus-point-at-bol) (gnus-point-at-eol)))
|
||||
(set-extent-face gnus-newsgroup-selected-overlay
|
||||
gnus-summary-selected-face))))
|
||||
(eval-and-compile
|
||||
(let ((case-fold-search t))
|
||||
(cond
|
||||
((string-match "windows-nt\\|os/2\\|emx" (format "%s" system-type))
|
||||
(setq nnheader-file-name-translation-alist
|
||||
(append nnheader-file-name-translation-alist
|
||||
'((?: . ?_)
|
||||
(?+ . ?-))))))))
|
||||
|
||||
(defun gnus-summary-recenter-xemacs ()
|
||||
(let* ((top (cond ((< (window-height) 4) 0)
|
||||
((< (window-height) 7) 1)
|
||||
(t 2)))
|
||||
(height (- (window-height) 2))
|
||||
(bottom (save-excursion (goto-char (point-max))
|
||||
(forward-line (- height))
|
||||
(point)))
|
||||
(window (get-buffer-window (current-buffer))))
|
||||
(and
|
||||
;; The user has to want it,
|
||||
gnus-auto-center-summary
|
||||
;; the article buffer must be displayed,
|
||||
(get-buffer-window gnus-article-buffer)
|
||||
;; Set the window start to either `bottom', which is the biggest
|
||||
;; possible valid number, or the second line from the top,
|
||||
;; whichever is the least.
|
||||
(set-window-start
|
||||
window (min bottom (save-excursion (forward-line (- top))
|
||||
(point)))))))
|
||||
|
||||
(defun gnus-group-insert-group-line-info-xemacs (group)
|
||||
(let ((entry (gnus-gethash group gnus-newsrc-hashtb))
|
||||
(beg (point))
|
||||
active info)
|
||||
(if entry
|
||||
(progn
|
||||
(setq info (nth 2 entry))
|
||||
(gnus-group-insert-group-line
|
||||
nil group (nth 1 info) (nth 3 info) (car entry) (nth 4 info)))
|
||||
(setq active (gnus-gethash group gnus-active-hashtb))
|
||||
|
||||
(gnus-group-insert-group-line
|
||||
nil group (if (member group gnus-zombie-list) gnus-level-zombie
|
||||
gnus-level-killed)
|
||||
nil (if active (- (1+ (cdr active)) (car active)) 0) nil))
|
||||
(save-excursion
|
||||
(goto-char beg)
|
||||
(remove-text-properties
|
||||
(1+ (gnus-point-at-bol)) (1+ (gnus-point-at-eol))
|
||||
'(gnus-group nil)))))
|
||||
|
||||
(defun gnus-summary-refer-article-xemacs (message-id)
|
||||
"Refer article specified by MESSAGE-ID.
|
||||
NOTE: This command only works with newsgroups that use real or simulated NNTP."
|
||||
(interactive "sMessage-ID: ")
|
||||
(if (or (not (stringp message-id))
|
||||
(zerop (length message-id)))
|
||||
()
|
||||
;; Construct the correct Message-ID if necessary.
|
||||
;; Suggested by tale@pawl.rpi.edu.
|
||||
(or (string-match "^<" message-id)
|
||||
(setq message-id (concat "<" message-id)))
|
||||
(or (string-match ">$" message-id)
|
||||
(setq message-id (concat message-id ">")))
|
||||
(let ((header (car (gnus-gethash (downcase message-id)
|
||||
gnus-newsgroup-dependencies))))
|
||||
(if header
|
||||
(or (gnus-summary-goto-article (mail-header-number header))
|
||||
;; The header has been read, but the article had been
|
||||
;; expunged, so we insert it again.
|
||||
(let ((beg (point)))
|
||||
(gnus-summary-insert-line
|
||||
nil header 0 nil gnus-read-mark nil nil
|
||||
(mail-header-subject header))
|
||||
(save-excursion
|
||||
(goto-char beg)
|
||||
(remove-text-properties
|
||||
(1+ (gnus-point-at-bol)) (1+ (gnus-point-at-eol))
|
||||
'(gnus-number nil gnus-mark nil gnus-level nil)))
|
||||
(forward-line -1)
|
||||
(mail-header-number header)))
|
||||
(let ((gnus-override-method gnus-refer-article-method)
|
||||
(gnus-ancient-mark gnus-read-mark)
|
||||
(tmp-point (window-start
|
||||
(get-buffer-window gnus-article-buffer)))
|
||||
number tmp-buf)
|
||||
(and gnus-refer-article-method
|
||||
(gnus-check-server gnus-refer-article-method))
|
||||
;; Save the old article buffer.
|
||||
(save-excursion
|
||||
(set-buffer gnus-article-buffer)
|
||||
(gnus-kill-buffer " *temp Article*")
|
||||
(setq tmp-buf (rename-buffer " *temp Article*")))
|
||||
(prog1
|
||||
(if (gnus-article-prepare
|
||||
message-id nil (gnus-read-header message-id))
|
||||
(progn
|
||||
(setq number (mail-header-number gnus-current-headers))
|
||||
(gnus-rebuild-thread message-id)
|
||||
(gnus-summary-goto-subject number)
|
||||
(gnus-summary-recenter)
|
||||
(gnus-article-set-window-start
|
||||
(cdr (assq number gnus-newsgroup-bookmarks)))
|
||||
message-id)
|
||||
;; We restore the old article buffer.
|
||||
(save-excursion
|
||||
(kill-buffer gnus-article-buffer)
|
||||
(set-buffer tmp-buf)
|
||||
(rename-buffer gnus-article-buffer)
|
||||
(let ((buffer-read-only nil))
|
||||
(and tmp-point
|
||||
(set-window-start (get-buffer-window (current-buffer))
|
||||
tmp-point)))))))))))
|
||||
|
||||
(defun gnus-summary-insert-pseudos-xemacs (pslist &optional not-view)
|
||||
(let ((buffer-read-only nil)
|
||||
(article (gnus-summary-article-number))
|
||||
b)
|
||||
(or (gnus-summary-goto-subject article)
|
||||
(error "No such article: %d" article))
|
||||
(or gnus-newsgroup-headers-hashtb-by-number
|
||||
(gnus-make-headers-hashtable-by-number))
|
||||
(gnus-summary-position-cursor)
|
||||
;; If all commands are to be bunched up on one line, we collect
|
||||
;; them here.
|
||||
(if gnus-view-pseudos-separately
|
||||
()
|
||||
(let ((ps (setq pslist (sort pslist 'gnus-pseudos<)))
|
||||
files action)
|
||||
(while ps
|
||||
(setq action (cdr (assq 'action (car ps))))
|
||||
(setq files (list (cdr (assq 'name (car ps)))))
|
||||
(while (and ps (cdr ps)
|
||||
(string= (or action "1")
|
||||
(or (cdr (assq 'action (car (cdr ps)))) "2")))
|
||||
(setq files (cons (cdr (assq 'name (car (cdr ps)))) files))
|
||||
(setcdr ps (cdr (cdr ps))))
|
||||
(if (not files)
|
||||
()
|
||||
(if (not (string-match "%s" action))
|
||||
(setq files (cons " " files)))
|
||||
(setq files (cons " " files))
|
||||
(and (assq 'execute (car ps))
|
||||
(setcdr (assq 'execute (car ps))
|
||||
(funcall (if (string-match "%s" action)
|
||||
'format 'concat)
|
||||
action
|
||||
(mapconcat (lambda (f) f) files " ")))))
|
||||
(setq ps (cdr ps)))))
|
||||
(if (and gnus-view-pseudos (not not-view))
|
||||
(while pslist
|
||||
(and (assq 'execute (car pslist))
|
||||
(gnus-execute-command (cdr (assq 'execute (car pslist)))
|
||||
(eq gnus-view-pseudos 'not-confirm)))
|
||||
(setq pslist (cdr pslist)))
|
||||
(save-excursion
|
||||
(while pslist
|
||||
(gnus-summary-goto-subject (or (cdr (assq 'article (car pslist)))
|
||||
(gnus-summary-article-number)))
|
||||
(forward-line 1)
|
||||
(setq b (point))
|
||||
(insert " "
|
||||
(file-name-nondirectory (cdr (assq 'name (car pslist))))
|
||||
": " (or (cdr (assq 'execute (car pslist))) "") "\n")
|
||||
(add-text-properties
|
||||
b (1+ b) (list 'gnus-number gnus-reffed-article-number
|
||||
'gnus-mark gnus-unread-mark
|
||||
'gnus-level 0
|
||||
'gnus-pseudo (car pslist)))
|
||||
;; Fire-trucking XEmacs redisplay bug with truncated lines.
|
||||
(goto-char b)
|
||||
(sit-for 0)
|
||||
;; Grumble.. fire-trucking XEmacs stickiness of text properties.
|
||||
(remove-text-properties
|
||||
(1+ b) (1+ (gnus-point-at-eol))
|
||||
'(gnus-number nil gnus-mark nil gnus-level nil))
|
||||
(forward-line -1)
|
||||
(gnus-sethash (int-to-string gnus-reffed-article-number)
|
||||
(car pslist) gnus-newsgroup-headers-hashtb-by-number)
|
||||
(setq gnus-reffed-article-number (1- gnus-reffed-article-number))
|
||||
(setq pslist (cdr pslist)))))))
|
||||
|
||||
|
||||
(defun gnus-copy-article-buffer-xemacs (&optional article-buffer)
|
||||
(setq gnus-article-copy (get-buffer-create " *gnus article copy*"))
|
||||
(buffer-disable-undo gnus-article-copy)
|
||||
(or (memq gnus-article-copy gnus-buffer-list)
|
||||
(setq gnus-buffer-list (cons gnus-article-copy gnus-buffer-list)))
|
||||
(let ((article-buffer (or article-buffer gnus-article-buffer))
|
||||
buf)
|
||||
(if (and (get-buffer article-buffer)
|
||||
(buffer-name (get-buffer article-buffer)))
|
||||
(save-excursion
|
||||
(set-buffer article-buffer)
|
||||
(widen)
|
||||
(setq buf (buffer-substring (point-min) (point-max)))
|
||||
(set-buffer gnus-article-copy)
|
||||
(erase-buffer)
|
||||
(insert (format "%s" buf))))))
|
||||
|
||||
(defun gnus-article-push-button-xemacs (event)
|
||||
"Check text under the mouse pointer for a callback function.
|
||||
If the text under the mouse pointer has a `gnus-callback' property,
|
||||
call it with the value of the `gnus-data' text property."
|
||||
(interactive "e")
|
||||
(set-buffer (window-buffer (event-window event)))
|
||||
(let* ((pos (event-closest-point event))
|
||||
(data (get-text-property pos 'gnus-data))
|
||||
(fun (get-text-property pos 'gnus-callback)))
|
||||
(if fun (funcall fun data))))
|
||||
|
||||
;; Re-build the thread containing ID.
|
||||
(defun gnus-rebuild-thread-xemacs (id)
|
||||
(let ((dep gnus-newsgroup-dependencies)
|
||||
(buffer-read-only nil)
|
||||
parent headers refs thread art)
|
||||
(while (and id (setq headers
|
||||
(car (setq art (gnus-gethash (downcase id)
|
||||
dep)))))
|
||||
(setq parent art)
|
||||
(setq id (and (setq refs (mail-header-references headers))
|
||||
(string-match "\\(<[^>]+>\\) *$" refs)
|
||||
(substring refs (match-beginning 1) (match-end 1)))))
|
||||
(setq thread (gnus-make-sub-thread (car parent)))
|
||||
(gnus-rebuild-remove-articles thread)
|
||||
(let ((beg (point)))
|
||||
(gnus-summary-prepare-threads (list thread) 0)
|
||||
(save-excursion
|
||||
(while (and (>= (point) beg)
|
||||
(not (bobp)))
|
||||
(or (eobp)
|
||||
(remove-text-properties
|
||||
(1+ (gnus-point-at-bol)) (1+ (gnus-point-at-eol))
|
||||
'(gnus-number nil gnus-mark nil gnus-level nil)))
|
||||
(forward-line -1)))
|
||||
(gnus-summary-update-lines beg (point)))))
|
||||
|
||||
|
||||
;; Fixed by Christopher Davis <ckd@loiosh.kei.com>.
|
||||
(defun gnus-article-add-button-xemacs (from to fun &optional data)
|
||||
"Create a button between FROM and TO with callback FUN and data DATA."
|
||||
(and gnus-article-button-face
|
||||
(gnus-overlay-put (gnus-make-overlay from to) 'face gnus-article-button-face))
|
||||
(add-text-properties from to
|
||||
(append
|
||||
(and gnus-article-mouse-face
|
||||
(list 'mouse-face gnus-article-mouse-face))
|
||||
(list 'gnus-callback fun)
|
||||
(and data (list 'gnus-data data))
|
||||
(list 'highlight t))))
|
||||
|
||||
(defun gnus-window-top-edge-xemacs (&optional window)
|
||||
(nth 1 (window-pixel-edges window)))
|
||||
|
||||
;; Select the lowest window on the frame.
|
||||
(defun gnus-appt-select-lowest-window-xemacs ()
|
||||
(let* ((lowest-window (selected-window))
|
||||
(bottom-edge (car (cdr (cdr (cdr (window-pixel-edges))))))
|
||||
(last-window (previous-window))
|
||||
(window-search t))
|
||||
(while window-search
|
||||
(let* ((this-window (next-window))
|
||||
(next-bottom-edge (car (cdr (cdr (cdr
|
||||
(window-pixel-edges
|
||||
this-window)))))))
|
||||
(if (< bottom-edge next-bottom-edge)
|
||||
(progn
|
||||
(setq bottom-edge next-bottom-edge)
|
||||
(setq lowest-window this-window)))
|
||||
|
||||
(select-window this-window)
|
||||
(if (eq last-window this-window)
|
||||
(progn
|
||||
(select-window lowest-window)
|
||||
(setq window-search nil)))))))
|
||||
(defvar gnus-tmp-unread)
|
||||
(defvar gnus-tmp-replied)
|
||||
(defvar gnus-tmp-score-char)
|
||||
(defvar gnus-tmp-indentation)
|
||||
(defvar gnus-tmp-opening-bracket)
|
||||
(defvar gnus-tmp-lines)
|
||||
(defvar gnus-tmp-name)
|
||||
(defvar gnus-tmp-closing-bracket)
|
||||
(defvar gnus-tmp-subject-or-nil)
|
||||
|
||||
(defun gnus-ems-redefine ()
|
||||
(cond
|
||||
((string-match "XEmacs\\|Lucid" emacs-version)
|
||||
;; XEmacs definitions.
|
||||
(fset 'gnus-mouse-face-function 'identity)
|
||||
(fset 'gnus-summary-make-display-table (lambda () nil))
|
||||
(fset 'gnus-visual-turn-off-edit-menu 'identity)
|
||||
(fset 'gnus-highlight-selected-summary
|
||||
'gnus-highlight-selected-summary-xemacs)
|
||||
(fset 'gnus-summary-recenter 'gnus-summary-recenter-xemacs)
|
||||
(fset 'gnus-group-insert-group-line-info
|
||||
'gnus-group-insert-group-line-info-xemacs)
|
||||
(fset 'gnus-copy-article-buffer 'gnus-copy-article-buffer-xemacs)
|
||||
(fset 'gnus-summary-refer-article 'gnus-summary-refer-article-xemacs)
|
||||
(fset 'gnus-summary-insert-pseudos 'gnus-summary-insert-pseudos-xemacs)
|
||||
(fset 'gnus-article-push-button 'gnus-article-push-button-xemacs)
|
||||
(fset 'gnus-rebuild-thread 'gnus-rebuild-thread-xemacs)
|
||||
(fset 'gnus-article-add-button 'gnus-article-add-button-xemacs)
|
||||
(fset 'gnus-window-top-edge 'gnus-window-top-edge-xemacs)
|
||||
(fset 'set-text-properties 'gnus-set-text-properties-xemacs)
|
||||
|
||||
(or (fboundp 'appt-select-lowest-window)
|
||||
(fset 'appt-select-lowest-window
|
||||
'gnus-appt-select-lowest-window-xemacs))
|
||||
|
||||
(if (not gnus-visual)
|
||||
()
|
||||
(setq gnus-group-mode-hook
|
||||
(cons
|
||||
'(lambda ()
|
||||
(easy-menu-add gnus-group-reading-menu)
|
||||
(easy-menu-add gnus-group-group-menu)
|
||||
(easy-menu-add gnus-group-misc-menu)
|
||||
(gnus-install-mouse-tracker))
|
||||
gnus-group-mode-hook))
|
||||
(setq gnus-summary-mode-hook
|
||||
(cons
|
||||
'(lambda ()
|
||||
(easy-menu-add gnus-summary-article-menu)
|
||||
(easy-menu-add gnus-summary-thread-menu)
|
||||
(easy-menu-add gnus-summary-misc-menu)
|
||||
(easy-menu-add gnus-summary-post-menu)
|
||||
(easy-menu-add gnus-summary-kill-menu)
|
||||
(gnus-install-mouse-tracker))
|
||||
gnus-summary-mode-hook))
|
||||
(setq gnus-article-mode-hook
|
||||
(cons
|
||||
'(lambda ()
|
||||
(easy-menu-add gnus-article-article-menu)
|
||||
(easy-menu-add gnus-article-treatment-menu))
|
||||
gnus-article-mode-hook)))
|
||||
|
||||
(defvar gnus-logo (make-glyph (make-specifier 'image)))
|
||||
|
||||
(defun gnus-group-startup-xmessage (&optional x y)
|
||||
"Insert startup message in current buffer."
|
||||
;; Insert the message.
|
||||
(erase-buffer)
|
||||
(if (featurep 'xpm)
|
||||
(progn
|
||||
(set-glyph-property gnus-logo 'image "~/tmp/gnus.xpm")
|
||||
(set-glyph-image gnus-logo "~/tmp/gnus.xpm" 'global 'x)
|
||||
|
||||
(insert " ")
|
||||
(set-extent-begin-glyph (make-extent (point) (point)) gnus-logo)
|
||||
(insert "
|
||||
Gnus * A newsreader for Emacsen
|
||||
A Praxis Release * larsi@ifi.uio.no")
|
||||
(goto-char (point-min))
|
||||
(while (not (eobp))
|
||||
(insert (make-string (/ (max (- (window-width) (or x 35)) 0) 2)
|
||||
? ))
|
||||
(forward-line 1))
|
||||
(goto-char (point-min))
|
||||
;; +4 is fuzzy factor.
|
||||
(insert-char ?\n (/ (max (- (window-height) (or y 24)) 0) 2)))
|
||||
|
||||
(insert
|
||||
(format "
|
||||
%s
|
||||
A newsreader
|
||||
for GNU Emacs
|
||||
|
||||
Based on GNUS
|
||||
written by
|
||||
Masanobu UMEDA
|
||||
|
||||
A Praxis Release
|
||||
larsi@ifi.uio.no
|
||||
"
|
||||
gnus-version))
|
||||
;; And then hack it.
|
||||
;; 18 is the longest line.
|
||||
(indent-rigidly (point-min) (point-max)
|
||||
(/ (max (- (window-width) (or x 28)) 0) 2))
|
||||
(goto-char (point-min))
|
||||
;; +4 is fuzzy factor.
|
||||
(insert-char ?\n (/ (max (- (window-height) (or y 12)) 0) 2)))
|
||||
|
||||
;; Fontify some.
|
||||
(goto-char (point-min))
|
||||
(search-forward "Praxis")
|
||||
(put-text-property (match-beginning 0) (match-end 0) 'face 'bold)
|
||||
(goto-char (point-min)))
|
||||
|
||||
|
||||
|
||||
)
|
||||
(gnus-xmas-redefine))
|
||||
|
||||
((boundp 'MULE)
|
||||
;; Mule definitions
|
||||
(if (not (fboundp 'truncate-string))
|
||||
(defun truncate-string (str width)
|
||||
(let ((w (string-width str))
|
||||
(col 0) (idx 0) (p-idx 0) chr)
|
||||
(if (<= w width)
|
||||
str
|
||||
(while (< col width)
|
||||
(setq chr (aref str idx)
|
||||
col (+ col (char-width chr))
|
||||
p-idx idx
|
||||
idx (+ idx (char-bytes chr))
|
||||
))
|
||||
(substring str 0 (if (= col width)
|
||||
idx
|
||||
p-idx))
|
||||
)))
|
||||
)
|
||||
(defalias 'gnus-truncate-string 'truncate-string)
|
||||
|
||||
(defun gnus-cite-add-face (number prefix face)
|
||||
;; At line NUMBER, ignore PREFIX and add FACE to the rest of the line.
|
||||
(if face
|
||||
(let ((inhibit-point-motion-hooks t)
|
||||
from to)
|
||||
(goto-line number)
|
||||
(if (boundp 'MULE)
|
||||
(forward-char (chars-in-string prefix))
|
||||
(forward-char (length prefix)))
|
||||
(skip-chars-forward " \t")
|
||||
(setq from (point))
|
||||
(end-of-line 1)
|
||||
(skip-chars-backward " \t")
|
||||
(setq to (point))
|
||||
(if (< from to)
|
||||
(gnus-overlay-put (gnus-make-overlay from to) 'face face)))))
|
||||
|
||||
(defun gnus-max-width-function (el max-width)
|
||||
(` (let* ((val (eval (, el)))
|
||||
(valstr (if (numberp val)
|
||||
(int-to-string val) val)))
|
||||
(if (> (length valstr) (, max-width))
|
||||
(truncate-string valstr (, max-width))
|
||||
valstr))))
|
||||
|
||||
(fset 'gnus-summary-make-display-table (lambda () nil))
|
||||
(fset 'gnus-cite-add-face 'gnus-mule-cite-add-face)
|
||||
(fset 'gnus-max-width-function 'gnus-mule-max-width-function)
|
||||
|
||||
(if (boundp 'gnus-check-before-posting)
|
||||
(setq gnus-check-before-posting
|
||||
(delq 'long-lines
|
||||
(delq 'control-chars gnus-check-before-posting)))
|
||||
)
|
||||
)
|
||||
))
|
||||
(delq 'control-chars gnus-check-before-posting))))
|
||||
|
||||
(defun gnus-summary-line-format-spec ()
|
||||
(insert gnus-tmp-unread gnus-tmp-replied
|
||||
gnus-tmp-score-char gnus-tmp-indentation)
|
||||
(put-text-property
|
||||
(point)
|
||||
(progn
|
||||
(insert
|
||||
gnus-tmp-opening-bracket
|
||||
(format "%4d: %-20s"
|
||||
gnus-tmp-lines
|
||||
(if (> (length gnus-tmp-name) 20)
|
||||
(truncate-string gnus-tmp-name 20)
|
||||
gnus-tmp-name))
|
||||
gnus-tmp-closing-bracket)
|
||||
(point))
|
||||
gnus-mouse-face-prop gnus-mouse-face)
|
||||
(insert " " gnus-tmp-subject-or-nil "\n"))
|
||||
)))
|
||||
|
||||
|
||||
(provide 'gnus-ems)
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
;;; gnus-kill.el --- kill commands for Gnus
|
||||
|
||||
;; Copyright (C) 1995 Free Software Foundation, Inc.
|
||||
;; Copyright (C) 1995,96 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Masanobu UMEDA <umerin@flab.flab.fujitsu.junet>
|
||||
;; Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
|
||||
@ -28,6 +27,7 @@
|
||||
;;; Code:
|
||||
|
||||
(require 'gnus)
|
||||
(eval-when-compile (require 'cl))
|
||||
|
||||
(defvar gnus-kill-file-mode-hook nil
|
||||
"*A hook for Gnus kill file mode.")
|
||||
@ -43,12 +43,12 @@
|
||||
|
||||
|
||||
(defmacro gnus-raise (field expression level)
|
||||
(` (gnus-kill (, field) (, expression)
|
||||
(function (gnus-summary-raise-score (, level))) t)))
|
||||
`(gnus-kill ,field ,expression
|
||||
(function (gnus-summary-raise-score ,level)) t))
|
||||
|
||||
(defmacro gnus-lower (field expression level)
|
||||
(` (gnus-kill (, field) (, expression)
|
||||
(function (gnus-summary-raise-score (- (, level)))) t)))
|
||||
`(gnus-kill ,field ,expression
|
||||
(function (gnus-summary-raise-score (- ,level))) t))
|
||||
|
||||
;;;
|
||||
;;; Gnus Kill File Mode
|
||||
@ -56,23 +56,16 @@
|
||||
|
||||
(defvar gnus-kill-file-mode-map nil)
|
||||
|
||||
(if gnus-kill-file-mode-map
|
||||
nil
|
||||
(setq gnus-kill-file-mode-map (copy-keymap emacs-lisp-mode-map))
|
||||
(define-key gnus-kill-file-mode-map
|
||||
"\C-c\C-k\C-s" 'gnus-kill-file-kill-by-subject)
|
||||
(define-key gnus-kill-file-mode-map
|
||||
"\C-c\C-k\C-a" 'gnus-kill-file-kill-by-author)
|
||||
(define-key gnus-kill-file-mode-map
|
||||
"\C-c\C-k\C-t" 'gnus-kill-file-kill-by-thread)
|
||||
(define-key gnus-kill-file-mode-map
|
||||
"\C-c\C-k\C-x" 'gnus-kill-file-kill-by-xref)
|
||||
(define-key gnus-kill-file-mode-map
|
||||
"\C-c\C-a" 'gnus-kill-file-apply-buffer)
|
||||
(define-key gnus-kill-file-mode-map
|
||||
"\C-c\C-e" 'gnus-kill-file-apply-last-sexp)
|
||||
(define-key gnus-kill-file-mode-map
|
||||
"\C-c\C-c" 'gnus-kill-file-exit))
|
||||
(unless gnus-kill-file-mode-map
|
||||
(gnus-define-keymap
|
||||
(setq gnus-kill-file-mode-map (copy-keymap emacs-lisp-mode-map))
|
||||
"\C-c\C-k\C-s" gnus-kill-file-kill-by-subject
|
||||
"\C-c\C-k\C-a" gnus-kill-file-kill-by-author
|
||||
"\C-c\C-k\C-t" gnus-kill-file-kill-by-thread
|
||||
"\C-c\C-k\C-x" gnus-kill-file-kill-by-xref
|
||||
"\C-c\C-a" gnus-kill-file-apply-buffer
|
||||
"\C-c\C-e" gnus-kill-file-apply-last-sexp
|
||||
"\C-c\C-c" gnus-kill-file-exit))
|
||||
|
||||
(defun gnus-kill-file-mode ()
|
||||
"Major mode for editing kill files.
|
||||
@ -181,7 +174,7 @@ If NEWSGROUP is nil, the global kill file is selected."
|
||||
(gnus-kill-file-mode)
|
||||
(bury-buffer buffer)))
|
||||
|
||||
(defun gnus-kill-file-enter-kill (field regexp)
|
||||
(defun gnus-kill-file-enter-kill (field regexp &optional dont-move)
|
||||
;; Enter kill file entry.
|
||||
;; FIELD: String containing the name of the header field to kill.
|
||||
;; REGEXP: The string to kill.
|
||||
@ -189,8 +182,8 @@ If NEWSGROUP is nil, the global kill file is selected."
|
||||
(let (string)
|
||||
(or (eq major-mode 'gnus-kill-file-mode)
|
||||
(gnus-kill-set-kill-buffer))
|
||||
(current-buffer)
|
||||
(goto-char (point-max))
|
||||
(unless dont-move
|
||||
(goto-char (point-max)))
|
||||
(insert (setq string (format "(gnus-kill %S %S)\n" field regexp)))
|
||||
(gnus-kill-file-apply-string string))))
|
||||
|
||||
@ -202,7 +195,7 @@ If NEWSGROUP is nil, the global kill file is selected."
|
||||
(if (vectorp gnus-current-headers)
|
||||
(regexp-quote
|
||||
(gnus-simplify-subject (mail-header-subject gnus-current-headers)))
|
||||
"")))
|
||||
"") t))
|
||||
|
||||
(defun gnus-kill-file-kill-by-author ()
|
||||
"Kill by author."
|
||||
@ -211,7 +204,7 @@ If NEWSGROUP is nil, the global kill file is selected."
|
||||
"From"
|
||||
(if (vectorp gnus-current-headers)
|
||||
(regexp-quote (mail-header-from gnus-current-headers))
|
||||
"")))
|
||||
"") t))
|
||||
|
||||
(defun gnus-kill-file-kill-by-thread ()
|
||||
"Kill by author."
|
||||
@ -237,8 +230,8 @@ If NEWSGROUP is nil, the global kill file is selected."
|
||||
(substring xref (match-beginning 1) (match-end 1)))
|
||||
gnus-newsgroup-name))
|
||||
(gnus-kill-file-enter-kill
|
||||
"Xref" (concat " " (regexp-quote group) ":"))))
|
||||
(gnus-kill-file-enter-kill "Xref" ""))))
|
||||
"Xref" (concat " " (regexp-quote group) ":") t)))
|
||||
(gnus-kill-file-enter-kill "Xref" "" t))))
|
||||
|
||||
(defun gnus-kill-file-raise-followups-to-author (level)
|
||||
"Raise score for all followups to the current author."
|
||||
@ -258,7 +251,8 @@ If NEWSGROUP is nil, the global kill file is selected."
|
||||
"From" name level))
|
||||
(insert string)
|
||||
(gnus-kill-file-apply-string string))
|
||||
(message "Added temporary score file entry for followups to %s." name)))
|
||||
(gnus-message
|
||||
6 "Added temporary score file entry for followups to %s." name)))
|
||||
|
||||
(defun gnus-kill-file-apply-buffer ()
|
||||
"Apply current buffer to current newsgroup."
|
||||
@ -267,7 +261,7 @@ If NEWSGROUP is nil, the global kill file is selected."
|
||||
(get-buffer gnus-summary-buffer))
|
||||
;; Assume newsgroup is selected.
|
||||
(gnus-kill-file-apply-string (buffer-string))
|
||||
(ding) (message "No newsgroup is selected.")))
|
||||
(ding) (gnus-message 2 "No newsgroup is selected.")))
|
||||
|
||||
(defun gnus-kill-file-apply-string (string)
|
||||
"Apply STRING to current newsgroup."
|
||||
@ -291,7 +285,7 @@ If NEWSGROUP is nil, the global kill file is selected."
|
||||
(save-window-excursion
|
||||
(pop-to-buffer gnus-summary-buffer)
|
||||
(eval (car (read-from-string string))))))
|
||||
(ding) (message "No newsgroup is selected.")))
|
||||
(ding) (gnus-message 2 "No newsgroup is selected.")))
|
||||
|
||||
(defun gnus-kill-file-exit ()
|
||||
"Save a kill file, then return to the previous buffer."
|
||||
@ -318,24 +312,37 @@ If NEWSGROUP is nil, return the global kill file instead."
|
||||
(cond ((or (null newsgroup)
|
||||
(string-equal newsgroup ""))
|
||||
;; The global kill file is placed at top of the directory.
|
||||
(expand-file-name gnus-kill-file-name
|
||||
(or gnus-kill-files-directory "~/News")))
|
||||
(expand-file-name gnus-kill-file-name gnus-kill-files-directory))
|
||||
(gnus-use-long-file-name
|
||||
;; Append ".KILL" to capitalized newsgroup name.
|
||||
(expand-file-name (concat (gnus-capitalize-newsgroup newsgroup)
|
||||
"." gnus-kill-file-name)
|
||||
(or gnus-kill-files-directory "~/News")))
|
||||
gnus-kill-files-directory))
|
||||
(t
|
||||
;; Place "KILL" under the hierarchical directory.
|
||||
(expand-file-name (concat (gnus-newsgroup-directory-form newsgroup)
|
||||
"/" gnus-kill-file-name)
|
||||
(or gnus-kill-files-directory "~/News")))))
|
||||
gnus-kill-files-directory))))
|
||||
|
||||
(defun gnus-expunge (marks)
|
||||
"Remove lines marked with MARKS."
|
||||
(save-excursion
|
||||
(set-buffer gnus-summary-buffer)
|
||||
(gnus-summary-remove-lines-marked-with marks)))
|
||||
(gnus-summary-limit-to-marks marks 'reverse)))
|
||||
|
||||
(defun gnus-apply-kill-file-unless-scored ()
|
||||
"Apply .KILL file, unless a .SCORE file for the same newsgroup exists."
|
||||
(cond ((file-exists-p (gnus-score-file-name gnus-newsgroup-name))
|
||||
;; Ignores global KILL.
|
||||
(if (file-exists-p (gnus-newsgroup-kill-file gnus-newsgroup-name))
|
||||
(gnus-message 3 "Note: Ignoring %s.KILL; preferring .SCORE"
|
||||
gnus-newsgroup-name))
|
||||
0)
|
||||
((or (file-exists-p (gnus-newsgroup-kill-file nil))
|
||||
(file-exists-p (gnus-newsgroup-kill-file gnus-newsgroup-name)))
|
||||
(gnus-apply-kill-file-internal))
|
||||
(t
|
||||
0)))
|
||||
|
||||
(defun gnus-apply-kill-file-internal ()
|
||||
"Apply a kill file to the current newsgroup.
|
||||
@ -346,8 +353,6 @@ Returns the number of articles marked as read."
|
||||
(gnus-summary-inhibit-highlight t)
|
||||
beg)
|
||||
(setq gnus-newsgroup-kill-headers nil)
|
||||
(or gnus-newsgroup-headers-hashtb-by-number
|
||||
(gnus-make-headers-hashtable-by-number))
|
||||
;; If there are any previously scored articles, we remove these
|
||||
;; from the `gnus-newsgroup-headers' list that the score functions
|
||||
;; will see. This is probably pretty wasteful when it comes to
|
||||
@ -378,7 +383,7 @@ Returns the number of articles marked as read."
|
||||
(while kill-files
|
||||
(if (not (file-exists-p (car kill-files)))
|
||||
()
|
||||
(message "Processing kill file %s..." (car kill-files))
|
||||
(gnus-message 6 "Processing kill file %s..." (car kill-files))
|
||||
(find-file (car kill-files))
|
||||
(gnus-add-current-to-buffer-list)
|
||||
(goto-char (point-min))
|
||||
@ -388,7 +393,8 @@ Returns the number of articles marked as read."
|
||||
(gnus-kill-parse-gnus-kill-file)
|
||||
(gnus-kill-parse-rn-kill-file))
|
||||
|
||||
(message "Processing kill file %s...done" (car kill-files)))
|
||||
(gnus-message
|
||||
6 "Processing kill file %s...done" (car kill-files)))
|
||||
(setq kill-files (cdr kill-files)))))
|
||||
|
||||
(gnus-set-mode-line 'summary)
|
||||
@ -396,7 +402,7 @@ Returns the number of articles marked as read."
|
||||
(if beg
|
||||
(let ((nunreads (- unreads (length gnus-newsgroup-unreads))))
|
||||
(or (eq nunreads 0)
|
||||
(message "Marked %d articles as read" nunreads))
|
||||
(gnus-message 6 "Marked %d articles as read" nunreads))
|
||||
nunreads)
|
||||
0))))
|
||||
|
||||
@ -408,7 +414,7 @@ Returns the number of articles marked as read."
|
||||
(erase-buffer)
|
||||
(insert string ":\n\n")
|
||||
(while alist
|
||||
(insert (format " %c: %s\n" (car (car alist)) (nth idx (car alist))))
|
||||
(insert (format " %c: %s\n" (caar alist) (nth idx (car alist))))
|
||||
(setq alist (cdr alist)))))
|
||||
|
||||
(defun gnus-kill-parse-gnus-kill-file ()
|
||||
@ -538,14 +544,14 @@ COMMAND must be a lisp expression or a string representing a key sequence."
|
||||
(if (or (not (consp (nth 2 object)))
|
||||
(not (consp (cdr (nth 2 object))))
|
||||
(and (eq 'quote (car (nth 2 object)))
|
||||
(not (consp (cdr (car (cdr (nth 2 object))))))))
|
||||
(not (consp (cdadr (nth 2 object))))))
|
||||
(concat "\n" (prin1-to-string object))
|
||||
(save-excursion
|
||||
(set-buffer (get-buffer-create "*Gnus PP*"))
|
||||
(buffer-disable-undo (current-buffer))
|
||||
(erase-buffer)
|
||||
(insert (format "\n(%S %S\n '(" (nth 0 object) (nth 1 object)))
|
||||
(let ((klist (car (cdr (nth 2 object))))
|
||||
(let ((klist (cadr (nth 2 object)))
|
||||
(first t))
|
||||
(while klist
|
||||
(insert (if first (progn (setq first nil) "") "\n ")
|
||||
@ -580,15 +586,19 @@ COMMAND must be a lisp expression or a string representing a key sequence."
|
||||
(or (stringp value)
|
||||
(setq value (prin1-to-string value)))
|
||||
(setq did-kill (string-match regexp value)))
|
||||
(if (stringp form) ;Keyboard macro.
|
||||
(execute-kbd-macro form)
|
||||
(funcall form))))
|
||||
(cond ((stringp form) ;Keyboard macro.
|
||||
(execute-kbd-macro form))
|
||||
((gnus-functionp form)
|
||||
(funcall form))
|
||||
(t
|
||||
(eval form)))))
|
||||
;; Search article body.
|
||||
(let ((gnus-current-article nil) ;Save article pointer.
|
||||
(gnus-last-article nil)
|
||||
(gnus-break-pages nil) ;No need to break pages.
|
||||
(gnus-mark-article-hook nil)) ;Inhibit marking as read.
|
||||
(message "Searching for article: %d..." (mail-header-number header))
|
||||
(gnus-message
|
||||
6 "Searching for article: %d..." (mail-header-number header))
|
||||
(gnus-article-setup-buffer)
|
||||
(gnus-article-prepare (mail-header-number header) t)
|
||||
(if (save-excursion
|
||||
@ -609,27 +619,37 @@ marked as read or ticked are ignored."
|
||||
(save-excursion
|
||||
(let ((killed-no 0)
|
||||
function article header)
|
||||
(if (or (null field) (string-equal field ""))
|
||||
(setq function nil)
|
||||
;; Get access function of header filed.
|
||||
(setq function (intern-soft (concat "gnus-header-" (downcase field))))
|
||||
(if (and function (fboundp function))
|
||||
(setq function (symbol-function function))
|
||||
(error "Unknown header field: \"%s\"" field))
|
||||
;; Make FORM funcallable.
|
||||
(if (and (listp form) (not (eq (car form) 'lambda)))
|
||||
(setq form (list 'lambda nil form))))
|
||||
(cond
|
||||
;; Search body.
|
||||
((or (null field)
|
||||
(string-equal field ""))
|
||||
(setq function nil))
|
||||
;; Get access function of header field.
|
||||
((fboundp
|
||||
(setq function
|
||||
(intern-soft
|
||||
(concat "mail-header-" (downcase field)))))
|
||||
(setq function `(lambda (h) (,function h))))
|
||||
;; Signal error.
|
||||
(t
|
||||
(error "Unknown header field: \"%s\"" field)))
|
||||
;; Starting from the current article.
|
||||
(while (or (and (not article)
|
||||
(setq article (gnus-summary-article-number))
|
||||
t)
|
||||
(setq article
|
||||
(gnus-summary-search-subject
|
||||
backward (not ignore-marked))))
|
||||
(while (or
|
||||
;; First article.
|
||||
(and (not article)
|
||||
(setq article (gnus-summary-article-number)))
|
||||
;; Find later articles.
|
||||
(setq article
|
||||
(gnus-summary-search-forward
|
||||
(not ignore-marked) nil backward)))
|
||||
(and (or (null gnus-newsgroup-kill-headers)
|
||||
(memq article gnus-newsgroup-kill-headers))
|
||||
(vectorp (setq header (gnus-get-header-by-number article)))
|
||||
(vectorp (setq header (gnus-summary-article-header article)))
|
||||
(gnus-execute-1 function regexp form header)
|
||||
(setq killed-no (1+ killed-no))))
|
||||
;; Return the number of killed articles.
|
||||
killed-no)))
|
||||
|
||||
(provide 'gnus-kill)
|
||||
|
||||
;;; gnus-kill.el ends here
|
||||
|
177
lisp/gnus-mh.el
177
lisp/gnus-mh.el
@ -1,6 +1,5 @@
|
||||
;;; gnus-mh.el --- mh-e interface for Gnus
|
||||
|
||||
;; Copyright (C) 1994,95 Free Software Foundation, Inc.
|
||||
;; Copyright (C) 1994,95,96 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Masanobu UMEDA <umerin@flab.flab.fujitsu.junet>
|
||||
;; Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
|
||||
@ -38,6 +37,7 @@
|
||||
(require 'mh-comp)
|
||||
(require 'gnus)
|
||||
(require 'gnus-msg)
|
||||
(eval-when-compile (require 'cl))
|
||||
|
||||
(defun gnus-summary-save-article-folder (&optional arg)
|
||||
"Append the current article to an mh folder.
|
||||
@ -55,155 +55,30 @@ Optional argument FOLDER specifies folder name."
|
||||
;; Thanks to yuki@flab.Fujitsu.JUNET and ohm@kaba.junet.
|
||||
(mh-find-path)
|
||||
(let ((folder
|
||||
(or folder
|
||||
(mh-prompt-for-folder
|
||||
"Save article in"
|
||||
(funcall gnus-folder-save-name gnus-newsgroup-name
|
||||
gnus-current-headers gnus-newsgroup-last-folder)
|
||||
t)))
|
||||
(errbuf (get-buffer-create " *Gnus rcvstore*")))
|
||||
(gnus-eval-in-buffer-window
|
||||
gnus-article-buffer
|
||||
(save-restriction
|
||||
(widen)
|
||||
(unwind-protect
|
||||
(call-process-region (point-min) (point-max)
|
||||
(expand-file-name "rcvstore" mh-lib)
|
||||
nil errbuf nil folder)
|
||||
(set-buffer errbuf)
|
||||
(if (zerop (buffer-size))
|
||||
(message "Article saved in folder: %s" folder)
|
||||
(message "%s" (buffer-string)))
|
||||
(kill-buffer errbuf))))
|
||||
(setq gnus-newsgroup-last-folder folder)))
|
||||
|
||||
(defun gnus-mail-reply-using-mhe (&optional yank)
|
||||
"Compose reply mail using mh-e.
|
||||
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)
|
||||
(setq buffer (current-buffer))
|
||||
(save-excursion
|
||||
(cond ((and (eq folder 'default)
|
||||
gnus-newsgroup-last-folder)
|
||||
gnus-newsgroup-last-folder)
|
||||
(folder folder)
|
||||
(t (mh-prompt-for-folder
|
||||
"Save article in"
|
||||
(funcall gnus-folder-save-name gnus-newsgroup-name
|
||||
gnus-current-headers gnus-newsgroup-last-folder)
|
||||
t))))
|
||||
(errbuf (get-buffer-create " *Gnus rcvstore*"))
|
||||
;; Find the rcvstore program.
|
||||
(exec-path (if mh-lib (cons mh-lib exec-path) exec-path)))
|
||||
(gnus-eval-in-buffer-window gnus-original-article-buffer
|
||||
(save-restriction
|
||||
(or gnus-user-login-name ; we need this
|
||||
(setq gnus-user-login-name (or (getenv "USER")
|
||||
(getenv "LOGNAME"))))
|
||||
|
||||
(gnus-article-show-all-headers);; so colors are happy
|
||||
;; lots of junk to avoid mh-send deleting other windows
|
||||
(setq from (or (gnus-fetch-field "from") "")
|
||||
subject (let ((subject (or (gnus-fetch-field "subject")
|
||||
"(None)")))
|
||||
(if (and subject
|
||||
(not (string-match "^[Rr][Ee]:.+$" subject)))
|
||||
(concat "Re: " subject) subject))
|
||||
reply-to (gnus-fetch-field "reply-to")
|
||||
cc (gnus-fetch-field "cc")
|
||||
orig-to (or (gnus-fetch-field "to") "")
|
||||
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)
|
||||
(not (string-match (substring to-userid 0
|
||||
(string-match "@" to-userid))
|
||||
gnus-user-login-name)))
|
||||
(setq cc (concat (if cc (concat cc ", ") "") orig-to)))
|
||||
;; mh-yank-cur-msg needs to have mh-show-buffer set in the
|
||||
;; *Article* buffer
|
||||
(setq mh-show-buffer buffer)))
|
||||
|
||||
(mh-find-path)
|
||||
(mh-send-sub (or to "") (or cc "")
|
||||
(or subject "(None)") config);; Erik Selberg 1/23/94
|
||||
|
||||
(let ((draft (current-buffer))
|
||||
(gnus-mail-buffer (current-buffer))
|
||||
mail-buf)
|
||||
(if (not yank)
|
||||
(gnus-configure-windows 'reply 'force)
|
||||
(gnus-configure-windows 'reply-yank 'force))
|
||||
(setq mail-buf gnus-mail-buffer)
|
||||
(pop-to-buffer mail-buf);; always in the display, so won't have window probs
|
||||
(switch-to-buffer draft))
|
||||
|
||||
;; (mh-send to (or cc "") subject);; shouldn't use according to mhe
|
||||
|
||||
;; note - current buffer is now draft!
|
||||
(save-excursion
|
||||
(mh-insert-fields
|
||||
"In-reply-to:"
|
||||
(concat
|
||||
(substring from 0 (string-match " *at \\| *@ \\| *(\\| *<" from))
|
||||
"'s message of " date))
|
||||
(nnheader-insert-references references message-id))
|
||||
|
||||
;; need this for mh-yank-cur-msg
|
||||
(setq mh-sent-from-folder buffer)
|
||||
(setq mh-sent-from-msg 1)
|
||||
(setq mh-show-buffer buffer)
|
||||
(setq mh-previous-window-config config))
|
||||
|
||||
;; Then, yank original article if requested.
|
||||
(if yank
|
||||
(let ((last (point)))
|
||||
(mh-yank-cur-msg)
|
||||
(goto-char last)))
|
||||
|
||||
(run-hooks 'gnus-mail-hook))
|
||||
|
||||
|
||||
;; gnus-mail-forward-using-mhe is contributed by Jun-ichiro Itoh
|
||||
;; <itojun@ingram.mt.cs.keio.ac.jp>
|
||||
|
||||
(defun gnus-mail-forward-using-mhe (&optional buffer)
|
||||
"Forward the current message to another user using mh-e."
|
||||
;; First of all, prepare mhe mail buffer.
|
||||
(let* ((to (read-string "To: "))
|
||||
(cc (read-string "Cc: "))
|
||||
(buffer (or buffer gnus-article-buffer))
|
||||
(config (current-window-configuration));; need to add this - erik
|
||||
(subject (gnus-forward-make-subject buffer)))
|
||||
(setq mh-show-buffer buffer)
|
||||
(mh-find-path)
|
||||
(mh-send-sub to (or cc "") (or subject "(None)") config);; Erik Selberg 1/23/94
|
||||
(let ((draft (current-buffer))
|
||||
(gnus-mail-buffer (current-buffer))
|
||||
mail-buf)
|
||||
(gnus-configure-windows 'reply-yank 'force)
|
||||
(setq mail-buf (eval (cdr (assq 'mail gnus-window-to-buffer))))
|
||||
(pop-to-buffer mail-buf);; always in the display, so won't have window probs
|
||||
(switch-to-buffer draft)
|
||||
)
|
||||
(save-excursion
|
||||
(goto-char (point-max))
|
||||
(insert "\n------- Forwarded Message\n\n")
|
||||
(insert-buffer buffer)
|
||||
(goto-char (point-max))
|
||||
(insert "\n------- End of Forwarded Message\n")
|
||||
(setq mh-sent-from-folder buffer)
|
||||
(setq mh-sent-from-msg 1)
|
||||
(setq mh-previous-window-config config)
|
||||
(run-hooks 'gnus-mail-hook)
|
||||
)))
|
||||
|
||||
(defun gnus-mail-other-window-using-mhe ()
|
||||
"Compose mail other window using mh-e."
|
||||
(let ((to (read-string "To: "))
|
||||
(cc (read-string "Cc: "))
|
||||
(subject (read-string "Subject: ")))
|
||||
(gnus-article-show-all-headers) ;I don't think this is really needed.
|
||||
(setq mh-show-buffer (current-buffer))
|
||||
(mh-find-path)
|
||||
(mh-send-other-window to cc subject)
|
||||
(setq mh-sent-from-folder (current-buffer))
|
||||
(setq mh-sent-from-msg 1)
|
||||
(run-hooks 'gnus-mail-hook)))
|
||||
(widen)
|
||||
(unwind-protect
|
||||
(call-process-region
|
||||
(point-min) (point-max) "rcvstore" nil errbuf nil folder)
|
||||
(set-buffer errbuf)
|
||||
(if (zerop (buffer-size))
|
||||
(message "Article saved in folder: %s" folder)
|
||||
(message "%s" (buffer-string)))
|
||||
(kill-buffer errbuf))))
|
||||
(setq gnus-newsgroup-last-folder folder)))
|
||||
|
||||
(defun gnus-Folder-save-name (newsgroup headers &optional last-folder)
|
||||
"Generate folder name from NEWSGROUP, HEADERS, and optional LAST-FOLDER.
|
||||
@ -225,4 +100,6 @@ Otherwise, it is like +news/group."
|
||||
newsgroup
|
||||
(gnus-newsgroup-directory-form newsgroup)))))
|
||||
|
||||
(provide 'gnus-mh)
|
||||
|
||||
;;; gnus-mh.el ends here
|
||||
|
2238
lisp/gnus-msg.el
2238
lisp/gnus-msg.el
File diff suppressed because it is too large
Load Diff
1886
lisp/gnus-score.el
1886
lisp/gnus-score.el
File diff suppressed because it is too large
Load Diff
966
lisp/gnus-uu.el
966
lisp/gnus-uu.el
File diff suppressed because it is too large
Load Diff
1493
lisp/gnus-vis.el
1493
lisp/gnus-vis.el
File diff suppressed because it is too large
Load Diff
166
lisp/gnus-vm.el
166
lisp/gnus-vm.el
@ -1,6 +1,5 @@
|
||||
;;; gnus-vm.el --- vm interface for Gnus
|
||||
|
||||
;; Copyright (C) 1994,95 Free Software Foundation, Inc.
|
||||
;; Copyright (C) 1994,95,96 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Per Persson <pp@solace.mh.se>
|
||||
;; Keywords: news, mail
|
||||
@ -32,6 +31,7 @@
|
||||
;;; Code:
|
||||
|
||||
(require 'sendmail)
|
||||
(require 'message)
|
||||
(require 'gnus)
|
||||
(require 'gnus-msg)
|
||||
|
||||
@ -90,19 +90,13 @@ save those articles instead."
|
||||
(let ((default-name
|
||||
(funcall gnus-mail-save-name gnus-newsgroup-name
|
||||
gnus-current-headers gnus-newsgroup-last-mail)))
|
||||
(or folder
|
||||
(setq folder
|
||||
(read-file-name
|
||||
(concat "Save article in VM folder: (default "
|
||||
(file-name-nondirectory default-name) ") ")
|
||||
(file-name-directory default-name)
|
||||
default-name)))
|
||||
(setq folder
|
||||
(expand-file-name folder
|
||||
(and default-name
|
||||
(file-name-directory default-name))))
|
||||
(cond ((eq folder 'default) default-name)
|
||||
(folder folder)
|
||||
(t (gnus-read-save-file-name
|
||||
"Save article in VM folder:" default-name))))
|
||||
(gnus-make-directory (file-name-directory folder))
|
||||
(set-buffer gnus-article-buffer)
|
||||
(set-buffer gnus-original-article-buffer)
|
||||
(save-excursion
|
||||
(save-restriction
|
||||
(widen)
|
||||
@ -111,152 +105,6 @@ save those articles instead."
|
||||
(kill-buffer vm-folder))))
|
||||
;; Remember the directory name to save articles.
|
||||
(setq gnus-newsgroup-last-mail folder)))
|
||||
|
||||
(defun gnus-mail-forward-using-vm (&optional buffer)
|
||||
"Forward the current message to another user using vm."
|
||||
(let* ((gnus-buffer (or buffer (current-buffer)))
|
||||
(subject (gnus-forward-make-subject gnus-buffer)))
|
||||
(or (featurep 'win-vm)
|
||||
(if gnus-use-full-window
|
||||
(pop-to-buffer gnus-article-buffer)
|
||||
(switch-to-buffer gnus-article-buffer)))
|
||||
(gnus-copy-article-buffer)
|
||||
(set-buffer gnus-article-copy)
|
||||
(save-excursion
|
||||
(save-restriction
|
||||
(widen)
|
||||
(let ((vm-folder (gnus-vm-make-folder))
|
||||
(vm-forward-message-hook
|
||||
(append (symbol-value 'vm-forward-message-hook)
|
||||
'((lambda ()
|
||||
(save-excursion
|
||||
(mail-position-on-field "Subject")
|
||||
(beginning-of-line)
|
||||
(looking-at "^\\(Subject: \\).*$")
|
||||
(replace-match (concat "\\1" subject))))))))
|
||||
(vm-forward-message)
|
||||
(gnus-vm-init-reply-buffer gnus-buffer)
|
||||
(run-hooks 'gnus-mail-hook)
|
||||
(kill-buffer vm-folder))))))
|
||||
|
||||
(defun gnus-vm-init-reply-buffer (buffer)
|
||||
(make-local-variable 'gnus-summary-buffer)
|
||||
(setq gnus-summary-buffer buffer)
|
||||
(set 'vm-mail-buffer nil)
|
||||
(use-local-map (copy-keymap (current-local-map)))
|
||||
(local-set-key "\C-c\C-y" 'gnus-yank-article))
|
||||
|
||||
(defun gnus-mail-reply-using-vm (&optional yank)
|
||||
"Compose reply mail using vm.
|
||||
Optional argument YANK means yank original article.
|
||||
The command \\[vm-yank-message] yank the original message into current buffer."
|
||||
(let ((gnus-buffer (current-buffer)))
|
||||
(gnus-copy-article-buffer)
|
||||
(set-buffer gnus-article-copy)
|
||||
(save-excursion
|
||||
(save-restriction
|
||||
(widen)
|
||||
(let ((vm-folder (gnus-vm-make-folder gnus-article-copy)))
|
||||
(vm-reply 1)
|
||||
(gnus-vm-init-reply-buffer gnus-buffer)
|
||||
(setq gnus-buffer (current-buffer))
|
||||
(and yank
|
||||
;; nil will (magically :-)) yank the current article
|
||||
(gnus-yank-article nil))
|
||||
(kill-buffer vm-folder))))
|
||||
(if (featurep 'win-vm) nil
|
||||
(pop-to-buffer gnus-buffer))
|
||||
(run-hooks 'gnus-mail-hook)))
|
||||
|
||||
(defun gnus-mail-other-window-using-vm ()
|
||||
"Compose mail in the other window using VM."
|
||||
(interactive)
|
||||
(let ((gnus-buffer (current-buffer)))
|
||||
(vm-mail)
|
||||
(gnus-vm-init-reply-buffer gnus-buffer))
|
||||
(run-hooks 'gnus-mail-hook))
|
||||
|
||||
(defun gnus-yank-article (article &optional prefix)
|
||||
;; Based on vm-yank-message by Kyle Jones.
|
||||
"Yank article number N into the current buffer at point.
|
||||
When called interactively N is read from the minibuffer.
|
||||
|
||||
This command is meant to be used in GNUS created Mail mode buffers;
|
||||
the yanked article comes from the newsgroup containing the article
|
||||
you are replying to or forwarding.
|
||||
|
||||
All article headers are yanked along with the text. Point is left
|
||||
before the inserted text, the mark after. Any hook functions bound to
|
||||
`mail-citation-hook' are run, after inserting the text and setting
|
||||
point and mark.
|
||||
|
||||
Prefix arg means to ignore `mail-citation-hook', don't set the mark,
|
||||
prepend the value of `vm-included-text-prefix' to every yanked line.
|
||||
For backwards compatibility, if `mail-citation-hook' is set to nil,
|
||||
`mail-yank-hooks' is run instead. If that is also nil, a default
|
||||
action is taken."
|
||||
(interactive
|
||||
(list
|
||||
(let ((result 0)
|
||||
default prompt)
|
||||
(setq default (and gnus-summary-buffer
|
||||
(save-excursion
|
||||
(set-buffer gnus-summary-buffer)
|
||||
(and gnus-current-article
|
||||
(int-to-string gnus-current-article))))
|
||||
prompt (if default
|
||||
(format "Yank article number: (default %s) " default)
|
||||
"Yank article number: "))
|
||||
(while (and (not (stringp result)) (zerop result))
|
||||
(setq result (read-string prompt))
|
||||
(and (string= result "") default (setq result default))
|
||||
(or (string-match "^<.*>$" result)
|
||||
(setq result (string-to-int result))))
|
||||
result)
|
||||
current-prefix-arg))
|
||||
(if gnus-summary-buffer
|
||||
(save-excursion
|
||||
(let ((message (current-buffer))
|
||||
(start (point)) end
|
||||
(tmp (generate-new-buffer " *tmp-yank*")))
|
||||
(set-buffer gnus-summary-buffer)
|
||||
;; Make sure the connection to the server is alive.
|
||||
(or (gnus-server-opened (gnus-find-method-for-group
|
||||
gnus-newsgroup-name))
|
||||
(progn
|
||||
(gnus-check-server
|
||||
(gnus-find-method-for-group gnus-newsgroup-name))
|
||||
(gnus-request-group gnus-newsgroup-name t)))
|
||||
(and (stringp article)
|
||||
(let ((gnus-override-method gnus-refer-article-method))
|
||||
(gnus-read-header article)))
|
||||
(gnus-request-article (or article
|
||||
gnus-current-article)
|
||||
gnus-newsgroup-name tmp)
|
||||
(set-buffer tmp)
|
||||
(run-hooks 'gnus-article-prepare-hook)
|
||||
;; Decode MIME message.
|
||||
(if (and gnus-show-mime
|
||||
(gnus-fetch-field "Mime-Version"))
|
||||
(funcall gnus-show-mime-method))
|
||||
;; Perform the article display hooks.
|
||||
(let ((buffer-read-only nil))
|
||||
(run-hooks 'gnus-article-display-hook))
|
||||
(append-to-buffer message (point-min) (point-max))
|
||||
(kill-buffer tmp)
|
||||
(set-buffer message)
|
||||
(setq end (point))
|
||||
(goto-char start)
|
||||
(if (or prefix
|
||||
(not (or mail-citation-hook mail-yank-hooks)))
|
||||
(save-excursion
|
||||
(while (< (point) end)
|
||||
(insert (symbol-value 'vm-included-text-prefix))
|
||||
(forward-line 1)))
|
||||
(push-mark end)
|
||||
(cond
|
||||
(mail-citation-hook (run-hooks 'mail-citation-hook))
|
||||
(mail-yank-hooks (run-hooks 'mail-yank-hooks))))))))
|
||||
|
||||
(provide 'gnus-vm)
|
||||
|
||||
|
18045
lisp/gnus.el
18045
lisp/gnus.el
File diff suppressed because it is too large
Load Diff
152
lisp/nndir.el
152
lisp/nndir.el
@ -1,9 +1,7 @@
|
||||
;;; nndir.el --- single directory newsgroup access for Gnus
|
||||
|
||||
;; Copyright (C) 1995 Free Software Foundation, Inc.
|
||||
;; Copyright (C) 1995,96 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
|
||||
;; Masanobu UMEDA <umerin@flab.flab.fujitsu.junet>
|
||||
;; Keywords: news
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
@ -30,113 +28,71 @@
|
||||
(require 'nnheader)
|
||||
(require 'nnmh)
|
||||
(require 'nnml)
|
||||
(require 'nnoo)
|
||||
(eval-when-compile (require 'cl))
|
||||
|
||||
(eval-and-compile
|
||||
(autoload 'mail-send-and-exit "sendmail"))
|
||||
(nnoo-declare nndir
|
||||
nnml nnmh)
|
||||
|
||||
(defvoo nndir-directory nil
|
||||
"Where nndir will look for groups."
|
||||
nnml-current-directory nnmh-current-directory)
|
||||
|
||||
(defvoo nndir-nov-is-evil nil
|
||||
"*Non-nil means that nndir will never retrieve NOV headers."
|
||||
nnml-nov-is-evil)
|
||||
|
||||
|
||||
|
||||
(defvoo nndir-current-group "" nil nnml-current-group nnmh-current-group)
|
||||
(defvoo nndir-top-directory nil nil nnml-directory nnmh-directory)
|
||||
(defvoo nndir-get-new-mail nil nil nnml-get-new-mail nnmh-get-new-mail)
|
||||
|
||||
(defvoo nndir-status-string "" nil nnmh-status-string)
|
||||
(defconst nndir-version "nndir 1.0")
|
||||
|
||||
(defvar nndir-current-directory nil
|
||||
"Current news group directory.")
|
||||
|
||||
(defvar nndir-status-string "")
|
||||
|
||||
(defvar nndir-nov-is-evil nil
|
||||
"*Non-nil means that nndir will never retrieve NOV headers.")
|
||||
|
||||
|
||||
|
||||
;;; Interface functions.
|
||||
|
||||
(nnoo-define-basics nndir)
|
||||
|
||||
(defun nndir-retrieve-headers (sequence &optional newsgroup server)
|
||||
(nndir-execute-nnml-command
|
||||
'(nnml-retrieve-headers sequence group server) server))
|
||||
(deffoo nndir-open-server (server &optional defs)
|
||||
(setq nndir-directory
|
||||
(or (cadr (assq 'nndir-directory defs))
|
||||
server))
|
||||
(unless (assq 'nndir-directory defs)
|
||||
(push `(nndir-directory ,server) defs))
|
||||
(push `(nndir-current-group
|
||||
,(file-name-nondirectory (directory-file-name nndir-directory)))
|
||||
defs)
|
||||
(push `(nndir-top-directory
|
||||
,(file-name-directory (directory-file-name nndir-directory)))
|
||||
defs)
|
||||
(nnoo-change-server 'nndir server defs)
|
||||
(let (err)
|
||||
(cond
|
||||
((not (condition-case arg
|
||||
(file-exists-p nndir-directory)
|
||||
(ftp-error (setq err (format "%s" arg)))))
|
||||
(nndir-close-server)
|
||||
(nnheader-report
|
||||
'nndir (or err "No such file or directory: %s" nndir-directory)))
|
||||
((not (file-directory-p (file-truename nndir-directory)))
|
||||
(nndir-close-server)
|
||||
(nnheader-report 'nndir "Not a directory: %s" nndir-directory))
|
||||
(t
|
||||
(nnheader-report 'nndir "Opened server %s using directory %s"
|
||||
server nndir-directory)
|
||||
t))))
|
||||
|
||||
(defun nndir-open-server (host &optional service)
|
||||
"Open nndir backend."
|
||||
(setq nndir-status-string "")
|
||||
(nnheader-init-server-buffer))
|
||||
|
||||
(defun nndir-close-server (&optional server)
|
||||
"Close news server."
|
||||
t)
|
||||
|
||||
(defun nndir-server-opened (&optional server)
|
||||
"Return server process status, T or NIL.
|
||||
If the stream is opened, return T, otherwise return NIL."
|
||||
(and nntp-server-buffer
|
||||
(get-buffer nntp-server-buffer)))
|
||||
|
||||
(defun nndir-status-message (&optional server)
|
||||
"Return server status response as string."
|
||||
nndir-status-string)
|
||||
|
||||
(defun nndir-request-article (id &optional newsgroup server buffer)
|
||||
(nndir-execute-nnmh-command
|
||||
'(nnmh-request-article id group server buffer) server))
|
||||
|
||||
(defun nndir-request-group (group &optional server dont-check)
|
||||
"Select news GROUP."
|
||||
(nndir-execute-nnmh-command
|
||||
'(nnmh-request-group group "" dont-check) server))
|
||||
|
||||
(defun nndir-request-list (&optional server dir)
|
||||
"Get list of active articles in all newsgroups."
|
||||
(nndir-execute-nnmh-command
|
||||
'(nnmh-request-list nil dir) server))
|
||||
|
||||
(defun nndir-request-newgroups (date &optional server)
|
||||
(nndir-execute-nnmh-command
|
||||
'(nnmh-request-newgroups date server) server))
|
||||
|
||||
(defun nndir-request-post (&optional server)
|
||||
"Post a new news in current buffer."
|
||||
(mail-send-and-exit nil))
|
||||
|
||||
(defalias 'nndir-request-post-buffer 'nnmail-request-post-buffer)
|
||||
|
||||
(defun nndir-request-expire-articles (articles newsgroup &optional server force)
|
||||
"Expire all articles in the ARTICLES list in group GROUP."
|
||||
(setq nndir-status-string "nndir: expire not possible")
|
||||
nil)
|
||||
|
||||
(defun nndir-close-group (group &optional server)
|
||||
t)
|
||||
|
||||
(defun nndir-request-move-article (article group server accept-form)
|
||||
(setq nndir-status-string "nndir: move not possible")
|
||||
nil)
|
||||
|
||||
(defun nndir-request-accept-article (group)
|
||||
(setq nndir-status-string "nndir: accept not possible")
|
||||
nil)
|
||||
|
||||
|
||||
;;; Low-Level Interface
|
||||
|
||||
(defun nndir-execute-nnmh-command (command server)
|
||||
(let ((dir (expand-file-name server)))
|
||||
(and (string-match "/$" dir)
|
||||
(setq dir (substring dir 0 (match-beginning 0))))
|
||||
(string-match "/[^/]+$" dir)
|
||||
(let ((group (substring dir (1+ (match-beginning 0))))
|
||||
(nnmh-directory (substring dir 0 (1+ (match-beginning 0))))
|
||||
(nnmh-get-new-mail nil))
|
||||
(eval command))))
|
||||
|
||||
(defun nndir-execute-nnml-command (command server)
|
||||
(let ((dir (expand-file-name server)))
|
||||
(and (string-match "/$" dir)
|
||||
(setq dir (substring dir 0 (match-beginning 0))))
|
||||
(string-match "/[^/]+$" dir)
|
||||
(let ((group (substring dir (1+ (match-beginning 0))))
|
||||
(nnml-directory (substring dir 0 (1+ (match-beginning 0))))
|
||||
(nnml-nov-is-evil nndir-nov-is-evil)
|
||||
(nnml-get-new-mail nil))
|
||||
(eval command))))
|
||||
(nnoo-map-functions nndir
|
||||
(nnml-retrieve-headers 0 nndir-current-group 0 0)
|
||||
(nnmh-request-article 0 nndir-current-group 0 0)
|
||||
(nnmh-request-group nndir-current-group 0 0)
|
||||
(nnmh-close-group nndir-current-group 0)
|
||||
(nnmh-request-list (nnoo-current-server 'nndir) nndir-directory)
|
||||
(nnmh-request-newsgroups (nnoo-current-server 'nndir) nndir-directory))
|
||||
|
||||
(provide 'nndir)
|
||||
|
||||
|
660
lisp/nndoc.el
660
lisp/nndoc.el
@ -1,6 +1,5 @@
|
||||
;;; nndoc.el --- single file access for Gnus
|
||||
|
||||
;; Copyright (C) 1995 Free Software Foundation, Inc.
|
||||
;; Copyright (C) 1995,96 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
|
||||
;; Masanobu UMEDA <umerin@flab.flab.fujitsu.junet>
|
||||
@ -28,239 +27,203 @@
|
||||
;;; Code:
|
||||
|
||||
(require 'nnheader)
|
||||
(require 'rmail)
|
||||
(require 'message)
|
||||
(require 'nnmail)
|
||||
(require 'nnoo)
|
||||
(eval-when-compile (require 'cl))
|
||||
|
||||
(defvar nndoc-article-type 'mbox
|
||||
"*Type of the file - one of `mbox', `babyl' or `digest'.")
|
||||
(nnoo-declare nndoc)
|
||||
|
||||
(defvar nndoc-digest-type 'traditional
|
||||
"Type of the last digest. Auto-detected from the article header.
|
||||
Possible values:
|
||||
`traditional' -- the \"lots of dashes\" (30+) rules used;
|
||||
we currently also do unconditional RFC 934 unquoting.
|
||||
`rfc1341' -- RFC 1341 digest (MIME, unique boundary, no quoting).")
|
||||
(defvoo nndoc-article-type 'guess
|
||||
"*Type of the file.
|
||||
One of `mbox', `babyl', `digest', `news', `rnews', `mmdf', `forward',
|
||||
`mime-digest', `standard-digest', `slack-digest', `clari-briefs' or
|
||||
`guess'.")
|
||||
|
||||
(defconst nndoc-type-to-regexp
|
||||
(list (list 'mbox
|
||||
(concat "^" rmail-unix-mail-delimiter)
|
||||
(concat "^" rmail-unix-mail-delimiter)
|
||||
nil "^$" nil nil nil)
|
||||
(list 'babyl "\^_\^L *\n" "\^_" "^[0-9].*\n" "^$" nil nil
|
||||
"\\*\\*\\* EOOH \\*\\*\\*\n\\(^.+\n\\)*")
|
||||
(list 'digest
|
||||
"^------------------------------*[\n \t]+"
|
||||
"^------------------------------*[\n \t]+"
|
||||
nil "^ ?$"
|
||||
"^------------------------------*[\n \t]+"
|
||||
"^End of" nil))
|
||||
"Regular expressions for articles of the various types.")
|
||||
(defvoo nndoc-post-type 'mail
|
||||
"*Whether the nndoc group is `mail' or `post'.")
|
||||
|
||||
(defvar nndoc-type-alist
|
||||
`((mmdf
|
||||
(article-begin . "^\^A\^A\^A\^A\n")
|
||||
(body-end . "^\^A\^A\^A\^A\n"))
|
||||
(news
|
||||
(article-begin . "^Path:"))
|
||||
(rnews
|
||||
(article-begin . "^#! *rnews +\\([0-9]+\\) *\n")
|
||||
(body-end-function . nndoc-rnews-body-end))
|
||||
(mbox
|
||||
(article-begin .
|
||||
,(let ((delim (concat "^" message-unix-mail-delimiter)))
|
||||
(if (string-match "\n\\'" delim)
|
||||
(substring delim 0 (match-beginning 0))
|
||||
delim)))
|
||||
(body-end-function . nndoc-mbox-body-end))
|
||||
(babyl
|
||||
(article-begin . "\^_\^L *\n")
|
||||
(body-end . "\^_")
|
||||
(body-begin-function . nndoc-babyl-body-begin)
|
||||
(head-begin-function . nndoc-babyl-head-begin))
|
||||
(forward
|
||||
(article-begin . "^-+ Start of forwarded message -+\n+")
|
||||
(body-end . "^-+ End of forwarded message -+$")
|
||||
(prepare-body . nndoc-unquote-dashes))
|
||||
(clari-briefs
|
||||
(article-begin . "^ \\*")
|
||||
(body-end . "^\t------*[ \t]^*\n^ \\*")
|
||||
(body-begin . "^\t")
|
||||
(head-end . "^\t")
|
||||
(generate-head . nndoc-generate-clari-briefs-head)
|
||||
(article-transform . nndoc-transform-clari-briefs))
|
||||
(slack-digest
|
||||
(article-begin . "^------------------------------*[\n \t]+")
|
||||
(head-end . "^ ?$")
|
||||
(body-end-function . nndoc-digest-body-end)
|
||||
(body-begin . "^ ?$")
|
||||
(file-end . "^End of")
|
||||
(prepare-body . nndoc-unquote-dashes))
|
||||
(mime-digest
|
||||
(article-begin . "")
|
||||
(head-end . "^ ?$")
|
||||
(body-end . "")
|
||||
(file-end . ""))
|
||||
(standard-digest
|
||||
(first-article . ,(concat "^" (make-string 70 ?-) "\n\n+"))
|
||||
(article-begin . ,(concat "\n\n" (make-string 30 ?-) "\n\n+"))
|
||||
(prepare-body . nndoc-unquote-dashes)
|
||||
(body-end-function . nndoc-digest-body-end)
|
||||
(head-end . "^ ?$")
|
||||
(body-begin . "^ ?\n")
|
||||
(file-end . "^End of .*digest.*[0-9].*\n\\*\\*\\|^End of.*Digest *$"))
|
||||
(guess
|
||||
(guess . nndoc-guess-type))
|
||||
(digest
|
||||
(guess . nndoc-guess-digest-type))
|
||||
))
|
||||
|
||||
|
||||
|
||||
(defvar nndoc-article-begin nil)
|
||||
(defvar nndoc-article-end nil)
|
||||
(defvar nndoc-head-begin nil)
|
||||
(defvar nndoc-head-end nil)
|
||||
(defvar nndoc-first-article nil)
|
||||
(defvar nndoc-end-of-file nil)
|
||||
(defvar nndoc-body-begin nil)
|
||||
(defvoo nndoc-file-begin nil)
|
||||
(defvoo nndoc-first-article nil)
|
||||
(defvoo nndoc-article-end nil)
|
||||
(defvoo nndoc-article-begin nil)
|
||||
(defvoo nndoc-head-begin nil)
|
||||
(defvoo nndoc-head-end nil)
|
||||
(defvoo nndoc-file-end nil)
|
||||
(defvoo nndoc-body-begin nil)
|
||||
(defvoo nndoc-body-end-function nil)
|
||||
(defvoo nndoc-body-begin-function nil)
|
||||
(defvoo nndoc-head-begin-function nil)
|
||||
(defvoo nndoc-body-end nil)
|
||||
(defvoo nndoc-dissection-alist nil)
|
||||
(defvoo nndoc-prepare-body nil)
|
||||
(defvoo nndoc-generate-head nil)
|
||||
(defvoo nndoc-article-transform nil)
|
||||
|
||||
(defvar nndoc-current-server nil)
|
||||
(defvar nndoc-server-alist nil)
|
||||
(defvar nndoc-server-variables
|
||||
(list
|
||||
(list 'nndoc-article-type nndoc-article-type)
|
||||
'(nndoc-article-begin nil)
|
||||
'(nndoc-article-end nil)
|
||||
'(nndoc-head-begin nil)
|
||||
'(nndoc-head-end nil)
|
||||
'(nndoc-first-article nil)
|
||||
'(nndoc-current-buffer nil)
|
||||
'(nndoc-group-alist nil)
|
||||
'(nndoc-end-of-file nil)
|
||||
'(nndoc-body-begin nil)
|
||||
'(nndoc-address nil)))
|
||||
(defvoo nndoc-status-string "")
|
||||
(defvoo nndoc-group-alist nil)
|
||||
(defvoo nndoc-current-buffer nil
|
||||
"Current nndoc news buffer.")
|
||||
(defvoo nndoc-address nil)
|
||||
|
||||
(defconst nndoc-version "nndoc 1.0"
|
||||
"nndoc version.")
|
||||
|
||||
(defvar nndoc-current-buffer nil
|
||||
"Current nndoc news buffer.")
|
||||
|
||||
(defvar nndoc-address nil)
|
||||
|
||||
|
||||
|
||||
(defvar nndoc-status-string "")
|
||||
|
||||
(defvar nndoc-group-alist nil)
|
||||
|
||||
;;; Interface functions
|
||||
|
||||
(defun nndoc-retrieve-headers (sequence &optional newsgroup server)
|
||||
(save-excursion
|
||||
(set-buffer nntp-server-buffer)
|
||||
(erase-buffer)
|
||||
(let ((prev 2)
|
||||
article p beg lines)
|
||||
(nndoc-possibly-change-buffer newsgroup server)
|
||||
(if (stringp (car sequence))
|
||||
'headers
|
||||
(set-buffer nndoc-current-buffer)
|
||||
(widen)
|
||||
(goto-char (point-min))
|
||||
(re-search-forward (or nndoc-first-article
|
||||
nndoc-article-begin) nil t)
|
||||
(or (not nndoc-head-begin)
|
||||
(re-search-forward nndoc-head-begin nil t))
|
||||
(re-search-forward nndoc-head-end nil t)
|
||||
(while sequence
|
||||
(setq article (car sequence))
|
||||
(set-buffer nndoc-current-buffer)
|
||||
(if (not (nndoc-forward-article (max 0 (- article prev))))
|
||||
()
|
||||
(setq p (point))
|
||||
(setq beg (or (and
|
||||
(re-search-backward nndoc-article-begin nil t)
|
||||
(match-end 0))
|
||||
(point-min)))
|
||||
(goto-char p)
|
||||
(setq lines (count-lines
|
||||
(point)
|
||||
(or
|
||||
(and (re-search-forward nndoc-article-end nil t)
|
||||
(goto-char (match-beginning 0)))
|
||||
(goto-char (point-max)))))
|
||||
(nnoo-define-basics nndoc)
|
||||
|
||||
(set-buffer nntp-server-buffer)
|
||||
(insert (format "221 %d Article retrieved.\n" article))
|
||||
(insert-buffer-substring nndoc-current-buffer beg p)
|
||||
(goto-char (point-max))
|
||||
(or (= (char-after (1- (point))) ?\n) (insert "\n"))
|
||||
(insert (format "Lines: %d\n" lines))
|
||||
(insert ".\n"))
|
||||
(deffoo nndoc-retrieve-headers (articles &optional newsgroup server fetch-old)
|
||||
(when (nndoc-possibly-change-buffer newsgroup server)
|
||||
(save-excursion
|
||||
(set-buffer nntp-server-buffer)
|
||||
(erase-buffer)
|
||||
(let (article entry)
|
||||
(if (stringp (car articles))
|
||||
'headers
|
||||
(while articles
|
||||
(when (setq entry (cdr (assq (setq article (pop articles))
|
||||
nndoc-dissection-alist)))
|
||||
(insert (format "221 %d Article retrieved.\n" article))
|
||||
(if nndoc-generate-head
|
||||
(funcall nndoc-generate-head article)
|
||||
(insert-buffer-substring
|
||||
nndoc-current-buffer (car entry) (nth 1 entry)))
|
||||
(goto-char (point-max))
|
||||
(or (= (char-after (1- (point))) ?\n) (insert "\n"))
|
||||
(insert (format "Lines: %d\n" (nth 4 entry)))
|
||||
(insert ".\n")))
|
||||
|
||||
(setq prev article
|
||||
sequence (cdr sequence)))
|
||||
(nnheader-fold-continuation-lines)
|
||||
'headers)))))
|
||||
|
||||
;; Fold continuation lines.
|
||||
(set-buffer nntp-server-buffer)
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward "\\(\r?\n[ \t]+\\)+" nil t)
|
||||
(replace-match " " t t))
|
||||
'headers))))
|
||||
|
||||
(defun nndoc-open-server (server &optional defs)
|
||||
(nnheader-init-server-buffer)
|
||||
(if (equal server nndoc-current-server)
|
||||
t
|
||||
(if nndoc-current-server
|
||||
(setq nndoc-server-alist
|
||||
(cons (list nndoc-current-server
|
||||
(nnheader-save-variables nndoc-server-variables))
|
||||
nndoc-server-alist)))
|
||||
(let ((state (assoc server nndoc-server-alist)))
|
||||
(if state
|
||||
(progn
|
||||
(nnheader-restore-variables (nth 1 state))
|
||||
(setq nndoc-server-alist (delq state nndoc-server-alist)))
|
||||
(nnheader-set-init-variables nndoc-server-variables defs)))
|
||||
(setq nndoc-current-server server)
|
||||
(let ((defs (cdr (assq nndoc-article-type nndoc-type-to-regexp))))
|
||||
(setq nndoc-article-begin (nth 0 defs))
|
||||
(setq nndoc-article-end (nth 1 defs))
|
||||
(setq nndoc-head-begin (nth 2 defs))
|
||||
(setq nndoc-head-end (nth 3 defs))
|
||||
(setq nndoc-first-article (nth 4 defs))
|
||||
(setq nndoc-end-of-file (nth 5 defs))
|
||||
(setq nndoc-body-begin (nth 6 defs)))
|
||||
t))
|
||||
|
||||
(defun nndoc-close-server (&optional server)
|
||||
t)
|
||||
|
||||
(defun nndoc-server-opened (&optional server)
|
||||
(and (equal server nndoc-current-server)
|
||||
nntp-server-buffer
|
||||
(buffer-name nntp-server-buffer)))
|
||||
|
||||
(defun nndoc-status-message (&optional server)
|
||||
nndoc-status-string)
|
||||
|
||||
(defun nndoc-request-article (article &optional newsgroup server buffer)
|
||||
(deffoo nndoc-request-article (article &optional newsgroup server buffer)
|
||||
(nndoc-possibly-change-buffer newsgroup server)
|
||||
(save-excursion
|
||||
(let ((buffer (or buffer nntp-server-buffer)))
|
||||
(let ((buffer (or buffer nntp-server-buffer))
|
||||
(entry (cdr (assq article nndoc-dissection-alist)))
|
||||
beg)
|
||||
(set-buffer buffer)
|
||||
(erase-buffer)
|
||||
(if (stringp article)
|
||||
nil
|
||||
(nndoc-insert-article article)
|
||||
;; Unquote quoted non-separators in digests.
|
||||
(if (and (eq nndoc-article-type 'digest)
|
||||
(eq nndoc-digest-type 'traditional))
|
||||
(progn
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward "^- -"nil t)
|
||||
(replace-match "-" t t))))
|
||||
;; Some assholish digests do not have a blank line after the
|
||||
;; headers. Aargh!
|
||||
(goto-char (point-min))
|
||||
(if (search-forward "\n\n" nil t)
|
||||
() ; We let this one pass.
|
||||
(if (re-search-forward "^[ \t]+$" nil t)
|
||||
(replace-match "" t t) ; We nix out a line of blanks.
|
||||
(while (and (looking-at "[^ ]+:")
|
||||
(zerop (forward-line 1))))
|
||||
;; We just insert a couple of lines. If you read digests
|
||||
;; that are so badly formatted, you don't deserve any
|
||||
;; better. Blphphpht!
|
||||
(insert "\n\n")))
|
||||
(insert-buffer-substring
|
||||
nndoc-current-buffer (car entry) (nth 1 entry))
|
||||
(insert "\n")
|
||||
(setq beg (point))
|
||||
(insert-buffer-substring
|
||||
nndoc-current-buffer (nth 2 entry) (nth 3 entry))
|
||||
(goto-char beg)
|
||||
(when nndoc-prepare-body
|
||||
(funcall nndoc-prepare-body))
|
||||
(when nndoc-article-transform
|
||||
(funcall nndoc-article-transform article))
|
||||
t))))
|
||||
|
||||
(defun nndoc-request-group (group &optional server dont-check)
|
||||
(deffoo nndoc-request-group (group &optional server dont-check)
|
||||
"Select news GROUP."
|
||||
(save-excursion
|
||||
(if (not (nndoc-possibly-change-buffer group server))
|
||||
(progn
|
||||
(setq nndoc-status-string "No such file or buffer")
|
||||
nil)
|
||||
(nndoc-set-header-dependent-regexps) ; hack for MIME digests
|
||||
(if dont-check
|
||||
t
|
||||
(save-excursion
|
||||
(set-buffer nntp-server-buffer)
|
||||
(erase-buffer)
|
||||
(let ((number (nndoc-number-of-articles)))
|
||||
(if (zerop number)
|
||||
(progn
|
||||
(nndoc-close-group group)
|
||||
nil)
|
||||
(insert (format "211 %d %d %d %s\n" number 1 number group))
|
||||
t)))))))
|
||||
(let (number)
|
||||
(cond
|
||||
((not (nndoc-possibly-change-buffer group server))
|
||||
(nnheader-report 'nndoc "No such file or buffer: %s"
|
||||
nndoc-address))
|
||||
(dont-check
|
||||
(nnheader-report 'nndoc "Selected group %s" group)
|
||||
t)
|
||||
((zerop (setq number (length nndoc-dissection-alist)))
|
||||
(nndoc-close-group group)
|
||||
(nnheader-report 'nndoc "No articles in group %s" group))
|
||||
(t
|
||||
(nnheader-insert "211 %d %d %d %s\n" number 1 number group)))))
|
||||
|
||||
(defun nndoc-close-group (group &optional server)
|
||||
(deffoo nndoc-request-type (group &optional article)
|
||||
(cond ((not article) 'unknown)
|
||||
(nndoc-post-type nndoc-post-type)
|
||||
(t 'unknown)))
|
||||
|
||||
(deffoo nndoc-close-group (group &optional server)
|
||||
(nndoc-possibly-change-buffer group server)
|
||||
(kill-buffer nndoc-current-buffer)
|
||||
(and nndoc-current-buffer
|
||||
(buffer-name nndoc-current-buffer)
|
||||
(kill-buffer nndoc-current-buffer))
|
||||
(setq nndoc-group-alist (delq (assoc group nndoc-group-alist)
|
||||
nndoc-group-alist))
|
||||
(setq nndoc-current-buffer nil)
|
||||
(setq nndoc-current-server nil)
|
||||
(nnoo-close-server 'nndoc server)
|
||||
(setq nndoc-dissection-alist nil)
|
||||
t)
|
||||
|
||||
(defun nndoc-request-list (&optional server)
|
||||
(deffoo nndoc-request-list (&optional server)
|
||||
nil)
|
||||
|
||||
(defun nndoc-request-newgroups (date &optional server)
|
||||
(deffoo nndoc-request-newgroups (date &optional server)
|
||||
nil)
|
||||
|
||||
(defun nndoc-request-list-newsgroups (&optional server)
|
||||
(deffoo nndoc-request-list-newsgroups (&optional server)
|
||||
nil)
|
||||
|
||||
(defalias 'nndoc-request-post 'nnmail-request-post)
|
||||
(defalias 'nndoc-request-post-buffer 'nnmail-request-post-buffer)
|
||||
|
||||
|
||||
;;; Internal functions.
|
||||
|
||||
@ -269,6 +232,7 @@ Possible values:
|
||||
(cond
|
||||
;; The current buffer is this group's buffer.
|
||||
((and nndoc-current-buffer
|
||||
(buffer-name nndoc-current-buffer)
|
||||
(eq nndoc-current-buffer
|
||||
(setq buf (cdr (assoc group nndoc-group-alist))))))
|
||||
;; We change buffers by taking an old from the group alist.
|
||||
@ -281,121 +245,231 @@ Possible values:
|
||||
(and (stringp nndoc-address)
|
||||
(file-exists-p nndoc-address)
|
||||
(not (file-directory-p nndoc-address))))
|
||||
(setq nndoc-group-alist
|
||||
(cons (cons group (setq nndoc-current-buffer
|
||||
(get-buffer-create
|
||||
(concat " *nndoc " group "*"))))
|
||||
nndoc-group-alist))
|
||||
(push (cons group (setq nndoc-current-buffer
|
||||
(get-buffer-create
|
||||
(concat " *nndoc " group "*"))))
|
||||
nndoc-group-alist)
|
||||
(setq nndoc-dissection-alist nil)
|
||||
(save-excursion
|
||||
(set-buffer nndoc-current-buffer)
|
||||
(buffer-disable-undo (current-buffer))
|
||||
(erase-buffer)
|
||||
(if (stringp nndoc-address)
|
||||
(insert-file-contents nndoc-address)
|
||||
(save-excursion
|
||||
(set-buffer nndoc-address)
|
||||
(widen))
|
||||
(insert-buffer-substring nndoc-address))
|
||||
t)))))
|
||||
|
||||
;; MIME (RFC 1341) digest hack by Ulrik Dickow <dickow@nbi.dk>.
|
||||
(defun nndoc-set-header-dependent-regexps ()
|
||||
(if (not (eq nndoc-article-type 'digest))
|
||||
()
|
||||
(let ((case-fold-search t) ; We match a bit too much, keep it simple.
|
||||
(boundary-id) (b-delimiter))
|
||||
(insert-buffer-substring nndoc-address)))))
|
||||
;; Initialize the nndoc structures according to this new document.
|
||||
(when (and nndoc-current-buffer
|
||||
(not nndoc-dissection-alist))
|
||||
(save-excursion
|
||||
(set-buffer nndoc-current-buffer)
|
||||
(goto-char (point-min))
|
||||
(if (and
|
||||
(re-search-forward
|
||||
(concat "\n\n\\|^Content-Type: *multipart/digest;[ \t\n]*[ \t]"
|
||||
"boundary=\"\\([^\"\n]*[^\" \t\n]\\)\"")
|
||||
nil t)
|
||||
(match-beginning 1))
|
||||
(setq nndoc-digest-type 'rfc1341
|
||||
boundary-id (format "%s"
|
||||
(buffer-substring
|
||||
(match-beginning 1) (match-end 1)))
|
||||
b-delimiter (concat "\n--" boundary-id "[\n \t]+")
|
||||
nndoc-article-begin b-delimiter ; Too strict: "[ \t]*$"
|
||||
nndoc-article-end (concat "\n--" boundary-id
|
||||
"\\(--\\)?[\n \t]+")
|
||||
nndoc-first-article b-delimiter ; ^eof ends article too.
|
||||
nndoc-end-of-file (concat "\n--" boundary-id "--[ \t]*$"))
|
||||
(setq nndoc-digest-type 'traditional))))))
|
||||
(nndoc-set-delims)
|
||||
(nndoc-dissect-buffer)))
|
||||
(unless nndoc-current-buffer
|
||||
(nndoc-close-server))
|
||||
;; Return whether we managed to select a file.
|
||||
nndoc-current-buffer))
|
||||
|
||||
(defun nndoc-forward-article (n)
|
||||
(while (and (> n 0)
|
||||
(re-search-forward nndoc-article-begin nil t)
|
||||
(or (not nndoc-head-begin)
|
||||
(re-search-forward nndoc-head-begin nil t))
|
||||
(re-search-forward nndoc-head-end nil t))
|
||||
(setq n (1- n)))
|
||||
(zerop n))
|
||||
|
||||
(defun nndoc-number-of-articles ()
|
||||
(save-excursion
|
||||
(set-buffer nndoc-current-buffer)
|
||||
(widen)
|
||||
;; MIME (RFC 1341) digest hack by Ulrik Dickow <dickow@nbi.dk>.
|
||||
(defun nndoc-guess-digest-type ()
|
||||
"Guess what digest type the current document is."
|
||||
(let ((case-fold-search t) ; We match a bit too much, keep it simple.
|
||||
boundary-id b-delimiter entry)
|
||||
(goto-char (point-min))
|
||||
(let ((num 0))
|
||||
(if (re-search-forward (or nndoc-first-article
|
||||
nndoc-article-begin) nil t)
|
||||
(progn
|
||||
(setq num 1)
|
||||
(while (and (re-search-forward nndoc-article-begin nil t)
|
||||
(or (not nndoc-end-of-file)
|
||||
(not (looking-at nndoc-end-of-file)))
|
||||
(or (not nndoc-head-begin)
|
||||
(re-search-forward nndoc-head-begin nil t))
|
||||
(re-search-forward nndoc-head-end nil t))
|
||||
(setq num (1+ num)))))
|
||||
num)))
|
||||
(cond
|
||||
;; MIME digest.
|
||||
((and
|
||||
(re-search-forward
|
||||
(concat "^Content-Type: *multipart/digest;[ \t\n]*[ \t]"
|
||||
"boundary=\"\\([^\"\n]*[^\" \t\n]\\)\"")
|
||||
nil t)
|
||||
(match-beginning 1))
|
||||
(setq boundary-id (match-string 1)
|
||||
b-delimiter (concat "\n--" boundary-id "[\n \t]+"))
|
||||
(setq entry (assq 'mime-digest nndoc-type-alist))
|
||||
(setcdr entry
|
||||
(list
|
||||
(cons 'head-end "^ ?$")
|
||||
(cons 'body-begin "^ ?\n")
|
||||
(cons 'article-begin b-delimiter)
|
||||
(cons 'body-end-function 'nndoc-digest-body-end)
|
||||
; (cons 'body-end
|
||||
; (concat "\n--" boundary-id "\\(--\\)?[\n \t]+"))
|
||||
(cons 'file-end (concat "\n--" boundary-id "--[ \t]*$"))))
|
||||
'mime-digest)
|
||||
;; Standard digest.
|
||||
((and (re-search-forward (concat "^" (make-string 70 ?-) "\n\n") nil t)
|
||||
(re-search-forward
|
||||
(concat "\n\n" (make-string 30 ?-) "\n\n") nil t))
|
||||
'standard-digest)
|
||||
;; Stupid digest.
|
||||
(t
|
||||
'slack-digest))))
|
||||
|
||||
(defun nndoc-narrow-to-article (article)
|
||||
(save-excursion
|
||||
(set-buffer nndoc-current-buffer)
|
||||
(widen)
|
||||
(goto-char (point-min))
|
||||
(while (and (re-search-forward nndoc-article-begin nil t)
|
||||
(not (zerop (setq article (1- article))))))
|
||||
(if (not (zerop article))
|
||||
()
|
||||
(narrow-to-region
|
||||
(match-end 0)
|
||||
(or (and (re-search-forward nndoc-article-end nil t)
|
||||
(match-beginning 0))
|
||||
(point-max)))
|
||||
t)))
|
||||
(defun nndoc-guess-type ()
|
||||
"Guess what document type is in the current buffer."
|
||||
(goto-char (point-min))
|
||||
(cond
|
||||
((looking-at message-unix-mail-delimiter)
|
||||
'mbox)
|
||||
((looking-at "\^A\^A\^A\^A$")
|
||||
'mmdf)
|
||||
((looking-at "^Path:.*\n")
|
||||
'news)
|
||||
((looking-at "#! *rnews")
|
||||
'rnews)
|
||||
((re-search-forward "\^_\^L *\n" nil t)
|
||||
'babyl)
|
||||
((save-excursion
|
||||
(and (re-search-forward "^-+ Start of forwarded message -+\n+" nil t)
|
||||
(not (re-search-forward "^Subject:.*digest" nil t))))
|
||||
'forward)
|
||||
((let ((case-fold-search nil))
|
||||
(re-search-forward "^\t[^a-z]+ ([^a-z]+) --" nil t))
|
||||
'clari-briefs)
|
||||
(t
|
||||
'digest)))
|
||||
|
||||
;; Insert article ARTICLE in the current buffer.
|
||||
(defun nndoc-insert-article (article)
|
||||
(let ((ibuf (current-buffer)))
|
||||
(defun nndoc-set-delims ()
|
||||
"Set the nndoc delimiter variables according to the type of the document."
|
||||
(let ((vars '(nndoc-file-begin
|
||||
nndoc-first-article
|
||||
nndoc-article-end nndoc-head-begin nndoc-head-end
|
||||
nndoc-file-end nndoc-article-begin
|
||||
nndoc-body-begin nndoc-body-end-function nndoc-body-end
|
||||
nndoc-prepare-body nndoc-article-transform
|
||||
nndoc-generate-head nndoc-body-begin-function
|
||||
nndoc-head-begin-function)))
|
||||
(while vars
|
||||
(set (pop vars) nil)))
|
||||
(let* (defs guess)
|
||||
;; Guess away until we find the real file type.
|
||||
(while (setq defs (cdr (assq nndoc-article-type nndoc-type-alist))
|
||||
guess (assq 'guess defs))
|
||||
(setq nndoc-article-type (funcall (cdr guess))))
|
||||
;; Set the nndoc variables.
|
||||
(while defs
|
||||
(set (intern (format "nndoc-%s" (caar defs)))
|
||||
(cdr (pop defs))))))
|
||||
|
||||
(defun nndoc-search (regexp)
|
||||
(prog1
|
||||
(re-search-forward regexp nil t)
|
||||
(beginning-of-line)))
|
||||
|
||||
(defun nndoc-dissect-buffer ()
|
||||
"Go through the document and partition it into heads/bodies/articles."
|
||||
(let ((i 0)
|
||||
(first t)
|
||||
head-begin head-end body-begin body-end)
|
||||
(setq nndoc-dissection-alist nil)
|
||||
(save-excursion
|
||||
(set-buffer nndoc-current-buffer)
|
||||
(widen)
|
||||
(goto-char (point-min))
|
||||
(while (and (re-search-forward nndoc-article-begin nil t)
|
||||
(not (zerop (setq article (1- article))))))
|
||||
(if (not (zerop article))
|
||||
()
|
||||
(narrow-to-region
|
||||
(match-end 0)
|
||||
(or (and (re-search-forward nndoc-article-end nil t)
|
||||
(match-beginning 0))
|
||||
(point-max)))
|
||||
;; Find the beginning of the file.
|
||||
(when nndoc-file-begin
|
||||
(nndoc-search nndoc-file-begin))
|
||||
;; Go through the file.
|
||||
(while (if (and first nndoc-first-article)
|
||||
(nndoc-search nndoc-first-article)
|
||||
(nndoc-search nndoc-article-begin))
|
||||
(setq first nil)
|
||||
(cond (nndoc-head-begin-function
|
||||
(funcall nndoc-head-begin-function))
|
||||
(nndoc-head-begin
|
||||
(nndoc-search nndoc-head-begin)))
|
||||
(if (and nndoc-file-end
|
||||
(looking-at nndoc-file-end))
|
||||
(goto-char (point-max))
|
||||
(setq head-begin (point))
|
||||
(nndoc-search (or nndoc-head-end "^$"))
|
||||
(setq head-end (point))
|
||||
(if nndoc-body-begin-function
|
||||
(funcall nndoc-body-begin-function)
|
||||
(nndoc-search (or nndoc-body-begin "^\n")))
|
||||
(setq body-begin (point))
|
||||
(or (and nndoc-body-end-function
|
||||
(funcall nndoc-body-end-function))
|
||||
(and nndoc-body-end
|
||||
(nndoc-search nndoc-body-end))
|
||||
(nndoc-search nndoc-article-begin)
|
||||
(progn
|
||||
(goto-char (point-max))
|
||||
(when nndoc-file-end
|
||||
(and (re-search-backward nndoc-file-end nil t)
|
||||
(beginning-of-line)))))
|
||||
(setq body-end (point))
|
||||
(push (list (incf i) head-begin head-end body-begin body-end
|
||||
(count-lines body-begin body-end))
|
||||
nndoc-dissection-alist))))))
|
||||
|
||||
(defun nndoc-unquote-dashes ()
|
||||
"Unquote quoted non-separators in digests."
|
||||
(while (re-search-forward "^- -"nil t)
|
||||
(replace-match "-" t t)))
|
||||
|
||||
(defun nndoc-digest-body-end ()
|
||||
(and (re-search-forward nndoc-article-begin nil t)
|
||||
(goto-char (match-beginning 0))))
|
||||
|
||||
(defun nndoc-mbox-body-end ()
|
||||
(let ((beg (point))
|
||||
len end)
|
||||
(when
|
||||
(save-excursion
|
||||
(and (re-search-backward nndoc-article-begin nil t)
|
||||
(setq end (point))
|
||||
(search-forward "\n\n" beg t)
|
||||
(re-search-backward
|
||||
"^Content-Length:[ \t]*\\([0-9]+\\) *$" end t)
|
||||
(setq len (string-to-int (match-string 1)))
|
||||
(search-forward "\n\n" beg t)
|
||||
(or (= (setq len (+ (point) len)) (point-max))
|
||||
(and (< len (point-max))
|
||||
(goto-char len)
|
||||
(looking-at nndoc-article-begin)))))
|
||||
(goto-char len))))
|
||||
|
||||
(defun nndoc-rnews-body-end ()
|
||||
(and (re-search-backward nndoc-article-begin nil t)
|
||||
(forward-line 1)
|
||||
(goto-char (+ (point) (string-to-int (match-string 1))))))
|
||||
|
||||
(defun nndoc-transform-clari-briefs (article)
|
||||
(goto-char (point-min))
|
||||
(when (looking-at " *\\*\\(.*\\)\n")
|
||||
(replace-match "" t t))
|
||||
(nndoc-generate-clari-briefs-head article))
|
||||
|
||||
(defun nndoc-generate-clari-briefs-head (article)
|
||||
(let ((entry (cdr (assq article nndoc-dissection-alist)))
|
||||
subject from)
|
||||
(save-excursion
|
||||
(set-buffer nndoc-current-buffer)
|
||||
(save-restriction
|
||||
(narrow-to-region (car entry) (nth 3 entry))
|
||||
(goto-char (point-min))
|
||||
(and nndoc-head-begin
|
||||
(re-search-forward nndoc-head-begin nil t)
|
||||
(narrow-to-region (point) (point-max)))
|
||||
(or (re-search-forward nndoc-head-end nil t)
|
||||
(goto-char (point-max)))
|
||||
(append-to-buffer ibuf (point-min) (point))
|
||||
(and nndoc-body-begin
|
||||
(re-search-forward nndoc-body-begin nil t))
|
||||
(append-to-buffer ibuf (point) (point-max))
|
||||
t))))
|
||||
(when (looking-at " *\\*\\(.*\\)$")
|
||||
(setq subject (match-string 1))
|
||||
(when (string-match "[ \t]+$" subject)
|
||||
(setq subject (substring subject 0 (match-beginning 0)))))
|
||||
(when
|
||||
(let ((case-fold-search nil))
|
||||
(re-search-forward
|
||||
"^\t\\([^a-z]+\\(,[^(]+\\)? ([^a-z]+)\\) --" nil t))
|
||||
(setq from (match-string 1)))))
|
||||
(insert "From: " "clari@clari.net (" (or from "unknown") ")"
|
||||
"\nSubject: " (or subject "(no subject)") "\n")))
|
||||
|
||||
(defun nndoc-babyl-body-begin ()
|
||||
(re-search-forward "^\n" nil t)
|
||||
(when (looking-at "\*\*\* EOOH \*\*\*")
|
||||
(re-search-forward "^\n" nil t)))
|
||||
|
||||
(defun nndoc-babyl-head-begin ()
|
||||
(when (re-search-forward "^[0-9].*\n" nil t)
|
||||
(when (looking-at "\*\*\* EOOH \*\*\*")
|
||||
(forward-line 1))
|
||||
t))
|
||||
|
||||
(provide 'nndoc)
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
;;; nneething.el --- random file access for Gnus
|
||||
|
||||
;; Copyright (C) 1995 Free Software Foundation, Inc.
|
||||
;; Copyright (C) 1995,96 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
|
||||
;; Masanobu UMEDA <umerin@flab.flab.fujitsu.junet>
|
||||
@ -33,103 +32,90 @@
|
||||
|
||||
(require 'nnheader)
|
||||
(require 'nnmail)
|
||||
(require 'nnoo)
|
||||
(eval-when-compile (require 'cl))
|
||||
|
||||
(defvar nneething-map-file-directory "~/.nneething/"
|
||||
"*Map files directory.")
|
||||
(nnoo-declare nneething)
|
||||
|
||||
(defvar nneething-exclude-files "~$"
|
||||
"*Regexp saying what files to exclude from the group.")
|
||||
(defvoo nneething-map-file-directory "~/.nneething/"
|
||||
"*Where nneething stores the map files.")
|
||||
|
||||
(defvar nneething-map-file ".nneething"
|
||||
"*Name of map files.")
|
||||
(defvoo nneething-map-file ".nneething"
|
||||
"*Name of the map files.")
|
||||
|
||||
(defvoo nneething-exclude-files nil
|
||||
"*Regexp saying what files to exclude from the group.
|
||||
If this variable is nil, no files will be excluded.")
|
||||
|
||||
|
||||
|
||||
;;; Internal variables.
|
||||
|
||||
(defconst nneething-version "nneething 1.0"
|
||||
"nneething version.")
|
||||
|
||||
(defvar nneething-current-directory nil
|
||||
(defvoo nneething-current-directory nil
|
||||
"Current news group directory.")
|
||||
|
||||
(defvar nneething-status-string "")
|
||||
(defvar nneething-group-alist nil)
|
||||
(defvoo nneething-status-string "")
|
||||
(defvoo nneething-group-alist nil)
|
||||
|
||||
|
||||
(defvoo nneething-message-id-number 0)
|
||||
(defvoo nneething-work-buffer " *nneething work*")
|
||||
|
||||
(defvar nneething-directory nil)
|
||||
(defvar nneething-group nil)
|
||||
(defvar nneething-map nil)
|
||||
(defvar nneething-read-only nil)
|
||||
(defvar nneething-active nil)
|
||||
(defvar nneething-server-variables
|
||||
(list
|
||||
(list 'nneething-directory nneething-directory)
|
||||
'(nneething-current-directory nil)
|
||||
'(nneething-status-string "")
|
||||
'(nneething-group-alist)))
|
||||
(defvoo nneething-directory nil)
|
||||
(defvoo nneething-group nil)
|
||||
(defvoo nneething-map nil)
|
||||
(defvoo nneething-read-only nil)
|
||||
(defvoo nneething-active nil)
|
||||
|
||||
|
||||
|
||||
;;; Interface functions.
|
||||
|
||||
(defun nneething-retrieve-headers (sequence &optional newsgroup server)
|
||||
(nneething-possibly-change-directory newsgroup)
|
||||
(nnoo-define-basics nneething)
|
||||
|
||||
(deffoo nneething-retrieve-headers (articles &optional group server fetch-old)
|
||||
(nneething-possibly-change-directory group)
|
||||
|
||||
(save-excursion
|
||||
(set-buffer nntp-server-buffer)
|
||||
(erase-buffer)
|
||||
(let* ((number (length sequence))
|
||||
(let* ((number (length articles))
|
||||
(count 0)
|
||||
(large (and (numberp nnmail-large-newsgroup)
|
||||
(> number nnmail-large-newsgroup)))
|
||||
article file)
|
||||
|
||||
(if (stringp (car sequence))
|
||||
(if (stringp (car articles))
|
||||
'headers
|
||||
|
||||
(while sequence
|
||||
(setq article (car sequence))
|
||||
(while (setq article (pop articles))
|
||||
(setq file (nneething-file-name article))
|
||||
|
||||
(if (and (file-exists-p file)
|
||||
(not (zerop (nth 7 (file-attributes file)))))
|
||||
(progn
|
||||
(insert (format "221 %d Article retrieved.\n" article))
|
||||
(nneething-insert-head file)
|
||||
(insert ".\n")))
|
||||
(when (and (file-exists-p file)
|
||||
(or (file-directory-p file)
|
||||
(not (zerop (nth 7 (file-attributes file))))))
|
||||
(insert (format "221 %d Article retrieved.\n" article))
|
||||
(nneething-insert-head file)
|
||||
(insert ".\n"))
|
||||
|
||||
(setq sequence (cdr sequence)
|
||||
count (1+ count))
|
||||
(incf count)
|
||||
|
||||
(and large
|
||||
(zerop (% count 20))
|
||||
(message "nneething: Receiving headers... %d%%"
|
||||
(/ (* count 100) number))))
|
||||
|
||||
(and large (message "nneething: Receiving headers...done"))
|
||||
(when large
|
||||
(message "nneething: Receiving headers...done"))
|
||||
|
||||
;; Fold continuation lines.
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward "\\(\r?\n[ \t]+\\)+" nil t)
|
||||
(replace-match " " t t))
|
||||
(nnheader-fold-continuation-lines)
|
||||
'headers))))
|
||||
|
||||
(defun nneething-open-server (server &optional defs)
|
||||
(setq nneething-status-string "")
|
||||
(nnheader-init-server-buffer))
|
||||
|
||||
(defun nneething-close-server (&optional server)
|
||||
t)
|
||||
|
||||
(defun nneething-server-opened (&optional server)
|
||||
t)
|
||||
|
||||
(defun nneething-status-message (&optional server)
|
||||
nneething-status-string)
|
||||
|
||||
(defun nneething-request-article (id &optional newsgroup server buffer)
|
||||
(nneething-possibly-change-directory newsgroup)
|
||||
(let ((file (if (stringp id) nil (nneething-file-name id)))
|
||||
(deffoo nneething-request-article (id &optional group server buffer)
|
||||
(nneething-possibly-change-directory group)
|
||||
(let ((file (unless (stringp id) (nneething-file-name id)))
|
||||
(nntp-server-buffer (or buffer nntp-server-buffer)))
|
||||
(and (stringp file) ; We did not request by Message-ID.
|
||||
(file-exists-p file) ; The file exists.
|
||||
@ -139,50 +125,41 @@
|
||||
(or (nnheader-article-p) ; Either it's a real article...
|
||||
(progn
|
||||
(goto-char (point-min))
|
||||
(nneething-make-head file) ; ... or we fake some headers.
|
||||
(nneething-make-head file (current-buffer)) ; ... or we fake some headers.
|
||||
(insert "\n")))
|
||||
t))))
|
||||
|
||||
(defun nneething-request-group (group &optional dir dont-check)
|
||||
(deffoo nneething-request-group (group &optional dir dont-check)
|
||||
(nneething-possibly-change-directory group dir)
|
||||
(or dont-check (nneething-create-mapping))
|
||||
(save-excursion
|
||||
(set-buffer nntp-server-buffer)
|
||||
(erase-buffer)
|
||||
(unless dont-check
|
||||
(nneething-create-mapping)
|
||||
(if (> (car nneething-active) (cdr nneething-active))
|
||||
(insert (format "211 0 1 0 %s\n" group))
|
||||
(insert (format "211 %d %d %d %s\n"
|
||||
(- (1+ (cdr nneething-active)) (car nneething-active))
|
||||
(car nneething-active) (cdr nneething-active)
|
||||
group)))
|
||||
t))
|
||||
(nnheader-insert "211 0 1 0 %s\n" group)
|
||||
(nnheader-insert
|
||||
"211 %d %d %d %s\n"
|
||||
(- (1+ (cdr nneething-active)) (car nneething-active))
|
||||
(car nneething-active) (cdr nneething-active)
|
||||
group)))
|
||||
t)
|
||||
|
||||
(defun nneething-request-list (&optional server dir)
|
||||
(save-excursion
|
||||
(set-buffer nntp-server-buffer)
|
||||
(erase-buffer))
|
||||
nil)
|
||||
(deffoo nneething-request-list (&optional server dir)
|
||||
(nnheader-report 'nneething "LIST is not implemented."))
|
||||
|
||||
(defun nneething-request-newgroups (date &optional server)
|
||||
(save-excursion
|
||||
(set-buffer nntp-server-buffer)
|
||||
(erase-buffer))
|
||||
nil)
|
||||
(deffoo nneething-request-newgroups (date &optional server)
|
||||
(nnheader-report 'nneething "NEWSGROUPS is not implemented."))
|
||||
|
||||
(defun nneething-request-post (&optional server)
|
||||
(mail-send-and-exit nil))
|
||||
(deffoo nneething-request-type (group &optional article)
|
||||
'unknown)
|
||||
|
||||
(defalias 'nneething-request-post-buffer 'nnmail-request-post-buffer)
|
||||
|
||||
(defun nneething-close-group (group &optional server)
|
||||
(deffoo nneething-close-group (group &optional server)
|
||||
(setq nneething-current-directory nil)
|
||||
t)
|
||||
|
||||
|
||||
;;; Internal functions.
|
||||
|
||||
(defun nneething-possibly-change-directory (group &optional dir)
|
||||
(if (not group)
|
||||
()
|
||||
(when group
|
||||
(if (and nneething-group
|
||||
(string= group nneething-group))
|
||||
t
|
||||
@ -198,42 +175,58 @@
|
||||
(setq nneething-map nil)
|
||||
(setq nneething-active (cons 1 0))
|
||||
(nneething-create-mapping)
|
||||
(setq nneething-group-alist
|
||||
(cons (list group dir nneething-map nneething-active)
|
||||
nneething-group-alist)))))))
|
||||
(push (list group dir nneething-map nneething-active)
|
||||
nneething-group-alist))))))
|
||||
|
||||
(defun nneething-map-file ()
|
||||
;; We make sure that the .nneething directory exists.
|
||||
(make-directory nneething-map-file-directory 'parents)
|
||||
(unless (file-exists-p nneething-map-file-directory)
|
||||
(make-directory nneething-map-file-directory 'parents))
|
||||
;; We store it in a special directory under the user's home dir.
|
||||
(concat (file-name-as-directory nneething-map-file-directory)
|
||||
nneething-group nneething-map-file))
|
||||
|
||||
(defun nneething-create-mapping ()
|
||||
;; Read nneething-active and nneething-map
|
||||
;; Read nneething-active and nneething-map.
|
||||
(let ((map-file (nneething-map-file))
|
||||
(files (directory-files nneething-directory))
|
||||
touched)
|
||||
touched map-files)
|
||||
(if (file-exists-p map-file)
|
||||
(condition-case nil
|
||||
(load map-file nil t t)
|
||||
(error nil)))
|
||||
(or nneething-active (setq nneething-active (cons 1 0)))
|
||||
;; Remove files matching that regexp.
|
||||
(let ((f files)
|
||||
prev)
|
||||
(while f
|
||||
(if (string-match nneething-exclude-files (car f))
|
||||
(if prev (setcdr prev (cdr f))
|
||||
(setq files (cdr files)))
|
||||
(setq prev f))
|
||||
(setq f (cdr f))))
|
||||
;; Remove files that have disappeared from the map.
|
||||
;; Old nneething had a different map format.
|
||||
(when (and (cdar nneething-map)
|
||||
(atom (cdar nneething-map)))
|
||||
(setq nneething-map
|
||||
(mapcar (lambda (n)
|
||||
(list (cdr n) (car n)
|
||||
(nth 5 (file-attributes
|
||||
(nneething-file-name (car n))))))
|
||||
nneething-map)))
|
||||
;; Remove files matching the exclusion regexp.
|
||||
(when nneething-exclude-files
|
||||
(let ((f files)
|
||||
prev)
|
||||
(while f
|
||||
(if (string-match nneething-exclude-files (car f))
|
||||
(if prev (setcdr prev (cdr f))
|
||||
(setq files (cdr files)))
|
||||
(setq prev f))
|
||||
(setq f (cdr f)))))
|
||||
;; Remove deleted files from the map.
|
||||
(let ((map nneething-map)
|
||||
prev)
|
||||
(while map
|
||||
(if (member (car (car map)) files)
|
||||
(setq prev map)
|
||||
(if (and (member (cadar map) files)
|
||||
;; We also remove files that have changed mod times.
|
||||
(equal (nth 5 (file-attributes
|
||||
(nneething-file-name (cadar map))))
|
||||
(caddar map)))
|
||||
(progn
|
||||
(push (cadar map) map-files)
|
||||
(setq prev map))
|
||||
(setq touched t)
|
||||
(if prev
|
||||
(setcdr prev (cdr map))
|
||||
@ -241,60 +234,87 @@
|
||||
(setq map (cdr map))))
|
||||
;; Find all new files and enter them into the map.
|
||||
(while files
|
||||
(or (assoc (car files) nneething-map) ; If already in the map, ignore.
|
||||
(progn
|
||||
(setq touched t)
|
||||
(setcdr nneething-active (1+ (cdr nneething-active)))
|
||||
(setq nneething-map
|
||||
(cons (cons (car files) (cdr nneething-active)) nneething-map))))
|
||||
(unless (member (car files) map-files)
|
||||
;; This file is not in the map, so we enter it.
|
||||
(setq touched t)
|
||||
(setcdr nneething-active (1+ (cdr nneething-active)))
|
||||
(push (list (cdr nneething-active) (car files)
|
||||
(nth 5 (file-attributes
|
||||
(nneething-file-name (car files)))))
|
||||
nneething-map))
|
||||
(setq files (cdr files)))
|
||||
(if (or (not touched) nneething-read-only)
|
||||
()
|
||||
(when (and touched
|
||||
(not nneething-read-only))
|
||||
(save-excursion
|
||||
(set-buffer (get-buffer-create " *nneething map*"))
|
||||
(buffer-disable-undo (current-buffer))
|
||||
(erase-buffer)
|
||||
(nnheader-set-temp-buffer " *nneething map*")
|
||||
(insert "(setq nneething-map '" (prin1-to-string nneething-map) ")\n"
|
||||
"(setq nneething-active '" (prin1-to-string nneething-active)
|
||||
")\n")
|
||||
(write-region (point-min) (point-max) map-file nil 'nomesg)
|
||||
(kill-buffer (current-buffer))))))
|
||||
|
||||
(defvar nneething-message-id-number 0)
|
||||
(defvar nneething-work-buffer " *nneething work*")
|
||||
|
||||
(defun nneething-insert-head (file)
|
||||
(and (nneething-get-head file)
|
||||
(insert-buffer-substring nneething-work-buffer)))
|
||||
"Insert the head of FILE."
|
||||
(when (nneething-get-head file)
|
||||
(insert-buffer-substring nneething-work-buffer)
|
||||
(goto-char (point-max))))
|
||||
|
||||
(defun nneething-make-head (file)
|
||||
(defun nneething-make-head (file &optional buffer)
|
||||
"Create a head by looking at the file attributes of FILE."
|
||||
(let ((atts (file-attributes file)))
|
||||
(insert "Subject: " (file-name-nondirectory file) "\n"
|
||||
"Message-ID: <nneething-"
|
||||
(int-to-string
|
||||
(setq nneething-message-id-number
|
||||
(1+ nneething-message-id-number)))
|
||||
"@" (system-name) ">\n"
|
||||
"Date: " (current-time-string (nth 5 atts)) "\n"
|
||||
(nneething-from-line (nth 2 atts))
|
||||
"Chars: " (int-to-string (nth 7 atts)) "\n")))
|
||||
(insert
|
||||
"Subject: " (file-name-nondirectory file) "\n"
|
||||
"Message-ID: <nneething-"
|
||||
(int-to-string (incf nneething-message-id-number))
|
||||
"@" (system-name) ">\n"
|
||||
(if (equal '(0 0) (nth 5 atts)) ""
|
||||
(concat "Date: " (current-time-string (nth 5 atts)) "\n"))
|
||||
(or (if buffer
|
||||
(save-excursion
|
||||
(set-buffer buffer)
|
||||
(if (re-search-forward "<[a-zA-Z0-9_]@[-a-zA-Z0-9_]>" 1000 t)
|
||||
(concat "From: " (match-string 0) "\n"))))
|
||||
(nneething-from-line (nth 2 atts) file))
|
||||
(if (> (string-to-int (int-to-string (nth 7 atts))) 0)
|
||||
(concat "Chars: " (int-to-string (nth 7 atts)) "\n")
|
||||
"")
|
||||
(if buffer
|
||||
(save-excursion
|
||||
(set-buffer buffer)
|
||||
(concat "Lines: " (int-to-string
|
||||
(count-lines (point-min) (point-max))) "\n"))
|
||||
"")
|
||||
)))
|
||||
|
||||
(defun nneething-from-line (uid)
|
||||
(let ((login (condition-case nil
|
||||
(user-login-name uid)
|
||||
(defun nneething-from-line (uid &optional file)
|
||||
"Return a From header based of UID."
|
||||
(let* ((login (condition-case nil
|
||||
(user-login-name uid)
|
||||
(error
|
||||
(cond ((= uid (user-uid)) (user-login-name))
|
||||
((zerop uid) "root")
|
||||
(t (int-to-string uid))))))
|
||||
(name (condition-case nil
|
||||
(user-full-name uid)
|
||||
(error
|
||||
(cond ((= uid (user-uid)) (user-login-name))
|
||||
((zerop uid) "root")
|
||||
(t (int-to-string uid))))))
|
||||
(name (condition-case nil
|
||||
(user-full-name uid)
|
||||
(error
|
||||
(cond ((= uid (user-uid)) (user-full-name))
|
||||
((zerop uid) "Ms. Root"))))))
|
||||
(concat "From: " login "@" (system-name)
|
||||
(cond ((= uid (user-uid)) (user-full-name))
|
||||
((zerop uid) "Ms. Root")))))
|
||||
(host (if (string-match "\\`/[^/@]*@\\([^:/]+\\):" file)
|
||||
(prog1
|
||||
(substring file
|
||||
(match-beginning 1)
|
||||
(match-end 1))
|
||||
(if (string-match "/\\(users\\|home\\)/\\([^/]+\\)/" file)
|
||||
(setq login (substring file
|
||||
(match-beginning 2)
|
||||
(match-end 2))
|
||||
name nil)))
|
||||
(system-name))))
|
||||
(concat "From: " login "@" host
|
||||
(if name (concat " (" name ")") "") "\n")))
|
||||
|
||||
(defun nneething-get-head (file)
|
||||
"Either find the head in FILE or make a head for FILE."
|
||||
(save-excursion
|
||||
(set-buffer (get-buffer-create nneething-work-buffer))
|
||||
(setq case-fold-search nil)
|
||||
@ -319,16 +339,16 @@
|
||||
(1- (point)))
|
||||
(point-max)))
|
||||
(point-max))
|
||||
(erase-buffer)
|
||||
(nneething-make-head file))
|
||||
(goto-char (point-min))
|
||||
(nneething-make-head file (current-buffer))
|
||||
(delete-region (point) (point-max)))
|
||||
t))))
|
||||
|
||||
(defun nneething-number-to-file (number)
|
||||
(car (rassq number nneething-map)))
|
||||
|
||||
(defun nneething-file-name (article)
|
||||
"Return the file name of ARTICLE."
|
||||
(concat (file-name-as-directory nneething-directory)
|
||||
(if (numberp article) (nneething-number-to-file article)
|
||||
(if (numberp article)
|
||||
(cadr (assq article nneething-map))
|
||||
article)))
|
||||
|
||||
(provide 'nneething)
|
||||
|
941
lisp/nnfolder.el
941
lisp/nnfolder.el
File diff suppressed because it is too large
Load Diff
671
lisp/nnheader.el
671
lisp/nnheader.el
@ -1,6 +1,5 @@
|
||||
;;; nnheader.el --- header access macros for Gnus and its backends
|
||||
|
||||
;; Copyright (C) 1987,88,89,90,93,94,95 Free Software Foundation, Inc.
|
||||
;; Copyright (C) 1987,88,89,90,93,94,95,96 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Masanobu UMEDA <umerin@flab.flab.fujitsu.junet>
|
||||
;; Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
|
||||
@ -25,10 +24,10 @@
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; These macros may look very much like the ones in GNUS 4.1. They
|
||||
;; These macros may look very much like the ones in GNUS 4.1. They
|
||||
;; are, in a way, but you should note that the indices they use have
|
||||
;; been changed from the internal GNUS format to the NOV format. Makes
|
||||
;; it possible to read headers from XOVER much faster.
|
||||
;; been changed from the internal GNUS format to the NOV format. The
|
||||
;; makes it possible to read headers from XOVER much faster.
|
||||
;;
|
||||
;; The format of a header is now:
|
||||
;; [number subject from date id references chars lines xref]
|
||||
@ -38,110 +37,237 @@
|
||||
|
||||
;;; Code:
|
||||
|
||||
(defalias 'nntp-header-number 'mail-header-number)
|
||||
(require 'mail-utils)
|
||||
(eval-when-compile (require 'cl))
|
||||
|
||||
(defvar nnheader-max-head-length 4096
|
||||
"*Max length of the head of articles.")
|
||||
|
||||
(defvar nnheader-file-name-translation-alist nil
|
||||
"*Alist that says how to translate characters in file names.
|
||||
For instance, if \":\" is illegal as a file character in file names
|
||||
on your system, you could say something like:
|
||||
|
||||
\(setq nnheader-file-name-translation-alist '((?: . ?_)))")
|
||||
|
||||
;;; Header access macros.
|
||||
|
||||
(defmacro mail-header-number (header)
|
||||
"Return article number in HEADER."
|
||||
(` (aref (, header) 0)))
|
||||
`(aref ,header 0))
|
||||
|
||||
(defalias 'nntp-set-header-number 'mail-header-set-number)
|
||||
(defmacro mail-header-set-number (header number)
|
||||
"Set article number of HEADER to NUMBER."
|
||||
(` (aset (, header) 0 (, number))))
|
||||
`(aset ,header 0 ,number))
|
||||
|
||||
(defalias 'nntp-header-subject 'mail-header-subject)
|
||||
(defmacro mail-header-subject (header)
|
||||
"Return subject string in HEADER."
|
||||
(` (aref (, header) 1)))
|
||||
`(aref ,header 1))
|
||||
|
||||
(defalias 'nntp-set-header-subject 'mail-header-set-subject)
|
||||
(defmacro mail-header-set-subject (header subject)
|
||||
"Set article subject of HEADER to SUBJECT."
|
||||
(` (aset (, header) 1 (, subject))))
|
||||
`(aset ,header 1 ,subject))
|
||||
|
||||
(defalias 'nntp-header-from 'mail-header-from)
|
||||
(defmacro mail-header-from (header)
|
||||
"Return author string in HEADER."
|
||||
(` (aref (, header) 2)))
|
||||
`(aref ,header 2))
|
||||
|
||||
(defalias 'nntp-set-header-from 'mail-header-set-from)
|
||||
(defmacro mail-header-set-from (header from)
|
||||
"Set article author of HEADER to FROM."
|
||||
(` (aset (, header) 2 (, from))))
|
||||
`(aset ,header 2 ,from))
|
||||
|
||||
(defalias 'nntp-header-date 'mail-header-date)
|
||||
(defmacro mail-header-date (header)
|
||||
"Return date in HEADER."
|
||||
(` (aref (, header) 3)))
|
||||
`(aref ,header 3))
|
||||
|
||||
(defalias 'nntp-set-header-date 'mail-header-set-date)
|
||||
(defmacro mail-header-set-date (header date)
|
||||
"Set article date of HEADER to DATE."
|
||||
(` (aset (, header) 3 (, date))))
|
||||
`(aset ,header 3 ,date))
|
||||
|
||||
(defalias 'nntp-header-id 'mail-header-id)
|
||||
(defalias 'mail-header-message-id 'mail-header-id)
|
||||
(defmacro mail-header-id (header)
|
||||
"Return Id in HEADER."
|
||||
(` (aref (, header) 4)))
|
||||
`(aref ,header 4))
|
||||
|
||||
(defalias 'nntp-set-header-id 'mail-header-set-id)
|
||||
(defalias 'mail-header-set-message-id 'mail-header-set-id)
|
||||
(defmacro mail-header-set-id (header id)
|
||||
"Set article Id of HEADER to ID."
|
||||
(` (aset (, header) 4 (, id))))
|
||||
`(aset ,header 4 ,id))
|
||||
|
||||
(defalias 'nntp-header-references 'mail-header-references)
|
||||
(defmacro mail-header-references (header)
|
||||
"Return references in HEADER."
|
||||
(` (aref (, header) 5)))
|
||||
`(aref ,header 5))
|
||||
|
||||
(defalias 'nntp-set-header-references 'mail-header-set-references)
|
||||
(defmacro mail-header-set-references (header ref)
|
||||
"Set article references of HEADER to REF."
|
||||
(` (aset (, header) 5 (, ref))))
|
||||
`(aset ,header 5 ,ref))
|
||||
|
||||
(defalias 'nntp-header-chars 'mail-header-chars)
|
||||
(defmacro mail-header-chars (header)
|
||||
"Return number of chars of article in HEADER."
|
||||
(` (aref (, header) 6)))
|
||||
`(aref ,header 6))
|
||||
|
||||
(defalias 'nntp-set-header-chars 'mail-header-set-chars)
|
||||
(defmacro mail-header-set-chars (header chars)
|
||||
"Set number of chars in article of HEADER to CHARS."
|
||||
(` (aset (, header) 6 (, chars))))
|
||||
`(aset ,header 6 ,chars))
|
||||
|
||||
(defalias 'nntp-header-lines 'mail-header-lines)
|
||||
(defmacro mail-header-lines (header)
|
||||
"Return lines in HEADER."
|
||||
(` (aref (, header) 7)))
|
||||
`(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))))
|
||||
`(aset ,header 7 ,lines))
|
||||
|
||||
(defalias 'nntp-header-xref 'mail-header-xref)
|
||||
(defmacro mail-header-xref (header)
|
||||
"Return xref string in HEADER."
|
||||
(` (aref (, header) 8)))
|
||||
`(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))))
|
||||
`(aset ,header 8 ,xref))
|
||||
|
||||
(defun make-mail-header (&optional init)
|
||||
"Create a new mail header structure initialized with INIT."
|
||||
(make-vector 9 init))
|
||||
|
||||
;; Parsing headers and NOV lines.
|
||||
|
||||
(defsubst nnheader-header-value ()
|
||||
(buffer-substring (match-end 0) (gnus-point-at-eol)))
|
||||
|
||||
(defvar nnheader-newsgroup-none-id 1)
|
||||
|
||||
(defun nnheader-parse-head (&optional naked)
|
||||
(let ((case-fold-search t)
|
||||
(cur (current-buffer))
|
||||
(buffer-read-only nil)
|
||||
end ref in-reply-to lines p)
|
||||
(goto-char (point-min))
|
||||
(when naked
|
||||
(insert "\n"))
|
||||
;; Search to the beginning of the next header. Error messages
|
||||
;; do not begin with 2 or 3.
|
||||
(prog1
|
||||
(when (or naked (re-search-forward "^[23][0-9]+ " nil t))
|
||||
;; This implementation of this function, with nine
|
||||
;; search-forwards instead of the one re-search-forward and
|
||||
;; a case (which basically was the old function) is actually
|
||||
;; about twice as fast, even though it looks messier. You
|
||||
;; can't have everything, I guess. Speed and elegance
|
||||
;; doesn't always go hand in hand.
|
||||
(vector
|
||||
;; Number.
|
||||
(if naked
|
||||
(progn
|
||||
(setq p (point-min))
|
||||
0)
|
||||
(prog1
|
||||
(read cur)
|
||||
(end-of-line)
|
||||
(setq p (point))
|
||||
(narrow-to-region (point)
|
||||
(or (and (search-forward "\n.\n" nil t)
|
||||
(- (point) 2))
|
||||
(point)))))
|
||||
;; Subject.
|
||||
(progn
|
||||
(goto-char p)
|
||||
(if (search-forward "\nsubject: " nil t)
|
||||
(nnheader-header-value) "(none)"))
|
||||
;; From.
|
||||
(progn
|
||||
(goto-char p)
|
||||
(if (search-forward "\nfrom: " nil t)
|
||||
(nnheader-header-value) "(nobody)"))
|
||||
;; Date.
|
||||
(progn
|
||||
(goto-char p)
|
||||
(if (search-forward "\ndate: " nil t)
|
||||
(nnheader-header-value) ""))
|
||||
;; Message-ID.
|
||||
(progn
|
||||
(goto-char p)
|
||||
(if (search-forward "\nmessage-id: " nil t)
|
||||
(nnheader-header-value)
|
||||
;; If there was no message-id, we just fake one to make
|
||||
;; subsequent routines simpler.
|
||||
(concat "none+"
|
||||
(int-to-string
|
||||
(incf nnheader-newsgroup-none-id)))))
|
||||
;; References.
|
||||
(progn
|
||||
(goto-char p)
|
||||
(if (search-forward "\nreferences: " nil t)
|
||||
(nnheader-header-value)
|
||||
;; Get the references from the in-reply-to header if there
|
||||
;; were no references and the in-reply-to header looks
|
||||
;; promising.
|
||||
(if (and (search-forward "\nin-reply-to: " nil t)
|
||||
(setq in-reply-to (nnheader-header-value))
|
||||
(string-match "<[^>]+>" in-reply-to))
|
||||
(substring in-reply-to (match-beginning 0)
|
||||
(match-end 0))
|
||||
"")))
|
||||
;; Chars.
|
||||
0
|
||||
;; Lines.
|
||||
(progn
|
||||
(goto-char p)
|
||||
(if (search-forward "\nlines: " nil t)
|
||||
(if (numberp (setq lines (read cur)))
|
||||
lines 0)
|
||||
0))
|
||||
;; Xref.
|
||||
(progn
|
||||
(goto-char p)
|
||||
(and (search-forward "\nxref: " nil t)
|
||||
(nnheader-header-value)))))
|
||||
(when naked
|
||||
(goto-char (point-min))
|
||||
(delete-char 1)))))
|
||||
|
||||
(defun nnheader-insert-nov (header)
|
||||
(princ (mail-header-number header) (current-buffer))
|
||||
(insert
|
||||
"\t"
|
||||
(or (mail-header-subject header) "(none)") "\t"
|
||||
(or (mail-header-from header) "(nobody)") "\t"
|
||||
(or (mail-header-date header) "") "\t"
|
||||
(or (mail-header-id header)
|
||||
(nnmail-message-id)) "\t"
|
||||
(or (mail-header-references header) "") "\t")
|
||||
(princ (or (mail-header-chars header) 0) (current-buffer))
|
||||
(insert "\t")
|
||||
(princ (or (mail-header-lines header) 0) (current-buffer))
|
||||
(insert "\t")
|
||||
(when (mail-header-xref header)
|
||||
(insert "Xref: " (mail-header-xref header) "\t"))
|
||||
(insert "\n"))
|
||||
|
||||
(defun nnheader-insert-article-line (article)
|
||||
(goto-char (point-min))
|
||||
(insert "220 ")
|
||||
(princ article (current-buffer))
|
||||
(insert " Article retrieved.\n")
|
||||
(search-forward "\n\n" nil 'move)
|
||||
(delete-region (point) (point-max))
|
||||
(forward-char -1)
|
||||
(insert "."))
|
||||
|
||||
;; Various cruft the backends and Gnus need to communicate.
|
||||
|
||||
(defvar nntp-server-buffer nil)
|
||||
(defvar gnus-verbose-backends t
|
||||
"*If non-nil, Gnus backends will generate lots of comments.")
|
||||
(defvar gnus-verbose-backends 7
|
||||
"*A number that says how talkative the Gnus backends should be.")
|
||||
(defvar gnus-nov-is-evil nil
|
||||
"If non-nil, Gnus backends will never output headers in the NOV format.")
|
||||
(defvar news-reply-yank-from nil)
|
||||
(defvar news-reply-yank-message-id nil)
|
||||
|
||||
;; All backends use this function, so I moved it to this file.
|
||||
(defvar nnheader-callback-function nil)
|
||||
|
||||
(defun nnheader-init-server-buffer ()
|
||||
"Initialize the Gnus-backend communication buffer."
|
||||
(save-excursion
|
||||
(setq nntp-server-buffer (get-buffer-create " *nntpd*"))
|
||||
(set-buffer nntp-server-buffer)
|
||||
@ -151,62 +277,41 @@
|
||||
(setq case-fold-search t) ;Should ignore case.
|
||||
t))
|
||||
|
||||
(defun nnheader-set-init-variables (server defs)
|
||||
(let ((s server)
|
||||
val)
|
||||
;; First we set the server variables in the sequence required. We
|
||||
;; use the definitions from the `defs' list where that is
|
||||
;; possible.
|
||||
(while s
|
||||
(set (car (car s))
|
||||
(if (setq val (assq (car (car s)) defs))
|
||||
(nth 1 val)
|
||||
(nth 1 (car s))))
|
||||
(setq s (cdr s)))
|
||||
;; The we go through the defs list and set any variables that were
|
||||
;; not set in the first sweep.
|
||||
(while defs
|
||||
(if (not (assq (car (car defs)) server))
|
||||
(set (car (car defs))
|
||||
(if (and (symbolp (nth 1 (car defs)))
|
||||
(not (boundp (nth 1 (car defs)))))
|
||||
(nth 1 (car defs))
|
||||
(eval (nth 1 (car defs))))))
|
||||
(setq defs (cdr defs)))))
|
||||
|
||||
(defun nnheader-save-variables (server)
|
||||
(let (out)
|
||||
(while server
|
||||
(setq out (cons (list (car (car server))
|
||||
(symbol-value (car (car server))))
|
||||
out))
|
||||
(setq server (cdr server)))
|
||||
(nreverse out)))
|
||||
;;; Various functions the backends use.
|
||||
|
||||
(defun nnheader-restore-variables (state)
|
||||
(while state
|
||||
(set (car (car state)) (nth 1 (car state)))
|
||||
(setq state (cdr state))))
|
||||
|
||||
(defvar nnheader-max-head-length 4096
|
||||
"The maximum length of a HEAD.")
|
||||
(defun nnheader-file-error (file)
|
||||
"Return a string that says what is wrong with FILE."
|
||||
(format
|
||||
(cond
|
||||
((not (file-exists-p file))
|
||||
"%s does not exist")
|
||||
((file-directory-p file)
|
||||
"%s is a directory")
|
||||
((not (file-readable-p file))
|
||||
"%s is not readable"))
|
||||
file))
|
||||
|
||||
(defun nnheader-insert-head (file)
|
||||
"Insert the head of the article."
|
||||
(if (eq nnheader-max-head-length t)
|
||||
;; Just read the entire file.
|
||||
(insert-file-contents-literally file)
|
||||
;; Read 1K blocks until we find a separator.
|
||||
(let ((beg 0)
|
||||
(chop 1024))
|
||||
(while (and (eq chop (nth 1 (insert-file-contents-literally
|
||||
file nil beg (setq beg (+ beg chop)))))
|
||||
(prog1 (not (search-forward "\n\n" nil t))
|
||||
(goto-char (point-max)))
|
||||
(or (null nnheader-max-head-length)
|
||||
(< beg nnheader-max-head-length)))))))
|
||||
(when (file-exists-p file)
|
||||
(if (eq nnheader-max-head-length t)
|
||||
;; Just read the entire file.
|
||||
(nnheader-insert-file-contents-literally file)
|
||||
;; Read 1K blocks until we find a separator.
|
||||
(let ((beg 0)
|
||||
format-alist
|
||||
(chop 1024))
|
||||
(while (and (eq chop (nth 1 (insert-file-contents
|
||||
file nil beg (incf beg chop))))
|
||||
(prog1 (not (search-forward "\n\n" nil t))
|
||||
(goto-char (point-max)))
|
||||
(or (null nnheader-max-head-length)
|
||||
(< beg nnheader-max-head-length))))))
|
||||
t))
|
||||
|
||||
(defun nnheader-article-p ()
|
||||
"Say whether the current buffer looks like an article."
|
||||
(goto-char (point-min))
|
||||
(if (not (search-forward "\n\n" nil t))
|
||||
nil
|
||||
@ -218,152 +323,264 @@
|
||||
(eobp)
|
||||
(widen))))
|
||||
|
||||
;; Written by Erik Naggum <erik@naggum.no>.
|
||||
(defun nnheader-insert-file-contents-literally (filename &optional visit beg end replace)
|
||||
"Like `insert-file-contents', q.v., but only reads in the file.
|
||||
A buffer may be modified in several ways after reading into the buffer due
|
||||
to advanced Emacs features, such as file-name-handlers, format decoding,
|
||||
find-file-hooks, etc.
|
||||
This function ensures that none of these modifications will take place."
|
||||
(let ( ; (file-name-handler-alist nil)
|
||||
(format-alist nil)
|
||||
(after-insert-file-functions nil)
|
||||
(find-buffer-file-type-function
|
||||
(if (fboundp 'find-buffer-file-type)
|
||||
(symbol-function 'find-buffer-file-type)
|
||||
nil)))
|
||||
(unwind-protect
|
||||
(progn
|
||||
(fset 'find-buffer-file-type (lambda (filename) t))
|
||||
(insert-file-contents filename visit beg end replace))
|
||||
(if find-buffer-file-type-function
|
||||
(fset 'find-buffer-file-type find-buffer-file-type-function)
|
||||
(fmakunbound 'find-buffer-file-type)))))
|
||||
|
||||
(defun nnheader-find-file-noselect (filename &optional nowarn rawfile)
|
||||
"Read file FILENAME into a buffer and return the buffer.
|
||||
If a buffer exists visiting FILENAME, return that one, but
|
||||
verify that the file has not changed since visited or saved.
|
||||
The buffer is not selected, just returned to the caller."
|
||||
(setq filename
|
||||
(abbreviate-file-name
|
||||
(expand-file-name filename)))
|
||||
(if (file-directory-p filename)
|
||||
(if find-file-run-dired
|
||||
(dired-noselect filename)
|
||||
(error "%s is a directory." filename))
|
||||
(let* ((buf (get-file-buffer filename))
|
||||
(truename (abbreviate-file-name (file-truename filename)))
|
||||
(number (nthcdr 10 (file-attributes truename)))
|
||||
;; Find any buffer for a file which has same truename.
|
||||
(other (and (not buf)
|
||||
(if (fboundp 'find-buffer-visiting)
|
||||
(find-buffer-visiting filename)
|
||||
(get-file-buffer filename))))
|
||||
error)
|
||||
;; Let user know if there is a buffer with the same truename.
|
||||
(if other
|
||||
(progn
|
||||
(or nowarn
|
||||
(string-equal filename (buffer-file-name other))
|
||||
(message "%s and %s are the same file"
|
||||
filename (buffer-file-name other)))
|
||||
;; Optionally also find that buffer.
|
||||
(if (or (and (boundp 'find-file-existing-other-name)
|
||||
find-file-existing-other-name)
|
||||
find-file-visit-truename)
|
||||
(setq buf other))))
|
||||
(if buf
|
||||
(or nowarn
|
||||
(verify-visited-file-modtime buf)
|
||||
(cond ((not (file-exists-p filename))
|
||||
(error "File %s no longer exists!" filename))
|
||||
((yes-or-no-p
|
||||
(if (string= (file-name-nondirectory filename)
|
||||
(buffer-name buf))
|
||||
(format
|
||||
(if (buffer-modified-p buf)
|
||||
"File %s changed on disk. Discard your edits? "
|
||||
"File %s changed on disk. Reread from disk? ")
|
||||
(file-name-nondirectory filename))
|
||||
(format
|
||||
(if (buffer-modified-p buf)
|
||||
"File %s changed on disk. Discard your edits in %s? "
|
||||
"File %s changed on disk. Reread from disk into %s? ")
|
||||
(file-name-nondirectory filename)
|
||||
(buffer-name buf))))
|
||||
(save-excursion
|
||||
(set-buffer buf)
|
||||
(revert-buffer t t)))))
|
||||
(save-excursion
|
||||
;;; The truename stuff makes this obsolete.
|
||||
;;; (let* ((link-name (car (file-attributes filename)))
|
||||
;;; (linked-buf (and (stringp link-name)
|
||||
;;; (get-file-buffer link-name))))
|
||||
;;; (if (bufferp linked-buf)
|
||||
;;; (message "Symbolic link to file in buffer %s"
|
||||
;;; (buffer-name linked-buf))))
|
||||
(setq buf (create-file-buffer filename))
|
||||
;; (set-buffer-major-mode buf)
|
||||
(set-buffer buf)
|
||||
(erase-buffer)
|
||||
(if rawfile
|
||||
(condition-case ()
|
||||
(nnheader-insert-file-contents-literally filename t)
|
||||
(file-error
|
||||
;; Unconditionally set error
|
||||
(setq error t)))
|
||||
(condition-case ()
|
||||
(insert-file-contents filename t)
|
||||
(file-error
|
||||
;; Run find-file-not-found-hooks until one returns non-nil.
|
||||
(or t ; (run-hook-with-args-until-success 'find-file-not-found-hooks)
|
||||
;; If they fail too, set error.
|
||||
(setq error t)))))
|
||||
;; Find the file's truename, and maybe use that as visited name.
|
||||
(setq buffer-file-truename truename)
|
||||
(setq buffer-file-number number)
|
||||
;; On VMS, we may want to remember which directory in a search list
|
||||
;; the file was found in.
|
||||
(and (eq system-type 'vax-vms)
|
||||
(let (logical)
|
||||
(if (string-match ":" (file-name-directory filename))
|
||||
(setq logical (substring (file-name-directory filename)
|
||||
0 (match-beginning 0))))
|
||||
(not (member logical find-file-not-true-dirname-list)))
|
||||
(setq buffer-file-name buffer-file-truename))
|
||||
(if find-file-visit-truename
|
||||
(setq buffer-file-name
|
||||
(setq filename
|
||||
(expand-file-name buffer-file-truename))))
|
||||
;; Set buffer's default directory to that of the file.
|
||||
(setq default-directory (file-name-directory filename))
|
||||
;; Turn off backup files for certain file names. Since
|
||||
;; this is a permanent local, the major mode won't eliminate it.
|
||||
(and (not (funcall backup-enable-predicate buffer-file-name))
|
||||
(progn
|
||||
(make-local-variable 'backup-inhibited)
|
||||
(setq backup-inhibited t)))
|
||||
(if rawfile
|
||||
nil
|
||||
(after-find-file error (not nowarn)))))
|
||||
buf)))
|
||||
|
||||
(defun nnheader-insert-references (references message-id)
|
||||
"Insert a References header based on REFERENCES and MESSAGE-ID."
|
||||
(if (and (not references) (not message-id))
|
||||
() ; This is illegal, but not all articles have Message-IDs.
|
||||
() ; This is illegal, but not all articles have Message-IDs.
|
||||
(mail-position-on-field "References")
|
||||
;; Fold long references line to follow RFC1036.
|
||||
(let ((begin (gnus-point-at-bol))
|
||||
(let ((begin (save-excursion (beginning-of-line) (point)))
|
||||
(fill-column 78)
|
||||
(fill-prefix "\t"))
|
||||
(if references (insert references))
|
||||
(if (and references message-id) (insert " "))
|
||||
(if message-id (insert message-id))
|
||||
;; Fold long References lines to conform to RFC1036 (sort of).
|
||||
;; The region must end with a newline to fill the region
|
||||
;; without inserting extra newline.
|
||||
(fill-region-as-paragraph begin (1+ (point))))))
|
||||
|
||||
(defun nnheader-replace-header (header new-value)
|
||||
"Remove HEADER and insert the NEW-VALUE."
|
||||
(save-excursion
|
||||
(save-restriction
|
||||
(nnheader-narrow-to-headers)
|
||||
(prog1
|
||||
(message-remove-header header)
|
||||
(goto-char (point-max))
|
||||
(insert header ": " new-value "\n")))))
|
||||
|
||||
(defun nnheader-narrow-to-headers ()
|
||||
"Narrow to the head of an article."
|
||||
(widen)
|
||||
(narrow-to-region
|
||||
(goto-char (point-min))
|
||||
(if (search-forward "\n\n" nil t)
|
||||
(1- (point))
|
||||
(point-max)))
|
||||
(goto-char (point-min)))
|
||||
|
||||
(defun nnheader-set-temp-buffer (name)
|
||||
"Set-buffer to an empty (possibly new) buffer called NAME with undo disabled."
|
||||
(set-buffer (get-buffer-create name))
|
||||
(buffer-disable-undo (current-buffer))
|
||||
(erase-buffer)
|
||||
(current-buffer))
|
||||
|
||||
(defmacro nnheader-temp-write (file &rest forms)
|
||||
"Create a new buffer, evaluate FORM there, and write the buffer to FILE."
|
||||
`(save-excursion
|
||||
(let ((nnheader-temp-file ,file)
|
||||
(nnheader-temp-cur-buffer
|
||||
(nnheader-set-temp-buffer
|
||||
(generate-new-buffer-name " *nnheader temp*"))))
|
||||
(when (and nnheader-temp-file
|
||||
(not (file-directory-p (file-name-directory
|
||||
nnheader-temp-file))))
|
||||
(make-directory (file-name-directory nnheader-temp-file) t))
|
||||
(unwind-protect
|
||||
(prog1
|
||||
(progn
|
||||
,@forms)
|
||||
(when nnheader-temp-file
|
||||
(set-buffer nnheader-temp-cur-buffer)
|
||||
(write-region (point-min) (point-max)
|
||||
nnheader-temp-file nil 'nomesg)))
|
||||
(when (buffer-name nnheader-temp-cur-buffer)
|
||||
(kill-buffer nnheader-temp-cur-buffer))))))
|
||||
|
||||
(put 'nnheader-temp-write 'lisp-indent-function 1)
|
||||
(put 'nnheader-temp-write 'lisp-indent-hook 1)
|
||||
(put 'nnheader-temp-write 'edebug-form-spec '(form body))
|
||||
|
||||
(defvar jka-compr-compression-info-list)
|
||||
(defvar nnheader-numerical-files
|
||||
(if (boundp 'jka-compr-compression-info-list)
|
||||
(concat "\\([0-9]+\\)\\("
|
||||
(mapconcat (lambda (i) (aref i 0))
|
||||
jka-compr-compression-info-list "\\|")
|
||||
"\\)?")
|
||||
"[0-9]+$")
|
||||
"Regexp that match numerical files.")
|
||||
|
||||
(defvar nnheader-numerical-short-files (concat "^" nnheader-numerical-files)
|
||||
"Regexp that matches numerical file names.")
|
||||
|
||||
(defvar nnheader-numerical-full-files (concat "/" nnheader-numerical-files)
|
||||
"Regexp that matches numerical full file paths.")
|
||||
|
||||
(defsubst nnheader-file-to-number (file)
|
||||
"Take a file name and return the article number."
|
||||
(if (not (boundp 'jka-compr-compression-info-list))
|
||||
(string-to-int file)
|
||||
(string-match nnheader-numerical-short-files file)
|
||||
(string-to-int (match-string 0 file))))
|
||||
|
||||
(defun nnheader-directory-files-safe (&rest args)
|
||||
;; It has been reported numerous times that `directory-files'
|
||||
;; fails with an alarming frequency on NFS mounted file systems.
|
||||
;; This function executes that function twice and returns
|
||||
;; the longest result.
|
||||
(let ((first (apply 'directory-files args))
|
||||
(second (apply 'directory-files args)))
|
||||
(if (> (length first) (length second))
|
||||
first
|
||||
second)))
|
||||
|
||||
(defun nnheader-directory-articles (dir)
|
||||
"Return a list of all article files in a directory."
|
||||
(mapcar 'nnheader-file-to-number
|
||||
(nnheader-directory-files-safe
|
||||
dir nil nnheader-numerical-short-files t)))
|
||||
|
||||
(defun nnheader-article-to-file-alist (dir)
|
||||
"Return an alist of article/file pairs in DIR."
|
||||
(mapcar (lambda (file) (cons (nnheader-file-to-number file) file))
|
||||
(nnheader-directory-files-safe
|
||||
dir nil nnheader-numerical-short-files t)))
|
||||
|
||||
(defun nnheader-fold-continuation-lines ()
|
||||
"Fold continuation lines in the current buffer."
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward "\\(\r?\n[ \t]+\\)+" nil t)
|
||||
(replace-match " " t t)))
|
||||
|
||||
(defun nnheader-translate-file-chars (file)
|
||||
(if (null nnheader-file-name-translation-alist)
|
||||
;; No translation is necessary.
|
||||
file
|
||||
;; We translate -- but only the file name. We leave the directory
|
||||
;; alone.
|
||||
(let* ((i 0)
|
||||
trans leaf path len)
|
||||
(if (string-match "/[^/]+\\'" file)
|
||||
;; This is needed on NT's and stuff.
|
||||
(setq leaf (substring file (1+ (match-beginning 0)))
|
||||
path (substring file 0 (1+ (match-beginning 0))))
|
||||
;; Fall back on this.
|
||||
(setq leaf (file-name-nondirectory file)
|
||||
path (file-name-directory file)))
|
||||
(setq len (length leaf))
|
||||
(while (< i len)
|
||||
(when (setq trans (cdr (assq (aref leaf i)
|
||||
nnheader-file-name-translation-alist)))
|
||||
(aset leaf i trans))
|
||||
(incf i))
|
||||
(concat path leaf))))
|
||||
|
||||
(defun nnheader-report (backend &rest args)
|
||||
"Report an error from the BACKEND.
|
||||
The first string in ARGS can be a format string."
|
||||
(set (intern (format "%s-status-string" backend))
|
||||
(if (< (length args) 2)
|
||||
(car args)
|
||||
(apply 'format args)))
|
||||
nil)
|
||||
|
||||
(defun nnheader-get-report (backend)
|
||||
(message "%s" (symbol-value (intern (format "%s-status-string" backend)))))
|
||||
|
||||
(defun nnheader-insert (format &rest args)
|
||||
"Clear the communicaton buffer and insert FORMAT and ARGS into the buffer.
|
||||
If FORMAT isn't a format string, it and all ARGS will be inserted
|
||||
without formatting."
|
||||
(save-excursion
|
||||
(set-buffer nntp-server-buffer)
|
||||
(erase-buffer)
|
||||
(if (string-match "%" format)
|
||||
(insert (apply 'format format args))
|
||||
(apply 'insert format args))
|
||||
t))
|
||||
|
||||
(defun nnheader-mail-file-mbox-p (file)
|
||||
"Say whether FILE looks like an Unix mbox file."
|
||||
(when (and (file-exists-p file)
|
||||
(file-readable-p file)
|
||||
(file-regular-p file))
|
||||
(save-excursion
|
||||
(nnheader-set-temp-buffer " *mail-file-mbox-p*")
|
||||
(nnheader-insert-file-contents-literally file)
|
||||
(goto-char (point-min))
|
||||
(prog1
|
||||
(looking-at message-unix-mail-delimiter)
|
||||
(kill-buffer (current-buffer))))))
|
||||
|
||||
(defun nnheader-replace-chars-in-string (string from to)
|
||||
"Replace characters in STRING from FROM to TO."
|
||||
(let ((string (substring string 0)) ;Copy string.
|
||||
(len (length string))
|
||||
(idx 0))
|
||||
;; Replace all occurrences of FROM with TO.
|
||||
(while (< idx len)
|
||||
(if (= (aref string idx) from)
|
||||
(aset string idx to))
|
||||
(setq idx (1+ idx)))
|
||||
string))
|
||||
|
||||
(defun nnheader-file-to-group (file &optional top)
|
||||
"Return a group name based on FILE and TOP."
|
||||
(nnheader-replace-chars-in-string
|
||||
(if (not top)
|
||||
file
|
||||
(condition-case ()
|
||||
(substring (expand-file-name file)
|
||||
(length
|
||||
(expand-file-name
|
||||
(file-name-as-directory top))))
|
||||
(error "")))
|
||||
?/ ?.))
|
||||
|
||||
(defun nnheader-message (level &rest args)
|
||||
"Message if the Gnus backends are talkative."
|
||||
(if (or (not (numberp gnus-verbose-backends))
|
||||
(<= level gnus-verbose-backends))
|
||||
(apply 'message args)
|
||||
(apply 'format args)))
|
||||
|
||||
(defun nnheader-be-verbose (level)
|
||||
"Return whether the backends should be verbose on LEVEL."
|
||||
(or (not (numberp gnus-verbose-backends))
|
||||
(<= level gnus-verbose-backends)))
|
||||
|
||||
(defun nnheader-group-pathname (group dir &optional file)
|
||||
"Make pathname for GROUP."
|
||||
(concat
|
||||
(let ((dir (file-name-as-directory (expand-file-name dir))))
|
||||
;; If this directory exists, we use it directly.
|
||||
(if (file-directory-p (concat dir group))
|
||||
(concat dir group "/")
|
||||
;; If not, we translate dots into slashes.
|
||||
(concat dir (nnheader-replace-chars-in-string group ?. ?/) "/")))
|
||||
(cond ((null file) "")
|
||||
((numberp file) (int-to-string file))
|
||||
(t file))))
|
||||
|
||||
(defun nnheader-functionp (form)
|
||||
"Return non-nil if FORM is funcallable."
|
||||
(or (and (symbolp form) (fboundp form))
|
||||
(and (listp form) (eq (car form) 'lambda))))
|
||||
|
||||
(defun nnheader-concat (dir file)
|
||||
"Concat DIR as directory to FILE."
|
||||
(concat (file-name-as-directory dir) file))
|
||||
|
||||
(defun nnheader-ms-strip-cr ()
|
||||
"Strip ^M from the end of all lines."
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward "\r$" nil t)
|
||||
(delete-backward-char 1))))
|
||||
|
||||
(fset 'nnheader-run-at-time 'run-at-time)
|
||||
(fset 'nnheader-cancel-timer 'cancel-timer)
|
||||
(fset 'nnheader-find-file-noselect 'find-file-noselect)
|
||||
(fset 'nnheader-insert-file-contents-literally
|
||||
'insert-file-contents-literally)
|
||||
|
||||
(when (string-match "XEmacs\\|Lucid" emacs-version)
|
||||
(require 'nnheaderxm))
|
||||
|
||||
(run-hooks 'nnheader-load-hook)
|
||||
|
||||
(provide 'nnheader)
|
||||
|
||||
;;; nnheader.el ends here
|
||||
|
180
lisp/nnkiboze.el
180
lisp/nnkiboze.el
@ -1,6 +1,5 @@
|
||||
;;; nnkiboze.el --- select virtual news access for Gnus
|
||||
|
||||
;; Copyright (C) 1995 Free Software Foundation, Inc.
|
||||
;; Copyright (C) 1995,96 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
|
||||
;; Keywords: news
|
||||
@ -34,25 +33,35 @@
|
||||
(require 'nnheader)
|
||||
(require 'gnus)
|
||||
(require 'gnus-score)
|
||||
(require 'nnoo)
|
||||
(eval-when-compile (require 'cl))
|
||||
|
||||
(defvar nnkiboze-directory
|
||||
(expand-file-name (or gnus-article-save-directory "~/News/"))
|
||||
(nnoo-declare nnkiboze)
|
||||
(defvoo nnkiboze-directory gnus-directory
|
||||
"nnkiboze will put its files in this directory.")
|
||||
|
||||
(defvoo nnkiboze-level 9
|
||||
"*The maximum level to be searched for articles.")
|
||||
|
||||
(defvoo nnkiboze-remove-read-articles t
|
||||
"*If non-nil, nnkiboze will remove read articles from the kiboze group.")
|
||||
|
||||
|
||||
|
||||
(defconst nnkiboze-version "nnkiboze 1.0"
|
||||
"Version numbers of this version of nnkiboze.")
|
||||
|
||||
(defvar nnkiboze-current-group nil)
|
||||
(defvar nnkiboze-current-score-group "")
|
||||
(defvar nnkiboze-status-string "")
|
||||
(defvoo nnkiboze-current-group nil)
|
||||
(defvoo nnkiboze-current-score-group "")
|
||||
(defvoo nnkiboze-status-string "")
|
||||
|
||||
|
||||
|
||||
;;; Interface functions.
|
||||
|
||||
(defun nnkiboze-retrieve-headers (articles &optional group server)
|
||||
(nnoo-define-basics nnkiboze)
|
||||
|
||||
(deffoo nnkiboze-retrieve-headers (articles &optional group server fetch-old)
|
||||
(nnkiboze-possibly-change-newsgroups group)
|
||||
(if gnus-nov-is-evil
|
||||
nil
|
||||
@ -78,36 +87,22 @@
|
||||
(if (not (eobp)) (delete-region (point) (point-max)))
|
||||
'nov))))))
|
||||
|
||||
(defun nnkiboze-open-server (newsgroups &optional something)
|
||||
"Open a virtual newsgroup that contains NEWSGROUPS."
|
||||
(deffoo nnkiboze-open-server (newsgroups &optional something)
|
||||
(gnus-make-directory nnkiboze-directory)
|
||||
(nnheader-init-server-buffer))
|
||||
|
||||
(defun nnkiboze-close-server (&rest dum)
|
||||
"Close news server."
|
||||
t)
|
||||
|
||||
(defalias 'nnkiboze-request-quit (symbol-function 'nnkiboze-close-server))
|
||||
|
||||
(defun nnkiboze-server-opened (&optional server)
|
||||
"Return server process status, T or NIL.
|
||||
If the stream is opened, return T, otherwise return NIL."
|
||||
(deffoo nnkiboze-server-opened (&optional server)
|
||||
(and nntp-server-buffer
|
||||
(get-buffer nntp-server-buffer)))
|
||||
|
||||
(defun nnkiboze-status-message (&optional server)
|
||||
"Return server status response as string."
|
||||
nnkiboze-status-string)
|
||||
|
||||
(defun nnkiboze-request-article (article &optional newsgroup server buffer)
|
||||
"Select article by message number."
|
||||
(deffoo nnkiboze-request-article (article &optional newsgroup server buffer)
|
||||
(nnkiboze-possibly-change-newsgroups newsgroup)
|
||||
(if (not (numberp article))
|
||||
;; This is a real kludge. It might not work at times, but it
|
||||
;; does no harm I think. The only alternative is to offer no
|
||||
;; article fetching by message-id at all.
|
||||
(nntp-request-article article newsgroup gnus-nntp-server buffer)
|
||||
(let* ((header (gnus-get-header-by-number article))
|
||||
(let* ((header (gnus-summary-article-header article))
|
||||
(xref (mail-header-xref header))
|
||||
igroup iarticle)
|
||||
(or xref (error "nnkiboze: No xref"))
|
||||
@ -119,7 +114,7 @@ If the stream is opened, return T, otherwise return NIL."
|
||||
(and (gnus-request-group igroup t)
|
||||
(gnus-request-article iarticle igroup buffer)))))
|
||||
|
||||
(defun nnkiboze-request-group (group &optional server dont-check)
|
||||
(deffoo nnkiboze-request-group (group &optional server dont-check)
|
||||
"Make GROUP the current newsgroup."
|
||||
(nnkiboze-possibly-change-newsgroups group)
|
||||
(if dont-check
|
||||
@ -144,15 +139,16 @@ If the stream is opened, return T, otherwise return NIL."
|
||||
(insert (format "211 %d %d %d %s\n" total beg end group)))))))
|
||||
t)
|
||||
|
||||
(defun nnkiboze-close-group (group &optional server)
|
||||
(deffoo nnkiboze-close-group (group &optional server)
|
||||
(nnkiboze-possibly-change-newsgroups group)
|
||||
;; Remove NOV lines of articles that are marked as read.
|
||||
(if (or (not (file-exists-p (nnkiboze-nov-file-name)))
|
||||
(not (eq major-mode 'gnus-summary-mode)))
|
||||
()
|
||||
(when (and (file-exists-p (nnkiboze-nov-file-name))
|
||||
nnkiboze-remove-read-articles
|
||||
(eq major-mode 'gnus-summary-mode))
|
||||
(save-excursion
|
||||
(let ((unreads gnus-newsgroup-unreads)
|
||||
(unselected gnus-newsgroup-unselected))
|
||||
(unselected gnus-newsgroup-unselected)
|
||||
(version-control 'never))
|
||||
(set-buffer (get-buffer-create "*nnkiboze work*"))
|
||||
(buffer-disable-undo (current-buffer))
|
||||
(erase-buffer)
|
||||
@ -170,22 +166,28 @@ If the stream is opened, return T, otherwise return NIL."
|
||||
(kill-buffer (current-buffer)))))
|
||||
(setq nnkiboze-current-group nil)))
|
||||
|
||||
(defun nnkiboze-request-list (&optional server)
|
||||
(setq nnkiboze-status-string "nnkiboze: LIST is not implemented.")
|
||||
nil)
|
||||
(deffoo nnkiboze-request-list (&optional server)
|
||||
(nnheader-report 'nnkiboze "LIST is not implemented."))
|
||||
|
||||
(defun nnkiboze-request-newgroups (date &optional server)
|
||||
(deffoo nnkiboze-request-newgroups (date &optional server)
|
||||
"List new groups."
|
||||
(setq nnkiboze-status-string "NEWGROUPS is not supported.")
|
||||
nil)
|
||||
(nnheader-report 'nnkiboze "NEWGROUPS is not supported."))
|
||||
|
||||
(defun nnkiboze-request-list-newsgroups (&optional server)
|
||||
(setq nnkiboze-status-string "nnkiboze: LIST NEWSGROUPS is not implemented.")
|
||||
nil)
|
||||
(deffoo nnkiboze-request-list-newsgroups (&optional server)
|
||||
(nnheader-report 'nnkiboze "LIST NEWSGROUPS is not implemented."))
|
||||
|
||||
(defalias 'nnkiboze-request-post 'nntp-request-post)
|
||||
|
||||
(defalias 'nnkiboze-request-post-buffer 'nntp-request-post-buffer)
|
||||
(deffoo nnkiboze-request-delete-group (group &optional force server)
|
||||
(nnkiboze-possibly-change-newsgroups group)
|
||||
(when force
|
||||
(let ((files (list (nnkiboze-nov-file-name)
|
||||
(concat nnkiboze-directory group ".newsrc")
|
||||
(nnkiboze-score-file group))))
|
||||
(while files
|
||||
(and (file-exists-p (car files))
|
||||
(file-writable-p (car files))
|
||||
(delete-file (car files)))
|
||||
(setq files (cdr files)))))
|
||||
(setq nnkiboze-current-group nil))
|
||||
|
||||
|
||||
;;; Internal functions.
|
||||
@ -207,16 +209,24 @@ Finds out what articles are to be part of the nnkiboze groups."
|
||||
(gnus-expert-user t))
|
||||
(gnus))
|
||||
(let* ((gnus-newsrc-alist (gnus-copy-sequence gnus-newsrc-alist))
|
||||
(newsrc gnus-newsrc-alist))
|
||||
(newsrc gnus-newsrc-alist)
|
||||
gnus-newsrc-hashtb)
|
||||
(gnus-make-hashtable-from-newsrc-alist)
|
||||
;; We have copied all the newsrc alist info over to local copies
|
||||
;; so that we can mess all we want with these lists.
|
||||
(while newsrc
|
||||
(if (string-match "nnkiboze" (car (car newsrc)))
|
||||
(nnkiboze-generate-group (car (car newsrc))))
|
||||
(if (string-match "nnkiboze" (caar newsrc))
|
||||
;; For each kiboze group, we call this function to generate
|
||||
;; it.
|
||||
(nnkiboze-generate-group (caar newsrc)))
|
||||
(setq newsrc (cdr newsrc)))))
|
||||
|
||||
(defun nnkiboze-score-file (group)
|
||||
(list (expand-file-name
|
||||
(concat gnus-kill-files-directory nnkiboze-current-score-group
|
||||
"." gnus-score-file-suffix))))
|
||||
(concat (file-name-as-directory gnus-kill-files-directory)
|
||||
(nnheader-translate-file-chars
|
||||
(concat nnkiboze-current-score-group
|
||||
"." gnus-score-file-suffix))))))
|
||||
|
||||
(defun nnkiboze-generate-group (group)
|
||||
(let* ((info (nth 2 (gnus-gethash group gnus-newsrc-hashtb)))
|
||||
@ -225,15 +235,18 @@ Finds out what articles are to be part of the nnkiboze groups."
|
||||
(regexp (nth 1 (nth 4 info)))
|
||||
(gnus-expert-user t)
|
||||
(gnus-large-newsgroup nil)
|
||||
(version-control 'never)
|
||||
(gnus-score-find-score-files-function 'nnkiboze-score-file)
|
||||
gnus-select-group-hook gnus-summary-prepare-hook
|
||||
gnus-thread-sort-functions gnus-show-threads
|
||||
gnus-visual
|
||||
method nnkiboze-newsrc nov-buffer gname newsrc active
|
||||
ginfo lowest)
|
||||
ginfo lowest glevel)
|
||||
(setq nnkiboze-current-score-group group)
|
||||
(or info (error "No such group: %s" group))
|
||||
;; Load the kiboze newsrc file for this group.
|
||||
(and (file-exists-p newsrc-file) (load newsrc-file))
|
||||
;; We also load the nov file for this group.
|
||||
(save-excursion
|
||||
(set-buffer (setq nov-buffer (find-file-noselect nov-file)))
|
||||
(buffer-disable-undo (current-buffer)))
|
||||
@ -241,50 +254,76 @@ Finds out what articles are to be part of the nnkiboze groups."
|
||||
;; kiboze regexp.
|
||||
(mapatoms
|
||||
(lambda (group)
|
||||
(if (and (string-match regexp (setq gname (symbol-name group))) ; Match
|
||||
(not (assoc gname nnkiboze-newsrc)) ; It isn't registered
|
||||
(numberp (car (symbol-value group))) ; It is active
|
||||
(not (string-match "^nnkiboze:" gname))) ; Exclude kibozes
|
||||
(setq nnkiboze-newsrc
|
||||
(cons (cons gname (1- (car (symbol-value group))))
|
||||
nnkiboze-newsrc))))
|
||||
(and (string-match regexp (setq gname (symbol-name group))) ; Match
|
||||
(not (assoc gname nnkiboze-newsrc)) ; It isn't registered
|
||||
(numberp (car (symbol-value group))) ; It is active
|
||||
(or (> nnkiboze-level 7)
|
||||
(and (setq glevel (nth 1 (nth 2 (gnus-gethash
|
||||
gname gnus-newsrc-hashtb))))
|
||||
(>= nnkiboze-level glevel)))
|
||||
(not (string-match "^nnkiboze:" gname)) ; Exclude kibozes
|
||||
(setq nnkiboze-newsrc
|
||||
(cons (cons gname (1- (car (symbol-value group))))
|
||||
nnkiboze-newsrc))))
|
||||
gnus-active-hashtb)
|
||||
;; `newsrc' is set to the list of groups that possibly are
|
||||
;; component groups to this kiboze group. This list has elements
|
||||
;; on the form `(GROUP . NUMBER)', where NUMBER is the highest
|
||||
;; number that has been kibozed in GROUP in this kiboze group.
|
||||
(setq newsrc nnkiboze-newsrc)
|
||||
(while newsrc
|
||||
(if (not (setq active (gnus-gethash
|
||||
(car (car newsrc)) gnus-active-hashtb)))
|
||||
(caar newsrc) gnus-active-hashtb)))
|
||||
;; This group isn't active after all, so we remove it from
|
||||
;; the list of component groups.
|
||||
(setq nnkiboze-newsrc (delq (car newsrc) nnkiboze-newsrc))
|
||||
(setq lowest (cdar newsrc))
|
||||
;; Ok, we have a valid component group, so we jump to it.
|
||||
(switch-to-buffer gnus-group-buffer)
|
||||
(gnus-group-jump-to-group (car (car newsrc)))
|
||||
(if (and (setq ginfo (nth 2 (gnus-gethash (gnus-group-group-name)
|
||||
gnus-newsrc-hashtb)))
|
||||
(nth 3 ginfo))
|
||||
(setcar (nthcdr 3 ginfo) nil))
|
||||
(gnus-group-jump-to-group (caar newsrc))
|
||||
;; We set all list of article marks to nil. Since we operate
|
||||
;; on copies of the real lists, we can destroy anything we
|
||||
;; want here.
|
||||
(and (setq ginfo (nth 2 (gnus-gethash (gnus-group-group-name)
|
||||
gnus-newsrc-hashtb)))
|
||||
(nth 3 ginfo)
|
||||
(setcar (nthcdr 3 ginfo) nil))
|
||||
;; We set the list of read articles to be what we expect for
|
||||
;; this kiboze group -- either nil or `(1 . LOWEST)'.
|
||||
(and ginfo (setcar (nthcdr 2 ginfo)
|
||||
(and (not (= lowest 1)) (cons 1 lowest))))
|
||||
(if (not (and (or (not ginfo)
|
||||
(> (length (gnus-list-of-unread-articles
|
||||
(car ginfo))) 0))
|
||||
(progn
|
||||
(gnus-group-select-group nil)
|
||||
(eq major-mode 'gnus-summary-mode))))
|
||||
()
|
||||
(setq lowest (cdr (car newsrc)))
|
||||
() ; No unread articles, or we couldn't enter this group.
|
||||
;; We are now in the group where we want to be.
|
||||
(setq method (gnus-find-method-for-group gnus-newsgroup-name))
|
||||
(and (eq method gnus-select-method) (setq method nil))
|
||||
;; We go through the list of scored articles.
|
||||
(while gnus-newsgroup-scored
|
||||
(if (> (car (car gnus-newsgroup-scored)) lowest)
|
||||
(if (> (caar gnus-newsgroup-scored) lowest)
|
||||
;; If it has a good score, then we enter this article
|
||||
;; into the kiboze group.
|
||||
(nnkiboze-enter-nov
|
||||
nov-buffer
|
||||
(gnus-get-header-by-number (car (car gnus-newsgroup-scored)))
|
||||
(gnus-summary-article-header
|
||||
(caar gnus-newsgroup-scored))
|
||||
(if method
|
||||
(gnus-group-prefixed-name gnus-newsgroup-name method)
|
||||
gnus-newsgroup-name)))
|
||||
(setq gnus-newsgroup-scored (cdr gnus-newsgroup-scored)))
|
||||
(gnus-summary-quit)))
|
||||
;; That's it. We exit this group.
|
||||
(gnus-summary-exit-no-update)))
|
||||
(setcdr (car newsrc) (car active))
|
||||
(setq newsrc (cdr newsrc)))
|
||||
;; We save the nov file.
|
||||
(set-buffer nov-buffer)
|
||||
(save-buffer)
|
||||
(kill-buffer (current-buffer))
|
||||
;; We save the kiboze newsrc for this group.
|
||||
(set-buffer (get-buffer-create "*nnkiboze work*"))
|
||||
(buffer-disable-undo (current-buffer))
|
||||
(erase-buffer)
|
||||
@ -340,8 +379,9 @@ Finds out what articles are to be part of the nnkiboze groups."
|
||||
(insert prefix)))))))
|
||||
|
||||
(defun nnkiboze-nov-file-name ()
|
||||
(concat nnkiboze-directory
|
||||
(nnkiboze-prefixed-name nnkiboze-current-group) ".nov"))
|
||||
(concat (file-name-as-directory nnkiboze-directory)
|
||||
(nnheader-translate-file-chars
|
||||
(concat (nnkiboze-prefixed-name nnkiboze-current-group) ".nov"))))
|
||||
|
||||
(provide 'nnkiboze)
|
||||
|
||||
|
1188
lisp/nnmail.el
1188
lisp/nnmail.el
File diff suppressed because it is too large
Load Diff
475
lisp/nnmbox.el
475
lisp/nnmbox.el
@ -1,6 +1,5 @@
|
||||
;;; nnmbox.el --- mail mbox access for Gnus
|
||||
|
||||
;; Copyright (C) 1995 Free Software Foundation, Inc.
|
||||
;; Copyright (C) 1995,96 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
|
||||
;; Masanobu UMEDA <umerin@flab.flab.fujitsu.junet>
|
||||
@ -31,19 +30,23 @@
|
||||
;;; Code:
|
||||
|
||||
(require 'nnheader)
|
||||
(require 'rmail)
|
||||
(require 'message)
|
||||
(require 'nnmail)
|
||||
(require 'nnoo)
|
||||
(eval-when-compile (require 'cl))
|
||||
|
||||
(defvar nnmbox-mbox-file (expand-file-name "~/mbox")
|
||||
(nnoo-declare nnmbox)
|
||||
|
||||
(defvoo nnmbox-mbox-file (expand-file-name "~/mbox")
|
||||
"The name of the mail box file in the user's home directory.")
|
||||
|
||||
(defvar nnmbox-active-file (expand-file-name "~/.mbox-active")
|
||||
(defvoo nnmbox-active-file (expand-file-name "~/.mbox-active")
|
||||
"The name of the active file for the mail box.")
|
||||
|
||||
(defvar nnmbox-get-new-mail t
|
||||
(defvoo nnmbox-get-new-mail t
|
||||
"If non-nil, nnmbox will check the incoming mail file and split the mail.")
|
||||
|
||||
(defvar nnmbox-prepare-save-mail-hook nil
|
||||
(defvoo nnmbox-prepare-save-mail-hook nil
|
||||
"Hook run narrowed to an article before saving.")
|
||||
|
||||
|
||||
@ -51,191 +54,172 @@
|
||||
(defconst nnmbox-version "nnmbox 1.0"
|
||||
"nnmbox version.")
|
||||
|
||||
(defvar nnmbox-current-group nil
|
||||
(defvoo nnmbox-current-group nil
|
||||
"Current nnmbox news group directory.")
|
||||
|
||||
(defconst nnmbox-mbox-buffer nil)
|
||||
|
||||
(defvar nnmbox-status-string "")
|
||||
(defvoo nnmbox-status-string "")
|
||||
|
||||
(defvar nnmbox-group-alist nil)
|
||||
(defvar nnmbox-active-timestamp nil)
|
||||
|
||||
|
||||
|
||||
(defvar nnmbox-current-server nil)
|
||||
(defvar nnmbox-server-alist nil)
|
||||
(defvar nnmbox-server-variables
|
||||
(list
|
||||
(list 'nnmbox-mbox-file nnmbox-mbox-file)
|
||||
(list 'nnmbox-active-file nnmbox-active-file)
|
||||
(list 'nnmbox-get-new-mail nnmbox-get-new-mail)
|
||||
'(nnmbox-current-group nil)
|
||||
'(nnmbox-status-string "")
|
||||
'(nnmbox-group-alist nil)))
|
||||
(defvoo nnmbox-group-alist nil)
|
||||
(defvoo nnmbox-active-timestamp nil)
|
||||
|
||||
|
||||
|
||||
;;; Interface functions
|
||||
|
||||
(defun nnmbox-retrieve-headers (sequence &optional newsgroup server)
|
||||
(nnoo-define-basics nnmbox)
|
||||
|
||||
(deffoo nnmbox-retrieve-headers (sequence &optional newsgroup server fetch-old)
|
||||
(save-excursion
|
||||
(set-buffer nntp-server-buffer)
|
||||
(erase-buffer)
|
||||
(let ((number (length sequence))
|
||||
(count 0)
|
||||
article art-string start stop)
|
||||
(nnmbox-possibly-change-newsgroup newsgroup)
|
||||
(if (stringp (car sequence))
|
||||
'headers
|
||||
(while sequence
|
||||
(setq article (car sequence))
|
||||
(setq art-string (nnmbox-article-string article))
|
||||
(set-buffer nnmbox-mbox-buffer)
|
||||
(if (or (search-forward art-string nil t)
|
||||
(progn (goto-char (point-min))
|
||||
(search-forward art-string nil t)))
|
||||
(progn
|
||||
(setq start
|
||||
(save-excursion
|
||||
(re-search-backward
|
||||
(concat "^" rmail-unix-mail-delimiter) nil t)
|
||||
(point)))
|
||||
(search-forward "\n\n" nil t)
|
||||
(setq stop (1- (point)))
|
||||
(set-buffer nntp-server-buffer)
|
||||
(insert (format "221 %d Article retrieved.\n" article))
|
||||
(insert-buffer-substring nnmbox-mbox-buffer start stop)
|
||||
(goto-char (point-max))
|
||||
(insert ".\n")))
|
||||
(setq sequence (cdr sequence))
|
||||
(setq count (1+ count))
|
||||
(and (numberp nnmail-large-newsgroup)
|
||||
(> number nnmail-large-newsgroup)
|
||||
(zerop (% count 20))
|
||||
gnus-verbose-backends
|
||||
(message "nnmbox: Receiving headers... %d%%"
|
||||
(/ (* count 100) number))))
|
||||
|
||||
(nnmbox-possibly-change-newsgroup newsgroup server)
|
||||
(while sequence
|
||||
(setq article (car sequence))
|
||||
(setq art-string (nnmbox-article-string article))
|
||||
(set-buffer nnmbox-mbox-buffer)
|
||||
(if (or (search-forward art-string nil t)
|
||||
(progn (goto-char (point-min))
|
||||
(search-forward art-string nil t)))
|
||||
(progn
|
||||
(setq start
|
||||
(save-excursion
|
||||
(re-search-backward
|
||||
(concat "^" message-unix-mail-delimiter) nil t)
|
||||
(point)))
|
||||
(search-forward "\n\n" nil t)
|
||||
(setq stop (1- (point)))
|
||||
(set-buffer nntp-server-buffer)
|
||||
(insert (format "221 %d Article retrieved.\n" article))
|
||||
(insert-buffer-substring nnmbox-mbox-buffer start stop)
|
||||
(goto-char (point-max))
|
||||
(insert ".\n")))
|
||||
(setq sequence (cdr sequence))
|
||||
(setq count (1+ count))
|
||||
(and (numberp nnmail-large-newsgroup)
|
||||
(> number nnmail-large-newsgroup)
|
||||
gnus-verbose-backends
|
||||
(message "nnmbox: Receiving headers...done"))
|
||||
(zerop (% count 20))
|
||||
(nnheader-message 5 "nnmbox: Receiving headers... %d%%"
|
||||
(/ (* count 100) number))))
|
||||
|
||||
;; Fold continuation lines.
|
||||
(set-buffer nntp-server-buffer)
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward "\\(\r?\n[ \t]+\\)+" nil t)
|
||||
(replace-match " " t t))
|
||||
'headers))))
|
||||
(and (numberp nnmail-large-newsgroup)
|
||||
(> number nnmail-large-newsgroup)
|
||||
(nnheader-message 5 "nnmbox: Receiving headers...done"))
|
||||
|
||||
(defun nnmbox-open-server (server &optional defs)
|
||||
(nnheader-init-server-buffer)
|
||||
(if (equal server nnmbox-current-server)
|
||||
t
|
||||
(if nnmbox-current-server
|
||||
(setq nnmbox-server-alist
|
||||
(cons (list nnmbox-current-server
|
||||
(nnheader-save-variables nnmbox-server-variables))
|
||||
nnmbox-server-alist)))
|
||||
(let ((state (assoc server nnmbox-server-alist)))
|
||||
(if state
|
||||
(progn
|
||||
(nnheader-restore-variables (nth 1 state))
|
||||
(setq nnmbox-server-alist (delq state nnmbox-server-alist)))
|
||||
(nnheader-set-init-variables nnmbox-server-variables defs)))
|
||||
(setq nnmbox-current-server server)))
|
||||
(set-buffer nntp-server-buffer)
|
||||
(nnheader-fold-continuation-lines)
|
||||
'headers)))
|
||||
|
||||
(defun nnmbox-close-server (&optional server)
|
||||
(deffoo nnmbox-open-server (server &optional defs)
|
||||
(nnoo-change-server 'nnmbox server defs)
|
||||
(cond
|
||||
((not (file-exists-p nnmbox-mbox-file))
|
||||
(nnmbox-close-server)
|
||||
(nnheader-report 'nnmbox "No such file: %s" nnmbox-mbox-file))
|
||||
((file-directory-p nnmbox-mbox-file)
|
||||
(nnmbox-close-server)
|
||||
(nnheader-report 'nnmbox "Not a regular file: %s" nnmbox-mbox-file))
|
||||
(t
|
||||
(nnheader-report 'nnmbox "Opened server %s using mbox %s" server
|
||||
nnmbox-mbox-file)
|
||||
t)))
|
||||
|
||||
(deffoo nnmbox-close-server (&optional server)
|
||||
(when (and nnmbox-mbox-buffer
|
||||
(buffer-name nnmbox-mbox-buffer))
|
||||
(kill-buffer nnmbox-mbox-buffer))
|
||||
(nnoo-close-server 'nnmbox server)
|
||||
t)
|
||||
|
||||
(defun nnmbox-server-opened (&optional server)
|
||||
(and (equal server nnmbox-current-server)
|
||||
(deffoo nnmbox-server-opened (&optional server)
|
||||
(and (nnoo-current-server-p 'nnmbox server)
|
||||
nnmbox-mbox-buffer
|
||||
(buffer-name nnmbox-mbox-buffer)
|
||||
nntp-server-buffer
|
||||
(buffer-name nntp-server-buffer)))
|
||||
|
||||
(defun nnmbox-status-message (&optional server)
|
||||
nnmbox-status-string)
|
||||
|
||||
(defun nnmbox-request-article (article &optional newsgroup server buffer)
|
||||
(nnmbox-possibly-change-newsgroup newsgroup)
|
||||
(if (stringp article)
|
||||
nil
|
||||
(save-excursion
|
||||
(set-buffer nnmbox-mbox-buffer)
|
||||
(goto-char (point-min))
|
||||
(if (search-forward (nnmbox-article-string article) nil t)
|
||||
(let (start stop)
|
||||
(re-search-backward (concat "^" rmail-unix-mail-delimiter) nil t)
|
||||
(setq start (point))
|
||||
(forward-line 1)
|
||||
(or (and (re-search-forward
|
||||
(concat "^" rmail-unix-mail-delimiter) nil t)
|
||||
(forward-line -1))
|
||||
(goto-char (point-max)))
|
||||
(setq stop (point))
|
||||
(let ((nntp-server-buffer (or buffer nntp-server-buffer)))
|
||||
(set-buffer nntp-server-buffer)
|
||||
(erase-buffer)
|
||||
(insert-buffer-substring nnmbox-mbox-buffer start stop)
|
||||
(goto-char (point-min))
|
||||
(while (looking-at "From ")
|
||||
(delete-char 5)
|
||||
(insert "X-From-Line: ")
|
||||
(forward-line 1))
|
||||
t))))))
|
||||
|
||||
(defun nnmbox-request-group (group &optional server dont-check)
|
||||
(deffoo nnmbox-request-article (article &optional newsgroup server buffer)
|
||||
(nnmbox-possibly-change-newsgroup newsgroup server)
|
||||
(save-excursion
|
||||
(if (nnmbox-possibly-change-newsgroup group)
|
||||
(if dont-check
|
||||
t
|
||||
(nnmbox-get-new-mail group)
|
||||
(save-excursion
|
||||
(set-buffer nnmbox-mbox-buffer)
|
||||
(goto-char (point-min))
|
||||
(if (search-forward (nnmbox-article-string article) nil t)
|
||||
(let (start stop)
|
||||
(re-search-backward (concat "^" message-unix-mail-delimiter) nil t)
|
||||
(setq start (point))
|
||||
(forward-line 1)
|
||||
(or (and (re-search-forward
|
||||
(concat "^" message-unix-mail-delimiter) nil t)
|
||||
(forward-line -1))
|
||||
(goto-char (point-max)))
|
||||
(setq stop (point))
|
||||
(let ((nntp-server-buffer (or buffer nntp-server-buffer)))
|
||||
(set-buffer nntp-server-buffer)
|
||||
(erase-buffer)
|
||||
(let ((active (assoc group nnmbox-group-alist)))
|
||||
(insert (format "211 %d %d %d %s\n"
|
||||
(1+ (- (cdr (car (cdr active)))
|
||||
(car (car (cdr active)))))
|
||||
(car (car (cdr active)))
|
||||
(cdr (car (cdr active)))
|
||||
(car active))))
|
||||
t)))))
|
||||
(insert-buffer-substring nnmbox-mbox-buffer start stop)
|
||||
(goto-char (point-min))
|
||||
(while (looking-at "From ")
|
||||
(delete-char 5)
|
||||
(insert "X-From-Line: ")
|
||||
(forward-line 1))
|
||||
(if (numberp article)
|
||||
(cons nnmbox-current-group article)
|
||||
(nnmbox-article-group-number)))))))
|
||||
|
||||
(defun nnmbox-close-group (group &optional server)
|
||||
(deffoo nnmbox-request-group (group &optional server dont-check)
|
||||
(let ((active (cadr (assoc group nnmbox-group-alist))))
|
||||
(cond
|
||||
((or (null active)
|
||||
(null (nnmbox-possibly-change-newsgroup group server)))
|
||||
(nnheader-report 'nnmbox "No such group: %s" group))
|
||||
(dont-check
|
||||
(nnheader-report 'nnmbox "Selected group %s" group)
|
||||
(nnheader-insert ""))
|
||||
(t
|
||||
(nnheader-report 'nnmbox "Selected group %s" group)
|
||||
(nnheader-insert "211 %d %d %d %s\n"
|
||||
(1+ (- (cdr active) (car active)))
|
||||
(car active) (cdr active) group)))))
|
||||
|
||||
(deffoo nnmbox-request-scan (&optional group server)
|
||||
(nnmbox-read-mbox)
|
||||
(nnmail-get-new-mail
|
||||
'nnmbox
|
||||
(lambda ()
|
||||
(save-excursion
|
||||
(set-buffer nnmbox-mbox-buffer)
|
||||
(save-buffer)))
|
||||
nnmbox-mbox-file group
|
||||
(lambda ()
|
||||
(save-excursion
|
||||
(let ((in-buf (current-buffer)))
|
||||
(set-buffer nnmbox-mbox-buffer)
|
||||
(goto-char (point-max))
|
||||
(insert-buffer-substring in-buf)))
|
||||
(nnmail-save-active nnmbox-group-alist nnmbox-active-file))))
|
||||
|
||||
(deffoo nnmbox-close-group (group &optional server)
|
||||
t)
|
||||
|
||||
(defun nnmbox-request-list (&optional server)
|
||||
(if server (nnmbox-get-new-mail))
|
||||
(deffoo nnmbox-request-list (&optional server)
|
||||
(save-excursion
|
||||
(or (nnmail-find-file nnmbox-active-file)
|
||||
(progn
|
||||
(setq nnmbox-group-alist (nnmail-get-active))
|
||||
(nnmail-save-active nnmbox-group-alist nnmbox-active-file)
|
||||
(nnmail-find-file nnmbox-active-file)))))
|
||||
(nnmail-find-file nnmbox-active-file)
|
||||
(setq nnmbox-group-alist (nnmail-get-active))))
|
||||
|
||||
(defun nnmbox-request-newgroups (date &optional server)
|
||||
(deffoo nnmbox-request-newgroups (date &optional server)
|
||||
(nnmbox-request-list server))
|
||||
|
||||
(defun nnmbox-request-list-newsgroups (&optional server)
|
||||
(setq nnmbox-status-string "nnmbox: LIST NEWSGROUPS is not implemented.")
|
||||
nil)
|
||||
(deffoo nnmbox-request-list-newsgroups (&optional server)
|
||||
(nnheader-report 'nnmbox "LIST NEWSGROUPS is not implemented."))
|
||||
|
||||
(defun nnmbox-request-post (&optional server)
|
||||
(mail-send-and-exit nil))
|
||||
|
||||
(defalias 'nnmbox-request-post-buffer 'nnmail-request-post-buffer)
|
||||
|
||||
(defun nnmbox-request-expire-articles
|
||||
(deffoo nnmbox-request-expire-articles
|
||||
(articles newsgroup &optional server force)
|
||||
(nnmbox-possibly-change-newsgroup newsgroup)
|
||||
(let* ((days (or (and nnmail-expiry-wait-function
|
||||
(funcall nnmail-expiry-wait-function newsgroup))
|
||||
nnmail-expiry-wait))
|
||||
(is-old t)
|
||||
(nnmbox-possibly-change-newsgroup newsgroup server)
|
||||
(let* ((is-old t)
|
||||
rest)
|
||||
(nnmail-activate 'nnmbox)
|
||||
|
||||
@ -244,16 +228,14 @@
|
||||
(while (and articles is-old)
|
||||
(goto-char (point-min))
|
||||
(if (search-forward (nnmbox-article-string (car articles)) nil t)
|
||||
(if (or force
|
||||
(setq is-old
|
||||
(> (nnmail-days-between
|
||||
(current-time-string)
|
||||
(buffer-substring
|
||||
(point) (progn (end-of-line) (point))))
|
||||
days)))
|
||||
(if (setq is-old
|
||||
(nnmail-expired-article-p
|
||||
newsgroup
|
||||
(buffer-substring
|
||||
(point) (progn (end-of-line) (point))) force))
|
||||
(progn
|
||||
(and gnus-verbose-backends
|
||||
(message "Deleting article %s..." (car articles)))
|
||||
(nnheader-message 5 "Deleting article %d in %s..."
|
||||
(car articles) newsgroup)
|
||||
(nnmbox-delete-mail))
|
||||
(setq rest (cons (car articles) rest))))
|
||||
(setq articles (cdr articles)))
|
||||
@ -269,9 +251,9 @@
|
||||
(nnmail-save-active nnmbox-group-alist nnmbox-active-file)
|
||||
(nconc rest articles))))
|
||||
|
||||
(defun nnmbox-request-move-article
|
||||
(deffoo nnmbox-request-move-article
|
||||
(article group server accept-form &optional last)
|
||||
(nnmbox-possibly-change-newsgroup group)
|
||||
(nnmbox-possibly-change-newsgroup group server)
|
||||
(let ((buf (get-buffer-create " *nnmbox move*"))
|
||||
result)
|
||||
(and
|
||||
@ -298,7 +280,9 @@
|
||||
(and last (save-buffer))))
|
||||
result))
|
||||
|
||||
(defun nnmbox-request-accept-article (group &optional last)
|
||||
(deffoo nnmbox-request-accept-article (group &optional server last)
|
||||
(nnmbox-possibly-change-newsgroup group server)
|
||||
(nnmail-check-syntax)
|
||||
(let ((buf (current-buffer))
|
||||
result)
|
||||
(goto-char (point-min))
|
||||
@ -317,13 +301,14 @@
|
||||
(setq result (nnmbox-save-mail (and (stringp group) group))))
|
||||
(save-excursion
|
||||
(set-buffer nnmbox-mbox-buffer)
|
||||
(goto-char (point-max))
|
||||
(insert-buffer-substring buf)
|
||||
(and last (save-buffer))
|
||||
result)
|
||||
(nnmail-save-active nnmbox-group-alist nnmbox-active-file))
|
||||
(car result)))
|
||||
|
||||
(defun nnmbox-request-replace-article (article group buffer)
|
||||
(deffoo nnmbox-request-replace-article (article group buffer)
|
||||
(nnmbox-possibly-change-newsgroup group)
|
||||
(save-excursion
|
||||
(set-buffer nnmbox-mbox-buffer)
|
||||
@ -335,6 +320,48 @@
|
||||
(save-buffer)
|
||||
t)))
|
||||
|
||||
(deffoo nnmbox-request-delete-group (group &optional force server)
|
||||
(nnmbox-possibly-change-newsgroup group server)
|
||||
;; Delete all articles in GROUP.
|
||||
(if (not force)
|
||||
() ; Don't delete the articles.
|
||||
(save-excursion
|
||||
(set-buffer nnmbox-mbox-buffer)
|
||||
(goto-char (point-min))
|
||||
;; Delete all articles in this group.
|
||||
(let ((ident (concat "\nX-Gnus-Newsgroup: " nnmbox-current-group ":"))
|
||||
found)
|
||||
(while (search-forward ident nil t)
|
||||
(setq found t)
|
||||
(nnmbox-delete-mail))
|
||||
(and found (save-buffer)))))
|
||||
;; Remove the group from all structures.
|
||||
(setq nnmbox-group-alist
|
||||
(delq (assoc group nnmbox-group-alist) nnmbox-group-alist)
|
||||
nnmbox-current-group nil)
|
||||
;; Save the active file.
|
||||
(nnmail-save-active nnmbox-group-alist nnmbox-active-file)
|
||||
t)
|
||||
|
||||
(deffoo nnmbox-request-rename-group (group new-name &optional server)
|
||||
(nnmbox-possibly-change-newsgroup group server)
|
||||
(save-excursion
|
||||
(set-buffer nnmbox-mbox-buffer)
|
||||
(goto-char (point-min))
|
||||
(let ((ident (concat "\nX-Gnus-Newsgroup: " nnmbox-current-group ":"))
|
||||
(new-ident (concat "\nX-Gnus-Newsgroup: " new-name ":"))
|
||||
found)
|
||||
(while (search-forward ident nil t)
|
||||
(replace-match new-ident t t)
|
||||
(setq found t))
|
||||
(and found (save-buffer))))
|
||||
(let ((entry (assoc group nnmbox-group-alist)))
|
||||
(and entry (setcar entry new-name))
|
||||
(setq nnmbox-current-group nil)
|
||||
;; Save the new group alist.
|
||||
(nnmail-save-active nnmbox-group-alist nnmbox-active-file)
|
||||
t))
|
||||
|
||||
|
||||
;;; Internal functions.
|
||||
|
||||
@ -352,12 +379,12 @@
|
||||
(save-restriction
|
||||
(narrow-to-region
|
||||
(save-excursion
|
||||
(re-search-backward (concat "^" rmail-unix-mail-delimiter) nil t)
|
||||
(re-search-backward (concat "^" message-unix-mail-delimiter) nil t)
|
||||
(if leave-delim (progn (forward-line 1) (point))
|
||||
(match-beginning 0)))
|
||||
(progn
|
||||
(forward-line 1)
|
||||
(or (and (re-search-forward (concat "^" rmail-unix-mail-delimiter)
|
||||
(or (and (re-search-forward (concat "^" message-unix-mail-delimiter)
|
||||
nil t)
|
||||
(if (and (not (bobp)) leave-delim)
|
||||
(progn (forward-line -2) (point))
|
||||
@ -368,7 +395,10 @@
|
||||
(if (or force (not (re-search-forward "^X-Gnus-Newsgroup: " nil t)))
|
||||
(delete-region (point-min) (point-max))))))
|
||||
|
||||
(defun nnmbox-possibly-change-newsgroup (newsgroup)
|
||||
(defun nnmbox-possibly-change-newsgroup (newsgroup &optional server)
|
||||
(when (and server
|
||||
(not (nnmbox-server-opened server)))
|
||||
(nnmbox-open-server server))
|
||||
(if (or (not nnmbox-mbox-buffer)
|
||||
(not (buffer-name nnmbox-mbox-buffer)))
|
||||
(save-excursion
|
||||
@ -380,21 +410,45 @@
|
||||
(nnmail-activate 'nnmbox))
|
||||
(if newsgroup
|
||||
(if (assoc newsgroup nnmbox-group-alist)
|
||||
(setq nnmbox-current-group newsgroup))))
|
||||
(setq nnmbox-current-group newsgroup))
|
||||
t))
|
||||
|
||||
(defun nnmbox-article-string (article)
|
||||
(concat "\nX-Gnus-Newsgroup: " nnmbox-current-group ":"
|
||||
(int-to-string article) " "))
|
||||
(if (numberp article)
|
||||
(concat "\nX-Gnus-Newsgroup: " nnmbox-current-group ":"
|
||||
(int-to-string article) " ")
|
||||
(concat "\nMessage-ID: " article)))
|
||||
|
||||
(defun nnmbox-article-group-number ()
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(and (re-search-forward "^X-Gnus-Newsgroup: +\\([^:]+\\):\\([0-9]+\\) "
|
||||
nil t)
|
||||
(cons (buffer-substring (match-beginning 1) (match-end 1))
|
||||
(string-to-int
|
||||
(buffer-substring (match-beginning 2) (match-end 2)))))))
|
||||
|
||||
(defun nnmbox-save-mail (&optional group)
|
||||
"Called narrowed to an article."
|
||||
(let* ((nnmail-split-methods
|
||||
(if group (list (list group "")) nnmail-split-methods))
|
||||
(group-art (nreverse (nnmail-article-group 'nnmbox-active-number))))
|
||||
(group-art (nreverse (nnmail-article-group 'nnmbox-active-number)))
|
||||
(delim (concat "^" message-unix-mail-delimiter)))
|
||||
(goto-char (point-min))
|
||||
;; This might come from somewhere else.
|
||||
(unless (looking-at delim)
|
||||
(insert "From nobody " (current-time-string) "\n")
|
||||
(goto-char (point-min)))
|
||||
;; Quote all "From " lines in the article.
|
||||
(forward-line 1)
|
||||
(while (re-search-forward delim nil t)
|
||||
(beginning-of-line)
|
||||
(insert "> "))
|
||||
(nnmail-insert-lines)
|
||||
(nnmail-insert-xref group-art)
|
||||
(nnmbox-insert-newsgroup-line group-art)
|
||||
(run-hooks 'nnml-prepare-save-mail-hook)
|
||||
(run-hooks 'nnmail-prepare-save-mail-hook)
|
||||
(run-hooks 'nnmbox-prepare-save-mail-hook)
|
||||
group-art))
|
||||
|
||||
(defun nnmbox-insert-newsgroup-line (group-art)
|
||||
@ -405,14 +459,14 @@
|
||||
(forward-char -1)
|
||||
(while group-art
|
||||
(insert (format "X-Gnus-Newsgroup: %s:%d %s\n"
|
||||
(car (car group-art)) (cdr (car group-art))
|
||||
(caar group-art) (cdar group-art)
|
||||
(current-time-string)))
|
||||
(setq group-art (cdr group-art)))))
|
||||
t))
|
||||
|
||||
(defun nnmbox-active-number (group)
|
||||
;; Find the next article number in GROUP.
|
||||
(let ((active (car (cdr (assoc group nnmbox-group-alist)))))
|
||||
(let ((active (cadr (assoc group nnmbox-group-alist))))
|
||||
(if active
|
||||
(setcdr active (1+ (cdr active)))
|
||||
;; This group is new, so we create a new entry for it.
|
||||
@ -433,12 +487,29 @@
|
||||
(= (buffer-size) (nth 7 (file-attributes nnmbox-mbox-file)))))
|
||||
()
|
||||
(save-excursion
|
||||
(let ((delim (concat "^" rmail-unix-mail-delimiter))
|
||||
start end)
|
||||
(let ((delim (concat "^" message-unix-mail-delimiter))
|
||||
(alist nnmbox-group-alist)
|
||||
start end number)
|
||||
(set-buffer (setq nnmbox-mbox-buffer
|
||||
(nnheader-find-file-noselect
|
||||
(nnheader-find-file-noselect
|
||||
nnmbox-mbox-file nil 'raw)))
|
||||
(buffer-disable-undo (current-buffer))
|
||||
|
||||
;; Go through the group alist and compare against
|
||||
;; the mbox file.
|
||||
(while alist
|
||||
(goto-char (point-max))
|
||||
(when (and (re-search-backward
|
||||
(format "^X-Gnus-Newsgroup: %s:\\([0-9]+\\) "
|
||||
(caar alist)) nil t)
|
||||
(>= (setq number
|
||||
(string-to-number
|
||||
(buffer-substring
|
||||
(match-beginning 1) (match-end 1))))
|
||||
(cdadar alist)))
|
||||
(setcdr (cadar alist) (1+ number)))
|
||||
(setq alist (cdr alist)))
|
||||
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward delim nil t)
|
||||
(setq start (match-beginning 0))
|
||||
@ -457,54 +528,6 @@
|
||||
(nnmbox-save-mail))))
|
||||
(goto-char end))))))
|
||||
|
||||
(defun nnmbox-get-new-mail (&optional group)
|
||||
"Read new incoming mail."
|
||||
(let* ((spools (nnmail-get-spool-files group))
|
||||
(group-in group)
|
||||
incoming incomings)
|
||||
(nnmbox-read-mbox)
|
||||
(if (or (not nnmbox-get-new-mail) (not nnmail-spool-file))
|
||||
()
|
||||
;; We go through all the existing spool files and split the
|
||||
;; mail from each.
|
||||
(while spools
|
||||
(and
|
||||
(file-exists-p (car spools))
|
||||
(> (nth 7 (file-attributes (car spools))) 0)
|
||||
(progn
|
||||
(and gnus-verbose-backends
|
||||
(message "nnmbox: Reading incoming mail..."))
|
||||
(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)
|
||||
(save-excursion
|
||||
(nnmail-save-active nnmbox-group-alist nnmbox-active-file)
|
||||
(set-buffer nnmbox-mbox-buffer)
|
||||
(save-buffer)))
|
||||
(if incomings (run-hooks 'nnmail-read-incoming-hook))
|
||||
(while incomings
|
||||
(setq incoming (car incomings))
|
||||
(and nnmail-delete-incoming
|
||||
(file-exists-p incoming)
|
||||
(file-writable-p incoming)
|
||||
(delete-file incoming))
|
||||
(setq incomings (cdr incomings))))))
|
||||
|
||||
|
||||
(provide 'nnmbox)
|
||||
|
||||
;;; nnmbox.el ends here
|
||||
|
504
lisp/nnmh.el
504
lisp/nnmh.el
@ -1,6 +1,5 @@
|
||||
;;; nnmh.el --- mhspool access for Gnus
|
||||
|
||||
;; Copyright (C) 1995 Free Software Foundation, Inc.
|
||||
;; Copyright (C) 1995,96 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
|
||||
;; Masanobu UMEDA <umerin@flab.flab.fujitsu.junet>
|
||||
@ -32,20 +31,23 @@
|
||||
;;; Code:
|
||||
|
||||
(require 'nnheader)
|
||||
(require 'rmail)
|
||||
(require 'nnmail)
|
||||
(require 'gnus)
|
||||
(require 'nnoo)
|
||||
(eval-and-compile (require 'cl))
|
||||
|
||||
(defvar nnmh-directory "~/Mail/"
|
||||
(nnoo-declare nnmh)
|
||||
|
||||
(defvoo nnmh-directory message-directory
|
||||
"*Mail spool directory.")
|
||||
|
||||
(defvar nnmh-get-new-mail t
|
||||
(defvoo nnmh-get-new-mail t
|
||||
"*If non-nil, nnmh will check the incoming mail file and split the mail.")
|
||||
|
||||
(defvar nnmh-prepare-save-mail-hook nil
|
||||
(defvoo nnmh-prepare-save-mail-hook nil
|
||||
"*Hook run narrowed to an article before saving.")
|
||||
|
||||
(defvar nnmh-be-safe nil
|
||||
(defvoo nnmh-be-safe nil
|
||||
"*If non-nil, nnmh will check all articles to make sure whether they are new or not.")
|
||||
|
||||
|
||||
@ -53,59 +55,49 @@
|
||||
(defconst nnmh-version "nnmh 1.0"
|
||||
"nnmh version.")
|
||||
|
||||
(defvar nnmh-current-directory nil
|
||||
(defvoo nnmh-current-directory nil
|
||||
"Current news group directory.")
|
||||
|
||||
(defvar nnmh-status-string "")
|
||||
(defvar nnmh-group-alist nil)
|
||||
|
||||
|
||||
|
||||
(defvar nnmh-current-server nil)
|
||||
(defvar nnmh-server-alist nil)
|
||||
(defvar nnmh-server-variables
|
||||
(list
|
||||
(list 'nnmh-directory nnmh-directory)
|
||||
(list 'nnmh-get-new-mail nnmh-get-new-mail)
|
||||
'(nnmh-current-directory nil)
|
||||
'(nnmh-status-string "")
|
||||
'(nnmh-group-alist)))
|
||||
(defvoo nnmh-status-string "")
|
||||
(defvoo nnmh-group-alist nil)
|
||||
|
||||
|
||||
|
||||
;;; Interface functions.
|
||||
|
||||
(defun nnmh-retrieve-headers (sequence &optional newsgroup server)
|
||||
(nnoo-define-basics nnmh)
|
||||
|
||||
(deffoo nnmh-retrieve-headers (articles &optional newsgroup server fetch-old)
|
||||
(save-excursion
|
||||
(set-buffer nntp-server-buffer)
|
||||
(erase-buffer)
|
||||
(let* ((file nil)
|
||||
(number (length sequence))
|
||||
(number (length articles))
|
||||
(large (and (numberp nnmail-large-newsgroup)
|
||||
(> number nnmail-large-newsgroup)))
|
||||
(count 0)
|
||||
beg article)
|
||||
(nnmh-possibly-change-directory newsgroup)
|
||||
(if (stringp (car sequence))
|
||||
(nnmh-possibly-change-directory newsgroup server)
|
||||
;; We don't support fetching by Message-ID.
|
||||
(if (stringp (car articles))
|
||||
'headers
|
||||
(while sequence
|
||||
(setq article (car sequence))
|
||||
(setq file
|
||||
(concat nnmh-current-directory (int-to-string article)))
|
||||
(if (and (file-exists-p file)
|
||||
(not (file-directory-p file)))
|
||||
(progn
|
||||
(insert (format "221 %d Article retrieved.\n" article))
|
||||
(setq beg (point))
|
||||
(nnheader-insert-head file)
|
||||
(goto-char beg)
|
||||
(if (search-forward "\n\n" nil t)
|
||||
(forward-char -1)
|
||||
(goto-char (point-max))
|
||||
(insert "\n\n"))
|
||||
(insert ".\n")
|
||||
(delete-region (point) (point-max))))
|
||||
(setq sequence (cdr sequence))
|
||||
(while articles
|
||||
(when (and (file-exists-p
|
||||
(setq file (concat (file-name-as-directory
|
||||
nnmh-current-directory)
|
||||
(int-to-string
|
||||
(setq article (pop articles))))))
|
||||
(not (file-directory-p file)))
|
||||
(insert (format "221 %d Article retrieved.\n" article))
|
||||
(setq beg (point))
|
||||
(nnheader-insert-head file)
|
||||
(goto-char beg)
|
||||
(if (search-forward "\n\n" nil t)
|
||||
(forward-char -1)
|
||||
(goto-char (point-max))
|
||||
(insert "\n\n"))
|
||||
(insert ".\n")
|
||||
(delete-region (point) (point-max)))
|
||||
(setq count (1+ count))
|
||||
|
||||
(and large
|
||||
@ -115,42 +107,29 @@
|
||||
|
||||
(and large (message "nnmh: Receiving headers...done"))
|
||||
|
||||
;; Fold continuation lines.
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward "\\(\r?\n[ \t]+\\)+" nil t)
|
||||
(replace-match " " t t))
|
||||
(nnheader-fold-continuation-lines)
|
||||
'headers))))
|
||||
|
||||
(defun nnmh-open-server (server &optional defs)
|
||||
(nnheader-init-server-buffer)
|
||||
(if (equal server nnmh-current-server)
|
||||
t
|
||||
(if nnmh-current-server
|
||||
(setq nnmh-server-alist
|
||||
(cons (list nnmh-current-server
|
||||
(nnheader-save-variables nnmh-server-variables))
|
||||
nnmh-server-alist)))
|
||||
(let ((state (assoc server nnmh-server-alist)))
|
||||
(if state
|
||||
(progn
|
||||
(nnheader-restore-variables (nth 1 state))
|
||||
(setq nnmh-server-alist (delq state nnmh-server-alist)))
|
||||
(nnheader-set-init-variables nnmh-server-variables defs)))
|
||||
(setq nnmh-current-server server)))
|
||||
(deffoo nnmh-open-server (server &optional defs)
|
||||
(nnoo-change-server 'nnmh server defs)
|
||||
(when (not (file-exists-p nnmh-directory))
|
||||
(condition-case ()
|
||||
(make-directory nnmh-directory t)
|
||||
(error t)))
|
||||
(cond
|
||||
((not (file-exists-p nnmh-directory))
|
||||
(nnmh-close-server)
|
||||
(nnheader-report 'nnmh "Couldn't create directory: %s" nnmh-directory))
|
||||
((not (file-directory-p (file-truename nnmh-directory)))
|
||||
(nnmh-close-server)
|
||||
(nnheader-report 'nnmh "Not a directory: %s" nnmh-directory))
|
||||
(t
|
||||
(nnheader-report 'nnmh "Opened server %s using directory %s"
|
||||
server nnmh-directory)
|
||||
t)))
|
||||
|
||||
(defun nnmh-close-server (&optional server)
|
||||
t)
|
||||
|
||||
(defun nnmh-server-opened (&optional server)
|
||||
(and (equal server nnmh-current-server)
|
||||
nntp-server-buffer
|
||||
(buffer-name nntp-server-buffer)))
|
||||
|
||||
(defun nnmh-status-message (&optional server)
|
||||
nnmh-status-string)
|
||||
|
||||
(defun nnmh-request-article (id &optional newsgroup server buffer)
|
||||
(nnmh-possibly-change-directory newsgroup)
|
||||
(deffoo nnmh-request-article (id &optional newsgroup server buffer)
|
||||
(nnmh-possibly-change-directory newsgroup server)
|
||||
(let ((file (if (stringp id)
|
||||
nil
|
||||
(concat nnmh-current-directory (int-to-string id))))
|
||||
@ -158,105 +137,101 @@
|
||||
(and (stringp file)
|
||||
(file-exists-p file)
|
||||
(not (file-directory-p file))
|
||||
(save-excursion (nnmail-find-file file)))))
|
||||
(save-excursion (nnmail-find-file file))
|
||||
(string-to-int (file-name-nondirectory file)))))
|
||||
|
||||
(defun nnmh-request-group (group &optional server dont-check)
|
||||
(and nnmh-get-new-mail (or dont-check (nnmh-get-new-mail group)))
|
||||
(let ((pathname (nnmh-article-pathname group nnmh-directory))
|
||||
(deffoo nnmh-request-group (group &optional server dont-check)
|
||||
(let ((pathname (nnmail-group-pathname group nnmh-directory))
|
||||
dir)
|
||||
(if (file-directory-p pathname)
|
||||
(progn
|
||||
(setq nnmh-current-directory pathname)
|
||||
(and nnmh-get-new-mail
|
||||
nnmh-be-safe
|
||||
(nnmh-update-gnus-unreads group))
|
||||
(or dont-check
|
||||
(progn
|
||||
(setq dir
|
||||
(sort
|
||||
(mapcar
|
||||
(function
|
||||
(lambda (name)
|
||||
(string-to-int name)))
|
||||
(directory-files pathname nil "^[0-9]+$" t))
|
||||
'<))
|
||||
(save-excursion
|
||||
(set-buffer nntp-server-buffer)
|
||||
(erase-buffer)
|
||||
(if dir
|
||||
(insert (format "211 %d %d %d %s\n" (length dir)
|
||||
(car dir)
|
||||
(progn (while (cdr dir)
|
||||
(setq dir (cdr dir)))
|
||||
(car dir))
|
||||
group))
|
||||
(insert (format "211 0 1 0 %s\n" group))))))
|
||||
t)
|
||||
(setq nnmh-status-string "No such group")
|
||||
nil)))
|
||||
(cond
|
||||
((not (file-directory-p pathname))
|
||||
(nnheader-report
|
||||
'nnmh "Can't select group (no such directory): %s" group))
|
||||
(t
|
||||
(setq nnmh-current-directory pathname)
|
||||
(and nnmh-get-new-mail
|
||||
nnmh-be-safe
|
||||
(nnmh-update-gnus-unreads group))
|
||||
(cond
|
||||
(dont-check
|
||||
(nnheader-report 'nnmh "Selected group %s" group)
|
||||
t)
|
||||
(t
|
||||
(setq dir
|
||||
(sort
|
||||
(mapcar (lambda (name) (string-to-int name))
|
||||
(directory-files pathname nil "^[0-9]+$" t))
|
||||
'<))
|
||||
(cond
|
||||
(dir
|
||||
(nnheader-report 'nnmh "Selected group %s" group)
|
||||
(nnheader-insert
|
||||
"211 %d %d %d %s\n" (length dir) (car dir)
|
||||
(progn (while (cdr dir) (setq dir (cdr dir))) (car dir))
|
||||
group))
|
||||
(t
|
||||
(nnheader-report 'nnmh "Empty group %s" group)
|
||||
(nnheader-insert (format "211 0 1 0 %s\n" group))))))))))
|
||||
|
||||
(defun nnmh-request-list (&optional server dir)
|
||||
(or dir
|
||||
(save-excursion
|
||||
(set-buffer nntp-server-buffer)
|
||||
(erase-buffer)
|
||||
(setq dir (file-truename (file-name-as-directory nnmh-directory)))))
|
||||
(deffoo nnmh-request-scan (&optional group server)
|
||||
(nnmail-get-new-mail 'nnmh nil nnmh-directory group))
|
||||
|
||||
(deffoo nnmh-request-list (&optional server dir)
|
||||
(nnheader-insert "")
|
||||
(let ((nnmh-toplev
|
||||
(or dir (file-truename (file-name-as-directory nnmh-directory)))))
|
||||
(nnmh-request-list-1 nnmh-toplev))
|
||||
(setq nnmh-group-alist (nnmail-get-active))
|
||||
t)
|
||||
|
||||
(defvar nnmh-toplev)
|
||||
(defun nnmh-request-list-1 (dir)
|
||||
(setq dir (expand-file-name dir))
|
||||
;; Recurse down all directories.
|
||||
(let ((dirs (and (file-readable-p dir)
|
||||
(> (nth 1 (file-attributes (file-chase-links dir))) 2)
|
||||
(directory-files dir t nil t))))
|
||||
(while dirs
|
||||
(if (and (not (string-match "/\\.\\.?$" (car dirs)))
|
||||
(file-directory-p (car dirs))
|
||||
(file-readable-p (car dirs)))
|
||||
(nnmh-request-list nil (car dirs)))
|
||||
(setq dirs (cdr dirs))))
|
||||
(directory-files dir t nil t)))
|
||||
dir)
|
||||
;; Recurse down directories.
|
||||
(while (setq dir (pop dirs))
|
||||
(when (and (not (member (file-name-nondirectory dir) '("." "..")))
|
||||
(file-directory-p dir)
|
||||
(file-readable-p dir))
|
||||
(nnmh-request-list-1 dir))))
|
||||
;; For each directory, generate an active file line.
|
||||
(if (not (string= (expand-file-name nnmh-directory) dir))
|
||||
(let ((files (mapcar
|
||||
(lambda (name) (string-to-int name))
|
||||
(directory-files dir nil "^[0-9]+$" t))))
|
||||
(if (null files)
|
||||
()
|
||||
(save-excursion
|
||||
(set-buffer nntp-server-buffer)
|
||||
(goto-char (point-max))
|
||||
(insert
|
||||
(format
|
||||
"%s %d %d y\n"
|
||||
(progn
|
||||
(string-match
|
||||
(file-truename (file-name-as-directory
|
||||
(expand-file-name nnmh-directory))) dir)
|
||||
(nnmail-replace-chars-in-string
|
||||
(substring dir (match-end 0)) ?/ ?.))
|
||||
(apply (function max) files)
|
||||
(apply (function min) files)))))))
|
||||
(setq nnmh-group-alist (nnmail-get-active))
|
||||
(and server nnmh-get-new-mail (nnmh-get-new-mail))
|
||||
(unless (string= (expand-file-name nnmh-toplev) dir)
|
||||
(let ((files (mapcar
|
||||
(lambda (name) (string-to-int name))
|
||||
(directory-files dir nil "^[0-9]+$" t))))
|
||||
(when files
|
||||
(save-excursion
|
||||
(set-buffer nntp-server-buffer)
|
||||
(goto-char (point-max))
|
||||
(insert
|
||||
(format
|
||||
"%s %d %d y\n"
|
||||
(progn
|
||||
(string-match
|
||||
(regexp-quote
|
||||
(file-truename (file-name-as-directory
|
||||
(expand-file-name nnmh-toplev)))) dir)
|
||||
(nnheader-replace-chars-in-string
|
||||
(substring dir (match-end 0)) ?/ ?.))
|
||||
(apply 'max files)
|
||||
(apply 'min files)))))))
|
||||
t)
|
||||
|
||||
(defun nnmh-request-newgroups (date &optional server)
|
||||
(deffoo nnmh-request-newgroups (date &optional server)
|
||||
(nnmh-request-list server))
|
||||
|
||||
(defun nnmh-request-post (&optional server)
|
||||
(mail-send-and-exit nil))
|
||||
|
||||
(defalias 'nnmh-request-post-buffer 'nnmail-request-post-buffer)
|
||||
|
||||
(defun nnmh-request-expire-articles (articles newsgroup &optional server force)
|
||||
(nnmh-possibly-change-directory newsgroup)
|
||||
(let* ((days (or (and nnmail-expiry-wait-function
|
||||
(funcall nnmail-expiry-wait-function newsgroup))
|
||||
nnmail-expiry-wait))
|
||||
(active-articles
|
||||
(deffoo nnmh-request-expire-articles (articles newsgroup &optional server force)
|
||||
(nnmh-possibly-change-directory newsgroup server)
|
||||
(let* ((active-articles
|
||||
(mapcar
|
||||
(function
|
||||
(lambda (name)
|
||||
(string-to-int name)))
|
||||
(directory-files nnmh-current-directory nil "^[0-9]+$" t)))
|
||||
(max-article (and active-articles (apply 'max active-articles)))
|
||||
(is-old t)
|
||||
article rest mod-time)
|
||||
(nnmail-activate 'nnmh)
|
||||
@ -265,36 +240,32 @@
|
||||
(setq article (concat nnmh-current-directory
|
||||
(int-to-string (car articles))))
|
||||
(if (setq mod-time (nth 5 (file-attributes article)))
|
||||
(if (and (or (not nnmail-keep-last-article)
|
||||
(not max-article)
|
||||
(not (= (car articles) max-article)))
|
||||
(not (equal mod-time '(0 0)))
|
||||
(or force
|
||||
(setq is-old
|
||||
(> (nnmail-days-between
|
||||
(current-time-string)
|
||||
(current-time-string mod-time))
|
||||
days))))
|
||||
(if (and (nnmh-deletable-article-p newsgroup (car articles))
|
||||
(setq is-old
|
||||
(nnmail-expired-article-p newsgroup mod-time force)))
|
||||
(progn
|
||||
(and gnus-verbose-backends
|
||||
(message "Deleting article %s..." article))
|
||||
(nnheader-message 5 "Deleting article %s in %s..."
|
||||
article newsgroup)
|
||||
(condition-case ()
|
||||
(delete-file article)
|
||||
(funcall nnmail-delete-file-function article)
|
||||
(file-error
|
||||
(nnheader-message 1 "Couldn't delete article %s in %s"
|
||||
article newsgroup)
|
||||
(setq rest (cons (car articles) rest)))))
|
||||
(setq rest (cons (car articles) rest))))
|
||||
(setq articles (cdr articles)))
|
||||
(message "")
|
||||
(nconc rest articles)))
|
||||
|
||||
(defun nnmh-close-group (group &optional server)
|
||||
(deffoo nnmh-close-group (group &optional server)
|
||||
t)
|
||||
|
||||
(defun nnmh-request-move-article
|
||||
(deffoo nnmh-request-move-article
|
||||
(article group server accept-form &optional last)
|
||||
(let ((buf (get-buffer-create " *nnmh move*"))
|
||||
result)
|
||||
(and
|
||||
(nnmh-deletable-article-p group article)
|
||||
(nnmh-request-article article group server)
|
||||
(save-excursion
|
||||
(set-buffer buf)
|
||||
@ -302,65 +273,134 @@
|
||||
(setq result (eval accept-form))
|
||||
(kill-buffer (current-buffer))
|
||||
result)
|
||||
(condition-case ()
|
||||
(delete-file (concat nnmh-current-directory
|
||||
(int-to-string article)))
|
||||
(file-error nil)))
|
||||
(progn
|
||||
(nnmh-possibly-change-directory group server)
|
||||
(condition-case ()
|
||||
(funcall nnmail-delete-file-function
|
||||
(concat nnmh-current-directory (int-to-string article)))
|
||||
(file-error nil))))
|
||||
result))
|
||||
|
||||
(defun nnmh-request-accept-article (group &optional last)
|
||||
(deffoo nnmh-request-accept-article (group &optional server last noinsert)
|
||||
(nnmh-possibly-change-directory group server)
|
||||
(nnmail-check-syntax)
|
||||
(if (stringp group)
|
||||
(and
|
||||
(nnmail-activate 'nnmh)
|
||||
;; We trick the choosing function into believing that only one
|
||||
;; group is available.
|
||||
(let ((nnmail-split-methods (list (list group ""))))
|
||||
(car (nnmh-save-mail))))
|
||||
(car (nnmh-save-mail noinsert))))
|
||||
(and
|
||||
(nnmail-activate 'nnmh)
|
||||
(car (nnmh-save-mail)))))
|
||||
(car (nnmh-save-mail noinsert)))))
|
||||
|
||||
(defun nnmh-request-replace-article (article group buffer)
|
||||
(deffoo nnmh-request-replace-article (article group buffer)
|
||||
(nnmh-possibly-change-directory group)
|
||||
(save-excursion
|
||||
(set-buffer buffer)
|
||||
(nnmh-possibly-create-directory group)
|
||||
(condition-case ()
|
||||
(progn
|
||||
(write-region (point-min) (point-max)
|
||||
(concat nnmh-current-directory (int-to-string article))
|
||||
nil (if gnus-verbose-backends nil 'nomesg))
|
||||
(write-region
|
||||
(point-min) (point-max)
|
||||
(concat nnmh-current-directory (int-to-string article))
|
||||
nil (if (nnheader-be-verbose 5) nil 'nomesg))
|
||||
t)
|
||||
(error nil))))
|
||||
|
||||
(deffoo nnmh-request-create-group (group &optional server)
|
||||
(nnmail-activate 'nnmh)
|
||||
(or (assoc group nnmh-group-alist)
|
||||
(let (active)
|
||||
(setq nnmh-group-alist (cons (list group (setq active (cons 1 0)))
|
||||
nnmh-group-alist))
|
||||
(nnmh-possibly-create-directory group)
|
||||
(nnmh-possibly-change-directory group server)
|
||||
(let ((articles (mapcar
|
||||
(lambda (file)
|
||||
(string-to-int file))
|
||||
(directory-files
|
||||
nnmh-current-directory nil "^[0-9]+$"))))
|
||||
(and articles
|
||||
(progn
|
||||
(setcar active (apply 'min articles))
|
||||
(setcdr active (apply 'max articles)))))))
|
||||
t)
|
||||
|
||||
(deffoo nnmh-request-delete-group (group &optional force server)
|
||||
(nnmh-possibly-change-directory group server)
|
||||
;; Delete all articles in GROUP.
|
||||
(if (not force)
|
||||
() ; Don't delete the articles.
|
||||
(let ((articles (directory-files nnmh-current-directory t "^[0-9]+$")))
|
||||
(while articles
|
||||
(and (file-writable-p (car articles))
|
||||
(progn
|
||||
(nnheader-message 5 "Deleting article %s in %s..."
|
||||
(car articles) group)
|
||||
(funcall nnmail-delete-file-function (car articles))))
|
||||
(setq articles (cdr articles))))
|
||||
;; Try to delete the directory itself.
|
||||
(condition-case ()
|
||||
(delete-directory nnmh-current-directory)
|
||||
(error nil)))
|
||||
;; Remove the group from all structures.
|
||||
(setq nnmh-group-alist
|
||||
(delq (assoc group nnmh-group-alist) nnmh-group-alist)
|
||||
nnmh-current-directory nil)
|
||||
t)
|
||||
|
||||
(deffoo nnmh-request-rename-group (group new-name &optional server)
|
||||
(nnmh-possibly-change-directory group server)
|
||||
;; Rename directory.
|
||||
(and (file-writable-p nnmh-current-directory)
|
||||
(condition-case ()
|
||||
(progn
|
||||
(rename-file
|
||||
(directory-file-name nnmh-current-directory)
|
||||
(directory-file-name
|
||||
(nnmail-group-pathname new-name nnmh-directory)))
|
||||
t)
|
||||
(error nil))
|
||||
;; That went ok, so we change the internal structures.
|
||||
(let ((entry (assoc group nnmh-group-alist)))
|
||||
(and entry (setcar entry new-name))
|
||||
(setq nnmh-current-directory nil)
|
||||
t)))
|
||||
|
||||
|
||||
;;; Internal functions.
|
||||
|
||||
(defun nnmh-possibly-change-directory (newsgroup)
|
||||
(defun nnmh-possibly-change-directory (newsgroup &optional server)
|
||||
(when (and server
|
||||
(not (nnmh-server-opened server)))
|
||||
(nnmh-open-server server))
|
||||
(if newsgroup
|
||||
(let ((pathname (nnmh-article-pathname newsgroup nnmh-directory)))
|
||||
(let ((pathname (nnmail-group-pathname newsgroup nnmh-directory)))
|
||||
(if (file-directory-p pathname)
|
||||
(setq nnmh-current-directory pathname)
|
||||
(error "No such newsgroup: %s" newsgroup)))))
|
||||
|
||||
(defun nnmh-possibly-create-directory (group)
|
||||
(let (dir dirs)
|
||||
(setq dir (nnmh-article-pathname group nnmh-directory))
|
||||
(setq dir (nnmail-group-pathname group nnmh-directory))
|
||||
(while (not (file-directory-p dir))
|
||||
(setq dirs (cons dir dirs))
|
||||
(setq dir (file-name-directory (directory-file-name dir))))
|
||||
(while dirs
|
||||
(if (make-directory (directory-file-name (car dirs)))
|
||||
(error "Could not create directory %s" (car dirs)))
|
||||
(and gnus-verbose-backends
|
||||
(message "Creating mail directory %s" (car dirs)))
|
||||
(nnheader-message 5 "Creating mail directory %s" (car dirs))
|
||||
(setq dirs (cdr dirs)))))
|
||||
|
||||
(defun nnmh-save-mail ()
|
||||
(defun nnmh-save-mail (&optional noinsert)
|
||||
"Called narrowed to an article."
|
||||
(let ((group-art (nreverse (nnmail-article-group 'nnmh-active-number))))
|
||||
(nnmail-insert-lines)
|
||||
(nnmail-insert-xref group-art)
|
||||
(unless noinsert
|
||||
(nnmail-insert-lines)
|
||||
(nnmail-insert-xref group-art))
|
||||
(run-hooks 'nnmail-prepare-save-mail-hook)
|
||||
(run-hooks 'nnmh-prepare-save-mail-hook)
|
||||
(goto-char (point-min))
|
||||
(while (looking-at "From ")
|
||||
@ -370,13 +410,13 @@
|
||||
(let ((ga group-art)
|
||||
first)
|
||||
(while ga
|
||||
(nnmh-possibly-create-directory (car (car ga)))
|
||||
(let ((file (concat (nnmh-article-pathname
|
||||
(car (car ga)) nnmh-directory)
|
||||
(int-to-string (cdr (car ga))))))
|
||||
(nnmh-possibly-create-directory (caar ga))
|
||||
(let ((file (concat (nnmail-group-pathname
|
||||
(caar ga) nnmh-directory)
|
||||
(int-to-string (cdar ga)))))
|
||||
(if first
|
||||
;; It was already saved, so we just make a hard link.
|
||||
(add-name-to-file first file t)
|
||||
(funcall nnmail-crosspost-link-function first file t)
|
||||
;; Save the article.
|
||||
(write-region (point-min) (point-max) file nil nil)
|
||||
(setq first file)))
|
||||
@ -385,7 +425,7 @@
|
||||
|
||||
(defun nnmh-active-number (group)
|
||||
"Compute the next article number in GROUP."
|
||||
(let ((active (car (cdr (assoc group nnmh-group-alist)))))
|
||||
(let ((active (cadr (assoc group nnmh-group-alist))))
|
||||
;; The group wasn't known to nnmh, so we just create an active
|
||||
;; entry for it.
|
||||
(or active
|
||||
@ -394,59 +434,11 @@
|
||||
(setq nnmh-group-alist (cons (list group active) nnmh-group-alist))))
|
||||
(setcdr active (1+ (cdr active)))
|
||||
(while (file-exists-p
|
||||
(concat (nnmh-article-pathname group nnmh-directory)
|
||||
(concat (nnmail-group-pathname group nnmh-directory)
|
||||
(int-to-string (cdr active))))
|
||||
(setcdr active (1+ (cdr active))))
|
||||
(cdr active)))
|
||||
|
||||
(defun nnmh-article-pathname (group mail-dir)
|
||||
"Make pathname for GROUP."
|
||||
(let ((mail-dir (file-name-as-directory (expand-file-name mail-dir))))
|
||||
(if (file-directory-p (concat mail-dir group))
|
||||
(concat mail-dir group "/")
|
||||
(concat mail-dir (nnmail-replace-chars-in-string group ?. ?/) "/"))))
|
||||
|
||||
(defun nnmh-get-new-mail (&optional group)
|
||||
"Read new incoming mail."
|
||||
(let* ((spools (nnmail-get-spool-files group))
|
||||
(group-in group)
|
||||
incoming incomings)
|
||||
(if (or (not nnmh-get-new-mail) (not nnmail-spool-file))
|
||||
()
|
||||
;; We first activate all the groups.
|
||||
(or nnmh-group-alist
|
||||
(nnmh-request-list))
|
||||
;; The we go through all the existing spool files and split the
|
||||
;; mail from each.
|
||||
(while spools
|
||||
(and
|
||||
(file-exists-p (car spools))
|
||||
(> (nth 7 (file-attributes (car spools))) 0)
|
||||
(progn
|
||||
(and gnus-verbose-backends
|
||||
(message "nnmh: Reading incoming mail..."))
|
||||
(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
|
||||
(message "nnmh: Reading incoming mail...done"))
|
||||
(while incomings
|
||||
(setq incoming (car incomings))
|
||||
(and nnmail-delete-incoming
|
||||
(file-exists-p incoming)
|
||||
(file-writable-p incoming)
|
||||
(delete-file incoming))
|
||||
(setq incomings (cdr incomings))))))
|
||||
|
||||
|
||||
(defun nnmh-update-gnus-unreads (group)
|
||||
;; Go through the .nnmh-articles file and compare with the actual
|
||||
;; articles in this folder. The articles that are "new" will be
|
||||
@ -471,7 +463,7 @@
|
||||
;; Remove all deleted articles.
|
||||
(let ((art articles))
|
||||
(while art
|
||||
(if (not (memq (car (car art)) files))
|
||||
(if (not (memq (caar art) files))
|
||||
(setq articles (delq (car art) articles)))
|
||||
(setq art (cdr art))))
|
||||
;; Check whether the highest-numbered articles really are the ones
|
||||
@ -480,10 +472,10 @@
|
||||
(while (and art
|
||||
(not (equal
|
||||
(nth 5 (file-attributes
|
||||
(concat dir (int-to-string (car (car art))))))
|
||||
(cdr (car art)))))
|
||||
(concat dir (int-to-string (caar art)))))
|
||||
(cdar art))))
|
||||
(setq articles (delq (car art) articles))
|
||||
(setq new (cons (car (car art)) new))
|
||||
(setq new (cons (caar art) new))
|
||||
(setq art (cdr art))))
|
||||
;; Go through all the new articles and add them, and their
|
||||
;; time-stamps to the list.
|
||||
@ -513,6 +505,14 @@
|
||||
(write-region (point-min) (point-max) nnmh-file nil 'nomesg)
|
||||
(kill-buffer (current-buffer)))))
|
||||
|
||||
(defun nnmh-deletable-article-p (group article)
|
||||
"Say whether ARTICLE in GROUP can be deleted."
|
||||
(let ((path (concat nnmh-current-directory (int-to-string article))))
|
||||
(and (file-writable-p path)
|
||||
(or (not nnmail-keep-last-article)
|
||||
(not (eq (cdr (nth 1 (assoc group nnmh-group-alist)))
|
||||
article))))))
|
||||
|
||||
(provide 'nnmh)
|
||||
|
||||
;;; nnmh.el ends here
|
||||
|
853
lisp/nnml.el
853
lisp/nnml.el
File diff suppressed because it is too large
Load Diff
603
lisp/nnspool.el
603
lisp/nnspool.el
@ -1,6 +1,5 @@
|
||||
;;; nnspool.el --- spool access for GNU Emacs
|
||||
|
||||
;; Copyright (C) 1988,89,90,93,94,95 Free Software Foundation, Inc.
|
||||
;; Copyright (C) 1988,89,90,93,94,95,96 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Masanobu UMEDA <umerin@flab.flab.fujitsu.junet>
|
||||
;; Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
|
||||
@ -30,45 +29,49 @@
|
||||
(require 'nnheader)
|
||||
(require 'nntp)
|
||||
(require 'timezone)
|
||||
(require 'nnoo)
|
||||
(eval-when-compile (require 'cl))
|
||||
|
||||
(defvar nnspool-inews-program news-inews-program
|
||||
(nnoo-declare nnspool)
|
||||
|
||||
(defvoo nnspool-inews-program news-inews-program
|
||||
"Program to post news.
|
||||
This is most commonly `inews' or `injnews'.")
|
||||
|
||||
(defvar nnspool-inews-switches '("-h")
|
||||
(defvoo nnspool-inews-switches '("-h" "-S")
|
||||
"Switches for nnspool-request-post to pass to `inews' for posting news.
|
||||
If you are using Cnews, you probably should set this variable to nil.")
|
||||
|
||||
(defvar nnspool-spool-directory news-path
|
||||
(defvoo nnspool-spool-directory (file-name-as-directory news-path)
|
||||
"Local news spool directory.")
|
||||
|
||||
(defvar nnspool-nov-directory (concat nnspool-spool-directory "over.view/")
|
||||
(defvoo nnspool-nov-directory (concat nnspool-spool-directory "over.view/")
|
||||
"Local news nov directory.")
|
||||
|
||||
(defvar nnspool-lib-dir "/usr/lib/news/"
|
||||
(defvoo nnspool-lib-dir "/usr/lib/news/"
|
||||
"Where the local news library files are stored.")
|
||||
|
||||
(defvar nnspool-active-file (concat nnspool-lib-dir "active")
|
||||
(defvoo nnspool-active-file (concat nnspool-lib-dir "active")
|
||||
"Local news active file.")
|
||||
|
||||
(defvar nnspool-newsgroups-file (concat nnspool-lib-dir "newsgroups")
|
||||
(defvoo nnspool-newsgroups-file (concat nnspool-lib-dir "newsgroups")
|
||||
"Local news newsgroups file.")
|
||||
|
||||
(defvar nnspool-distributions-file (concat nnspool-lib-dir "distributions")
|
||||
(defvoo nnspool-distributions-file (concat nnspool-lib-dir "distribs.pat")
|
||||
"Local news distributions file.")
|
||||
|
||||
(defvar nnspool-history-file (concat nnspool-lib-dir "history")
|
||||
(defvoo nnspool-history-file (concat nnspool-lib-dir "history")
|
||||
"Local news history file.")
|
||||
|
||||
(defvar nnspool-active-times-file (concat nnspool-lib-dir "active.times")
|
||||
(defvoo nnspool-active-times-file (concat nnspool-lib-dir "active.times")
|
||||
"Local news active date file.")
|
||||
|
||||
(defvar nnspool-large-newsgroup 50
|
||||
(defvoo nnspool-large-newsgroup 50
|
||||
"The number of the articles which indicates a large newsgroup.
|
||||
If the number of the articles is greater than the value, verbose
|
||||
messages will be shown to indicate the current status.")
|
||||
|
||||
(defvar nnspool-nov-is-evil nil
|
||||
(defvoo nnspool-nov-is-evil nil
|
||||
"Non-nil means that nnspool will never return NOV lines instead of headers.")
|
||||
|
||||
(defconst nnspool-sift-nov-with-sed nil
|
||||
@ -76,214 +79,195 @@ messages will be shown to indicate the current status.")
|
||||
If nil, nnspool will load the entire file into a buffer and process it
|
||||
there.")
|
||||
|
||||
(defvoo nnspool-rejected-article-hook nil
|
||||
"*A hook that will be run when an article has been rejected by the server.")
|
||||
|
||||
|
||||
|
||||
(defconst nnspool-version "nnspool 2.0"
|
||||
"Version numbers of this version of NNSPOOL.")
|
||||
|
||||
(defvar nnspool-current-directory nil
|
||||
(defvoo nnspool-current-directory nil
|
||||
"Current news group directory.")
|
||||
|
||||
(defvar nnspool-current-group nil)
|
||||
(defvar nnspool-status-string "")
|
||||
|
||||
|
||||
|
||||
(defvar nnspool-current-server nil)
|
||||
(defvar nnspool-server-alist nil)
|
||||
(defvar nnspool-server-variables
|
||||
(list
|
||||
(list 'nnspool-inews-program nnspool-inews-program)
|
||||
(list 'nnspool-inews-switches nnspool-inews-switches)
|
||||
(list 'nnspool-spool-directory nnspool-spool-directory)
|
||||
(list 'nnspool-nov-directory nnspool-nov-directory)
|
||||
(list 'nnspool-lib-dir nnspool-lib-dir)
|
||||
(list 'nnspool-active-file nnspool-active-file)
|
||||
(list 'nnspool-newsgroups-file nnspool-newsgroups-file)
|
||||
(list 'nnspool-distributions-file nnspool-distributions-file)
|
||||
(list 'nnspool-history-file nnspool-history-file)
|
||||
(list 'nnspool-active-times-file nnspool-active-times-file)
|
||||
(list 'nnspool-large-newsgroup nnspool-large-newsgroup)
|
||||
(list 'nnspool-nov-is-evil nnspool-nov-is-evil)
|
||||
(list 'nnspool-sift-nov-with-sed nnspool-sift-nov-with-sed)
|
||||
'(nnspool-current-directory nil)
|
||||
'(nnspool-current-group nil)
|
||||
'(nnspool-status-string "")))
|
||||
(defvoo nnspool-current-group nil)
|
||||
(defvoo nnspool-status-string "")
|
||||
|
||||
|
||||
;;; Interface functions.
|
||||
|
||||
(defun nnspool-retrieve-headers (sequence &optional newsgroup server)
|
||||
"Retrieve the headers for the articles in SEQUENCE.
|
||||
Newsgroup must be selected before calling this function."
|
||||
(nnoo-define-basics nnspool)
|
||||
|
||||
(deffoo nnspool-retrieve-headers (articles &optional group server fetch-old)
|
||||
"Retrieve the headers of ARTICLES."
|
||||
(save-excursion
|
||||
(set-buffer nntp-server-buffer)
|
||||
(erase-buffer)
|
||||
(let* ((number (length sequence))
|
||||
(count 0)
|
||||
(do-message (and (numberp nnspool-large-newsgroup)
|
||||
(> number nnspool-large-newsgroup)))
|
||||
file beg article)
|
||||
(if (not (nnspool-possibly-change-directory newsgroup))
|
||||
()
|
||||
(if (and (numberp (car sequence))
|
||||
(nnspool-retrieve-headers-with-nov sequence))
|
||||
(when (nnspool-possibly-change-directory group)
|
||||
(let* ((number (length articles))
|
||||
(count 0)
|
||||
(default-directory nnspool-current-directory)
|
||||
(do-message (and (numberp nnspool-large-newsgroup)
|
||||
(> number nnspool-large-newsgroup)))
|
||||
file beg article ag)
|
||||
(if (and (numberp (car articles))
|
||||
(nnspool-retrieve-headers-with-nov articles fetch-old))
|
||||
;; We successfully retrieved the NOV headers.
|
||||
'nov
|
||||
(while sequence
|
||||
(setq article (car sequence))
|
||||
;; No NOV headers here, so we do it the hard way.
|
||||
(while (setq article (pop articles))
|
||||
(if (stringp article)
|
||||
(progn
|
||||
(setq file (nnspool-find-article-by-message-id article))
|
||||
(setq article 0))
|
||||
(setq file (concat nnspool-current-directory
|
||||
(int-to-string article))))
|
||||
(and file (file-exists-p file)
|
||||
(progn
|
||||
(insert (format "221 %d Article retrieved.\n" article))
|
||||
(setq beg (point))
|
||||
(nnheader-insert-head file)
|
||||
(goto-char beg)
|
||||
(search-forward "\n\n" nil t)
|
||||
(forward-char -1)
|
||||
(insert ".\n")
|
||||
(delete-region (point) (point-max))))
|
||||
(setq sequence (cdr sequence))
|
||||
;; This is a Message-ID.
|
||||
(setq ag (nnspool-find-id article)
|
||||
file (and ag (nnspool-article-pathname
|
||||
(car ag) (cdr ag)))
|
||||
article (cdr ag))
|
||||
;; This is an article in the current group.
|
||||
(setq file (int-to-string article)))
|
||||
;; Insert the head of the article.
|
||||
(when (and file
|
||||
(file-exists-p file))
|
||||
(insert "221 ")
|
||||
(princ article (current-buffer))
|
||||
(insert " Article retrieved.\n")
|
||||
(setq beg (point))
|
||||
(inline (nnheader-insert-head file))
|
||||
(goto-char beg)
|
||||
(search-forward "\n\n" nil t)
|
||||
(forward-char -1)
|
||||
(insert ".\n")
|
||||
(delete-region (point) (point-max)))
|
||||
|
||||
(and do-message
|
||||
(zerop (% (setq count (1+ count)) 20))
|
||||
(message "NNSPOOL: Receiving headers... %d%%"
|
||||
(zerop (% (incf count) 20))
|
||||
(message "nnspool: Receiving headers... %d%%"
|
||||
(/ (* count 100) number))))
|
||||
|
||||
(and do-message (message "NNSPOOL: Receiving headers...done"))
|
||||
(and do-message
|
||||
(message "nnspool: Receiving headers...done"))
|
||||
|
||||
;; Fold continuation lines.
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward "\\(\r?\n[ \t]+\\)+" nil t)
|
||||
(replace-match " " t t))
|
||||
(nnheader-fold-continuation-lines)
|
||||
'headers)))))
|
||||
|
||||
(defun nnspool-open-server (server &optional defs)
|
||||
(nnheader-init-server-buffer)
|
||||
(if (equal server nnspool-current-server)
|
||||
t
|
||||
(if nnspool-current-server
|
||||
(setq nnspool-server-alist
|
||||
(cons (list nnspool-current-server
|
||||
(nnheader-save-variables nnspool-server-variables))
|
||||
nnspool-server-alist)))
|
||||
(let ((state (assoc server nnspool-server-alist)))
|
||||
(if state
|
||||
(progn
|
||||
(nnheader-restore-variables (nth 1 state))
|
||||
(setq nnspool-server-alist (delq state nnspool-server-alist)))
|
||||
(nnheader-set-init-variables nnspool-server-variables defs)))
|
||||
(setq nnspool-current-server server)))
|
||||
(deffoo nnspool-open-server (server &optional defs)
|
||||
(nnoo-change-server 'nnspool server defs)
|
||||
(cond
|
||||
((not (file-exists-p nnspool-spool-directory))
|
||||
(nnspool-close-server)
|
||||
(nnheader-report 'nnspool "Spool directory doesn't exist: %s"
|
||||
nnspool-spool-directory))
|
||||
((not (file-directory-p
|
||||
(directory-file-name
|
||||
(file-truename nnspool-spool-directory))))
|
||||
(nnspool-close-server)
|
||||
(nnheader-report 'nnspool "Not a directory: %s" nnspool-spool-directory))
|
||||
((not (file-exists-p nnspool-active-file))
|
||||
(nnheader-report 'nnspool "The active file doesn't exist: %s"
|
||||
nnspool-active-file))
|
||||
(t
|
||||
(nnheader-report 'nnspool "Opened server %s using directory %s"
|
||||
server nnspool-spool-directory)
|
||||
t)))
|
||||
|
||||
(defun nnspool-close-server (&optional server)
|
||||
t)
|
||||
|
||||
(defun nnspool-server-opened (&optional server)
|
||||
(and (equal server nnspool-current-server)
|
||||
nntp-server-buffer
|
||||
(buffer-name nntp-server-buffer)))
|
||||
|
||||
(defun nnspool-status-message (&optional server)
|
||||
"Return server status response as string."
|
||||
nnspool-status-string)
|
||||
|
||||
(defun nnspool-request-article (id &optional newsgroup server buffer)
|
||||
(deffoo nnspool-request-article (id &optional group server buffer)
|
||||
"Select article by message ID (or number)."
|
||||
(nnspool-possibly-change-directory newsgroup)
|
||||
(let ((file (if (stringp id)
|
||||
(nnspool-find-article-by-message-id id)
|
||||
(concat nnspool-current-directory (prin1-to-string id))))
|
||||
(nntp-server-buffer (or buffer nntp-server-buffer)))
|
||||
(if (and (stringp file)
|
||||
(file-exists-p file)
|
||||
(not (file-directory-p file)))
|
||||
(save-excursion
|
||||
(nnspool-find-file file)))))
|
||||
|
||||
(defun nnspool-request-body (id &optional newsgroup server)
|
||||
(nnspool-possibly-change-directory group)
|
||||
(let ((nntp-server-buffer (or buffer nntp-server-buffer))
|
||||
file ag)
|
||||
(if (stringp id)
|
||||
;; This is a Message-ID.
|
||||
(when (setq ag (nnspool-find-id id))
|
||||
(setq file (nnspool-article-pathname (car ag) (cdr ag))))
|
||||
(setq file (nnspool-article-pathname nnspool-current-group id)))
|
||||
(and file
|
||||
(file-exists-p file)
|
||||
(not (file-directory-p file))
|
||||
(save-excursion (nnspool-find-file file))
|
||||
;; We return the article number and group name.
|
||||
(if (numberp id)
|
||||
(cons nnspool-current-group id)
|
||||
ag))))
|
||||
|
||||
(deffoo nnspool-request-body (id &optional group server)
|
||||
"Select article body by message ID (or number)."
|
||||
(nnspool-possibly-change-directory newsgroup)
|
||||
(if (nnspool-request-article id)
|
||||
(nnspool-possibly-change-directory group)
|
||||
(let ((res (nnspool-request-article id)))
|
||||
(when res
|
||||
(save-excursion
|
||||
(set-buffer nntp-server-buffer)
|
||||
(goto-char (point-min))
|
||||
(if (search-forward "\n\n" nil t)
|
||||
(delete-region (point-min) (point)))
|
||||
t)))
|
||||
(when (search-forward "\n\n" nil t)
|
||||
(delete-region (point-min) (point)))
|
||||
res))))
|
||||
|
||||
(defun nnspool-request-head (id &optional newsgroup server)
|
||||
(deffoo nnspool-request-head (id &optional group server)
|
||||
"Select article head by message ID (or number)."
|
||||
(nnspool-possibly-change-directory newsgroup)
|
||||
(if (nnspool-request-article id)
|
||||
(nnspool-possibly-change-directory group)
|
||||
(let ((res (nnspool-request-article id)))
|
||||
(when res
|
||||
(save-excursion
|
||||
(set-buffer nntp-server-buffer)
|
||||
(goto-char (point-min))
|
||||
(if (search-forward "\n\n" nil t)
|
||||
(delete-region (1- (point)) (point-max)))
|
||||
t)))
|
||||
(when (search-forward "\n\n" nil t)
|
||||
(delete-region (1- (point)) (point-max)))
|
||||
(nnheader-fold-continuation-lines)))
|
||||
res))
|
||||
|
||||
(defun nnspool-request-group (group &optional server dont-check)
|
||||
(deffoo nnspool-request-group (group &optional server dont-check)
|
||||
"Select news GROUP."
|
||||
(let ((pathname (nnspool-article-pathname
|
||||
(nnspool-replace-chars-in-string group ?. ?/)))
|
||||
(let ((pathname (nnspool-article-pathname group))
|
||||
dir)
|
||||
(if (not (file-directory-p pathname))
|
||||
(progn
|
||||
(setq nnspool-status-string
|
||||
"Invalid group name (no such directory)")
|
||||
nil)
|
||||
(nnheader-report
|
||||
'nnspool "Invalid group name (no such directory): %s" group)
|
||||
(setq nnspool-current-directory pathname)
|
||||
(setq nnspool-status-string "")
|
||||
(if (not dont-check)
|
||||
(nnheader-report 'nnspool "Selected group %s" group)
|
||||
(if dont-check
|
||||
(progn
|
||||
(setq dir (directory-files pathname nil "^[0-9]+$" t))
|
||||
;; yes, completely empty spool directories *are* possible
|
||||
;; Fix by Sudish Joseph <joseph@cis.ohio-state.edu>
|
||||
(and dir
|
||||
(setq dir
|
||||
(sort
|
||||
(mapcar
|
||||
(function
|
||||
(lambda (name)
|
||||
(string-to-int name)))
|
||||
dir)
|
||||
'<)))
|
||||
(save-excursion
|
||||
(set-buffer nntp-server-buffer)
|
||||
(erase-buffer)
|
||||
(if dir
|
||||
(insert
|
||||
(format "211 %d %d %d %s\n" (length dir) (car dir)
|
||||
(progn (while (cdr dir) (setq dir (cdr dir)))
|
||||
(car dir))
|
||||
group))
|
||||
(insert (format "211 0 0 0 %s\n" group))))))
|
||||
t)))
|
||||
(nnheader-report 'nnspool "Selected group %s" group)
|
||||
t)
|
||||
;; Yes, completely empty spool directories *are* possible.
|
||||
;; Fix by Sudish Joseph <joseph@cis.ohio-state.edu>
|
||||
(when (setq dir (directory-files pathname nil "^[0-9]+$" t))
|
||||
(setq dir
|
||||
(sort (mapcar (lambda (name) (string-to-int name)) dir) '<)))
|
||||
(if dir
|
||||
(nnheader-insert
|
||||
"211 %d %d %d %s\n" (length dir) (car dir)
|
||||
(progn (while (cdr dir) (setq dir (cdr dir))) (car dir))
|
||||
group)
|
||||
(nnheader-report 'nnspool "Empty group %s" group)
|
||||
(nnheader-insert "211 0 0 0 %s\n" group))))))
|
||||
|
||||
(defun nnspool-close-group (group &optional server)
|
||||
(deffoo nnspool-request-type (group &optional article)
|
||||
'news)
|
||||
|
||||
(deffoo nnspool-close-group (group &optional server)
|
||||
t)
|
||||
|
||||
(defun nnspool-request-list (&optional server)
|
||||
(deffoo nnspool-request-list (&optional server)
|
||||
"List active newsgroups."
|
||||
(save-excursion
|
||||
(nnspool-find-file nnspool-active-file)))
|
||||
(or (nnspool-find-file nnspool-active-file)
|
||||
(nnheader-report 'nnspool (nnheader-file-error nnspool-active-file)))))
|
||||
|
||||
(defun nnspool-request-list-newsgroups (&optional server)
|
||||
(deffoo nnspool-request-list-newsgroups (&optional server)
|
||||
"List newsgroups (defined in NNTP2)."
|
||||
(save-excursion
|
||||
(nnspool-find-file nnspool-newsgroups-file)))
|
||||
(or (nnspool-find-file nnspool-newsgroups-file)
|
||||
(nnheader-report 'nnspool (nnheader-file-error
|
||||
nnspool-newsgroups-file)))))
|
||||
|
||||
(defun nnspool-request-list-distributions (&optional server)
|
||||
(deffoo nnspool-request-list-distributions (&optional server)
|
||||
"List distributions (defined in NNTP2)."
|
||||
(save-excursion
|
||||
(nnspool-find-file nnspool-distributions-file)))
|
||||
(or (nnspool-find-file nnspool-distributions-file)
|
||||
(nnheader-report 'nnspool (nnheader-file-error
|
||||
nnspool-distributions-file)))))
|
||||
|
||||
;; Suggested by Hallvard B Furuseth <h.b.furuseth@usit.uio.no>.
|
||||
(defun nnspool-request-newgroups (date &optional server)
|
||||
(deffoo nnspool-request-newgroups (date &optional server)
|
||||
"List groups created after DATE."
|
||||
(if (nnspool-find-file nnspool-active-times-file)
|
||||
(save-excursion
|
||||
@ -317,21 +301,33 @@ Newsgroup must be selected before calling this function."
|
||||
t)
|
||||
nil))
|
||||
|
||||
(defun nnspool-request-post (&optional server)
|
||||
(deffoo nnspool-request-post (&optional server)
|
||||
"Post a new news in current buffer."
|
||||
(save-excursion
|
||||
(let* ((process-connection-type nil) ; t bugs out on Solaris
|
||||
(inews-buffer (generate-new-buffer " *nnspool post*"))
|
||||
(proc (apply 'start-process "*nnspool inews*" inews-buffer
|
||||
nnspool-inews-program nnspool-inews-switches)))
|
||||
(set-process-sentinel proc 'nnspool-inews-sentinel)
|
||||
(process-send-region proc (point-min) (point-max))
|
||||
;; We slap a condition-case around this, because the process may
|
||||
;; have exited already...
|
||||
(condition-case nil
|
||||
(process-send-eof proc)
|
||||
(error nil))
|
||||
t)))
|
||||
(proc
|
||||
(condition-case err
|
||||
(apply 'start-process "*nnspool inews*" inews-buffer
|
||||
nnspool-inews-program nnspool-inews-switches)
|
||||
(error
|
||||
(nnheader-report 'nnspool "inews error: %S" err)))))
|
||||
(if (not proc)
|
||||
;; The inews program failed.
|
||||
()
|
||||
(nnheader-report 'nnspool "")
|
||||
(set-process-sentinel proc 'nnspool-inews-sentinel)
|
||||
(process-send-region proc (point-min) (point-max))
|
||||
;; We slap a condition-case around this, because the process may
|
||||
;; have exited already...
|
||||
(condition-case nil
|
||||
(process-send-eof proc)
|
||||
(error nil))
|
||||
t))))
|
||||
|
||||
|
||||
|
||||
;;; Internal functions.
|
||||
|
||||
(defun nnspool-inews-sentinel (proc status)
|
||||
(save-excursion
|
||||
@ -340,69 +336,118 @@ Newsgroup must be selected before calling this function."
|
||||
(if (or (zerop (buffer-size))
|
||||
(search-forward "spooled" nil t))
|
||||
(kill-buffer (current-buffer))
|
||||
;; Make status message by unfolding lines.
|
||||
(subst-char-in-region (point-min) (point-max) ?\n ?\\ 'noundo)
|
||||
(setq nnspool-status-string (buffer-string))
|
||||
;; Make status message by folding lines.
|
||||
(while (re-search-forward "[ \t\n]+" nil t)
|
||||
(replace-match " " t t))
|
||||
(nnheader-report 'nnspool "%s" (buffer-string))
|
||||
(message "nnspool: %s" nnspool-status-string)
|
||||
;(kill-buffer (current-buffer))
|
||||
)))
|
||||
(ding)
|
||||
(run-hooks 'nnspool-rejected-article-hook))))
|
||||
|
||||
(defalias 'nnspool-request-post-buffer 'nntp-request-post-buffer)
|
||||
|
||||
|
||||
;;; Internal functions.
|
||||
|
||||
(defun nnspool-retrieve-headers-with-nov (articles)
|
||||
(defun nnspool-retrieve-headers-with-nov (articles &optional fetch-old)
|
||||
(if (or gnus-nov-is-evil nnspool-nov-is-evil)
|
||||
nil
|
||||
(let ((nov (concat (file-name-as-directory nnspool-nov-directory)
|
||||
(nnspool-replace-chars-in-string
|
||||
nnspool-current-group ?. ?/)
|
||||
"/.overview"))
|
||||
article)
|
||||
(if (file-exists-p nov)
|
||||
(save-excursion
|
||||
(set-buffer nntp-server-buffer)
|
||||
(erase-buffer)
|
||||
(if nnspool-sift-nov-with-sed
|
||||
(nnspool-sift-nov-with-sed articles nov)
|
||||
(insert-file-contents nov)
|
||||
;; First we find the first wanted line. We issue a number
|
||||
;; of search-forwards - the first article we are looking
|
||||
;; for may be expired, so we have to go on searching until
|
||||
;; we find one of the articles we want.
|
||||
(while (and articles
|
||||
(setq article (concat (int-to-string
|
||||
(car articles)) "\t"))
|
||||
(not (or (looking-at article)
|
||||
(search-forward (concat "\n" article)
|
||||
nil t))))
|
||||
(setq articles (cdr articles)))
|
||||
(if (not articles)
|
||||
()
|
||||
(beginning-of-line)
|
||||
(delete-region (point-min) (point))
|
||||
;; Then we find the last wanted line. We go to the end
|
||||
;; of the buffer and search backward much the same way
|
||||
;; we did to find the first article.
|
||||
;; !!! Perhaps it would be better just to do a (last articles),
|
||||
;; and go forward successively over each line and
|
||||
;; compare to avoid this (reverse), like this:
|
||||
;; (while (and (>= last (read nntp-server-buffer)))
|
||||
;; (zerop (forward-line 1))))
|
||||
(setq articles (reverse articles))
|
||||
(goto-char (point-max))
|
||||
(while (and articles
|
||||
(not (search-backward
|
||||
(concat "\n" (int-to-string (car articles))
|
||||
"\t") nil t)))
|
||||
(setq articles (cdr articles)))
|
||||
(if articles
|
||||
(progn
|
||||
(forward-line 2)
|
||||
(delete-region (point) (point-max)))))
|
||||
(or articles (progn (erase-buffer) nil))))))))
|
||||
(let ((nov (nnheader-group-pathname
|
||||
nnspool-current-group nnspool-nov-directory ".overview"))
|
||||
(arts articles)
|
||||
last)
|
||||
(if (not (file-exists-p nov))
|
||||
()
|
||||
(save-excursion
|
||||
(set-buffer nntp-server-buffer)
|
||||
(erase-buffer)
|
||||
(if nnspool-sift-nov-with-sed
|
||||
(nnspool-sift-nov-with-sed articles nov)
|
||||
(insert-file-contents nov)
|
||||
(if (and fetch-old
|
||||
(not (numberp fetch-old)))
|
||||
t ; We want all the headers.
|
||||
(condition-case ()
|
||||
(progn
|
||||
;; First we find the first wanted line.
|
||||
(nnspool-find-nov-line
|
||||
(if fetch-old (max 1 (- (car articles) fetch-old))
|
||||
(car articles)))
|
||||
(delete-region (point-min) (point))
|
||||
;; Then we find the last wanted line.
|
||||
(if (nnspool-find-nov-line
|
||||
(progn (while (cdr articles)
|
||||
(setq articles (cdr articles)))
|
||||
(car articles)))
|
||||
(forward-line 1))
|
||||
(delete-region (point) (point-max))
|
||||
;; If the buffer is empty, this wasn't very successful.
|
||||
(unless (zerop (buffer-size))
|
||||
;; We check what the last article number was.
|
||||
;; The NOV file may be out of sync with the articles
|
||||
;; in the group.
|
||||
(forward-line -1)
|
||||
(setq last (read (current-buffer)))
|
||||
(if (= last (car articles))
|
||||
;; Yup, it's all there.
|
||||
t
|
||||
;; Perhaps not. We try to find the missing articles.
|
||||
(while (and arts
|
||||
(<= last (car arts)))
|
||||
(pop arts))
|
||||
;; The articles in `arts' are missing from the buffer.
|
||||
(while arts
|
||||
(nnspool-insert-nov-head (pop arts)))
|
||||
t)))
|
||||
;; The NOV file was corrupted.
|
||||
(error nil)))))))))
|
||||
|
||||
(defun nnspool-insert-nov-head (article)
|
||||
"Read the head of ARTICLE, convert to NOV headers, and insert."
|
||||
(save-excursion
|
||||
(let ((cur (current-buffer))
|
||||
buf)
|
||||
(setq buf (nnheader-set-temp-buffer " *nnspool head*"))
|
||||
(when (nnheader-insert-head
|
||||
(nnspool-article-pathname nnspool-current-group article))
|
||||
(nnheader-insert-article-line article)
|
||||
(let ((headers (nnheader-parse-head)))
|
||||
(set-buffer cur)
|
||||
(goto-char (point-max))
|
||||
(nnheader-insert-nov headers)))
|
||||
(kill-buffer buf))))
|
||||
|
||||
(defun nnspool-find-nov-line (article)
|
||||
(let ((max (point-max))
|
||||
(min (goto-char (point-min)))
|
||||
(cur (current-buffer))
|
||||
(prev (point-min))
|
||||
num found)
|
||||
(while (not found)
|
||||
(goto-char (/ (+ max min) 2))
|
||||
(beginning-of-line)
|
||||
(if (or (= (point) prev)
|
||||
(eobp))
|
||||
(setq found t)
|
||||
(setq prev (point))
|
||||
(cond ((> (setq num (read cur)) article)
|
||||
(setq max (point)))
|
||||
((< num article)
|
||||
(setq min (point)))
|
||||
(t
|
||||
(setq found 'yes)))))
|
||||
;; Now we may have found the article we're looking for, or we
|
||||
;; may be somewhere near it.
|
||||
(when (and (not (eq found 'yes))
|
||||
(not (eq num article)))
|
||||
(setq found (point))
|
||||
(while (and (< (point) max)
|
||||
(or (not (numberp num))
|
||||
(< num article)))
|
||||
(forward-line 1)
|
||||
(setq found (point))
|
||||
(or (eobp)
|
||||
(= (setq num (read cur)) article)))
|
||||
(unless (eq num article)
|
||||
(goto-char found)))
|
||||
(beginning-of-line)
|
||||
(eq num article)))
|
||||
|
||||
(defun nnspool-sift-nov-with-sed (articles file)
|
||||
(let ((first (car articles))
|
||||
(last (progn (while (cdr articles) (setq articles (cdr articles)))
|
||||
@ -413,70 +458,41 @@ Newsgroup must be selected before calling this function."
|
||||
file)))
|
||||
|
||||
;; Fixed by fdc@cliwe.ping.de (Frank D. Cringle).
|
||||
(defun nnspool-find-article-by-message-id (id)
|
||||
"Return full pathname of an article identified by message-ID."
|
||||
;; Find out what group an article identified by a Message-ID is in.
|
||||
(defun nnspool-find-id (id)
|
||||
(save-excursion
|
||||
(let ((buf (get-buffer-create " *nnspool work*")))
|
||||
(set-buffer buf)
|
||||
(erase-buffer)
|
||||
(call-process "grep" nil t nil id nnspool-history-file)
|
||||
(goto-char (point-min))
|
||||
(if (looking-at "<[^>]+>[ \t]+[-0-9~]+[ \t]+\\([^ \t\n]*\\)")
|
||||
(concat nnspool-spool-directory
|
||||
(nnspool-replace-chars-in-string
|
||||
(buffer-substring (match-beginning 1) (match-end 1))
|
||||
?. ?/))))))
|
||||
(set-buffer (get-buffer-create " *nnspool work*"))
|
||||
(buffer-disable-undo (current-buffer))
|
||||
(erase-buffer)
|
||||
(condition-case ()
|
||||
(call-process "grep" nil t nil id nnspool-history-file)
|
||||
(error nil))
|
||||
(goto-char (point-min))
|
||||
(prog1
|
||||
(if (looking-at "<[^>]+>[ \t]+[-0-9~]+[ \t]+\\([^ /\t\n]+\\)/\\([0-9]+\\)[ \t\n]")
|
||||
(cons (match-string 1) (string-to-int (match-string 2))))
|
||||
(kill-buffer (current-buffer)))))
|
||||
|
||||
(defun nnspool-find-file (file)
|
||||
"Insert FILE in server buffer safely."
|
||||
(set-buffer nntp-server-buffer)
|
||||
(erase-buffer)
|
||||
(condition-case ()
|
||||
(progn (insert-file-contents file) t)
|
||||
(progn (nnheader-insert-file-contents-literally file) t)
|
||||
(file-error nil)))
|
||||
|
||||
(defun nnspool-possibly-change-directory (newsgroup)
|
||||
(if newsgroup
|
||||
(let ((pathname (nnspool-article-pathname
|
||||
(nnspool-replace-chars-in-string newsgroup ?. ?/))))
|
||||
(if (file-directory-p pathname)
|
||||
(progn
|
||||
(setq nnspool-current-directory pathname)
|
||||
(setq nnspool-current-group newsgroup))
|
||||
(setq nnspool-status-string
|
||||
(format "No such newsgroup: %s" newsgroup))
|
||||
nil))
|
||||
t))
|
||||
(defun nnspool-possibly-change-directory (group)
|
||||
(if (not group)
|
||||
t
|
||||
(let ((pathname (nnspool-article-pathname group)))
|
||||
(if (file-directory-p pathname)
|
||||
(setq nnspool-current-directory pathname
|
||||
nnspool-current-group group)
|
||||
(nnheader-report 'nnspool "No such newsgroup: %s" group)))))
|
||||
|
||||
(defun nnspool-article-pathname (group)
|
||||
"Make pathname for GROUP."
|
||||
(concat (file-name-as-directory nnspool-spool-directory) group "/"))
|
||||
|
||||
(defun nnspool-replace-chars-in-string (string from to)
|
||||
"Replace characters in STRING from FROM to TO."
|
||||
(let ((string (substring string 0)) ;Copy string.
|
||||
(len (length string))
|
||||
(idx 0))
|
||||
;; Replace all occurrences of FROM with TO.
|
||||
(while (< idx len)
|
||||
(if (= (aref string idx) from)
|
||||
(aset string idx to))
|
||||
(setq idx (1+ idx)))
|
||||
string))
|
||||
|
||||
(defun nnspool-number-base-10 (num pos)
|
||||
(if (<= pos 0) ""
|
||||
(setcdr num (+ (* (% (car num) 10) 65536) (cdr num)))
|
||||
(apply
|
||||
'concat
|
||||
(reverse
|
||||
(list
|
||||
(char-to-string
|
||||
(aref "0123456789" (% (cdr num) 10)))
|
||||
(progn
|
||||
(setcdr num (/ (cdr num) 10))
|
||||
(setcar num (/ (car num) 10))
|
||||
(nnspool-number-base-10 num (1- pos))))))))
|
||||
(defun nnspool-article-pathname (group &optional article)
|
||||
"Find the path for GROUP."
|
||||
(nnheader-group-pathname group nnspool-spool-directory article))
|
||||
|
||||
(defun nnspool-seconds-since-epoch (date)
|
||||
(let* ((tdate (mapcar (lambda (ti) (and ti (string-to-int ti)))
|
||||
@ -485,9 +501,10 @@ Newsgroup must be selected before calling this function."
|
||||
(timezone-parse-time
|
||||
(aref (timezone-parse-date date) 3))))
|
||||
(unix (encode-time (nth 2 ttime) (nth 1 ttime) (nth 0 ttime)
|
||||
(nth 2 tdate) (nth 1 tdate) (nth 0 tdate) (nth 4 tdate))))
|
||||
(nth 2 tdate) (nth 1 tdate) (nth 0 tdate)
|
||||
(nth 4 tdate))))
|
||||
(+ (* (car unix) 65536.0)
|
||||
(car (cdr unix)))))
|
||||
(cadr unix))))
|
||||
|
||||
(provide 'nnspool)
|
||||
|
||||
|
1187
lisp/nntp.el
1187
lisp/nntp.el
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,5 @@
|
||||
;;; nnvirtual.el --- virtual newsgroups access for Gnus
|
||||
|
||||
;; Copyright (C) 1994,95 Free Software Foundation, Inc.
|
||||
;; Copyright (C) 1994,95,96 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
|
||||
;; Masanobu UMEDA <umerin@flab.flab.fujitsu.junet>
|
||||
@ -34,445 +33,377 @@
|
||||
(require 'nntp)
|
||||
(require 'nnheader)
|
||||
(require 'gnus)
|
||||
(require 'nnoo)
|
||||
(eval-when-compile (require 'cl))
|
||||
|
||||
(nnoo-declare nnvirtual)
|
||||
|
||||
(defvoo nnvirtual-always-rescan nil
|
||||
"*If non-nil, always scan groups for unread articles when entering a group.
|
||||
If this variable is nil (which is the default) and you read articles
|
||||
in a component group after the virtual group has been activated, the
|
||||
read articles from the component group will show up when you enter the
|
||||
virtual group.")
|
||||
|
||||
(defvoo nnvirtual-component-regexp nil
|
||||
"*Regexp to match component groups.")
|
||||
|
||||
|
||||
|
||||
(defconst nnvirtual-version "nnvirtual 1.0"
|
||||
"Version number of this version of nnvirtual.")
|
||||
(defconst nnvirtual-version "nnvirtual 1.0")
|
||||
|
||||
(defvar nnvirtual-group-alist nil)
|
||||
(defvar nnvirtual-current-group nil)
|
||||
(defvar nnvirtual-current-groups nil)
|
||||
(defvar nnvirtual-current-mapping nil)
|
||||
(defvoo nnvirtual-current-group nil)
|
||||
(defvoo nnvirtual-component-groups nil)
|
||||
(defvoo nnvirtual-mapping nil)
|
||||
|
||||
(defvar nnvirtual-do-not-open nil)
|
||||
(defvoo nnvirtual-status-string "")
|
||||
|
||||
(defvar nnvirtual-status-string "")
|
||||
(eval-and-compile
|
||||
(autoload 'gnus-cache-articles-in-group "gnus-cache"))
|
||||
|
||||
|
||||
|
||||
;;; Interface functions.
|
||||
|
||||
(defun nnvirtual-retrieve-headers (sequence &optional newsgroup server)
|
||||
"Retrieve the headers for the articles in SEQUENCE."
|
||||
(nnvirtual-possibly-change-newsgroups newsgroup server t)
|
||||
(save-excursion
|
||||
(set-buffer (get-buffer-create "*virtual headers*"))
|
||||
(buffer-disable-undo (current-buffer))
|
||||
(erase-buffer)
|
||||
(if (stringp (car sequence))
|
||||
'headers
|
||||
(let ((map nnvirtual-current-mapping)
|
||||
(offset 0)
|
||||
articles beg group active top article result prefix
|
||||
fetched-articles group-method)
|
||||
(while sequence
|
||||
(while (< (car (car map)) (car sequence))
|
||||
(setq offset (car (car map)))
|
||||
(setq map (cdr map)))
|
||||
(setq top (car (car map)))
|
||||
(setq group (nth 1 (car map)))
|
||||
(setq prefix (gnus-group-real-prefix group))
|
||||
(setq active (nth 2 (car map)))
|
||||
(setq articles nil)
|
||||
(while (and sequence (<= (car sequence) top))
|
||||
(setq articles (cons (- (+ active (car sequence)) offset)
|
||||
articles))
|
||||
(setq sequence (cdr sequence)))
|
||||
(setq articles (nreverse articles))
|
||||
(if (and articles
|
||||
(setq result
|
||||
(progn
|
||||
(setq group-method
|
||||
(gnus-find-method-for-group group))
|
||||
(and (or (gnus-server-opened group-method)
|
||||
(gnus-open-server group-method))
|
||||
(gnus-request-group group t)
|
||||
(gnus-retrieve-headers articles group)))))
|
||||
(save-excursion
|
||||
(set-buffer nntp-server-buffer)
|
||||
;; If we got HEAD headers, we convert them into NOV
|
||||
;; headers. This is slow, inefficient and, come to think
|
||||
;; of it, downright evil. So sue me. I couldn't be
|
||||
;; bothered to write a header parse routine that could
|
||||
;; parse a mixed HEAD/NOV buffer.
|
||||
(and (eq result 'headers) (nnvirtual-convert-headers))
|
||||
(goto-char (point-min))
|
||||
(setq fetched-articles nil)
|
||||
(while (not (eobp))
|
||||
(setq beg (point)
|
||||
article (read nntp-server-buffer)
|
||||
fetched-articles (cons article fetched-articles))
|
||||
(delete-region beg (point))
|
||||
(insert (int-to-string (+ (- article active) offset)))
|
||||
(beginning-of-line)
|
||||
(looking-at
|
||||
"[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t")
|
||||
(goto-char (match-end 0))
|
||||
(or (search-forward
|
||||
"\t" (save-excursion (end-of-line) (point)) t)
|
||||
(end-of-line))
|
||||
(while (= (char-after (1- (point))) ? )
|
||||
(forward-char -1)
|
||||
(delete-char 1))
|
||||
(if (eolp)
|
||||
(progn
|
||||
(end-of-line)
|
||||
(or (= (char-after (1- (point))) ?\t)
|
||||
(insert ?\t))
|
||||
(insert (format "Xref: %s %s:%d\t" (system-name)
|
||||
group article)))
|
||||
(if (not (string= "" prefix))
|
||||
(while (re-search-forward
|
||||
"[^ ]+:[0-9]+"
|
||||
(save-excursion (end-of-line) (point)) t)
|
||||
(save-excursion
|
||||
(goto-char (match-beginning 0))
|
||||
(insert prefix))))
|
||||
(end-of-line)
|
||||
(or (= (char-after (1- (point))) ?\t)
|
||||
(insert ?\t)))
|
||||
(forward-line 1))))
|
||||
(goto-char (point-max))
|
||||
(insert-buffer-substring nntp-server-buffer)
|
||||
;; We have now massaged and inserted the headers from one
|
||||
;; group. In case some of the articles have expired or been
|
||||
;; cancelled, we have to mark them as read in the component
|
||||
;; group.
|
||||
(let ((unfetched (gnus-sorted-complement
|
||||
articles (nreverse fetched-articles))))
|
||||
(and unfetched
|
||||
(gnus-group-make-articles-read group unfetched nil))))
|
||||
;; The headers are ready for reading, so they are inserted into
|
||||
;; the nntp-server-buffer, which is where Gnus expects to find
|
||||
;; them.
|
||||
(prog1
|
||||
(save-excursion
|
||||
(set-buffer nntp-server-buffer)
|
||||
(erase-buffer)
|
||||
(insert-buffer-substring "*virtual headers*")
|
||||
'nov)
|
||||
(kill-buffer (current-buffer)))))))
|
||||
(nnoo-define-basics nnvirtual)
|
||||
|
||||
(defun nnvirtual-open-server (newsgroups &optional something)
|
||||
"Open a virtual newsgroup that contains NEWSGROUPS."
|
||||
(nnheader-init-server-buffer))
|
||||
|
||||
(defun nnvirtual-close-server (&rest dum)
|
||||
"Close news server."
|
||||
t)
|
||||
|
||||
(defun nnvirtual-request-close ()
|
||||
(setq nnvirtual-current-group nil
|
||||
nnvirtual-current-groups nil
|
||||
nnvirtual-current-mapping nil
|
||||
nnvirtual-group-alist nil)
|
||||
t)
|
||||
|
||||
(defun nnvirtual-server-opened (&optional server)
|
||||
"Return server process status, T or NIL.
|
||||
If the stream is opened, return T, otherwise return NIL."
|
||||
(and nntp-server-buffer
|
||||
(get-buffer nntp-server-buffer)))
|
||||
|
||||
(defun nnvirtual-status-message (&optional server)
|
||||
"Return server status response as string."
|
||||
nnvirtual-status-string)
|
||||
|
||||
(defun nnvirtual-request-article (article &optional newsgroup server buffer)
|
||||
"Select article by message number."
|
||||
(nnvirtual-possibly-change-newsgroups newsgroup server t)
|
||||
(and (numberp article)
|
||||
(let ((map nnvirtual-current-mapping)
|
||||
(offset 0)
|
||||
group-method)
|
||||
(while (< (car (car map)) article)
|
||||
(setq offset (car (car map)))
|
||||
(setq map (cdr map)))
|
||||
(setq group-method (gnus-find-method-for-group (nth 1 (car map))))
|
||||
(or (gnus-server-opened group-method)
|
||||
(gnus-open-server group-method))
|
||||
(gnus-request-group (nth 1 (car map)) t)
|
||||
(gnus-request-article (- (+ (nth 2 (car map)) article) offset)
|
||||
(nth 1 (car map)) buffer))))
|
||||
|
||||
(defun nnvirtual-request-group (group &optional server dont-check)
|
||||
"Make GROUP the current newsgroup."
|
||||
(nnvirtual-possibly-change-newsgroups group server dont-check)
|
||||
(let ((map nnvirtual-current-mapping))
|
||||
(deffoo nnvirtual-retrieve-headers (articles &optional newsgroup
|
||||
server fetch-old)
|
||||
(when (nnvirtual-possibly-change-server server)
|
||||
(save-excursion
|
||||
(set-buffer nntp-server-buffer)
|
||||
(erase-buffer)
|
||||
(if map
|
||||
(progn
|
||||
(while (cdr map)
|
||||
(setq map (cdr map)))
|
||||
(insert (format "211 %d 1 %d %s\n" (car (car map))
|
||||
(car (car map)) group))
|
||||
t)
|
||||
(setq nnvirtual-status-string "No component groups")
|
||||
(setq nnvirtual-current-group nil)
|
||||
nil))))
|
||||
(if (stringp (car articles))
|
||||
'headers
|
||||
(let ((vbuf (nnheader-set-temp-buffer
|
||||
(get-buffer-create " *virtual headers*")))
|
||||
(unfetched (mapcar (lambda (g) (list g))
|
||||
nnvirtual-component-groups))
|
||||
(system-name (system-name))
|
||||
cgroup article result prefix)
|
||||
(while articles
|
||||
(setq article (assq (pop articles) nnvirtual-mapping))
|
||||
(when (and (setq cgroup (cadr article))
|
||||
(gnus-check-server
|
||||
(gnus-find-method-for-group cgroup) t)
|
||||
(gnus-request-group cgroup t))
|
||||
(setq prefix (gnus-group-real-prefix cgroup))
|
||||
(when (setq result (gnus-retrieve-headers
|
||||
(list (caddr article)) cgroup nil))
|
||||
(set-buffer nntp-server-buffer)
|
||||
(if (zerop (buffer-size))
|
||||
(nconc (assq cgroup unfetched) (list (caddr article)))
|
||||
;; If we got HEAD headers, we convert them into NOV
|
||||
;; headers. This is slow, inefficient and, come to think
|
||||
;; of it, downright evil. So sue me. I couldn't be
|
||||
;; bothered to write a header parse routine that could
|
||||
;; parse a mixed HEAD/NOV buffer.
|
||||
(when (eq result 'headers)
|
||||
(nnvirtual-convert-headers))
|
||||
(goto-char (point-min))
|
||||
(while (not (eobp))
|
||||
(delete-region
|
||||
(point) (progn (read nntp-server-buffer) (point)))
|
||||
(princ (car article) (current-buffer))
|
||||
(beginning-of-line)
|
||||
(looking-at
|
||||
"[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t")
|
||||
(goto-char (match-end 0))
|
||||
(or (search-forward
|
||||
"\t" (save-excursion (end-of-line) (point)) t)
|
||||
(end-of-line))
|
||||
(while (= (char-after (1- (point))) ? )
|
||||
(forward-char -1)
|
||||
(delete-char 1))
|
||||
(if (eolp)
|
||||
(progn
|
||||
(end-of-line)
|
||||
(or (= (char-after (1- (point))) ?\t)
|
||||
(insert ?\t))
|
||||
(insert "Xref: " system-name " " cgroup ":")
|
||||
(princ (caddr article) (current-buffer))
|
||||
(insert "\t"))
|
||||
(insert "Xref: " system-name " " cgroup ":")
|
||||
(princ (caddr article) (current-buffer))
|
||||
(insert " ")
|
||||
(if (not (string= "" prefix))
|
||||
(while (re-search-forward
|
||||
"[^ ]+:[0-9]+"
|
||||
(save-excursion (end-of-line) (point)) t)
|
||||
(save-excursion
|
||||
(goto-char (match-beginning 0))
|
||||
(insert prefix))))
|
||||
(end-of-line)
|
||||
(or (= (char-after (1- (point))) ?\t)
|
||||
(insert ?\t)))
|
||||
(forward-line 1))
|
||||
(set-buffer vbuf)
|
||||
(goto-char (point-max))
|
||||
(insert-buffer-substring nntp-server-buffer)))))
|
||||
|
||||
;; In case some of the articles have expired or been
|
||||
;; cancelled, we have to mark them as read in the
|
||||
;; component group.
|
||||
(while unfetched
|
||||
(when (cdar unfetched)
|
||||
(gnus-group-make-articles-read
|
||||
(caar unfetched) (sort (cdar unfetched) '<)))
|
||||
(setq unfetched (cdr unfetched)))
|
||||
|
||||
;; The headers are ready for reading, so they are inserted into
|
||||
;; the nntp-server-buffer, which is where Gnus expects to find
|
||||
;; them.
|
||||
(prog1
|
||||
(save-excursion
|
||||
(set-buffer nntp-server-buffer)
|
||||
(erase-buffer)
|
||||
(insert-buffer-substring vbuf)
|
||||
'nov)
|
||||
(kill-buffer vbuf)))))))
|
||||
|
||||
(deffoo nnvirtual-request-article (article &optional group server buffer)
|
||||
(when (and (nnvirtual-possibly-change-server server)
|
||||
(numberp article))
|
||||
(let* ((amap (assq article nnvirtual-mapping))
|
||||
(cgroup (cadr amap)))
|
||||
(cond
|
||||
((not amap)
|
||||
(nnheader-report 'nnvirtual "No such article: %s" article))
|
||||
((not (gnus-check-group cgroup))
|
||||
(nnheader-report
|
||||
'nnvirtual "Can't open server where %s exists" cgroup))
|
||||
((not (gnus-request-group cgroup t))
|
||||
(nnheader-report 'nnvirtual "Can't open component group %s" cgroup))
|
||||
(t
|
||||
(if buffer
|
||||
(save-excursion
|
||||
(set-buffer buffer)
|
||||
(gnus-request-article-this-buffer (caddr amap) cgroup))
|
||||
(gnus-request-article (caddr amap) cgroup)))))))
|
||||
|
||||
(deffoo nnvirtual-open-server (server &optional defs)
|
||||
(unless (assq 'nnvirtual-component-regexp defs)
|
||||
(push `(nnvirtual-component-regexp ,server)
|
||||
defs))
|
||||
(nnoo-change-server 'nnvirtual server defs)
|
||||
(if nnvirtual-component-groups
|
||||
t
|
||||
(setq nnvirtual-mapping nil)
|
||||
;; Go through the newsrc alist and find all component groups.
|
||||
(let ((newsrc (cdr gnus-newsrc-alist))
|
||||
group)
|
||||
(while (setq group (car (pop newsrc)))
|
||||
(when (string-match nnvirtual-component-regexp group) ; Match
|
||||
;; Add this group to the list of component groups.
|
||||
(setq nnvirtual-component-groups
|
||||
(cons group (delete group nnvirtual-component-groups))))))
|
||||
(if (not nnvirtual-component-groups)
|
||||
(nnheader-report 'nnvirtual "No component groups: %s" server)
|
||||
t)))
|
||||
|
||||
(deffoo nnvirtual-request-group (group &optional server dont-check)
|
||||
(nnvirtual-possibly-change-server server)
|
||||
(setq nnvirtual-component-groups
|
||||
(delete (nnvirtual-current-group) nnvirtual-component-groups))
|
||||
(cond
|
||||
((null nnvirtual-component-groups)
|
||||
(setq nnvirtual-current-group nil)
|
||||
(nnheader-report 'nnvirtual "No component groups in %s" group))
|
||||
(t
|
||||
(unless dont-check
|
||||
(nnvirtual-create-mapping))
|
||||
(setq nnvirtual-current-group group)
|
||||
(let ((len (length nnvirtual-mapping)))
|
||||
(nnheader-insert "211 %d 1 %d %s\n" len len group)))))
|
||||
|
||||
(deffoo nnvirtual-request-type (group &optional article)
|
||||
(if (not article)
|
||||
'unknown
|
||||
(let ((mart (assq article nnvirtual-mapping)))
|
||||
(when mart
|
||||
(gnus-request-type (cadr mart) (car mart))))))
|
||||
|
||||
(deffoo nnvirtual-request-update-mark (group article mark)
|
||||
(let* ((nart (assq article nnvirtual-mapping))
|
||||
(cgroup (cadr nart))
|
||||
;; The component group might be a virtual group.
|
||||
(nmark (gnus-request-update-mark cgroup (caddr nart) mark)))
|
||||
(when (and nart
|
||||
(= mark nmark)
|
||||
(gnus-group-auto-expirable-p cgroup))
|
||||
(setq mark gnus-expirable-mark)))
|
||||
mark)
|
||||
|
||||
(defun nnvirtual-close-group (group &optional server)
|
||||
(if (not nnvirtual-current-group)
|
||||
()
|
||||
(nnvirtual-possibly-change-newsgroups group server t)
|
||||
(nnvirtual-update-marked)
|
||||
(setq nnvirtual-current-group nil
|
||||
nnvirtual-current-groups nil
|
||||
nnvirtual-current-mapping nil)
|
||||
(setq nnvirtual-group-alist
|
||||
(delq (assoc group nnvirtual-group-alist) nnvirtual-group-alist))))
|
||||
(deffoo nnvirtual-close-group (group &optional server)
|
||||
(when (nnvirtual-possibly-change-server server)
|
||||
;; Copy (un)read articles.
|
||||
(nnvirtual-update-reads)
|
||||
;; We copy the marks from this group to the component
|
||||
;; groups here.
|
||||
(nnvirtual-update-marked))
|
||||
t)
|
||||
|
||||
(deffoo nnvirtual-request-list (&optional server)
|
||||
(nnheader-report 'nnvirtual "LIST is not implemented."))
|
||||
|
||||
(defun nnvirtual-request-list (&optional server)
|
||||
(setq nnvirtual-status-string "nnvirtual: LIST is not implemented.")
|
||||
nil)
|
||||
(deffoo nnvirtual-request-newgroups (date &optional server)
|
||||
(nnheader-report 'nnvirtual "NEWGROUPS is not supported."))
|
||||
|
||||
(defun nnvirtual-request-newgroups (date &optional server)
|
||||
"List new groups."
|
||||
(setq nnvirtual-status-string "NEWGROUPS is not supported.")
|
||||
nil)
|
||||
(deffoo nnvirtual-request-list-newsgroups (&optional server)
|
||||
(nnheader-report 'nnvirtual "LIST NEWSGROUPS is not implemented."))
|
||||
|
||||
(defun nnvirtual-request-list-newsgroups (&optional server)
|
||||
(setq nnvirtual-status-string
|
||||
"nnvirtual: LIST NEWSGROUPS is not implemented.")
|
||||
nil)
|
||||
|
||||
(defalias 'nnvirtual-request-post 'nntp-request-post)
|
||||
|
||||
(defun nnvirtual-request-post-buffer
|
||||
(post group subject header article-buffer info follow-to respect-poster)
|
||||
(nntp-request-post-buffer post "" subject header article-buffer
|
||||
info follow-to respect-poster))
|
||||
|
||||
|
||||
;;; Internal functions.
|
||||
|
||||
;; Convert HEAD headers into NOV headers.
|
||||
(defun nnvirtual-convert-headers ()
|
||||
(save-excursion
|
||||
(set-buffer nntp-server-buffer)
|
||||
(let* ((gnus-newsgroup-dependencies (make-vector 100 0))
|
||||
(headers (gnus-get-newsgroup-headers))
|
||||
header)
|
||||
(erase-buffer)
|
||||
(while headers
|
||||
(setq header (car headers)
|
||||
headers (cdr headers))
|
||||
(insert (int-to-string (mail-header-number header)) "\t"
|
||||
(or (mail-header-subject header) "") "\t"
|
||||
(or (mail-header-from header) "") "\t"
|
||||
(or (mail-header-date header) "") "\t"
|
||||
(or (mail-header-id header) "") "\t"
|
||||
(or (mail-header-references header) "") "\t"
|
||||
(int-to-string (or (mail-header-chars header) 0)) "\t"
|
||||
(int-to-string (or (mail-header-lines header) 0)) "\t"
|
||||
(if (mail-header-xref header)
|
||||
(concat "Xref: " (mail-header-xref header) "\t")
|
||||
"") "\n")))))
|
||||
|
||||
(defun nnvirtual-possibly-change-newsgroups (group regexp &optional check)
|
||||
(let ((inf t))
|
||||
(or (not group)
|
||||
(and nnvirtual-current-group
|
||||
(string= group nnvirtual-current-group))
|
||||
(and (setq inf (assoc group nnvirtual-group-alist))
|
||||
(string= (nth 3 inf) regexp)
|
||||
(progn
|
||||
(setq nnvirtual-current-group (car inf))
|
||||
(setq nnvirtual-current-groups (nth 1 inf))
|
||||
(setq nnvirtual-current-mapping (nth 2 inf)))))
|
||||
(if (or (not check) (not inf))
|
||||
(progn
|
||||
(and inf (setq nnvirtual-group-alist
|
||||
(delq inf nnvirtual-group-alist)))
|
||||
(setq nnvirtual-current-mapping nil)
|
||||
(setq nnvirtual-current-group group)
|
||||
(let ((newsrc gnus-newsrc-alist)
|
||||
(virt-group (gnus-group-prefixed-name
|
||||
nnvirtual-current-group '(nnvirtual ""))))
|
||||
(setq nnvirtual-current-groups nil)
|
||||
(while newsrc
|
||||
(and (string-match regexp (car (car newsrc)))
|
||||
(not (string= (car (car newsrc)) virt-group))
|
||||
(setq nnvirtual-current-groups
|
||||
(cons (car (car newsrc)) nnvirtual-current-groups)))
|
||||
(setq newsrc (cdr newsrc))))
|
||||
(if nnvirtual-current-groups
|
||||
(progn
|
||||
(nnvirtual-create-mapping group)
|
||||
(setq nnvirtual-group-alist
|
||||
(cons (list group nnvirtual-current-groups
|
||||
nnvirtual-current-mapping regexp)
|
||||
nnvirtual-group-alist)))
|
||||
(setq nnvirtual-status-string
|
||||
(format
|
||||
"nnvirtual: No newsgroups for this virtual newsgroup"))))))
|
||||
nnvirtual-current-groups)
|
||||
|
||||
(defun nnvirtual-create-mapping (group)
|
||||
(let* ((group (gnus-group-prefixed-name group (list 'nnvirtual "")))
|
||||
(info (nth 2 (gnus-gethash group gnus-newsrc-hashtb)))
|
||||
(groups nnvirtual-current-groups)
|
||||
(offset 0)
|
||||
reads unread igroup itotal ireads)
|
||||
;; The virtual group doesn't exist. (?)
|
||||
(or info (error "No such group: %s" group))
|
||||
(setq nnvirtual-current-mapping nil)
|
||||
(while groups
|
||||
;; Added by Sudish Joseph <joseph@cis.ohio-state.edu>.
|
||||
(setq igroup (car groups))
|
||||
(let ((info (nth 2 (gnus-gethash igroup gnus-newsrc-hashtb)))
|
||||
(active (gnus-gethash igroup gnus-active-hashtb)))
|
||||
;; See if the group has had its active list read this session
|
||||
;; if not, we do it now.
|
||||
(if (null active)
|
||||
(if (gnus-activate-group igroup)
|
||||
(progn
|
||||
(gnus-get-unread-articles-in-group
|
||||
info (gnus-gethash igroup gnus-active-hashtb))
|
||||
(setq active (gnus-gethash igroup gnus-active-hashtb)))
|
||||
(message "Couldn't open component group %s" igroup)))
|
||||
(if (null active)
|
||||
()
|
||||
;; And then we do the mapping for this component group. If
|
||||
;; you feel tempted to cast your eyes to the soup below -
|
||||
;; don't. It'll hurt your soul. Suffice to say that it
|
||||
;; assigns ranges of nnvirtual article numbers to the
|
||||
;; different component groups. To get the article number
|
||||
;; from the nnvirtual number, one does something like
|
||||
;; (+ (- number offset) (car active)), where `offset' is the
|
||||
;; slice the mess below assigns, and active is the lowest
|
||||
;; active article in the component group.
|
||||
(setq itotal (1+ (- (cdr active) (car active))))
|
||||
(if (setq ireads (nth 2 info))
|
||||
(let ((itreads
|
||||
(if (not (listp (cdr ireads)))
|
||||
(setq ireads (list (cons (car ireads) (cdr ireads))))
|
||||
(setq ireads (copy-alist ireads)))))
|
||||
(if (< (or (and (numberp (car ireads)) (car ireads))
|
||||
(cdr (car ireads))) (car active))
|
||||
(setq ireads (setq itreads (cdr ireads))))
|
||||
(if (and ireads (< (or (and (numberp (car ireads))
|
||||
(car ireads))
|
||||
(car (car ireads))) (car active)))
|
||||
(setcar (or (and (numberp (car ireads)) ireads)
|
||||
(car ireads)) (1+ (car active))))
|
||||
(while itreads
|
||||
(setcar (or (and (numberp (car itreads)) itreads)
|
||||
(car itreads))
|
||||
(+ (max
|
||||
1 (- (if (numberp (car itreads))
|
||||
(car itreads)
|
||||
(car (car itreads)))
|
||||
(car active)))
|
||||
offset))
|
||||
(if (not (numberp (car itreads)))
|
||||
(setcdr (car itreads)
|
||||
(+ (- (cdr (car itreads)) (car active)) offset)))
|
||||
(setq itreads (cdr itreads)))
|
||||
(setq reads (nconc reads ireads))))
|
||||
(setq offset (+ offset (1- itotal)))
|
||||
(setq nnvirtual-current-mapping
|
||||
(cons (list offset igroup (car active))
|
||||
nnvirtual-current-mapping)))
|
||||
(setq groups (cdr groups))))
|
||||
(setq nnvirtual-current-mapping
|
||||
(nreverse nnvirtual-current-mapping))
|
||||
;; Set Gnus active info.
|
||||
(gnus-sethash group (cons 1 (1- offset)) gnus-active-hashtb)
|
||||
;; Set Gnus read info.
|
||||
(setcar (nthcdr 2 info) reads)
|
||||
|
||||
;; Then we deal with the marks.
|
||||
(let ((map nnvirtual-current-mapping)
|
||||
(marks '(tick dormant reply expire score))
|
||||
(offset 0)
|
||||
tick dormant reply expire score marked active)
|
||||
(deffoo nnvirtual-request-update-info (group info &optional server)
|
||||
(when (nnvirtual-possibly-change-server server)
|
||||
(let ((map nnvirtual-mapping)
|
||||
(marks (mapcar (lambda (m) (list (cdr m))) gnus-article-mark-lists))
|
||||
reads mr m op)
|
||||
;; Go through the mapping.
|
||||
(while map
|
||||
(setq igroup (nth 1 (car map)))
|
||||
(setq active (nth 2 (car map)))
|
||||
(setq marked (nth 3 (nth 2 (gnus-gethash igroup gnus-newsrc-hashtb))))
|
||||
(let ((m marks))
|
||||
(while m
|
||||
(and (assq (car m) marked)
|
||||
(set (car m)
|
||||
(nconc (mapcar
|
||||
(lambda (art)
|
||||
(if (numberp art)
|
||||
(if (< art active)
|
||||
0 (+ (- art active) offset))
|
||||
(cons (+ (- (car art) active) offset)
|
||||
(cdr art))))
|
||||
(cdr (assq (car m) marked)))
|
||||
(symbol-value (car m)))))
|
||||
(setq m (cdr m))))
|
||||
(setq offset (car (car map)))
|
||||
(setq map (cdr map)))
|
||||
;; Put the list of marked articles in the info of the virtual group.
|
||||
(let ((m marks)
|
||||
marked)
|
||||
(while m
|
||||
(and (symbol-value (car m))
|
||||
(setq marked (cons (cons (car m) (symbol-value (car m)))
|
||||
marked)))
|
||||
(setq m (cdr m)))
|
||||
(if (nthcdr 3 info)
|
||||
(setcar (nthcdr 3 info) marked)
|
||||
(setcdr (nthcdr 2 info) (list marked)))))))
|
||||
|
||||
(defun nnvirtual-update-marked ()
|
||||
(let ((mark-lists '((gnus-newsgroup-marked . tick)
|
||||
(gnus-newsgroup-dormant . dormant)
|
||||
(gnus-newsgroup-expirable . expire)
|
||||
(gnus-newsgroup-replied . reply)))
|
||||
marks art-group group-alist g)
|
||||
(while mark-lists
|
||||
(setq marks (symbol-value (car (car mark-lists))))
|
||||
;; Find out what groups the mark belong to.
|
||||
(while marks
|
||||
(setq art-group (nnvirtual-art-group (car marks)))
|
||||
(if (setq g (assoc (car art-group) group-alist))
|
||||
(nconc g (list (cdr art-group)))
|
||||
(setq group-alist (cons (list (car art-group) (cdr art-group))
|
||||
group-alist)))
|
||||
(unless (nth 3 (setq m (pop map)))
|
||||
;; Read article.
|
||||
(push (car m) reads))
|
||||
;; Copy marks.
|
||||
(when (setq mr (nth 4 m))
|
||||
(while mr
|
||||
(setcdr (setq op (assq (pop mr) marks)) (cons (car m) (cdr op))))))
|
||||
;; Compress the marks and the reads.
|
||||
(setq mr marks)
|
||||
(while mr
|
||||
(setcdr (car mr) (gnus-compress-sequence (sort (cdr (pop mr)) '<))))
|
||||
(setcar (cddr info) (gnus-compress-sequence (nreverse reads)))
|
||||
;; Remove empty marks lists.
|
||||
(while (and marks (not (cdar marks)))
|
||||
(setq marks (cdr marks)))
|
||||
;; The groups that don't have marks must have no marks. (Yup.)
|
||||
(let ((groups nnvirtual-current-groups))
|
||||
(while groups
|
||||
(or (assoc (car groups) group-alist)
|
||||
(setq group-alist (cons (list (car groups)) group-alist)))
|
||||
(setq groups (cdr groups))))
|
||||
;; The we update the list of marks.
|
||||
(while group-alist
|
||||
(gnus-add-marked-articles
|
||||
(car (car group-alist)) (cdr (car mark-lists))
|
||||
(cdr (car group-alist)) nil t)
|
||||
(gnus-group-update-group (car (car group-alist)) t)
|
||||
(setq group-alist (cdr group-alist)))
|
||||
(setq mark-lists (cdr mark-lists)))))
|
||||
(setq mr marks)
|
||||
(while (cdr mr)
|
||||
(if (cdadr mr)
|
||||
(setq mr (cdr mr))
|
||||
(setcdr mr (cddr mr))))
|
||||
|
||||
(defun nnvirtual-art-group (article)
|
||||
(let ((map nnvirtual-current-mapping)
|
||||
(offset 0))
|
||||
(while (< (car (car map)) (if (numberp article) article (car article)))
|
||||
(setq offset (car (car map))
|
||||
map (cdr map)))
|
||||
(cons (nth 1 (car map))
|
||||
(if (numberp article)
|
||||
(- (+ article (nth 2 (car map))) offset)
|
||||
(cons (- (+ (car article) (nth 2 (car map))) offset)
|
||||
(cdr article))))))
|
||||
;; Enter these new marks into the info of the group.
|
||||
(if (nthcdr 3 info)
|
||||
(setcar (nthcdr 3 info) marks)
|
||||
;; Add the marks lists to the end of the info.
|
||||
(when marks
|
||||
(setcdr (nthcdr 2 info) (list marks))))
|
||||
t)))
|
||||
|
||||
(defun nnvirtual-catchup-group (group &optional server all)
|
||||
(nnvirtual-possibly-change-newsgroups group server)
|
||||
(let ((gnus-group-marked nnvirtual-current-groups)
|
||||
(deffoo nnvirtual-catchup-group (group &optional server all)
|
||||
(nnvirtual-possibly-change-server server)
|
||||
(let ((gnus-group-marked (copy-sequence nnvirtual-component-groups))
|
||||
(gnus-expert-user t))
|
||||
;; Make sure all groups are activated.
|
||||
(mapcar
|
||||
(lambda (g)
|
||||
(when (not (numberp (car (gnus-gethash g gnus-newsrc-hashtb))))
|
||||
(gnus-activate-group g)))
|
||||
nnvirtual-component-groups)
|
||||
(save-excursion
|
||||
(set-buffer gnus-group-buffer)
|
||||
(gnus-group-catchup-current nil all))))
|
||||
|
||||
(deffoo nnvirtual-find-group-art (group article)
|
||||
"Return the real group and article for virtual GROUP and ARTICLE."
|
||||
(let ((mart (assq article nnvirtual-mapping)))
|
||||
(when mart
|
||||
(cons (cadr mart) (caddr mart)))))
|
||||
|
||||
|
||||
;;; Internal functions.
|
||||
|
||||
(defun nnvirtual-convert-headers ()
|
||||
"Convert HEAD headers into NOV headers."
|
||||
(save-excursion
|
||||
(set-buffer nntp-server-buffer)
|
||||
(let* ((dependencies (make-vector 100 0))
|
||||
(headers (gnus-get-newsgroup-headers dependencies))
|
||||
header)
|
||||
(erase-buffer)
|
||||
(while (setq header (pop headers))
|
||||
(nnheader-insert-nov header)))))
|
||||
|
||||
(defun nnvirtual-possibly-change-server (server)
|
||||
(or (not server)
|
||||
(nnoo-current-server-p 'nnvirtual server)
|
||||
(nnvirtual-open-server server)))
|
||||
|
||||
(defun nnvirtual-update-marked ()
|
||||
"Copy marks from the virtual group to the component groups."
|
||||
(let ((mark-lists gnus-article-mark-lists)
|
||||
(marks (gnus-info-marks (gnus-get-info (nnvirtual-current-group))))
|
||||
type list mart cgroups)
|
||||
(while (setq type (cdr (pop mark-lists)))
|
||||
(setq list (gnus-uncompress-range (cdr (assq type marks))))
|
||||
(setq cgroups
|
||||
(mapcar (lambda (g) (list g)) nnvirtual-component-groups))
|
||||
(while list
|
||||
(nconc (assoc (cadr (setq mart (assq (pop list) nnvirtual-mapping)))
|
||||
cgroups)
|
||||
(list (caddr mart))))
|
||||
(while cgroups
|
||||
(gnus-add-marked-articles
|
||||
(caar cgroups) type (cdar cgroups) nil t)
|
||||
(gnus-group-update-group (car (pop cgroups)) t)))))
|
||||
|
||||
(defun nnvirtual-update-reads ()
|
||||
"Copy (un)reads from the current group to the component groups."
|
||||
(let ((groups (mapcar (lambda (g) (list g)) nnvirtual-component-groups))
|
||||
(articles (gnus-list-of-unread-articles
|
||||
(nnvirtual-current-group)))
|
||||
m)
|
||||
(while articles
|
||||
(setq m (assq (pop articles) nnvirtual-mapping))
|
||||
(nconc (assoc (nth 1 m) groups) (list (nth 2 m))))
|
||||
(while groups
|
||||
(gnus-update-read-articles (caar groups) (cdr (pop groups))))))
|
||||
|
||||
(defun nnvirtual-current-group ()
|
||||
"Return the prefixed name of the current nnvirtual group."
|
||||
(concat "nnvirtual:" nnvirtual-current-group))
|
||||
|
||||
(defsubst nnvirtual-marks (article marks)
|
||||
"Return a list of mark types for ARTICLE."
|
||||
(let (out)
|
||||
(while marks
|
||||
(when (memq article (cdar marks))
|
||||
(push (caar marks) out))
|
||||
(setq marks (cdr marks)))
|
||||
out))
|
||||
|
||||
(defun nnvirtual-create-mapping ()
|
||||
"Create an article mapping for the current group."
|
||||
(let* ((div nil)
|
||||
m marks list article unreads marks active
|
||||
(map (sort
|
||||
(apply
|
||||
'nconc
|
||||
(mapcar
|
||||
(lambda (g)
|
||||
(when (and (setq active (gnus-activate-group g))
|
||||
(> (cdr active) (car active)))
|
||||
(setq unreads (gnus-list-of-unread-articles g)
|
||||
marks (gnus-uncompress-marks
|
||||
(gnus-info-marks (gnus-get-info g))))
|
||||
(when gnus-use-cache
|
||||
(push (cons 'cache (gnus-cache-articles-in-group g))
|
||||
marks))
|
||||
(setq div (/ (float (car active))
|
||||
(if (zerop (cdr active))
|
||||
1 (cdr active))))
|
||||
(mapcar (lambda (n)
|
||||
(list (* div (- n (car active)))
|
||||
g n (and (memq n unreads) t)
|
||||
(inline (nnvirtual-marks n marks))))
|
||||
(gnus-uncompress-range active))))
|
||||
nnvirtual-component-groups))
|
||||
(lambda (m1 m2)
|
||||
(< (car m1) (car m2)))))
|
||||
(i 0))
|
||||
(setq nnvirtual-mapping map)
|
||||
;; Set the virtual article numbers.
|
||||
(while (setq m (pop map))
|
||||
(setcar m (setq article (incf i))))))
|
||||
|
||||
(provide 'nnvirtual)
|
||||
|
||||
;;; nnvirtual.el ends here
|
||||
|
Loading…
Reference in New Issue
Block a user