This commit is contained in:
Dave Love 1999-10-04 17:15:48 +00:00
parent d56a50549d
commit 6edcb099fc
50 changed files with 23251 additions and 46842 deletions

View file

@ -1,3 +1,29 @@
1999-10-04 Dave Love <fx@gnu.org>
* cus-start.el: Add x-stretch-cursor, indicate-empty-lines,
scroll-up-aggressively, scroll-down-aggressively.
* widget.el (define-widget-keywords): Make dummy definition and
comment-out its use.
* time.el (display-time-mode): Add autoload cookie.
* term.el: Avoid ange-ftp related compilation warnings.
* sun-curs.el: Require sun-fns.
* msb.el: (msb--choose-file-menu): Use `completion-ignore-case' in
name comparisons.
* rect.el: Add/fix various doc strings. Add `*' to all the
interactive specs.
(delete-extract-rectangle): Doc fix from verna.
* tooltip.el (tooltip-mode): Customize this, per convention.
(tooltip-active): Option deleted.
* help-macro.el (three-step-help): Customize.
1999-10-03 Dave Love <fx@gnu.org>
* image.el (defimage): Remove redundant code. Substitute file in

23143
lisp/ChangeLog.7 Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,403 +0,0 @@
;;; bdf.el --- BDF font file handler for ps-print.
;; Copyright (C) 1998 Electrotechnical Laboratory, JAPAN.
;; Licensed to the Free Software Foundation.
;; Keywords: BDF, font, PostScript
;; 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:
;; Functions for getting bitmap information from X's BDF font file are
;; provided.
;;; Code:
(eval-when-compile (require 'ps-print))
(defvar bdf-directory-list
nil
"*List of directories to search for `BDF' font files.")
(defun bdf-expand-file-name (bdfname)
"Return an abosolute path name of a `BDF' font file BDFNAME.
It searches directories listed in the variable `bdf-directory-list'
for BDFNAME."
(if (file-name-absolute-p bdfname)
(if (file-readable-p bdfname)
bdfname)
(let ((l bdf-directory-list))
(catch 'tag
(while l
(if (file-readable-p (expand-file-name bdfname (car l)))
(throw 'tag (expand-file-name bdfname (car l))))
(setq l (cdr l)))))))
(defsubst bdf-file-mod-time (filename)
"Return modification time of FILENAME.
The value is a list of two integers, the first integer has high-order
16 bits, the second has low 16 bits."
(nth 5 (file-attributes filename)))
(defun bdf-file-newer-than-time (filename mod-time)
"Return non-nil if and only if FILENAME is newer than MOD-TIME.
MOD-TIME is a modification time as a list of two integers, the first
integer has high-order 16 bits, the second has low 16 bits."
(let ((new-mod-time (bdf-file-mod-time (bdf-expand-file-name filename))))
(or (> (car new-mod-time) (car mod-time))
(and (= (car new-mod-time) (car mod-time))
(> (nth 1 new-mod-time) (nth 1 mod-time))))))
(defun bdf-find-file (bdfname)
"Return a buffer visiting a bdf file BDFNAME.
If BDFNAME is not an absolute path, directories listed in
`bdf-directory-list' is searched.
If BDFNAME doesn't exist, return nil."
(let ((buf (generate-new-buffer " *bdf-work*"))
(coding-system-for-read 'no-conversion))
(save-excursion
(set-buffer buf)
(insert-file-contents (bdf-expand-file-name bdfname))
buf)))
(defvar bdf-cache-file "~/.bdfcache.el"
"Name of cache file which contains information of `BDF' font files.")
(defvar bdf-cache nil
"Cached information of `BDF' font files. It is a list of FONT-INFO.
FONT-INFO is a list of the following format:
(BDFFILE ABSOLUTE-PATH MOD-TIME SIZE FONT-BOUNDING-BOX
RELATIVE-COMPOSE BASELINE-OFFSET CODE-RANGE MAXLEN OFFSET-VECTOR)
See the documentation of the function `bdf-read-font-info' for more detail.")
(defun bdf-read-cache ()
"Return a cached information about `BDF' font files from a cache file.
The variable `bdf-cache-file' holds the cache file name.
If the cache file is not readable, this return nil."
(setq bdf-cache nil)
(condition-case nil
(if (file-readable-p bdf-cache-file)
(load-file bdf-cache-file))
(error nil))
(if (not (listp bdf-cache))
(setq bdf-cache nil)))
(defun bdf-write-cache ()
"Write out cached information of `BDF' font file to a file.
The variable `bdf-cache-file' holds the cache file name.
The file is written if and only if the file alreay exists and writable."
(if (and bdf-cache
(file-exists-p bdf-cache-file)
(file-writable-p bdf-cache-file))
(write-region (format "(setq bdf-cache '%S)\n" bdf-cache)
nil bdf-cache-file)))
(defun bdf-set-cache (font-info)
"Cache FONT-INFO as information about one `BDF' font file.
FONT-INFO is a list of the following format:
(BDFFILE ABSOLUTE-PATH MOD-TIME SIZE FONT-BOUNDING-BOX
RELATIVE-COMPOSE BASELINE-OFFSET CODE-RANGE MAXLEN OFFSET-VECTOR)
See the documentation of the function `bdf-read-font-info' for more detail."
(let ((slot (assoc (car font-info) bdf-cache)))
(if slot
(setcdr slot (cdr font-info))
(setq bdf-cache (cons font-info bdf-cache)))))
(defun bdf-initialize ()
"Initialize `bdf' library."
(if (bdf-read-cache)
(add-hook 'kill-emacs-hook 'bdf-write-cache)))
(defun bdf-compact-code (code code-range)
(if (or (< code (aref code-range 4))
(> code (aref code-range 5)))
(setq code (aref code-range 6)))
(+ (* (- (lsh code -8) (aref code-range 0))
(1+ (- (aref code-range 3) (aref code-range 2))))
(- (logand code 255) (aref code-range 2))))
(defun bdf-expand-code (code code-range)
(let ((code0-range (1+ (- (aref code-range 3) (aref code-range 2)))))
(+ (* (+ (/ code code0-range) (aref code-range 0)) 256)
(+ (% code code0-range) (aref code-range 2)))))
(defun bdf-read-font-info (bdfname)
"Read `BDF' font file BDFNAME and return information (FONT-INFO) of the file.
FONT-INFO is a list of the following format:
(BDFFILE ABSOLUTE-PATH MOD-TIME FONT-BOUNDING-BOX
RELATIVE-COMPOSE BASELINE-OFFSET CODE-RANGE MAXLEN OFFSET-VECTOR)
BDFFILE is a name of a font file (excluding directory part).
ABSOLUTE-PATH is an absolute path of the font file.
MOD-TIME is last modification time as a list of two integers, the
first integer has high-order 16 bits, the second has low 16 bits.
SIZE is a size of the font. This value is got from SIZE record of the
font.
FONT-BOUNDING-BOX is the font bounding box as a list of four integers,
BBX-WIDTH, BBX-HEIGHT, BBX-XOFF, and BBX-YOFF.
RELATIVE-COMPOSE is an integer value of the font's property
`_MULE_RELATIVE_COMPOSE'. If the font doesn't have this property, the
value is 0.
BASELINE-OFFSET is an integer value of the font's property
`_MULE_BASELINE_OFFSET'. If the font doesn't have this property, the
value is 0.
CODE-RANGE is a vector of minimum 1st byte, maximum 1st byte, minimum
2nd byte, maximum 2nd byte, minimum code, maximum code, and default
code. For 1-byte fonts, the first two elements are 0.
MAXLEN is a maximum bytes of one glyph informaion in the font file.
OFFSET-VECTOR is a vector of a file position which starts bitmap data
of the glyph in the font file.
Nth element of OFFSET-VECTOR is a file position for the glyph of code
CODE, where N and CODE are in the following relation:
(bdf-compact-code CODE) => N, (bdf-expand-code N) => CODE"
(let ((absolute-path (bdf-expand-file-name bdfname))
(maxlen 0)
size
font-bounding-box
(relative-compose 'false)
(baseline-offset 0)
default-char
code-range
offset-vector
buf)
(if absolute-path
(message "Reading %s..." bdfname)
(error "BDF file %s doesn't exist" bdfname))
(setq buf (bdf-find-file absolute-path))
(unwind-protect
(save-excursion
(set-buffer buf)
(goto-char (point-min))
(search-forward "\nFONTBOUNDINGBOX")
(setq font-bounding-box (vector (read (current-buffer))
(read (current-buffer))
(read (current-buffer))
(read (current-buffer))))
;; The following kludgy code is to avoid bugs of fonts
;; jiskan16.bdf and jiskan24.bdf distributed with X.
;; They contain wrong FONTBOUNDINGBOX.
(if (and (> (aref font-bounding-box 3) 0)
(string-match "jiskan\\(16\\|24\\)" bdfname))
(aset font-bounding-box 3
(- (aref font-bounding-box 3))))
(goto-char (point-min))
(search-forward "\nSIZE ")
(setq size (read (current-buffer)))
;; The following kludgy code is t avoid bugs of several
;; fonts which have wrong SIZE record.
(if (<= size (/ (aref font-bounding-box 1) 2))
(setq size (aref font-bounding-box 1)))
(goto-char (point-min))
(if (search-forward "\nDEFAULT_CHAR" nil t)
(setq default-char (read (current-buffer))))
(search-forward "\nSTARTCHAR")
(forward-line -1)
(let ((limit (point)))
(goto-char (point-min))
(if (search-forward "\n_MULE_RELATIVE_COMPOSE" limit t)
(progn
(goto-char (match-end 0))
(setq relative-compose (read (current-buffer)))))
(goto-char (point-min))
(if (search-forward "\n_MULE_BASELINE_OFFSET" limit t)
(progn
(goto-char (match-end 0))
(setq baseline-offset (read (current-buffer))))))
(let ((min-code0 256) (min-code1 256) (max-code0 0) (max-code1 0)
(min-code 65536)
(max-code 0)
(glyph-list nil)
code bbx offset)
(while (search-forward "\nSTARTCHAR" nil t)
(setq offset (line-beginning-position))
(search-forward "\nENCODING")
(setq code (read (current-buffer)))
(let ((code0 (lsh code -8))
(code1 (logand code 255)))
(if (< code0 min-code0) (setq min-code0 code0)
(if (> code0 max-code0) (setq max-code0 code0)))
(if (< code1 min-code1) (setq min-code1 code1)
(if (> code1 max-code1) (setq max-code1 code1))))
(if (< code min-code)
(setq min-code code)
(if (> code max-code)
(setq max-code code)))
(search-forward "ENDCHAR")
(if (< maxlen (- (point) offset))
(setq maxlen (- (point) offset)))
(setq glyph-list (cons (cons code offset) glyph-list)))
(setq code-range
(vector min-code0 max-code0 min-code1 max-code1
min-code max-code (or default-char min-code)))
(setq offset-vector
(make-vector (1+ (bdf-compact-code max-code code-range))
nil))
(while glyph-list
(let ((glyph (car glyph-list)))
(aset offset-vector
(bdf-compact-code (car glyph) code-range)
(cdr glyph)))
(setq glyph-list (cdr glyph-list)))))
(kill-buffer buf))
(message "Reading %s...done" bdfname)
(list bdfname absolute-path (bdf-file-mod-time absolute-path)
size font-bounding-box relative-compose baseline-offset
code-range maxlen offset-vector)))
(defsubst bdf-info-absolute-path (font-info) (nth 1 font-info))
(defsubst bdf-info-mod-time (font-info) (nth 2 font-info))
(defsubst bdf-info-size (font-info) (nth 3 font-info))
(defsubst bdf-info-font-bounding-box (font-info) (nth 4 font-info))
(defsubst bdf-info-relative-compose (font-info) (nth 5 font-info))
(defsubst bdf-info-baseline-offset (font-info) (nth 6 font-info))
(defsubst bdf-info-code-range (font-info) (nth 7 font-info))
(defsubst bdf-info-maxlen (font-info) (nth 8 font-info))
(defsubst bdf-info-offset-vector (font-info) (nth 9 font-info))
(defun bdf-get-font-info (bdfname)
"Return information about `BDF' font file BDFNAME.
The value FONT-INFO is a list of the following format:
(BDFFILE ABSOLUTE-PATH MOD-TIME SIZE FONT-BOUNDING-BOX
RELATIVE-COMPOSE BASELINE-OFFSET CODE-RANGE MAXLEN OFFSET-VECTOR)
See the documentation of the function `bdf-read-font-info' for more detail."
(or bdf-cache
(bdf-read-cache))
(let ((font-info (assoc bdfname bdf-cache)))
(if (or (not font-info)
(not (file-readable-p (bdf-info-absolute-path font-info)))
(bdf-file-newer-than-time bdfname (bdf-info-mod-time font-info)))
(progn
(setq font-info (bdf-read-font-info bdfname))
(bdf-set-cache font-info)))
font-info))
(defun bdf-read-bitmap (bdfname offset maxlen)
"Read `BDF' font file BDFNAME to get bitmap data at file poistion OFFSET.
BDFNAME is an abosolute path name of the font file.
MAXLEN specifies how many bytes we should read at least.
The value is a list of DWIDTH, BBX, and BITMAP-STRING.
DWIDTH is a pixel width of a glyph.
BBX is a bounding box of the glyph.
BITMAP-STRING is a string representing bits by hexadecimal digits."
(let ((coding-system-for-read 'no-conversion)
dwidth bbx height yoff bitmap-string)
(condition-case nil
(with-temp-buffer
(insert-file-contents bdfname nil offset (+ offset maxlen))
(goto-char (point-min))
(search-forward "\nDWIDTH")
(setq dwidth (read (current-buffer)))
(goto-char (point-min))
(search-forward "\nBBX")
(setq bbx (vector (read (current-buffer)) (read (current-buffer))
(read (current-buffer)) (read (current-buffer))))
(setq height (aref bbx 1) yoff (aref bbx 3))
(search-forward "\nBITMAP")
(forward-line 1)
(delete-region (point-min) (point))
(if (looking-at "\\(0+\n\\)+")
(progn
(setq height
(- height (count-lines (point) (match-end 0))))
(delete-region (point) (match-end 0))))
(or (looking-at "ENDCHAR")
(progn
(search-forward "ENDCHAR" nil 'move)
(forward-line -1)
(while (looking-at "0+$")
(setq yoff (1+ yoff) height (1- height))
(forward-line -1))
(forward-line 1)))
(aset bbx 1 height)
(aset bbx 3 yoff)
(delete-region (point) (point-max))
(goto-char (point-min))
(while (not (eobp))
(end-of-line)
(delete-char 1))
(setq bitmap-string (buffer-string)))
(error nil))
(list dwidth bbx bitmap-string)))
(defun bdf-get-bitmaps (bdfname codes)
"Return bitmap information of glyphs of CODES in `BDF' font file BDFNAME.
CODES is a list of encoding number of glyphs in the file.
The value is a list of CODE, DWIDTH, BBX, and BITMAP-STRING.
DWIDTH is a pixel width of a glyph.
BBX is a bounding box of the glyph.
BITMAP-STRING is a string representing bits by hexadecimal digits."
(let* ((font-info (bdf-get-font-info bdfname))
(absolute-path (bdf-info-absolute-path font-info))
(font-bounding-box (bdf-info-font-bounding-box font-info))
(maxlen (bdf-info-maxlen font-info))
(code-range (bdf-info-code-range font-info))
(offset-vector (bdf-info-offset-vector font-info)))
(mapcar (function
(lambda (x)
(cons x (bdf-read-bitmap
absolute-path
(aref offset-vector (bdf-compact-code x code-range))
maxlen))))
codes)))
;;; Interface to ps-print.el
;; Called from ps-mule-init-external-library.
(defun bdf-generate-prologue ()
(or bdf-cache
(bdf-initialize))
(ps-mule-generate-bitmap-prologue))
;; Called from ps-mule-generate-font.
(defun bdf-generate-font (charset font-spec)
(let* ((font-name (ps-mule-font-spec-name font-spec))
(font-info (bdf-get-font-info font-name)))
(ps-mule-generate-bitmap-font font-name
(ps-mule-font-spec-bytes font-spec)
(charset-width charset)
(bdf-info-size font-info)
(bdf-info-relative-compose font-info)
(bdf-info-baseline-offset font-info)
(bdf-info-font-bounding-box font-info))))
;; Called from ps-mule-generate-glyphs.
(defun bdf-generate-glyphs (font-spec code-list bytes)
(let ((font-name (ps-mule-font-spec-name font-spec)))
(mapcar (function
(lambda (x)
(apply 'ps-mule-generate-bitmap-glyph font-name x)))
(bdf-get-bitmaps font-name code-list))))
(provide 'bdf)
;;; bdf.el ends here

View file

@ -1,296 +0,0 @@
;;; docref.el --- Simple cross references for Elisp documentation strings
;; Copyright (C) 1994 Free Software Foundation, Inc.
;; Author: Vadim Geshel <vadik@unas.cs.kiev.ua>
;; Created: 12 Jul 1994
;; Keywords: docs, help, lisp
;; original name was cross-ref.el.
;; 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:
;; This package allows you to use a simple form of cross references in
;; your Emacs Lisp documentation strings. Cross-references look like
;; \\(type@[label@]data), where type defines a method for retrieving
;; reference information, data is used by a method routine as an argument,
;; and label "represents" the reference in text. If label is absent, data
;; is used instead.
;;
;; Special reference labeled `back', when present, can be used to return
;; to the previous contents of help buffer.
;;
;; Cross-referencing currently is intended for use in doc strings only
;; and works only in temporary buffers (created by `with-output-to-temp-buffer').
;; List of temp buffers in which cross-referencing is to be active is specified
;; by variable DOCREF-BUFFERS-LIST, which contains only "*Help*" by default.
;;
;; Documentation strings for this package's functions and variables can serve
;; as examples of usage.
;;
;;; Customization:
;;
;; See source. The main customization variable is `docref-methods-alist'.
;; It consists of (type . function) pairs, where type is a string which
;; corresponds to type in cross-references and function is called with
;; one argument - reference `data' - when a reference is activated.
;;
;;; Installation:
;;
;; Place this file somewhere in your load-path, byte-compiled it, and add
;; (require 'cross-ref)
;; to your .emacs.
;;; Code:
;; User customizable variables
(defgroup docref nil
"Simple cross references for Elisp documentation strings."
:prefix "docref-"
:group 'help
:group 'lisp
:group 'docs)
(defcustom docref-highlight-p t
"*If non-nil, \\(f@docref-subst) highlights cross-references.
Under window system it highlights them with face defined by
\\(v@docref-highlight-face), on character terminal highlighted references
look like cross-references in info mode."
:type 'boolean
:group 'docref)
(defcustom docref-highlight-face 'highlight
"*Face used to highlight cross-references (used by \\(f@docref-subst))"
:type 'face
:group 'docref)
(defcustom docref-methods-alist
'(("f" . docref-describe-function) ; reference to a function documentation
("v" . docref-describe-variable) ; reference to a variable documentation
("F" . docref-read-file) ; reference to a file contents
("s" . docref-use-string) ; reference to a string
("V" . docref-use-variable-value) ; reference to variable value
("0" . beep)) ; just highlighted text
"Alist which maps cross-reference ``types'' to retrieval functions.
The car of each element is a string that serves as `type' in cross-references.
\(See \\(f@docref-subst)). The cdr is a function of one argument,
to be called to find this reference."
:type '(repeat (cons string function))
:group 'docref)
(defcustom docref-back-label "\nback"
"Label to use by \\(f@docref-subst) for the go-back reference."
:type 'string
:group 'docref)
(defvar docref-back-reference nil
"If non-nil, this is a go-back reference to add to the current buffer.
The value specifies how to go back. It should be suitable for use
as the second argument to \\(f@docref-insert-label).
\\(f@docref-subst) uses this to set up the go-back reference.")
(defvar docref-last-active-buffer)
;;;###autoload
(defun docref-setup ()
"Process docref cross-references in the current buffer.
See also \\(f@docref-subst)."
(interactive)
(docref-subst (current-buffer))
(docref-mode))
(defvar docref-mode-map nil)
(or docref-mode-map
(let ((map (make-sparse-keymap)))
(define-key map [mouse-2] 'docref-follow-mouse)
(define-key map "\C-c\C-b" 'docref-go-back)
(define-key map "\C-c\C-c" 'docref-follow)
(setq docref-mode-map map)))
(defun docref-mode ()
"Major mode for help buffers that contain cross references.
To follow a reference, move to it and type \\[docref-follow], or use
\\[docref-follow-mouse]. The command \\[docref-go-back] can used to go
back to where you came from."
(interactive)
(kill-all-local-variables)
(setq major-mode 'docref-mode)
(setq mode-name "Docref")
(use-local-map docref-mode-map)
(run-hooks 'docref-mode))
(defun docref-subst (buf)
"Parse documentation cross-references in buffer BUF.
Find cross-reference information in a buffer and
highlight them with face defined by \\(v@docref-highlight-face).
Cross-reference has the following format: \\ (TYPE[@LABEL]@DATA), where
TYPE defines method used to retrieve xref data (like reading from file or
calling \\(f@describe-function)), DATA is an argument to this method
\(like file name or function name), and LABEL is displayed in text using
\\(v@docref-highlight-face).
The special reference `back' can be used to return back.
The variable \\(v@docref-back-label) specifies the label to use for that.
See \\(v@docref-methods-alist) for currently defined methods."
(interactive "b")
(save-excursion
(set-buffer buf)
(goto-char (point-min))
;; The docref-seen property indicates that we have processed this
;; buffer's contents already, so don't do it again.
(if (not (get-text-property (point-min) 'docref-seen))
(let ((old-modified (buffer-modified-p)))
(while (re-search-forward "[\\](\\([^\)\@]+\\)\\(@[^\)\@]+\\)?@\\([^\)]*\\))"
nil t)
(let* ((start (match-beginning 0))
(type (buffer-substring (match-beginning 1) (match-end 1)))
(data (buffer-substring (match-beginning 3) (match-end 3)))
(label
(if (match-beginning 2)
(buffer-substring (+ (match-beginning 2) 1) (match-end 2))
data)))
(replace-match "" t)
(docref-insert-label label (cons type data))))
;; Make a back-reference in this buffer, if desired.
;; (This is true if called from docref-follow.)
(if docref-back-reference
(progn
(goto-char (point-max))
(put-text-property (point-min) (1+ (point-min))
'docref-back-position (point))
(docref-insert-label docref-back-label docref-back-reference)))
(put-text-property (point-min) (1+ (point-min)) 'docref-seen t)
(set-buffer-modified-p old-modified)))))
(defun docref-insert-label (string ref)
(let ((label (concat string))
(pos (point)))
;; decorate the label
(let ((leading-space-end (save-match-data
(if (string-match "^\\([ \t\n]+\\)" label)
(match-end 1)
0)))
(trailing-space-start (save-match-data
(if (string-match "\\([ \t\n]+\\)$" label)
(match-beginning 1)
(length label)))))
(if docref-highlight-p
(if (not window-system)
(setq label
(concat (substring label 0 leading-space-end)
"(*note "
(substring label leading-space-end trailing-space-start)
")"
(substring label trailing-space-start)))
;; window-system
(put-text-property leading-space-end
trailing-space-start
'face docref-highlight-face label)))
(put-text-property 0 (length label) 'docref ref label)
(insert label))))
(defun docref-follow-mouse (click)
"Follow the cross-reference that you click on."
(interactive "e")
(save-excursion
(let* ((start (event-start click))
(window (car start))
(pos (car (cdr start)))
(docref-last-active-buffer (current-buffer)))
(set-buffer (window-buffer window))
(docref-follow pos))))
(defun docref-go-back ()
"Go back to the previous contents of help buffer."
(interactive)
(let ((pos (get-text-property (point-min) 'docref-back-position)))
(if pos
(docref-follow pos)
(error "No go-back reference"))))
(defun docref-follow (&optional pos)
"Follow cross-reference at point.
For the cross-reference format, see \\(f@docref-subst).
The special reference named `back' can be used to return back"
(interactive)
(or pos (setq pos (point)))
(let ((docref-data (get-text-property pos 'docref)))
(if docref-data
;; There is a reference at point. Follow it.
(let* ((type (car docref-data))
(name (cdr docref-data))
(method (assoc type docref-methods-alist))
(cur-contents (buffer-string))
(opoint (point))
(docref-back-reference (cons "s" cur-contents))
success)
(if (null method)
(error "Unknown cross-reference type: %s" type))
(unwind-protect
(save-excursion
(funcall (cdr method) name)
(setq success t))
(or success
(progn
;; (cdr method) got an error.
;; Put back the text that we had.
(erase-buffer)
(insert cur-contents)
(goto-char opoint)))
(set-buffer-modified-p nil))))))
;; Builtin methods for accessing a reference.
(defun docref-describe-function (data)
(save-excursion
(if (boundp 'docref-last-active-buffer)
(set-buffer docref-last-active-buffer))
(describe-function (intern data))))
(defun docref-describe-variable (data)
(save-excursion
(if (boundp 'docref-last-active-buffer)
(set-buffer docref-last-active-buffer))
(describe-variable (intern data))))
(defun docref-read-file (data)
(with-output-to-temp-buffer (buffer-name)
(erase-buffer)
(insert-file-contents (expand-file-name data))))
(defun docref-use-string (data)
(with-output-to-temp-buffer (buffer-name)
(erase-buffer)
(insert data)))
(defun docref-use-variable-value (data)
(let ((sym (intern data)))
(with-output-to-temp-buffer (buffer-name)
(erase-buffer)
(princ (symbol-value sym)))))
(provide 'docref)
;;; docref.el ends here

View file

@ -1,170 +0,0 @@
;;; dos-win32.el --- Functions shared among MS-DOS and Win32 (NT/95) platforms
;; Copyright (C) 1996 Free Software Foundation, Inc.
;; Maintainer: Geoff Voelker (voelker@cs.washington.edu)
;; Keywords: internal
;; 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:
;; Parts of this code are duplicated functions taken from dos-fns.el
;; and winnt.el.
;;; Code:
;;; Add %t: into the mode line format just after the open-paren.
(let ((tail (member " %[(" mode-line-format)))
(setcdr tail (cons (purecopy "%t:")
(cdr tail))))
;; Use ";" instead of ":" as a path separator (from files.el).
(setq path-separator ";")
;; Set the null device (for compile.el).
(setq grep-null-device "NUL")
;; Set the grep regexp to match entries with drive letters.
(setq grep-regexp-alist
'(("^\\(\\([a-zA-Z]:\\)?[^:( \t\n]+\\)[:( \t]+\\([0-9]+\\)[:) \t]" 1 3)))
;; For distinguishing file types based upon suffixes.
(defvar file-name-buffer-file-type-alist
'(
("[:/].*config.sys$" . nil) ; config.sys text
("\\.elc$" . t) ; emacs stuff
("\\.\\(obj\\|exe\\|com\\|lib\\|sys\\|chk\\|out\\|bin\\|ico\\|pif\\)$" . t)
; MS-Dos stuff
("\\.\\(arc\\|zip\\|pak\\|lzh\\|zoo\\)$" . t)
; Packers
("\\.\\(a\\|o\\|tar\\|z\\|gz\\|taz\\)$" . t)
; Unix stuff
("\\.tp[ulpw]$" . t)
; Borland Pascal stuff
("[:/]tags$" . t)
; Emacs TAGS file
)
"*Alist for distinguishing text files from binary files.
Each element has the form (REGEXP . TYPE), where REGEXP is matched
against the file name, and TYPE is nil for text, t for binary.")
(defun find-buffer-file-type (filename)
;; First check if file is on an untranslated filesystem, then on the alist.
(if (untranslated-file-p filename)
t ; for binary
(let ((alist file-name-buffer-file-type-alist)
(found nil)
(code nil))
(let ((case-fold-search t))
(setq filename (file-name-sans-versions filename))
(while (and (not found) alist)
(if (string-match (car (car alist)) filename)
(setq code (cdr (car alist))
found t))
(setq alist (cdr alist))))
(if found
(cond ((memq code '(nil t)) code)
((and (symbolp code) (fboundp code))
(funcall code filename)))
default-buffer-file-type))))
(defun find-file-binary (filename)
"Visit file FILENAME and treat it as binary."
(interactive "FFind file binary: ")
(let ((file-name-buffer-file-type-alist '(("" . t))))
(find-file filename)))
(defun find-file-text (filename)
"Visit file FILENAME and treat it as a text file."
(interactive "FFind file text: ")
(let ((file-name-buffer-file-type-alist '(("" . nil))))
(find-file filename)))
(defun find-file-not-found-set-buffer-file-type ()
(save-excursion
(set-buffer (current-buffer))
(setq buffer-file-type (find-buffer-file-type (buffer-file-name))))
nil)
;;; To set the default file type on new files.
(add-hook 'find-file-not-found-hooks 'find-file-not-found-set-buffer-file-type)
;;; To accomodate filesystems that do not require CR/LF translation.
(defvar untranslated-filesystem-list nil
"List of filesystems that require no CR/LF translation during file I/O.
Each element in the list is a string naming the directory prefix
corresponding to the filesystem.")
(defun untranslated-canonical-name (filename)
"Return FILENAME in a canonicalized form.
This is for use with the functions dealing with untranslated filesystems."
(if (memq system-type '(ms-dos windows-nt))
;; The canonical form for DOS/NT/Win95 is with A-Z downcased and all
;; directory separators changed to directory-sep-char.
(let ((name nil))
(setq name (mapconcat
'(lambda (char)
(if (and (<= ?A char) (<= char ?Z))
(char-to-string (+ (- char ?A) ?a))
(char-to-string char)))
filename nil))
;; Use expand-file-name to canonicalize directory separators, except
;; with bare drive letters (which would have the cwd appended).
(if (string-match "^.:$" name)
name
(expand-file-name name)))
filename))
(defun untranslated-file-p (filename)
"Test whether CR/LF translation should be disabled for FILENAME.
Return t if FILENAME is on a filesystem that does not require
CR/LF translation, and nil otherwise."
(let ((fs (untranslated-canonical-name filename))
(ufs-list untranslated-filesystem-list)
(found nil))
(while (and (not found) ufs-list)
(if (string-match (concat "^" (regexp-quote (car ufs-list))) fs)
(setq found t)
(setq ufs-list (cdr ufs-list))))
found))
(defun add-untranslated-filesystem (filesystem)
"Record that FILESYSTEM does not require CR/LF translation.
FILESYSTEM is a string containing the directory prefix corresponding to
the filesystem. For example, for a Unix filesystem mounted on drive Z:,
FILESYSTEM could be \"Z:\"."
(let ((fs (untranslated-canonical-name filesystem)))
(if (member fs untranslated-filesystem-list)
untranslated-filesystem-list
(setq untranslated-filesystem-list
(cons fs untranslated-filesystem-list)))))
(defun remove-untranslated-filesystem (filesystem)
"Record that FILESYSTEM requires CR/LF translation.
FILESYSTEM is a string containing the directory prefix corresponding to
the filesystem. For example, for a Unix filesystem mounted on drive Z:,
FILESYSTEM could be \"Z:\"."
(setq untranslated-filesystem-list
(delete (untranslated-canonical-name filesystem)
untranslated-filesystem-list)))
(provide 'dos-win32)
;;; dos-win32.el ends here

View file

@ -1,623 +0,0 @@
;;; gnus-cache.el --- cache interface for Gnus
;; Copyright (C) 1995,96 Free Software Foundation, Inc.
;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
;; Keywords: news
;; This file is part of GNU Emacs.
;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Commentary:
;;; Code:
(require 'gnus)
(eval-when-compile (require 'cl))
(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"))
;;; Functions called from Gnus.
(defun gnus-cache-open ()
"Initialize the cache."
(when (or (file-exists-p gnus-cache-directory)
(and gnus-use-cache
(not (eq gnus-use-cache 'passive))))
(gnus-cache-read-active)))
(condition-case ()
(gnus-add-shutdown 'gnus-cache-close 'gnus)
;; Complexities of byte-compiling makes this kludge necessary. Eeek.
(error nil))
(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
;; delete empty cache subdirectories
(if (null gnus-cache-buffer)
()
(let ((buffer (cdr gnus-cache-buffer))
(overview-file (gnus-cache-file-name
(car gnus-cache-buffer) ".overview")))
;; write the overview only if it was modified
(if (buffer-modified-p buffer)
(save-excursion
(set-buffer buffer)
(if (> (buffer-size) 0)
;; non-empty overview, write it out
(progn
(gnus-make-directory (file-name-directory overview-file))
(write-region (point-min) (point-max)
overview-file nil 'quietly))
;; empty overview file, remove it
(and (file-exists-p overview-file)
(delete-file overview-file))
;; if possible, remove group's cache subdirectory
(condition-case nil
;; FIXME: we can detect the error type and warn the user
;; of any inconsistencies (articles w/o nov entries?).
;; for now, just be conservative...delete only if safe -- sj
(delete-directory (file-name-directory overview-file))
(error nil)))))
;; kill the buffer, it's either unmodified or saved
(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."
(when gnus-cache-active-hashtb
(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) (not ticked) (not dormant) (memq 'read class))))
(defun gnus-cache-file-name (group article)
(concat (file-name-as-directory gnus-cache-directory)
(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-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-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 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)))))
(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)
(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*"))
beg end)
(gnus-cache-save-buffers)
(save-excursion
(set-buffer cache-buf)
(buffer-disable-undo (current-buffer))
(erase-buffer)
(insert-file-contents (gnus-cache-file-name group ".overview"))
(goto-char (point-min))
(insert "\n")
(goto-char (point-min)))
(set-buffer nntp-server-buffer)
(goto-char (point-min))
(while cached
(while (and (not (eobp))
(< (read (current-buffer)) (car cached)))
(forward-line 1))
(beginning-of-line)
(save-excursion
(set-buffer cache-buf)
(if (search-forward (concat "\n" (int-to-string (car cached)) "\t")
nil t)
(setq beg (progn (beginning-of-line) (point))
end (progn (end-of-line) (point)))
(setq beg nil)))
(if beg (progn (insert-buffer-substring cache-buf beg end)
(insert "\n")))
(setq cached (cdr cached)))
(kill-buffer cache-buf)))
(defun gnus-cache-braid-heads (group cached)
(let ((cache-buf (get-buffer-create " *gnus-cache*")))
(save-excursion
(set-buffer cache-buf)
(buffer-disable-undo (current-buffer))
(erase-buffer))
(set-buffer nntp-server-buffer)
(goto-char (point-min))
(while cached
(while (and (not (eobp))
(looking-at "2.. +\\([0-9]+\\) ")
(< (progn (goto-char (match-beginning 1))
(read (current-buffer)))
(car cached)))
(search-forward "\n.\n" nil 'move))
(beginning-of-line)
(save-excursion
(set-buffer cache-buf)
(erase-buffer)
(insert-file-contents (gnus-cache-file-name group (car cached)))
(goto-char (point-min))
(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)
(insert "."))
(insert-buffer-substring cache-buf)
(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 ((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))
;; 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)
;;; gnus-cache.el ends here

View file

@ -1,732 +0,0 @@
;;; gnus-cite.el --- parse citations in articles for Gnus
;; Copyright (C) 1995,96 Free Software Foundation, Inc.
;; Author: Per Abrahamsen <abraham@iesd.auc.dk>
;; Keywords: news, mail
;; This file is part of GNU Emacs.
;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Commentary:
;;; Code:
(require 'gnus)
(require 'gnus-msg)
(require 'gnus-ems)
(eval-when-compile (require 'cl))
(eval-and-compile
(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.")
(defvar gnus-cite-prefix-regexp
"^[]>|:}+ ]*[]>|:}+]\\(.*>\\)?\\|^.*>"
"Regexp matching the longest possible citation prefix on a line.")
(defvar gnus-cite-max-prefix 20
"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 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.")
(defvar gnus-cite-minimum-match-count 2
"Minimum number of identical prefixes before we believe it's a citation.")
;see gnus-cus.el
;(defvar gnus-cite-face-list
; (if (eq gnus-display-type 'color)
; (if (eq gnus-background-mode 'dark) 'light 'dark)
; '(italic))
; "Faces used for displaying different citations.
;It is either a list of face names, or one of the following special
;values:
;dark: Create faces from `gnus-face-dark-name-list'.
;light: Create faces from `gnus-face-light-name-list'.
;The variable `gnus-make-foreground' determines whether the created
;faces change the foreground or the background colors.")
(defvar gnus-cite-attribution-prefix "in article\\|in <"
"Regexp matching the beginning of an attribution line.")
(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.")
;see gnus-cus.el
;(defvar gnus-cite-attribution-face 'underline
; "Face used for attribution lines.
;It is merged with the face for the cited text belonging to the attribution.")
;see gnus-cus.el
;(defvar gnus-cite-hide-percentage 50
; "Only hide cited text if it is larger than this percent of the body.")
;see gnus-cus.el
;(defvar gnus-cite-hide-absolute 10
; "Only hide cited text if there is at least this number of cited lines.")
;see gnus-cus.el
;(defvar gnus-face-light-name-list
; '("light blue" "light cyan" "light yellow" "light pink"
; "pale green" "beige" "orange" "magenta" "violet" "medium purple"
; "turquoise")
; "Names of light colors.")
;see gnus-cus.el
;(defvar gnus-face-dark-name-list
; '("dark salmon" "firebrick"
; "dark green" "dark orange" "dark khaki" "dark violet"
; "dark turquoise")
; "Names of dark colors.")
;;; Internal Variables:
(defvar gnus-cite-article nil)
(defvar gnus-cite-prefix-alist nil)
;; Alist of citation prefixes.
;; The cdr is a list of lines with that prefix.
(defvar gnus-cite-attribution-alist nil)
;; Alist of attribution lines.
;; The car is a line number.
;; The cdr is the prefix for the citation started by that line.
(defvar gnus-cite-loose-prefix-alist nil)
;; Alist of citation prefixes that have no matching attribution.
;; The cdr is a list of lines with that prefix.
(defvar gnus-cite-loose-attribution-alist nil)
;; Alist of attribution lines that have no matching citation.
;; Each member has the form (WROTE IN PREFIX TAG), where
;; 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.
(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:
(defun gnus-article-highlight-citation (&optional force)
"Highlight cited text.
Each citation in the article will be highlighted with a different face.
The faces are taken from `gnus-cite-face-list'.
Attribution lines are highlighted with the same face as the
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-suffix' and perhaps
`gnus-cite-attribution-prefix' are considered attribution lines."
(interactive (list 'force))
;; Create dark or light faces if necessary.
(cond ((eq gnus-cite-face-list 'light)
(setq gnus-cite-face-list
(mapcar 'gnus-make-face gnus-face-light-name-list)))
((eq gnus-cite-face-list 'dark)
(setq gnus-cite-face-list
(mapcar 'gnus-make-face gnus-face-dark-name-list))))
(save-excursion
(set-buffer gnus-article-buffer)
(gnus-cite-parse-maybe force)
(let ((buffer-read-only nil)
(alist gnus-cite-prefix-alist)
(faces gnus-cite-face-list)
(inhibit-point-motion-hooks t)
face entry prefix skip numbers number face-alist)
;; Loop through citation prefixes.
(while alist
(setq entry (car alist)
alist (cdr alist)
prefix (car entry)
numbers (cdr entry)
face (car faces)
faces (or (cdr faces) gnus-cite-face-list)
face-alist (cons (cons prefix face) face-alist))
(while numbers
(setq number (car numbers)
numbers (cdr numbers))
(and (not (assq number gnus-cite-attribution-alist))
(not (assq number gnus-cite-loose-attribution-alist))
(gnus-cite-add-face number prefix face))))
;; Loop through attribution lines.
(setq alist gnus-cite-attribution-alist)
(while alist
(setq entry (car alist)
alist (cdr alist)
number (car entry)
prefix (cdr entry)
skip (gnus-cite-find-prefix number)
face (cdr (assoc prefix face-alist)))
;; Add attribution button.
(goto-line number)
(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)
'gnus-cite-toggle prefix))
;; Highlight attribution line.
(gnus-cite-add-face number skip face)
(gnus-cite-add-face number skip gnus-cite-attribution-face))
;; Loop through attribution lines.
(setq alist gnus-cite-loose-attribution-alist)
(while alist
(setq entry (car alist)
alist (cdr alist)
number (car entry)
skip (gnus-cite-find-prefix number))
(gnus-cite-add-face number skip gnus-cite-attribution-face)))))
(defun gnus-dissect-cited-text ()
"Dissect the article buffer looking for cited text."
(save-excursion
(set-buffer gnus-article-buffer)
(gnus-cite-parse-maybe)
(let ((alist gnus-cite-prefix-alist)
prefix numbers number marks m)
;; Loop through citation prefixes.
(while alist
(setq numbers (pop alist)
prefix (pop numbers))
(while numbers
(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-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 (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)
(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 (equal gnus-cite-article gnus-article-current)
()
;;Reset parser information.
(setq gnus-cite-prefix-alist nil
gnus-cite-attribution-alist nil
gnus-cite-loose-prefix-alist nil
gnus-cite-loose-attribution-alist nil)
;; Parse if not too large.
(if (and (not force)
gnus-cite-parse-max-size
(> (buffer-size) gnus-cite-parse-max-size))
()
(setq gnus-cite-article (cons (car gnus-article-current)
(cdr gnus-article-current)))
(gnus-cite-parse))))
(defun gnus-cite-parse ()
;; Parse and connect citation prefixes and attribution lines.
;; Parse current buffer searching for citation prefixes.
(goto-char (point-min))
(or (search-forward "\n\n" nil t)
(goto-char (point-max)))
(let ((line (1+ (count-lines (point-min) (point))))
(case-fold-search t)
(max (save-excursion
(goto-char (point-max))
(re-search-backward gnus-signature-separator nil t)
(point)))
alist entry start begin end numbers prefix)
;; Get all potential prefixes in `alist'.
(while (< (point) max)
;; Each line.
(setq begin (point)
end (progn (beginning-of-line 2) (point))
start end)
(goto-char begin)
;; Ignore standard Supercite attribution prefix.
(if (looking-at gnus-supercite-regexp)
(if (match-end 1)
(setq end (1+ (match-end 1)))
(setq end (1+ begin))))
;; Ignore very long prefixes.
(if (> end (+ (point) gnus-cite-max-prefix))
(setq end (+ (point) gnus-cite-max-prefix)))
(while (re-search-forward gnus-cite-prefix-regexp (1- end) t)
;; Each prefix.
(setq end (match-end 0)
prefix (buffer-substring begin end))
(gnus-set-text-properties 0 (length prefix) nil prefix)
(setq entry (assoc prefix alist))
(if entry
(setcdr entry (cons line (cdr entry)))
(setq alist (cons (list prefix line) alist)))
(goto-char begin))
(goto-char start)
(setq line (1+ line)))
;; We got all the potential prefixes. Now create
;; `gnus-cite-prefix-alist' containing the oldest prefix for each
;; line that appears at least gnus-cite-minimum-match-count
;; times. First sort them by length. Longer is older.
(setq alist (sort alist (lambda (a b)
(> (length (car a)) (length (car b))))))
(while alist
(setq entry (car alist)
prefix (car entry)
numbers (cdr entry)
alist (cdr alist))
(cond ((null numbers)
;; No lines with this prefix that wasn't also part of
;; a longer prefix.
)
((< (length numbers) gnus-cite-minimum-match-count)
;; Too few lines with this prefix. We keep it a bit
;; longer in case it is an exact match for an attribution
;; line, but we don't remove the line from other
;; prefixes.
(setq gnus-cite-prefix-alist
(cons entry gnus-cite-prefix-alist)))
(t
(setq gnus-cite-prefix-alist (cons entry
gnus-cite-prefix-alist))
;; Remove articles from other prefixes.
(let ((loop alist)
current)
(while loop
(setq current (car loop)
loop (cdr loop))
(setcdr current
(gnus-set-difference (cdr current) numbers))))))))
;; No citations have been connected to attribution lines yet.
(setq gnus-cite-loose-prefix-alist (append gnus-cite-prefix-alist nil))
;; Parse current buffer searching for attribution lines.
(goto-char (point-min))
(search-forward "\n\n" nil 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))
(prefix (gnus-cite-find-prefix wrote))
;; Check previous line for an attribution leader.
(tag (progn
(beginning-of-line 1)
(and (looking-at gnus-supercite-secondary-regexp)
(buffer-substring (match-beginning 1)
(match-end 1)))))
(in (progn
(goto-char start)
(and (re-search-backward gnus-cite-attribution-prefix
(save-excursion
(beginning-of-line 0)
(point))
t)
(not (re-search-forward gnus-cite-attribution-suffix
start t))
(count-lines (point-min) (1+ (point)))))))
(if (eq wrote in)
(setq in nil))
(goto-char end)
(setq gnus-cite-loose-attribution-alist
(cons (list wrote in prefix tag)
gnus-cite-loose-attribution-alist))))
;; Find exact supercite citations.
(gnus-cite-match-attributions 'small nil
(lambda (prefix tag)
(if tag
(concat "\\`"
(regexp-quote prefix) "[ \t]*"
(regexp-quote tag) ">"))))
;; Find loose supercite citations after attributions.
(gnus-cite-match-attributions 'small t
(lambda (prefix tag)
(if tag (concat "\\<"
(regexp-quote tag)
"\\>"))))
;; Find loose supercite citations anywhere.
(gnus-cite-match-attributions 'small nil
(lambda (prefix tag)
(if tag (concat "\\<"
(regexp-quote tag)
"\\>"))))
;; Find nested citations after attributions.
(gnus-cite-match-attributions 'small-if-unique t
(lambda (prefix tag)
(concat "\\`" (regexp-quote prefix) ".+")))
;; Find nested citations anywhere.
(gnus-cite-match-attributions 'small nil
(lambda (prefix tag)
(concat "\\`" (regexp-quote prefix) ".+")))
;; Remove loose prefixes with too few lines.
(let ((alist gnus-cite-loose-prefix-alist)
entry)
(while alist
(setq entry (car alist)
alist (cdr alist))
(if (< (length (cdr entry)) gnus-cite-minimum-match-count)
(setq gnus-cite-prefix-alist
(delq entry gnus-cite-prefix-alist)
gnus-cite-loose-prefix-alist
(delq entry gnus-cite-loose-prefix-alist)))))
;; Find flat attributions.
(gnus-cite-match-attributions 'first t nil)
;; Find any attributions (are we getting desperate yet?).
(gnus-cite-match-attributions 'first nil nil))
(defun gnus-cite-match-attributions (sort after fun)
;; Match all loose attributions and citations (SORT AFTER FUN) .
;;
;; If SORT is `small', the citation with the shortest prefix will be
;; used, if it is `first' the first prefix will be used, if it is
;; `small-if-unique' the shortest prefix will be used if the
;; attribution line does not share its own prefix with other
;; loose attribution lines, otherwise the first prefix will be used.
;;
;; If AFTER is non-nil, only citations after the attribution line
;; will be considered.
;;
;; If FUN is non-nil, it will be called with the arguments (WROTE
;; PREFIX TAG) and expected to return a regular expression. Only
;; citations whose prefix matches the regular expression will be
;; considered.
;;
;; WROTE is the attribution line number.
;; PREFIX is the attribution line prefix.
;; 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)
(while atts
(setq att (car atts)
atts (cdr atts)
wrote (nth 0 att)
in (nth 1 att)
prefix (nth 2 att)
tag (nth 3 att)
regexp (if fun (funcall fun prefix tag) "")
size (cond ((eq sort 'small) t)
((eq sort 'first) nil)
(t (< (length (gnus-cite-find-loose prefix)) 2)))
limit (if after wrote -1)
smallest 1000000
best nil)
(let ((cites gnus-cite-loose-prefix-alist)
cite candidate numbers first compare)
(while cites
(setq cite (car cites)
cites (cdr cites)
candidate (car cite)
numbers (cdr cite)
first (apply 'min numbers)
compare (if size (length candidate) first))
(and (> first limit)
regexp
(string-match regexp candidate)
(< compare smallest)
(setq best cite
smallest compare))))
(if (null best)
()
(setq gnus-cite-loose-attribution-alist
(delq att gnus-cite-loose-attribution-alist))
(setq gnus-cite-attribution-alist
(cons (cons wrote (car best)) gnus-cite-attribution-alist))
(if in
(setq gnus-cite-attribution-alist
(cons (cons in (car best)) gnus-cite-attribution-alist)))
(if (memq best gnus-cite-loose-prefix-alist)
(let ((loop gnus-cite-prefix-alist)
(numbers (cdr best))
current)
(setq gnus-cite-loose-prefix-alist
(delq best gnus-cite-loose-prefix-alist))
(while loop
(setq current (car loop)
loop (cdr loop))
(if (eq current best)
()
(setcdr current (gnus-set-difference (cdr current) numbers))
(if (null (cdr current))
(setq gnus-cite-loose-prefix-alist
(delq current gnus-cite-loose-prefix-alist)
atts (delq current atts)))))))))))
(defun gnus-cite-find-loose (prefix)
;; Return a list of loose attribution lines prefixed by PREFIX.
(let* ((atts gnus-cite-loose-attribution-alist)
att line lines)
(while atts
(setq att (car atts)
line (car att)
atts (cdr atts))
(if (string-equal (gnus-cite-find-prefix line) prefix)
(setq lines (cons line lines))))
lines))
(defun gnus-cite-add-face (number prefix face)
;; At line NUMBER, ignore PREFIX and add FACE to the rest of the line.
(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))
(when (< from to)
(gnus-overlay-put (gnus-make-overlay from to) 'face face))))))
(defun gnus-cite-toggle (prefix)
(save-excursion
(set-buffer gnus-article-buffer)
(let ((buffer-read-only nil)
(numbers (cdr (assoc prefix gnus-cite-prefix-alist)))
(inhibit-point-motion-hooks t)
number)
(while numbers
(setq number (car numbers)
numbers (cdr numbers))
(goto-line number)
(cond ((get-text-property (point) 'invisible)
(remove-text-properties (point) (progn (forward-line 1) (point))
gnus-hidden-properties))
((assq number gnus-cite-attribution-alist))
(t
(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.
(let ((alist gnus-cite-prefix-alist)
(prefix "")
entry)
(while alist
(setq entry (car alist)
alist (cdr alist))
(if (memq line (cdr entry))
(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)
;;; gnus-cite.el ends here

View file

@ -1,683 +0,0 @@
;;; gnus-cus.el --- User friendly customization of Gnus
;; Copyright (C) 1995,96 Free Software Foundation, Inc.
;;
;; Author: Per Abrahamsen <abraham@iesd.auc.dk>
;; Keywords: help, news
;; Version: 0.1
;; This file is part of GNU Emacs.
;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Commentary:
;;; Code:
(require 'custom)
(require 'gnus-ems)
(require 'browse-url)
(eval-when-compile (require 'cl))
;; 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 nil nil nil))
(defvar gnus-face-light-name-list
'("light blue" "light cyan" "light yellow" "light pink"
"pale green" "beige" "orange" "magenta" "violet" "medium purple"
"turquoise"))
(defvar gnus-face-dark-name-list
(list
;; Not all servers have dark blue in rgb.txt.
(if (and (eq window-system 'x) (x-color-defined-p "dark blue"))
"dark blue"
"royal blue")
"firebrick" "dark green" "OrangeRed"
"dark khaki" "dark violet" "SteelBlue4"))
; CornflowerBlue SeaGreen OrangeRed SteelBlue4 DeepPink3
; DarkOlviveGreen4
(custom-declare '()
'((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.
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
less space and be faster as a result.")
(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 . 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 . "\
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 . (condition-case ()
(if (gnus-visual-p 'mouse-face 'highlight)
(if (boundp 'gnus-mouse-face)
gnus-mouse-face
'highlight)
'default)
(error 'default)))
(type . face))
((tag . "Article Display")
(doc . "Controls how the article buffer will look.
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
single bold letter, while a letter followed by a backspace and an
underscore will be displayed as a single underlined letter. This
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 . "\
Format too long lines.
")
(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 . "\
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 . "\
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 . "\
Convert date header to universal time.
")
(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 . "\
Replace date header with a header showing the articles age.
")
(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 . "\
Highlight headers, signature, and buttons if `Visual' is turned on.
")
(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 . "\
Highlight headers as specified by `Article Header Highlighting'.
")
(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 . "\
Highlight the citations as specified by `Citation Faces'.
")
(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 . "\
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 . "\
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 . "\
Hide the signature.
")
(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 . "\
Hide all cited text.
")
(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 . "\
Name of Lisp function to call.
Push the `Filter' button to select one of the predefined filters.
")
(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 . "\
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 . "\
Face used for highlighting a signature in the article buffer.")
(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
displaying the name and content of the header. The `Header' field
should contain the name of the header. The field actually contains a
regular expression that should match the beginning of the header line,
but if you don't know what a regular expression is, just write the
name of the header. The second field is the `Name' field, which
determines how the the header name (i.e. the part of the header left
of the `:') is displayed. The third field is the `Content' field,
which determines how the content (i.e. the part of the header right of
the `:') is displayed.
If you leave the last `Header' field in the list empty, the `Name' and
`Content' fields will determine how headers not listed above are
displayed.
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 "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 . "\
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 . "\
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 . "\
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 . "\
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 . "\
Controls the highlighting of summary buffer lines.
Below is a list of `Form'/`Face' pairs. When deciding how a a
particular summary 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 summary 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:
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
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 "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 '(> 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 '(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-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 '(> 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
;; Not all servers have dark magenta in rgb.txt.
(if (and (eq window-system 'x)
(x-color-defined-p "dark magenta"))
"dark magenta"
"maroon")
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.
(cond ((eq alist 'light)
(setq alist (mapcar 'gnus-make-face gnus-face-light-name-list)))
((eq alist 'dark)
(setq alist (mapcar 'gnus-make-face gnus-face-dark-name-list))))
(funcall (custom-super custom 'import) custom alist))
(provide 'gnus-cus)
;;; gnus-cus.el ends here

View file

@ -1,222 +0,0 @@
;;; gnus-demon.el --- daemonic Gnus behaviour
;; Copyright (C) 1995,96 Free Software Foundation, Inc.
;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
;; Keywords: news
;; This file is part of GNU Emacs.
;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Commentary:
;;; Code:
(require 'gnus)
(eval-when-compile (require 'cl))
(defvar gnus-demon-handlers nil
"Alist of daemonic handlers to be run at intervals.
Each handler is a list on the form
\(FUNCTION TIME IDLE)
FUNCTION is the function to be called.
TIME is the number of `gnus-demon-timestep's between each call.
If nil, never call. If t, call each `gnus-demon-timestep'.
If IDLE is t, only call if Emacs has been idle for a while. If IDLE
is a number, only call when Emacs has been idle more than this number
of `gnus-demon-timestep's. If IDLE is nil, don't care about
idleness. If IDLE is a number and TIME is nil, then call once each
time Emacs has been idle for IDLE `gnus-demon-timestep's.")
(defvar gnus-demon-timestep 60
"*Number of seconds in each demon timestep.")
;;; Internal variables.
(defvar gnus-demon-timer nil)
(defvar gnus-demon-idle-has-been-called nil)
(defvar gnus-demon-idle-time 0)
(defvar gnus-demon-handler-state nil)
(defvar gnus-demon-is-idle nil)
(defvar gnus-demon-last-keys nil)
(eval-and-compile
(autoload 'timezone-parse-date "timezone")
(autoload 'timezone-make-arpa-date "timezone"))
;;; Functions.
(defun gnus-demon-add-handler (function time idle)
"Add the handler FUNCTION to be run at TIME and IDLE."
;; First remove any old handlers that use this function.
(gnus-demon-remove-handler function)
;; Then add the new one.
(push (list function time idle) gnus-demon-handlers)
(gnus-demon-init))
(defun gnus-demon-remove-handler (function &optional no-init)
"Remove the handler FUNCTION from the list of handlers."
(setq gnus-demon-handlers
(delq (assq function gnus-demon-handlers)
gnus-demon-handlers))
(or no-init (gnus-demon-init)))
(defun gnus-demon-init ()
"Initialize the Gnus daemon."
(interactive)
(gnus-demon-cancel)
(if (null gnus-demon-handlers)
() ; Nothing to do.
;; Set up timer.
(setq gnus-demon-timer
(nnheader-run-at-time
gnus-demon-timestep gnus-demon-timestep 'gnus-demon))
;; Reset control variables.
(setq gnus-demon-handler-state
(mapcar
(lambda (handler)
(list (car handler) (gnus-demon-time-to-step (nth 1 handler))
(nth 2 handler)))
gnus-demon-handlers))
(setq gnus-demon-idle-time 0)
(setq gnus-demon-idle-has-been-called nil)
(setq gnus-use-demon t)))
(gnus-add-shutdown 'gnus-demon-cancel 'gnus)
(defun gnus-demon-cancel ()
"Cancel any Gnus daemons."
(interactive)
(and gnus-demon-timer
(nnheader-cancel-timer gnus-demon-timer))
(setq gnus-demon-timer nil
gnus-use-demon nil))
(defun gnus-demon-is-idle-p ()
"Whether Emacs is idle or not."
;; We do this simply by comparing the 100 most recent keystrokes
;; with the ones we had last time. If they are the same, one might
;; guess that Emacs is indeed idle. This only makes sense if one
;; calls this function seldom -- like once a minute, which is what
;; we do here.
(let ((keys (recent-keys)))
(or (equal keys gnus-demon-last-keys)
(progn
(setq gnus-demon-last-keys keys)
nil))))
(defun gnus-demon-time-to-step (time)
"Find out how many seconds to TIME, which is on the form \"17:43\"."
(if (not (stringp time))
time
(let* ((date (current-time-string))
(dv (timezone-parse-date date))
(tdate (timezone-make-arpa-date
(string-to-number (aref dv 0))
(string-to-number (aref dv 1))
(string-to-number (aref dv 2)) time
(or (aref dv 4) "UT")))
(nseconds (gnus-time-minus
(gnus-encode-date tdate) (gnus-encode-date date))))
(round
(/ (if (< nseconds 0)
(+ nseconds (* 60 60 24))
nseconds) gnus-demon-timestep)))))
(defun gnus-demon ()
"The Gnus daemon that takes care of running all Gnus handlers."
;; Increase or reset the time Emacs has been idle.
(if (gnus-demon-is-idle-p)
(incf gnus-demon-idle-time)
(setq gnus-demon-idle-time 0)
(setq gnus-demon-idle-has-been-called nil))
;; Then we go through all the handler and call those that are
;; sufficiently ripe.
(let ((handlers gnus-demon-handler-state)
handler time idle)
(while handlers
(setq handler (pop handlers))
(cond
((numberp (setq time (nth 1 handler)))
;; These handlers use a regular timeout mechanism. We decrease
;; the timer if it hasn't reached zero yet.
(or (zerop time)
(setcar (nthcdr 1 handler) (decf time)))
(and (zerop time) ; If the timer now is zero...
(or (not (setq idle (nth 2 handler))) ; Don't care about idle.
(and (numberp idle) ; Numerical idle...
(< idle gnus-demon-idle-time)) ; Idle timed out.
gnus-demon-is-idle) ; Or just need to be idle.
;; So we call the handler.
(progn
(funcall (car handler))
;; And reset the timer.
(setcar (nthcdr 1 handler)
(gnus-demon-time-to-step
(nth 1 (assq (car handler) gnus-demon-handlers)))))))
;; These are only supposed to be called when Emacs is idle.
((null (setq idle (nth 2 handler)))
;; We do nothing.
)
((not (numberp idle))
;; We want to call this handler each and every time that
;; Emacs is idle.
(funcall (car handler)))
(t
;; We want to call this handler only if Emacs has been idle
;; for a specified number of timesteps.
(and (not (memq (car handler) gnus-demon-idle-has-been-called))
(< idle gnus-demon-idle-time)
(progn
(funcall (car handler))
;; Make sure the handler won't be called once more in
;; this idle-cycle.
(push (car handler) gnus-demon-idle-has-been-called))))))))
(defun gnus-demon-add-nocem ()
"Add daemonic NoCeM handling to Gnus."
(gnus-demon-add-handler 'gnus-demon-scan-nocem 60 t))
(defun gnus-demon-scan-nocem ()
"Scan NoCeM groups for NoCeM messages."
(gnus-nocem-scan-groups))
(defun gnus-demon-add-disconnection ()
"Add daemonic server disconnection to Gnus."
(gnus-demon-add-handler 'gnus-demon-close-connections nil 30))
(defun gnus-demon-close-connections ()
(gnus-close-backends))
(defun gnus-demon-add-scanmail ()
"Add daemonic scanning of mail from the mail backends."
(gnus-demon-add-handler 'gnus-demon-scan-mail 120 60))
(defun gnus-demon-scan-mail ()
(let ((servers gnus-opened-servers)
server)
(while (setq server (car (pop servers)))
(and (gnus-check-backend-function 'request-scan (car server))
(or (gnus-server-opened server)
(gnus-open-server server))
(gnus-request-scan nil server)))))
(provide 'gnus-demon)
;;; gnus-demon.el ends here

View file

@ -1,630 +0,0 @@
;;; gnus-edit.el --- Gnus SCORE file editing
;; Copyright (C) 1995,96 Free Software Foundation, Inc.
;;
;; Author: Per Abrahamsen <abraham@iesd.auc.dk>
;; Keywords: news, help
;; Version: 0.2
;;; 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")
(doc . "Customization of Gnus SCORE files.
SCORE files allow you to assign a score to each article when you enter
a group, and automatically mark the articles as read or delete them
based on the score. In the summary buffer you can use the score to
sort the articles by score (`C-c C-s C-s') or to jump to the unread
article with the highest score (`,').")
(type . group)
(data "\n"
((header . nil)
(doc . "Name of SCORE file to customize.
Enter the name in the `File' field, then push the [Load] button to
load it. When done editing, push the [Save] button to save the file.
Several score files may apply to each group, and several groups may
use the same score file. This is controlled implicitly by the name of
the score file and the value of the global variable
`gnus-score-find-score-files-function', and explicitly by the the
`Files' and `Exclude Files' entries.")
(compact . t)
(type . group)
(data ((tag . "Load")
(type . button)
(query . gnus-score-custom-load))
((tag . "Save")
(type . button)
(query . gnus-score-custom-save))
((name . file)
(tag . "File")
(directory . gnus-kill-files-directory)
(default-file . "SCORE")
(type . file))))
((name . files)
(tag . "Files")
(doc . "\
List of score files to load when the the current score file is loaded.
You can use this to share score entries between multiple score files.
Push the `[INS]' button add a score file to the list, or `[DEL]' to
delete a score file from the list.")
(type . list)
(data ((type . repeat)
(header . nil)
(data (type . file)
(directory . gnus-kill-files-directory)))))
((name . exclude-files)
(tag . "Exclude Files")
(doc . "\
List of score files to exclude when the the current score file is loaded.
You can use this if you have a score file you want to share between a
number of newsgroups, except for the newsgroup this score file
matches. [ Did anyone get that? ]
Push the `[INS]' button add a score file to the list, or `[DEL]' to
delete a score file from the list.")
(type . list)
(data ((type . repeat)
(header . nil)
(data (type . file)
(directory . gnus-kill-files-directory)))))
((name . mark)
(tag . "Mark")
(doc . "\
Articles below this score will be automatically marked as read.
This means that when you enter the summary buffer, the articles will
be shown but will already be marked as read. You can then press `x'
to get rid of them entirely.
By default articles with a negative score will be marked as read. To
change this, push the `Mark' button, and choose `Integer'. You can
then enter a value in the `Mark' field.")
(type . gnus-score-custom-maybe-type))
((name . expunge)
(tag . "Expunge")
(doc . "\
Articles below this score will not be shown in the summary buffer.")
(type . gnus-score-custom-maybe-type))
((name . mark-and-expunge)
(tag . "Mark and Expunge")
(doc . "\
Articles below this score will be marked as read, but not shown.
Someone should explain me the difference between this and `expunge'
alone or combined with `mark'.")
(type . gnus-score-custom-maybe-type))
((name . eval)
(tag . "Eval")
(doc . "\
Evaluate this lisp expression when the entering summary buffer.")
(type . sexp))
((name . read-only)
(tag . "Read Only")
(doc . "Read-only score files will not be updated or saved.
Except from this buffer, of course!")
(type . toggle))
((type . doc)
(doc . "\
Each news header has an associated list of score entries.
You can use the [INS] buttons to add new score entries anywhere in the
list, or the [DEL] buttons to delete specific score entries.
Each score entry should specify a string that should be matched with
the content actual header in order to determine whether the entry
applies to that header. Enter that string in the `Match' field.
If the score entry matches, the articles score will be adjusted with
some amount. Enter that amount in the in the `Score' field. You
should specify a positive amount for score entries that matches
articles you find interesting, and a negative amount for score entries
matching articles you would rather avoid. The final score for the
article will be the sum of the score of all score entries that match
the article.
The score entry can be either permanent or expirable. To make the
entry permanent, push the `Date' button and choose the `Permanent'
entry. To make the entry expirable, choose instead the `Integer'
entry. After choosing the you can enter the date the score entry was
last matched in the `Date' field. The date will be automatically
updated each time the score entry matches an article. When the date
become too old, the the score entry will be removed.
For your convenience, the date is specified as the number of days
elapsed since the (imaginary) Gregorian date Sunday, December 31, 1
BC.
Finally, you can choose what kind of match you want to perform by
pushing the `Type' button. For most entries you can choose between
`Exact' which mean the header content must be exactly identical to the
match string, or `Substring' meaning the match string should be
somewhere in the header content, or even `Regexp' to use Emacs regular
expression matching. The last choice is `Fuzzy' which is like `Exact'
except that whitespace derivations, a beginning `Re:' or a terminating
parenthetical remark are all ignored. Each of the four types have a
variant which will ignore case in the comparison. That variant is
indicated with a `(fold)' after its name."))
((name . from)
(tag . "From")
(doc . "Scoring based on the authors email address.")
(type . gnus-score-custom-string-type))
((name . subject)
(tag . "Subject")
(doc . "Scoring based on the articles subject.")
(type . gnus-score-custom-string-type))
((name . followup)
(tag . "Followup")
(doc . "Scoring based on who the article is a followup to.
If you want to see all followups to your own articles, add an entry
with a positive score matching your email address here. You can also
put an entry with a negative score matching someone who is so annoying
that you don't even want to see him quoted in followups.")
(type . gnus-score-custom-string-type))
((name . xref)
(tag . "Xref")
(doc . "Scoring based on article crossposting.
If you want to score based on which newsgroups an article is posted
to, this is the header to use. The syntax is a little different from
the `Newsgroups' header, but scoring in `Xref' is much faster. As an
example, to match all crossposted articles match on `:.*:' using the
`Regexp' type.")
(type . gnus-score-custom-string-type))
((name . references)
(tag . "References")
(doc . "Scoring based on article references.
The `References' header gives you an alternative way to score on
followups. If you for example want to see follow all discussions
where people from `iesd.auc.dk' school participate, you can add a
substring match on `iesd.auc.dk>' on this header.")
(type . gnus-score-custom-string-type))
((name . message-id)
(tag . "Message-ID")
(doc . "Scoring based on the articles message-id.
This isn't very useful, but Lars like completeness. You can use it to
match all messaged generated by recent Gnus version with a `Substring'
match on `.fsf@'.")
(type . gnus-score-custom-string-type))
((type . doc)
(doc . "\
WARNING: Scoring on the following three pseudo headers is very slow!
Scoring on any of the real headers use a technique that avoids
scanning the entire article, only the actual headers you score on are
scanned, and this scanning has been heavily optimized. Using just a
single entry for one the three pseudo-headers `Head', `Body', and
`All' will require GNUS to retrieve and scan the entire article, which
can be very slow on large groups. However, if you add one entry for
any of these headers, you can just as well add several. Each
subsequent entry cost relatively little extra time."))
((name . head)
(tag . "Head")
(doc . "Scoring based on the article header.
Instead of matching the content of a single header, the entire header
section of the article is matched. You can use this to match on
arbitrary headers, foe example to single out TIN lusers, use a substring
match on `Newsreader: TIN'. That should get 'em!")
(type . gnus-score-custom-string-type))
((name . body)
(tag . "Body")
(doc . "Scoring based on the article body.
If you think any article that mentions `Kibo' is inherently
interesting, do a substring match on His name. You Are Allowed.")
(type . gnus-score-custom-string-type))
((name . all)
(tag . "All")
(doc . "Scoring based on the whole article.")
(type . gnus-score-custom-string-type))
((name . date)
(tag . "Date")
(doc . "Scoring based on article date.
You can change the score of articles that have been posted before,
after, or at a specific date. You should add the date in the `Match'
field, and then select `before', `after', or `at' by pushing the
`Type' button. Imagine you want to lower the score of very old
articles, or want to raise the score of articles from the future (such
things happen!). Then you can't use date scoring for that. In fact,
I can't imagine anything you would want to use this for.
For your convenience, the date is specified in Usenet date format.")
(type . gnus-score-custom-date-type))
((type . doc)
(doc . "\
The Lines and Chars headers use integer based scoring.
This means that you should write an integer in the `Match' field, and
the push the `Type' field to if the `Chars' or `Lines' header should
be larger, equal, or smaller than the number you wrote in the match
field."))
((name . chars)
(tag . "Characters")
(doc . "Scoring based on the number of characters in the article.")
(type . gnus-score-custom-integer-type))
((name . lines)
(tag . "Lines")
(doc . "Scoring based on the number of lines in the article.")
(type . gnus-score-custom-integer-type))
((name . orphan)
(tag . "Orphan")
(doc . "Score to add to articles with no parents.")
(type . gnus-score-custom-maybe-type))
((name . adapt)
(tag . "Adapt")
(doc . "Adapting the score files to your newsreading habits.
When you have finished reading a group GNUS can automatically create
new score entries based on which articles you read and which you
skipped. This is normally controlled by the two global variables
`gnus-use-adaptive-scoring' and `gnus-default-adaptive-score-alist',
The first determines whether adaptive scoring should be enabled or
not, while the second determines what score entries should be created.
You can overwrite the setting of `gnus-use-adaptive-scoring' by
selecting `Enable' or `Disable' by pressing the `Adapt' button.
Selecting `Custom' will allow you to specify the exact adaptation
rules (overwriting `gnus-default-adaptive-score-alist').")
(type . choice)
(data ((tag . "Default")
(default . nil)
(type . const))
((tag . "Enable")
(default . t)
(type . const))
((tag . "Disable")
(default . ignore)
(type . const))
((tag . "Custom")
(doc . "Customization of adaptive scoring.
Each time you read an article it will be marked as read. Likewise, if
you delete it it will be marked as deleted, and if you tick it it will
be marked as ticked. When you leave a group, GNUS can automatically
create score file entries based on these marks, so next time you enter
the group articles with subjects that you read last time have higher
score and articles with subjects that deleted will have lower score.
Below is a list of such marks. You can insert new marks to the list
by pushing on one of the `[INS]' buttons in the left margin to create
a new entry and then pushing the `Mark' button to select the mark.
For each mark there is another list, this time of article headers,
which determine how the mark should affect that header. The `[INS]'
buttons of this list are indented to indicate that the belong to the
mark above. Push the `Header' button to choose a header, and then
enter a score value in the `Score' field.
For each article that are marked with `Mark' when you leave the
group, a temporary score entry for the articles `Header' with the
value of `Score' will be added the adapt file. If the score entry
already exists, `Score' will be added to its value. If you understood
that, you are smart.
You can select the special value `Other' when pressing the `Mark' or
`Header' buttons. This is because Lars might add more useful values
there. If he does, it is up to you to figure out what they are named.")
(type . list)
(default . ((__uninitialized__)))
(data ((type . repeat)
(header . nil)
(data . ((type . list)
(header . nil)
(compact . t)
(data ((type . choice)
(tag . "Mark")
(data ((tag . "Unread")
(default . gnus-unread-mark)
(type . const))
((tag . "Ticked")
(default . gnus-ticked-mark)
(type . const))
((tag . "Dormant")
(default . gnus-dormant-mark)
(type . const))
((tag . "Deleted")
(default . gnus-del-mark)
(type . const))
((tag . "Read")
(default . gnus-read-mark)
(type . const))
((tag . "Expirable")
(default . gnus-expirable-mark)
(type . const))
((tag . "Killed")
(default . gnus-killed-mark)
(type . const))
((tag . "Kill-file")
(default . gnus-kill-file-mark)
(type . const))
((tag . "Low-score")
(default . gnus-low-score-mark)
(type . const))
((tag . "Catchup")
(default . gnus-catchup-mark)
(type . const))
((tag . "Ancient")
(default . gnus-ancient-mark)
(type . const))
((tag . "Canceled")
(default . gnus-canceled-mark)
(type . const))
((prompt . "Other")
(default . ??)
(type . sexp))))
((type . repeat)
(prefix . " ")
(data . ((type . list)
(compact . t)
(data ((tag . "Header")
(type . choice)
(data ((tag . "Subject")
(default . subject)
(type . const))
((prompt . "From")
(tag . "From ")
(default . from)
(type . const))
((prompt . "Other")
(width . 7)
(default . nil)
(type . symbol))))
((tag . "Score")
(type . integer))))))))))))))
((name . local)
(tag . "Local")
(doc . "\
List of local variables to set when this score file is loaded.
Using this entry can provide a convenient way to set variables that
will affect the summary mode for only some specific groups, i.e. those
groups matched by the current score file.")
(type . list)
(data ((type . repeat)
(header . nil)
(data . ((type . list)
(compact . t)
(data ((tag . "Name")
(width . 26)
(type . symbol))
((tag . "Value")
(width . 26)
(type . sexp)))))))))))
(defconst gnus-score-custom-type-properties
'((gnus-score-custom-maybe-type
(type . choice)
(data ((type . integer)
(default . 0))
((tag . "Default")
(type . const)
(default . nil))))
(gnus-score-custom-string-type
(type . list)
(data ((type . repeat)
(header . nil)
(data . ((type . list)
(compact . t)
(data ((tag . "Match")
(width . 59)
(type . string))
"\n "
((tag . "Score")
(type . integer))
((tag . "Date")
(type . choice)
(data ((type . integer)
(default . 0)
(width . 9))
((tag . "Permanent")
(type . const)
(default . nil))))
((tag . "Type")
(type . choice)
(data ((tag . "Exact")
(default . E)
(type . const))
((tag . "Substring")
(default . S)
(type . const))
((tag . "Regexp")
(default . R)
(type . const))
((tag . "Fuzzy")
(default . F)
(type . const))
((tag . "Exact (fold)")
(default . e)
(type . const))
((tag . "Substring (fold)")
(default . s)
(type . const))
((tag . "Regexp (fold)")
(default . r)
(type . const))
((tag . "Fuzzy (fold)")
(default . f)
(type . const))))))))))
(gnus-score-custom-integer-type
(type . list)
(data ((type . repeat)
(header . nil)
(data . ((type . list)
(compact . t)
(data ((tag . "Match")
(type . integer))
((tag . "Score")
(type . integer))
((tag . "Date")
(type . choice)
(data ((type . integer)
(default . 0)
(width . 9))
((tag . "Permanent")
(type . const)
(default . nil))))
((tag . "Type")
(type . choice)
(data ((tag . "<")
(default . <)
(type . const))
((tag . ">")
(default . >)
(type . const))
((tag . "=")
(default . =)
(type . const))
((tag . ">=")
(default . >=)
(type . const))
((tag . "<=")
(default . <=)
(type . const))))))))))
(gnus-score-custom-date-type
(type . list)
(data ((type . repeat)
(header . nil)
(data . ((type . list)
(compact . t)
(data ((tag . "Match")
(width . 59)
(type . string))
"\n "
((tag . "Score")
(type . integer))
((tag . "Date")
(type . choice)
(data ((type . integer)
(default . 0)
(width . 9))
((tag . "Permanent")
(type . const)
(default . nil))))
((tag . "Type")
(type . choice)
(data ((tag . "Before")
(default . before)
(type . const))
((tag . "After")
(default . after)
(type . const))
((tag . "At")
(default . at)
(type . const))))))))))))
(defvar gnus-score-custom-file nil
"Name of SCORE file being customized.")
(defun gnus-score-customize ()
"Create a buffer for editing gnus SCORE files."
(interactive)
(let (gnus-score-alist)
(custom-buffer-create "*Score Edit*" gnus-score-custom-data
gnus-score-custom-type-properties
'gnus-score-custom-set
'gnus-score-custom-get
'gnus-score-custom-save))
(make-local-variable 'gnus-score-custom-file)
(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))
(defun gnus-score-custom-get (name)
(if (eq name 'file)
gnus-score-custom-file
(let ((entry (assoc (symbol-name name) gnus-score-alist)))
(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))))))
(cdr entry)
(car (cdr entry)))))))
(defun gnus-score-custom-set (name value)
(cond ((eq name 'file)
(setq gnus-score-custom-file value))
((assoc (symbol-name name) gnus-score-alist)
(if value
(setcdr (assoc (symbol-name name) gnus-score-alist) value)
(setq gnus-score-alist (delq (assoc (symbol-name name)
gnus-score-alist)
gnus-score-alist))))
((assoc (symbol-name name) gnus-header-index)
(if value
(setq gnus-score-alist
(cons (cons (symbol-name name) value) gnus-score-alist))))
((assoc name gnus-score-alist)
(cond ((null value)
(setq gnus-score-alist (delq (assoc name gnus-score-alist)
gnus-score-alist)))
((and (listp value) (not (eq name 'eval)))
(setcdr (assoc name gnus-score-alist) value))
(t
(setcdr (assoc name gnus-score-alist) (list value)))))
((null value))
((and (listp value) (not (eq name 'eval)))
(setq gnus-score-alist (cons (cons name value) gnus-score-alist)))
(t
(setq gnus-score-alist
(cons (cons name (list value)) gnus-score-alist)))))
(defun gnus-score-custom-sanify (entry)
(list (nth 0 entry)
(or (nth 1 entry) gnus-score-interactive-default-score)
(nth 2 entry)
(cond ((null (nth 3 entry))
's)
((memq (nth 3 entry) '(before after at >= <=))
(nth 3 entry))
(t
(intern (substring (symbol-name (nth 3 entry)) 0 1))))))
(defvar gnus-score-cache nil)
(defun gnus-score-custom-load ()
(interactive)
(let ((file (custom-name-value 'file)))
(if (eq file custom-nil)
(error "You must specify a file name"))
(setq file (expand-file-name file gnus-kill-files-directory))
(gnus-score-load file)
(setq gnus-score-custom-file file)
(custom-reset-all)
(gnus-message 4 "Loaded")))
(defun gnus-score-custom-save ()
(interactive)
(custom-apply-all)
(gnus-score-remove-from-cache gnus-score-custom-file)
(let ((file gnus-score-custom-file)
(score gnus-score-alist)
emacs-lisp-mode-hook)
(save-excursion
(set-buffer (get-buffer-create "*Score*"))
(buffer-disable-undo (current-buffer))
(erase-buffer)
(pp score (current-buffer))
(gnus-make-directory (file-name-directory file))
(write-region (point-min) (point-max) file nil 'silent)
(kill-buffer (current-buffer))))
(gnus-message 4 "Saved"))
(provide 'gnus-edit)
;;; gnus-edit.el end here

View file

@ -1,242 +0,0 @@
;;; gnus-ems.el --- functions for making Gnus work under different Emacsen
;; Copyright (C) 1995,96 Free Software Foundation, Inc.
;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
;; Keywords: news
;; This file is part of GNU Emacs.
;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Commentary:
;;; Code:
(eval-when-compile (require 'cl))
(defvar gnus-mouse-2 [mouse-2])
(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))
;;; 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)))))
(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))))
(eval-and-compile
(if (string-match "XEmacs\\|Lucid" emacs-version)
()
(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
`~/.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.")
(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
`~/.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."))
(cond
((string-match "XEmacs\\|Lucid" emacs-version)
(gnus-xmas-define))
((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))))
(eval-and-compile
(cond
((not window-system)
(defun gnus-dummy-func (&rest args))
(let ((funcs '(mouse-set-point set-face-foreground
set-face-background x-popup-menu)))
(while funcs
(or (fboundp (car funcs))
(fset (car funcs) 'gnus-dummy-func))
(setq funcs (cdr funcs))))))
(or (fboundp 'file-regular-p)
(defun file-regular-p (file)
(and (not (file-directory-p file))
(not (file-symlink-p file))
(file-exists-p file))))
(or (fboundp 'face-list)
(defun face-list (&rest args))))
(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
'((?: . ?_)
(?+ . ?-))))))))
(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)
(gnus-xmas-redefine))
((boundp 'MULE)
;; Mule definitions
(defalias 'gnus-truncate-string 'truncate-string)
(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))))
(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)
;; Local Variables:
;; byte-compile-warnings: '(redefine callargs)
;; End:
;;; gnus-ems.el ends here

View file

@ -1,872 +0,0 @@
;;; gnus-gl.el --- an interface to GroupLens for Gnus
;; Copyright (C) 1995,96 Free Software Foundation, Inc.
;; Author: Brad Miller <bmiller@cs.umn.edu>
;; Keywords: news, score
;; 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:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; GroupLens software and documentation is copyright (c) 1995 by Paul
;; Resnick (Massachusetts Institute of Technology); Brad Miller, John
;; Riedl, Jon Herlocker, and Joseph Konstan (University of Minnesota),
;; and David Maltz (Carnegie-Mellon University).
;;
;; Permission to use, copy, modify, and distribute this documentation
;; for non-commercial and commercial purposes without fee is hereby
;; granted provided that this copyright notice and permission notice
;; appears in all copies and that the names of the individuals and
;; institutions holding this copyright are not used in advertising or
;; publicity pertaining to this software without specific, written
;; prior permission. The copyright holders make no representations
;; about the suitability of this software and documentation for any
;; purpose. It is provided ``as is'' without express or implied
;; warranty.
;;
;; The copyright holders request that they be notified of
;; modifications of this code. Please send electronic mail to
;; grouplens@cs.umn.edu for more information or to announce derived
;; works.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Author: Brad Miller
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; User Documentation:
;; To use GroupLens you must load this file.
;; You must also register a pseudonym with the Better Bit Bureau.
;; http://www.cs.umn.edu/Research/GroupLens
;;
;; ---------------- For your .emacs or .gnus file ----------------
;;
;; As of version 2.5, grouplens now works as a minor mode of
;; gnus-summary-mode. To get make that work you just need a couple of
;; hooks.
;; (setq gnus-use-grouplens t)
;; (setq grouplens-pseudonym "")
;; (setq grouplens-bbb-host "grouplens.cs.umn.edu")
;;
;; (setq gnus-summary-default-score 0)
;;
;; USING GROUPLENS
;; How do I Rate an article??
;; Before you type n to go to the next article, hit a number from 1-5
;; Type r in the summary buffer and you will be prompted.
;; Note that when you're in grouplens-minor-mode 'r' maskes the
;; usual reply binding for 'r'
;;
;; What if, Gasp, I find a bug???
;; Please type M-x gnus-gl-submit-bug-report. This will set up a
;; mail buffer with the state of variables and buffers that will help
;; me debug the problem. A short description up front would help too!
;;
;; How do I display the prediction for an aritcle:
;; If you set the gnus-summary-line-format as shown above, the score
;; (prediction) will be shown automatically.
;;
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Programmer Notes
;; 10/9/95
;; gnus-scores-articles contains the articles
;; When scoring is done, the call tree looks something like:
;; gnus-possibly-score-headers
;; ==> gnus-score-headers
;; ==> gnus-score-load-file
;; ==> get-all-mids (from the eval form)
;;
;; it would be nice to have one that gets called after all the other
;; headers have been scored.
;; we may want a variable gnus-grouplens-scale-factor
;; and gnus-grouplens-offset this would probably be either -3 or 0
;; to make the scores centered around zero or not.
;; Notes 10/12/95
;; According to Lars, Norse god of gnus, the simple way to insert a
;; call to an external function is to have a function added to the
;; variable gnus-score-find-files-function This new function
;; gnus-grouplens-score-alist will return a core alist that
;; has (("message-id" ("<message-id-xxxx>" score) ("<message-id-xxxy>" score))
;; This seems like it would be pretty inefficient, though workable.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TODO
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; 3. Add some more ways to rate messages
;; 4. Better error handling for token timeouts.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; bugs
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;; Code:
(require 'gnus-score)
(require 'cl)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; User variables
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defvar gnus-summary-grouplens-line-format
"%U%R%z%l%I%(%[%4L: %-20,20n%]%) %s\n"
"*The line format spec in summary GroupLens mode buffers.")
(defvar grouplens-pseudonym ""
"User's pseudonym. This pseudonym is obtained during the registration process")
(defvar grouplens-bbb-host "grouplens.cs.umn.edu"
"Host where the bbbd is running" )
(defvar grouplens-bbb-port 9000
"Port where the bbbd is listening" )
(defvar grouplens-newsgroups
'("comp.lang.c++" "rec.humor" "rec.food.recipes" "comp.groupware"
"mn.general" "rec.arts.movies" "rec.arts.movies.current-films"
"comp.lang.java" "comp.os.linux.announce" "comp.os.linux.misc"
"comp.os.linux.development.apps" "comp.os.linux.development.system")
"*Groups that are part of the GroupLens experiment.")
(defvar grouplens-prediction-display 'prediction-spot
"valid values are:
prediction-spot -- an * corresponding to the prediction between 1 and 5,
confidence-interval -- a numeric confidence interval
prediction-bar -- |##### | the longer the bar, the better the article,
confidence-bar -- | ----- } the prediction is in the middle of the bar,
confidence-spot -- ) * | the spot gets bigger with more confidence,
prediction-num -- plain-old numeric value,
confidence-plus-minus -- prediction +/i confidence")
(defvar grouplens-score-offset 0
"Offset the prediction by this value.
Setting this variable to -2 would have the following effect on
GroupLens scores:
1 --> -2
2 --> -1
3 --> 0
4 --> 1
5 --> 2
The reason is that a user might want to do this is to combine
GroupLens predictions with scores calculated by other score methods.")
(defvar grouplens-score-scale-factor 1
"This variable allows the user to magnify the effect of GroupLens scores.
The scale factor is applied after the offset.")
(defvar gnus-grouplens-override-scoring 'override
"Tell Grouplens to override the normal Gnus scoring mechanism.
GroupLens scores can be combined with gnus scores in one of three ways.
'override -- just use grouplens predictions for grouplens groups
'combine -- combine grouplens scores with gnus scores
'separate -- treat grouplens scores completely separate from gnus")
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; Program global variables
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defvar grouplens-bbb-token "0"
"Current session token number")
(defvar grouplens-bbb-process nil
"Process Id of current bbbd network stream process")
(defvar grouplens-bbb-buffer nil
"Buffer associated with the BBBD process")
(defvar grouplens-rating-alist nil
"Current set of message-id rating pairs")
(defvar grouplens-current-hashtable (make-hash-table :test 'equal :size 100))
;; this seems like a pretty ugly way to get around the problem, but If
;; I don't do this, then the compiler complains when I call gethash
;;
(eval-when-compile (setq grouplens-current-hashtable
(make-hash-table :test 'equal :size 100)))
(defvar grouplens-current-group nil)
(defvar bbb-mid-list nil)
(defvar bbb-alist nil)
(defvar bbb-timeout-secs 10
"Number of seconds to wait for some response from the BBB.
If this times out we give up and assume that something has died..." )
(defvar grouplens-previous-article nil
"Message-ID of the last article read.")
(defvar bbb-read-point)
(defvar bbb-response-point)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; Utility Functions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun bbb-connect-to-bbbd (host port)
(unless grouplens-bbb-buffer
(setq grouplens-bbb-buffer
(get-buffer-create (format " *BBBD trace: %s*" host)))
(save-excursion
(set-buffer grouplens-bbb-buffer)
(make-local-variable 'bbb-read-point)
(setq bbb-read-point (point-min))))
;; clear the trace buffer of old output
(save-excursion
(set-buffer grouplens-bbb-buffer)
(erase-buffer))
;; open the connection to the server
(setq grouplens-bbb-process nil)
(catch 'done
(condition-case error
(setq grouplens-bbb-process
(open-network-stream "BBBD" grouplens-bbb-buffer host port))
(error (gnus-message 3 "Error: Failed to connect to BBB")
nil))
(and (null grouplens-bbb-process)
(throw 'done nil))
;; (set-process-filter grouplens-bbb-process 'bbb-process-filter)
(save-excursion
(set-buffer grouplens-bbb-buffer)
(setq bbb-read-point (point-min))
(or (bbb-read-response grouplens-bbb-process)
(throw 'done nil))))
grouplens-bbb-process)
;; (defun bbb-process-filter (process output)
;; (save-excursion
;; (set-buffer (bbb-process-buffer process))
;; (goto-char (point-max))
;; (insert output)))
(defun bbb-send-command (process command)
(goto-char (point-max))
(insert command)
(insert "\r\n")
(setq bbb-read-point (point))
(setq bbb-response-point (point))
(set-marker (process-mark process) (point)) ; process output also comes here
(process-send-string process command)
(process-send-string process "\r\n"))
(defun bbb-read-response (process) ; &optional return-response-string)
"This function eats the initial response of OK or ERROR from the BBB."
(let ((case-fold-search nil)
match-end)
(goto-char bbb-read-point)
(while (and (not (search-forward "\r\n" nil t))
(accept-process-output process bbb-timeout-secs))
(goto-char bbb-read-point))
(setq match-end (point))
(goto-char bbb-read-point)
(setq bbb-read-point match-end)
(looking-at "OK")))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; Login Functions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun bbb-login ()
"return the token number if login is successful, otherwise return nil"
(interactive)
(setq grouplens-bbb-token nil)
(if (not (equal grouplens-pseudonym ""))
(let ((bbb-process
(bbb-connect-to-bbbd grouplens-bbb-host grouplens-bbb-port)))
(if bbb-process
(save-excursion
(set-buffer (process-buffer bbb-process))
(bbb-send-command bbb-process
(concat "login " grouplens-pseudonym))
(if (bbb-read-response bbb-process)
(setq grouplens-bbb-token (bbb-extract-token-number))
(gnus-message 3 "Error: Grouplens login failed")))))
(gnus-message 3 "Error: you must set a pseudonym"))
grouplens-bbb-token)
(defun bbb-extract-token-number ()
(let ((token-pos (search-forward "token=" nil t) ))
(if (looking-at "[0-9]+")
(buffer-substring token-pos (match-end 0)))))
(gnus-add-shutdown 'bbb-logout 'gnus)
(defun bbb-logout ()
"logout of bbb session"
(let ((bbb-process
(bbb-connect-to-bbbd grouplens-bbb-host grouplens-bbb-port)))
(if bbb-process
(save-excursion
(set-buffer (process-buffer bbb-process))
(bbb-send-command bbb-process (concat "logout " grouplens-bbb-token))
(bbb-read-response bbb-process))
nil)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; Get Predictions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun bbb-build-mid-scores-alist (groupname)
"this function can be called as part of the function to return the
list of score files to use. See the gnus variable
gnus-score-find-score-files-function.
*Note:* If you want to use grouplens scores along with calculated scores,
you should see the offset and scale variables. At this point, I don't
recommend using both scores and grouplens predictions together."
(setq grouplens-current-group groupname)
(if (member groupname grouplens-newsgroups)
(let* ((mid-list (bbb-get-all-mids))
(predict-list (bbb-get-predictions mid-list groupname)))
(setq grouplens-previous-article nil)
;; scores-alist should be a list of lists:
;; ((("message-id" ("<mid1>" score1 nil s) ("<mid2> score2 nil s))))
;;`((("message-id" . ,predict-list))) ; Yes, this is the return value
(list (list (list (append (list "message-id") predict-list)))))
nil))
(defun bbb-get-predictions (midlist groupname)
"Ask the bbb for predictions, and build up the score alist."
(if (or (null grouplens-bbb-token)
(equal grouplens-bbb-token "0"))
(progn
(gnus-message 3 "Error: You are not logged in to a BBB")
nil)
(gnus-message 5 "Fetching Predictions...")
(let (predict-list
(predict-command (bbb-build-predict-command midlist groupname
grouplens-bbb-token))
(bbb-process (bbb-connect-to-bbbd grouplens-bbb-host
grouplens-bbb-port)))
(if bbb-process
(save-excursion
(set-buffer (process-buffer bbb-process))
(bbb-send-command bbb-process predict-command)
(if (bbb-read-response bbb-process)
(setq predict-list (bbb-get-prediction-response bbb-process))
(gnus-message 1 "Invalid Token, login and try again")
(ding))))
(setq bbb-alist predict-list))))
(defun bbb-get-all-mids ()
(let ((index (nth 1 (assoc "message-id" gnus-header-index)))
(articles gnus-newsgroup-headers)
art this)
(setq bbb-mid-list nil)
(while articles
(progn (setq art (car articles)
this (aref art index)
articles (cdr articles))
(setq bbb-mid-list (cons this bbb-mid-list))))
bbb-mid-list))
(defun bbb-build-predict-command (mlist grpname token)
(let ((cmd (concat "getpredictions " token " " grpname "\r\n"))
art)
(while mlist
(setq art (car mlist)
cmd (concat cmd art "\r\n")
mlist (cdr mlist)))
(setq cmd (concat cmd ".\r\n"))
cmd))
(defun bbb-get-prediction-response (process)
(let ((case-fold-search nil)
match-end)
(goto-char bbb-read-point)
(while (and (not (search-forward ".\r\n" nil t))
(accept-process-output process bbb-timeout-secs))
(goto-char bbb-read-point))
(setq match-end (point))
(goto-char (+ bbb-response-point 4)) ;; we ought to be right before OK
(bbb-build-response-alist)))
;; build-response-alist assumes that the cursor has been positioned at
;; the first line of the list of mid/rating pairs. For now we will
;; use a prediction of 99 to signify no prediction. Ultimately, we
;; should just ignore messages with no predictions.
(defun bbb-build-response-alist ()
(let ((resp nil)
(match-end (point)))
(setq grouplens-current-hashtable (make-hash-table :test 'equal :size 100))
(while
(cond ((looking-at "\\(<.*>\\) :nopred=")
(push `(,(bbb-get-mid) ,gnus-summary-default-score nil s) resp)
(forward-line 1)
t)
((looking-at "\\(<.*>\\) :pred=\\([0-9]\.[0-9][0-9]\\) :conflow=\\([0-9]\.[0-9][0-9]\\) :confhigh=\\([0-9]\.[0-9][0-9]\\)")
(push `(,(bbb-get-mid) ,(bbb-get-pred) nil s) resp)
(cl-puthash (bbb-get-mid)
(list (bbb-get-pred) (bbb-get-confl) (bbb-get-confh))
grouplens-current-hashtable)
(forward-line 1)
t)
((looking-at "\\(<.*>\\) :pred=\\([0-9]\.[0-9][0-9]\\)")
(push `(,(bbb-get-mid) ,(bbb-get-pred) nil s) resp)
(cl-puthash (bbb-get-mid)
(list (bbb-get-pred) 0 0)
grouplens-current-hashtable)
(forward-line 1)
t)
(t nil)))
resp))
;; these two functions assume that there is an active match lying
;; around. Where the first parenthesized expression is the
;; message-id, and the second is the prediction. Since gnus assumes
;; that scores are integer values?? we round the prediction.
(defun bbb-get-mid ()
(buffer-substring (match-beginning 1) (match-end 1)))
(defun bbb-get-pred ()
(let ((tpred (string-to-number (buffer-substring
(match-beginning 2)
(match-end 2)))))
(if (> tpred 0)
(round (* grouplens-score-scale-factor (+ grouplens-score-offset tpred)))
1)))
(defun bbb-get-confl ()
(string-to-number (buffer-substring (match-beginning 3) (match-end 3))))
(defun bbb-get-confh ()
(string-to-number (buffer-substring (match-beginning 4) (match-end 4))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; Prediction Display
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defconst grplens-rating-range 4.0)
(defconst grplens-maxrating 5)
(defconst grplens-minrating 1)
(defconst grplens-predstringsize 12)
(defvar gnus-tmp-score)
(defun bbb-grouplens-score (header)
(if (eq gnus-grouplens-override-scoring 'separate)
(bbb-grouplens-other-score header)
(let* ((rate-string (make-string 12 ? ))
(mid (aref header (nth 1 (assoc "message-id" gnus-header-index))))
(hashent (gethash mid grouplens-current-hashtable))
(iscore gnus-tmp-score)
(low (car (cdr hashent)))
(high (car (cdr (cdr hashent)))))
(aset rate-string 0 ?|)
(aset rate-string 11 ?|)
(unless (member grouplens-current-group grouplens-newsgroups)
(unless (equal grouplens-prediction-display 'prediction-num)
(cond ((< iscore 0)
(setq iscore 1))
((> iscore 5)
(setq iscore 5))))
(setq low 0)
(setq high 0))
(if (and (bbb-valid-score iscore)
(not (null mid)))
(cond
;; prediction-spot
((equal grouplens-prediction-display 'prediction-spot)
(setq rate-string (bbb-fmt-prediction-spot rate-string iscore)))
;; confidence-interval
((equal grouplens-prediction-display 'confidence-interval)
(setq rate-string (bbb-fmt-confidence-interval iscore low high)))
;; prediction-bar
((equal grouplens-prediction-display 'prediction-bar)
(setq rate-string (bbb-fmt-prediction-bar rate-string iscore)))
;; confidence-bar
((equal grouplens-prediction-display 'confidence-bar)
(setq rate-string (format "| %4.2f |" iscore)))
;; confidence-spot
((equal grouplens-prediction-display 'confidence-spot)
(setq rate-string (format "| %4.2f |" iscore)))
;; prediction-num
((equal grouplens-prediction-display 'prediction-num)
(setq rate-string (bbb-fmt-prediction-num iscore)))
;; confidence-plus-minus
((equal grouplens-prediction-display 'confidence-plus-minus)
(setq rate-string (bbb-fmt-confidence-plus-minus iscore low high))
)
(t (gnus-message 3 "Invalid prediction display type")))
(aset rate-string 5 ?N) (aset rate-string 6 ?A))
rate-string)))
;;
;; Gnus user format function that doesn't depend on
;; bbb-build-mid-scores-alist being used as the score function, but is
;; instead called from gnus-select-group-hook. -- LAB
(defun bbb-grouplens-other-score (header)
(if (not (member grouplens-current-group grouplens-newsgroups))
;; Return an empty string
""
(let* ((rate-string (make-string 12 ? ))
(mid (aref header (nth 1 (assoc "message-id" gnus-header-index))))
(hashent (gethash mid grouplens-current-hashtable))
(pred (or (nth 0 hashent) 0))
(low (nth 1 hashent))
(high (nth 2 hashent)))
;; Init rate-string
(aset rate-string 0 ?|)
(aset rate-string 11 ?|)
(unless (equal grouplens-prediction-display 'prediction-num)
(cond ((< pred 0)
(setq pred 1))
((> pred 5)
(setq pred 5))))
;; If no entry in BBB hash mark rate string as NA and return
(cond
((null hashent)
(aset rate-string 5 ?N)
(aset rate-string 6 ?A)
rate-string)
((equal grouplens-prediction-display 'prediction-spot)
(bbb-fmt-prediction-spot rate-string pred))
((equal grouplens-prediction-display 'confidence-interval)
(bbb-fmt-confidence-interval pred low high))
((equal grouplens-prediction-display 'prediction-bar)
(bbb-fmt-prediction-bar rate-string pred))
((equal grouplens-prediction-display 'confidence-bar)
(format "| %4.2f |" pred))
((equal grouplens-prediction-display 'confidence-spot)
(format "| %4.2f |" pred))
((equal grouplens-prediction-display 'prediction-num)
(bbb-fmt-prediction-num pred))
((equal grouplens-prediction-display 'confidence-plus-minus)
(bbb-fmt-confidence-plus-minus pred low high))
(t
(gnus-message 3 "Invalid prediction display type")
(aset rate-string 0 ?|)
(aset rate-string 11 ?|)
rate-string)))))
(defun bbb-valid-score (score)
(or (equal grouplens-prediction-display 'prediction-num)
(and (>= score grplens-minrating)
(<= score grplens-maxrating))))
(defun bbb-requires-confidence (format-type)
(or (equal format-type 'confidence-plus-minus)
(equal format-type 'confidence-spot)
(equal format-type 'confidence-interval)))
(defun bbb-have-confidence (clow chigh)
(not (or (null clow)
(null chigh))))
(defun bbb-fmt-prediction-spot (rate-string score)
(aset rate-string
(round (* (/ (- score grplens-minrating) grplens-rating-range)
(+ (- grplens-predstringsize 4) 1.49)))
?*)
rate-string)
(defun bbb-fmt-confidence-interval (score low high)
(if (bbb-have-confidence low high)
(format "|%4.2f-%4.2f |" low high)
(bbb-fmt-prediction-num score)))
(defun bbb-fmt-confidence-plus-minus (score low high)
(if (bbb-have-confidence low high)
(format "|%3.1f+/-%4.2f|" score (/ (- high low) 2.0))
(bbb-fmt-prediction-num score)))
(defun bbb-fmt-prediction-bar (rate-string score)
(let* ((i 1)
(step (/ grplens-rating-range (- grplens-predstringsize 4)))
(half-step (/ step 2))
(loc (- grplens-minrating half-step)))
(while (< i (- grplens-predstringsize 2))
(if (> score loc)
(aset rate-string i ?#)
(aset rate-string i ? ))
(setq i (+ i 1))
(setq loc (+ loc step)))
)
rate-string)
(defun bbb-fmt-prediction-num (score)
(format "| %4.2f |" score))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; Put Ratings
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; The message-id for the current article can be found in
;; (aref gnus-current-headers (nth 1 (assoc "message-id" gnus-header-index)))
(defun bbb-put-ratings ()
(if (and grouplens-rating-alist
(member gnus-newsgroup-name grouplens-newsgroups))
(let ((bbb-process (bbb-connect-to-bbbd grouplens-bbb-host
grouplens-bbb-port))
(rate-command (bbb-build-rate-command grouplens-rating-alist)))
(if bbb-process
(save-excursion
(set-buffer (process-buffer bbb-process))
(gnus-message 5 "Sending Ratings...")
(bbb-send-command bbb-process rate-command)
(if (bbb-read-response bbb-process)
(setq grouplens-rating-alist nil)
(gnus-message 1
"Token timed out: call bbb-login and quit again")
(ding))
(gnus-message 5 "Sending Ratings...Done"))
(gnus-message 3 "No BBB connection")))
(setq grouplens-rating-alist nil)))
(defun bbb-build-rate-command (rate-alist)
(let (this
(cmd (concat "putratings " grouplens-bbb-token
" " grouplens-current-group " \r\n")))
(while rate-alist
(setq this (car rate-alist)
cmd (concat cmd (car this) " :rating=" (cadr this) ".00"
" :time=" (cddr this) "\r\n")
rate-alist (cdr rate-alist)))
(concat cmd ".\r\n")))
;; Interactive rating functions.
(defun bbb-summary-rate-article (rating &optional midin)
(interactive "nRating: ")
(when (member gnus-newsgroup-name grouplens-newsgroups)
(let ((mid (or midin (bbb-get-current-id))))
(if (and rating
(>= rating grplens-minrating)
(<= rating grplens-maxrating)
mid)
(let ((oldrating (assoc mid grouplens-rating-alist)))
(if oldrating
(setcdr oldrating (cons rating 0))
(push `(,mid . (,rating . 0)) grouplens-rating-alist))
(gnus-summary-mark-article nil (int-to-string rating)))
(gnus-message 3 "Invalid rating")))))
(defun grouplens-next-unread-article (rating)
"Select unread article after current one."
(interactive "P")
(if rating (bbb-summary-rate-article rating))
(gnus-summary-next-unread-article))
(defun grouplens-best-unread-article (rating)
"Select unread article after current one."
(interactive "P")
(if rating (bbb-summary-rate-article rating))
(gnus-summary-best-unread-article))
(defun grouplens-summary-catchup-and-exit (rating)
"Mark all articles not marked as unread in this newsgroup as read,
then exit. If prefix argument ALL is non-nil, all articles are
marked as read."
(interactive "P")
(if rating
(bbb-summary-rate-article rating))
(if (numberp rating)
(gnus-summary-catchup-and-exit)
(gnus-summary-catchup-and-exit rating)))
(defun grouplens-score-thread (score)
"Raise the score of the articles in the current thread with SCORE."
(interactive "nRating: ")
(let (e)
(save-excursion
(let ((articles (gnus-summary-articles-in-thread)))
(while articles
(gnus-summary-goto-subject (car articles))
(gnus-set-global-variables)
(bbb-summary-rate-article score
(mail-header-id
(gnus-summary-article-header
(car articles))))
(setq articles (cdr articles))))
(setq e (point)))
(let ((gnus-summary-check-current t))
(or (zerop (gnus-summary-next-subject 1 t))
(goto-char e))))
(gnus-summary-recenter)
(gnus-summary-position-point)
(gnus-set-mode-line 'summary))
(defun bbb-get-current-id ()
(if gnus-current-headers
(aref gnus-current-headers
(nth 1 (assoc "message-id" gnus-header-index)))
(gnus-message 3 "You must select an article before you rate it")))
(defun bbb-grouplens-group-p (group)
"Say whether GROUP is a GroupLens group."
(if (member group grouplens-newsgroups) " (GroupLens Enhanced)" ""))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TIME SPENT READING
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defvar grouplens-current-starting-time nil)
(defun grouplens-start-timer ()
(setq grouplens-current-starting-time (current-time)))
(defun grouplens-elapsed-time ()
(let ((et (bbb-time-float (current-time))))
(- et (bbb-time-float grouplens-current-starting-time))))
(defun bbb-time-float (timeval)
(+ (* (car timeval) 65536)
(cadr timeval)))
(defun grouplens-do-time ()
(when (member gnus-newsgroup-name grouplens-newsgroups)
(when grouplens-previous-article
(let ((elapsed-time (grouplens-elapsed-time))
(oldrating (assoc grouplens-previous-article
grouplens-rating-alist)))
(if (not oldrating)
(push `(,grouplens-previous-article . (0 . ,elapsed-time))
grouplens-rating-alist)
(setcdr oldrating (cons (cadr oldrating) elapsed-time)))))
(grouplens-start-timer)
(setq grouplens-previous-article (bbb-get-current-id))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; BUG REPORTING
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defconst gnus-gl-version "gnus-gl.el 2.12")
(defconst gnus-gl-maintainer-address "grouplens-bug@cs.umn.edu")
(defun gnus-gl-submit-bug-report ()
"Submit via mail a bug report on gnus-gl"
(interactive)
(require 'reporter)
(reporter-submit-bug-report gnus-gl-maintainer-address
(concat "gnus-gl.el " gnus-gl-version)
(list 'grouplens-pseudonym
'grouplens-bbb-host
'grouplens-bbb-port
'grouplens-newsgroups
'grouplens-bbb-token
'grouplens-bbb-process
'grouplens-current-group
'grouplens-previous-article
'grouplens-mid-list
'bbb-alist)
nil
'gnus-gl-get-trace))
(defun gnus-gl-get-trace ()
"Insert the contents of the BBBD trace buffer"
(if grouplens-bbb-buffer (insert-buffer grouplens-bbb-buffer)))
;;;
;;; Additions to make gnus-grouplens-mode Warning Warning!!
;;; This version of the gnus-grouplens-mode does
;;; not work with gnus-5.x. The "old" way of
;;; setting up GroupLens still works however.
;;;
(defvar gnus-grouplens-mode nil
"Minor mode for providing a GroupLens interface in Gnus summary buffers.")
(defvar gnus-grouplens-mode-map nil)
(unless gnus-grouplens-mode-map
(setq gnus-grouplens-mode-map (make-keymap))
(gnus-define-keys
gnus-grouplens-mode-map
"n" grouplens-next-unread-article
"r" bbb-summary-rate-article
"k" grouplens-score-thread
"c" grouplens-summary-catchup-and-exit
"," grouplens-best-unread-article))
(defun gnus-grouplens-make-menu-bar ()
(unless (boundp 'gnus-grouplens-menu)
(easy-menu-define
gnus-grouplens-menu gnus-grouplens-mode-map ""
'("GroupLens"
["Login" bbb-login t]
["Rate" bbb-summary-rate-article t]
["Next article" grouplens-next-unread-article t]
["Best article" grouplens-best-unread-article t]
["Raise thread" grouplens-score-thread t]
["Report bugs" gnus-gl-submit-bug-report t]))))
(defun gnus-grouplens-mode (&optional arg)
"Minor mode for providing a GroupLens interface in Gnus summary buffers."
(interactive "P")
(when (and (eq major-mode 'gnus-summary-mode)
(member gnus-newsgroup-name grouplens-newsgroups))
(make-local-variable 'gnus-grouplens-mode)
(setq gnus-grouplens-mode
(if (null arg) (not gnus-grouplens-mode)
(> (prefix-numeric-value arg) 0)))
(when gnus-grouplens-mode
(if (not (fboundp 'make-local-hook))
(add-hook 'gnus-select-article-hook 'grouplens-do-time)
(make-local-hook 'gnus-select-article-hook)
(add-hook 'gnus-select-article-hook 'grouplens-do-time nil 'local))
(if (not (fboundp 'make-local-hook))
(add-hook 'gnus-exit-group-hook 'bbb-put-ratings)
(make-local-hook 'gnus-exit-group-hook)
(add-hook 'gnus-exit-group-hook 'bbb-put-ratings nil 'local))
(make-local-variable 'gnus-score-find-score-files-function)
(cond ((eq gnus-grouplens-override-scoring 'combine)
;; either add bbb-buld-mid-scores-alist to a list
;; or make a list
(if (listp gnus-score-find-score-files-function)
(setq gnus-score-find-score-files-function
(append 'bbb-build-mid-scores-alist
gnus-score-find-score-files-function ))
(setq gnus-score-find-score-files-function
(list gnus-score-find-score-files-function
'bbb-build-mid-scores-alist))))
;; leave the gnus-score-find-score-files variable alone
((eq gnus-grouplens-override-scoring 'separate)
(add-hook 'gnus-select-group-hook
'(lambda()
(bbb-build-mid-scores-alist gnus-newsgroup-name))))
;; default is to override
(t (setq gnus-score-find-score-files-function
'bbb-build-mid-scores-alist)))
(make-local-variable 'gnus-summary-line-format)
(setq gnus-summary-line-format
gnus-summary-grouplens-line-format)
(make-local-variable 'gnus-summary-line-format-spec)
(setq gnus-summary-line-format-spec nil)
;; Set up the menu.
(when (and menu-bar-mode
(gnus-visual-p 'grouplens-menu 'menu))
(gnus-grouplens-make-menu-bar))
(unless (assq 'gnus-grouplens-mode minor-mode-alist)
(push '(gnus-grouplens-mode " GroupLens") minor-mode-alist))
(unless (assq 'gnus-grouplens-mode minor-mode-map-alist)
(push (cons 'gnus-grouplens-mode gnus-grouplens-mode-map)
minor-mode-map-alist))
(run-hooks 'gnus-grouplens-mode-hook))))
(provide 'gnus-gl)
;;; gnus-gl.el ends here

View file

@ -1,655 +0,0 @@
;;; gnus-kill.el --- kill commands for Gnus
;; Copyright (C) 1995,96 Free Software Foundation, Inc.
;; Author: Masanobu UMEDA <umerin@flab.flab.fujitsu.junet>
;; Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
;; Keywords: news
;; This file is part of GNU Emacs.
;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Commentary:
;;; Code:
(require 'gnus)
(eval-when-compile (require 'cl))
(defvar gnus-kill-file-mode-hook nil
"*A hook for Gnus kill file mode.")
(defvar gnus-kill-expiry-days 7
"*Number of days before expiring unused kill file entries.")
(defvar gnus-kill-save-kill-file nil
"*If non-nil, will save kill files after processing them.")
(defvar gnus-winconf-kill-file nil)
(defmacro gnus-raise (field expression level)
`(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 File Mode
;;;
(defvar gnus-kill-file-mode-map nil)
(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.
If you are using this mode - you probably shouldn't. Kill files
perform badly and paint with a pretty broad brush. Score files, on
the other hand, are vastly faster (40x speedup) and give you more
control over what to do.
In addition to Emacs-Lisp Mode, the following commands are available:
\\{gnus-kill-file-mode-map}
A kill file contains Lisp expressions to be applied to a selected
newsgroup. The purpose is to mark articles as read on the basis of
some set of regexps. A global kill file is applied to every newsgroup,
and a local kill file is applied to a specified newsgroup. Since a
global kill file is applied to every newsgroup, for better performance
use a local one.
A kill file can contain any kind of Emacs Lisp expressions expected
to be evaluated in the Summary buffer. Writing Lisp programs for this
purpose is not so easy because the internal working of Gnus must be
well-known. For this reason, Gnus provides a general function which
does this easily for non-Lisp programmers.
The `gnus-kill' function executes commands available in Summary Mode
by their key sequences. `gnus-kill' should be called with FIELD,
REGEXP and optional COMMAND and ALL. FIELD is a string representing
the header field or an empty string. If FIELD is an empty string, the
entire article body is searched for. REGEXP is a string which is
compared with FIELD value. COMMAND is a string representing a valid
key sequence in Summary mode or Lisp expression. COMMAND defaults to
'(gnus-summary-mark-as-read nil \"X\"). Make sure that COMMAND is
executed in the Summary buffer. If the second optional argument ALL
is non-nil, the COMMAND is applied to articles which are already
marked as read or unread. Articles which are marked are skipped over
by default.
For example, if you want to mark articles of which subjects contain
the string `AI' as read, a possible kill file may look like:
(gnus-kill \"Subject\" \"AI\")
If you want to mark articles with `D' instead of `X', you can use
the following expression:
(gnus-kill \"Subject\" \"AI\" \"d\")
In this example it is assumed that the command
`gnus-summary-mark-as-read-forward' is assigned to `d' in Summary Mode.
It is possible to delete unnecessary headers which are marked with
`X' in a kill file as follows:
(gnus-expunge \"X\")
If the Summary buffer is empty after applying kill files, Gnus will
exit the selected newsgroup normally. If headers which are marked
with `D' are deleted in a kill file, it is impossible to read articles
which are marked as read in the previous Gnus sessions. Marks other
than `D' should be used for articles which should really be deleted.
Entry to this mode calls emacs-lisp-mode-hook and
gnus-kill-file-mode-hook with no arguments, if that value is non-nil."
(interactive)
(kill-all-local-variables)
(use-local-map gnus-kill-file-mode-map)
(set-syntax-table emacs-lisp-mode-syntax-table)
(setq major-mode 'gnus-kill-file-mode)
(setq mode-name "Kill")
(lisp-mode-variables nil)
(run-hooks 'emacs-lisp-mode-hook 'gnus-kill-file-mode-hook))
(defun gnus-kill-file-edit-file (newsgroup)
"Begin editing a kill file for NEWSGROUP.
If NEWSGROUP is nil, the global kill file is selected."
(interactive "sNewsgroup: ")
(let ((file (gnus-newsgroup-kill-file newsgroup)))
(gnus-make-directory (file-name-directory file))
;; Save current window configuration if this is first invocation.
(or (and (get-file-buffer file)
(get-buffer-window (get-file-buffer file)))
(setq gnus-winconf-kill-file (current-window-configuration)))
;; Hack windows.
(let ((buffer (find-file-noselect file)))
(cond ((get-buffer-window buffer)
(pop-to-buffer buffer))
((eq major-mode 'gnus-group-mode)
(gnus-configure-windows 'group) ;Take all windows.
(pop-to-buffer buffer))
((eq major-mode 'gnus-summary-mode)
(gnus-configure-windows 'article)
(pop-to-buffer gnus-article-buffer)
(bury-buffer gnus-article-buffer)
(switch-to-buffer buffer))
(t ;No good rules.
(find-file-other-window file))))
(gnus-kill-file-mode)))
;; Fix by Sudish Joseph <joseph@cis.ohio-state.edu>.
(defun gnus-kill-set-kill-buffer ()
(let* ((file (gnus-newsgroup-kill-file gnus-newsgroup-name))
(buffer (find-file-noselect file)))
(set-buffer buffer)
(gnus-kill-file-mode)
(bury-buffer buffer)))
(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.
(save-excursion
(let (string)
(or (eq major-mode 'gnus-kill-file-mode)
(gnus-kill-set-kill-buffer))
(unless dont-move
(goto-char (point-max)))
(insert (setq string (format "(gnus-kill %S %S)\n" field regexp)))
(gnus-kill-file-apply-string string))))
(defun gnus-kill-file-kill-by-subject ()
"Kill by subject."
(interactive)
(gnus-kill-file-enter-kill
"Subject"
(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."
(interactive)
(gnus-kill-file-enter-kill
"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."
(interactive)
(gnus-kill-file-enter-kill
"References"
(if (vectorp gnus-current-headers)
(regexp-quote (mail-header-id gnus-current-headers))
"")))
(defun gnus-kill-file-kill-by-xref ()
"Kill by Xref."
(interactive)
(let ((xref (and (vectorp gnus-current-headers)
(mail-header-xref gnus-current-headers)))
(start 0)
group)
(if xref
(while (string-match " \\([^ \t]+\\):" xref start)
(setq start (match-end 0))
(if (not (string=
(setq group
(substring xref (match-beginning 1) (match-end 1)))
gnus-newsgroup-name))
(gnus-kill-file-enter-kill
"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."
(interactive "p")
(let ((name (mail-header-from gnus-current-headers))
string)
(save-excursion
(gnus-kill-set-kill-buffer)
(goto-char (point-min))
(setq name (read-string (concat "Add " level
" to followup articles to: ")
(regexp-quote name)))
(setq
string
(format
"(gnus-kill %S %S '(gnus-summary-temporarily-raise-by-thread %S))\n"
"From" name level))
(insert string)
(gnus-kill-file-apply-string string))
(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."
(interactive)
(if (and gnus-current-kill-article
(get-buffer gnus-summary-buffer))
;; Assume newsgroup is selected.
(gnus-kill-file-apply-string (buffer-string))
(ding) (gnus-message 2 "No newsgroup is selected.")))
(defun gnus-kill-file-apply-string (string)
"Apply STRING to current newsgroup."
(interactive)
(let ((string (concat "(progn \n" string "\n)")))
(save-excursion
(save-window-excursion
(pop-to-buffer gnus-summary-buffer)
(eval (car (read-from-string string)))))))
(defun gnus-kill-file-apply-last-sexp ()
"Apply sexp before point in current buffer to current newsgroup."
(interactive)
(if (and gnus-current-kill-article
(get-buffer gnus-summary-buffer))
;; Assume newsgroup is selected.
(let ((string
(buffer-substring
(save-excursion (forward-sexp -1) (point)) (point))))
(save-excursion
(save-window-excursion
(pop-to-buffer gnus-summary-buffer)
(eval (car (read-from-string string))))))
(ding) (gnus-message 2 "No newsgroup is selected.")))
(defun gnus-kill-file-exit ()
"Save a kill file, then return to the previous buffer."
(interactive)
(save-buffer)
(let ((killbuf (current-buffer)))
;; We don't want to return to article buffer.
(and (get-buffer gnus-article-buffer)
(bury-buffer gnus-article-buffer))
;; Delete the KILL file windows.
(delete-windows-on killbuf)
;; Restore last window configuration if available.
(and gnus-winconf-kill-file
(set-window-configuration gnus-winconf-kill-file))
(setq gnus-winconf-kill-file nil)
;; Kill the KILL file buffer. Suggested by tale@pawl.rpi.edu.
(kill-buffer killbuf)))
;; For kill files
(defun gnus-Newsgroup-kill-file (newsgroup)
"Return the name of a kill file for NEWSGROUP.
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 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)
gnus-kill-files-directory))
(t
;; Place "KILL" under the hierarchical directory.
(expand-file-name (concat (gnus-newsgroup-directory-form newsgroup)
"/" gnus-kill-file-name)
gnus-kill-files-directory))))
(defun gnus-expunge (marks)
"Remove lines marked with MARKS."
(save-excursion
(set-buffer gnus-summary-buffer)
(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.
Returns the number of articles marked as read."
(let* ((kill-files (list (gnus-newsgroup-kill-file nil)
(gnus-newsgroup-kill-file gnus-newsgroup-name)))
(unreads (length gnus-newsgroup-unreads))
(gnus-summary-inhibit-highlight t)
beg)
(setq gnus-newsgroup-kill-headers nil)
;; 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
;; conses, but is, I think, faster than having to assq in every
;; single score function.
(let ((files kill-files))
(while files
(if (file-exists-p (car files))
(let ((headers gnus-newsgroup-headers))
(if gnus-kill-killed
(setq gnus-newsgroup-kill-headers
(mapcar (lambda (header) (mail-header-number header))
headers))
(while headers
(or (gnus-member-of-range
(mail-header-number (car headers))
gnus-newsgroup-killed)
(setq gnus-newsgroup-kill-headers
(cons (mail-header-number (car headers))
gnus-newsgroup-kill-headers)))
(setq headers (cdr headers))))
(setq files nil))
(setq files (cdr files)))))
(if (not gnus-newsgroup-kill-headers)
()
(save-window-excursion
(save-excursion
(while kill-files
(if (not (file-exists-p (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))
(if (consp (condition-case nil (read (current-buffer))
(error nil)))
(gnus-kill-parse-gnus-kill-file)
(gnus-kill-parse-rn-kill-file))
(gnus-message
6 "Processing kill file %s...done" (car kill-files)))
(setq kill-files (cdr kill-files)))))
(gnus-set-mode-line 'summary)
(if beg
(let ((nunreads (- unreads (length gnus-newsgroup-unreads))))
(or (eq nunreads 0)
(gnus-message 6 "Marked %d articles as read" nunreads))
nunreads)
0))))
;; Parse a Gnus killfile.
(defun gnus-score-insert-help (string alist idx)
(save-excursion
(pop-to-buffer "*Score Help*")
(buffer-disable-undo (current-buffer))
(erase-buffer)
(insert string ":\n\n")
(while alist
(insert (format " %c: %s\n" (caar alist) (nth idx (car alist))))
(setq alist (cdr alist)))))
(defun gnus-kill-parse-gnus-kill-file ()
(goto-char (point-min))
(gnus-kill-file-mode)
(let (beg form)
(while (progn
(setq beg (point))
(setq form (condition-case () (read (current-buffer))
(error nil))))
(or (listp form)
(error "Illegal kill entry (possibly rn kill file?): %s" form))
(if (or (eq (car form) 'gnus-kill)
(eq (car form) 'gnus-raise)
(eq (car form) 'gnus-lower))
(progn
(delete-region beg (point))
(insert (or (eval form) "")))
(save-excursion
(set-buffer gnus-summary-buffer)
(condition-case () (eval form) (error nil)))))
(and (buffer-modified-p)
gnus-kill-save-kill-file
(save-buffer))
(set-buffer-modified-p nil)))
;; Parse an rn killfile.
(defun gnus-kill-parse-rn-kill-file ()
(goto-char (point-min))
(gnus-kill-file-mode)
(let ((mod-to-header
'((?a . "")
(?h . "")
(?f . "from")
(?: . "subject")))
(com-to-com
'((?m . " ")
(?j . "X")))
pattern modifier commands)
(while (not (eobp))
(if (not (looking-at "[ \t]*/\\([^/]*\\)/\\([ahfcH]\\)?:\\([a-z=:]*\\)"))
()
(setq pattern (buffer-substring (match-beginning 1) (match-end 1)))
(setq modifier (if (match-beginning 2) (char-after (match-beginning 2))
?s))
(setq commands (buffer-substring (match-beginning 3) (match-end 3)))
;; The "f:+" command marks everything *but* the matches as read,
;; so we simply first match everything as read, and then unmark
;; PATTERN later.
(and (string-match "\\+" commands)
(progn
(gnus-kill "from" ".")
(setq commands "m")))
(gnus-kill
(or (cdr (assq modifier mod-to-header)) "subject")
pattern
(if (string-match "m" commands)
'(gnus-summary-mark-as-unread nil " ")
'(gnus-summary-mark-as-read nil "X"))
nil t))
(forward-line 1))))
;; Kill changes and new format by suggested by JWZ and Sudish Joseph
;; <joseph@cis.ohio-state.edu>.
(defun gnus-kill (field regexp &optional exe-command all silent)
"If FIELD of an article matches REGEXP, execute COMMAND.
Optional 1st argument COMMAND is default to
(gnus-summary-mark-as-read nil \"X\").
If optional 2nd argument ALL is non-nil, articles marked are also applied to.
If FIELD is an empty string (or nil), entire article body is searched for.
COMMAND must be a lisp expression or a string representing a key sequence."
;; We don't want to change current point nor window configuration.
(let ((old-buffer (current-buffer)))
(save-excursion
(save-window-excursion
;; Selected window must be summary buffer to execute keyboard
;; macros correctly. See command_loop_1.
(switch-to-buffer gnus-summary-buffer 'norecord)
(goto-char (point-min)) ;From the beginning.
(let ((kill-list regexp)
(date (current-time-string))
(command (or exe-command '(gnus-summary-mark-as-read
nil gnus-kill-file-mark)))
kill kdate prev)
(if (listp kill-list)
;; It is a list.
(if (not (consp (cdr kill-list)))
;; It's of the form (regexp . date).
(if (zerop (gnus-execute field (car kill-list)
command nil (not all)))
(if (> (gnus-days-between date (cdr kill-list))
gnus-kill-expiry-days)
(setq regexp nil))
(setcdr kill-list date))
(while (setq kill (car kill-list))
(if (consp kill)
;; It's a temporary kill.
(progn
(setq kdate (cdr kill))
(if (zerop (gnus-execute
field (car kill) command nil (not all)))
(if (> (gnus-days-between date kdate)
gnus-kill-expiry-days)
;; Time limit has been exceeded, so we
;; remove the match.
(if prev
(setcdr prev (cdr kill-list))
(setq regexp (cdr regexp))))
;; Successful kill. Set the date to today.
(setcdr kill date)))
;; It's a permanent kill.
(gnus-execute field kill command nil (not all)))
(setq prev kill-list)
(setq kill-list (cdr kill-list))))
(gnus-execute field kill-list command nil (not all))))))
(switch-to-buffer old-buffer)
(if (and (eq major-mode 'gnus-kill-file-mode) regexp (not silent))
(gnus-pp-gnus-kill
(nconc (list 'gnus-kill field
(if (consp regexp) (list 'quote regexp) regexp))
(if (or exe-command all) (list (list 'quote exe-command)))
(if all (list t) nil))))))
(defun gnus-pp-gnus-kill (object)
(if (or (not (consp (nth 2 object)))
(not (consp (cdr (nth 2 object))))
(and (eq 'quote (car (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 (cadr (nth 2 object)))
(first t))
(while klist
(insert (if first (progn (setq first nil) "") "\n ")
(prin1-to-string (car klist)))
(setq klist (cdr klist))))
(insert ")")
(and (nth 3 object)
(insert "\n "
(if (and (consp (nth 3 object))
(not (eq 'quote (car (nth 3 object)))))
"'" "")
(prin1-to-string (nth 3 object))))
(and (nth 4 object)
(insert "\n t"))
(insert ")")
(prog1
(buffer-substring (point-min) (point-max))
(kill-buffer (current-buffer))))))
(defun gnus-execute-1 (function regexp form header)
(save-excursion
(let (did-kill)
(if (null header)
nil ;Nothing to do.
(if function
;; Compare with header field.
(let (value)
(and header
(progn
(setq value (funcall function header))
;; Number (Lines:) or symbol must be converted to string.
(or (stringp value)
(setq value (prin1-to-string value)))
(setq did-kill (string-match regexp value)))
(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.
(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
(set-buffer gnus-article-buffer)
(goto-char (point-min))
(setq did-kill (re-search-forward regexp nil t)))
(if (stringp form) ;Keyboard macro.
(execute-kbd-macro form)
(eval form))))))
did-kill)))
(defun gnus-execute (field regexp form &optional backward ignore-marked)
"If FIELD of article header matches REGEXP, execute lisp FORM (or a string).
If FIELD is an empty string (or nil), entire article body is searched for.
If optional 1st argument BACKWARD is non-nil, do backward instead.
If optional 2nd argument IGNORE-MARKED is non-nil, articles which are
marked as read or ticked are ignored."
(save-excursion
(let ((killed-no 0)
function article header)
(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
;; 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-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

View file

@ -1,105 +0,0 @@
;;; gnus-mh.el --- mh-e interface for Gnus
;; Copyright (C) 1994,95,96 Free Software Foundation, Inc.
;; Author: Masanobu UMEDA <umerin@flab.flab.fujitsu.junet>
;; Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
;; Keywords: news
;; 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:
;;; Send mail using mh-e.
;; The following mh-e interface is all cooperative works of
;; tanaka@flab.fujitsu.CO.JP (TANAKA Hiroshi), kawabe@sra.CO.JP
;; (Yoshikatsu Kawabe), and shingu@casund.cpr.canon.co.jp (Toshiaki
;; SHINGU).
;;; Code:
(require 'mh-e)
(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.
If N is a positive number, save the N next articles.
If N is a negative number, save the N previous articles.
If N is nil and any articles have been marked with the process mark,
save those articles instead."
(interactive "P")
(let ((gnus-default-article-saver 'gnus-summary-save-in-folder))
(gnus-summary-save-article arg)))
(defun gnus-summary-save-in-folder (&optional folder)
"Save this article to MH folder (using `rcvstore' in MH library).
Optional argument FOLDER specifies folder name."
;; Thanks to yuki@flab.Fujitsu.JUNET and ohm@kaba.junet.
(mh-find-path)
(let ((folder
(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
(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.
If variable `gnus-use-long-file-name' is nil, it is +News.group.
Otherwise, it is like +news/group."
(or last-folder
(concat "+"
(if gnus-use-long-file-name
(gnus-capitalize-newsgroup newsgroup)
(gnus-newsgroup-directory-form newsgroup)))))
(defun gnus-folder-save-name (newsgroup headers &optional last-folder)
"Generate folder name from NEWSGROUP, HEADERS, and optional LAST-FOLDER.
If variable `gnus-use-long-file-name' is nil, it is +news.group.
Otherwise, it is like +news/group."
(or last-folder
(concat "+"
(if gnus-use-long-file-name
newsgroup
(gnus-newsgroup-directory-form newsgroup)))))
(provide 'gnus-mh)
;;; gnus-mh.el ends here

View file

@ -1,929 +0,0 @@
;;; gnus-msg.el --- mail and post interface for Gnus
;; Copyright (C) 1995,96 Free Software Foundation, Inc.
;; Author: Masanobu UMEDA <umerin@flab.flab.fujitsu.junet>
;; Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
;; Keywords: news
;; This file is part of GNU Emacs.
;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Commentary:
;;; Code:
(require 'gnus)
(require 'gnus-ems)
(require 'message)
(eval-when-compile (require 'cl))
;; Added by Sudish Joseph <joseph@cis.ohio-state.edu>.
(defvar gnus-post-method nil
"*Preferred method for posting USENET news.
If this variable is nil, Gnus will use the current method to decide
which method to use when posting. If it is non-nil, it will override
the current method. This method will not be used in mail groups and
the like, only in \"real\" newsgroups.
The value must be a valid method as discussed in the documentation of
`gnus-select-method'. It can also be a list of methods. If that is
the case, the user will be queried for what select method to use when
posting.")
(defvar gnus-outgoing-message-group nil
"*All outgoing messages will be put in this group.
If you want to store all your outgoing mail and articles in the group
\"nnml:archive\", you set this variable to that value. This variable
can also be a list of group names.
If you want to have greater control over what group to put each
message in, you can set this variable to a function that checks the
current newsgroup name and then returns a suitable group name (or list
of names).")
(defvar gnus-mailing-list-groups nil
"*Regexp matching groups that are really mailing lists.
This is useful when you're reading a mailing list that has been
gatewayed to a newsgroup, and you want to followup to an article in
the group.")
(defvar gnus-sent-message-ids-file
(nnheader-concat gnus-directory "Sent-Message-IDs")
"File where Gnus saves a cache of sent message ids.")
(defvar gnus-sent-message-ids-length 1000
"The number of sent Message-IDs to save.")
;;; Internal variables.
(defvar gnus-message-buffer "*Mail Gnus*")
(defvar gnus-article-copy nil)
(defvar gnus-last-posting-server nil)
(eval-and-compile
(autoload 'gnus-uu-post-news "gnus-uu" nil t)
(autoload 'news-setup "rnewspost")
(autoload 'news-reply-mode "rnewspost")
(autoload 'rmail-dont-reply-to "mail-utils")
(autoload 'rmail-output "rmailout"))
;;;
;;; Gnus Posting Functions
;;;
(gnus-define-keys
(gnus-summary-send-map "S" gnus-summary-mode-map)
"p" gnus-summary-post-news
"f" gnus-summary-followup
"F" gnus-summary-followup-with-original
"c" gnus-summary-cancel-article
"s" gnus-summary-supersede-article
"r" gnus-summary-reply
"R" gnus-summary-reply-with-original
"m" gnus-summary-mail-other-window
"u" gnus-uu-post-news
"om" gnus-summary-mail-forward
"op" gnus-summary-post-forward
"Om" gnus-uu-digest-mail-forward
"Op" gnus-uu-digest-post-forward)
(gnus-define-keys
(gnus-send-bounce-map "D" gnus-summary-send-map)
"b" gnus-summary-resend-bounced-mail
; "c" gnus-summary-send-draft
"r" gnus-summary-resend-message)
;;; Internal functions.
(defvar gnus-article-reply nil)
(defmacro gnus-setup-message (config &rest forms)
(let ((winconf (make-symbol "winconf"))
(buffer (make-symbol "buffer"))
(article (make-symbol "article")))
`(let ((,winconf (current-window-configuration))
(,buffer (current-buffer))
(,article (and gnus-article-reply (gnus-summary-article-number)))
(message-header-setup-hook
(copy-sequence message-header-setup-hook)))
(add-hook 'message-header-setup-hook 'gnus-inews-insert-gcc)
(add-hook 'message-header-setup-hook 'gnus-inews-insert-archive-gcc)
,@forms
(gnus-inews-add-send-actions ,winconf ,buffer ,article)
(setq gnus-message-buffer (current-buffer))
(gnus-configure-windows ,config t))))
(defun gnus-inews-add-send-actions (winconf buffer article)
(gnus-make-local-hook 'message-sent-hook)
(gnus-add-hook 'message-sent-hook 'gnus-inews-do-gcc nil t)
(setq message-post-method
`(lambda (arg)
(gnus-post-method arg ,gnus-newsgroup-name)))
(setq message-newsreader (setq message-mailer (gnus-extended-version)))
(message-add-action
`(set-window-configuration ,winconf) 'exit 'postpone 'kill)
(message-add-action
`(when (buffer-name ,buffer)
(save-excursion
(set-buffer ,buffer)
,(when article
`(gnus-summary-mark-article-as-replied ,article))))
'send))
(put 'gnus-setup-message 'lisp-indent-function 1)
(put 'gnus-setup-message 'lisp-indent-hook 1)
(put 'gnus-setup-message 'edebug-form-spec '(form body))
;;; Post news commands of Gnus group mode and summary mode
(defun gnus-group-mail ()
"Start composing a mail."
(interactive)
(gnus-setup-message 'message
(message-mail)))
(defun gnus-group-post-news (&optional arg)
"Start composing a news message.
If ARG, post to the group under point.
If ARG is 1, prompt for a group name."
(interactive "P")
;; Bind this variable here to make message mode hooks
;; work ok.
(let ((gnus-newsgroup-name
(if arg
(if (= 1 (prefix-numeric-value arg))
(completing-read "Newsgroup: " gnus-active-hashtb nil
(gnus-read-active-file-p))
(gnus-group-group-name))
"")))
(gnus-post-news 'post gnus-newsgroup-name)))
(defun gnus-summary-post-news ()
"Start composing a news message."
(interactive)
(gnus-set-global-variables)
(gnus-post-news 'post gnus-newsgroup-name))
(defun gnus-summary-followup (yank &optional force-news)
"Compose a followup to an article.
If prefix argument YANK is non-nil, original article is yanked automatically."
(interactive
(list (and current-prefix-arg
(gnus-summary-work-articles 1))))
(gnus-set-global-variables)
(when yank
(gnus-summary-goto-subject (car yank)))
(save-window-excursion
(gnus-summary-select-article))
(let ((headers (gnus-summary-article-header (gnus-summary-article-number)))
(gnus-newsgroup-name gnus-newsgroup-name))
;; Send a followup.
(gnus-post-news nil gnus-newsgroup-name
headers gnus-article-buffer
yank nil force-news)))
(defun gnus-summary-followup-with-original (n &optional force-news)
"Compose a followup to an article and include the original article."
(interactive "P")
(gnus-summary-followup (gnus-summary-work-articles n) force-news))
(defun gnus-inews-yank-articles (articles)
(let (beg article)
(while (setq article (pop articles))
(save-window-excursion
(set-buffer gnus-summary-buffer)
(gnus-summary-select-article nil nil nil article)
(gnus-summary-remove-process-mark article))
(gnus-copy-article-buffer)
(let ((message-reply-buffer gnus-article-copy)
(message-reply-headers gnus-current-headers))
(message-yank-original)
(setq beg (or beg (mark t))))
(when articles (insert "\n")))
(push-mark)
(goto-char beg)))
(defun gnus-summary-cancel-article (n)
"Cancel an article you posted."
(interactive "P")
(gnus-set-global-variables)
(let ((articles (gnus-summary-work-articles n))
(message-post-method
`(lambda (arg)
(gnus-post-method nil ,gnus-newsgroup-name)))
article)
(while (setq article (pop articles))
(when (gnus-summary-select-article t nil nil article)
(when (gnus-eval-in-buffer-window
gnus-original-article-buffer (message-cancel-news))
(gnus-summary-mark-as-read article gnus-canceled-mark)
(gnus-cache-remove-article 1))
(gnus-article-hide-headers-if-wanted))
(gnus-summary-remove-process-mark article))))
(defun gnus-summary-supersede-article ()
"Compose an article that will supersede a previous article.
This is done simply by taking the old article and adding a Supersedes
header line with the old Message-ID."
(interactive)
(gnus-set-global-variables)
(let ((article (gnus-summary-article-number)))
(gnus-setup-message 'reply-yank
(gnus-summary-select-article t)
(set-buffer gnus-original-article-buffer)
(message-supersede)
(push
`((lambda ()
(gnus-cache-possibly-remove-article ,article nil nil nil t)))
message-send-actions))))
(defun gnus-copy-article-buffer (&optional article-buffer)
;; make a copy of the article buffer with all text properties removed
;; this copy is in the buffer gnus-article-copy.
;; if ARTICLE-BUFFER is nil, gnus-article-buffer is used
;; this buffer should be passed to all mail/news reply/post routines.
(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))
end beg contents)
(when (and (get-buffer article-buffer)
(buffer-name (get-buffer article-buffer)))
(save-excursion
(set-buffer article-buffer)
(save-restriction
(widen)
(setq contents (format "%s" (buffer-string)))
(set-buffer gnus-original-article-buffer)
(goto-char (point-min))
(while (looking-at message-unix-mail-delimiter)
(forward-line 1))
(setq beg (point))
(setq end (or (search-forward "\n\n" nil t) (point)))
(set-buffer gnus-article-copy)
(erase-buffer)
(insert contents)
(delete-region (goto-char (point-min))
(or (search-forward "\n\n" nil t) (point)))
(insert-buffer-substring gnus-original-article-buffer beg end)))
gnus-article-copy)))
(defun gnus-post-news (post &optional group header article-buffer yank subject
force-news)
(when article-buffer
(gnus-copy-article-buffer))
(let ((gnus-article-reply article-buffer))
(gnus-setup-message (cond (yank 'reply-yank)
(article-buffer 'reply)
(t 'message))
(let* ((group (or group gnus-newsgroup-name))
(pgroup group)
to-address to-group mailing-list to-list)
(when group
(setq to-address (gnus-group-get-parameter group 'to-address)
to-group (gnus-group-get-parameter group 'to-group)
to-list (gnus-group-get-parameter group 'to-list)
mailing-list (when gnus-mailing-list-groups
(string-match gnus-mailing-list-groups group))
group (gnus-group-real-name group)))
(if (or (and to-group
(gnus-news-group-p to-group))
force-news
(and (gnus-news-group-p
(or pgroup gnus-newsgroup-name)
(if header (mail-header-number header)
gnus-current-article))
(not mailing-list)
(not to-list)
(not to-address)))
;; This is news.
(if post
(message-news (or to-group group))
(set-buffer gnus-article-copy)
(message-followup))
;; The is mail.
(if post
(progn
(message-mail (or to-address to-list))
;; Arrange for mail groups that have no `to-address' to
;; get that when the user sends off the mail.
(push (list 'gnus-inews-add-to-address group)
message-send-actions))
(set-buffer gnus-article-copy)
(message-wide-reply to-address)))
(when yank
(gnus-inews-yank-articles yank))))))
(defun gnus-post-method (arg group &optional silent)
"Return the posting method based on GROUP and ARG.
If SILENT, don't prompt the user."
(let ((group-method (gnus-find-method-for-group group)))
(cond
;; If the group-method is nil (which shouldn't happen) we use
;; the default method.
((null arg)
(or gnus-post-method gnus-select-method message-post-method))
;; We want this group's method.
((and arg (not (eq arg 0)))
group-method)
;; We query the user for a post method.
((or arg
(and gnus-post-method
(listp (car gnus-post-method))))
(let* ((methods
;; Collect all methods we know about.
(append
(when gnus-post-method
(if (listp (car gnus-post-method))
gnus-post-method
(list gnus-post-method)))
gnus-secondary-select-methods
(list gnus-select-method)
(list group-method)))
method-alist post-methods method)
;; Weed out all mail methods.
(while methods
(setq method (gnus-server-get-method "" (pop methods)))
(when (or (gnus-method-option-p method 'post)
(gnus-method-option-p method 'post-mail))
(push method post-methods)))
;; Create a name-method alist.
(setq method-alist
(mapcar
(lambda (m)
(list (concat (cadr m) " (" (symbol-name (car m)) ")") m))
post-methods))
;; Query the user.
(cadr
(assoc
(setq gnus-last-posting-server
(if (and silent
gnus-last-posting-server)
;; Just use the last value.
gnus-last-posting-server
(completing-read
"Posting method: " method-alist nil t
(cons (or gnus-last-posting-server "") 0))))
method-alist))))
;; Override normal method.
((and gnus-post-method
(or (gnus-method-option-p group-method 'post)
(gnus-method-option-p group-method 'post-mail)))
gnus-post-method)
;; Perhaps this is a mail group?
((and (not (gnus-member-of-valid 'post group))
(not (gnus-method-option-p group-method 'post-mail)))
group-method)
;; Use the normal select method.
(t gnus-select-method))))
(defun gnus-inews-narrow-to-headers ()
(widen)
(narrow-to-region
(goto-char (point-min))
(or (and (re-search-forward
(concat "^" (regexp-quote mail-header-separator) "$") nil t)
(match-beginning 0))
(point-max)))
(goto-char (point-min)))
;;;
;;; Check whether the message has been sent already.
;;;
(defvar gnus-inews-sent-ids nil)
(defun gnus-inews-reject-message ()
"Check whether this message has already been sent."
(when gnus-sent-message-ids-file
(let ((message-id (save-restriction (gnus-inews-narrow-to-headers)
(mail-fetch-field "message-id")))
end)
(when message-id
(unless gnus-inews-sent-ids
(condition-case ()
(load t t t)
(error nil)))
(if (member message-id gnus-inews-sent-ids)
;; Reject this message.
(not (gnus-yes-or-no-p
(format "Message %s already sent. Send anyway? "
message-id)))
(push message-id gnus-inews-sent-ids)
;; Chop off the last Message-IDs.
(when (setq end (nthcdr gnus-sent-message-ids-length
gnus-inews-sent-ids))
(setcdr end nil))
(nnheader-temp-write gnus-sent-message-ids-file
(prin1 `(setq gnus-inews-sent-ids ',gnus-inews-sent-ids)
(current-buffer)))
nil)))))
;; Dummy to avoid byte-compile warning.
(defvar nnspool-rejected-article-hook)
;;; Since the X-Newsreader/X-Mailer are ``vanity'' headers, they might
;;; as well include the Emacs version as well.
;;; The following function works with later GNU Emacs, and XEmacs.
(defun gnus-extended-version ()
"Stringified Gnus version and Emacs version"
(interactive)
(concat
gnus-version
"/"
(cond
((string-match "^\\([0-9]+\\.[0-9]+\\)\\.[.0-9]+$" emacs-version)
(concat "Emacs " (substring emacs-version
(match-beginning 1)
(match-end 1))))
((string-match "\\([A-Z]*[Mm][Aa][Cc][Ss]\\)" emacs-version)
(concat (substring emacs-version
(match-beginning 1)
(match-end 1))
(format " %d.%d" emacs-major-version emacs-minor-version)))
(t emacs-version))))
;; Written by "Mr. Per Persson" <pp@solace.mh.se>.
(defun gnus-inews-insert-mime-headers ()
(goto-char (point-min))
(let ((mail-header-separator
(progn
(goto-char (point-min))
(if (and (search-forward (concat "\n" mail-header-separator "\n")
nil t)
(not (search-backward "\n\n" nil t)))
mail-header-separator
""))))
(or (mail-position-on-field "Mime-Version")
(insert "1.0")
(cond ((save-restriction
(widen)
(goto-char (point-min))
(re-search-forward "[\200-\377]" nil t))
(or (mail-position-on-field "Content-Type")
(insert "text/plain; charset=ISO-8859-1"))
(or (mail-position-on-field "Content-Transfer-Encoding")
(insert "8bit")))
(t (or (mail-position-on-field "Content-Type")
(insert "text/plain; charset=US-ASCII"))
(or (mail-position-on-field "Content-Transfer-Encoding")
(insert "7bit")))))))
;;;
;;; Gnus Mail Functions
;;;
;;; Mail reply commands of Gnus summary mode
(defun gnus-summary-reply (&optional yank)
"Reply mail to news author.
If prefix argument YANK is non-nil, original article is yanked automatically."
(interactive
(list (and current-prefix-arg
(gnus-summary-work-articles 1))))
;; Bug fix by jbw@bigbird.bu.edu (Joe Wells)
;; Stripping headers should be specified with mail-yank-ignored-headers.
(gnus-set-global-variables)
(when yank
(gnus-summary-goto-subject (car yank)))
(let ((gnus-article-reply t))
(gnus-setup-message (if yank 'reply-yank 'reply)
(gnus-summary-select-article)
(set-buffer (gnus-copy-article-buffer))
(message-reply nil nil (gnus-group-get-parameter
gnus-newsgroup-name 'broken-reply-to))
(when yank
(gnus-inews-yank-articles yank)))))
(defun gnus-summary-reply-with-original (n)
"Reply mail to news author with original article."
(interactive "P")
(gnus-summary-reply (gnus-summary-work-articles n)))
(defun gnus-summary-mail-forward (&optional post)
"Forward the current message to another user."
(interactive "P")
(gnus-set-global-variables)
(gnus-setup-message 'forward
(gnus-summary-select-article)
(set-buffer gnus-original-article-buffer)
(message-forward post)))
(defun gnus-summary-resend-message (address)
"Resend the current article to ADDRESS."
(interactive "sResend message to: ")
(gnus-summary-select-article)
(save-excursion
(set-buffer gnus-original-article-buffer)
(message-resend address)))
(defun gnus-summary-post-forward ()
"Forward the current article to a newsgroup."
(interactive)
(gnus-summary-mail-forward t))
(defvar gnus-nastygram-message
"The following article was inappropriately posted to %s.\n"
"Format string to insert in nastygrams.
The current group name will be inserted at \"%s\".")
(defun gnus-summary-mail-nastygram (n)
"Send a nastygram to the author of the current article."
(interactive "P")
(if (or gnus-expert-user
(gnus-y-or-n-p
"Really send a nastygram to the author of the current article? "))
(let ((group gnus-newsgroup-name))
(gnus-summary-reply-with-original n)
(set-buffer gnus-message-buffer)
(insert (format gnus-nastygram-message group))
(message-send-and-exit))))
(defun gnus-summary-mail-other-window ()
"Compose mail in other window."
(interactive)
(gnus-setup-message 'message
(message-mail)))
(defun gnus-mail-parse-comma-list ()
(let (accumulated
beg)
(skip-chars-forward " ")
(while (not (eobp))
(setq beg (point))
(skip-chars-forward "^,")
(while (zerop
(save-excursion
(save-restriction
(let ((i 0))
(narrow-to-region beg (point))
(goto-char beg)
(logand (progn
(while (search-forward "\"" nil t)
(incf i))
(if (zerop i) 2 i)) 2)))))
(skip-chars-forward ",")
(skip-chars-forward "^,"))
(skip-chars-backward " ")
(setq accumulated
(cons (buffer-substring beg (point))
accumulated))
(skip-chars-forward "^,")
(skip-chars-forward ", "))
accumulated))
(defun gnus-mail-yank-original ()
(interactive)
(save-excursion
(mail-yank-original nil))
(or mail-yank-hooks mail-citation-hook
(run-hooks 'news-reply-header-hook)))
(defun gnus-inews-add-to-address (group)
(let ((to-address (mail-fetch-field "to")))
(when (and to-address
(gnus-alive-p))
;; This mail group doesn't have a `to-list', so we add one
;; here. Magic!
(gnus-group-add-parameter group (cons 'to-list to-address)))))
(defun gnus-put-message ()
"Put the current message in some group and return to Gnus."
(interactive)
(let ((reply gnus-article-reply)
(winconf gnus-prev-winconf)
(group gnus-newsgroup-name))
(or (and group (not (gnus-group-read-only-p group)))
(setq group (read-string "Put in group: " nil
(gnus-writable-groups))))
(and (gnus-gethash group gnus-newsrc-hashtb)
(error "No such group: %s" group))
(save-excursion
(save-restriction
(widen)
(gnus-inews-narrow-to-headers)
(let (gnus-deletable-headers)
(if (message-news-p)
(message-generate-headers message-required-news-headers)
(message-generate-headers message-required-mail-headers)))
(goto-char (point-max))
(insert "Gcc: " group "\n")
(widen)))
(gnus-inews-do-gcc)
(if (get-buffer gnus-group-buffer)
(progn
(if (gnus-buffer-exists-p (car-safe reply))
(progn
(set-buffer (car reply))
(and (cdr reply)
(gnus-summary-mark-article-as-replied
(cdr reply)))))
(and winconf (set-window-configuration winconf))))))
(defun gnus-article-mail (yank)
"Send a reply to the address near point.
If YANK is non-nil, include the original article."
(interactive "P")
(let ((address
(buffer-substring
(save-excursion (re-search-backward "[ \t\n]" nil t) (1+ (point)))
(save-excursion (re-search-forward "[ \t\n]" nil t) (1- (point))))))
(when address
(message-reply address)
(when yank
(gnus-inews-yank-articles (list (cdr gnus-article-current)))))))
(defun gnus-bug ()
"Send a bug report to the Gnus maintainers."
(interactive)
(gnus-setup-message 'bug
(delete-other-windows)
(switch-to-buffer "*Gnus Help Bug*")
(erase-buffer)
(insert gnus-bug-message)
(goto-char (point-min))
(message-pop-to-buffer "*Gnus Bug*")
(message-setup `((To . ,gnus-maintainer) (Subject . "")))
(push `(gnus-bug-kill-buffer) message-send-actions)
(goto-char (point-min))
(re-search-forward (concat "^" (regexp-quote mail-header-separator) "$"))
(forward-line 1)
(insert (gnus-version) "\n")
(insert (emacs-version))
(insert "\n\n\n\n\n")
(gnus-debug)
(goto-char (point-min))
(search-forward "Subject: " nil t)
(message "")))
(defun gnus-bug-kill-buffer ()
(and (get-buffer "*Gnus Help Bug*")
(kill-buffer "*Gnus Help Bug*")))
(defun gnus-debug ()
"Attemps to go through the Gnus source file and report what variables have been changed.
The source file has to be in the Emacs load path."
(interactive)
(let ((files '("gnus.el" "gnus-msg.el" "gnus-score.el" "nnmail.el"
"message.el"))
file dirs expr olist sym)
(gnus-message 4 "Please wait while we snoop your variables...")
(sit-for 0)
(save-excursion
(set-buffer (get-buffer-create " *gnus bug info*"))
(buffer-disable-undo (current-buffer))
(while files
(erase-buffer)
(setq dirs load-path)
(while dirs
(if (or (not (car dirs))
(not (stringp (car dirs)))
(not (file-exists-p
(setq file (concat (file-name-as-directory
(car dirs)) (car files))))))
(setq dirs (cdr dirs))
(setq dirs nil)
(insert-file-contents file)
(goto-char (point-min))
(if (not (re-search-forward "^;;* *Internal variables" nil t))
(gnus-message 4 "Malformed sources in file %s" file)
(narrow-to-region (point-min) (point))
(goto-char (point-min))
(while (setq expr (condition-case ()
(read (current-buffer)) (error nil)))
(condition-case ()
(and (eq (car expr) 'defvar)
(stringp (nth 3 expr))
(or (not (boundp (nth 1 expr)))
(not (equal (eval (nth 2 expr))
(symbol-value (nth 1 expr)))))
(setq olist (cons (nth 1 expr) olist)))
(error nil))))))
(setq files (cdr files)))
(kill-buffer (current-buffer)))
(when (setq olist (nreverse olist))
(insert "------------------ Environment follows ------------------\n\n"))
(while olist
(if (boundp (car olist))
(condition-case ()
(pp `(setq ,(car olist)
,(if (or (consp (setq sym (symbol-value (car olist))))
(and (symbolp sym)
(not (or (eq sym nil)
(eq sym t)))))
(list 'quote (symbol-value (car olist)))
(symbol-value (car olist))))
(current-buffer))
(error
(format "(setq %s 'whatever)\n" (car olist))))
(insert ";; (makeunbound '" (symbol-name (car olist)) ")\n"))
(setq olist (cdr olist)))
(insert "\n\n")
;; Remove any null chars - they seem to cause trouble for some
;; mailers. (Byte-compiled output from the stuff above.)
(goto-char (point-min))
(while (re-search-forward "[\000\200]" nil t)
(replace-match "" t t))))
;;; Treatment of rejected articles.
;;; Bounced mail.
(defun gnus-summary-resend-bounced-mail (&optional fetch)
"Re-mail the current message.
This only makes sense if the current message is a bounce message than
contains some mail you have written which has been bounced back to
you.
If FETCH, try to fetch the article that this is a reply to, if indeed
this is a reply."
(interactive "P")
(gnus-summary-select-article t)
(set-buffer gnus-original-article-buffer)
(gnus-setup-message 'compose-bounce
(let* ((references (mail-fetch-field "references"))
(parent (and references (gnus-parent-id references))))
(message-bounce)
;; If there are references, we fetch the article we answered to.
(and fetch parent
(gnus-summary-refer-article parent)
(gnus-summary-show-all-headers)))))
;;; Gcc handling.
;; Do Gcc handling, which copied the message over to some group.
(defun gnus-inews-do-gcc (&optional gcc)
(when (gnus-alive-p)
(save-excursion
(save-restriction
(message-narrow-to-headers)
(let ((gcc (or gcc (mail-fetch-field "gcc" nil t)))
(cur (current-buffer))
groups group method)
(when gcc
(message-remove-header "gcc")
(widen)
(setq groups (message-tokenize-header gcc " ,"))
;; Copy the article over to some group(s).
(while (setq group (pop groups))
(gnus-check-server
(setq method
(cond ((and (null (gnus-get-info group))
(eq (car gnus-message-archive-method)
(car
(gnus-server-to-method
(gnus-group-method group)))))
;; If the group doesn't exist, we assume
;; it's an archive group...
gnus-message-archive-method)
;; Use the method.
((gnus-info-method (gnus-get-info group))
(gnus-info-method (gnus-get-info group)))
;; Find the method.
(t (gnus-group-method group)))))
(gnus-check-server method)
(unless (gnus-request-group group t method)
(gnus-request-create-group group method))
(save-excursion
(nnheader-set-temp-buffer " *acc*")
(insert-buffer-substring cur)
(goto-char (point-min))
(when (re-search-forward
(concat "^" (regexp-quote mail-header-separator) "$")
nil t)
(replace-match "" t t ))
(unless (gnus-request-accept-article group method t)
(gnus-message 1 "Couldn't store article in group %s: %s"
group (gnus-status-message method))
(sit-for 2))
(kill-buffer (current-buffer))))))))))
(defun gnus-inews-insert-gcc ()
"Insert Gcc headers based on `gnus-outgoing-message-group'."
(save-excursion
(save-restriction
(gnus-inews-narrow-to-headers)
(let* ((group gnus-outgoing-message-group)
(gcc (cond
((gnus-functionp group)
(funcall group))
((or (stringp group) (list group))
group))))
(when gcc
(insert "Gcc: "
(if (stringp gcc) gcc
(mapconcat 'identity gcc " "))
"\n"))))))
(defun gnus-inews-insert-archive-gcc (&optional group)
"Insert the Gcc to say where the article is to be archived."
(let* ((var gnus-message-archive-group)
(group (or group gnus-newsgroup-name ""))
result
(groups
(cond
((null gnus-message-archive-method)
;; Ignore.
nil)
((stringp var)
;; Just a single group.
(list var))
((null var)
;; We don't want this.
nil)
((and (listp var) (stringp (car var)))
;; A list of groups.
var)
((gnus-functionp var)
;; A function.
(funcall var group))
(t
;; An alist of regexps/functions/forms.
(while (and var
(not
(setq result
(cond
((stringp (caar var))
;; Regexp.
(when (string-match (caar var) group)
(cdar var)))
((gnus-functionp (car var))
;; Function.
(funcall (car var) group))
(t
(eval (car var)))))))
(setq var (cdr var)))
result)))
name)
(when groups
(when (stringp groups)
(setq groups (list groups)))
(save-excursion
(save-restriction
(gnus-inews-narrow-to-headers)
(goto-char (point-max))
(insert "Gcc: ")
(while (setq name (pop groups))
(insert (if (string-match ":" name)
name
(gnus-group-prefixed-name
name gnus-message-archive-method)))
(if groups (insert " ")))
(insert "\n"))))))
(defun gnus-summary-send-draft ()
"Enter a mail/post buffer to edit and send the draft."
(interactive)
(gnus-set-global-variables)
(let (buf)
(if (not (setq buf (gnus-request-restore-buffer
(gnus-summary-article-number) gnus-newsgroup-name)))
(error "Couldn't restore the article")
(switch-to-buffer buf)
(when (eq major-mode 'news-reply-mode)
(local-set-key "\C-c\C-c" 'gnus-inews-news))
;; Insert the separator.
(goto-char (point-min))
(search-forward "\n\n")
(forward-char -1)
(insert mail-header-separator)
;; Configure windows.
(let ((gnus-draft-buffer (current-buffer)))
(gnus-configure-windows 'draft t)
(goto-char (point))))))
(gnus-add-shutdown 'gnus-inews-close 'gnus)
(defun gnus-inews-close ()
(setq gnus-inews-sent-ids nil))
;;; Allow redefinition of functions.
(gnus-ems-redefine)
(provide 'gnus-msg)
;;; gnus-msg.el ends here

View file

@ -1,247 +0,0 @@
;;; gnus-nocem.el --- NoCeM pseudo-cancellation treatment
;; Copyright (C) 1995,96 Free Software Foundation, Inc.
;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
;; Keywords: news
;; This file is part of GNU Emacs.
;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Commentary:
;;; Code:
(require 'gnus)
(require 'nnmail)
(eval-when-compile (require 'cl))
(defvar gnus-nocem-groups
'("alt.nocem.misc" "news.admin.net-abuse.announce")
"*List of groups that will be searched for NoCeM messages.")
(defvar gnus-nocem-issuers
'("Automoose-1" ; The CancelMoose[tm] on autopilot.
"clewis@ferret.ocunix.on.ca;" ; Chris Lewis -- Canadian angel & despammer.
"jem@xpat.com;" ; John Milburn -- despammer in Korea.
"red@redpoll.mrfs.oh.us (Richard E. Depew)" ; Spew/bincancel guy.
)
"*List of NoCeM issuers to pay attention to.")
(defvar gnus-nocem-directory
(concat (file-name-as-directory gnus-article-save-directory) "NoCeM/")
"*Directory where NoCeM files will be stored.")
(defvar gnus-nocem-expiry-wait 15
"*Number of days to keep NoCeM headers in the cache.")
(defvar gnus-nocem-verifyer nil
"*Function called to verify that the NoCeM message is valid.
One likely value is `mc-verify'. If the function in this variable
isn't bound, the message will be used unconditionally.")
;;; Internal variables
(defvar gnus-nocem-active nil)
(defvar gnus-nocem-alist nil)
(defvar gnus-nocem-touched-alist nil)
(defvar gnus-nocem-hashtb nil)
;;; Functions
(defun gnus-nocem-active-file ()
(concat (file-name-as-directory gnus-nocem-directory) "active"))
(defun gnus-nocem-cache-file ()
(concat (file-name-as-directory gnus-nocem-directory) "cache"))
(defun gnus-nocem-scan-groups ()
"Scan all NoCeM groups for new NoCeM messages."
(interactive)
(let ((groups gnus-nocem-groups)
group active gactive articles)
(or (file-exists-p gnus-nocem-directory)
(make-directory gnus-nocem-directory t))
;; Load any previous NoCeM headers.
(gnus-nocem-load-cache)
;; Read the active file if it hasn't been read yet.
(and (file-exists-p (gnus-nocem-active-file))
(not gnus-nocem-active)
(condition-case ()
(load (gnus-nocem-active-file) t t t)
(error nil)))
;; Go through all groups and see whether new articles have
;; arrived.
(while (setq group (pop groups))
(if (not (setq gactive (gnus-activate-group group)))
() ; This group doesn't exist.
(setq active (nth 1 (assoc group gnus-nocem-active)))
(when (and (not (< (cdr gactive) (car gactive))) ; Empty group.
(or (not active)
(< (cdr active) (cdr gactive))))
;; Ok, there are new articles in this group, se we fetch the
;; headers.
(save-excursion
(let ((dependencies (make-vector 10 nil))
(buffer (nnheader-set-temp-buffer " *Gnus NoCeM*"))
headers)
(setq headers
(if (eq 'nov
(gnus-retrieve-headers
(setq articles
(gnus-uncompress-range
(cons
(if active (1+ (cdr active))
(car gactive))
(cdr gactive))))
group))
(gnus-get-newsgroup-headers-xover
articles nil dependencies)
(gnus-get-newsgroup-headers dependencies)))
(while headers
;; We take a closer look on all articles that have
;; "@@NCM" in the subject.
(when (string-match "@@NCM"
(mail-header-subject (car headers)))
(gnus-nocem-check-article group (car headers)))
(setq headers (cdr headers)))
(kill-buffer (current-buffer)))))
(setq gnus-nocem-active
(cons (list group gactive)
(delq (assoc group gnus-nocem-active)
gnus-nocem-active)))))
;; Save the results, if any.
(gnus-nocem-save-cache)
(gnus-nocem-save-active)))
(defun gnus-nocem-check-article (group header)
"Check whether the current article is an NCM article and that we want it."
;; Get the article.
(gnus-message 7 "Checking article %d in %s for NoCeM..."
(mail-header-number header) group)
(let ((date (mail-header-date header))
issuer b e)
(when (or (not date)
(nnmail-time-less
(nnmail-time-since (nnmail-date-to-time date))
(nnmail-days-to-time gnus-nocem-expiry-wait)))
(gnus-request-article-this-buffer (mail-header-number header) group)
(goto-char (point-min))
;; The article has to have proper NoCeM headers.
(when (and (setq b (search-forward "\n@@BEGIN NCM HEADERS\n" nil t))
(setq e (search-forward "\n@@BEGIN NCM BODY\n" nil t)))
;; We get the name of the issuer.
(narrow-to-region b e)
(setq issuer (mail-fetch-field "issuer"))
(and (member issuer gnus-nocem-issuers) ; We like her...
(gnus-nocem-verify-issuer issuer) ; She is who she says she is..
(gnus-nocem-enter-article)))))) ; We gobble the message.
(defun gnus-nocem-verify-issuer (person)
"Verify using PGP that the canceler is who she says she is."
(widen)
(if (fboundp gnus-nocem-verifyer)
(funcall gnus-nocem-verifyer)
;; If we don't have MailCrypt, then we use the message anyway.
t))
(defun gnus-nocem-enter-article ()
"Enter the current article into the NoCeM cache."
(goto-char (point-min))
(let ((b (search-forward "\n@@BEGIN NCM BODY\n" nil t))
(e (search-forward "\n@@END NCM BODY\n" nil t))
(buf (current-buffer))
ncm id)
(when (and b e)
(narrow-to-region b (1+ (match-beginning 0)))
(goto-char (point-min))
(while (search-forward "\t" nil t)
(when (condition-case nil
(boundp (let ((obarray gnus-active-hashtb)) (read buf)))
(error nil))
(beginning-of-line)
(while (= (following-char) ?\t)
(forward-line -1))
(setq id (buffer-substring (point) (1- (search-forward "\t"))))
(push id ncm)
(gnus-sethash id t gnus-nocem-hashtb)
(forward-line 1)
(while (= (following-char) ?\t)
(forward-line 1))))
(when ncm
(setq gnus-nocem-touched-alist t)
(push (cons (let ((time (current-time))) (setcdr (cdr time) nil) time)
ncm)
gnus-nocem-alist)))))
(defun gnus-nocem-load-cache ()
"Load the NoCeM cache."
(unless gnus-nocem-alist
;; The buffer doesn't exist, so we create it and load the NoCeM
;; cache.
(when (file-exists-p (gnus-nocem-cache-file))
(load (gnus-nocem-cache-file) t t t)
(gnus-nocem-alist-to-hashtb))))
(defun gnus-nocem-save-cache ()
"Save the NoCeM cache."
(when (and gnus-nocem-alist
gnus-nocem-touched-alist)
(nnheader-temp-write (gnus-nocem-cache-file)
(prin1 `(setq gnus-nocem-alist ',gnus-nocem-alist) (current-buffer)))
(setq gnus-nocem-touched-alist nil)))
(defun gnus-nocem-save-active ()
"Save the NoCeM active file."
(nnheader-temp-write (gnus-nocem-active-file)
(prin1 `(setq gnus-nocem-active ',gnus-nocem-active) (current-buffer))))
(defun gnus-nocem-alist-to-hashtb ()
"Create a hashtable from the Message-IDs we have."
(let* ((alist gnus-nocem-alist)
(pprev (cons nil alist))
(prev pprev)
(expiry (nnmail-days-to-time gnus-nocem-expiry-wait))
entry)
(setq gnus-nocem-hashtb (gnus-make-hashtable (* (length alist) 51)))
(while (setq entry (car alist))
(if (not (nnmail-time-less (nnmail-time-since (car entry)) expiry))
;; This entry has expired, so we remove it.
(setcdr prev (cdr alist))
(setq prev alist)
;; This is ok, so we enter it into the hashtable.
(setq entry (cdr entry))
(while entry
(gnus-sethash (car entry) t gnus-nocem-hashtb)
(setq entry (cdr entry))))
(setq alist (cdr alist)))))
(gnus-add-shutdown 'gnus-nocem-close 'gnus)
(defun gnus-nocem-close ()
"Clear internal NoCeM variables."
(setq gnus-nocem-alist nil
gnus-nocem-hashtb nil
gnus-nocem-active nil
gnus-nocem-touched-alist nil))
(defun gnus-nocem-unwanted-article-p (id)
"Say whether article ID in the current group is wanted."
(gnus-gethash id gnus-nocem-hashtb))
(provide 'gnus-nocem)
;;; gnus-nocem.el ends here

View file

@ -1,654 +0,0 @@
;;; gnus-salt.el --- alternate summary mode interfaces for Gnus
;; Copyright (C) 1996 Free Software Foundation, Inc.
;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
;; This file is part of GNU Emacs.
;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Commentary:
;;; Code:
(require 'gnus)
(eval-when-compile (require 'cl))
;;;
;;; gnus-pick-mode
;;;
(defvar gnus-pick-mode nil
"Minor mode for providing a pick-and-read interface in Gnus summary buffers.")
(defvar gnus-pick-display-summary nil
"*Display summary while reading.")
(defvar gnus-pick-mode-hook nil
"Hook run in summary pick mode buffers.")
;;; Internal variables.
(defvar gnus-pick-mode-map nil)
(unless gnus-pick-mode-map
(setq gnus-pick-mode-map (make-sparse-keymap))
(gnus-define-keys
gnus-pick-mode-map
"t" gnus-uu-mark-thread
"T" gnus-uu-unmark-thread
" " gnus-summary-mark-as-processable
"u" gnus-summary-unmark-as-processable
"U" gnus-summary-unmark-all-processable
"v" gnus-uu-mark-over
"r" gnus-uu-mark-region
"R" gnus-uu-unmark-region
"e" gnus-uu-mark-by-regexp
"E" gnus-uu-mark-by-regexp
"b" gnus-uu-mark-buffer
"B" gnus-uu-unmark-buffer
"\r" gnus-pick-start-reading))
(defun gnus-pick-make-menu-bar ()
(unless (boundp 'gnus-pick-menu)
(easy-menu-define
gnus-pick-menu gnus-pick-mode-map ""
'("Pick"
("Pick"
["Article" gnus-summary-mark-as-processable t]
["Thread" gnus-uu-mark-thread t]
["Region" gnus-uu-mark-region t]
["Regexp" gnus-uu-mark-regexp t]
["Buffer" gnus-uu-mark-buffer t])
("Unpick"
["Article" gnus-summary-unmark-as-processable t]
["Thread" gnus-uu-unmark-thread t]
["Region" gnus-uu-unmark-region t]
["Regexp" gnus-uu-unmark-regexp t]
["Buffer" gnus-uu-unmark-buffer t])
["Start reading" gnus-pick-start-reading t]
["Switch pick mode off" gnus-pick-mode gnus-pick-mode]))))
(defun gnus-pick-mode (&optional arg)
"Minor mode for providing a pick-and-read interface in Gnus summary buffers.
\\{gnus-pick-mode-map}"
(interactive "P")
(when (eq major-mode 'gnus-summary-mode)
(make-local-variable 'gnus-pick-mode)
(setq gnus-pick-mode
(if (null arg) (not gnus-pick-mode)
(> (prefix-numeric-value arg) 0)))
(when gnus-pick-mode
;; Make sure that we don't select any articles upon group entry.
(make-local-variable 'gnus-auto-select-first)
(setq gnus-auto-select-first nil)
;; Set up the menu.
(when (and menu-bar-mode
(gnus-visual-p 'pick-menu 'menu))
(gnus-pick-make-menu-bar))
(unless (assq 'gnus-pick-mode minor-mode-alist)
(push '(gnus-pick-mode " Pick") minor-mode-alist))
(unless (assq 'gnus-pick-mode minor-mode-map-alist)
(push (cons 'gnus-pick-mode gnus-pick-mode-map)
minor-mode-map-alist))
(run-hooks 'gnus-pick-mode-hook))))
(defun gnus-pick-start-reading (&optional catch-up)
"Start reading the picked articles.
If given a prefix, mark all unpicked articles as read."
(interactive "P")
(unless gnus-newsgroup-processable
(error "No articles have been picked"))
(gnus-summary-limit-to-articles nil)
(when catch-up
(gnus-summary-limit-mark-excluded-as-read))
(gnus-summary-first-unread-article)
(gnus-configure-windows (if gnus-pick-display-summary 'article 'pick) t))
;;;
;;; gnus-binary-mode
;;;
(defvar gnus-binary-mode nil
"Minor mode for provind a binary group interface in Gnus summary buffers.")
(defvar gnus-binary-mode-hook nil
"Hook run in summary binary mode buffers.")
(defvar gnus-binary-mode-map nil)
(unless gnus-binary-mode-map
(setq gnus-binary-mode-map (make-sparse-keymap))
(gnus-define-keys
gnus-binary-mode-map
"g" gnus-binary-show-article))
(defun gnus-binary-make-menu-bar ()
(unless (boundp 'gnus-binary-menu)
(easy-menu-define
gnus-binary-menu gnus-binary-mode-map ""
'("Pick"
["Switch binary mode off" gnus-binary-mode t]))))
(defun gnus-binary-mode (&optional arg)
"Minor mode for providing a binary group interface in Gnus summary buffers."
(interactive "P")
(when (eq major-mode 'gnus-summary-mode)
(make-local-variable 'gnus-binary-mode)
(setq gnus-binary-mode
(if (null arg) (not gnus-binary-mode)
(> (prefix-numeric-value arg) 0)))
(when gnus-binary-mode
;; Make sure that we don't select any articles upon group entry.
(make-local-variable 'gnus-auto-select-first)
(setq gnus-auto-select-first nil)
(make-local-variable 'gnus-summary-display-article-function)
(setq gnus-summary-display-article-function 'gnus-binary-display-article)
;; Set up the menu.
(when (and menu-bar-mode
(gnus-visual-p 'binary-menu 'menu))
(gnus-binary-make-menu-bar))
(unless (assq 'gnus-binary-mode minor-mode-alist)
(push '(gnus-binary-mode " Binary") minor-mode-alist))
(unless (assq 'gnus-binary-mode minor-mode-map-alist)
(push (cons 'gnus-binary-mode gnus-binary-mode-map)
minor-mode-map-alist))
(run-hooks 'gnus-binary-mode-hook))))
(defun gnus-binary-display-article (article &optional all-header)
"Run ARTICLE through the binary decode functions."
(when (gnus-summary-goto-subject article)
(let ((gnus-view-pseudos 'automatic))
(gnus-uu-decode-uu))))
(defun gnus-binary-show-article (&optional arg)
"Bypass the binary functions and show the article."
(interactive "P")
(let (gnus-summary-display-article-function)
(gnus-summary-show-article arg)))
;;;
;;; gnus-tree-mode
;;;
(defvar gnus-tree-line-format "%(%[%3,3n%]%)"
"Format of tree elements.")
(defvar gnus-tree-minimize-window t
"If non-nil, minimize the tree buffer window.
If a number, never let the tree buffer grow taller than that number of
lines.")
(defvar gnus-selected-tree-face 'modeline
"*Face used for highlighting selected articles in the thread tree.")
(defvar gnus-tree-brackets '((?\[ . ?\]) (?\( . ?\))
(?\{ . ?\}) (?< . ?>))
"Brackets used in tree nodes.")
(defvar gnus-tree-parent-child-edges '(?- ?\\ ?|)
"Charaters used to connect parents with children.")
(defvar gnus-tree-mode-line-format "Gnus: %%b %S %Z"
"*The format specification for the tree mode line.")
(defvar gnus-generate-tree-function 'gnus-generate-vertical-tree
"*Function for generating a thread tree.
Two predefined functions are available:
`gnus-generate-horizontal-tree' and `gnus-generate-vertical-tree'.")
(defvar gnus-tree-mode-hook nil
"*Hook run in tree mode buffers.")
;;; Internal variables.
(defvar gnus-tree-line-format-alist
`((?n gnus-tmp-name ?s)
(?f gnus-tmp-from ?s)
(?N gnus-tmp-number ?d)
(?\[ gnus-tmp-open-bracket ?c)
(?\] gnus-tmp-close-bracket ?c)
(?s gnus-tmp-subject ?s)))
(defvar gnus-tree-mode-line-format-alist gnus-summary-mode-line-format-alist)
(defvar gnus-tree-mode-line-format-spec nil)
(defvar gnus-tree-line-format-spec nil)
(defvar gnus-tree-node-length nil)
(defvar gnus-selected-tree-overlay nil)
(defvar gnus-tree-displayed-thread nil)
(defvar gnus-tree-mode-map nil)
(put 'gnus-tree-mode 'mode-class 'special)
(unless gnus-tree-mode-map
(setq gnus-tree-mode-map (make-keymap))
(suppress-keymap gnus-tree-mode-map)
(gnus-define-keys
gnus-tree-mode-map
"\r" gnus-tree-select-article
gnus-mouse-2 gnus-tree-pick-article
"\C-?" gnus-tree-read-summary-keys
"\C-c\C-i" gnus-info-find-node)
(substitute-key-definition
'undefined 'gnus-tree-read-summary-keys gnus-tree-mode-map))
(defun gnus-tree-make-menu-bar ()
(unless (boundp 'gnus-tree-menu)
(easy-menu-define
gnus-tree-menu gnus-tree-mode-map ""
'("Tree"
["Select article" gnus-tree-select-article t]))))
(defun gnus-tree-mode ()
"Major mode for displaying thread trees."
(interactive)
(setq gnus-tree-mode-line-format-spec
(gnus-parse-format gnus-tree-mode-line-format
gnus-summary-mode-line-format-alist))
(setq gnus-tree-line-format-spec
(gnus-parse-format gnus-tree-line-format
gnus-tree-line-format-alist t))
(when (and menu-bar-mode
(gnus-visual-p 'tree-menu 'menu))
(gnus-tree-make-menu-bar))
(kill-all-local-variables)
(gnus-simplify-mode-line)
(setq mode-name "Tree")
(setq major-mode 'gnus-tree-mode)
(use-local-map gnus-tree-mode-map)
(buffer-disable-undo (current-buffer))
(setq buffer-read-only t)
(setq truncate-lines t)
(save-excursion
(gnus-set-work-buffer)
(gnus-tree-node-insert (make-mail-header "") nil)
(setq gnus-tree-node-length (1- (point))))
(run-hooks 'gnus-tree-mode-hook))
(defun gnus-tree-read-summary-keys (&optional arg)
"Read a summary buffer key sequence and execute it."
(interactive "P")
(let ((buf (current-buffer))
win)
(gnus-article-read-summary-keys arg nil t)
(when (setq win (get-buffer-window buf))
(select-window win)
(when gnus-selected-tree-overlay
(goto-char (or (gnus-overlay-end gnus-selected-tree-overlay) 1)))
(gnus-tree-minimize))))
(defun gnus-tree-select-article (article)
"Select the article under point, if any."
(interactive (list (gnus-tree-article-number)))
(let ((buf (current-buffer)))
(when article
(save-excursion
(set-buffer gnus-summary-buffer)
(gnus-summary-goto-article article))
(select-window (get-buffer-window buf)))))
(defun gnus-tree-pick-article (e)
"Select the article under the mouse pointer."
(interactive "e")
(mouse-set-point e)
(gnus-tree-select-article (gnus-tree-article-number)))
(defun gnus-tree-article-number ()
(get-text-property (point) 'gnus-number))
(defun gnus-tree-article-region (article)
"Return a cons with BEG and END of the article region."
(let ((pos (text-property-any (point-min) (point-max) 'gnus-number article)))
(when pos
(cons pos (next-single-property-change pos 'gnus-number)))))
(defun gnus-tree-goto-article (article)
(let ((pos (text-property-any (point-min) (point-max) 'gnus-number article)))
(when pos
(goto-char pos))))
(defun gnus-tree-recenter ()
"Center point in the tree window."
(let ((selected (selected-window))
(tree-window (get-buffer-window gnus-tree-buffer t)))
(when tree-window
(select-window tree-window)
(when gnus-selected-tree-overlay
(goto-char (or (gnus-overlay-end gnus-selected-tree-overlay) 1)))
(let* ((top (cond ((< (window-height) 4) 0)
((< (window-height) 7) 1)
(t 2)))
(height (1- (window-height)))
(bottom (save-excursion (goto-char (point-max))
(forward-line (- height))
(point))))
;; 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
tree-window (min bottom (save-excursion
(forward-line (- top)) (point)))))
(select-window selected))))
(defun gnus-get-tree-buffer ()
"Return the tree buffer properly initialized."
(save-excursion
(set-buffer (get-buffer-create gnus-tree-buffer))
(unless (eq major-mode 'gnus-tree-mode)
(gnus-add-current-to-buffer-list)
(gnus-tree-mode))
(current-buffer)))
(defun gnus-tree-minimize ()
(when (and gnus-tree-minimize-window
(not (one-window-p)))
(let ((windows 0)
tot-win-height)
(walk-windows (lambda (window) (incf windows)))
(setq tot-win-height
(- (frame-height)
(* window-min-height (1- windows))
2))
(let* ((window-min-height 2)
(height (count-lines (point-min) (point-max)))
(min (max (1- window-min-height) height))
(tot (if (numberp gnus-tree-minimize-window)
(min gnus-tree-minimize-window min)
min))
(win (get-buffer-window (current-buffer)))
(wh (and win (1- (window-height win)))))
(setq tot (min tot tot-win-height))
(when (and win
(not (eq tot wh)))
(let ((selected (selected-window)))
(select-window win)
(enlarge-window (- tot wh))
(select-window selected)))))))
;;; Generating the tree.
(defun gnus-tree-node-insert (header sparse &optional adopted)
(let* ((dummy (stringp header))
(header (if (vectorp header) header
(progn
(setq header (make-mail-header "*****"))
(mail-header-set-number header 0)
(mail-header-set-lines header 0)
(mail-header-set-chars header 0)
header)))
(gnus-tmp-from (mail-header-from header))
(gnus-tmp-subject (mail-header-subject header))
(gnus-tmp-number (mail-header-number header))
(gnus-tmp-name
(cond
((string-match "(.+)" gnus-tmp-from)
(substring gnus-tmp-from
(1+ (match-beginning 0)) (1- (match-end 0))))
((string-match "<[^>]+> *$" gnus-tmp-from)
(let ((beg (match-beginning 0)))
(or (and (string-match "^\"[^\"]*\"" gnus-tmp-from)
(substring gnus-tmp-from (1+ (match-beginning 0))
(1- (match-end 0))))
(substring gnus-tmp-from 0 beg))))
((memq gnus-tmp-number sparse)
"***")
(t gnus-tmp-from)))
(gnus-tmp-open-bracket
(cond ((memq gnus-tmp-number sparse)
(caadr gnus-tree-brackets))
(dummy (caaddr gnus-tree-brackets))
(adopted (car (nth 3 gnus-tree-brackets)))
(t (caar gnus-tree-brackets))))
(gnus-tmp-close-bracket
(cond ((memq gnus-tmp-number sparse)
(cdadr gnus-tree-brackets))
(adopted (cdr (nth 3 gnus-tree-brackets)))
(dummy
(cdaddr gnus-tree-brackets))
(t (cdar gnus-tree-brackets))))
(buffer-read-only nil)
beg end)
(gnus-add-text-properties
(setq beg (point))
(setq end (progn (eval gnus-tree-line-format-spec) (point)))
(list 'gnus-number gnus-tmp-number))
(when (or t (gnus-visual-p 'tree-highlight 'highlight))
(gnus-tree-highlight-node gnus-tmp-number beg end))))
(defun gnus-tree-highlight-node (article beg end)
"Highlight current line according to `gnus-summary-highlight'."
(let ((list gnus-summary-highlight)
face)
(save-excursion
(set-buffer gnus-summary-buffer)
(let* ((score (or (cdr (assq article gnus-newsgroup-scored))
gnus-summary-default-score 0))
(default gnus-summary-default-score)
(mark (or (gnus-summary-article-mark article) gnus-unread-mark)))
;; Eval the cars of the lists until we find a match.
(while (and list
(not (eval (caar list))))
(setq list (cdr list)))))
(unless (eq (setq face (cdar list)) (get-text-property beg 'face))
(gnus-put-text-property
beg end 'face
(if (boundp face) (symbol-value face) face)))))
(defun gnus-tree-indent (level)
(insert (make-string (1- (* (1+ gnus-tree-node-length) level)) ? )))
(defvar gnus-tmp-limit)
(defvar gnus-tmp-sparse)
(defvar gnus-tmp-indent)
(defun gnus-generate-tree (thread)
"Generate a thread tree for THREAD."
(save-excursion
(set-buffer (gnus-get-tree-buffer))
(let ((buffer-read-only nil)
(gnus-tmp-indent 0))
(erase-buffer)
(funcall gnus-generate-tree-function thread 0)
(gnus-set-mode-line 'tree)
(goto-char (point-min))
(gnus-tree-minimize)
(gnus-tree-recenter)
(let ((selected (selected-window)))
(when (get-buffer-window (set-buffer gnus-tree-buffer) t)
(select-window (get-buffer-window (set-buffer gnus-tree-buffer) t))
(gnus-horizontal-recenter)
(select-window selected))))))
(defun gnus-generate-horizontal-tree (thread level &optional dummyp adopted)
"Generate a horizontal tree."
(let* ((dummy (stringp (car thread)))
(do (or dummy
(memq (mail-header-number (car thread)) gnus-tmp-limit)))
col beg)
(if (not do)
;; We don't want this article.
(setq thread (cdr thread))
(if (not (bolp))
;; Not the first article on the line, so we insert a "-".
(insert (car gnus-tree-parent-child-edges))
;; If the level isn't zero, then we insert some indentation.
(unless (zerop level)
(gnus-tree-indent level)
(insert (cadr gnus-tree-parent-child-edges))
(setq col (- (setq beg (point)) (gnus-point-at-bol) 1))
;; Draw "|" lines upwards.
(while (progn
(forward-line -1)
(forward-char col)
(= (following-char) ? ))
(delete-char 1)
(insert (caddr gnus-tree-parent-child-edges)))
(goto-char beg)))
(setq dummyp nil)
;; Insert the article node.
(gnus-tree-node-insert (pop thread) gnus-tmp-sparse adopted))
(if (null thread)
;; End of the thread, so we go to the next line.
(unless (bolp)
(insert "\n"))
;; Recurse downwards in all children of this article.
(while thread
(gnus-generate-horizontal-tree
(pop thread) (if do (1+ level) level)
(or dummyp dummy) dummy)))))
(defsubst gnus-tree-indent-vertical ()
(let ((len (- (* (1+ gnus-tree-node-length) gnus-tmp-indent)
(- (point) (gnus-point-at-bol)))))
(when (> len 0)
(insert (make-string len ? )))))
(defsubst gnus-tree-forward-line (n)
(while (>= (decf n) 0)
(unless (zerop (forward-line 1))
(end-of-line)
(insert "\n")))
(end-of-line))
(defun gnus-generate-vertical-tree (thread level &optional dummyp adopted)
"Generate a vertical tree."
(let* ((dummy (stringp (car thread)))
(do (or dummy
(memq (mail-header-number (car thread)) gnus-tmp-limit)))
beg)
(if (not do)
;; We don't want this article.
(setq thread (cdr thread))
(if (not (save-excursion (beginning-of-line) (bobp)))
;; Not the first article on the line, so we insert a "-".
(progn
(gnus-tree-indent-vertical)
(insert (make-string (/ gnus-tree-node-length 2) ? ))
(insert (caddr gnus-tree-parent-child-edges))
(gnus-tree-forward-line 1))
;; If the level isn't zero, then we insert some indentation.
(unless (zerop gnus-tmp-indent)
(gnus-tree-forward-line (1- (* 2 level)))
(gnus-tree-indent-vertical)
(delete-char -1)
(insert (cadr gnus-tree-parent-child-edges))
(setq beg (point))
;; Draw "-" lines leftwards.
(while (progn
(forward-char -2)
(= (following-char) ? ))
(delete-char 1)
(insert (car gnus-tree-parent-child-edges)))
(goto-char beg)
(gnus-tree-forward-line 1)))
(setq dummyp nil)
;; Insert the article node.
(gnus-tree-indent-vertical)
(gnus-tree-node-insert (pop thread) gnus-tmp-sparse adopted)
(gnus-tree-forward-line 1))
(if (null thread)
;; End of the thread, so we go to the next line.
(progn
(goto-char (point-min))
(end-of-line)
(incf gnus-tmp-indent))
;; Recurse downwards in all children of this article.
(while thread
(gnus-generate-vertical-tree
(pop thread) (if do (1+ level) level)
(or dummyp dummy) dummy)))))
;;; Interface functions.
(defun gnus-possibly-generate-tree (article &optional force)
"Generate the thread tree for ARTICLE if it isn't displayed already."
(when (save-excursion
(set-buffer gnus-summary-buffer)
(and gnus-use-trees
(vectorp (gnus-summary-article-header article))))
(save-excursion
(let ((top (save-excursion
(set-buffer gnus-summary-buffer)
(gnus-cut-thread
(gnus-remove-thread
(mail-header-id
(gnus-summary-article-header article)) t))))
(gnus-tmp-limit gnus-newsgroup-limit)
(gnus-tmp-sparse gnus-newsgroup-sparse))
(when (or force
(not (eq top gnus-tree-displayed-thread)))
(gnus-generate-tree top)
(setq gnus-tree-displayed-thread top))))))
(defun gnus-tree-open (group)
(gnus-get-tree-buffer))
(defun gnus-tree-close (group)
;(gnus-kill-buffer gnus-tree-buffer)
)
(defun gnus-highlight-selected-tree (article)
"Highlight the selected article in the tree."
(let ((buf (current-buffer))
region)
(set-buffer gnus-tree-buffer)
(when (setq region (gnus-tree-article-region article))
(when (or (not gnus-selected-tree-overlay)
(gnus-extent-detached-p gnus-selected-tree-overlay))
;; Create a new overlay.
(gnus-overlay-put
(setq gnus-selected-tree-overlay (gnus-make-overlay 1 2))
'face gnus-selected-tree-face))
;; Move the overlay to the article.
(gnus-move-overlay
gnus-selected-tree-overlay (goto-char (car region)) (cdr region))
(gnus-tree-minimize)
(gnus-tree-recenter)
(let ((selected (selected-window)))
(when (get-buffer-window (set-buffer gnus-tree-buffer) t)
(select-window (get-buffer-window (set-buffer gnus-tree-buffer) t))
(gnus-horizontal-recenter)
(select-window selected))))
;; If we remove this save-excursion, it updates the wrong mode lines?!?
(save-excursion
(set-buffer gnus-tree-buffer)
(gnus-set-mode-line 'tree))
(set-buffer buf)))
(defun gnus-tree-highlight-article (article face)
(save-excursion
(set-buffer (gnus-get-tree-buffer))
(let (region)
(when (setq region (gnus-tree-article-region article))
(gnus-put-text-property (car region) (cdr region) 'face face)
(set-window-point
(get-buffer-window (current-buffer) t) (cdr region))))))
;;; Allow redefinition of functions.
(gnus-ems-redefine)
(provide 'gnus-salt)
;;; gnus-salt.el ends here

View file

@ -1,110 +0,0 @@
;;; gnus-scomo.el --- mode for editing Gnus score files
;; Copyright (C) 1996 Free Software Foundation, Inc.
;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
;; Keywords: news, mail
;; This file is part of GNU Emacs.
;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Commentary:
;;; Code:
(require 'easymenu)
(require 'timezone)
(eval-when-compile (require 'cl))
(defvar gnus-score-mode-hook nil
"*Hook run in score mode buffers.")
(defvar gnus-score-menu-hook nil
"*Hook run after creating the score mode menu.")
(defvar gnus-score-edit-exit-function nil
"Function run on exit from the score buffer.")
(defvar gnus-score-mode-map nil)
(unless gnus-score-mode-map
(setq gnus-score-mode-map (copy-keymap emacs-lisp-mode-map))
(define-key gnus-score-mode-map "\C-c\C-c" 'gnus-score-edit-exit)
(define-key gnus-score-mode-map "\C-c\C-d" 'gnus-score-edit-insert-date)
(define-key gnus-score-mode-map "\C-c\C-p" 'gnus-score-pretty-print))
;;;###autoload
(defun gnus-score-mode ()
"Mode for editing Gnus score files.
This mode is an extended emacs-lisp mode.
\\{gnus-score-mode-map}"
(interactive)
(kill-all-local-variables)
(use-local-map gnus-score-mode-map)
(when menu-bar-mode
(gnus-score-make-menu-bar))
(set-syntax-table emacs-lisp-mode-syntax-table)
(setq major-mode 'gnus-score-mode)
(setq mode-name "Score")
(lisp-mode-variables nil)
(make-local-variable 'gnus-score-edit-exit-function)
(run-hooks 'emacs-lisp-mode-hook 'gnus-score-mode-hook))
(defun gnus-score-make-menu-bar ()
(unless (boundp 'gnus-score-menu)
(easy-menu-define
gnus-score-menu gnus-score-mode-map ""
'("Score"
["Exit" gnus-score-edit-exit t]
["Insert date" gnus-score-edit-insert-date t]
["Format" gnus-score-pretty-print t]))
(run-hooks 'gnus-score-menu-hook)))
(defun gnus-score-edit-insert-date ()
"Insert date in numerical format."
(interactive)
(princ (gnus-score-day-number (current-time)) (current-buffer)))
(defun gnus-score-pretty-print ()
"Format the current score file."
(interactive)
(goto-char (point-min))
(let ((form (read (current-buffer))))
(erase-buffer)
(pp form (current-buffer)))
(goto-char (point-min)))
(defun gnus-score-edit-exit ()
"Stop editing the score file."
(interactive)
(unless (file-exists-p (file-name-directory (buffer-file-name)))
(make-directory (file-name-directory (buffer-file-name)) t))
(save-buffer)
(bury-buffer (current-buffer))
(let ((buf (current-buffer)))
(when gnus-score-edit-exit-function
(funcall gnus-score-edit-exit-function))
(when (eq buf (current-buffer))
(switch-to-buffer (other-buffer (current-buffer))))))
(defun gnus-score-day-number (time)
(let ((dat (decode-time time)))
(timezone-absolute-from-gregorian
(nth 4 dat) (nth 3 dat) (nth 5 dat))))
(provide 'gnus-scomo)
;;; gnus-scomo.el ends here

File diff suppressed because it is too large Load diff

View file

@ -1,210 +0,0 @@
;;; gnus-setup.el --- Initialization & Setup for Gnus 5
;; Copyright (C) 1995, 96 Free Software Foundation, Inc.
;; Author: Steven L. Baur <steve@miranova.com>
;; Keywords: news
;; 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:
;; My head is starting to spin with all the different mail/news packages.
;; Stop The Madness!
;; Given that Emacs Lisp byte codes may be diverging, it is probably best
;; not to byte compile this, and just arrange to have the .el loaded out
;; of .emacs.
;;; Code:
(defvar running-xemacs (string-match "XEmacs\\|Lucid" emacs-version))
(defvar gnus-emacs-lisp-directory (if running-xemacs
"/usr/local/lib/xemacs/"
"/usr/local/share/emacs/")
"Directory where Emacs site lisp is located.")
(defvar gnus-gnus-lisp-directory (concat gnus-emacs-lisp-directory
"gnus-5.0.15/lisp/")
"Directory where Gnus Emacs lisp is found.")
(defvar gnus-sgnus-lisp-directory (concat gnus-emacs-lisp-directory
"sgnus/lisp/")
"Directory where September Gnus Emacs lisp is found.")
(defvar gnus-tm-lisp-directory (concat gnus-emacs-lisp-directory
"site-lisp/")
"Directory where TM Emacs lisp is found.")
(defvar gnus-mailcrypt-lisp-directory (concat gnus-emacs-lisp-directory
"site-lisp/mailcrypt-3.4/")
"Directory where Mailcrypt Emacs Lisp is found.")
(defvar gnus-bbdb-lisp-directory (concat gnus-emacs-lisp-directory
"site-lisp/bbdb-1.50/")
"Directory where Big Brother Database is found.")
(defvar gnus-use-tm t
"Set this if you want MIME support for Gnus")
(defvar gnus-use-mhe nil
"Set this if you want to use MH-E for mail reading")
(defvar gnus-use-rmail nil
"Set this if you want to use RMAIL for mail reading")
(defvar gnus-use-sendmail t
"Set this if you want to use SENDMAIL for mail reading")
(defvar gnus-use-vm nil
"Set this if you want to use the VM package for mail reading")
(defvar gnus-use-sc t
"Set this if you want to use Supercite")
(defvar gnus-use-mailcrypt t
"Set this if you want to use Mailcrypt for dealing with PGP messages")
(defvar gnus-use-bbdb nil
"Set this if you want to use the Big Brother DataBase")
(defvar gnus-use-september nil
"Set this if you are using the experimental September Gnus")
(let ((gnus-directory (if gnus-use-september
gnus-sgnus-lisp-directory
gnus-gnus-lisp-directory)))
(if (null (member gnus-directory load-path))
(setq load-path (cons gnus-directory load-path))))
;;; Tools for MIME by
;;; UMEDA Masanobu <umerin@mse.kyutech.ac.jp>
;;; MORIOKA Tomohiko <morioka@jaist.ac.jp>
(if gnus-use-tm
(progn
(if (null (member gnus-tm-lisp-directory load-path))
(setq load-path (cons gnus-tm-lisp-directory load-path)))
(load "mime-setup")))
;;; Mailcrypt by
;;; Jin Choi <jin@atype.com>
;;; Patrick LoPresti <patl@lcs.mit.edu>
(if gnus-use-mailcrypt
(progn
(if (null (member gnus-mailcrypt-lisp-directory load-path))
(setq load-path (cons gnus-mailcrypt-lisp-directory load-path)))
(autoload 'mc-install-write-mode "mailcrypt" nil t)
(autoload 'mc-install-read-mode "mailcrypt" nil t)
(add-hook 'message-mode-hook 'mc-install-write-mode)
(add-hook 'gnus-summary-mode-hook 'mc-install-read-mode)
(if gnus-use-mhe
(progn
(add-hook 'mh-folder-mode-hook 'mc-install-read-mode)
(add-hook 'mh-letter-mode-hook 'mc-install-write-mode)))))
;;; BBDB by
;;; Jamie Zawinski <jwz@lucid.com>
(if gnus-use-bbdb
(progn
(if (null (member gnus-bbdb-lisp-directory load-path))
(setq load-path (cons gnus-bbdb-lisp-directory load-path)))
(autoload 'bbdb "bbdb-com"
"Insidious Big Brother Database" t)
(autoload 'bbdb-name "bbdb-com"
"Insidious Big Brother Database" t)
(autoload 'bbdb-company "bbdb-com"
"Insidious Big Brother Database" t)
(autoload 'bbdb-net "bbdb-com"
"Insidious Big Brother Database" t)
(autoload 'bbdb-notes "bbdb-com"
"Insidious Big Brother Database" t)
(if gnus-use-vm
(progn
(autoload 'bbdb-insinuate-vm "bbdb-vm"
"Hook BBDB into VM" t)))
(if gnus-use-rmail
(progn
(autoload 'bbdb-insinuate-rmail "bbdb-rmail"
"Hook BBDB into RMAIL" t)
(add-hook 'rmail-mode-hook 'bbdb-insinuate-rmail)))
(if gnus-use-mhe
(progn
(autoload 'bbdb-insinuate-mh "bbdb-mh"
"Hook BBDB into MH-E" t)
(add-hook 'mh-folder-mode-hook 'bbdb-insinuate-mh)))
(autoload 'bbdb-insinuate-gnus "bbdb-gnus"
"Hook BBDB into Gnus" t)
(add-hook 'gnus-startup-hook 'bbdb-insinuate-gnus)
(if gnus-use-sendmail
(progn
(autoload 'bbdb-insinuate-sendmail "bbdb"
"Insidious Big Brother Database" t)
(add-hook 'mail-setup-hook 'bbdb-insinuate-sendmail)
(add-hook 'message-setup-hook 'bbdb-insinuate-sendmail)))))
(if gnus-use-sc
(progn
(add-hook 'mail-citation-hook 'sc-cite-original)
(setq message-cite-function 'sc-cite-original)
(autoload 'sc-cite-original "supercite")))
;;;### (autoloads (gnus-batch-score gnus-fetch-group gnus gnus-slave gnus-no-server gnus-update-format) "gnus" "lisp/gnus.el" (12473 2137))
;;; Generated autoloads from lisp/gnus.el
(autoload 'gnus-update-format "gnus" "\
Update the format specification near point." t nil)
(autoload 'gnus-slave-no-server "gnus" "\
Read network news as a slave without connecting to local server." t nil)
(autoload 'gnus-no-server "gnus" "\
Read network news.
If ARG is a positive number, Gnus will use that as the
startup level. If ARG is nil, Gnus will be started at level 2.
If ARG is non-nil and not a positive number, Gnus will
prompt the user for the name of an NNTP server to use.
As opposed to `gnus', this command will not connect to the local server." t nil)
(autoload 'gnus-slave "gnus" "\
Read news as a slave." t nil)
(autoload 'gnus "gnus" "\
Read network news.
If ARG is non-nil and a positive number, Gnus will use that as the
startup level. If ARG is non-nil and not a positive number, Gnus will
prompt the user for the name of an NNTP server to use." t nil)
(autoload 'gnus-fetch-group "gnus" "\
Start Gnus if necessary and enter GROUP.
Returns whether the fetching was successful or not." t nil)
(defalias 'gnus-batch-kill 'gnus-batch-score)
(autoload 'gnus-batch-score "gnus" "\
Run batched scoring.
Usage: emacs -batch -l gnus -f gnus-batch-score <newsgroups> ...
Newsgroups is a list of strings in Bnews format. If you want to score
the comp hierarchy, you'd say \"comp.all\". If you would not like to
score the alt hierarchy, you'd say \"!alt.all\"." t nil)
;;;***
(provide 'gnus-setup)
(run-hooks 'gnus-setup-load-hook)
;;; gnus-setup.el ends here

View file

@ -1,563 +0,0 @@
;;; gnus-soup.el --- SOUP packet writing support for Gnus
;; Copyright (C) 1995,96 Free Software Foundation, Inc.
;; Author: Per Abrahamsen <abraham@iesd.auc.dk>
;; Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
;; Keywords: news, mail
;; This file is part of GNU Emacs.
;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Commentary:
;;; Code:
(require 'gnus-msg)
(require 'gnus)
(eval-when-compile (require 'cl))
;;; User Variables:
(defvar gnus-soup-directory "~/SoupBrew/"
"*Directory containing an unpacked SOUP packet.")
(defvar gnus-soup-replies-directory (concat gnus-soup-directory "SoupReplies/")
"*Directory where Gnus will do processing of replies.")
(defvar gnus-soup-prefix-file "gnus-prefix"
"*Name of the file where Gnus stores the last used prefix.")
(defvar gnus-soup-packer "tar cf - %s | gzip > $HOME/Soupout%d.tgz"
"Format string command for packing a SOUP packet.
The SOUP files will be inserted where the %s is in the string.
This string MUST contain both %s and %d. The file number will be
inserted where %d appears.")
(defvar gnus-soup-unpacker "gunzip -c %s | tar xvf -"
"*Format string command for unpacking a SOUP packet.
The SOUP packet file name will be inserted at the %s.")
(defvar gnus-soup-packet-directory "~/"
"*Where gnus-soup will look for REPLIES packets.")
(defvar gnus-soup-packet-regexp "Soupin"
"*Regular expression matching SOUP REPLIES packets in `gnus-soup-packet-directory'.")
(defvar gnus-soup-ignored-headers "^Xref:"
"*Regexp to match headers to be removed when brewing SOUP packets.")
;;; Internal Variables:
(defvar gnus-soup-encoding-type ?n
"*Soup encoding type.
`n' is news format, `m' is Unix mbox format, and `M' is MMDF mailbox
format.")
(defvar gnus-soup-index-type ?c
"*Soup index type.
`n' means no index file and `c' means standard Cnews overview
format.")
(defvar gnus-soup-areas nil)
(defvar gnus-soup-last-prefix nil)
(defvar gnus-soup-prev-prefix nil)
(defvar gnus-soup-buffers nil)
;;; Access macros:
(defmacro gnus-soup-area-prefix (area)
`(aref ,area 0))
(defmacro gnus-soup-set-area-prefix (area prefix)
`(aset ,area 0 ,prefix))
(defmacro gnus-soup-area-name (area)
`(aref ,area 1))
(defmacro gnus-soup-area-encoding (area)
`(aref ,area 2))
(defmacro gnus-soup-area-description (area)
`(aref ,area 3))
(defmacro gnus-soup-area-number (area)
`(aref ,area 4))
(defmacro gnus-soup-area-set-number (area value)
`(aset ,area 4 ,value))
(defmacro gnus-soup-encoding-format (encoding)
`(aref ,encoding 0))
(defmacro gnus-soup-encoding-index (encoding)
`(aref ,encoding 1))
(defmacro gnus-soup-encoding-kind (encoding)
`(aref ,encoding 2))
(defmacro gnus-soup-reply-prefix (reply)
`(aref ,reply 0))
(defmacro gnus-soup-reply-kind (reply)
`(aref ,reply 1))
(defmacro gnus-soup-reply-encoding (reply)
`(aref ,reply 2))
;;; Commands:
(defun gnus-soup-send-replies ()
"Unpack and send all replies in the reply packet."
(interactive)
(let ((packets (directory-files
gnus-soup-packet-directory t gnus-soup-packet-regexp)))
(while packets
(and (gnus-soup-send-packet (car packets))
(delete-file (car packets)))
(setq packets (cdr packets)))))
(defun gnus-soup-add-article (n)
"Add the current article to SOUP packet.
If N is a positive number, add the N next articles.
If N is a negative number, add the N previous articles.
If N is nil and any articles have been marked with the process mark,
move those articles instead."
(interactive "P")
(gnus-set-global-variables)
(let* ((articles (gnus-summary-work-articles n))
(tmp-buf (get-buffer-create "*soup work*"))
(area (gnus-soup-area gnus-newsgroup-name))
(prefix (gnus-soup-area-prefix area))
headers)
(buffer-disable-undo tmp-buf)
(save-excursion
(while articles
;; Find the header of the article.
(set-buffer gnus-summary-buffer)
(when (setq headers (gnus-summary-article-header (car articles)))
;; Put the article in a buffer.
(set-buffer tmp-buf)
(when (gnus-request-article-this-buffer
(car articles) gnus-newsgroup-name)
(save-restriction
(message-narrow-to-head)
(message-remove-header gnus-soup-ignored-headers t))
(gnus-soup-store gnus-soup-directory prefix headers
gnus-soup-encoding-type
gnus-soup-index-type)
(gnus-soup-area-set-number
area (1+ (or (gnus-soup-area-number area) 0)))))
;; Mark article as read.
(set-buffer gnus-summary-buffer)
(gnus-summary-remove-process-mark (car articles))
(gnus-summary-mark-as-read (car articles) gnus-souped-mark)
(setq articles (cdr articles)))
(kill-buffer tmp-buf))
(gnus-soup-save-areas)))
(defun gnus-soup-pack-packet ()
"Make a SOUP packet from the SOUP areas."
(interactive)
(gnus-soup-read-areas)
(gnus-soup-pack gnus-soup-directory gnus-soup-packer))
(defun gnus-group-brew-soup (n)
"Make a soup packet from the current group.
Uses the process/prefix convention."
(interactive "P")
(let ((groups (gnus-group-process-prefix n)))
(while groups
(gnus-group-remove-mark (car groups))
(gnus-soup-group-brew (car groups) t)
(setq groups (cdr groups)))
(gnus-soup-save-areas)))
(defun gnus-brew-soup (&optional level)
"Go through all groups on LEVEL or less and make a soup packet."
(interactive "P")
(let ((level (or level gnus-level-subscribed))
(newsrc (cdr gnus-newsrc-alist)))
(while newsrc
(and (<= (nth 1 (car newsrc)) level)
(gnus-soup-group-brew (caar newsrc) t))
(setq newsrc (cdr newsrc)))
(gnus-soup-save-areas)))
;;;###autoload
(defun gnus-batch-brew-soup ()
"Brew a SOUP packet from groups mention on the command line.
Will use the remaining command line arguments as regular expressions
for matching on group names.
For instance, if you want to brew on all the nnml groups, as well as
groups with \"emacs\" in the name, you could say something like:
$ emacs -batch -f gnus-batch-brew-soup ^nnml \".*emacs.*\""
(interactive)
)
;;; Internal Functions:
;; Store the current buffer.
(defun gnus-soup-store (directory prefix headers format index)
;; Create the directory, if needed.
(or (file-directory-p directory)
(gnus-make-directory directory))
(let* ((msg-buf (find-file-noselect
(concat directory prefix ".MSG")))
(idx-buf (if (= index ?n)
nil
(find-file-noselect
(concat directory prefix ".IDX"))))
(article-buf (current-buffer))
from head-line beg type)
(setq gnus-soup-buffers (cons msg-buf (delq msg-buf gnus-soup-buffers)))
(buffer-disable-undo msg-buf)
(and idx-buf
(progn
(setq gnus-soup-buffers (cons idx-buf gnus-soup-buffers))
(buffer-disable-undo idx-buf)))
(save-excursion
;; Make sure the last char in the buffer is a newline.
(goto-char (point-max))
(or (= (current-column) 0)
(insert "\n"))
;; Find the "from".
(goto-char (point-min))
(setq from
(gnus-mail-strip-quoted-names
(or (mail-fetch-field "from")
(mail-fetch-field "really-from")
(mail-fetch-field "sender"))))
(goto-char (point-min))
;; Depending on what encoding is supposed to be used, we make
;; a soup header.
(setq head-line
(cond
((= gnus-soup-encoding-type ?n)
(format "#! rnews %d\n" (buffer-size)))
((= gnus-soup-encoding-type ?m)
(while (search-forward "\nFrom " nil t)
(replace-match "\n>From " t t))
(concat "From " (or from "unknown")
" " (current-time-string) "\n"))
((= gnus-soup-encoding-type ?M)
"\^a\^a\^a\^a\n")
(t (error "Unsupported type: %c" gnus-soup-encoding-type))))
;; Insert the soup header and the article in the MSG buf.
(set-buffer msg-buf)
(goto-char (point-max))
(insert head-line)
(setq beg (point))
(insert-buffer-substring article-buf)
;; Insert the index in the IDX buf.
(cond ((= index ?c)
(set-buffer idx-buf)
(gnus-soup-insert-idx beg headers))
((/= index ?n)
(error "Unknown index type: %c" type)))
;; Return the MSG buf.
msg-buf)))
(defun gnus-soup-group-brew (group &optional not-all)
"Enter GROUP and add all articles to a SOUP package.
If NOT-ALL, don't pack ticked articles."
(let ((gnus-expert-user t)
(gnus-large-newsgroup nil)
(entry (gnus-gethash group gnus-newsrc-hashtb)))
(when (or (null entry)
(eq (car entry) t)
(and (car entry)
(> (car entry) 0))
(and (not not-all)
(gnus-range-length (cdr (assq 'tick (gnus-info-marks
(nth 2 entry)))))))
(when (gnus-summary-read-group group nil t)
(setq gnus-newsgroup-processable
(reverse
(if (not not-all)
(append gnus-newsgroup-marked gnus-newsgroup-unreads)
gnus-newsgroup-unreads)))
(gnus-soup-add-article nil)
(gnus-summary-exit)))))
(defun gnus-soup-insert-idx (offset header)
;; [number subject from date id references chars lines xref]
(goto-char (point-max))
(insert
(format "%d\t%s\t%s\t%s\t%s\t%s\t%d\t%s\t\t\n"
offset
(or (mail-header-subject header) "(none)")
(or (mail-header-from header) "(nobody)")
(or (mail-header-date header) "")
(or (mail-header-id header)
(concat "soup-dummy-id-"
(mapconcat
(lambda (time) (int-to-string time))
(current-time) "-")))
(or (mail-header-references header) "")
(or (mail-header-chars header) 0)
(or (mail-header-lines header) "0"))))
(defun gnus-soup-save-areas ()
(gnus-soup-write-areas)
(save-excursion
(let (buf)
(while gnus-soup-buffers
(setq buf (car gnus-soup-buffers)
gnus-soup-buffers (cdr gnus-soup-buffers))
(if (not (buffer-name buf))
()
(set-buffer buf)
(and (buffer-modified-p) (save-buffer))
(kill-buffer (current-buffer)))))
(gnus-soup-write-prefixes)))
(defun gnus-soup-write-prefixes ()
(let ((prefix gnus-soup-last-prefix))
(save-excursion
(while prefix
(gnus-set-work-buffer)
(insert (format "(setq gnus-soup-prev-prefix %d)\n" (cdar prefix)))
(gnus-make-directory (caar prefix))
(write-region (point-min) (point-max)
(concat (caar prefix) gnus-soup-prefix-file)
nil 'nomesg)
(setq prefix (cdr prefix))))))
(defun gnus-soup-pack (dir packer)
(let* ((files (mapconcat 'identity
'("AREAS" "*.MSG" "*.IDX" "INFO"
"LIST" "REPLIES" "COMMANDS" "ERRORS")
" "))
(packer (if (< (string-match "%s" packer)
(string-match "%d" packer))
(format packer files
(string-to-int (gnus-soup-unique-prefix dir)))
(format packer
(string-to-int (gnus-soup-unique-prefix dir))
files)))
(dir (expand-file-name dir)))
(or (file-directory-p dir)
(gnus-make-directory dir))
(setq gnus-soup-areas nil)
(gnus-message 4 "Packing %s..." packer)
(if (zerop (call-process shell-file-name
nil nil nil shell-command-switch
(concat "cd " dir " ; " packer)))
(progn
(call-process shell-file-name nil nil nil shell-command-switch
(concat "cd " dir " ; rm " files))
(gnus-message 4 "Packing...done" packer))
(error "Couldn't pack packet."))))
(defun gnus-soup-parse-areas (file)
"Parse soup area file FILE.
The result is a of vectors, each containing one entry from the AREA file.
The vector contain five strings,
[prefix name encoding description number]
though the two last may be nil if they are missing."
(let (areas)
(save-excursion
(set-buffer (find-file-noselect file 'force))
(buffer-disable-undo (current-buffer))
(goto-char (point-min))
(while (not (eobp))
(setq areas
(cons (vector (gnus-soup-field)
(gnus-soup-field)
(gnus-soup-field)
(and (eq (preceding-char) ?\t)
(gnus-soup-field))
(and (eq (preceding-char) ?\t)
(string-to-int (gnus-soup-field))))
areas))
(if (eq (preceding-char) ?\t)
(beginning-of-line 2)))
(kill-buffer (current-buffer)))
areas))
(defun gnus-soup-parse-replies (file)
"Parse soup REPLIES file FILE.
The result is a of vectors, each containing one entry from the REPLIES
file. The vector contain three strings, [prefix name encoding]."
(let (replies)
(save-excursion
(set-buffer (find-file-noselect file))
(buffer-disable-undo (current-buffer))
(goto-char (point-min))
(while (not (eobp))
(setq replies
(cons (vector (gnus-soup-field) (gnus-soup-field)
(gnus-soup-field))
replies))
(if (eq (preceding-char) ?\t)
(beginning-of-line 2)))
(kill-buffer (current-buffer)))
replies))
(defun gnus-soup-field ()
(prog1
(buffer-substring (point) (progn (skip-chars-forward "^\t\n") (point)))
(forward-char 1)))
(defun gnus-soup-read-areas ()
(or gnus-soup-areas
(setq gnus-soup-areas
(gnus-soup-parse-areas (concat gnus-soup-directory "AREAS")))))
(defun gnus-soup-write-areas ()
"Write the AREAS file."
(interactive)
(when gnus-soup-areas
(nnheader-temp-write (concat gnus-soup-directory "AREAS")
(let ((areas gnus-soup-areas)
area)
(while (setq area (pop areas))
(insert
(format
"%s\t%s\t%s%s\n"
(gnus-soup-area-prefix area)
(gnus-soup-area-name area)
(gnus-soup-area-encoding area)
(if (or (gnus-soup-area-description area)
(gnus-soup-area-number area))
(concat "\t" (or (gnus-soup-area-description
area) "")
(if (gnus-soup-area-number area)
(concat "\t" (int-to-string
(gnus-soup-area-number area)))
"")) ""))))))))
(defun gnus-soup-write-replies (dir areas)
"Write a REPLIES file in DIR containing AREAS."
(nnheader-temp-write (concat dir "REPLIES")
(let (area)
(while (setq area (pop areas))
(insert (format "%s\t%s\t%s\n"
(gnus-soup-reply-prefix area)
(gnus-soup-reply-kind area)
(gnus-soup-reply-encoding area)))))))
(defun gnus-soup-area (group)
(gnus-soup-read-areas)
(let ((areas gnus-soup-areas)
(real-group (gnus-group-real-name group))
area result)
(while areas
(setq area (car areas)
areas (cdr areas))
(if (equal (gnus-soup-area-name area) real-group)
(setq result area)))
(or result
(setq result
(vector (gnus-soup-unique-prefix)
real-group
(format "%c%c%c"
gnus-soup-encoding-type
gnus-soup-index-type
(if (gnus-member-of-valid 'mail group) ?m ?n))
nil nil)
gnus-soup-areas (cons result gnus-soup-areas)))
result))
(defun gnus-soup-unique-prefix (&optional dir)
(let* ((dir (file-name-as-directory (or dir gnus-soup-directory)))
(entry (assoc dir gnus-soup-last-prefix))
gnus-soup-prev-prefix)
(if entry
()
(and (file-exists-p (concat dir gnus-soup-prefix-file))
(condition-case nil
(load (concat dir gnus-soup-prefix-file) nil t t)
(error nil)))
(setq gnus-soup-last-prefix
(cons (setq entry (cons dir (or gnus-soup-prev-prefix 0)))
gnus-soup-last-prefix)))
(setcdr entry (1+ (cdr entry)))
(gnus-soup-write-prefixes)
(int-to-string (cdr entry))))
(defun gnus-soup-unpack-packet (dir unpacker packet)
"Unpack PACKET into DIR using UNPACKER.
Return whether the unpacking was successful."
(gnus-make-directory dir)
(gnus-message 4 "Unpacking: %s" (format unpacker packet))
(prog1
(zerop (call-process
shell-file-name nil nil nil shell-command-switch
(format "cd %s ; %s" (expand-file-name dir)
(format unpacker packet))))
(gnus-message 4 "Unpacking...done")))
(defun gnus-soup-send-packet (packet)
(gnus-soup-unpack-packet
gnus-soup-replies-directory gnus-soup-unpacker packet)
(let ((replies (gnus-soup-parse-replies
(concat gnus-soup-replies-directory "REPLIES"))))
(save-excursion
(while replies
(let* ((msg-file (concat gnus-soup-replies-directory
(gnus-soup-reply-prefix (car replies))
".MSG"))
(msg-buf (and (file-exists-p msg-file)
(find-file-noselect msg-file)))
(tmp-buf (get-buffer-create " *soup send*"))
beg end)
(cond
((/= (gnus-soup-encoding-format
(gnus-soup-reply-encoding (car replies))) ?n)
(error "Unsupported encoding"))
((null msg-buf)
t)
(t
(buffer-disable-undo msg-buf)
(buffer-disable-undo tmp-buf)
(set-buffer msg-buf)
(goto-char (point-min))
(while (not (eobp))
(or (looking-at "#! *rnews +\\([0-9]+\\)")
(error "Bad header."))
(forward-line 1)
(setq beg (point)
end (+ (point) (string-to-int
(buffer-substring
(match-beginning 1) (match-end 1)))))
(switch-to-buffer tmp-buf)
(erase-buffer)
(insert-buffer-substring msg-buf beg end)
(goto-char (point-min))
(search-forward "\n\n")
(forward-char -1)
(insert mail-header-separator)
(setq message-newsreader (setq message-mailer
(gnus-extended-version)))
(cond
((string= (gnus-soup-reply-kind (car replies)) "news")
(gnus-message 5 "Sending news message to %s..."
(mail-fetch-field "newsgroups"))
(sit-for 1)
(funcall message-send-news-function))
((string= (gnus-soup-reply-kind (car replies)) "mail")
(gnus-message 5 "Sending mail to %s..."
(mail-fetch-field "to"))
(sit-for 1)
(message-send-mail))
(t
(error "Unknown reply kind")))
(set-buffer msg-buf)
(goto-char end))
(delete-file (buffer-file-name))
(kill-buffer msg-buf)
(kill-buffer tmp-buf)
(gnus-message 4 "Sent packet"))))
(setq replies (cdr replies)))
t)))
(provide 'gnus-soup)
;;; gnus-soup.el ends here

View file

@ -1,708 +0,0 @@
;;; gnus-srvr.el --- virtual server support for Gnus
;; Copyright (C) 1995,96 Free Software Foundation, Inc.
;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
;; Keywords: news
;; This file is part of GNU Emacs.
;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Commentary:
;;; Code:
(require 'gnus)
(eval-when-compile (require 'cl))
(defvar gnus-server-mode-hook nil
"Hook run in `gnus-server-mode' buffers.")
(defconst gnus-server-line-format " {%(%h:%w%)} %s\n"
"Format of server lines.
It works along the same lines as a normal formatting string,
with some simple extensions.")
(defvar gnus-server-mode-line-format "Gnus List of servers"
"The format specification for the server mode line.")
(defvar gnus-server-exit-hook nil
"*Hook run when exiting the server buffer.")
;;; Internal variables.
(defvar gnus-inserted-opened-servers nil)
(defvar gnus-server-line-format-alist
`((?h how ?s)
(?n name ?s)
(?w where ?s)
(?s status ?s)))
(defvar gnus-server-mode-line-format-alist
`((?S news-server ?s)
(?M news-method ?s)
(?u user-defined ?s)))
(defvar gnus-server-line-format-spec nil)
(defvar gnus-server-mode-line-format-spec nil)
(defvar gnus-server-killed-servers nil)
(defvar gnus-server-mode-map)
(defvar gnus-server-menu-hook nil
"*Hook run after the creation of the server mode menu.")
(defun gnus-server-make-menu-bar ()
(gnus-visual-turn-off-edit-menu 'server)
(unless (boundp 'gnus-server-server-menu)
(easy-menu-define
gnus-server-server-menu gnus-server-mode-map ""
'("Server"
["Add" gnus-server-add-server t]
["Browse" gnus-server-read-server t]
["List" gnus-server-list-servers t]
["Kill" gnus-server-kill-server t]
["Yank" gnus-server-yank-server t]
["Copy" gnus-server-copy-server t]
["Edit" gnus-server-edit-server t]
["Exit" gnus-server-exit t]
))
(easy-menu-define
gnus-server-connections-menu gnus-server-mode-map ""
'("Connections"
["Open" gnus-server-open-server t]
["Close" gnus-server-close-server t]
["Deny" gnus-server-deny-server t]
["Reset" gnus-server-remove-denials t]
))
(run-hooks 'gnus-server-menu-hook)))
(defvar gnus-server-mode-map nil)
(put 'gnus-server-mode 'mode-class 'special)
(unless gnus-server-mode-map
(setq gnus-server-mode-map (make-sparse-keymap))
(suppress-keymap gnus-server-mode-map)
(gnus-define-keys
gnus-server-mode-map
" " gnus-server-read-server
"\r" gnus-server-read-server
gnus-mouse-2 gnus-server-pick-server
"q" gnus-server-exit
"l" gnus-server-list-servers
"k" gnus-server-kill-server
"y" gnus-server-yank-server
"c" gnus-server-copy-server
"a" gnus-server-add-server
"e" gnus-server-edit-server
"O" gnus-server-open-server
"C" gnus-server-close-server
"D" gnus-server-deny-server
"R" gnus-server-remove-denials
"\C-c\C-i" gnus-info-find-node))
(defun gnus-server-mode ()
"Major mode for listing and editing servers.
All normal editing commands are switched off.
\\<gnus-server-mode-map>
For more in-depth information on this mode, read the manual
(`\\[gnus-info-find-node]').
The following commands are available:
\\{gnus-server-mode-map}"
(interactive)
(when (and menu-bar-mode
(gnus-visual-p 'server-menu 'menu))
(gnus-server-make-menu-bar))
(kill-all-local-variables)
(gnus-simplify-mode-line)
(setq major-mode 'gnus-server-mode)
(setq mode-name "Server")
; (gnus-group-set-mode-line)
(setq mode-line-process nil)
(use-local-map gnus-server-mode-map)
(buffer-disable-undo (current-buffer))
(setq truncate-lines t)
(setq buffer-read-only t)
(run-hooks 'gnus-server-mode-hook))
(defun gnus-server-insert-server-line (name method)
(let* ((how (car method))
(where (nth 1 method))
(elem (assoc method gnus-opened-servers))
(status (cond ((eq (nth 1 elem) 'denied)
"(denied)")
((or (gnus-server-opened method)
(eq (nth 1 elem) 'ok))
"(opened)")
(t
"(closed)"))))
(beginning-of-line)
(gnus-add-text-properties
(point)
(prog1 (1+ (point))
;; Insert the text.
(eval gnus-server-line-format-spec))
(list 'gnus-server (intern name)))))
(defun gnus-enter-server-buffer ()
"Set up the server buffer."
(gnus-server-setup-buffer)
(gnus-configure-windows 'server)
(gnus-server-prepare))
(defun gnus-server-setup-buffer ()
"Initialize the server buffer."
(unless (get-buffer gnus-server-buffer)
(save-excursion
(set-buffer (get-buffer-create gnus-server-buffer))
(gnus-server-mode)
(when gnus-carpal
(gnus-carpal-setup-buffer 'server)))))
(defun gnus-server-prepare ()
(setq gnus-server-mode-line-format-spec
(gnus-parse-format gnus-server-mode-line-format
gnus-server-mode-line-format-alist))
(setq gnus-server-line-format-spec
(gnus-parse-format gnus-server-line-format
gnus-server-line-format-alist t))
(let ((alist gnus-server-alist)
(buffer-read-only nil)
(opened gnus-opened-servers)
done server op-ser)
(erase-buffer)
(setq gnus-inserted-opened-servers nil)
;; First we do the real list of servers.
(while alist
(push (cdr (setq server (pop alist))) done)
(when (and server (car server) (cdr server))
(gnus-server-insert-server-line (car server) (cdr server))))
;; Then we insert the list of servers that have been opened in
;; this session.
(while opened
(unless (member (caar opened) done)
(gnus-server-insert-server-line
(setq op-ser (format "%s:%s" (caaar opened) (nth 1 (caar opened))))
(caar opened))
(push (list op-ser (caar opened)) gnus-inserted-opened-servers))
(setq opened (cdr opened))))
(goto-char (point-min))
(gnus-server-position-point))
(defun gnus-server-server-name ()
(let ((server (get-text-property (gnus-point-at-bol) 'gnus-server)))
(and server (symbol-name server))))
(defalias 'gnus-server-position-point 'gnus-goto-colon)
(defconst gnus-server-edit-buffer "*Gnus edit server*")
(defun gnus-server-update-server (server)
(save-excursion
(set-buffer gnus-server-buffer)
(let* ((buffer-read-only nil)
(entry (assoc server gnus-server-alist))
(oentry (assoc (gnus-server-to-method server)
gnus-opened-servers)))
(when entry
(gnus-dribble-enter
(concat "(gnus-server-set-info \"" server "\" '"
(prin1-to-string (cdr entry)) ")")))
(when (or entry oentry)
;; Buffer may be narrowed.
(save-restriction
(widen)
(when (gnus-server-goto-server server)
(gnus-delete-line))
(if entry
(gnus-server-insert-server-line (car entry) (cdr entry))
(gnus-server-insert-server-line
(format "%s:%s" (caar oentry) (nth 1 (car oentry)))
(car oentry)))
(gnus-server-position-point))))))
(defun gnus-server-set-info (server info)
;; Enter a select method into the virtual server alist.
(when (and server info)
(gnus-dribble-enter
(concat "(gnus-server-set-info \"" server "\" '"
(prin1-to-string info) ")"))
(let* ((server (nth 1 info))
(entry (assoc server gnus-server-alist)))
(if entry (setcdr entry info)
(setq gnus-server-alist
(nconc gnus-server-alist (list (cons server info))))))))
;;; Interactive server functions.
(defun gnus-server-kill-server (server)
"Kill the server on the current line."
(interactive (list (gnus-server-server-name)))
(unless (gnus-server-goto-server server)
(if server (error "No such server: %s" server)
(error "No server on the current line")))
(unless (assoc server gnus-server-alist)
(error "Read-only server %s" server))
(gnus-dribble-enter "")
(let ((buffer-read-only nil))
(gnus-delete-line))
(setq gnus-server-killed-servers
(cons (assoc server gnus-server-alist) gnus-server-killed-servers))
(setq gnus-server-alist (delq (car gnus-server-killed-servers)
gnus-server-alist))
(gnus-server-position-point))
(defun gnus-server-yank-server ()
"Yank the previously killed server."
(interactive)
(or gnus-server-killed-servers
(error "No killed servers to be yanked"))
(let ((alist gnus-server-alist)
(server (gnus-server-server-name))
(killed (car gnus-server-killed-servers)))
(if (not server)
(setq gnus-server-alist (nconc gnus-server-alist (list killed)))
(if (string= server (caar gnus-server-alist))
(setq gnus-server-alist (cons killed gnus-server-alist))
(while (and (cdr alist)
(not (string= server (caadr alist))))
(setq alist (cdr alist)))
(if alist
(setcdr alist (cons killed (cdr alist)))
(setq gnus-server-alist (list killed)))))
(gnus-server-update-server (car killed))
(setq gnus-server-killed-servers (cdr gnus-server-killed-servers))
(gnus-server-position-point)))
(defun gnus-server-exit ()
"Return to the group buffer."
(interactive)
(kill-buffer (current-buffer))
(switch-to-buffer gnus-group-buffer)
(run-hooks 'gnus-server-exit-hook))
(defun gnus-server-list-servers ()
"List all available servers."
(interactive)
(let ((cur (gnus-server-server-name)))
(gnus-server-prepare)
(if cur (gnus-server-goto-server cur)
(goto-char (point-max))
(forward-line -1))
(gnus-server-position-point)))
(defun gnus-server-set-status (method status)
"Make METHOD have STATUS."
(let ((entry (assoc method gnus-opened-servers)))
(if entry
(setcar (cdr entry) status)
(push (list method status) gnus-opened-servers))))
(defun gnus-opened-servers-remove (method)
"Remove METHOD from the list of opened servers."
(setq gnus-opened-servers (delq (assoc method gnus-opened-servers)
gnus-opened-servers)))
(defun gnus-server-open-server (server)
"Force an open of SERVER."
(interactive (list (gnus-server-server-name)))
(let ((method (gnus-server-to-method server)))
(or method (error "No such server: %s" server))
(gnus-server-set-status method 'ok)
(prog1
(or (gnus-open-server method)
(progn (message "Couldn't open %s" server) nil))
(gnus-server-update-server server)
(gnus-server-position-point))))
(defun gnus-server-close-server (server)
"Close SERVER."
(interactive (list (gnus-server-server-name)))
(let ((method (gnus-server-to-method server)))
(or method (error "No such server: %s" server))
(gnus-server-set-status method 'closed)
(prog1
(gnus-close-server method)
(gnus-server-update-server server)
(gnus-server-position-point))))
(defun gnus-server-deny-server (server)
"Make sure SERVER will never be attempted opened."
(interactive (list (gnus-server-server-name)))
(let ((method (gnus-server-to-method server)))
(or method (error "No such server: %s" server))
(gnus-server-set-status method 'denied))
(gnus-server-update-server server)
(gnus-server-position-point)
t)
(defun gnus-server-remove-denials ()
"Make all denied servers into closed servers."
(interactive)
(let ((servers gnus-opened-servers))
(while servers
(when (eq (nth 1 (car servers)) 'denied)
(setcar (nthcdr 1 (car servers)) 'closed))
(setq servers (cdr servers))))
(gnus-server-list-servers))
(defun gnus-server-copy-server (from to)
(interactive
(list
(or (gnus-server-server-name)
(error "No server on the current line"))
(read-string "Copy to: ")))
(or from (error "No server on current line"))
(or (and to (not (string= to ""))) (error "No name to copy to"))
(and (assoc to gnus-server-alist) (error "%s already exists" to))
(or (assoc from gnus-server-alist)
(error "%s: no such server" from))
(let ((to-entry (gnus-copy-sequence (assoc from gnus-server-alist))))
(setcar to-entry to)
(setcar (nthcdr 2 to-entry) to)
(setq gnus-server-killed-servers
(cons to-entry gnus-server-killed-servers))
(gnus-server-yank-server)))
(defun gnus-server-add-server (how where)
(interactive
(list (intern (completing-read "Server method: "
gnus-valid-select-methods nil t))
(read-string "Server name: ")))
(setq gnus-server-killed-servers
(cons (list where how where) gnus-server-killed-servers))
(gnus-server-yank-server))
(defun gnus-server-goto-server (server)
"Jump to a server line."
(interactive
(list (completing-read "Goto server: " gnus-server-alist nil t)))
(let ((to (text-property-any (point-min) (point-max)
'gnus-server (intern server))))
(and to
(progn
(goto-char to)
(gnus-server-position-point)))))
(defun gnus-server-edit-server (server)
"Edit the server on the current line."
(interactive (list (gnus-server-server-name)))
(unless server
(error "No server on current line"))
(unless (assoc server gnus-server-alist)
(error "This server can't be edited"))
(let ((winconf (current-window-configuration))
(info (cdr (assoc server gnus-server-alist))))
(gnus-close-server info)
(get-buffer-create gnus-server-edit-buffer)
(gnus-configure-windows 'edit-server)
(gnus-add-current-to-buffer-list)
(emacs-lisp-mode)
(make-local-variable 'gnus-prev-winconf)
(setq gnus-prev-winconf winconf)
(use-local-map (copy-keymap (current-local-map)))
(let ((done-func '(lambda ()
"Exit editing mode and update the information."
(interactive)
(gnus-server-edit-server-done 'group))))
(setcar (cdr (nth 4 done-func)) server)
(local-set-key "\C-c\C-c" done-func))
(erase-buffer)
(insert ";; Type `C-c C-c' after you have edited the server.\n\n")
(insert (pp-to-string info))))
(defun gnus-server-edit-server-done (server)
(interactive)
(set-buffer (get-buffer-create gnus-server-edit-buffer))
(goto-char (point-min))
(let ((form (read (current-buffer)))
(winconf gnus-prev-winconf))
(gnus-server-set-info server form)
(kill-buffer (current-buffer))
(and winconf (set-window-configuration winconf))
(set-buffer gnus-server-buffer)
(gnus-server-update-server server)
(gnus-server-list-servers)
(gnus-server-position-point)))
(defun gnus-server-read-server (server)
"Browse a server."
(interactive (list (gnus-server-server-name)))
(let ((buf (current-buffer)))
(prog1
(gnus-browse-foreign-server (gnus-server-to-method server) buf)
(save-excursion
(set-buffer buf)
(gnus-server-update-server (gnus-server-server-name))
(gnus-server-position-point)))))
(defun gnus-server-pick-server (e)
(interactive "e")
(mouse-set-point e)
(gnus-server-read-server (gnus-server-server-name)))
;;;
;;; Browse Server Mode
;;;
(defvar gnus-browse-menu-hook nil
"*Hook run after the creation of the browse mode menu.")
(defvar gnus-browse-mode-hook nil)
(defvar gnus-browse-mode-map nil)
(put 'gnus-browse-mode 'mode-class 'special)
(unless gnus-browse-mode-map
(setq gnus-browse-mode-map (make-keymap))
(suppress-keymap gnus-browse-mode-map)
(gnus-define-keys
gnus-browse-mode-map
" " gnus-browse-read-group
"=" gnus-browse-select-group
"n" gnus-browse-next-group
"p" gnus-browse-prev-group
"\177" gnus-browse-prev-group
"N" gnus-browse-next-group
"P" gnus-browse-prev-group
"\M-n" gnus-browse-next-group
"\M-p" gnus-browse-prev-group
"\r" gnus-browse-select-group
"u" gnus-browse-unsubscribe-current-group
"l" gnus-browse-exit
"L" gnus-browse-exit
"q" gnus-browse-exit
"Q" gnus-browse-exit
"\C-c\C-c" gnus-browse-exit
"?" gnus-browse-describe-briefly
"\C-c\C-i" gnus-info-find-node))
(defun gnus-browse-make-menu-bar ()
(gnus-visual-turn-off-edit-menu 'browse)
(or
(boundp 'gnus-browse-menu)
(progn
(easy-menu-define
gnus-browse-menu gnus-browse-mode-map ""
'("Browse"
["Subscribe" gnus-browse-unsubscribe-current-group t]
["Read" gnus-browse-read-group t]
["Select" gnus-browse-read-group t]
["Next" gnus-browse-next-group t]
["Prev" gnus-browse-next-group t]
["Exit" gnus-browse-exit t]
))
(run-hooks 'gnus-browse-menu-hook))))
(defvar gnus-browse-current-method nil)
(defvar gnus-browse-return-buffer nil)
(defvar gnus-browse-buffer "*Gnus Browse Server*")
(defun gnus-browse-foreign-server (method &optional return-buffer)
"Browse the server METHOD."
(setq gnus-browse-current-method method)
(setq gnus-browse-return-buffer return-buffer)
(let ((gnus-select-method method)
groups group)
(gnus-message 5 "Connecting to %s..." (nth 1 method))
(cond
((not (gnus-check-server method))
(gnus-message
1 "Unable to contact server: %s" (gnus-status-message method))
nil)
((not (gnus-request-list method))
(gnus-message
1 "Couldn't request list: %s" (gnus-status-message method))
nil)
(t
(get-buffer-create gnus-browse-buffer)
(gnus-add-current-to-buffer-list)
(and gnus-carpal (gnus-carpal-setup-buffer 'browse))
(gnus-configure-windows 'browse)
(buffer-disable-undo (current-buffer))
(let ((buffer-read-only nil))
(erase-buffer))
(gnus-browse-mode)
(setq mode-line-buffer-identification
(list
(format
"Gnus: %%b {%s:%s}" (car method) (cadr method))))
(save-excursion
(set-buffer nntp-server-buffer)
(let ((cur (current-buffer)))
(goto-char (point-min))
(or (string= gnus-ignored-newsgroups "")
(delete-matching-lines gnus-ignored-newsgroups))
(while (re-search-forward
"\\(^[^ \t]+\\)[ \t]+[0-9]+[ \t]+[0-9]+" nil t)
(goto-char (match-end 1))
(setq groups (cons (cons (match-string 1)
(max 0 (- (1+ (read cur)) (read cur))))
groups)))))
(setq groups (sort groups
(lambda (l1 l2)
(string< (car l1) (car l2)))))
(let ((buffer-read-only nil))
(while groups
(setq group (car groups))
(insert
(format "K%7d: %s\n" (cdr group) (car group)))
(setq groups (cdr groups))))
(switch-to-buffer (current-buffer))
(goto-char (point-min))
(gnus-group-position-point)
(gnus-message 5 "Connecting to %s...done" (nth 1 method))
t))))
(defun gnus-browse-mode ()
"Major mode for browsing a foreign server.
All normal editing commands are switched off.
\\<gnus-browse-mode-map>
The only things you can do in this buffer is
1) `\\[gnus-browse-unsubscribe-current-group]' to subscribe to a group.
The group will be inserted into the group buffer upon exit from this
buffer.
2) `\\[gnus-browse-read-group]' to read a group ephemerally.
3) `\\[gnus-browse-exit]' to return to the group buffer."
(interactive)
(kill-all-local-variables)
(when (and menu-bar-mode
(gnus-visual-p 'browse-menu 'menu))
(gnus-browse-make-menu-bar))
(gnus-simplify-mode-line)
(setq major-mode 'gnus-browse-mode)
(setq mode-name "Browse Server")
(setq mode-line-process nil)
(use-local-map gnus-browse-mode-map)
(buffer-disable-undo (current-buffer))
(setq truncate-lines t)
(setq buffer-read-only t)
(run-hooks 'gnus-browse-mode-hook))
(defun gnus-browse-read-group (&optional no-article)
"Enter the group at the current line."
(interactive)
(let ((group (gnus-browse-group-name)))
(or (gnus-group-read-ephemeral-group
group gnus-browse-current-method nil
(cons (current-buffer) 'browse))
(error "Couldn't enter %s" group))))
(defun gnus-browse-select-group ()
"Select the current group."
(interactive)
(gnus-browse-read-group 'no))
(defun gnus-browse-next-group (n)
"Go to the next group."
(interactive "p")
(prog1
(forward-line n)
(gnus-group-position-point)))
(defun gnus-browse-prev-group (n)
"Go to the next group."
(interactive "p")
(gnus-browse-next-group (- n)))
(defun gnus-browse-unsubscribe-current-group (arg)
"(Un)subscribe to the next ARG groups."
(interactive "p")
(when (eobp)
(error "No group at current line."))
(let ((ward (if (< arg 0) -1 1))
(arg (abs arg)))
(while (and (> arg 0)
(not (eobp))
(gnus-browse-unsubscribe-group)
(zerop (gnus-browse-next-group ward)))
(decf arg))
(gnus-group-position-point)
(if (/= 0 arg) (gnus-message 7 "No more newsgroups"))
arg))
(defun gnus-browse-group-name ()
(save-excursion
(beginning-of-line)
(when (re-search-forward ": \\(.*\\)$" (gnus-point-at-eol) t)
(gnus-group-prefixed-name (match-string 1) gnus-browse-current-method))))
(defun gnus-browse-unsubscribe-group ()
"Toggle subscription of the current group in the browse buffer."
(let ((sub nil)
(buffer-read-only nil)
group)
(save-excursion
(beginning-of-line)
;; If this group it killed, then we want to subscribe it.
(if (= (following-char) ?K) (setq sub t))
(setq group (gnus-browse-group-name))
(delete-char 1)
(if sub
(progn
(gnus-group-change-level
(list t group gnus-level-default-subscribed
nil nil gnus-browse-current-method)
gnus-level-default-subscribed gnus-level-killed
(and (car (nth 1 gnus-newsrc-alist))
(gnus-gethash (car (nth 1 gnus-newsrc-alist))
gnus-newsrc-hashtb))
t)
(insert ? ))
(gnus-group-change-level
group gnus-level-killed gnus-level-default-subscribed)
(insert ?K)))
t))
(defun gnus-browse-exit ()
"Quit browsing and return to the group buffer."
(interactive)
(when (eq major-mode 'gnus-browse-mode)
(kill-buffer (current-buffer)))
;; Insert the newly subscribed groups in the group buffer.
(save-excursion
(set-buffer gnus-group-buffer)
(gnus-group-list-groups nil))
(if gnus-browse-return-buffer
(gnus-configure-windows 'server 'force)
(gnus-configure-windows 'group 'force)))
(defun gnus-browse-describe-briefly ()
"Give a one line description of the group mode commands."
(interactive)
(gnus-message 6
(substitute-command-keys "\\<gnus-browse-mode-map>\\[gnus-group-next-group]:Forward \\[gnus-group-prev-group]:Backward \\[gnus-browse-exit]:Exit \\[gnus-info-find-node]:Run Info \\[gnus-browse-describe-briefly]:This help")))
(provide 'gnus-srvr)
;;; gnus-srvr.el ends here.

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,111 +0,0 @@
;;; gnus-vm.el --- vm interface for Gnus
;; Copyright (C) 1994,95,96 Free Software Foundation, Inc.
;; Author: Per Persson <pp@solace.mh.se>
;; Keywords: news, mail
;; This file is part of GNU Emacs.
;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Commentary:
;; Major contributors:
;; Christian Limpach <Christian.Limpach@nice.ch>
;; Some code stolen from:
;; Rick Sladkey <jrs@world.std.com>
;;; Code:
(require 'sendmail)
(require 'message)
(require 'gnus)
(require 'gnus-msg)
(eval-when-compile
(autoload 'vm-mode "vm")
(autoload 'vm-save-message "vm")
(autoload 'vm-forward-message "vm")
(autoload 'vm-reply "vm")
(autoload 'vm-mail "vm"))
(defvar gnus-vm-inhibit-window-system nil
"Inhibit loading `win-vm' if using a window-system.
Has to be set before gnus-vm is loaded.")
(or gnus-vm-inhibit-window-system
(condition-case nil
(if window-system
(require 'win-vm))
(error nil)))
(if (not (featurep 'vm))
(load "vm"))
(defun gnus-vm-make-folder (&optional buffer)
(let ((article (or buffer (current-buffer)))
(tmp-folder (generate-new-buffer " *tmp-folder*"))
(start (point-min))
(end (point-max)))
(set-buffer tmp-folder)
(insert-buffer-substring article start end)
(goto-char (point-min))
(if (looking-at "^\\(From [^ ]+ \\).*$")
(replace-match (concat "\\1" (current-time-string)))
(insert "From " gnus-newsgroup-name " "
(current-time-string) "\n"))
(while (re-search-forward "\n\nFrom " nil t)
(replace-match "\n\n>From "))
;; insert a newline, otherwise the last line gets lost
(goto-char (point-max))
(insert "\n")
(vm-mode)
tmp-folder))
(defun gnus-summary-save-article-vm (&optional arg)
"Append the current article to a vm folder.
If N is a positive number, save the N next articles.
If N is a negative number, save the N previous articles.
If N is nil and any articles have been marked with the process mark,
save those articles instead."
(interactive "P")
(let ((gnus-default-article-saver 'gnus-summary-save-in-vm))
(gnus-summary-save-article arg)))
(defun gnus-summary-save-in-vm (&optional folder)
(interactive)
(let ((default-name
(funcall gnus-mail-save-name gnus-newsgroup-name
gnus-current-headers gnus-newsgroup-last-mail)))
(setq folder
(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-original-article-buffer)
(save-excursion
(save-restriction
(widen)
(let ((vm-folder (gnus-vm-make-folder)))
(vm-save-message folder)
(kill-buffer vm-folder))))
;; Remember the directory name to save articles.
(setq gnus-newsgroup-last-mail folder)))
(provide 'gnus-vm)
;;; gnus-vm.el ends here.

17270
lisp/gnus.el

File diff suppressed because it is too large Load diff

View file

@ -1,124 +0,0 @@
;;; iso02-acc.el --- electric accent keys for Eastern Europe (ISO latin2)
;; Copyright (C) 1995 Free Software Foundation, Inc.
;; 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.
;;; Code:
(defvar iso-accents-list
'(((?' ?A) ?\301)
((?' ?C) ?\306)
((?' ?D) ?\320)
((?' ?E) ?\311)
((?' ?I) ?\315)
((?' ?L) ?\305)
((?' ?N) ?\321)
((?' ?O) ?\323)
((?' ?R) ?\300)
((?' ?S) ?\246)
((?' ?U) ?\332)
((?' ?Y) ?\335)
((?' ?Z) ?\254)
((?' ?a) ?\341)
((?' ?c) ?\346)
((?' ?d) ?\360)
((?' ?e) ?\351)
((?' ?i) ?\355)
((?' ?l) ?\345)
((?' ?n) ?\361)
((?' ?o) ?\363)
((?' ?r) ?\340)
((?' ?s) ?\266)
((?' ?u) ?\372)
((?' ?y) ?\375)
((?' ?z) ?\274)
((?' ?') ?\264)
((?' ? ) ?')
((?` ?A) ?\241)
((?` ?C) ?\307)
((?` ?E) ?\312)
((?` ?L) ?\243)
((?` ?S) ?\252)
((?` ?T) ?\336)
((?` ?Z) ?\257)
((?` ?a) ?\261)
((?` ?l) ?\263)
((?` ?c) ?\347)
((?` ?e) ?\352)
((?` ?s) ?\272)
((?` ?t) ?\376)
((?` ?z) ?\277)
((?` ? ) ?`)
((?` ?`) ?\252)
((?` ?.) ?\377)
((?^ ?A) ?\302)
((?^ ?O) ?\324)
((?^ ?a) ?\342)
((?^ ?o) ?\364)
((?^ ? ) ?^)
((?^ ?^) ?^) ; no special code?
((?\" ?A) ?\304)
((?\" ?E) ?\313)
((?\" ?O) ?\326)
((?\" ?U) ?\334)
((?\" ?a) ?\344)
((?\" ?e) ?\353)
((?\" ?o) ?\366)
((?\" ?s) ?\337)
((?\" ?u) ?\374)
((?\" ? ) ?\")
((?\" ?\") ?\250)
((?\~ ?A) ?\303)
((?\~ ?C) ?\310)
((?\~ ?D) ?\317)
((?\~ ?L) ?\245)
((?\~ ?N) ?\322)
((?\~ ?O) ?\325)
((?\~ ?R) ?\330)
((?\~ ?S) ?\251)
((?\~ ?T) ?\253)
((?\~ ?U) ?\333)
((?\~ ?Z) ?\256)
((?\~ ?a) ?\323)
((?\~ ?c) ?\350)
((?\~ ?d) ?\357)
((?\~ ?l) ?\265)
((?\~ ?n) ?\362)
((?\~ ?o) ?\365)
((?\~ ?r) ?\370)
((?\~ ?s) ?\271)
((?\~ ?t) ?\273)
((?\~ ?u) ?\373)
((?\~ ?z) ?\276)
((?\~ ?\ ) ?\~)
((?\~ ?v) ?\242) ;; v accent
((?\~ ?\~) ?\242) ;; v accent
((?\~ ?\.) ?\270) ;; cedilla accent
)
"Association list for ISO latin-2 accent combinations.")
(defvar iso-accents-enable '(?' ?` ?^ ?\" ?~)
"*List of accent keys that become prefixes in ISO Accents mode.
The default is (?' ?` ?^ ?\" ?~), which contains all the supported
accent keys. For certain languages, you might want to remove some of
those characters that are not actually used.")
(require 'iso-acc)
;;; iso02-acc.el ends here

File diff suppressed because it is too large Load diff

View file

@ -1,240 +0,0 @@
;;; mldrag.el --- mode line and vertical line dragging to resize windows
;; Copyright (C) 1994 Free Software Foundation, Inc.
;; Author: Kyle E. Jones <kyle@wonderworks.com>
;; Keywords: mouse
;; 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:
;; This package lets you drag the modeline, vertical bar and
;; scrollbar to resize windows. Suggested bindings are:
;;
;; (global-set-key [mode-line down-mouse-1] 'mldrag-drag-mode-line)
;; (global-set-key [vertical-line down-mouse-1] 'mldrag-drag-vertical-line)
;; (global-set-key [vertical-scroll-bar S-down-mouse-1]
;; 'mldrag-drag-vertical-line)
;;
;; Put the bindings and (require 'mldrag) in your .emacs file.
;;; Code:
(provide 'mldrag)
(defun mldrag-drag-mode-line (start-event)
"Change the height of the current window with the mouse.
This command should be bound to a down-mouse- event, and is most
usefully bound with the `mode-line' prefix. Holding down a mouse
button and moving the mouse up and down will make the clicked-on
window taller or shorter."
(interactive "e")
(let ((done nil)
(echo-keystrokes 0)
(start-event-frame (window-frame (car (car (cdr start-event)))))
(start-event-window (car (car (cdr start-event))))
(start-nwindows (count-windows t))
(old-selected-window (selected-window))
should-enlarge-minibuffer
event mouse minibuffer y top bot edges wconfig params growth)
(setq params (frame-parameters))
(if (and (not (setq minibuffer (cdr (assq 'minibuffer params))))
(one-window-p t))
(error "Attempt to resize sole window"))
(unwind-protect
(track-mouse
(progn
;; enlarge-window only works on the selected window, so
;; we must select the window where the start event originated.
;; unwind-protect will restore the old selected window later.
(select-window start-event-window)
;; if this is the bottommost ordinary window, then to
;; move its modeline the minibuffer must be enlarged.
(setq should-enlarge-minibuffer
(and minibuffer
(not (one-window-p t))
(= (nth 1 (window-edges minibuffer))
(nth 3 (window-edges)))))
;; loop reading events and sampling the position of
;; the mouse.
(while (not done)
(setq event (read-event)
mouse (mouse-position))
;; do nothing if
;; - there is a switch-frame event.
;; - the mouse isn't in the frame that we started in
;; - the mouse isn't in any Emacs frame
;; drag if
;; - there is a mouse-movement event
;; - there is a scroll-bar-movement event
;; (same as mouse movement for our purposes)
;; quit if
;; - there is a keyboard event or some other unknown event
;; unknown event.
(cond ((integerp event)
(setq done t))
((eq (car event) 'switch-frame)
nil)
((not (memq (car event)
'(mouse-movement scroll-bar-movement)))
(setq done t))
((not (eq (car mouse) start-event-frame))
nil)
((null (car (cdr mouse)))
nil)
(t
(setq y (cdr (cdr mouse))
edges (window-edges)
top (nth 1 edges)
bot (nth 3 edges))
;; scale back a move that would make the
;; window too short.
(cond ((< (- y top -1) window-min-height)
(setq y (+ top window-min-height -1))))
;; compute size change needed
(setq growth (- y bot -1)
wconfig (current-window-configuration))
;; grow/shrink minibuffer?
(if should-enlarge-minibuffer
(progn
;; yes. briefly select minibuffer so
;; enlarge-window will affect the
;; correct window.
(select-window minibuffer)
;; scale back shrinkage if it would
;; make the minibuffer less than 1
;; line tall.
(if (and (> growth 0)
(< (- (window-height minibuffer)
growth)
1))
(setq growth (1- (window-height minibuffer))))
(enlarge-window (- growth))
(select-window start-event-window))
;; no. grow/shrink the selected window
(enlarge-window growth))
;; if this window's growth caused another
;; window to be deleted because it was too
;; short, rescind the change.
;;
;; if size change caused space to be stolen
;; from a window above this one, rescind the
;; change, but only if we didn't grow/srhink
;; the minibuffer. minibuffer size changes
;; can cause all windows to shrink... no way
;; around it.
(if (or (/= start-nwindows (count-windows t))
(and (not should-enlarge-minibuffer)
(/= top (nth 1 (window-edges)))))
(set-window-configuration wconfig)))))))
;; restore the old selected window
(select-window old-selected-window))))
(defun mldrag-drag-vertical-line (start-event)
"Change the width of the current window with the mouse.
This command should be bound to a down-mouse- event, and is most
usefully bound with the `vertical-line' or the `vertical-scroll-bar'
prefix. Holding down a mouse button and moving the mouse left and
right will make the clicked-on window thinner or wider."
(interactive "e")
(let* ((done nil)
(echo-keystrokes 0)
(start-event-frame (window-frame (car (car (cdr start-event)))))
(scroll-bar-left
(eq (cdr (assq 'vertical-scroll-bars (frame-parameters))) 'left))
(start-event-window (car (car (cdr start-event))))
(start-nwindows (count-windows t))
(old-selected-window (selected-window))
event mouse x left right edges wconfig growth)
(if (one-window-p t)
(error "Attempt to resize sole ordinary window"))
(if scroll-bar-left
(when (= (nth 0 (window-edges start-event-window)) 0)
(error "Attempt to drag leftmost scrollbar"))
(when (>= (nth 2 (window-edges start-event-window))
(frame-width start-event-frame))
(error "Attempt to drag rightmost scrollbar")))
(unwind-protect
(track-mouse
(progn
;; enlarge-window only works on the selected window, so
;; we must select the window where the start event originated.
;; unwind-protect will restore the old selected window later.
(select-window start-event-window)
;; loop reading events and sampling the position of
;; the mouse.
(while (not done)
(setq event (read-event)
mouse (mouse-position))
;; do nothing if
;; - there is a switch-frame event.
;; - the mouse isn't in the frame that we started in
;; - the mouse isn't in any Emacs frame
;; drag if
;; - there is a mouse-movement event
;; - there is a scroll-bar-movement event
;; (same as mouse movement for our purposes)
;; quit if
;; - there is a keyboard event or some other unknown event
;; unknown event.
(cond ((integerp event)
(setq done t))
((eq (car event) 'switch-frame)
nil)
((not (memq (car event)
'(mouse-movement scroll-bar-movement)))
(setq done t))
((not (eq (car mouse) start-event-frame))
nil)
((null (car (cdr mouse)))
nil)
(t
(setq x (car (cdr mouse))
edges (window-edges)
left (nth 0 edges)
right (nth 2 edges))
;; scale back a move that would make the
;; window too thin.
(if scroll-bar-left
(cond ((< (- right x) window-min-width)
(setq x (- right window-min-width))))
(cond ((< (- x left -1) window-min-width)
(setq x (+ left window-min-width -1)))))
;; compute size change needed
(setq growth (if scroll-bar-left
(- left x)
(- x right -1))
wconfig (current-window-configuration))
(enlarge-window growth t)
;; if this window's growth caused another
;; window to be deleted because it was too
;; thin, rescind the change.
;;
;; if size change caused space to be stolen
;; from a window to the left of this one,
;; rescind the change.
(if (or (/= start-nwindows (count-windows t))
(if scroll-bar-left
(/= right (nth 2 (window-edges)))
(/= left (nth 0 (window-edges)))))
(set-window-configuration wconfig)))))))
;; restore the old selected window
(select-window old-selected-window))))
;; mldrag.el ends here

View file

@ -1,625 +0,0 @@
;;; nnbabyl.el --- rmail mbox access for Gnus
;; 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, mail
;; This file is part of GNU Emacs.
;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Commentary:
;; For an overview of what the interface functions do, please see the
;; Gnus sources.
;;; Code:
(require 'nnheader)
(require 'rmail)
(require 'nnmail)
(require 'nnoo)
(eval-when-compile (require 'cl))
(nnoo-declare nnbabyl)
(defvoo nnbabyl-mbox-file (expand-file-name "~/RMAIL")
"The name of the rmail box file in the users home directory.")
(defvoo nnbabyl-active-file (expand-file-name "~/.rmail-active")
"The name of the active file for the rmail box.")
(defvoo nnbabyl-get-new-mail t
"If non-nil, nnbabyl will check the incoming mail file and split the mail.")
(defvoo nnbabyl-prepare-save-mail-hook nil
"Hook run narrowed to an article before saving.")
(defvar nnbabyl-mail-delimiter "\^_")
(defconst nnbabyl-version "nnbabyl 1.0"
"nnbabyl version.")
(defvoo nnbabyl-mbox-buffer nil)
(defvoo nnbabyl-current-group nil)
(defvoo nnbabyl-status-string "")
(defvoo nnbabyl-group-alist nil)
(defvoo nnbabyl-active-timestamp nil)
(defvoo nnbabyl-previous-buffer-mode nil)
(eval-and-compile
(autoload 'gnus-set-text-properties "gnus-ems"))
;;; Interface functions
(nnoo-define-basics nnbabyl)
(deffoo nnbabyl-retrieve-headers (articles &optional group server fetch-old)
(save-excursion
(set-buffer nntp-server-buffer)
(erase-buffer)
(let ((number (length articles))
(count 0)
(delim (concat "^" nnbabyl-mail-delimiter))
article art-string start stop)
(nnbabyl-possibly-change-newsgroup group server)
(while (setq article (pop articles))
(setq art-string (nnbabyl-article-string article))
(set-buffer nnbabyl-mbox-buffer)
(beginning-of-line)
(when (or (search-forward art-string nil t)
(search-backward art-string nil t))
(re-search-backward delim nil t)
(while (and (not (looking-at ".+:"))
(zerop (forward-line 1))))
(setq start (point))
(search-forward "\n\n" nil t)
(setq stop (1- (point)))
(set-buffer nntp-server-buffer)
(insert "221 ")
(princ article (current-buffer))
(insert " Article retrieved.\n")
(insert-buffer-substring nnbabyl-mbox-buffer start stop)
(goto-char (point-max))
(insert ".\n"))
(and (numberp nnmail-large-newsgroup)
(> number nnmail-large-newsgroup)
(zerop (% (incf count) 20))
(nnheader-message 5 "nnbabyl: Receiving headers... %d%%"
(/ (* count 100) number))))
(and (numberp nnmail-large-newsgroup)
(> number nnmail-large-newsgroup)
(nnheader-message 5 "nnbabyl: Receiving headers...done"))
(set-buffer nntp-server-buffer)
(nnheader-fold-continuation-lines)
'headers)))
(deffoo nnbabyl-open-server (server &optional defs)
(nnoo-change-server 'nnbabyl server defs)
(cond
((not (file-exists-p nnbabyl-mbox-file))
(nnbabyl-close-server)
(nnheader-report 'nnbabyl "No such file: %s" nnbabyl-mbox-file))
((file-directory-p nnbabyl-mbox-file)
(nnbabyl-close-server)
(nnheader-report 'nnbabyl "Not a regular file: %s" nnbabyl-mbox-file))
(t
(nnheader-report 'nnbabyl "Opened server %s using mbox %s" server
nnbabyl-mbox-file)
t)))
(deffoo nnbabyl-close-server (&optional server)
;; Restore buffer mode.
(when (and (nnbabyl-server-opened)
nnbabyl-previous-buffer-mode)
(save-excursion
(set-buffer nnbabyl-mbox-buffer)
(narrow-to-region
(caar nnbabyl-previous-buffer-mode)
(cdar nnbabyl-previous-buffer-mode))
(funcall (cdr nnbabyl-previous-buffer-mode))))
(nnoo-close-server 'nnbabyl server)
(setq nnbabyl-mbox-buffer nil)
t)
(deffoo nnbabyl-server-opened (&optional server)
(and (nnoo-current-server-p 'nnbabyl server)
nnbabyl-mbox-buffer
(buffer-name nnbabyl-mbox-buffer)
nntp-server-buffer
(buffer-name nntp-server-buffer)))
(deffoo nnbabyl-request-article (article &optional newsgroup server buffer)
(nnbabyl-possibly-change-newsgroup newsgroup server)
(save-excursion
(set-buffer nnbabyl-mbox-buffer)
(goto-char (point-min))
(when (search-forward (nnbabyl-article-string article) nil t)
(let (start stop summary-line)
(re-search-backward (concat "^" nnbabyl-mail-delimiter) nil t)
(while (and (not (looking-at ".+:"))
(zerop (forward-line 1))))
(setq start (point))
(or (and (re-search-forward
(concat "^" nnbabyl-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 nnbabyl-mbox-buffer start stop)
(goto-char (point-min))
;; If there is an EOOH header, then we have to remove some
;; duplicated headers.
(setq summary-line (looking-at "Summary-line:"))
(when (search-forward "\n*** EOOH ***" nil t)
(if summary-line
;; The headers to be deleted are located before the
;; EOOH line...
(delete-region (point-min) (progn (forward-line 1)
(point)))
;; ...or after.
(delete-region (progn (beginning-of-line) (point))
(or (search-forward "\n\n" nil t)
(point)))))
(if (numberp article)
(cons nnbabyl-current-group article)
(nnbabyl-article-group-number)))))))
(deffoo nnbabyl-request-group (group &optional server dont-check)
(let ((active (cadr (assoc group nnbabyl-group-alist))))
(save-excursion
(cond
((or (null active)
(null (nnbabyl-possibly-change-newsgroup group server)))
(nnheader-report 'nnbabyl "No such group: %s" group))
(dont-check
(nnheader-report 'nnbabyl "Selected group %s" group)
(nnheader-insert ""))
(t
(nnheader-report 'nnbabyl "Selected group %s" group)
(nnheader-insert "211 %d %d %d %s\n"
(1+ (- (cdr active) (car active)))
(car active) (cdr active) group))))))
(deffoo nnbabyl-request-scan (&optional group server)
(nnbabyl-read-mbox)
(nnmail-get-new-mail
'nnbabyl
(lambda ()
(save-excursion
(set-buffer nnbabyl-mbox-buffer)
(save-buffer)))
nnbabyl-mbox-file group
(lambda ()
(save-excursion
(let ((in-buf (current-buffer)))
(goto-char (point-min))
(while (search-forward "\n\^_\n" nil t)
(delete-char -1))
(set-buffer nnbabyl-mbox-buffer)
(goto-char (point-max))
(search-backward "\n\^_" nil t)
(goto-char (match-end 0))
(insert-buffer-substring in-buf)))
(nnmail-save-active nnbabyl-group-alist nnbabyl-active-file))))
(deffoo nnbabyl-close-group (group &optional server)
t)
(deffoo nnbabyl-request-create-group (group &optional server)
(nnmail-activate 'nnbabyl)
(unless (assoc group nnbabyl-group-alist)
(setq nnbabyl-group-alist (cons (list group (cons 1 0))
nnbabyl-group-alist))
(nnmail-save-active nnbabyl-group-alist nnbabyl-active-file))
t)
(deffoo nnbabyl-request-list (&optional server)
(save-excursion
(nnmail-find-file nnbabyl-active-file)
(setq nnbabyl-group-alist (nnmail-get-active))))
(deffoo nnbabyl-request-newgroups (date &optional server)
(nnbabyl-request-list server))
(deffoo nnbabyl-request-list-newsgroups (&optional server)
(nnheader-report 'nnbabyl "nnbabyl: LIST NEWSGROUPS is not implemented."))
(deffoo nnbabyl-request-expire-articles
(articles newsgroup &optional server force)
(nnbabyl-possibly-change-newsgroup newsgroup server)
(let* ((is-old t)
rest)
(nnmail-activate 'nnbabyl)
(save-excursion
(set-buffer nnbabyl-mbox-buffer)
(gnus-set-text-properties (point-min) (point-max) nil)
(while (and articles is-old)
(goto-char (point-min))
(if (search-forward (nnbabyl-article-string (car articles)) nil t)
(if (setq is-old
(nnmail-expired-article-p
newsgroup
(buffer-substring
(point) (progn (end-of-line) (point))) force))
(progn
(nnheader-message 5 "Deleting article %d in %s..."
(car articles) newsgroup)
(nnbabyl-delete-mail))
(setq rest (cons (car articles) rest))))
(setq articles (cdr articles)))
(save-buffer)
;; Find the lowest active article in this group.
(let ((active (nth 1 (assoc newsgroup nnbabyl-group-alist))))
(goto-char (point-min))
(while (and (not (search-forward
(nnbabyl-article-string (car active)) nil t))
(<= (car active) (cdr active)))
(setcar active (1+ (car active)))
(goto-char (point-min))))
(nnmail-save-active nnbabyl-group-alist nnbabyl-active-file)
(nconc rest articles))))
(deffoo nnbabyl-request-move-article
(article group server accept-form &optional last)
(nnbabyl-possibly-change-newsgroup group server)
(let ((buf (get-buffer-create " *nnbabyl move*"))
result)
(and
(nnbabyl-request-article article group server)
(save-excursion
(set-buffer buf)
(insert-buffer-substring nntp-server-buffer)
(goto-char (point-min))
(if (re-search-forward
"^X-Gnus-Newsgroup:"
(save-excursion (search-forward "\n\n" nil t) (point)) t)
(delete-region (progn (beginning-of-line) (point))
(progn (forward-line 1) (point))))
(setq result (eval accept-form))
(kill-buffer (current-buffer))
result)
(save-excursion
(set-buffer nnbabyl-mbox-buffer)
(goto-char (point-min))
(if (search-forward (nnbabyl-article-string article) nil t)
(nnbabyl-delete-mail))
(and last (save-buffer))))
result))
(deffoo nnbabyl-request-accept-article (group &optional server last)
(nnbabyl-possibly-change-newsgroup group server)
(nnmail-check-syntax)
(let ((buf (current-buffer))
result beg)
(and
(nnmail-activate 'nnbabyl)
(save-excursion
(goto-char (point-min))
(search-forward "\n\n" nil t)
(forward-line -1)
(save-excursion
(while (re-search-backward "^X-Gnus-Newsgroup: " beg t)
(delete-region (point) (progn (forward-line 1) (point)))))
(let ((nnmail-split-methods
(if (stringp group) (list (list group ""))
nnmail-split-methods)))
(setq result (car (nnbabyl-save-mail))))
(set-buffer nnbabyl-mbox-buffer)
(goto-char (point-max))
(search-backward "\n\^_")
(goto-char (match-end 0))
(insert-buffer-substring buf)
(when last
(save-buffer)
(nnmail-save-active nnbabyl-group-alist nnbabyl-active-file))
result))))
(deffoo nnbabyl-request-replace-article (article group buffer)
(nnbabyl-possibly-change-newsgroup group)
(save-excursion
(set-buffer nnbabyl-mbox-buffer)
(goto-char (point-min))
(if (not (search-forward (nnbabyl-article-string article) nil t))
nil
(nnbabyl-delete-mail t t)
(insert-buffer-substring buffer)
(save-buffer)
t)))
(deffoo nnbabyl-request-delete-group (group &optional force server)
(nnbabyl-possibly-change-newsgroup group server)
;; Delete all articles in GROUP.
(if (not force)
() ; Don't delete the articles.
(save-excursion
(set-buffer nnbabyl-mbox-buffer)
(goto-char (point-min))
;; Delete all articles in this group.
(let ((ident (concat "\nX-Gnus-Newsgroup: " nnbabyl-current-group ":"))
found)
(while (search-forward ident nil t)
(setq found t)
(nnbabyl-delete-mail))
(and found (save-buffer)))))
;; Remove the group from all structures.
(setq nnbabyl-group-alist
(delq (assoc group nnbabyl-group-alist) nnbabyl-group-alist)
nnbabyl-current-group nil)
;; Save the active file.
(nnmail-save-active nnbabyl-group-alist nnbabyl-active-file)
t)
(deffoo nnbabyl-request-rename-group (group new-name &optional server)
(nnbabyl-possibly-change-newsgroup group server)
(save-excursion
(set-buffer nnbabyl-mbox-buffer)
(goto-char (point-min))
(let ((ident (concat "\nX-Gnus-Newsgroup: " nnbabyl-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 nnbabyl-group-alist)))
(and entry (setcar entry new-name))
(setq nnbabyl-current-group nil)
;; Save the new group alist.
(nnmail-save-active nnbabyl-group-alist nnbabyl-active-file)
t))
;;; Internal functions.
;; If FORCE, delete article no matter how many X-Gnus-Newsgroup
;; headers there are. If LEAVE-DELIM, don't delete the Unix mbox
;; delimiter line.
(defun nnbabyl-delete-mail (&optional force leave-delim)
;; Delete the current X-Gnus-Newsgroup line.
(or force
(delete-region
(progn (beginning-of-line) (point))
(progn (forward-line 1) (point))))
;; Beginning of the article.
(save-excursion
(save-restriction
(widen)
(narrow-to-region
(save-excursion
(re-search-backward (concat "^" nnbabyl-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 "^" nnbabyl-mail-delimiter)
nil t)
(if (and (not (bobp)) leave-delim)
(progn (forward-line -2) (point))
(match-beginning 0)))
(point-max))))
(goto-char (point-min))
;; Only delete the article if no other groups owns it as well.
(if (or force (not (re-search-forward "^X-Gnus-Newsgroup: " nil t)))
(delete-region (point-min) (point-max))))))
(defun nnbabyl-possibly-change-newsgroup (newsgroup &optional server)
(when (and server
(not (nnbabyl-server-opened server)))
(nnbabyl-open-server server))
(if (or (not nnbabyl-mbox-buffer)
(not (buffer-name nnbabyl-mbox-buffer)))
(save-excursion (nnbabyl-read-mbox)))
(or nnbabyl-group-alist
(nnmail-activate 'nnbabyl))
(if newsgroup
(if (assoc newsgroup nnbabyl-group-alist)
(setq nnbabyl-current-group newsgroup)
(nnheader-report 'nnbabyl "No such group in file"))
t))
(defun nnbabyl-article-string (article)
(if (numberp article)
(concat "\nX-Gnus-Newsgroup: " nnbabyl-current-group ":"
(int-to-string article) " ")
(concat "\nMessage-ID: " article)))
(defun nnbabyl-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 nnbabyl-insert-lines ()
"Insert how many lines and chars there are in the body of the mail."
(let (lines chars)
(save-excursion
(goto-char (point-min))
(when (search-forward "\n\n" nil t)
;; There may be an EOOH line here...
(when (looking-at "\\*\\*\\* EOOH \\*\\*\\*")
(search-forward "\n\n" nil t))
(setq chars (- (point-max) (point))
lines (max (- (count-lines (point) (point-max)) 1) 0))
;; Move back to the end of the headers.
(goto-char (point-min))
(search-forward "\n\n" nil t)
(forward-char -1)
(save-excursion
(when (re-search-backward "^Lines: " nil t)
(delete-region (point) (progn (forward-line 1) (point)))))
(insert (format "Lines: %d\n" lines))
chars))))
(defun nnbabyl-save-mail ()
;; Called narrowed to an article.
(let ((group-art (nreverse (nnmail-article-group 'nnbabyl-active-number))))
(nnbabyl-insert-lines)
(nnmail-insert-xref group-art)
(nnbabyl-insert-newsgroup-line group-art)
(run-hooks 'nnbabyl-prepare-save-mail-hook)
group-art))
(defun nnbabyl-insert-newsgroup-line (group-art)
(save-excursion
(goto-char (point-min))
(while (looking-at "From ")
(replace-match "Mail-from: From " t t)
(forward-line 1))
;; If there is a C-l at the beginning of the narrowed region, this
;; isn't really a "save", but rather a "scan".
(goto-char (point-min))
(or (looking-at "\^L")
(save-excursion
(insert "\^L\n0, unseen,,\n*** EOOH ***\n")
(goto-char (point-max))
(insert "\^_\n")))
(if (search-forward "\n\n" nil t)
(progn
(forward-char -1)
(while group-art
(insert (format "X-Gnus-Newsgroup: %s:%d %s\n"
(caar group-art) (cdar group-art)
(current-time-string)))
(setq group-art (cdr group-art)))))
t))
(defun nnbabyl-active-number (group)
;; Find the next article number in GROUP.
(let ((active (cadr (assoc group nnbabyl-group-alist))))
(if active
(setcdr active (1+ (cdr active)))
;; This group is new, so we create a new entry for it.
;; This might be a bit naughty... creating groups on the drop of
;; a hat, but I don't know...
(setq nnbabyl-group-alist (cons (list group (setq active (cons 1 1)))
nnbabyl-group-alist)))
(cdr active)))
(defun nnbabyl-read-mbox ()
(nnmail-activate 'nnbabyl)
(unless (file-exists-p nnbabyl-mbox-file)
;; Create a new, empty RMAIL mbox file.
(save-excursion
(set-buffer (setq nnbabyl-mbox-buffer
(create-file-buffer nnbabyl-mbox-file)))
(setq buffer-file-name nnbabyl-mbox-file)
(insert "BABYL OPTIONS:\n\n\^_")
(write-region (point-min) (point-max) nnbabyl-mbox-file t 'nomesg)))
(if (and nnbabyl-mbox-buffer
(buffer-name nnbabyl-mbox-buffer)
(save-excursion
(set-buffer nnbabyl-mbox-buffer)
(= (buffer-size) (nnheader-file-size nnbabyl-mbox-file))))
() ; This buffer hasn't changed since we read it last. Possibly.
(save-excursion
(let ((delim (concat "^" nnbabyl-mail-delimiter))
(alist nnbabyl-group-alist)
start end number)
(set-buffer (setq nnbabyl-mbox-buffer
(nnheader-find-file-noselect
nnbabyl-mbox-file nil 'raw)))
;; Save previous buffer mode.
(setq nnbabyl-previous-buffer-mode
(cons (cons (point-min) (point-max))
major-mode))
(buffer-disable-undo (current-buffer))
(widen)
(setq buffer-read-only nil)
(fundamental-mode)
;; Go through the group alist and compare against
;; the rmail 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)))
;; We go through the mbox and make sure that each and
;; every mail belongs to some group or other.
(goto-char (point-min))
(re-search-forward delim nil t)
(setq start (match-end 0))
(while (re-search-forward delim nil t)
(setq end (match-end 0))
(unless (search-backward "\nX-Gnus-Newsgroup: " start t)
(goto-char end)
(save-excursion
(save-restriction
(narrow-to-region (goto-char start) end)
(nnbabyl-save-mail)
(setq end (point-max)))))
(goto-char (setq start end)))
(when (buffer-modified-p (current-buffer))
(save-buffer))
(nnmail-save-active nnbabyl-group-alist nnbabyl-active-file)))))
(defun nnbabyl-remove-incoming-delims ()
(goto-char (point-min))
(while (search-forward "\^_" nil t)
(replace-match "?" t t)))
(defun nnbabyl-check-mbox ()
"Go through the nnbabyl mbox and make sure that no article numbers are reused."
(interactive)
(let ((idents (make-vector 1000 0))
id)
(save-excursion
(when (or (not nnbabyl-mbox-buffer)
(not (buffer-name nnbabyl-mbox-buffer)))
(nnbabyl-read-mbox))
(set-buffer nnbabyl-mbox-buffer)
(goto-char (point-min))
(while (re-search-forward "^X-Gnus-Newsgroup: \\([^ ]+\\) " nil t)
(if (intern-soft (setq id (match-string 1)) idents)
(progn
(delete-region (progn (beginning-of-line) (point))
(progn (forward-line 1) (point)))
(nnheader-message 7 "Moving %s..." id)
(nnbabyl-save-mail))
(intern id idents)))
(when (buffer-modified-p (current-buffer))
(save-buffer))
(nnmail-save-active nnbabyl-group-alist nnbabyl-active-file)
(message ""))))
(provide 'nnbabyl)
;;; nnbabyl.el ends here

View file

@ -1,229 +0,0 @@
;;; nndb.el --- nndb access for Gnus
;; Copyright (C) 1996 Free Software Foundation, Inc.
;; Author: Kai Grossjohann <grossjohann@ls6.informatik.uni-dortmund.de>
;; Keywords: news
;; 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:
;; I have shamelessly snarfed the code of nntp.el from sgnus.
;; Kai
;;-
;; Register nndb with known select methods.
(setq gnus-valid-select-methods
(cons '("nndb" mail address respool prompt-address)
gnus-valid-select-methods))
;;; Code:
(require 'nnheader)
(require 'nntp)
(eval-when-compile (require 'cl))
(eval-and-compile
(unless (fboundp 'open-network-stream)
(require 'tcp)))
(eval-when-compile (require 'cl))
(eval-and-compile
(autoload 'news-setup "rnewspost")
(autoload 'news-reply-mode "rnewspost")
(autoload 'cancel-timer "timer")
(autoload 'telnet "telnet" nil t)
(autoload 'telnet-send-input "telnet" nil t)
(autoload 'timezone-parse-date "timezone"))
;; Declare nndb as derived from nntp
(nnoo-declare nndb nntp)
;; Variables specific to nndb
;;- currently not used but just in case...
(defvoo nndb-deliver-program "nndel"
"*The program used to put a message in an NNDB group.")
;; Variables copied from nntp
(defvoo nndb-server-opened-hook '(nntp-send-authinfo-from-file)
"Like nntp-server-opened-hook."
nntp-server-opened-hook)
;(defvoo nndb-rlogin-parameters '("telnet" "${NNDBSERVER:=localhost}" "9000")
; "*Parameters to nndb-open-login. Like nntp-rlogin-parameters."
; nntp-rlogin-parameters)
;(defvoo nndb-rlogin-user-name nil
; "*User name for rlogin connect method."
; nntp-rlogin-user-name)
(defvoo nndb-address "localhost"
"*The name of the NNDB server."
nntp-address)
(defvoo nndb-port-number 9000
"*Port number to connect to."
nntp-port-number)
;(defvoo nndb-current-group ""
; "Like nntp-current-group."
; nntp-current-group)
(defvoo nndb-status-string nil "" nntp-status-string)
(defconst nndb-version "nndb 0.3"
"Version numbers of this version of NNDB.")
;;; Interface functions.
(nnoo-define-basics nndb)
;; Import other stuff from nntp as is.
(nnoo-import nndb
(nntp))
;;- maybe this should be mail??
;;-(defun nndb-request-type (group &optional article)
;;- 'news)
;;------------------------------------------------------------------
;;- only new stuff below
; nndb-request-update-info does not exist and is not needed
; nndb-request-update-mark does not exist and is not needed
; nndb-request-scan does not exist
; get new mail from somewhere -- maybe this is not needed?
; --> todo
(deffoo nndb-request-create-group (group &optional server)
"Creates a group if it doesn't exist yet."
(nntp-send-command "^[23].*\n" "MKGROUP" group))
; todo -- use some other time than the creation time of the article
; best is time since article has been marked as expirable
(deffoo nndb-request-expire-articles
(articles &optional group server force)
"Expires ARTICLES from GROUP on SERVER.
If FORCE, delete regardless of exiration date, otherwise use normal
expiry mechanism."
(let (msg art)
(nntp-possibly-change-server group server) ;;-
(while articles
(setq art (pop articles))
(nntp-send-command "^\\([23]\\|^423\\).*\n" "DATE" art)
(setq msg (nndb-status-message))
;; CCC we shouldn't be using the variable nndb-status-string?
(if (string-match "^423" (nnheader-get-report 'nndb))
()
(or (string-match "\\([0-9]+\\) \\([0-9]+\\)$" msg)
(error "Not a valid response for DATE command: %s"
msg))
(if (nnmail-expired-article-p
group
(list (string-to-int
(substring msg (match-beginning 1) (match-end 1)))
(string-to-int
(substring msg (match-beginning 2) (match-end 2))))
force)
(nnheader-message 5 "Deleting article %s in %s..."
art group)
(nntp-send-command "^[23].*\n" "DELETE" art))))))
(deffoo nndb-request-move-article
(article group server accept-form &optional last)
"Move ARTICLE (a number) from GROUP on SERVER.
Evals ACCEPT-FORM in current buffer, where the article is.
Optional LAST is ignored."
(let ((artbuf (get-buffer-create " *nndb move*"))
result)
(and
(nndb-request-article article group server artbuf)
(save-excursion
(set-buffer artbuf)
(setq result (eval accept-form))
(kill-buffer (current-buffer))
result)
(nndb-request-expire-articles (list article)
group
server
t))
result))
(deffoo nndb-request-accept-article (group server &optional last)
"The article in the current buffer is put into GROUP."
(nntp-possibly-change-server group server) ;;-
(let (art statmsg)
(when (nntp-send-command "^[23].*\r?\n" "ACCEPT" group)
(nnheader-insert "")
(nntp-encode-text)
(nntp-send-region-to-server (point-min) (point-max))
;; 1.2a NNTP's post command is buggy. "^M" (\r) is not
;; appended to end of the status message.
(nntp-wait-for-response "^[23].*\n")
(setq statmsg (nntp-status-message))
(or (string-match "^\\([0-9]+\\)" statmsg)
(error "nndb: %s" statmsg))
(setq art (substring statmsg
(match-beginning 1)
(match-end 1)))
(message "nndb: accepted %s" art)
(list art))))
(deffoo nndb-request-replace-article (article group buffer)
"ARTICLE is the number of the article in GROUP to be replaced
with the contents of the BUFFER."
(set-buffer buffer)
(let (art statmsg)
(when (nntp-send-command "^[23].*\r?\n" "REPLACE" (int-to-string article))
(nnheader-insert "")
(nntp-encode-text)
(nntp-send-region-to-server (point-min) (point-max))
;; 1.2a NNTP's post command is buggy. "^M" (\r) is not
;; appended to end of the status message.
(nntp-wait-for-response "^[23].*\n")
; (setq statmsg (nntp-status-message))
; (or (string-match "^\\([0-9]+\\)" statmsg)
; (error "nndb: %s" statmsg))
; (setq art (substring statmsg
; (match-beginning 1)
; (match-end 1)))
; (message "nndb: replaced %s" art)
(list (int-to-string article)))))
; nndb-request-delete-group does not exist
; todo -- maybe later
; nndb-request-rename-group does not exist
; todo -- maybe later
(provide 'nndb)

View file

@ -1,99 +0,0 @@
;;; nndir.el --- single directory newsgroup access for Gnus
;; Copyright (C) 1995,96 Free Software Foundation, Inc.
;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
;; Keywords: news
;; This file is part of GNU Emacs.
;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Commentary:
;;; Code:
(require 'nnheader)
(require 'nnmh)
(require 'nnml)
(require 'nnoo)
(eval-when-compile (require 'cl))
(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")
;;; Interface functions.
(nnoo-define-basics nndir)
(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))))
(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)
;;; nndir.el ends here

View file

@ -1,482 +0,0 @@
;;; nndoc.el --- single file access for Gnus
;; 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.
;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Commentary:
;;; Code:
(require 'nnheader)
(require 'message)
(require 'nnmail)
(require 'nnoo)
(eval-when-compile (require 'cl))
(nnoo-declare nndoc)
(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'.")
(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 . "^From \\([^ \n]*\\(\\|\".*\"[^ \n]*\\)\\) ?\\([^ \n]*\\) *\\([^ ]*\\) *\\([0-9]*\\) *\\([0-9:]*\\) *\\([A-Z]?[A-Z]?[A-Z][A-Z]\\( DST\\)?\\|[-+]?[0-9][0-9][0-9][0-9]\\|\\) * [0-9][0-9]\\([0-9]*\\) *\\([A-Z]?[A-Z]?[A-Z][A-Z]\\( DST\\)?\\|[-+]?[0-9][0-9][0-9][0-9]\\|\\) *\\(remote from .*\\)?\n")
(article-begin-function . nndoc-mbox-article-begin)
(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))
))
(defvoo nndoc-file-begin nil)
(defvoo nndoc-first-article nil)
(defvoo nndoc-article-end nil)
(defvoo nndoc-article-begin nil)
(defvoo nndoc-article-begin-function 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)
(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.")
;;; Interface functions
(nnoo-define-basics nndoc)
(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")))
(nnheader-fold-continuation-lines)
'headers)))))
(deffoo nndoc-request-article (article &optional newsgroup server buffer)
(nndoc-possibly-change-buffer newsgroup server)
(save-excursion
(let ((buffer (or buffer nntp-server-buffer))
(entry (cdr (assq article nndoc-dissection-alist)))
beg)
(set-buffer buffer)
(erase-buffer)
(if (stringp article)
nil
(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))))
(deffoo nndoc-request-group (group &optional server dont-check)
"Select news GROUP."
(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)))))
(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)
(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)
(nnoo-close-server 'nndoc server)
(setq nndoc-dissection-alist nil)
t)
(deffoo nndoc-request-list (&optional server)
nil)
(deffoo nndoc-request-newgroups (date &optional server)
nil)
(deffoo nndoc-request-list-newsgroups (&optional server)
nil)
;;; Internal functions.
(defun nndoc-possibly-change-buffer (group source)
(let (buf)
(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.
;; `source' is either a string (a file name) or a buffer object.
(buf
(setq nndoc-current-buffer buf))
;; It's a totally new group.
((or (and (bufferp nndoc-address)
(buffer-name nndoc-address))
(and (stringp nndoc-address)
(file-exists-p nndoc-address)
(not (file-directory-p nndoc-address))))
(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)
(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)
(nndoc-set-delims)
(nndoc-dissect-buffer)))
(unless nndoc-current-buffer
(nndoc-close-server))
;; Return whether we managed to select a file.
nndoc-current-buffer))
;; 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))
(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-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)))
(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 nndoc-article-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)
(goto-char (point-min))
;; 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)
(if nndoc-article-begin-function
(funcall nndoc-article-begin-function)
(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))
(if nndoc-article-begin-function
(funcall nndoc-article-begin-function)
(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-article-begin ()
(when (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))
(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)
;;; nndoc.el ends here

View file

@ -1,356 +0,0 @@
;;; nneething.el --- random file access for Gnus
;; 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, mail
;; This file is part of GNU Emacs.
;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Commentary:
;; Based on nnspool.el by Masanobu UMEDA <umerin@flab.flab.fujitsu.junet>.
;; For an overview of what the interface functions do, please see the
;; Gnus sources.
;;; Code:
(require 'nnheader)
(require 'nnmail)
(require 'nnoo)
(eval-when-compile (require 'cl))
(nnoo-declare nneething)
(defvoo nneething-map-file-directory "~/.nneething/"
"*Where nneething stores the 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.")
(defvoo nneething-current-directory nil
"Current news group directory.")
(defvoo nneething-status-string "")
(defvoo nneething-group-alist nil)
(defvoo nneething-message-id-number 0)
(defvoo nneething-work-buffer " *nneething work*")
(defvoo nneething-directory nil)
(defvoo nneething-group nil)
(defvoo nneething-map nil)
(defvoo nneething-read-only nil)
(defvoo nneething-active nil)
;;; Interface functions.
(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 articles))
(count 0)
(large (and (numberp nnmail-large-newsgroup)
(> number nnmail-large-newsgroup)))
article file)
(if (stringp (car articles))
'headers
(while (setq article (pop articles))
(setq file (nneething-file-name article))
(when (and (file-exists-p file)
(or (file-directory-p file)
(not (zerop (nnheader-file-size file)))))
(insert (format "221 %d Article retrieved.\n" article))
(nneething-insert-head file)
(insert ".\n"))
(incf count)
(and large
(zerop (% count 20))
(message "nneething: Receiving headers... %d%%"
(/ (* count 100) number))))
(when large
(message "nneething: Receiving headers...done"))
(nnheader-fold-continuation-lines)
'headers))))
(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.
(not (file-directory-p file)) ; It's not a dir.
(save-excursion
(nnmail-find-file file) ; Insert the file in the nntp buf.
(or (nnheader-article-p) ; Either it's a real article...
(progn
(goto-char (point-min))
(nneething-make-head file (current-buffer)) ; ... or we fake some headers.
(insert "\n")))
t))))
(deffoo nneething-request-group (group &optional dir dont-check)
(nneething-possibly-change-directory group dir)
(unless dont-check
(nneething-create-mapping)
(if (> (car nneething-active) (cdr nneething-active))
(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)
(deffoo nneething-request-list (&optional server dir)
(nnheader-report 'nneething "LIST is not implemented."))
(deffoo nneething-request-newgroups (date &optional server)
(nnheader-report 'nneething "NEWSGROUPS is not implemented."))
(deffoo nneething-request-type (group &optional article)
'unknown)
(deffoo nneething-close-group (group &optional server)
(setq nneething-current-directory nil)
t)
;;; Internal functions.
(defun nneething-possibly-change-directory (group &optional dir)
(when group
(if (and nneething-group
(string= group nneething-group))
t
(let (entry)
(if (setq entry (assoc group nneething-group-alist))
(progn
(setq nneething-group group)
(setq nneething-directory (nth 1 entry))
(setq nneething-map (nth 2 entry))
(setq nneething-active (nth 3 entry)))
(setq nneething-group group)
(setq nneething-directory dir)
(setq nneething-map nil)
(setq nneething-active (cons 1 0))
(nneething-create-mapping)
(push (list group dir nneething-map nneething-active)
nneething-group-alist))))))
(defun nneething-map-file ()
;; We make sure that the .nneething directory exists.
(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.
(let ((map-file (nneething-map-file))
(files (directory-files nneething-directory))
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)))
;; 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 (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))
(setq nneething-map (cdr nneething-map))))
(setq map (cdr map))))
;; Find all new files and enter them into the map.
(while files
(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)))
(when (and touched
(not nneething-read-only))
(save-excursion
(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))))))
(defun nneething-insert-head (file)
"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 &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 (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 &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-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)
(buffer-disable-undo (current-buffer))
(erase-buffer)
(cond
((not (file-exists-p file))
;; The file do not exist.
nil)
((or (file-directory-p file)
(file-symlink-p file))
;; It's a dir, so we fudge a head.
(nneething-make-head file) t)
(t
;; We examine the file.
(nnheader-insert-head file)
(if (nnheader-article-p)
(delete-region
(progn
(goto-char (point-min))
(or (and (search-forward "\n\n" nil t)
(1- (point)))
(point-max)))
(point-max))
(goto-char (point-min))
(nneething-make-head file (current-buffer))
(delete-region (point) (point-max)))
t))))
(defun nneething-file-name (article)
"Return the file name of ARTICLE."
(concat (file-name-as-directory nneething-directory)
(if (numberp article)
(cadr (assq article nneething-map))
article)))
(provide 'nneething)
;;; nneething.el ends here

View file

@ -1,784 +0,0 @@
;;; nnfolder.el --- mail folder access for Gnus
;; Copyright (C) 1995,96 Free Software Foundation, Inc.
;; Author: Scott Byer <byer@mv.us.adobe.com>
;; Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
;; Masanobu UMEDA <umerin@flab.flab.fujitsu.junet>
;; Keywords: mail
;; This file is part of GNU Emacs.
;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Commentary:
;; For an overview of what the interface functions do, please see the
;; Gnus sources.
;; Various enhancements by byer@mv.us.adobe.com (Scott Byer).
;;; Code:
(require 'nnheader)
(require 'message)
(require 'nnmail)
(require 'nnoo)
(eval-when-compile (require 'cl))
(nnoo-declare nnfolder)
(defvoo nnfolder-directory (expand-file-name message-directory)
"The name of the nnfolder directory.")
(defvoo nnfolder-active-file
(nnheader-concat nnfolder-directory "active")
"The name of the active file.")
;; I renamed this variable to something more in keeping with the general GNU
;; style. -SLB
(defvoo nnfolder-ignore-active-file nil
"If non-nil, causes nnfolder to do some extra work in order to determine
the true active ranges of an mbox file. Note that the active file is still
saved, but its values are not used. This costs some extra time when
scanning an mbox when opening it.")
(defvoo nnfolder-distrust-mbox nil
"If non-nil, causes nnfolder to not trust the user with respect to
inserting unaccounted for mail in the middle of an mbox file. This can greatly
slow down scans, which now must scan the entire file for unmarked messages.
When nil, scans occur forward from the last marked message, a huge
time saver for large mailboxes.")
(defvoo nnfolder-newsgroups-file
(concat (file-name-as-directory nnfolder-directory) "newsgroups")
"Mail newsgroups description file.")
(defvoo nnfolder-get-new-mail t
"If non-nil, nnfolder will check the incoming mail file and split the mail.")
(defvoo nnfolder-prepare-save-mail-hook nil
"Hook run narrowed to an article before saving.")
(defvoo nnfolder-save-buffer-hook nil
"Hook run before saving the nnfolder mbox buffer.")
(defvoo nnfolder-inhibit-expiry nil
"If non-nil, inhibit expiry.")
(defconst nnfolder-version "nnfolder 1.0"
"nnfolder version.")
(defconst nnfolder-article-marker "X-Gnus-Article-Number: "
"String used to demarcate what the article number for a message is.")
(defvoo nnfolder-current-group nil)
(defvoo nnfolder-current-buffer nil)
(defvoo nnfolder-status-string "")
(defvoo nnfolder-group-alist nil)
(defvoo nnfolder-buffer-alist nil)
(defvoo nnfolder-scantime-alist nil)
;;; Interface functions
(nnoo-define-basics nnfolder)
(deffoo nnfolder-retrieve-headers (articles &optional group server fetch-old)
(save-excursion
(set-buffer nntp-server-buffer)
(erase-buffer)
(let ((delim-string (concat "^" message-unix-mail-delimiter))
article art-string start stop)
(nnfolder-possibly-change-group group server)
(when nnfolder-current-buffer
(set-buffer nnfolder-current-buffer)
(goto-char (point-min))
(if (stringp (car articles))
'headers
(while articles
(setq article (car articles))
(setq art-string (nnfolder-article-string article))
(set-buffer nnfolder-current-buffer)
(if (or (search-forward art-string nil t)
;; Don't search the whole file twice! Also, articles
;; probably have some locality by number, so searching
;; backwards will be faster. Especially if we're at the
;; beginning of the buffer :-). -SLB
(search-backward art-string nil t))
(progn
(setq start (or (re-search-backward delim-string 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 nnfolder-current-buffer start stop)
(goto-char (point-max))
(insert ".\n")))
(setq articles (cdr articles)))
(set-buffer nntp-server-buffer)
(nnheader-fold-continuation-lines)
'headers)))))
(deffoo nnfolder-open-server (server &optional defs)
(nnoo-change-server 'nnfolder server defs)
(when (not (file-exists-p nnfolder-directory))
(condition-case ()
(make-directory nnfolder-directory t)
(error t)))
(cond
((not (file-exists-p nnfolder-directory))
(nnfolder-close-server)
(nnheader-report 'nnfolder "Couldn't create directory: %s"
nnfolder-directory))
((not (file-directory-p (file-truename nnfolder-directory)))
(nnfolder-close-server)
(nnheader-report 'nnfolder "Not a directory: %s" nnfolder-directory))
(t
(nnheader-report 'nnfolder "Opened server %s using directory %s"
server nnfolder-directory)
t)))
(deffoo nnfolder-request-close ()
(let ((alist nnfolder-buffer-alist))
(while alist
(nnfolder-close-group (caar alist) nil t)
(setq alist (cdr alist))))
(nnoo-close-server 'nnfolder)
(setq nnfolder-buffer-alist nil
nnfolder-group-alist nil))
(deffoo nnfolder-request-article (article &optional group server buffer)
(nnfolder-possibly-change-group group server)
(save-excursion
(set-buffer nnfolder-current-buffer)
(goto-char (point-min))
(if (search-forward (nnfolder-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)
(insert-buffer-substring nnfolder-current-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 nnfolder-current-group article)
(goto-char (point-min))
(search-forward (concat "\n" nnfolder-article-marker))
(cons nnfolder-current-group
(string-to-int
(buffer-substring
(point) (progn (end-of-line) (point)))))))))))
(deffoo nnfolder-request-group (group &optional server dont-check)
(save-excursion
(nnmail-activate 'nnfolder)
(if (not (assoc group nnfolder-group-alist))
(nnheader-report 'nnfolder "No such group: %s" group)
(nnfolder-possibly-change-group group server)
(if dont-check
(progn
(nnheader-report 'nnfolder "Selected group %s" group)
t)
(let* ((active (assoc group nnfolder-group-alist))
(group (car active))
(range (cadr active)))
(cond
((null active)
(nnheader-report 'nnfolder "No such group: %s" group))
((null nnfolder-current-group)
(nnheader-report 'nnfolder "Empty group: %s" group))
(t
(nnheader-report 'nnfolder "Selected group %s" group)
(nnheader-insert "211 %d %d %d %s\n"
(1+ (- (cdr range) (car range)))
(car range) (cdr range) group))))))))
(deffoo nnfolder-request-scan (&optional group server)
(nnfolder-possibly-change-group group server t)
(nnmail-get-new-mail
'nnfolder
(lambda ()
(let ((bufs nnfolder-buffer-alist))
(save-excursion
(while bufs
(if (not (buffer-name (nth 1 (car bufs))))
(setq nnfolder-buffer-alist
(delq (car bufs) nnfolder-buffer-alist))
(set-buffer (nth 1 (car bufs)))
(nnfolder-save-buffer)
(kill-buffer (current-buffer)))
(setq bufs (cdr bufs))))))
nnfolder-directory
group))
;; Don't close the buffer if we're not shutting down the server. This way,
;; we can keep the buffer in the group buffer cache, and not have to grovel
;; over the buffer again unless we add new mail to it or modify it in some
;; way.
(deffoo nnfolder-close-group (group &optional server force)
;; Make sure we _had_ the group open.
(when (or (assoc group nnfolder-buffer-alist)
(equal group nnfolder-current-group))
(let ((inf (assoc group nnfolder-buffer-alist)))
(when inf
(when nnfolder-current-group
(push (list nnfolder-current-group nnfolder-current-buffer)
nnfolder-buffer-alist))
(setq nnfolder-buffer-alist
(delq inf nnfolder-buffer-alist))
(setq nnfolder-current-buffer (cadr inf)
nnfolder-current-group (car inf))))
(when (and nnfolder-current-buffer
(buffer-name nnfolder-current-buffer))
(save-excursion
(set-buffer nnfolder-current-buffer)
;; If the buffer was modified, write the file out now.
(nnfolder-save-buffer)
;; If we're shutting the server down, we need to kill the
;; buffer and remove it from the open buffer list. Or, of
;; course, if we're trying to minimize our space impact.
(kill-buffer (current-buffer))
(setq nnfolder-buffer-alist (delq (assoc group nnfolder-buffer-alist)
nnfolder-buffer-alist)))))
(setq nnfolder-current-group nil
nnfolder-current-buffer nil)
t)
(deffoo nnfolder-request-create-group (group &optional server)
(nnfolder-possibly-change-group nil server)
(nnmail-activate 'nnfolder)
(when group
(unless (assoc group nnfolder-group-alist)
(push (list group (cons 1 0)) nnfolder-group-alist)
(nnmail-save-active nnfolder-group-alist nnfolder-active-file)))
t)
(deffoo nnfolder-request-list (&optional server)
(nnfolder-possibly-change-group nil server)
(save-excursion
(nnmail-find-file nnfolder-active-file)
(setq nnfolder-group-alist (nnmail-get-active))))
(deffoo nnfolder-request-newgroups (date &optional server)
(nnfolder-possibly-change-group nil server)
(nnfolder-request-list server))
(deffoo nnfolder-request-list-newsgroups (&optional server)
(nnfolder-possibly-change-group nil server)
(save-excursion
(nnmail-find-file nnfolder-newsgroups-file)))
(deffoo nnfolder-request-expire-articles
(articles newsgroup &optional server force)
(nnfolder-possibly-change-group newsgroup server)
(let* ((is-old t)
rest)
(nnmail-activate 'nnfolder)
(save-excursion
(set-buffer nnfolder-current-buffer)
(while (and articles is-old)
(goto-char (point-min))
(if (search-forward (nnfolder-article-string (car articles)) nil t)
(if (setq is-old
(nnmail-expired-article-p
newsgroup
(buffer-substring
(point) (progn (end-of-line) (point)))
force nnfolder-inhibit-expiry))
(progn
(nnheader-message 5 "Deleting article %d..."
(car articles) newsgroup)
(nnfolder-delete-mail))
(setq rest (cons (car articles) rest))))
(setq articles (cdr articles)))
(nnfolder-save-buffer)
;; Find the lowest active article in this group.
(let* ((active (cadr (assoc newsgroup nnfolder-group-alist)))
(marker (concat "\n" nnfolder-article-marker))
(number "[0-9]+")
(activemin (cdr active)))
(goto-char (point-min))
(while (and (search-forward marker nil t)
(re-search-forward number nil t))
(setq activemin (min activemin
(string-to-number (buffer-substring
(match-beginning 0)
(match-end 0))))))
(setcar active activemin))
(nnmail-save-active nnfolder-group-alist nnfolder-active-file)
(nconc rest articles))))
(deffoo nnfolder-request-move-article
(article group server accept-form &optional last)
(nnfolder-possibly-change-group group server)
(let ((buf (get-buffer-create " *nnfolder move*"))
result)
(and
(nnfolder-request-article article group server)
(save-excursion
(set-buffer buf)
(buffer-disable-undo (current-buffer))
(erase-buffer)
(insert-buffer-substring nntp-server-buffer)
(goto-char (point-min))
(while (re-search-forward
(concat "^" nnfolder-article-marker)
(save-excursion (search-forward "\n\n" nil t) (point)) t)
(delete-region (progn (beginning-of-line) (point))
(progn (forward-line 1) (point))))
(setq result (eval accept-form))
(kill-buffer buf)
result)
(save-excursion
(nnfolder-possibly-change-group group server)
(set-buffer nnfolder-current-buffer)
(goto-char (point-min))
(if (search-forward (nnfolder-article-string article) nil t)
(nnfolder-delete-mail))
(and last (nnfolder-save-buffer))))
result))
(deffoo nnfolder-request-accept-article (group &optional server last)
(nnfolder-possibly-change-group group server)
(nnmail-check-syntax)
(and (stringp group) (nnfolder-possibly-change-group group))
(let ((buf (current-buffer))
result)
(goto-char (point-min))
(when (looking-at "X-From-Line: ")
(replace-match "From "))
(and
(nnfolder-request-list)
(save-excursion
(set-buffer buf)
(goto-char (point-min))
(search-forward "\n\n" nil t)
(forward-line -1)
(while (re-search-backward (concat "^" nnfolder-article-marker) nil t)
(delete-region (point) (progn (forward-line 1) (point))))
(setq result (car (nnfolder-save-mail (and (stringp group) group)))))
(save-excursion
(set-buffer nnfolder-current-buffer)
(and last (nnfolder-save-buffer))))
(nnmail-save-active nnfolder-group-alist nnfolder-active-file)
(unless result
(nnheader-report 'nnfolder "Couldn't store article"))
result))
(deffoo nnfolder-request-replace-article (article group buffer)
(nnfolder-possibly-change-group group)
(save-excursion
(set-buffer nnfolder-current-buffer)
(goto-char (point-min))
(if (not (search-forward (nnfolder-article-string article) nil t))
nil
(nnfolder-delete-mail t t)
(insert-buffer-substring buffer)
(nnfolder-save-buffer)
t)))
(deffoo nnfolder-request-delete-group (group &optional force server)
(nnfolder-close-group group server t)
;; Delete all articles in GROUP.
(if (not force)
() ; Don't delete the articles.
;; Delete the file that holds the group.
(condition-case nil
(delete-file (nnfolder-group-pathname group))
(error nil)))
;; Remove the group from all structures.
(setq nnfolder-group-alist
(delq (assoc group nnfolder-group-alist) nnfolder-group-alist)
nnfolder-current-group nil
nnfolder-current-buffer nil)
;; Save the active file.
(nnmail-save-active nnfolder-group-alist nnfolder-active-file)
t)
(deffoo nnfolder-request-rename-group (group new-name &optional server)
(nnfolder-possibly-change-group group server)
(save-excursion
(set-buffer nnfolder-current-buffer)
(and (file-writable-p buffer-file-name)
(condition-case ()
(progn
(rename-file
buffer-file-name
(nnfolder-group-pathname new-name))
t)
(error nil))
;; That went ok, so we change the internal structures.
(let ((entry (assoc group nnfolder-group-alist)))
(and entry (setcar entry new-name))
(setq nnfolder-current-buffer nil
nnfolder-current-group nil)
;; Save the new group alist.
(nnmail-save-active nnfolder-group-alist nnfolder-active-file)
;; We kill the buffer instead of renaming it and stuff.
(kill-buffer (current-buffer))
t))))
;;; Internal functions.
(defun nnfolder-article-string (article)
(if (numberp article)
(concat "\n" nnfolder-article-marker (int-to-string article) " ")
(concat "\nMessage-ID: " article)))
(defun nnfolder-delete-mail (&optional force leave-delim)
"Delete the message that point is in."
(save-excursion
(delete-region
(save-excursion
(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)
(if (re-search-forward (concat "^" message-unix-mail-delimiter) nil t)
(if (and (not (bobp)) leave-delim)
(progn (forward-line -2) (point))
(match-beginning 0))
(point-max))))))
;; When scanning, we're not looking t immediately switch into the group - if
;; we know our information is up to date, don't even bother reading the file.
(defun nnfolder-possibly-change-group (group &optional server scanning)
(when (and server
(not (nnfolder-server-opened server)))
(nnfolder-open-server server))
(when (and group (or nnfolder-current-buffer
(not (equal group nnfolder-current-group))))
(unless (file-exists-p nnfolder-directory)
(make-directory (directory-file-name nnfolder-directory) t))
(nnfolder-possibly-activate-groups nil)
(or (assoc group nnfolder-group-alist)
(not (file-exists-p
(nnfolder-group-pathname group)))
(progn
(setq nnfolder-group-alist
(cons (list group (cons 1 0)) nnfolder-group-alist))
(nnmail-save-active nnfolder-group-alist nnfolder-active-file)))
(let (inf file)
(if (and (equal group nnfolder-current-group)
nnfolder-current-buffer
(buffer-name nnfolder-current-buffer))
()
(setq nnfolder-current-group group)
;; If we have to change groups, see if we don't already have the mbox
;; in memory. If we do, verify the modtime and destroy the mbox if
;; needed so we can rescan it.
(if (setq inf (assoc group nnfolder-buffer-alist))
(setq nnfolder-current-buffer (nth 1 inf)))
;; If the buffer is not live, make sure it isn't in the alist. If it
;; is live, verify that nobody else has touched the file since last
;; time.
(if (or (not (and nnfolder-current-buffer
(buffer-name nnfolder-current-buffer)))
(not (and (bufferp nnfolder-current-buffer)
(verify-visited-file-modtime
nnfolder-current-buffer))))
(progn
(if (and nnfolder-current-buffer
(buffer-name nnfolder-current-buffer)
(bufferp nnfolder-current-buffer))
(kill-buffer nnfolder-current-buffer))
(setq nnfolder-buffer-alist (delq inf nnfolder-buffer-alist))
(setq inf nil)))
(if inf
()
(save-excursion
(setq file (nnfolder-group-pathname group))
(if (file-directory-p (file-truename file))
()
(unless (file-exists-p file)
(unless (file-exists-p (file-name-directory file))
(make-directory (file-name-directory file) t))
(write-region 1 1 file t 'nomesg))
(setq nnfolder-current-buffer
(nnfolder-read-folder file scanning))
(if nnfolder-current-buffer
(progn
(set-buffer nnfolder-current-buffer)
(setq nnfolder-buffer-alist
(cons (list group nnfolder-current-buffer)
nnfolder-buffer-alist)))))))))
(setq nnfolder-current-group group)))
(defun nnfolder-save-mail (&optional group)
"Called narrowed to an article."
(let* ((nnmail-split-methods
(if group (list (list group "")) nnmail-split-methods))
(group-art-list
(nreverse (nnmail-article-group 'nnfolder-active-number)))
(delim (concat "^" message-unix-mail-delimiter))
save-list group-art)
(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 "> "))
(setq save-list group-art-list)
(nnmail-insert-lines)
(nnmail-insert-xref group-art-list)
(run-hooks 'nnmail-prepare-save-mail-hook)
(run-hooks 'nnfolder-prepare-save-mail-hook)
;; Insert the mail into each of the destination groups.
(while group-art-list
(setq group-art (car group-art-list)
group-art-list (cdr group-art-list))
;; Kill the previous newsgroup markers.
(goto-char (point-min))
(search-forward "\n\n" nil t)
(forward-line -1)
(while (search-backward (concat "\n" nnfolder-article-marker) nil t)
(delete-region (1+ (point)) (progn (forward-line 2) (point))))
(nnfolder-possibly-change-group (car group-art))
;; Insert the new newsgroup marker.
(nnfolder-insert-newsgroup-line group-art)
(unless nnfolder-current-buffer
(nnfolder-close-group (car group-art))
(nnfolder-request-create-group (car group-art))
(nnfolder-possibly-change-group (car group-art)))
(let ((beg (point-min))
(end (point-max))
(obuf (current-buffer)))
(set-buffer nnfolder-current-buffer)
(goto-char (point-max))
(unless (eolp)
(insert "\n"))
(insert "\n")
(insert-buffer-substring obuf beg end)
(set-buffer obuf)))
;; Did we save it anywhere?
save-list))
(defun nnfolder-insert-newsgroup-line (group-art)
(save-excursion
(goto-char (point-min))
(if (search-forward "\n\n" nil t)
(progn
(forward-char -1)
(insert (format (concat nnfolder-article-marker "%d %s\n")
(cdr group-art) (current-time-string)))))))
(defun nnfolder-possibly-activate-groups (&optional group)
(save-excursion
;; If we're looking for the activation of a specific group, find out
;; its real name and switch to it.
(if group (nnfolder-possibly-change-group group))
;; If the group alist isn't active, activate it now.
(nnmail-activate 'nnfolder)))
(defun nnfolder-active-number (group)
(when group
(save-excursion
;; Find the next article number in GROUP.
(prog1
(let ((active (cadr (assoc group nnfolder-group-alist))))
(if active
(setcdr active (1+ (cdr active)))
;; This group is new, so we create a new entry for it.
;; This might be a bit naughty... creating groups on the drop of
;; a hat, but I don't know...
(setq nnfolder-group-alist
(cons (list group (setq active (cons 1 1)))
nnfolder-group-alist)))
(cdr active))
(nnmail-save-active nnfolder-group-alist nnfolder-active-file)
(nnfolder-possibly-activate-groups group)))))
;; This method has a problem if you've accidentally let the active list get
;; out of sync with the files. This could happen, say, if you've
;; accidentally gotten new mail with something other than Gnus (but why
;; would _that_ ever happen? :-). In that case, we will be in the middle of
;; processing the file, ready to add new X-Gnus article number markers, and
;; we'll run across a message with no ID yet - the active list _may_not_ be
;; ready for us yet.
;; To handle this, I'm modifying this routine to maintain the maximum ID seen
;; so far, and when we hit a message with no ID, we will _manually_ scan the
;; rest of the message looking for any more, possibly higher IDs. We'll
;; assume the maximum that we find is the highest active. Note that this
;; shouldn't cost us much extra time at all, but will be a lot less
;; vulnerable to glitches between the mbox and the active file.
(defun nnfolder-read-folder (file &optional scanning)
;; This is an attempt at a serious shortcut - don't even read in the file
;; if we know we've seen it since the last time it was touched.
(let ((scantime (cadr (assoc nnfolder-current-group
nnfolder-scantime-alist)))
(modtime (nth 5 (or (file-attributes file) '(nil nil nil nil nil)))))
(if (and scanning scantime
(eq (car scantime) (car modtime))
(eq (cdr scantime) (cadr modtime)))
nil
(save-excursion
(nnfolder-possibly-activate-groups nil)
;; Read in the file.
(set-buffer (setq nnfolder-current-buffer
(nnheader-find-file-noselect file nil 'raw)))
(buffer-disable-undo (current-buffer))
;; If the file hasn't been touched since the last time we scanned it,
;; don't bother doing anything with it.
(let ((delim (concat "^" message-unix-mail-delimiter))
(marker (concat "\n" nnfolder-article-marker))
(number "[0-9]+")
(active (or (cadr (assoc nnfolder-current-group
nnfolder-group-alist))
(cons 1 0)))
(scantime (assoc nnfolder-current-group nnfolder-scantime-alist))
(minid (lsh -1 -1))
maxid start end newscantime)
(setq maxid (or (cdr active) 0))
(goto-char (point-min))
;; Anytime the active number is 1 or 0, it is suspect. In that
;; case, search the file manually to find the active number. Or,
;; of course, if we're being paranoid. (This would also be the
;; place to build other lists from the header markers, such as
;; expunge lists, etc., if we ever desired to abandon the active
;; file entirely for mboxes.)
(when (or nnfolder-ignore-active-file
(< maxid 2))
(while (and (search-forward marker nil t)
(re-search-forward number nil t))
(let ((newnum (string-to-number (match-string 0))))
(setq maxid (max maxid newnum))
(setq minid (min minid newnum))))
(setcar active (max 1 (min minid maxid)))
(setcdr active (max maxid (cdr active)))
(goto-char (point-min)))
;; As long as we trust that the user will only insert unmarked mail
;; at the end, go to the end and search backwards for the last
;; marker. Find the start of that message, and begin to search for
;; unmarked messages from there.
(if (not (or nnfolder-distrust-mbox
(< maxid 2)))
(progn
(goto-char (point-max))
(if (not (re-search-backward marker nil t))
(goto-char (point-min))
(if (not (re-search-backward delim nil t))
(goto-char (point-min))))))
;; Keep track of the active number on our own, and insert it back
;; into the active list when we're done. Also, prime the pump to
;; cut down on the number of searches we do.
(setq end (point-marker))
(set-marker end (or (and (re-search-forward delim nil t)
(match-beginning 0))
(point-max)))
(while (not (= end (point-max)))
(setq start (marker-position end))
(goto-char end)
;; There may be more than one "From " line, so we skip past
;; them.
(while (looking-at delim)
(forward-line 1))
(set-marker end (or (and (re-search-forward delim nil t)
(match-beginning 0))
(point-max)))
(goto-char start)
(if (not (search-forward marker end t))
(progn
(narrow-to-region start end)
(nnmail-insert-lines)
(nnfolder-insert-newsgroup-line
(cons nil (nnfolder-active-number nnfolder-current-group)))
(widen))))
;; Make absolutely sure that the active list reflects reality!
(nnmail-save-active nnfolder-group-alist nnfolder-active-file)
;; Set the scantime for this group.
(setq newscantime (visited-file-modtime))
(if scantime
(setcdr scantime (list newscantime))
(push (list nnfolder-current-group newscantime)
nnfolder-scantime-alist))
(current-buffer))))))
;;;###autoload
(defun nnfolder-generate-active-file ()
"Look for mbox folders in the nnfolder directory and make them into groups."
(interactive)
(nnmail-activate 'nnfolder)
(let ((files (directory-files nnfolder-directory))
file)
(while (setq file (pop files))
(when (and (not (backup-file-name-p file))
(nnheader-mail-file-mbox-p file))
(nnheader-message 5 "Adding group %s..." file)
(push (list file (cons 1 0)) nnfolder-group-alist)
(nnfolder-possibly-change-group file)
;; (nnfolder-read-folder file)
(nnfolder-close-group file))
(message ""))))
(defun nnfolder-group-pathname (group)
"Make pathname for GROUP."
(let ((dir (file-name-as-directory (expand-file-name nnfolder-directory))))
;; If this file exists, we use it directly.
(if (or nnmail-use-long-file-names
(file-exists-p (concat dir group)))
(concat dir group)
;; If not, we translate dots into slashes.
(concat dir (nnheader-replace-chars-in-string group ?. ?/)))))
(defun nnfolder-save-buffer ()
"Save the buffer."
(when (buffer-modified-p)
(run-hooks 'nnfolder-save-buffer-hook)
(save-buffer)))
(provide 'nnfolder)
;;; nnfolder.el ends here

View file

@ -1,620 +0,0 @@
;;; nnheader.el --- header access macros for Gnus and its backends
;; 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>
;; Keywords: news
;; 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:
;; 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. 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]
;;
;; (That last entry is defined as "misc" in the NOV format, but Gnus
;; uses it for xrefs.)
;;; Code:
(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))
(defmacro mail-header-set-number (header number)
"Set article number of HEADER to NUMBER."
`(aset ,header 0 ,number))
(defmacro mail-header-subject (header)
"Return subject string in HEADER."
`(aref ,header 1))
(defmacro mail-header-set-subject (header subject)
"Set article subject of HEADER to SUBJECT."
`(aset ,header 1 ,subject))
(defmacro mail-header-from (header)
"Return author string in HEADER."
`(aref ,header 2))
(defmacro mail-header-set-from (header from)
"Set article author of HEADER to FROM."
`(aset ,header 2 ,from))
(defmacro mail-header-date (header)
"Return date in HEADER."
`(aref ,header 3))
(defmacro mail-header-set-date (header date)
"Set article date of HEADER to DATE."
`(aset ,header 3 ,date))
(defalias 'mail-header-message-id 'mail-header-id)
(defmacro mail-header-id (header)
"Return Id in HEADER."
`(aref ,header 4))
(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))
(defmacro mail-header-references (header)
"Return references in HEADER."
`(aref ,header 5))
(defmacro mail-header-set-references (header ref)
"Set article references of HEADER to REF."
`(aset ,header 5 ,ref))
(defmacro mail-header-chars (header)
"Return number of chars of article in HEADER."
`(aref ,header 6))
(defmacro mail-header-set-chars (header chars)
"Set number of chars in article of HEADER to CHARS."
`(aset ,header 6 ,chars))
(defmacro mail-header-lines (header)
"Return lines in HEADER."
`(aref ,header 7))
(defmacro mail-header-set-lines (header lines)
"Set article lines of HEADER to LINES."
`(aset ,header 7 ,lines))
(defmacro mail-header-xref (header)
"Return xref string in HEADER."
`(aref ,header 8))
(defmacro mail-header-set-xref (header xref)
"Set article xref of HEADER to 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 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)
(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)
(buffer-disable-undo (current-buffer))
(erase-buffer)
(kill-all-local-variables)
(setq case-fold-search t) ;Should ignore case.
t))
;;; Various functions the backends use.
(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."
(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 (not (zerop (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
(narrow-to-region (point-min) (1- (point)))
(goto-char (point-min))
(while (looking-at "[A-Z][^ \t]+:.*\n\\([ \t].*\n\\)*\\|From .*\n")
(goto-char (match-end 0)))
(prog1
(eobp)
(widen))))
(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.
(mail-position-on-field "References")
(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))))
(defun nnheader-file-size (file)
"Return the file size of FILE or 0."
(or (nth 7 (file-attributes file)) 0))
(defun nnheader-find-etc-directory (package)
"Go through the path and find the \".../etc/PACKAGE\" directory."
(let ((path load-path)
dir result)
;; We try to find the dir by looking at the load path,
;; stripping away the last component and adding "etc/".
(while path
(if (and (car path)
(file-exists-p
(setq dir (concat
(file-name-directory
(directory-file-name (car path)))
"etc/" package "/")))
(file-directory-p dir))
(setq result dir
path nil)
(setq path (cdr path))))
result))
(defvar ange-ftp-path-format)
(defvar efs-path-regexp)
(defun nnheader-re-read-dir (path)
"Re-read directory PATH if PATH is on a remote system."
(if (boundp 'ange-ftp-path-format)
(when (string-match (car ange-ftp-path-format) path)
(ange-ftp-re-read-dir path))
(if (boundp 'efs-path-regexp)
(when (string-match efs-path-regexp path)
(efs-re-read-dir path)))))
(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

View file

@ -1,201 +0,0 @@
;;; nnheaderems.el --- making Gnus backends work under different Emacsen
;; Copyright (C) 1996 Free Software Foundation, Inc.
;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
;; Keywords: news
;; This file is part of GNU Emacs.
;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Commentary:
;;; Code:
(defun nnheader-xmas-run-at-time (time repeat function &rest args)
(start-itimer
"nnheader-run-at-time"
`(lambda ()
(,function ,@args))
time repeat))
(defun nnheader-xmas-cancel-timer (timer)
(delete-itimer timer))
;; Written by Erik Naggum <erik@naggum.no>.
;; Saved by Steve Baur <steve@miranova.com>.
(defun nnheader-xmas-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-xmas-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-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))))
(eval-and-compile
(cond
;; Do XEmacs function bindings.
((string-match "XEmacs\\|Lucid" emacs-version)
(fset 'nnheader-run-at-time 'nnheader-xmas-run-at-time)
(fset 'nnheader-cancel-timer 'nnheader-xmas-cancel-timer)
(fset 'nnheader-find-file-noselect 'nnheader-xmas-find-file-noselect)
(fset 'nnheader-insert-file-contents-literally
(if (fboundp 'insert-file-contents-literally)
'insert-file-contents-literally
'nnheader-xmas-insert-file-contents-literally)))
;; Do Emacs function bindings.
(t
(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 (memq system-type '(windows-nt))
(add-hook 'nnmail-prepare-incoming-hook 'nnheader-ms-strip-cr)))
(provide 'nnheaderems)
;;; nnheaderems.el ends here.

View file

@ -1,388 +0,0 @@
;;; nnkiboze.el --- select virtual news access for Gnus
;; Copyright (C) 1995,96 Free Software Foundation, Inc.
;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
;; Keywords: news
;; 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:
;; The other access methods (nntp, nnspool, etc) are general news
;; access methods. This module relies on Gnus and can not be used
;; separately.
;;; Code:
(require 'nntp)
(require 'nnheader)
(require 'gnus)
(require 'gnus-score)
(require 'nnoo)
(eval-when-compile (require 'cl))
(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.")
(defvoo nnkiboze-current-group nil)
(defvoo nnkiboze-current-score-group "")
(defvoo nnkiboze-status-string "")
;;; Interface functions.
(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
(if (stringp (car articles))
'headers
(let ((first (car articles))
(last (progn (while (cdr articles) (setq articles (cdr articles)))
(car articles)))
(nov (nnkiboze-nov-file-name)))
(if (file-exists-p nov)
(save-excursion
(set-buffer nntp-server-buffer)
(erase-buffer)
(insert-file-contents nov)
(goto-char (point-min))
(while (and (not (eobp)) (< first (read (current-buffer))))
(forward-line 1))
(beginning-of-line)
(if (not (eobp)) (delete-region 1 (point)))
(while (and (not (eobp)) (>= last (read (current-buffer))))
(forward-line 1))
(beginning-of-line)
(if (not (eobp)) (delete-region (point) (point-max)))
'nov))))))
(deffoo nnkiboze-open-server (newsgroups &optional something)
(gnus-make-directory nnkiboze-directory)
(nnheader-init-server-buffer))
(deffoo nnkiboze-server-opened (&optional server)
(and nntp-server-buffer
(get-buffer nntp-server-buffer)))
(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-summary-article-header article))
(xref (mail-header-xref header))
igroup iarticle)
(or xref (error "nnkiboze: No xref"))
(or (string-match " \\([^ ]+\\):\\([0-9]+\\)" xref)
(error "nnkiboze: Malformed xref"))
(setq igroup (substring xref (match-beginning 1) (match-end 1)))
(setq iarticle (string-to-int
(substring xref (match-beginning 2) (match-end 2))))
(and (gnus-request-group igroup t)
(gnus-request-article iarticle igroup buffer)))))
(deffoo nnkiboze-request-group (group &optional server dont-check)
"Make GROUP the current newsgroup."
(nnkiboze-possibly-change-newsgroups group)
(if dont-check
()
(let ((nov-file (nnkiboze-nov-file-name))
beg end total)
(save-excursion
(set-buffer nntp-server-buffer)
(erase-buffer)
(if (not (file-exists-p nov-file))
(insert (format "211 0 0 0 %s\n" group))
(insert-file-contents nov-file)
(if (zerop (buffer-size))
(insert (format "211 0 0 0 %s\n" group))
(goto-char (point-min))
(and (looking-at "[0-9]+") (setq beg (read (current-buffer))))
(goto-char (point-max))
(and (re-search-backward "^[0-9]" nil t)
(setq end (read (current-buffer))))
(setq total (count-lines (point-min) (point-max)))
(erase-buffer)
(insert (format "211 %d %d %d %s\n" total beg end group)))))))
t)
(deffoo nnkiboze-close-group (group &optional server)
(nnkiboze-possibly-change-newsgroups group)
;; Remove NOV lines of articles that are marked as read.
(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)
(version-control 'never))
(set-buffer (get-buffer-create "*nnkiboze work*"))
(buffer-disable-undo (current-buffer))
(erase-buffer)
(let ((cur (current-buffer))
article)
(insert-file-contents (nnkiboze-nov-file-name))
(goto-char (point-min))
(while (looking-at "[0-9]+")
(if (or (memq (setq article (read cur)) unreads)
(memq article unselected))
(forward-line 1)
(delete-region (progn (beginning-of-line) (point))
(progn (forward-line 1) (point)))))
(write-file (nnkiboze-nov-file-name))
(kill-buffer (current-buffer)))))
(setq nnkiboze-current-group nil)))
(deffoo nnkiboze-request-list (&optional server)
(nnheader-report 'nnkiboze "LIST is not implemented."))
(deffoo nnkiboze-request-newgroups (date &optional server)
"List new groups."
(nnheader-report 'nnkiboze "NEWGROUPS is not supported."))
(deffoo nnkiboze-request-list-newsgroups (&optional server)
(nnheader-report 'nnkiboze "LIST NEWSGROUPS is not implemented."))
(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.
(defun nnkiboze-possibly-change-newsgroups (group)
(setq nnkiboze-current-group group))
(defun nnkiboze-prefixed-name (group)
(gnus-group-prefixed-name group '(nnkiboze "")))
;;;###autoload
(defun nnkiboze-generate-groups ()
"Usage: emacs -batch -l nnkiboze -f nnkiboze-generate-groups
Finds out what articles are to be part of the nnkiboze groups."
(interactive)
(let ((nnmail-spool-file nil)
(gnus-use-dribble-file nil)
(gnus-read-active-file t)
(gnus-expert-user t))
(gnus))
(let* ((gnus-newsrc-alist (gnus-copy-sequence 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" (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 (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)))
(newsrc-file (concat nnkiboze-directory group ".newsrc"))
(nov-file (concat nnkiboze-directory group ".nov"))
(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 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)))
;; Go through the active hashtb and add new all groups that match the
;; kiboze regexp.
(mapatoms
(lambda (group)
(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
(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 (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))))
() ; 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 (> (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-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)))
;; 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)
(insert "(setq nnkiboze-newsrc '" (prin1-to-string nnkiboze-newsrc)
")\n")
(write-file newsrc-file)
(kill-buffer (current-buffer))
(switch-to-buffer gnus-group-buffer)
(gnus-group-list-groups 5 nil)))
(defun nnkiboze-enter-nov (buffer header group)
(save-excursion
(set-buffer buffer)
(goto-char (point-max))
(let ((xref (mail-header-xref header))
(prefix (gnus-group-real-prefix group))
(first t)
article)
(if (zerop (forward-line -1))
(progn
(setq article (1+ (read (current-buffer))))
(forward-line 1))
(setq article 1))
(insert (int-to-string article) "\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 (or (not xref) (equal "" xref))
(insert "Xref: " (system-name) " " group ":"
(int-to-string (mail-header-number header))
"\t\n")
(insert (mail-header-xref header) "\t\n")
(search-backward "\t" nil t)
(search-backward "\t" nil t)
(while (re-search-forward
"[^ ]+:[0-9]+"
(save-excursion (end-of-line) (point)) t)
(if first
;; The first xref has to be the group this article
;; really came for - this is the article nnkiboze
;; will request when it is asked for the article.
(save-excursion
(goto-char (match-beginning 0))
(insert prefix group ":"
(int-to-string (mail-header-number header)) " ")
(setq first nil)))
(save-excursion
(goto-char (match-beginning 0))
(insert prefix)))))))
(defun nnkiboze-nov-file-name ()
(concat (file-name-as-directory nnkiboze-directory)
(nnheader-translate-file-chars
(concat (nnkiboze-prefixed-name nnkiboze-current-group) ".nov"))))
(provide 'nnkiboze)
;;; nnkiboze.el ends here

File diff suppressed because it is too large Load diff

View file

@ -1,533 +0,0 @@
;;; nnmbox.el --- mail mbox access for Gnus
;; 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, mail
;; This file is part of GNU Emacs.
;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Commentary:
;; For an overview of what the interface functions do, please see the
;; Gnus sources.
;;; Code:
(require 'nnheader)
(require 'message)
(require 'nnmail)
(require 'nnoo)
(eval-when-compile (require 'cl))
(nnoo-declare nnmbox)
(defvoo nnmbox-mbox-file (expand-file-name "~/mbox")
"The name of the mail box file in the user's home directory.")
(defvoo nnmbox-active-file (expand-file-name "~/.mbox-active")
"The name of the active file for the mail box.")
(defvoo nnmbox-get-new-mail t
"If non-nil, nnmbox will check the incoming mail file and split the mail.")
(defvoo nnmbox-prepare-save-mail-hook nil
"Hook run narrowed to an article before saving.")
(defconst nnmbox-version "nnmbox 1.0"
"nnmbox version.")
(defvoo nnmbox-current-group nil
"Current nnmbox news group directory.")
(defconst nnmbox-mbox-buffer nil)
(defvoo nnmbox-status-string "")
(defvoo nnmbox-group-alist nil)
(defvoo nnmbox-active-timestamp nil)
;;; Interface functions
(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 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)
(zerop (% count 20))
(nnheader-message 5 "nnmbox: Receiving headers... %d%%"
(/ (* count 100) number))))
(and (numberp nnmail-large-newsgroup)
(> number nnmail-large-newsgroup)
(nnheader-message 5 "nnmbox: Receiving headers...done"))
(set-buffer nntp-server-buffer)
(nnheader-fold-continuation-lines)
'headers)))
(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)
(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)))
(deffoo nnmbox-request-article (article &optional newsgroup server buffer)
(nnmbox-possibly-change-newsgroup newsgroup server)
(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)
(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)))))))
(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)
(deffoo nnmbox-request-list (&optional server)
(save-excursion
(nnmail-find-file nnmbox-active-file)
(setq nnmbox-group-alist (nnmail-get-active))))
(deffoo nnmbox-request-newgroups (date &optional server)
(nnmbox-request-list server))
(deffoo nnmbox-request-list-newsgroups (&optional server)
(nnheader-report 'nnmbox "LIST NEWSGROUPS is not implemented."))
(deffoo nnmbox-request-expire-articles
(articles newsgroup &optional server force)
(nnmbox-possibly-change-newsgroup newsgroup server)
(let* ((is-old t)
rest)
(nnmail-activate 'nnmbox)
(save-excursion
(set-buffer nnmbox-mbox-buffer)
(while (and articles is-old)
(goto-char (point-min))
(if (search-forward (nnmbox-article-string (car articles)) nil t)
(if (setq is-old
(nnmail-expired-article-p
newsgroup
(buffer-substring
(point) (progn (end-of-line) (point))) force))
(progn
(nnheader-message 5 "Deleting article %d in %s..."
(car articles) newsgroup)
(nnmbox-delete-mail))
(setq rest (cons (car articles) rest))))
(setq articles (cdr articles)))
(save-buffer)
;; Find the lowest active article in this group.
(let ((active (nth 1 (assoc newsgroup nnmbox-group-alist))))
(goto-char (point-min))
(while (and (not (search-forward
(nnmbox-article-string (car active)) nil t))
(<= (car active) (cdr active)))
(setcar active (1+ (car active)))
(goto-char (point-min))))
(nnmail-save-active nnmbox-group-alist nnmbox-active-file)
(nconc rest articles))))
(deffoo nnmbox-request-move-article
(article group server accept-form &optional last)
(nnmbox-possibly-change-newsgroup group server)
(let ((buf (get-buffer-create " *nnmbox move*"))
result)
(and
(nnmbox-request-article article group server)
(save-excursion
(set-buffer buf)
(buffer-disable-undo (current-buffer))
(erase-buffer)
(insert-buffer-substring nntp-server-buffer)
(goto-char (point-min))
(while (re-search-forward
"^X-Gnus-Newsgroup:"
(save-excursion (search-forward "\n\n" nil t) (point)) t)
(delete-region (progn (beginning-of-line) (point))
(progn (forward-line 1) (point))))
(setq result (eval accept-form))
(kill-buffer buf)
result)
(save-excursion
(set-buffer nnmbox-mbox-buffer)
(goto-char (point-min))
(if (search-forward (nnmbox-article-string article) nil t)
(nnmbox-delete-mail))
(and last (save-buffer))))
result))
(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))
(if (looking-at "X-From-Line: ")
(replace-match "From ")
(insert "From nobody " (current-time-string) "\n"))
(and
(nnmail-activate 'nnmbox)
(progn
(set-buffer buf)
(goto-char (point-min))
(search-forward "\n\n" nil t)
(forward-line -1)
(while (re-search-backward "^X-Gnus-Newsgroup: " nil t)
(delete-region (point) (progn (forward-line 1) (point))))
(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)))
(deffoo nnmbox-request-replace-article (article group buffer)
(nnmbox-possibly-change-newsgroup group)
(save-excursion
(set-buffer nnmbox-mbox-buffer)
(goto-char (point-min))
(if (not (search-forward (nnmbox-article-string article) nil t))
nil
(nnmbox-delete-mail t t)
(insert-buffer-substring buffer)
(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.
;; If FORCE, delete article no matter how many X-Gnus-Newsgroup
;; headers there are. If LEAVE-DELIM, don't delete the Unix mbox
;; delimiter line.
(defun nnmbox-delete-mail (&optional force leave-delim)
;; Delete the current X-Gnus-Newsgroup line.
(or force
(delete-region
(progn (beginning-of-line) (point))
(progn (forward-line 1) (point))))
;; Beginning of the article.
(save-excursion
(save-restriction
(narrow-to-region
(save-excursion
(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 "^" message-unix-mail-delimiter)
nil t)
(if (and (not (bobp)) leave-delim)
(progn (forward-line -2) (point))
(match-beginning 0)))
(point-max))))
(goto-char (point-min))
;; Only delete the article if no other groups owns it as well.
(if (or force (not (re-search-forward "^X-Gnus-Newsgroup: " nil t)))
(delete-region (point-min) (point-max))))))
(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
(set-buffer (setq nnmbox-mbox-buffer
(nnheader-find-file-noselect
nnmbox-mbox-file nil 'raw)))
(buffer-disable-undo (current-buffer))))
(if (not nnmbox-group-alist)
(nnmail-activate 'nnmbox))
(if newsgroup
(if (assoc newsgroup nnmbox-group-alist)
(setq nnmbox-current-group newsgroup))
t))
(defun nnmbox-article-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)))
(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 'nnmail-prepare-save-mail-hook)
(run-hooks 'nnmbox-prepare-save-mail-hook)
group-art))
(defun nnmbox-insert-newsgroup-line (group-art)
(save-excursion
(goto-char (point-min))
(if (search-forward "\n\n" nil t)
(progn
(forward-char -1)
(while group-art
(insert (format "X-Gnus-Newsgroup: %s:%d %s\n"
(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 (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.
;; This might be a bit naughty... creating groups on the drop of
;; a hat, but I don't know...
(setq nnmbox-group-alist (cons (list group (setq active (cons 1 1)))
nnmbox-group-alist)))
(cdr active)))
(defun nnmbox-read-mbox ()
(nnmail-activate 'nnmbox)
(if (not (file-exists-p nnmbox-mbox-file))
(write-region 1 1 nnmbox-mbox-file t 'nomesg))
(if (and nnmbox-mbox-buffer
(buffer-name nnmbox-mbox-buffer)
(save-excursion
(set-buffer nnmbox-mbox-buffer)
(= (buffer-size) (nnheader-file-size nnmbox-mbox-file))))
()
(save-excursion
(let ((delim (concat "^" message-unix-mail-delimiter))
(alist nnmbox-group-alist)
start end number)
(set-buffer (setq nnmbox-mbox-buffer
(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))
(if (not (search-forward "\nX-Gnus-Newsgroup: "
(save-excursion
(setq end
(or
(and
(re-search-forward delim nil t)
(match-beginning 0))
(point-max))))
t))
(save-excursion
(save-restriction
(narrow-to-region start end)
(nnmbox-save-mail))))
(goto-char end))))))
(provide 'nnmbox)
;;; nnmbox.el ends here

View file

@ -1,520 +0,0 @@
;;; nnmh.el --- mhspool access for Gnus
;; 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, mail
;; This file is part of GNU Emacs.
;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Commentary:
;; Based on nnspool.el by Masanobu UMEDA <umerin@flab.flab.fujitsu.junet>.
;; For an overview of what the interface functions do, please see the
;; Gnus sources.
;;; Code:
(require 'nnheader)
(require 'nnmail)
(require 'gnus)
(require 'nnoo)
(eval-and-compile (require 'cl))
(nnoo-declare nnmh)
(defvoo nnmh-directory message-directory
"*Mail spool directory.")
(defvoo nnmh-get-new-mail t
"*If non-nil, nnmh will check the incoming mail file and split the mail.")
(defvoo nnmh-prepare-save-mail-hook nil
"*Hook run narrowed to an article before saving.")
(defvoo nnmh-be-safe nil
"*If non-nil, nnmh will check all articles to make sure whether they are new or not.")
(defconst nnmh-version "nnmh 1.0"
"nnmh version.")
(defvoo nnmh-current-directory nil
"Current news group directory.")
(defvoo nnmh-status-string "")
(defvoo nnmh-group-alist nil)
;;; Interface functions.
(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 articles))
(large (and (numberp nnmail-large-newsgroup)
(> number nnmail-large-newsgroup)))
(count 0)
beg article)
(nnmh-possibly-change-directory newsgroup server)
;; We don't support fetching by Message-ID.
(if (stringp (car articles))
'headers
(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
(zerop (% count 20))
(message "nnmh: Receiving headers... %d%%"
(/ (* count 100) number))))
(and large (message "nnmh: Receiving headers...done"))
(nnheader-fold-continuation-lines)
'headers))))
(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)))
(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))))
(nntp-server-buffer (or buffer nntp-server-buffer)))
(and (stringp file)
(file-exists-p file)
(not (file-directory-p file))
(save-excursion (nnmail-find-file file))
(string-to-int (file-name-nondirectory file)))))
(deffoo nnmh-request-group (group &optional server dont-check)
(let ((pathname (nnmail-group-pathname group nnmh-directory))
dir)
(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
;; Re-scan the directory if it's on a foreign system.
(nnheader-re-read-dir pathname)
(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))))))))))
(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)))
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.
(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)
(deffoo nnmh-request-newgroups (date &optional server)
(nnmh-request-list server))
(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)))
(is-old t)
article rest mod-time)
(nnmail-activate 'nnmh)
(while (and articles is-old)
(setq article (concat nnmh-current-directory
(int-to-string (car articles))))
(if (setq mod-time (nth 5 (file-attributes article)))
(if (and (nnmh-deletable-article-p newsgroup (car articles))
(setq is-old
(nnmail-expired-article-p newsgroup mod-time force)))
(progn
(nnheader-message 5 "Deleting article %s in %s..."
article newsgroup)
(condition-case ()
(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)))
(deffoo nnmh-close-group (group &optional server)
t)
(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)
(insert-buffer-substring nntp-server-buffer)
(setq result (eval accept-form))
(kill-buffer (current-buffer))
result)
(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))
(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 noinsert))))
(and
(nnmail-activate 'nnmh)
(car (nnmh-save-mail noinsert)))))
(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 (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 &optional server)
(when (and server
(not (nnmh-server-opened server)))
(nnmh-open-server server))
(if newsgroup
(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 (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)))
(nnheader-message 5 "Creating mail directory %s" (car dirs))
(setq dirs (cdr dirs)))))
(defun nnmh-save-mail (&optional noinsert)
"Called narrowed to an article."
(let ((group-art (nreverse (nnmail-article-group 'nnmh-active-number))))
(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 ")
(replace-match "X-From-Line: ")
(forward-line 1))
;; We save the article in all the newsgroups it belongs in.
(let ((ga group-art)
first)
(while 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.
(funcall nnmail-crosspost-link-function first file t)
;; Save the article.
(write-region (point-min) (point-max) file nil nil)
(setq first file)))
(setq ga (cdr ga))))
group-art))
(defun nnmh-active-number (group)
"Compute the next article number in GROUP."
(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
(progn
(setq active (cons 1 0))
(setq nnmh-group-alist (cons (list group active) nnmh-group-alist))))
(setcdr active (1+ (cdr active)))
(while (file-exists-p
(concat (nnmail-group-pathname group nnmh-directory)
(int-to-string (cdr active))))
(setcdr active (1+ (cdr active))))
(cdr active)))
(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
;; marked as unread by Gnus.
(let* ((dir nnmh-current-directory)
(files (sort (mapcar (function (lambda (name) (string-to-int name)))
(directory-files nnmh-current-directory
nil "^[0-9]+$" t)) '<))
(nnmh-file (concat dir ".nnmh-articles"))
new articles)
;; Load the .nnmh-articles file.
(if (file-exists-p nnmh-file)
(setq articles
(let (nnmh-newsgroup-articles)
(condition-case nil (load nnmh-file nil t t) (error nil))
nnmh-newsgroup-articles)))
;; Add all new articles to the `new' list.
(let ((art files))
(while art
(if (not (assq (car art) articles)) (setq new (cons (car art) new)))
(setq art (cdr art))))
;; Remove all deleted articles.
(let ((art articles))
(while art
(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
;; that Gnus thinks they are by looking at the time-stamps.
(let ((art articles))
(while (and art
(not (equal
(nth 5 (file-attributes
(concat dir (int-to-string (caar art)))))
(cdar art))))
(setq articles (delq (car art) articles))
(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.
(let ((n new))
(while n
(setq articles
(cons (cons
(car n)
(nth 5 (file-attributes
(concat dir (int-to-string (car n))))))
articles))
(setq n (cdr n))))
;; Make Gnus mark all new articles as unread.
(or (zerop (length new))
(gnus-make-articles-unread
(gnus-group-prefixed-name group (list 'nnmh ""))
(setq new (sort new '<))))
;; Sort the article list with highest numbers first.
(setq articles (sort articles (lambda (art1 art2)
(> (car art1) (car art2)))))
;; Finally write this list back to the .nnmh-articles file.
(save-excursion
(set-buffer (get-buffer-create "*nnmh out*"))
(insert ";; Gnus article active file for " group "\n\n")
(insert "(setq nnmh-newsgroup-articles '")
(insert (prin1-to-string articles) ")\n")
(write-region (point-min) (point-max) nnmh-file nil 'nomesg)
(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

View file

@ -1,764 +0,0 @@
;;; nnml.el --- mail spool access for Gnus
;; 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, mail
;; This file is part of GNU Emacs.
;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Commentary:
;; Based on nnspool.el by Masanobu UMEDA <umerin@flab.flab.fujitsu.junet>.
;; For an overview of what the interface functions do, please see the
;; Gnus sources.
;;; Code:
(require 'nnheader)
(require 'nnmail)
(require 'nnoo)
(require 'cl)
(nnoo-declare nnml)
(defvoo nnml-directory message-directory
"Mail spool directory.")
(defvoo nnml-active-file
(concat (file-name-as-directory nnml-directory) "active")
"Mail active file.")
(defvoo nnml-newsgroups-file
(concat (file-name-as-directory nnml-directory) "newsgroups")
"Mail newsgroups description file.")
(defvoo nnml-get-new-mail t
"If non-nil, nnml will check the incoming mail file and split the mail.")
(defvoo nnml-nov-is-evil nil
"If non-nil, Gnus will never generate and use nov databases for mail groups.
Using nov databases will speed up header fetching considerably.
This variable shouldn't be flipped much. If you have, for some reason,
set this to t, and want to set it to nil again, you should always run
the `nnml-generate-nov-databases' command. The function will go
through all nnml directories and generate nov databases for them
all. This may very well take some time.")
(defvoo nnml-prepare-save-mail-hook nil
"Hook run narrowed to an article before saving.")
(defvoo nnml-inhibit-expiry nil
"If non-nil, inhibit expiry.")
(defconst nnml-version "nnml 1.0"
"nnml version.")
(defvoo nnml-nov-file-name ".overview")
(defvoo nnml-current-directory nil)
(defvoo nnml-current-group nil)
(defvoo nnml-status-string "")
(defvoo nnml-nov-buffer-alist nil)
(defvoo nnml-group-alist nil)
(defvoo nnml-active-timestamp nil)
(defvoo nnml-article-file-alist nil)
(defvoo nnml-generate-active-function 'nnml-generate-active-info)
;;; Interface functions.
(nnoo-define-basics nnml)
(deffoo nnml-retrieve-headers (sequence &optional newsgroup server fetch-old)
(save-excursion
(set-buffer nntp-server-buffer)
(erase-buffer)
(let ((file nil)
(number (length sequence))
(count 0)
beg article)
(if (stringp (car sequence))
'headers
(nnml-possibly-change-directory newsgroup server)
(unless nnml-article-file-alist
(setq nnml-article-file-alist
(nnheader-article-to-file-alist nnml-current-directory)))
(if (nnml-retrieve-headers-with-nov sequence fetch-old)
'nov
(while sequence
(setq article (car sequence))
(setq file
(concat nnml-current-directory
(or (cdr (assq article nnml-article-file-alist))
"")))
(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))
(setq count (1+ count))
(and (numberp nnmail-large-newsgroup)
(> number nnmail-large-newsgroup)
(zerop (% count 20))
(nnheader-message 6 "nnml: Receiving headers... %d%%"
(/ (* count 100) number))))
(and (numberp nnmail-large-newsgroup)
(> number nnmail-large-newsgroup)
(nnheader-message 6 "nnml: Receiving headers...done"))
(nnheader-fold-continuation-lines)
'headers)))))
(deffoo nnml-open-server (server &optional defs)
(nnoo-change-server 'nnml server defs)
(when (not (file-exists-p nnml-directory))
(condition-case ()
(make-directory nnml-directory t)
(error t)))
(cond
((not (file-exists-p nnml-directory))
(nnml-close-server)
(nnheader-report 'nnml "Couldn't create directory: %s" nnml-directory))
((not (file-directory-p (file-truename nnml-directory)))
(nnml-close-server)
(nnheader-report 'nnml "Not a directory: %s" nnml-directory))
(t
(nnheader-report 'nnml "Opened server %s using directory %s"
server nnml-directory)
t)))
(deffoo nnml-request-article (id &optional newsgroup server buffer)
(nnml-possibly-change-directory newsgroup server)
(let* ((nntp-server-buffer (or buffer nntp-server-buffer))
file path gpath group-num)
(if (stringp id)
(when (and (setq group-num (nnml-find-group-number id))
(setq file (cdr
(assq (cdr group-num)
(nnheader-article-to-file-alist
(setq gpath
(nnmail-group-pathname
(car group-num)
nnml-directory)))))))
(setq path (concat gpath (int-to-string (cdr group-num)))))
(unless nnml-article-file-alist
(setq nnml-article-file-alist
(nnheader-article-to-file-alist nnml-current-directory)))
(when (setq file (cdr (assq id nnml-article-file-alist)))
(setq path (concat nnml-current-directory file))))
(cond
((not path)
(nnheader-report 'nnml "No such article: %s" id))
((not (file-exists-p path))
(nnheader-report 'nnml "No such file: %s" path))
((file-directory-p path)
(nnheader-report 'nnml "File is a directory: %s" path))
((not (save-excursion (nnmail-find-file path)))
(nnheader-report 'nnml "Couldn't read file: %s" path))
(t
(nnheader-report 'nnml "Article %s retrieved" id)
;; We return the article number.
(cons newsgroup (string-to-int (file-name-nondirectory path)))))))
(deffoo nnml-request-group (group &optional server dont-check)
(cond
((not (nnml-possibly-change-directory group server))
(nnheader-report 'nnml "Invalid group (no such directory)"))
((not (file-directory-p nnml-current-directory))
(nnheader-report 'nnml "%s is not a directory" nnml-current-directory))
(dont-check
(nnheader-report 'nnml "Group %s selected" group)
t)
(t
(nnmail-activate 'nnml)
(let ((active (nth 1 (assoc group nnml-group-alist))))
(if (not active)
(nnheader-report 'nnml "No such group: %s" group)
(nnheader-report 'nnml "Selected group %s" group)
(nnheader-insert "211 %d %d %d %s\n"
(max (1+ (- (cdr active) (car active))) 0)
(car active) (cdr active) group))))))
(deffoo nnml-request-scan (&optional group server)
(setq nnml-article-file-alist nil)
(nnmail-get-new-mail 'nnml 'nnml-save-nov nnml-directory group))
(deffoo nnml-close-group (group &optional server)
(setq nnml-article-file-alist nil)
t)
(deffoo nnml-request-create-group (group &optional server)
(nnmail-activate 'nnml)
(or (assoc group nnml-group-alist)
(let (active)
(setq nnml-group-alist (cons (list group (setq active (cons 1 0)))
nnml-group-alist))
(nnml-possibly-create-directory group)
(nnml-possibly-change-directory group server)
(let ((articles
(nnheader-directory-articles nnml-current-directory )))
(and articles
(progn
(setcar active (apply 'min articles))
(setcdr active (apply 'max articles)))))
(nnmail-save-active nnml-group-alist nnml-active-file)))
t)
(deffoo nnml-request-list (&optional server)
(save-excursion
(nnmail-find-file nnml-active-file)
(setq nnml-group-alist (nnmail-get-active))))
(deffoo nnml-request-newgroups (date &optional server)
(nnml-request-list server))
(deffoo nnml-request-list-newsgroups (&optional server)
(save-excursion
(nnmail-find-file nnml-newsgroups-file)))
(deffoo nnml-request-expire-articles (articles newsgroup &optional server force)
(nnml-possibly-change-directory newsgroup server)
(let* ((active-articles
(nnheader-directory-articles nnml-current-directory))
(is-old t)
article rest mod-time number)
(nnmail-activate 'nnml)
(unless nnml-article-file-alist
(setq nnml-article-file-alist
(nnheader-article-to-file-alist nnml-current-directory)))
(while (and articles is-old)
(setq article (concat nnml-current-directory
(int-to-string
(setq number (pop articles)))))
(when (setq mod-time (nth 5 (file-attributes article)))
(if (and (nnml-deletable-article-p newsgroup number)
(setq is-old
(nnmail-expired-article-p newsgroup mod-time force
nnml-inhibit-expiry)))
(progn
(nnheader-message 5 "Deleting article %s in %s..."
article newsgroup)
(condition-case ()
(funcall nnmail-delete-file-function article)
(file-error
(push number rest)))
(setq active-articles (delq number active-articles))
(nnml-nov-delete-article newsgroup number))
(push number rest))))
(let ((active (nth 1 (assoc newsgroup nnml-group-alist))))
(when active
(setcar active (or (and active-articles
(apply 'min active-articles))
(1+ (cdr active)))))
(nnmail-save-active nnml-group-alist nnml-active-file))
(nnml-save-nov)
(message "")
(nconc rest articles)))
(deffoo nnml-request-move-article
(article group server accept-form &optional last)
(let ((buf (get-buffer-create " *nnml move*"))
result)
(nnml-possibly-change-directory group server)
(unless nnml-article-file-alist
(setq nnml-article-file-alist
(nnheader-article-to-file-alist nnml-current-directory)))
(and
(nnml-deletable-article-p group article)
(nnml-request-article article group server)
(save-excursion
(set-buffer buf)
(insert-buffer-substring nntp-server-buffer)
(setq result (eval accept-form))
(kill-buffer (current-buffer))
result)
(progn
(nnml-possibly-change-directory group server)
(condition-case ()
(funcall nnmail-delete-file-function
(concat nnml-current-directory
(int-to-string article)))
(file-error nil))
(nnml-nov-delete-article group article)
(and last (nnml-save-nov))))
result))
(deffoo nnml-request-accept-article (group &optional server last)
(nnml-possibly-change-directory group server)
(nnmail-check-syntax)
(let (result)
(if (stringp group)
(and
(nnmail-activate 'nnml)
;; We trick the choosing function into believing that only one
;; group is available.
(let ((nnmail-split-methods (list (list group ""))))
(setq result (car (nnml-save-mail))))
(progn
(nnmail-save-active nnml-group-alist nnml-active-file)
(and last (nnml-save-nov))))
(and
(nnmail-activate 'nnml)
(setq result (car (nnml-save-mail)))
(progn
(nnmail-save-active nnml-group-alist nnml-active-file)
(and last (nnml-save-nov)))))
result))
(deffoo nnml-request-replace-article (article group buffer)
(nnml-possibly-change-directory group)
(save-excursion
(set-buffer buffer)
(nnml-possibly-create-directory group)
(let ((chars (nnmail-insert-lines))
(art (concat (int-to-string article) "\t"))
headers)
(when (condition-case ()
(progn
(write-region
(point-min) (point-max)
(concat nnml-current-directory (int-to-string article))
nil (if (nnheader-be-verbose 5) nil 'nomesg))
t)
(error nil))
(setq headers (nnml-parse-head chars article))
;; Replace the NOV line in the NOV file.
(save-excursion
(set-buffer (nnml-open-nov group))
(goto-char (point-min))
(if (or (looking-at art)
(search-forward (concat "\n" art) nil t))
;; Delete the old NOV line.
(delete-region (progn (beginning-of-line) (point))
(progn (forward-line 1) (point)))
;; The line isn't here, so we have to find out where
;; we should insert it. (This situation should never
;; occur, but one likes to make sure...)
(while (and (looking-at "[0-9]+\t")
(< (string-to-int
(buffer-substring
(match-beginning 0) (match-end 0)))
article)
(zerop (forward-line 1)))))
(beginning-of-line)
(nnheader-insert-nov headers)
(nnml-save-nov)
t)))))
(deffoo nnml-request-delete-group (group &optional force server)
(nnml-possibly-change-directory group server)
(when force
;; Delete all articles in GROUP.
(let ((articles
(directory-files
nnml-current-directory t
(concat nnheader-numerical-short-files
"\\|" (regexp-quote nnml-nov-file-name) "$")))
article)
(while articles
(setq article (pop articles))
(when (file-writable-p article)
(nnheader-message 5 "Deleting article %s in %s..." article group)
(funcall nnmail-delete-file-function article))))
;; Try to delete the directory itself.
(condition-case ()
(delete-directory nnml-current-directory)
(error nil)))
;; Remove the group from all structures.
(setq nnml-group-alist
(delq (assoc group nnml-group-alist) nnml-group-alist)
nnml-current-group nil
nnml-current-directory nil)
;; Save the active file.
(nnmail-save-active nnml-group-alist nnml-active-file)
t)
(deffoo nnml-request-rename-group (group new-name &optional server)
(nnml-possibly-change-directory group server)
;; Rename directory.
(and (file-writable-p nnml-current-directory)
(condition-case ()
(let ((parent
(file-name-directory
(directory-file-name
(nnmail-group-pathname new-name nnml-directory)))))
(unless (file-exists-p parent)
(make-directory parent t))
(rename-file
(directory-file-name nnml-current-directory)
(directory-file-name
(nnmail-group-pathname new-name nnml-directory)))
t)
(error nil))
;; That went ok, so we change the internal structures.
(let ((entry (assoc group nnml-group-alist)))
(and entry (setcar entry new-name))
(setq nnml-current-directory nil
nnml-current-group nil)
;; Save the new group alist.
(nnmail-save-active nnml-group-alist nnml-active-file)
t)))
;;; Internal functions.
(defun nnml-deletable-article-p (group article)
"Say whether ARTICLE in GROUP can be deleted."
(let (file path)
(when (setq file (cdr (assq article nnml-article-file-alist)))
(setq path (concat nnml-current-directory file))
(and (file-writable-p path)
(or (not nnmail-keep-last-article)
(not (eq (cdr (nth 1 (assoc group nnml-group-alist)))
article)))))))
;; Find an article number in the current group given the Message-ID.
(defun nnml-find-group-number (id)
(save-excursion
(set-buffer (get-buffer-create " *nnml id*"))
(buffer-disable-undo (current-buffer))
(let ((alist nnml-group-alist)
number)
;; We want to look through all .overview files, but we want to
;; start with the one in the current directory. It seems most
;; likely that the article we are looking for is in that group.
(if (setq number (nnml-find-id nnml-current-group id))
(cons nnml-current-group number)
;; It wasn't there, so we look through the other groups as well.
(while (and (not number)
alist)
(or (string= (caar alist) nnml-current-group)
(setq number (nnml-find-id (caar alist) id)))
(or number
(setq alist (cdr alist))))
(and number
(cons (caar alist) number))))))
(defun nnml-find-id (group id)
(erase-buffer)
(let ((nov (concat (nnmail-group-pathname group nnml-directory)
nnml-nov-file-name))
number found)
(when (file-exists-p nov)
(insert-file-contents nov)
(while (and (not found)
(search-forward id nil t)) ; We find the ID.
;; And the id is in the fourth field.
(if (search-backward
"\t" (save-excursion (beginning-of-line) (point)) t 4)
(progn
(beginning-of-line)
(setq found t)
;; We return the article number.
(setq number
(condition-case ()
(read (current-buffer))
(error nil))))))
number)))
(defun nnml-retrieve-headers-with-nov (articles &optional fetch-old)
(if (or gnus-nov-is-evil nnml-nov-is-evil)
nil
(let ((first (car articles))
(last (progn (while (cdr articles) (setq articles (cdr articles)))
(car articles)))
(nov (concat nnml-current-directory nnml-nov-file-name)))
(when (file-exists-p nov)
(save-excursion
(set-buffer nntp-server-buffer)
(erase-buffer)
(insert-file-contents nov)
(if (and fetch-old
(not (numberp fetch-old)))
t ; Don't remove anything.
(if fetch-old
(setq first (max 1 (- first fetch-old))))
(goto-char (point-min))
(while (and (not (eobp)) (> first (read (current-buffer))))
(forward-line 1))
(beginning-of-line)
(if (not (eobp)) (delete-region 1 (point)))
(while (and (not (eobp)) (>= last (read (current-buffer))))
(forward-line 1))
(beginning-of-line)
(if (not (eobp)) (delete-region (point) (point-max)))
t))))))
(defun nnml-possibly-change-directory (group &optional server)
(when (and server
(not (nnml-server-opened server)))
(nnml-open-server server))
(when group
(let ((pathname (nnmail-group-pathname group nnml-directory)))
(when (not (equal pathname nnml-current-directory))
(setq nnml-current-directory pathname
nnml-current-group group
nnml-article-file-alist nil))))
t)
(defun nnml-possibly-create-directory (group)
(let (dir dirs)
(setq dir (nnmail-group-pathname group nnml-directory))
(while (not (file-directory-p dir))
(setq dirs (cons dir dirs))
(setq dir (file-name-directory (directory-file-name dir))))
(while dirs
(make-directory (directory-file-name (car dirs)))
(nnheader-message 5 "Creating mail directory %s" (car dirs))
(setq dirs (cdr dirs)))))
(defun nnml-save-mail ()
"Called narrowed to an article."
(let ((group-art (nreverse (nnmail-article-group 'nnml-active-number)))
chars headers)
(setq chars (nnmail-insert-lines))
(nnmail-insert-xref group-art)
(run-hooks 'nnmail-prepare-save-mail-hook)
(run-hooks 'nnml-prepare-save-mail-hook)
(goto-char (point-min))
(while (looking-at "From ")
(replace-match "X-From-Line: ")
(forward-line 1))
;; We save the article in all the newsgroups it belongs in.
(let ((ga group-art)
first)
(while ga
(nnml-possibly-create-directory (caar ga))
(let ((file (concat (nnmail-group-pathname
(caar ga) nnml-directory)
(int-to-string (cdar ga)))))
(if first
;; It was already saved, so we just make a hard link.
(funcall nnmail-crosspost-link-function first file t)
;; Save the article.
(write-region (point-min) (point-max) file nil
(if (nnheader-be-verbose 5) nil 'nomesg))
(setq first file)))
(setq ga (cdr ga))))
;; Generate a nov line for this article. We generate the nov
;; line after saving, because nov generation destroys the
;; header.
(setq headers (nnml-parse-head chars))
;; Output the nov line to all nov databases that should have it.
(let ((ga group-art))
(while ga
(nnml-add-nov (caar ga) (cdar ga) headers)
(setq ga (cdr ga))))
group-art))
(defun nnml-active-number (group)
"Compute the next article number in GROUP."
(let ((active (cadr (assoc group nnml-group-alist))))
;; The group wasn't known to nnml, so we just create an active
;; entry for it.
(unless active
;; Perhaps the active file was corrupt? See whether
;; there are any articles in this group.
(nnml-possibly-create-directory group)
(nnml-possibly-change-directory group)
(unless nnml-article-file-alist
(setq nnml-article-file-alist
(sort
(nnheader-article-to-file-alist nnml-current-directory)
(lambda (a1 a2) (< (car a1) (car a2))))))
(setq active
(if nnml-article-file-alist
(cons (caar nnml-article-file-alist)
(caar (last nnml-article-file-alist)))
(cons 1 0)))
(setq nnml-group-alist (cons (list group active) nnml-group-alist)))
(setcdr active (1+ (cdr active)))
(while (file-exists-p
(concat (nnmail-group-pathname group nnml-directory)
(int-to-string (cdr active))))
(setcdr active (1+ (cdr active))))
(cdr active)))
(defun nnml-add-nov (group article headers)
"Add a nov line for the GROUP base."
(save-excursion
(set-buffer (nnml-open-nov group))
(goto-char (point-max))
(mail-header-set-number headers article)
(nnheader-insert-nov headers)))
(defsubst nnml-header-value ()
(buffer-substring (match-end 0) (progn (end-of-line) (point))))
(defun nnml-parse-head (chars &optional number)
"Parse the head of the current buffer."
(save-excursion
(save-restriction
(goto-char (point-min))
(narrow-to-region
(point)
(1- (or (search-forward "\n\n" nil t) (point-max))))
;; Fold continuation lines.
(goto-char (point-min))
(while (re-search-forward "\\(\r?\n[ \t]+\\)+" nil t)
(replace-match " " t t))
;; Remove any tabs; they are too confusing.
(subst-char-in-region (point-min) (point-max) ?\t ? )
(let ((headers (nnheader-parse-head t)))
(mail-header-set-chars headers chars)
(mail-header-set-number headers number)
headers))))
(defun nnml-open-nov (group)
(or (cdr (assoc group nnml-nov-buffer-alist))
(let ((buffer (find-file-noselect
(concat (nnmail-group-pathname group nnml-directory)
nnml-nov-file-name))))
(save-excursion
(set-buffer buffer)
(buffer-disable-undo (current-buffer)))
(setq nnml-nov-buffer-alist
(cons (cons group buffer) nnml-nov-buffer-alist))
buffer)))
(defun nnml-save-nov ()
(save-excursion
(while nnml-nov-buffer-alist
(when (buffer-name (cdar nnml-nov-buffer-alist))
(set-buffer (cdar nnml-nov-buffer-alist))
(and (buffer-modified-p)
(write-region
1 (point-max) (buffer-file-name) nil 'nomesg))
(set-buffer-modified-p nil)
(kill-buffer (current-buffer)))
(setq nnml-nov-buffer-alist (cdr nnml-nov-buffer-alist)))))
;;;###autoload
(defun nnml-generate-nov-databases ()
"Generate nov databases in all nnml directories."
(interactive)
;; Read the active file to make sure we don't re-use articles
;; numbers in empty groups.
(nnmail-activate 'nnml)
(nnml-open-server (or (nnoo-current-server 'nnml) ""))
(setq nnml-directory (expand-file-name nnml-directory))
;; Recurse down the directories.
(nnml-generate-nov-databases-1 nnml-directory)
;; Save the active file.
(nnmail-save-active nnml-group-alist nnml-active-file))
(defun nnml-generate-nov-databases-1 (dir)
(setq dir (file-name-as-directory dir))
;; We descend recursively
(let ((dirs (directory-files dir t nil t))
dir)
(while dirs
(setq dir (pop dirs))
(when (and (not (member (file-name-nondirectory dir) '("." "..")))
(file-directory-p dir))
(nnml-generate-nov-databases-1 dir))))
;; Do this directory.
(let ((files (sort
(mapcar
(lambda (name) (string-to-int name))
(directory-files dir nil "^[0-9]+$" t))
'<)))
(when files
(funcall nnml-generate-active-function dir)
;; Generate the nov file.
(nnml-generate-nov-file dir files))))
(defvar files)
(defun nnml-generate-active-info (dir)
;; Update the active info for this group.
(let ((group (nnheader-file-to-group
(directory-file-name dir) nnml-directory)))
(setq nnml-group-alist
(delq (assoc group nnml-group-alist) nnml-group-alist))
(push (list group
(cons (car files)
(let ((f files))
(while (cdr f) (setq f (cdr f)))
(car f))))
nnml-group-alist)))
(defun nnml-generate-nov-file (dir files)
(let* ((dir (file-name-as-directory dir))
(nov (concat dir nnml-nov-file-name))
(nov-buffer (get-buffer-create " *nov*"))
nov-line chars file headers)
(save-excursion
;; Init the nov buffer.
(set-buffer nov-buffer)
(buffer-disable-undo (current-buffer))
(erase-buffer)
(set-buffer nntp-server-buffer)
;; Delete the old NOV file.
(when (file-exists-p nov)
(funcall nnmail-delete-file-function nov))
(while files
(unless (file-directory-p
(setq file (concat dir (int-to-string (car files)))))
(erase-buffer)
(insert-file-contents file)
(narrow-to-region
(goto-char (point-min))
(progn
(search-forward "\n\n" nil t)
(setq chars (- (point-max) (point)))
(max 1 (1- (point)))))
(when (and (not (= 0 chars)) ; none of them empty files...
(not (= (point-min) (point-max))))
(goto-char (point-min))
(setq headers (nnml-parse-head chars (car files)))
(save-excursion
(set-buffer nov-buffer)
(goto-char (point-max))
(nnheader-insert-nov headers)))
(widen))
(setq files (cdr files)))
(save-excursion
(set-buffer nov-buffer)
(write-region 1 (point-max) (expand-file-name nov) nil
'nomesg)
(kill-buffer (current-buffer))))))
(defun nnml-nov-delete-article (group article)
(save-excursion
(set-buffer (nnml-open-nov group))
(goto-char (point-min))
(if (re-search-forward (concat "^" (int-to-string article) "\t") nil t)
(delete-region (match-beginning 0) (progn (forward-line 1) (point))))
t))
(provide 'nnml)
;;; nnml.el ends here

View file

@ -1,251 +0,0 @@
;;; nnoo.el --- OO Gnus Backends
;; Copyright (C) 1996 Free Software Foundation, Inc.
;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
;; Keywords: news
;; This file is part of GNU Emacs.
;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Commentary:
;;; Code:
(eval-when-compile (require 'cl))
(defvar nnoo-definition-alist nil)
(defvar nnoo-state-alist nil)
(defmacro defvoo (var init &optional doc &rest map)
"The same as `defvar', only takes list of variables to MAP to."
`(prog1
,(if doc
`(defvar ,var ,init ,doc)
`(defvar ,var ,init))
(nnoo-define ',var ',map)))
(put 'defvoo 'lisp-indent-function 2)
(put 'defvoo 'lisp-indent-hook 2)
(put 'defvoo 'edebug-form-spec '(var init &optional doc &rest map))
(defmacro deffoo (func args &rest forms)
"The same as `defun', only register FUNC."
`(prog1
(defun ,func ,args ,@forms)
(nnoo-register-function ',func)))
(put 'deffoo 'lisp-indent-function 2)
(put 'deffoo 'lisp-indent-hook 2)
(put 'deffoo 'edebug-form-spec '(&define name lambda-list def-body))
(defun nnoo-register-function (func)
(let ((funcs (nthcdr 3 (assoc (nnoo-backend func)
nnoo-definition-alist))))
(unless funcs
(error "%s belongs to a backend that hasn't been declared" func))
(setcar funcs (cons func (car funcs)))))
(defmacro nnoo-declare (backend &rest parents)
`(eval-and-compile
(push (list ',backend
(mapcar (lambda (p) (list p)) ',parents)
nil nil)
nnoo-definition-alist)))
(put 'nnoo-declare 'lisp-indent-function 1)
(put 'nnoo-declare 'lisp-indent-hook 1)
(defun nnoo-parents (backend)
(nth 1 (assoc backend nnoo-definition-alist)))
(defun nnoo-variables (backend)
(nth 2 (assoc backend nnoo-definition-alist)))
(defun nnoo-functions (backend)
(nth 3 (assoc backend nnoo-definition-alist)))
(defmacro nnoo-import (backend &rest imports)
`(nnoo-import-1 ',backend ',imports))
(put 'nnoo-import 'lisp-indent-function 1)
(put 'nnoo-import 'lisp-indent-hook 1)
(defun nnoo-import-1 (backend imports)
(let ((call-function
(if (symbolp (car imports)) (pop imports) 'nnoo-parent-function))
imp functions function)
(while (setq imp (pop imports))
(setq functions
(or (cdr imp)
(nnoo-functions (car imp))))
(while functions
(unless (fboundp (setq function
(nnoo-symbol backend (nnoo-rest-symbol
(car functions)))))
(eval `(deffoo ,function (&rest args)
(,call-function ',backend ',(car functions) args))))
(pop functions)))))
(defun nnoo-parent-function (backend function args)
(let* ((pbackend (nnoo-backend function)))
(nnoo-change-server pbackend (nnoo-current-server backend)
(cdr (assq pbackend (nnoo-parents backend))))
(apply function args)))
(defun nnoo-execute (backend function &rest args)
"Execute FUNCTION on behalf of BACKEND."
(let* ((pbackend (nnoo-backend function)))
(nnoo-change-server pbackend (nnoo-current-server backend)
(cdr (assq pbackend (nnoo-parents backend))))
(apply function args)))
(defmacro nnoo-map-functions (backend &rest maps)
`(nnoo-map-functions-1 ',backend ',maps))
(put 'nnoo-map-functions 'lisp-indent-function 1)
(put 'nnoo-map-functions 'lisp-indent-hook 1)
(defun nnoo-map-functions-1 (backend maps)
(let (m margs i)
(while (setq m (pop maps))
(setq i 0
margs nil)
(while (< i (length (cdr m)))
(if (numberp (nth i (cdr m)))
(push `(nth ,i args) margs)
(push (nth i (cdr m)) margs))
(incf i))
(eval `(deffoo ,(nnoo-symbol backend (nnoo-rest-symbol (car m)))
(&rest args)
(nnoo-parent-function ',backend ',(car m)
,(cons 'list (nreverse margs))))))))
(defun nnoo-backend (symbol)
(string-match "^[^-]+-" (symbol-name symbol))
(intern (substring (symbol-name symbol) 0 (1- (match-end 0)))))
(defun nnoo-rest-symbol (symbol)
(string-match "^[^-]+-" (symbol-name symbol))
(intern (substring (symbol-name symbol) (match-end 0))))
(defun nnoo-symbol (backend symbol)
(intern (format "%s-%s" backend symbol)))
(defun nnoo-define (var map)
(let* ((backend (nnoo-backend var))
(def (assq backend nnoo-definition-alist))
(parents (nth 1 def)))
(unless def
(error "%s belongs to a backend that hasn't been declared." var))
(setcar (nthcdr 2 def)
(delq (assq var (nth 2 def)) (nth 2 def)))
(setcar (nthcdr 2 def)
(cons (cons var (symbol-value var))
(nth 2 def)))
(while map
(nconc (assq (nnoo-backend (car map)) parents)
(list (list (pop map) var))))))
(defun nnoo-change-server (backend server defs)
(let* ((bstate (cdr (assq backend nnoo-state-alist)))
(sdefs (assq backend nnoo-definition-alist))
(current (car bstate))
(parents (nnoo-parents backend))
state)
(unless bstate
(push (setq bstate (list backend nil))
nnoo-state-alist)
(pop bstate))
(if (equal server current)
t
(nnoo-push-server backend current)
(setq state (or (cdr (assoc server (cddr bstate)))
(nnoo-variables backend)))
(while state
(set (caar state) (cdar state))
(pop state))
(setcar bstate server)
(unless (cdr (assoc server (cddr bstate)))
(while defs
(set (caar defs) (cadar defs))
(pop defs)))
(while parents
(nnoo-change-server
(caar parents) server
(mapcar (lambda (def) (list (car def) (symbol-value (cadr def))))
(cdar parents)))
(pop parents))))
t)
(defun nnoo-push-server (backend current)
(let ((bstate (assq backend nnoo-state-alist))
(defs (nnoo-variables backend)))
;; Remove the old definition.
(setcdr (cdr bstate) (delq (assoc current (cddr bstate)) (cddr bstate)))
(let (state)
(while defs
(push (cons (caar defs) (symbol-value (caar defs)))
state)
(pop defs))
(nconc bstate (list (cons current state))))))
(defun nnoo-current-server-p (backend server)
(equal (nnoo-current-server backend) server))
(defun nnoo-current-server (backend)
(nth 1 (assq backend nnoo-state-alist)))
(defun nnoo-close-server (backend &optional server)
(unless server
(setq server (nnoo-current-server backend)))
(when server
(let* ((bstate (cdr (assq backend nnoo-state-alist)))
(defs (assoc server (cdr bstate))))
(when bstate
(setcar bstate nil)
(setcdr bstate (delq defs (cdr bstate)))
(pop defs)
(while defs
(set (car (pop defs)) nil)))))
t)
(defun nnoo-close (backend)
(setq nnoo-state-alist
(delq (assq backend nnoo-state-alist)
nnoo-state-alist))
t)
(defun nnoo-status-message (backend server)
(nnheader-get-report backend))
(defun nnoo-server-opened (backend server)
(and (nnoo-current-server-p backend server)
nntp-server-buffer
(buffer-name nntp-server-buffer)))
(defmacro nnoo-define-basics (backend)
`(eval-and-compile
(nnoo-define-basics-1 ',backend)))
(defun nnoo-define-basics-1 (backend)
(let ((functions '(close-server server-opened status-message)))
(while functions
(eval `(deffoo ,(nnoo-symbol backend (car functions))
(&optional server)
(,(nnoo-symbol 'nnoo (pop functions)) ',backend server)))))
(eval `(deffoo ,(nnoo-symbol backend 'open-server)
(server &optional defs)
(nnoo-change-server ',backend server defs))))
(provide 'nnoo)
;;; nnoo.el ends here.

View file

@ -1,747 +0,0 @@
;;; nnsoup.el --- SOUP access for Gnus
;; 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, mail
;; This file is part of GNU Emacs.
;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Commentary:
;;; Code:
(require 'nnheader)
(require 'nnmail)
(require 'gnus-soup)
(require 'gnus-msg)
(require 'nnoo)
(eval-when-compile (require 'cl))
(nnoo-declare nnsoup)
(defvoo nnsoup-directory "~/SOUP/"
"*SOUP packet directory.")
(defvoo nnsoup-tmp-directory "/tmp/"
"*Where nnsoup will store temporary files.")
(defvoo nnsoup-replies-directory (concat nnsoup-directory "replies/")
"*Directory where outgoing packets will be composed.")
(defvoo nnsoup-replies-format-type ?n
"*Format of the replies packages.")
(defvoo nnsoup-replies-index-type ?n
"*Index type of the replies packages.")
(defvoo nnsoup-active-file (concat nnsoup-directory "active")
"Active file.")
(defvoo nnsoup-packer "tar cf - %s | gzip > $HOME/Soupin%d.tgz"
"Format string command for packing a SOUP packet.
The SOUP files will be inserted where the %s is in the string.
This string MUST contain both %s and %d. The file number will be
inserted where %d appears.")
(defvoo nnsoup-unpacker "gunzip -c %s | tar xvf -"
"*Format string command for unpacking a SOUP packet.
The SOUP packet file name will be inserted at the %s.")
(defvoo nnsoup-packet-directory "~/"
"*Where nnsoup will look for incoming packets.")
(defvoo nnsoup-packet-regexp "Soupout"
"*Regular expression matching SOUP packets in `nnsoup-packet-directory'.")
(defconst nnsoup-version "nnsoup 0.0"
"nnsoup version.")
(defvoo nnsoup-status-string "")
(defvoo nnsoup-group-alist nil)
(defvoo nnsoup-current-prefix 0)
(defvoo nnsoup-replies-list nil)
(defvoo nnsoup-buffers nil)
(defvoo nnsoup-current-group nil)
(defvoo nnsoup-group-alist-touched nil)
;;; Interface functions.
(nnoo-define-basics nnsoup)
(deffoo nnsoup-retrieve-headers (sequence &optional group server fetch-old)
(nnsoup-possibly-change-group group)
(save-excursion
(set-buffer nntp-server-buffer)
(erase-buffer)
(let ((areas (cddr (assoc nnsoup-current-group nnsoup-group-alist)))
(articles sequence)
(use-nov t)
useful-areas this-area-seq msg-buf)
(if (stringp (car sequence))
;; We don't support fetching by Message-ID.
'headers
;; We go through all the areas and find which files the
;; articles in SEQUENCE come from.
(while (and areas sequence)
;; Peel off areas that are below sequence.
(while (and areas (< (cdaar areas) (car sequence)))
(setq areas (cdr areas)))
(when areas
;; This is a useful area.
(push (car areas) useful-areas)
(setq this-area-seq nil)
;; We take note whether this MSG has a corresponding IDX
;; for later use.
(when (or (= (gnus-soup-encoding-index
(gnus-soup-area-encoding (nth 1 (car areas)))) ?n)
(not (file-exists-p
(nnsoup-file
(gnus-soup-area-prefix (nth 1 (car areas)))))))
(setq use-nov nil))
;; We assign the portion of `sequence' that is relevant to
;; this MSG packet to this packet.
(while (and sequence (<= (car sequence) (cdaar areas)))
(push (car sequence) this-area-seq)
(setq sequence (cdr sequence)))
(setcar useful-areas (cons (nreverse this-area-seq)
(car useful-areas)))))
;; We now have a list of article numbers and corresponding
;; areas.
(setq useful-areas (nreverse useful-areas))
;; Two different approaches depending on whether all the MSG
;; files have corresponding IDX files. If they all do, we
;; simply return the relevant IDX files and let Gnus sort out
;; what lines are relevant. If some of the IDX files are
;; missing, we must return HEADs for all the articles.
(if use-nov
;; We have IDX files for all areas.
(progn
(while useful-areas
(goto-char (point-max))
(let ((b (point))
(number (car (nth 1 (car useful-areas))))
(index-buffer (nnsoup-index-buffer
(gnus-soup-area-prefix
(nth 2 (car useful-areas))))))
(when index-buffer
(insert-buffer-substring index-buffer)
(goto-char b)
;; We have to remove the index number entires and
;; insert article numbers instead.
(while (looking-at "[0-9]+")
(replace-match (int-to-string number) t t)
(incf number)
(forward-line 1))))
(setq useful-areas (cdr useful-areas)))
'nov)
;; We insert HEADs.
(while useful-areas
(setq articles (caar useful-areas)
useful-areas (cdr useful-areas))
(while articles
(when (setq msg-buf
(nnsoup-narrow-to-article
(car articles) (cdar useful-areas) 'head))
(goto-char (point-max))
(insert (format "221 %d Article retrieved.\n" (car articles)))
(insert-buffer-substring msg-buf)
(goto-char (point-max))
(insert ".\n"))
(setq articles (cdr articles))))
(nnheader-fold-continuation-lines)
'headers)))))
(deffoo nnsoup-open-server (server &optional defs)
(nnoo-change-server 'nnsoup server defs)
(when (not (file-exists-p nnsoup-directory))
(condition-case ()
(make-directory nnsoup-directory t)
(error t)))
(cond
((not (file-exists-p nnsoup-directory))
(nnsoup-close-server)
(nnheader-report 'nnsoup "Couldn't create directory: %s" nnsoup-directory))
((not (file-directory-p (file-truename nnsoup-directory)))
(nnsoup-close-server)
(nnheader-report 'nnsoup "Not a directory: %s" nnsoup-directory))
(t
(nnsoup-read-active-file)
(nnheader-report 'nnsoup "Opened server %s using directory %s"
server nnsoup-directory)
t)))
(deffoo nnsoup-request-close ()
(nnsoup-write-active-file)
(nnsoup-write-replies)
(gnus-soup-save-areas)
;; Kill all nnsoup buffers.
(let (buffer)
(while nnsoup-buffers
(setq buffer (cdr (pop nnsoup-buffers)))
(and buffer
(buffer-name buffer)
(kill-buffer buffer))))
(setq nnsoup-group-alist nil
nnsoup-group-alist-touched nil
nnsoup-current-group nil
nnsoup-replies-list nil)
(nnoo-close-server 'nnoo)
t)
(deffoo nnsoup-request-article (id &optional newsgroup server buffer)
(nnsoup-possibly-change-group newsgroup)
(let (buf)
(save-excursion
(set-buffer (or buffer nntp-server-buffer))
(erase-buffer)
(when (and (not (stringp id))
(setq buf (nnsoup-narrow-to-article id)))
(insert-buffer-substring buf)
t))))
(deffoo nnsoup-request-group (group &optional server dont-check)
(nnsoup-possibly-change-group group)
(if dont-check
t
(let ((active (cadr (assoc group nnsoup-group-alist))))
(if (not active)
(nnheader-report 'nnsoup "No such group: %s" group)
(nnheader-insert
"211 %d %d %d %s\n"
(max (1+ (- (cdr active) (car active))) 0)
(car active) (cdr active) group)))))
(deffoo nnsoup-request-type (group &optional article)
(nnsoup-possibly-change-group group)
(if (not article)
'unknown
(let ((kind (gnus-soup-encoding-kind
(gnus-soup-area-encoding
(nth 1 (nnsoup-article-to-area
article nnsoup-current-group))))))
(cond ((= kind ?m) 'mail)
((= kind ?n) 'news)
(t 'unknown)))))
(deffoo nnsoup-close-group (group &optional server)
;; Kill all nnsoup buffers.
(let ((buffers nnsoup-buffers)
elem)
(while buffers
(when (equal (car (setq elem (pop buffers))) group)
(setq nnsoup-buffers (delq elem nnsoup-buffers))
(and (cdr elem) (buffer-name (cdr elem))
(kill-buffer (cdr elem))))))
t)
(deffoo nnsoup-request-list (&optional server)
(save-excursion
(set-buffer nntp-server-buffer)
(erase-buffer)
(unless nnsoup-group-alist
(nnsoup-read-active-file))
(let ((alist nnsoup-group-alist)
(standard-output (current-buffer))
entry)
(while (setq entry (pop alist))
(insert (car entry) " ")
(princ (cdadr entry))
(insert " ")
(princ (caadr entry))
(insert " y\n"))
t)))
(deffoo nnsoup-request-scan (group &optional server)
(nnsoup-unpack-packets))
(deffoo nnsoup-request-newgroups (date &optional server)
(nnsoup-request-list))
(deffoo nnsoup-request-list-newsgroups (&optional server)
nil)
(deffoo nnsoup-request-post (&optional server)
(nnsoup-store-reply "news")
t)
(deffoo nnsoup-request-mail (&optional server)
(nnsoup-store-reply "mail")
t)
(deffoo nnsoup-request-expire-articles (articles group &optional server force)
(nnsoup-possibly-change-group group)
(let* ((total-infolist (assoc group nnsoup-group-alist))
(active (cadr total-infolist))
(infolist (cddr total-infolist))
info range-list mod-time prefix)
(while infolist
(setq info (pop infolist)
range-list (gnus-uncompress-range (car info))
prefix (gnus-soup-area-prefix (nth 1 info)))
(when ;; All the articles in this file are marked for expiry.
(and (or (setq mod-time (nth 5 (file-attributes
(nnsoup-file prefix))))
(setq mod-time (nth 5 (file-attributes
(nnsoup-file prefix t)))))
(gnus-sublist-p articles range-list)
;; This file is old enough.
(nnmail-expired-article-p group mod-time force))
;; Ok, we delete this file.
(when (condition-case nil
(progn
(nnheader-message
5 "Deleting %s in group %s..." (nnsoup-file prefix)
group)
(when (file-exists-p (nnsoup-file prefix))
(delete-file (nnsoup-file prefix)))
(nnheader-message
5 "Deleting %s in group %s..." (nnsoup-file prefix t)
group)
(when (file-exists-p (nnsoup-file prefix t))
(delete-file (nnsoup-file prefix t)))
t)
(error nil))
(setcdr (cdr total-infolist) (delq info (cddr total-infolist)))
(setq articles (gnus-sorted-complement articles range-list))))
(when (not mod-time)
(setcdr (cdr total-infolist) (delq info (cddr total-infolist)))))
(if (cddr total-infolist)
(setcar active (caaadr (cdr total-infolist)))
(setcar active (1+ (cdr active))))
(nnsoup-write-active-file t)
;; Return the articles that weren't expired.
articles))
;;; Internal functions
(defun nnsoup-possibly-change-group (group &optional force)
(if group
(setq nnsoup-current-group group)
t))
(defun nnsoup-read-active-file ()
(setq nnsoup-group-alist nil)
(when (file-exists-p nnsoup-active-file)
(condition-case ()
(load nnsoup-active-file t t t)
(error nil))
;; Be backwards compatible.
(when (and nnsoup-group-alist
(not (atom (caadar nnsoup-group-alist))))
(let ((alist nnsoup-group-alist)
entry e min max)
(while (setq e (cdr (setq entry (pop alist))))
(setq min (caaar e))
(while (cdr e)
(setq e (cdr e)))
(setq max (cdaar e))
(setcdr entry (cons (cons min max) (cdr entry)))))
(setq nnsoup-group-alist-touched t))
nnsoup-group-alist))
(defun nnsoup-write-active-file (&optional force)
(when (and nnsoup-group-alist
(or force
nnsoup-group-alist-touched))
(setq nnsoup-group-alist-touched nil)
(nnheader-temp-write nnsoup-active-file
(let ((standard-output (current-buffer)))
(prin1 `(setq nnsoup-group-alist ',nnsoup-group-alist))
(insert "\n")
(prin1 `(setq nnsoup-current-prefix ,nnsoup-current-prefix))
(insert "\n")))))
(defun nnsoup-next-prefix ()
"Return the next free prefix."
(let (prefix)
(while (or (file-exists-p
(nnsoup-file (setq prefix (int-to-string
nnsoup-current-prefix))))
(file-exists-p (nnsoup-file prefix t)))
(incf nnsoup-current-prefix))
(incf nnsoup-current-prefix)
prefix))
(defun nnsoup-read-areas ()
(save-excursion
(set-buffer nntp-server-buffer)
(let ((areas (gnus-soup-parse-areas (concat nnsoup-tmp-directory "AREAS")))
entry number area lnum cur-prefix file)
;; Go through all areas in the new AREAS file.
(while (setq area (pop areas))
;; Change the name to the permanent name and move the files.
(setq cur-prefix (nnsoup-next-prefix))
(message "Incorporating file %s..." cur-prefix)
(when (file-exists-p
(setq file (concat nnsoup-tmp-directory
(gnus-soup-area-prefix area) ".IDX")))
(rename-file file (nnsoup-file cur-prefix)))
(when (file-exists-p
(setq file (concat nnsoup-tmp-directory
(gnus-soup-area-prefix area) ".MSG")))
(rename-file file (nnsoup-file cur-prefix t))
(gnus-soup-set-area-prefix area cur-prefix)
;; Find the number of new articles in this area.
(setq number (nnsoup-number-of-articles area))
(if (not (setq entry (assoc (gnus-soup-area-name area)
nnsoup-group-alist)))
;; If this is a new area (group), we just add this info to
;; the group alist.
(push (list (gnus-soup-area-name area)
(cons 1 number)
(list (cons 1 number) area))
nnsoup-group-alist)
;; There are already articles in this group, so we add this
;; info to the end of the entry.
(nconc entry (list (list (cons (1+ (setq lnum (cdadr entry)))
(+ lnum number))
area)))
(setcdr (cadr entry) (+ lnum number))))))
(nnsoup-write-active-file t)
(delete-file (concat nnsoup-tmp-directory "AREAS"))))
(defun nnsoup-number-of-articles (area)
(save-excursion
(cond
;; If the number is in the area info, we just return it.
((gnus-soup-area-number area)
(gnus-soup-area-number area))
;; If there is an index file, we just count the lines.
((/= (gnus-soup-encoding-index (gnus-soup-area-encoding area)) ?n)
(set-buffer (nnsoup-index-buffer (gnus-soup-area-prefix area)))
(count-lines (point-min) (point-max)))
;; We do it the hard way - re-searching through the message
;; buffer.
(t
(set-buffer (nnsoup-message-buffer (gnus-soup-area-prefix area)))
(goto-char (point-min))
(let ((regexp (nnsoup-header (gnus-soup-encoding-format
(gnus-soup-area-encoding area))))
(num 0))
(while (re-search-forward regexp nil t)
(setq num (1+ num)))
num)))))
(defun nnsoup-index-buffer (prefix &optional message)
(let* ((file (concat prefix (if message ".MSG" ".IDX")))
(buffer-name (concat " *nnsoup " file "*")))
(or (get-buffer buffer-name) ; File aready loaded.
(when (file-exists-p (concat nnsoup-directory file))
(save-excursion ; Load the file.
(set-buffer (get-buffer-create buffer-name))
(buffer-disable-undo (current-buffer))
(push (cons nnsoup-current-group (current-buffer)) nnsoup-buffers)
(insert-file-contents (concat nnsoup-directory file))
(current-buffer))))))
(defun nnsoup-file (prefix &optional message)
(expand-file-name
(concat nnsoup-directory prefix (if message ".MSG" ".IDX"))))
(defun nnsoup-message-buffer (prefix)
(nnsoup-index-buffer prefix 'msg))
(defun nnsoup-unpack-packets ()
"Unpack all packets in `nnsoup-packet-directory'."
(let ((packets (directory-files
nnsoup-packet-directory t nnsoup-packet-regexp))
packet)
(while (setq packet (pop packets))
(message (format "nnsoup: unpacking %s..." packet))
(if (not (gnus-soup-unpack-packet
nnsoup-tmp-directory nnsoup-unpacker packet))
(message "Couldn't unpack %s" packet)
(delete-file packet)
(nnsoup-read-areas)
(message "Unpacking...done")))))
(defun nnsoup-narrow-to-article (article &optional area head)
(let* ((area (or area (nnsoup-article-to-area article nnsoup-current-group)))
(prefix (and area (gnus-soup-area-prefix (nth 1 area))))
(msg-buf (and prefix (nnsoup-index-buffer prefix 'msg)))
beg end)
(when area
(save-excursion
(cond
;; There is no MSG file.
((null msg-buf)
nil)
;; We use the index file to find out where the article begins and ends.
((and (= (gnus-soup-encoding-index
(gnus-soup-area-encoding (nth 1 area)))
?c)
(file-exists-p (nnsoup-file prefix)))
(set-buffer (nnsoup-index-buffer prefix))
(widen)
(goto-char (point-min))
(forward-line (- article (caar area)))
(setq beg (read (current-buffer)))
(forward-line 1)
(if (looking-at "[0-9]+")
(progn
(setq end (read (current-buffer)))
(set-buffer msg-buf)
(widen)
(let ((format (gnus-soup-encoding-format
(gnus-soup-area-encoding (nth 1 area)))))
(goto-char end)
(if (or (= format ?n) (= format ?m))
(setq end (progn (forward-line -1) (point))))))
(set-buffer msg-buf))
(widen)
(narrow-to-region beg (or end (point-max))))
(t
(set-buffer msg-buf)
(widen)
(goto-char (point-min))
(let ((header (nnsoup-header
(gnus-soup-encoding-format
(gnus-soup-area-encoding (nth 1 area))))))
(re-search-forward header nil t (- article (caar area)))
(narrow-to-region
(match-beginning 0)
(if (re-search-forward header nil t)
(match-beginning 0)
(point-max))))))
(goto-char (point-min))
(if (not head)
()
(narrow-to-region
(point-min)
(if (search-forward "\n\n" nil t)
(1- (point))
(point-max))))
msg-buf))))
(defun nnsoup-header (format)
(cond
((= format ?n)
"^#! *rnews +[0-9]+ *$")
((= format ?m)
(concat "^" message-unix-mail-delimiter))
((= format ?M)
"^\^A\^A\^A\^A\n")
(t
(error "Unknown format: %c" format))))
;;;###autoload
(defun nnsoup-pack-replies ()
"Make an outbound package of SOUP replies."
(interactive)
;; Write all data buffers.
(gnus-soup-save-areas)
;; Write the active file.
(nnsoup-write-active-file)
;; Write the REPLIES file.
(nnsoup-write-replies)
;; Pack all these files into a SOUP packet.
(gnus-soup-pack nnsoup-replies-directory nnsoup-packer))
(defun nnsoup-write-replies ()
"Write the REPLIES file."
(when nnsoup-replies-list
(gnus-soup-write-replies nnsoup-replies-directory nnsoup-replies-list)
(setq nnsoup-replies-list nil)))
(defun nnsoup-article-to-area (article group)
"Return the area that ARTICLE in GROUP is located in."
(let ((areas (cddr (assoc group nnsoup-group-alist))))
(while (and areas (< (cdaar areas) article))
(setq areas (cdr areas)))
(and areas (car areas))))
(defvar nnsoup-old-functions
(list message-send-mail-function message-send-news-function))
;;;###autoload
(defun nnsoup-set-variables ()
"Use the SOUP methods for posting news and mailing mail."
(interactive)
(setq message-send-news-function 'nnsoup-request-post)
(setq message-send-mail-function 'nnsoup-request-mail))
;;;###autoload
(defun nnsoup-revert-variables ()
"Revert posting and mailing methods to the standard Emacs methods."
(interactive)
(setq message-send-mail-function (car nnsoup-old-functions))
(setq message-send-news-function (cadr nnsoup-old-functions)))
(defun nnsoup-store-reply (kind)
;; Mostly stolen from `message.el'.
(require 'mail-utils)
(let ((tembuf (generate-new-buffer " message temp"))
(case-fold-search nil)
(news (message-news-p))
(resend-to-addresses (mail-fetch-field "resent-to"))
delimline
(mailbuf (current-buffer)))
(unwind-protect
(save-excursion
(save-restriction
(message-narrow-to-headers)
(if (equal kind "mail")
(message-generate-headers message-required-mail-headers)
(message-generate-headers message-required-news-headers)))
(set-buffer tembuf)
(erase-buffer)
(insert-buffer-substring mailbuf)
;; Remove some headers.
(save-restriction
(message-narrow-to-headers)
;; Remove some headers.
(message-remove-header message-ignored-mail-headers t))
(goto-char (point-max))
;; require one newline at the end.
(or (= (preceding-char) ?\n)
(insert ?\n))
(when (and news
(equal kind "mail")
(or (mail-fetch-field "cc")
(mail-fetch-field "to")))
(message-insert-courtesy-copy))
(let ((case-fold-search t))
;; Change header-delimiter to be what sendmail expects.
(goto-char (point-min))
(re-search-forward
(concat "^" (regexp-quote mail-header-separator) "\n"))
(replace-match "\n")
(backward-char 1)
(setq delimline (point-marker))
;; Insert an extra newline if we need it to work around
;; Sun's bug that swallows newlines.
(goto-char (1+ delimline))
(when (eval message-mailer-swallows-blank-line)
(newline))
(let ((msg-buf
(gnus-soup-store
nnsoup-replies-directory
(nnsoup-kind-to-prefix kind) nil nnsoup-replies-format-type
nnsoup-replies-index-type))
(num 0))
(when (and msg-buf (bufferp msg-buf))
(save-excursion
(set-buffer msg-buf)
(goto-char (point-min))
(while (re-search-forward "^#! *rnews" nil t)
(incf num)))
(message "Stored %d messages" num)))
(nnsoup-write-replies)
(kill-buffer tembuf))))))
(defun nnsoup-kind-to-prefix (kind)
(unless nnsoup-replies-list
(setq nnsoup-replies-list
(gnus-soup-parse-replies
(concat nnsoup-replies-directory "REPLIES"))))
(let ((replies nnsoup-replies-list))
(while (and replies
(not (string= kind (gnus-soup-reply-kind (car replies)))))
(setq replies (cdr replies)))
(if replies
(gnus-soup-reply-prefix (car replies))
(setq nnsoup-replies-list
(cons (vector (gnus-soup-unique-prefix nnsoup-replies-directory)
kind
(format "%c%c%c"
nnsoup-replies-format-type
nnsoup-replies-index-type
(if (string= kind "news")
?n ?m)))
nnsoup-replies-list))
(gnus-soup-reply-prefix (car nnsoup-replies-list)))))
(defun nnsoup-make-active ()
"(Re-)create the SOUP active file."
(interactive)
(let ((files (sort (directory-files nnsoup-directory t "IDX$")
(lambda (f1 f2)
(< (progn (string-match "/\\([0-9]+\\)\\." f1)
(string-to-int (match-string 1 f1)))
(progn (string-match "/\\([0-9]+\\)\\." f2)
(string-to-int (match-string 1 f2)))))))
active group lines ident elem min)
(set-buffer (get-buffer-create " *nnsoup work*"))
(buffer-disable-undo (current-buffer))
(while files
(message "Doing %s..." (car files))
(erase-buffer)
(insert-file-contents (car files))
(goto-char (point-min))
(if (not (re-search-forward "^[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t *\\(Xref: \\)? *[^ ]* \\([^ ]+\\):[0-9]" nil t))
(setq group "unknown")
(setq group (match-string 2)))
(setq lines (count-lines (point-min) (point-max)))
(setq ident (progn (string-match
"/\\([0-9]+\\)\\." (car files))
(substring
(car files) (match-beginning 1)
(match-end 1))))
(if (not (setq elem (assoc group active)))
(push (list group (cons 1 lines)
(list (cons 1 lines)
(vector ident group "ncm" "" lines)))
active)
(nconc elem
(list
(list (cons (1+ (setq min (cdadr elem)))
(+ min lines))
(vector ident group "ncm" "" lines))))
(setcdr (cadr elem) (+ min lines)))
(setq files (cdr files)))
(message "")
(setq nnsoup-group-alist active)
(nnsoup-write-active-file t)))
(defun nnsoup-delete-unreferenced-message-files ()
"Delete any *.MSG and *.IDX files that aren't known by nnsoup."
(interactive)
(let* ((known (apply 'nconc (mapcar
(lambda (ga)
(mapcar
(lambda (area)
(gnus-soup-area-prefix (cadr area)))
(cddr ga)))
nnsoup-group-alist)))
(regexp "\\.MSG$\\|\\.IDX$")
(files (directory-files nnsoup-directory nil regexp))
non-files file)
;; Find all files that aren't known by nnsoup.
(while (setq file (pop files))
(string-match regexp file)
(unless (member (substring file 0 (match-beginning 0)) known)
(push file non-files)))
;; Sort and delete the files.
(setq non-files (sort non-files 'string<))
(map-y-or-n-p "Delete file %s? "
(lambda (file) (delete-file (concat nnsoup-directory file)))
non-files)))
(provide 'nnsoup)
;;; nnsoup.el ends here

View file

@ -1,511 +0,0 @@
;;; nnspool.el --- spool access for GNU Emacs
;; 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>
;; Keywords: news
;; This file is part of GNU Emacs.
;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Commentary:
;;; Code:
(require 'nnheader)
(require 'nntp)
(require 'timezone)
(require 'nnoo)
(eval-when-compile (require 'cl))
(nnoo-declare nnspool)
(defvoo nnspool-inews-program news-inews-program
"Program to post news.
This is most commonly `inews' or `injnews'.")
(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.")
(defvoo nnspool-spool-directory (file-name-as-directory news-path)
"Local news spool directory.")
(defvoo nnspool-nov-directory (concat nnspool-spool-directory "over.view/")
"Local news nov directory.")
(defvoo nnspool-lib-dir "/usr/lib/news/"
"Where the local news library files are stored.")
(defvoo nnspool-active-file (concat nnspool-lib-dir "active")
"Local news active file.")
(defvoo nnspool-newsgroups-file (concat nnspool-lib-dir "newsgroups")
"Local news newsgroups file.")
(defvoo nnspool-distributions-file (concat nnspool-lib-dir "distribs.pat")
"Local news distributions file.")
(defvoo nnspool-history-file (concat nnspool-lib-dir "history")
"Local news history file.")
(defvoo nnspool-active-times-file (concat nnspool-lib-dir "active.times")
"Local news active date file.")
(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.")
(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
"If non-nil, use sed to get the relevant portion from the overview file.
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.")
(defvoo nnspool-current-directory nil
"Current news group directory.")
(defvoo nnspool-current-group nil)
(defvoo nnspool-status-string "")
;;; Interface functions.
(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)
(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
;; No NOV headers here, so we do it the hard way.
(while (setq article (pop articles))
(if (stringp article)
;; 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 (% (incf count) 20))
(message "nnspool: Receiving headers... %d%%"
(/ (* count 100) number))))
(and do-message
(message "nnspool: Receiving headers...done"))
;; Fold continuation lines.
(nnheader-fold-continuation-lines)
'headers)))))
(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)))
(deffoo nnspool-request-article (id &optional group server buffer)
"Select article by message ID (or number)."
(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 group)
(let ((res (nnspool-request-article id)))
(when res
(save-excursion
(set-buffer nntp-server-buffer)
(goto-char (point-min))
(when (search-forward "\n\n" nil t)
(delete-region (point-min) (point)))
res))))
(deffoo nnspool-request-head (id &optional group server)
"Select article head by message ID (or number)."
(nnspool-possibly-change-directory group)
(let ((res (nnspool-request-article id)))
(when res
(save-excursion
(set-buffer nntp-server-buffer)
(goto-char (point-min))
(when (search-forward "\n\n" nil t)
(delete-region (1- (point)) (point-max)))
(nnheader-fold-continuation-lines)))
res))
(deffoo nnspool-request-group (group &optional server dont-check)
"Select news GROUP."
(let ((pathname (nnspool-article-pathname group))
dir)
(if (not (file-directory-p pathname))
(nnheader-report
'nnspool "Invalid group name (no such directory): %s" group)
(setq nnspool-current-directory pathname)
(nnheader-report 'nnspool "Selected group %s" group)
(if dont-check
(progn
(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))))))
(deffoo nnspool-request-type (group &optional article)
'news)
(deffoo nnspool-close-group (group &optional server)
t)
(deffoo nnspool-request-list (&optional server)
"List active newsgroups."
(save-excursion
(or (nnspool-find-file nnspool-active-file)
(nnheader-report 'nnspool (nnheader-file-error nnspool-active-file)))))
(deffoo nnspool-request-list-newsgroups (&optional server)
"List newsgroups (defined in NNTP2)."
(save-excursion
(or (nnspool-find-file nnspool-newsgroups-file)
(nnheader-report 'nnspool (nnheader-file-error
nnspool-newsgroups-file)))))
(deffoo nnspool-request-list-distributions (&optional server)
"List distributions (defined in NNTP2)."
(save-excursion
(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>.
(deffoo nnspool-request-newgroups (date &optional server)
"List groups created after DATE."
(if (nnspool-find-file nnspool-active-times-file)
(save-excursion
;; Find the last valid line.
(goto-char (point-max))
(while (and (not (looking-at
"\\([^ ]+\\) +\\([0-9]+\\)[0-9][0-9][0-9] "))
(zerop (forward-line -1))))
(let ((seconds (nnspool-seconds-since-epoch date))
groups)
;; Go through lines and add the latest groups to a list.
(while (and (looking-at "\\([^ ]+\\) +[0-9]+ ")
(progn
;; We insert a .0 to make the list reader
;; interpret the number as a float. It is far
;; too big to be stored in a lisp integer.
(goto-char (1- (match-end 0)))
(insert ".0")
(> (progn
(goto-char (match-end 1))
(read (current-buffer)))
seconds))
(setq groups (cons (buffer-substring
(match-beginning 1) (match-end 1))
groups))
(zerop (forward-line -1))))
(erase-buffer)
(while groups
(insert (car groups) " 0 0 y\n")
(setq groups (cdr groups))))
t)
nil))
(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
(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
(set-buffer (process-buffer proc))
(goto-char (point-min))
(if (or (zerop (buffer-size))
(search-forward "spooled" nil t))
(kill-buffer (current-buffer))
;; 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)
(ding)
(run-hooks 'nnspool-rejected-article-hook))))
(defun nnspool-retrieve-headers-with-nov (articles &optional fetch-old)
(if (or gnus-nov-is-evil nnspool-nov-is-evil)
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)))
(car articles))))
(call-process "awk" nil t nil
(format "BEGIN {firstmsg=%d; lastmsg=%d;}\n $1 >= firstmsg && $1 <= lastmsg {print;}"
(1- first) (1+ last))
file)))
;; Fixed by fdc@cliwe.ping.de (Frank D. Cringle).
;; Find out what group an article identified by a Message-ID is in.
(defun nnspool-find-id (id)
(save-excursion
(set-buffer (get-buffer-create " *nnspool work*"))
(buffer-disable-undo (current-buffer))
(erase-buffer)
(condition-case ()
(call-process "grep" nil t nil (regexp-quote 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 (nnheader-insert-file-contents-literally file) t)
(file-error nil)))
(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 &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)))
(timezone-parse-date date)))
(ttime (mapcar (lambda (ti) (and ti (string-to-int ti)))
(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))))
(+ (* (car unix) 65536.0)
(cadr unix))))
(provide 'nnspool)
;;; nnspool.el ends here

File diff suppressed because it is too large Load diff

View file

@ -1,409 +0,0 @@
;;; nnvirtual.el --- virtual newsgroups access for Gnus
;; Copyright (C) 1994,95,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.
;; 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:
;; The other access methods (nntp, nnspool, etc) are general news
;; access methods. This module relies on Gnus and can not be used
;; separately.
;;; Code:
(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")
(defvoo nnvirtual-current-group nil)
(defvoo nnvirtual-component-groups nil)
(defvoo nnvirtual-mapping nil)
(defvoo nnvirtual-status-string "")
(eval-and-compile
(autoload 'gnus-cache-articles-in-group "gnus-cache"))
;;; Interface functions.
(nnoo-define-basics nnvirtual)
(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 (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)
(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."))
(deffoo nnvirtual-request-newgroups (date &optional server)
(nnheader-report 'nnvirtual "NEWGROUPS is not supported."))
(deffoo nnvirtual-request-list-newsgroups (&optional server)
(nnheader-report 'nnvirtual "LIST NEWSGROUPS is not implemented."))
(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
(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)))
(setq mr marks)
(while (cdr mr)
(if (cdadr mr)
(setq mr (cdr mr))
(setcdr mr (cddr mr))))
;; 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)))
(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

View file

@ -1,110 +0,0 @@
;;; score-mode.el --- mode for editing Gnus score files
;; Copyright (C) 1996 Free Software Foundation, Inc.
;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
;; Keywords: news, mail
;; This file is part of GNU Emacs.
;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Commentary:
;;; Code:
(require 'easymenu)
(require 'timezone)
(eval-when-compile (require 'cl))
(defvar gnus-score-mode-hook nil
"*Hook run in score mode buffers.")
(defvar gnus-score-menu-hook nil
"*Hook run after creating the score mode menu.")
(defvar gnus-score-edit-exit-function nil
"Function run on exit from the score buffer.")
(defvar gnus-score-mode-map nil)
(unless gnus-score-mode-map
(setq gnus-score-mode-map (copy-keymap emacs-lisp-mode-map))
(define-key gnus-score-mode-map "\C-c\C-c" 'gnus-score-edit-exit)
(define-key gnus-score-mode-map "\C-c\C-d" 'gnus-score-edit-insert-date)
(define-key gnus-score-mode-map "\C-c\C-p" 'gnus-score-pretty-print))
;;;###autoload
(defun gnus-score-mode ()
"Mode for editing Gnus score files.
This mode is an extended emacs-lisp mode.
\\{gnus-score-mode-map}"
(interactive)
(kill-all-local-variables)
(use-local-map gnus-score-mode-map)
(when menu-bar-mode
(gnus-score-make-menu-bar))
(set-syntax-table emacs-lisp-mode-syntax-table)
(setq major-mode 'gnus-score-mode)
(setq mode-name "Score")
(lisp-mode-variables nil)
(make-local-variable 'gnus-score-edit-exit-function)
(run-hooks 'emacs-lisp-mode-hook 'gnus-score-mode-hook))
(defun gnus-score-make-menu-bar ()
(unless (boundp 'gnus-score-menu)
(easy-menu-define
gnus-score-menu gnus-score-mode-map ""
'("Score"
["Exit" gnus-score-edit-exit t]
["Insert date" gnus-score-edit-insert-date t]
["Format" gnus-score-pretty-print t]))
(run-hooks 'gnus-score-menu-hook)))
(defun gnus-score-edit-insert-date ()
"Insert date in numerical format."
(interactive)
(princ (gnus-score-day-number (current-time)) (current-buffer)))
(defun gnus-score-pretty-print ()
"Format the current score file."
(interactive)
(goto-char (point-min))
(let ((form (read (current-buffer))))
(erase-buffer)
(pp form (current-buffer)))
(goto-char (point-min)))
(defun gnus-score-edit-exit ()
"Stop editing the score file."
(interactive)
(unless (file-exists-p (file-name-directory (buffer-file-name)))
(make-directory (file-name-directory (buffer-file-name)) t))
(save-buffer)
(bury-buffer (current-buffer))
(let ((buf (current-buffer)))
(when gnus-score-edit-exit-function
(funcall gnus-score-edit-exit-function))
(when (eq buf (current-buffer))
(switch-to-buffer (other-buffer (current-buffer))))))
(defun gnus-score-day-number (time)
(let ((dat (decode-time time)))
(timezone-absolute-from-gregorian
(nth 4 dat) (nth 3 dat) (nth 5 dat))))
(provide 'score-mode)
;;; score-mode.el ends here

82
lisp/version.el Normal file
View file

@ -0,0 +1,82 @@
;;; version.el --- record version number of Emacs.
;;; Copyright (C) 1985, 1992, 1994, 1995, 1999 Free Software Foundation, Inc.
;; Maintainer: FSF
;; Keywords: internal
;; 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.
;;; Code:
(defconst emacs-version "20.5.92" "\
Version numbers of this version of Emacs.")
(defconst emacs-major-version
(progn (string-match "^[0-9]+" emacs-version)
(string-to-int (match-string 0 emacs-version)))
"Major version number of this version of Emacs.
This variable first existed in version 19.23.")
(defconst emacs-minor-version
(progn (string-match "^[0-9]+\\.\\([0-9]+\\)" emacs-version)
(string-to-int (match-string 1 emacs-version)))
"Minor version number of this version of Emacs.
This variable first existed in version 19.23.")
(defconst emacs-build-time (current-time) "\
Time at which Emacs was dumped out.")
(defconst emacs-build-system (system-name))
(defun emacs-version (&optional here) "\
Return string describing the version of Emacs that is running.
If optional argument HERE is non-nil, insert string at point.
Don't use this function in programs to choose actions according
to the system configuration; look at `system-configuration' instead."
(interactive "P")
(let ((version-string
(format (if (not (interactive-p))
"GNU Emacs %s (%s%s)\n of %s on %s"
"GNU Emacs %s (%s%s) of %s on %s")
emacs-version
system-configuration
(cond ((featurep 'motif) ", Motif")
((featurep 'x-toolkit) ", X toolkit")
(t ""))
(format-time-string "%a %b %e %Y" emacs-build-time)
emacs-build-system)))
(if here
(insert version-string)
(if (interactive-p)
(message "%s" version-string)
version-string))))
;;; We hope that this alias is easier for people to find.
(defalias 'version 'emacs-version)
;;; We put version info into the executable in the form that ident(1) uses.
(or (memq system-type '(vax-vms windows-nt ms-dos))
(purecopy (concat "\n$Id: " (subst-char-in-string ?\n ? (emacs-version))
" $\n")))
;;Local variables:
;;version-control: never
;;End:
;;; version.el ends here