Add new gnus-sync.el library.
authorTed Zlatanov <tzz@lifelogs.com>
Thu, 12 Aug 2010 19:50:40 +0000 (14:50 -0500)
committerTed Zlatanov <tzz@lifelogs.com>
Thu, 12 Aug 2010 19:50:40 +0000 (14:50 -0500)
* gnus-registry.el (gnus-registry-follow-group-p): Use `gnus-grep-in-list'.

* gnus-util.el (gnus-grep-in-list): Moved from gnus-registry.el and
renamed from `gnus-registry-grep-in-list'.

* gnus-sync.el: New library for synchronization of marks.

lisp/ChangeLog
lisp/gnus-registry.el
lisp/gnus-sync.el [new file with mode: 0644]
lisp/gnus-util.el

index 94e3084..617c4a5 100644 (file)
@@ -1,5 +1,12 @@
 2010-08-12  Teodor Zlatanov  <tzz@lifelogs.com>
 
+       * gnus-sync.el: New library for synchronization of marks.
+
+       * gnus-util.el (gnus-grep-in-list): Moved from gnus-registry.el and
+       renamed from `gnus-registry-grep-in-list'.
+
+       * gnus-registry.el (gnus-registry-follow-group-p): Use `gnus-grep-in-list'.
+
        * gnus-start.el (gnus-start-draft-setup): Make it interactive.
 
 2010-08-06  Katsumi Yamaoka  <yamaoka@jpl.org>
index db10440..e3af088 100644 (file)
@@ -661,10 +661,10 @@ necessary."
   "Determines if a group name should be followed.
 Consults `gnus-registry-unfollowed-groups' and
 `nnmail-split-fancy-with-parent-ignore-groups'."
