--- /dev/null
+;;; gnus-kill-to-score.el --- translate simple kill files to score files
+;; Copyright (C) 1995 Free Software Foundation, Inc.
+
+;; Author: Ethan Bradford <ethanb@phys.washington.edu>
+;; Keywords: news
+
+;; This file is not 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, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+;;; Commentary:
+
+;;; If you don't like the changes which were made, edit out the new code from
+;;; the SCORE file and revert the kill file from the backup (.KILL~).
+
+;;; Caveats:
+;;; -> Sometimes commands in a kill file work together. For example, killing
+;;; the negative of a pattern used to be done by killing all, then
+;;; unkilling. If the unkill fails to translate (which is likely), the
+;;; configuration will be invalid, with the kill translated to a
+;;; score entry and the unkill left as a kill.
+;;; -> The score entries are always applied to all entries in a file, unlike
+;;; gnus-kill, which only applies to marked entries if the fourth argument
+;;; is t.
+;;; -> If the kill file did anything funny with marks, it will be translated
+;;; wrong.
+;;; -> Doesn't delete comments, so won't delete file w/ only comments.
+
+;;; Code:
+
+(require 'gnus)
+(require 'gnus-score)
+(load-library "gnus-kill")
+
+(defvar gnus-convert-loads nil
+ "If t, kill-file loads are converted to score-file loads.
+If nil, we ask whether to convert. Otherwise we don't load or ask.")
+
+(defun gnus-convert-kill-name-to-score-name (kill-file)
+ (concat
+ (if (string-equal (file-name-nondirectory kill-file) "KILL")
+ (concat (file-name-directory kill-file) "all")
+ (substring kill-file 0 (string-match ".KILL$" kill-file)))
+ ".SCORE"))
+
+(defun gnus-convert-one-kill-file (kill-file)
+ "Convert (as far as possible) the elements of KILL-FILE into a score file.
+See also the variable gnus-convert-loads."
+ (interactive "f")
+ (let* ((mark-below (or gnus-summary-mark-below gnus-summary-default-score 0))
+ (expunge-below gnus-summary-expunge-below)
+ (score-file-name (gnus-convert-kill-name-to-score-name kill-file))
+ beg form command recognized)
+ (message "Converting kill file %s..." kill-file)
+ (gnus-score-load score-file-name)
+ (find-file kill-file)
+ (goto-char (point-min))
+ (gnus-kill-file-mode)
+ (while (progn
+ (setq beg (point))
+ (setq recognized nil)
+ (setq form (condition-case nil
+ (read (current-buffer))
+ (error nil))))
+ (setq command (car form))
+
+ (if (eq command 'load)
+ (let ((loaded-kill-file-name
+ (condition-case nil
+ (expand-file-name
+ (gnus-convert-kill-name-to-score-name
+ (eval (nth 1 form))))
+ (error nil))))
+ (if (stringp loaded-kill-file-name)
+ (progn
+ (if (string-match
+ (expand-file-name
+ (or (file-name-directory gnus-kill-files-directory)
+ "~/News/"))
+ loaded-kill-file-name)
+ (setq loaded-kill-file-name
+ (substring loaded-kill-file-name (match-end 0))))
+ (if (or (eq gnus-convert-loads t)
+ (and (not gnus-convert-loads)
+ (message
+ "Convert kill-file load to score-file load for %s (y, n, a=always, v=never)? " loaded-kill-file-name)
+ (let ((c (upcase (read-char-exclusive))))
+ (if (= c ?A)
+ (setq gnus-convert-loads t)
+ (if (= c ?V)
+ (setq gnus-convert-loads 'never)))
+ (or (= c ?A) (= c ?Y) (= c ?\ )))))
+ (progn
+ (gnus-score-set 'files (list loaded-kill-file-name))
+ (setq recognized t))))))
+
+ ;; The only other thing we understand is some form of gnus-kill
+ ;; Check all the fields because they influence whether we recognize.
+ (let
+ ((header (condition-case nil (eval (nth 1 form)) (error nil)))
+ (match (condition-case nil (eval (nth 2 form)) (error nil)))
+ (cmd (nth 3 form))
+ (all (condition-case nil (eval (nth 4 form)) (error nil)))
+ (date nil)
+ (score nil)) ;score also indicates if a cmd was
+ ;recognized.
+ (if (and (listp cmd) (or (eq (car cmd) 'quote)
+ (eq (car cmd) 'function)))
+ (setq cmd (nth 1 cmd)))
+ (if (and (listp cmd) (eq (car cmd) 'lambda))
+ (setq cmd (nth 2 cmd)))
+ (if (and (listp cmd) (eq (length cmd) 1))
+ (setq cmd (car cmd)))
+ (cond
+ ((eq command 'gnus-kill)
+ (cond
+ ((not cmd) ;; Simple kill
+ (setq score (- gnus-score-interactive-default-score)))
+
+ ((and (eq cmd 'gnus-summary-unkill) all) ;; An unkill
+ (setq score gnus-score-interactive-default-score))
+
+ ((not (listp cmd))) ; Only cmds w/ args from here on.
+
+ ((and (eq (car cmd) 'gnus-summary-mark-as-read) ;mod of standard
+ (not (nth 1 cmd)))
+ (if (eqs (nth 2 cmd) " ")
+ (if all
+ (setq score gnus-score-interactive-default-score))
+ (setq score (- gnus-score-interactive-default-score))))
+
+ ((apply (lambda (c) ; Matching the unkill in the FAQ
+ (and (listp c)
+ (eq (car c) 'gnus-summary-clear-mark-forward)
+ (= (nth 1 c) 1)))
+ (list (if (eq (car cmd) 'if) (nth 2 cmd) cmd)))
+ (setq score gnus-score-interactive-default-score))
+
+ ((and ;; Old (ding) gnus kill form.
+ (= (length cmd) 2)
+ (eq (car cmd) 'gnus-summary-raise-score))
+ (setq score (nth 1 cmd)))
+ ))
+ ((eq command 'gnus-raise)
+ (setq score (nth 2 form)))
+ ((eq command 'gnus-lower)
+ (setq score (- (nth 2 form))))
+ ((eq command 'expire-kill)
+ (if (= (length form) 3)
+ (progn
+ (setq date (nth 2 form))
+ (setq score (- gnus-score-interactive-default-score))))))
+ (if (and score (stringp header) (stringp match))
+ (progn
+ (gnus-summary-score-entry
+ header match 'r score date nil t)
+ (setq recognized t)))))
+ (if recognized
+ (delete-region beg (point))
+ (message "Cannot convert this form:") (sit-for 0 500)
+ (print form) (sit-for 0 500)))
+
+ ;; Eliminate white space and delete the file if it is empty, else save.
+ (goto-char (point-min))
+ (delete-region (point)
+ (progn
+ (if (re-search-forward "[^ \t\n]" nil 'end)
+ (backward-char 1))
+ (point)))
+ (and (buffer-modified-p) (save-buffer))
+ (if (= (point-min) (point-max))
+ (progn
+ (message "Deleting %s; it is now empty." kill-file)
+ (delete-file kill-file))
+ (message "%s was not completed converted." kill-file))
+
+ (gnus-score-save)
+ (kill-buffer (current-buffer))))
+
+(defun gnus-convert-kill-file-directory (kill-directory)
+ "Convert kill files in KILL-DIRECTORY into score files.
+Uses gnus-convert-one-kill-file.
+See also the variable gnus-convert-loads."
+ (interactive "DDirectory to convert (empty string = current kill directory): ")
+ (if (string= kill-directory "")
+ (setq kill-directory (or gnus-kill-files-directory "~/News")))
+ (let ((all-kill-files (directory-files kill-directory)))
+ (while all-kill-files
+ (if (string-match "\\(.\\|^\\)KILL$" (car all-kill-files))
+ (gnus-convert-one-kill-file
+ (expand-file-name (car all-kill-files) kill-directory)))
+ (setq all-kill-files (cdr all-kill-files)))))