-  (not (or (gnus-registry-grep-in-list
+  (not (or (gnus-grep-in-list
            group
            gnus-registry-unfollowed-groups)
-          (gnus-registry-grep-in-list
+          (gnus-grep-in-list
            group
            nnmail-split-fancy-with-parent-ignore-groups))))
 
@@ -745,14 +745,6 @@ Consults `gnus-registry-unfollowed-groups' and
                          (assoc article (gnus-data-list nil)))))
     nil))
 
-(defun gnus-registry-grep-in-list (word list)
-"Find if a WORD matches any regular expression in the given LIST."
-  (when (and word list)
-    (catch 'found
-      (dolist (r list)
-       (when (string-match r word)
-         (throw 'found r))))))
-
 (defun gnus-registry-do-marks (type function)
   "For each known mark, call FUNCTION for each cell of type TYPE.
 
diff --git a/lisp/gnus-sync.el b/lisp/gnus-sync.el
new file mode 100644 (file)
index 0000000..1bf0740
--- /dev/null
@@ -0,0 +1,215 @@
+;;; gnus-sync.el --- synchronization facility for Gnus
+
+;;; Copyright (C) 2010
+;;; Free Software Foundation, Inc.
+
+;; Author: Ted Zlatanov <tzz@lifelogs.com>
+;; Keywords: news synchronization nntp nnrss
+
+;; 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 3 of the License, 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.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This is the gnus-sync.el package.
+
+;; Put this in your startup file (~/.gnus.el for instance)
+
+;; (setq gnus-sync-backend `("/remote:/path.gpg") ; will use Tramp+EPA if loaded
+;;       gnus-sync-global-vars `(gnus-newsrc-last-checked-date)
+;;       gnus-sync-newsrc-groups `("nntp" "nnrss")
+;;       gnus-sync-newsrc-vars `(read marks))
+
+;; TODO:
+
+;; - after gnus-sync-read, the message counts are wrong
+
+;;; Code:
+
+(eval-when-compile (require 'cl))
+(require 'gnus-util)
+
+(defgroup gnus-sync nil
+  "The Gnus synchronization facility."
+  :version "23.1"
+  :group 'gnus)
+
+(defcustom gnus-sync-newsrc-groups `("nntp" "nnrss")
+  "List of groups to be synchronized in the gnus-newsrc-alist.
+The group names are matched, they don't have to be fully
+qualified.  Typically you would choose all of these.  That's the
+default because there is no active sync backend by default, so
+this setting is harmless until the user chooses a sync backend."
+  :group 'gnus-sync
+  :type '(repeat regexp))
+
+(defcustom gnus-sync-newsrc-offsets '(2 3)
+  "List of per-group data to be synchronized."
+  :group 'gnus-sync
+  :type '(set (const :tag "Read ranges" 2)
+              (const :tag "Marks" 3)))
+
+(defcustom gnus-sync-global-vars nil
+  "List of global variables to be synchronized.
+You may want to sync `gnus-newsrc-last-checked-date' but pretty
+much any symbol is fair game.  You could additionally sync
+`gnus-newsrc-alist', `gnus-server-alist', `gnus-topic-topology',
+and `gnus-topic-alist' to cover all the variables in
+newsrc.eld (except for `gnus-format-specs' which should not be
+synchronized, I believe).  Also see `gnus-variable-list'."
+  :group 'gnus-sync
+  :type '(repeat (choice (variable :tag "A known variable")
+                         (symbol :tag "Any symbol"))))
+
+(defcustom gnus-sync-backend nil
+  "The synchronization backend."
+  :group 'gnus-sync
+  :type '(radio (const :format "None" nil)
+                (string :tag "Sync to a file")))
+
+(defvar gnus-sync-newsrc-loader nil
+  "Carrier for newsrc data")
+
+(defun gnus-sync-save ()
+"Save the Gnus sync data to the backend."
+  (interactive)
+  (gnus-message 6 "Saving the Gnus sync data")
+  (cond
+   ((stringp gnus-sync-backend)
+    (gnus-message 7 "gnus-sync: saving to backend %s" gnus-sync-backend)
+    ;; populate gnus-sync-newsrc-loader from all but the first dummy
+    ;; entry in gnus-newsrc-alist whose group matches any of the
+    ;; gnus-sync-newsrc-groups
+    (let ((gnus-sync-newsrc-loader
+           (loop for entry in (cdr gnus-newsrc-alist)
+                 when (gnus-grep-in-list
+                       (car entry)     ;the group name
+                       gnus-sync-newsrc-groups)
+                 collect (cons (car entry)
+                               (mapcar (lambda (offset)
+                                         (cons offset (nth offset entry)))
+                                       gnus-sync-newsrc-offsets)))))
+
+      (with-temp-file gnus-sync-backend
+        (progn
+          (let ((coding-system-for-write gnus-ding-file-coding-system)
+                (standard-output (current-buffer)))
+            (princ (format ";; -*- mode:emacs-lisp; coding: %s; -*-\n"
+                           gnus-ding-file-coding-system))
+            (princ ";; Gnus sync data v. 0.0.1\n")
+            (let* ((print-quoted t)
+                   (print-readably t)
+                   (print-escape-multibyte nil)
+                   (print-escape-nonascii t)
+                   (print-length nil)
+                   (print-level nil)
+                   (print-circle nil)
+                   (print-escape-newlines t)
+                   (variables (cons 'gnus-sync-newsrc-loader
+                                    gnus-sync-global-vars)))
+              (while variables
+                (when (and (boundp (setq variable (pop variables)))
+                           (symbol-value variable))
+                  (princ "\n(setq ")
+                  (princ (symbol-name variable))
+                  (princ " '")
+                  (prin1 (symbol-value variable))
+                  (princ ")\n"))))
+            (gnus-message
+             7
+             "gnus-sync: stored variables %s and %d groups in %s"
+             gnus-sync-global-vars
+             (length gnus-sync-newsrc-loader)
+             gnus-sync-backend)
+
+            ;; Idea from Dan Christensen <jdc@chow.mat.jhu.edu>
+            ;; Save the .eld file with extra line breaks.
+            (gnus-message 8 "gnus-sync: adding whitespace to %s"
+                          gnus-sync-backend)
+            (save-excursion
+              (goto-char (point-min))
+              (while (re-search-forward "^(\\|(\\\"" nil t)
+                (replace-match "\n\\&" t))
+              (goto-char (point-min))
+              (while (re-search-forward " $" nil t)
+                (replace-match "" t t))))))))
+    ;; the pass-through case: gnus-sync-backend is not a known choice
+    (nil)))
+
+(defun gnus-sync-read ()
+"Load the Gnus sync data from the backend."
+  (interactive)
+  (when gnus-sync-backend
+    (gnus-message 7 "gnus-sync: loading from backend %s" gnus-sync-backend)
+    (cond ((stringp gnus-sync-backend)
+           ;; read data here...
+           (if (or debug-on-error debug-on-quit)
+               (load gnus-sync-backend nil t)
+             (condition-case var
+                 (load gnus-sync-backend nil t)
+               (error
+                (error "Error in %s: %s" gnus-sync-backend (cadr var)))))
+           (let ((valid-nodes
+                  (loop for node in gnus-sync-newsrc-loader
+                        if (gnus-gethash (car node) gnus-newsrc-hashtb)
+                        collect node)))
+             (dolist (node valid-nodes)
+               (loop for store in (cdr node)
+                     do (setf (nth (car store)
+                                   (assoc (car node) gnus-newsrc-alist))
+                              (cdr store))))
+            (gnus-message
+             7
+             "gnus-sync: loaded %d groups (out of %d) from %s"
+             (length valid-nodes)
+             (length gnus-sync-newsrc-loader)
+             gnus-sync-backend)
+           (setq gnus-sync-newsrc-loader nil)))
+          (nil))
+    ;; make the hashtable again because the newsrc-alist may have been modified
+    (when gnus-sync-newsrc-vars
+      (gnus-message 9 "gnus-sync: remaking the newsrc hashtable")
+      (gnus-make-hashtable-from-newsrc-alist))))
+
+;;;###autoload
+(defun gnus-sync-initialize ()
+"Initialize the Gnus sync facility."
+  (interactive)
+  (gnus-message 5 "Initializing the sync facility")
+  (gnus-sync-install-hooks))
+
+;;;###autoload
+(defun gnus-sync-install-hooks ()
+  "Install the sync hooks."
+  (interactive)
+  (add-hook 'gnus-get-new-news-hook 'gnus-sync-read)
+  (add-hook 'gnus-save-newsrc-hook 'gnus-sync-save)
+  (add-hook 'gnus-read-newsrc-el-hook 'gnus-sync-read))
+
+(defun gnus-sync-unload-hook ()
+  "Uninstall the sync hooks."
+  (interactive)
+  (remove-hook 'gnus-get-new-news-hook 'gnus-sync-read)
+  (remove-hook 'gnus-save-newsrc-hook 'gnus-sync-save)
+  (remove-hook 'gnus-read-newsrc-el-hook 'gnus-sync-read))
+
+(add-hook 'gnus-sync-unload-hook 'gnus-sync-unload-hook)
+
+;; this is harmless by default, until the gnus-sync-backend is set
+(gnus-sync-initialize)
+
+(provide 'gnus-sync)
+
+;;; gnus-sync.el ends here
index b8a1c26..93cc1f0 100644 (file)
@@ -1297,6 +1297,14 @@ Return the modified alist."
        (setq alist (delq entry alist)))
       alist)))
 
+(defun gnus-grep-in-list (word list)
+  "Find if a WORD matches any regular expression in the given LIST."
+  (when (and word list)
+    (catch 'found
+      (dolist (r list)
+       (when (string-match r word)
+         (throw 'found r))))))
+
 (defmacro gnus-pull (key alist &optional assoc-p)
   "Modify ALIST to be without KEY."
   (unless (symbolp alist)