*** empty log message ***
[gnus] / lisp / gnus.el
index 45514a1..b80cb48 100644 (file)
 
 (eval '(run-hooks 'gnus-load-hook))
 
-(require 'mail-utils)
-(require 'timezone)
-(require 'nnheader)
-(require 'message)
-
-(eval-when-compile (require 'cl))
-
-(defvar gnus-directory (or (getenv "SAVEDIR") "~/News/")
-  "*Directory variable from which all other Gnus file variables are derived.")
-
-;; Site dependent variables.  These variables should be defined in
-;; paths.el.
-
-(defvar gnus-default-nntp-server nil
-  "Specify a default NNTP server.
-This variable should be defined in paths.el, and should never be set
-by the user.
-If you want to change servers, you should use `gnus-select-method'.
-See the documentation to that variable.")
-
-(defvar gnus-backup-default-subscribed-newsgroups
-  '("news.announce.newusers" "news.groups.questions" "gnu.emacs.gnus")
-  "Default default new newsgroups the first time Gnus is run.
-Should be set in paths.el, and shouldn't be touched by the user.")
+(defconst gnus-version-number "0.15"
+  "Version number for this version of Gnus.")
 
-(defvar gnus-local-domain nil
-  "Local domain name without a host name.
-The DOMAINNAME environment variable is used instead if it is defined.
-If the `system-name' function returns the full Internet name, there is
-no need to set this variable.")
+(defconst gnus-version (format "Red Gnus v%s" gnus-version-number)
+  "Version string for this version of Gnus.")
 
-(defvar gnus-local-organization nil
-  "String with a description of what organization (if any) the user belongs to.
-The ORGANIZATION environment variable is used instead if it is defined.
-If this variable contains a function, this function will be called
-with the current newsgroup name as the argument.  The function should
-return a string.
+(defvar gnus-inhibit-startup-message nil
+  "*If non-nil, the startup message will not be displayed.")
 
-In any case, if the string (either in the variable, in the environment
-variable, or returned by the function) is a file name, the contents of
-this file will be used as the organization.")
+;;; Internal variables
 
-;; Customization variables
+(defvar gnus-group-buffer "*Group*")
 
-;; Don't touch this variable.
-(defvar gnus-nntp-service "nntp"
-  "*NNTP service name (\"nntp\" or 119).
-This is an obsolete variable, which is scarcely used.  If you use an
-nntp server for your newsgroup and want to change the port number
-used to 899, you would say something along these lines:
+;;; Splash screen.
 
- (setq gnus-select-method '(nntp \"my.nntp.server\" (nntp-port-number 899)))")
+(defun gnus-splash ()
+  (save-excursion
+    (switch-to-buffer gnus-group-buffer)
+    (let ((buffer-read-only nil))
+      (erase-buffer)
+      (unless gnus-inhibit-startup-message
+       (gnus-group-startup-message)
+       (sit-for 0)))))
 
-(defvar gnus-nntpserver-file "/etc/nntpserver"
-  "*A file with only the name of the nntp server in it.")
+(defun gnus-indent-rigidly (start end arg)
+  "Indent rigidly using only spaces and no tabs."
+  (save-excursion
+    (save-restriction
+      (narrow-to-region start end)
+      (indent-rigidly start end arg)
+      ;; We translate tabs into spaces -- not everybody uses
+      ;; an 8-character tab.
+      (goto-char (point-min))
+      (while (search-forward "\t" nil t)
+       (replace-match "        " t t)))))
 
-;; This function is used to check both the environment variable
-;; NNTPSERVER and the /etc/nntpserver file to see whether one can find
-;; an nntp server name default.
-(defun gnus-getenv-nntpserver ()
-  (or (getenv "NNTPSERVER")
-      (and (file-readable-p gnus-nntpserver-file)
-          (save-excursion
-            (set-buffer (get-buffer-create " *gnus nntp*"))
-            (buffer-disable-undo (current-buffer))
-            (insert-file-contents gnus-nntpserver-file)
-            (let ((name (buffer-string)))
-              (prog1
-                  (if (string-match "^[ \t\n]*$" name)
-                      nil
-                    name)
-                (kill-buffer (current-buffer))))))))
+(defun gnus-group-startup-message (&optional x y)
+  "Insert startup message in current buffer."
+  ;; Insert the message.
+  (erase-buffer)
+  (insert
+   (format "              %s
+          _    ___ _             _
+          _ ___ __ ___  __    _ ___
+          __   _     ___    __  ___
+              _           ___     _
+             _  _ __             _
+             ___   __            _
+                   __           _
+                    _      _   _
+                   _      _    _
+                      _  _    _
+                  __  ___
+                 _   _ _     _
+                _   _
+              _    _
+             _    _
+            _
+          __
 
-(defvar gnus-select-method
-  (nconc
-   (list 'nntp (or (condition-case ()
-                      (gnus-getenv-nntpserver)
-                    (error nil))
-                  (if (and gnus-default-nntp-server
-                           (not (string= gnus-default-nntp-server "")))
-                      gnus-default-nntp-server)
-                  (system-name)))
-   (if (or (null gnus-nntp-service)
-          (equal gnus-nntp-service "nntp"))
-       nil
-     (list gnus-nntp-service)))
-  "*Default method for selecting a newsgroup.
-This variable should be a list, where the first element is how the
-news is to be fetched, the second is the address.
+"
+           ""))
+  ;; And then hack it.
+  (gnus-indent-rigidly (point-min) (point-max)
+                      (/ (max (- (window-width) (or x 46)) 0) 2))
+  (goto-char (point-min))
+  (forward-line 1)
+  (let* ((pheight (count-lines (point-min) (point-max)))
+        (wheight (window-height))
+        (rest (- wheight pheight)))
+    (insert (make-string (max 0 (* 2 (/ rest 3))) ?\n)))
+  ;; Fontify some.
+  (goto-char (point-min))
+  (and (search-forward "Praxis" nil t)
+       (put-text-property (match-beginning 0) (match-end 0) 'face 'bold))
+  (goto-char (point-min))
+  (setq mode-line-buffer-identification gnus-version)
+  (set-buffer-modified-p t))
 
-For instance, if you want to get your news via NNTP from
-\"flab.flab.edu\", you could say:
+(eval-when (load)
+  (gnus-splash))
 
-(setq gnus-select-method '(nntp \"flab.flab.edu\"))
+;;; Do the rest.
 
-If you want to use your local spool, say:
+(require 'gnus-load)
 
-(setq gnus-select-method (list 'nnspool (system-name)))
+\f
 
-If you use this variable, you must set `gnus-nntp-server' to nil.
+;; Fix by Hallvard B Furuseth <h.b.furuseth@usit.uio.no>.
+;; If you want the cursor to go somewhere else, set these two
+;; functions in some startup hook to whatever you want.
+(defalias 'gnus-summary-position-point 'gnus-goto-colon)
+(defalias 'gnus-group-position-point 'gnus-goto-colon)
 
-There is a lot more to know about select methods and virtual servers -
-see the manual for details.")
+;;; Various macros and substs.
 
-(defvar gnus-message-archive-method 
-  `(nnfolder
-    "archive"
-    (nnfolder-directory ,(nnheader-concat message-directory "archive"))
-    (nnfolder-active-file 
-     ,(nnheader-concat message-directory "archive/active"))
-    (nnfolder-get-new-mail nil)
-    (nnfolder-inhibit-expiry t))
-  "*Method used for archiving messages you've sent.
-This should be a mail method.")
+(defun gnus-header-from (header)
+  (mail-header-from header))
 
-(defvar gnus-refer-article-method nil
-  "*Preferred method for fetching an article by Message-ID.
-If you are reading news from the local spool (with nnspool), fetching
-articles by Message-ID is painfully slow.  By setting this method to an
-nntp method, you might get acceptable results.
+(defmacro gnus-gethash (string hashtable)
+  "Get hash value of STRING in HASHTABLE."
+  `(symbol-value (intern-soft ,string ,hashtable)))
 
-The value of this variable must be a valid select method as discussed
-in the documentation of `gnus-select-method'.")
+(defmacro gnus-sethash (string value hashtable)
+  "Set hash value.  Arguments are STRING, VALUE, and HASHTABLE."
+  `(set (intern ,string ,hashtable) ,value))
+(put 'nnheader-temp-write 'edebug-form-spec '(form form form))
 
-(defvar gnus-secondary-select-methods nil
-  "*A list of secondary methods that will be used for reading news.
-This is a list where each element is a complete select method (see
-`gnus-select-method').
+(defmacro gnus-group-unread (group)
+  "Get the currently computed number of unread articles in GROUP."
+  `(car (gnus-gethash ,group gnus-newsrc-hashtb)))
 
-If, for instance, you want to read your mail with the nnml backend,
-you could set this variable:
+(defmacro gnus-group-entry (group)
+  "Get the newsrc entry for GROUP."
+  `(gnus-gethash ,group gnus-newsrc-hashtb))
 
-(setq gnus-secondary-select-methods '((nnml \"\")))")
+(defmacro gnus-active (group)
+  "Get active info on GROUP."
+  `(gnus-gethash ,group gnus-active-hashtb))
 
-(defvar gnus-secondary-servers nil
-  "*List of NNTP servers that the user can choose between interactively.
-To make Gnus query you for a server, you have to give `gnus' a
-non-numeric prefix - `C-u M-x gnus', in short.")
+(defmacro gnus-set-active (group active)
+  "Set GROUP's active info."
+  `(gnus-sethash ,group ,active gnus-active-hashtb))
 
-(defvar gnus-nntp-server nil
-  "*The name of the host running the NNTP server.
-This variable is semi-obsolete.         Use the `gnus-select-method'
-variable instead.")
+(defun gnus-alive-p ()
+  "Say whether Gnus is running or not."
+  (and gnus-group-buffer
+       (get-buffer gnus-group-buffer)
+       (save-excursion
+        (set-buffer gnus-group-buffer)
+        (eq major-mode 'gnus-group-mode))))
 
-(defvar gnus-startup-file "~/.newsrc"
-  "*Your `.newsrc' file.
-`.newsrc-SERVER' will be used instead if that exists.")
+;; Info access macros.
 
-(defvar gnus-init-file "~/.gnus"
-  "*Your Gnus elisp startup file.
-If a file with the .el or .elc suffixes exist, it will be read
-instead.")
+(defmacro gnus-info-group (info)
+  `(nth 0 ,info))
+(defmacro gnus-info-rank (info)
+  `(nth 1 ,info))
+(defmacro gnus-info-read (info)
+  `(nth 2 ,info))
+(defmacro gnus-info-marks (info)
+  `(nth 3 ,info))
+(defmacro gnus-info-method (info)
+  `(nth 4 ,info))
+(defmacro gnus-info-params (info)
+  `(nth 5 ,info))
 
-(defvar gnus-group-faq-directory
-  '("/ftp@mirrors.aol.com:/pub/rtfm/usenet/"
-    "/ftp@sunsite.auc.dk:/pub/usenet/"
-    "/ftp@src.doc.ic.ac.uk:/usenet/news-FAQS/"
-    "/ftp@ftp.seas.gwu.edu:/pub/rtfm/"
-    "/ftp@rtfm.mit.edu:/pub/usenet/"
-    "/ftp@ftp.uni-paderborn.de:/pub/FAQ/"
-    "/ftp@ftp.sunet.se:/pub/usenet/"
-    "/ftp@nctuccca.edu.tw:/USENET/FAQ/"
-    "/ftp@hwarang.postech.ac.kr:/pub/usenet/"
-    "/ftp@ftp.hk.super.net:/mirror/faqs/")
-  "*Directory where the group FAQs are stored.
-This will most commonly be on a remote machine, and the file will be
-fetched by ange-ftp.
+(defmacro gnus-info-level (info)
+  `(let ((rank (gnus-info-rank ,info)))
+     (if (consp rank)
+        (car rank)
+       rank)))
+(defmacro gnus-info-score (info)
+  `(let ((rank (gnus-info-rank ,info)))
+     (or (and (consp rank) (cdr rank)) 0)))
 
-This variable can also be a list of directories.  In that case, the
-first element in the list will be used by default, and the others will
-be used as backup sites.
+(defmacro gnus-info-set-group (info group)
+  `(setcar ,info ,group))
+(defmacro gnus-info-set-rank (info rank)
+  `(setcar (nthcdr 1 ,info) ,rank))
+(defmacro gnus-info-set-read (info read)
+  `(setcar (nthcdr 2 ,info) ,read))
+(defmacro gnus-info-set-marks (info marks &optional extend)
+  (if extend
+      `(gnus-info-set-entry ,info ,marks 3)
+    `(setcar (nthcdr 3 ,info) ,marks)))
+(defmacro gnus-info-set-method (info method &optional extend)
+  (if extend
+      `(gnus-info-set-entry ,info ,method 4)
+    `(setcar (nthcdr 4 ,info) ,method)))
+(defmacro gnus-info-set-params (info params &optional extend)
+  (if extend
+      `(gnus-info-set-entry ,info ,params 5)
+    `(setcar (nthcdr 5 ,info) ,params)))
+
+(defun gnus-info-set-entry (info entry number)
+  ;; Extend the info until we have enough elements.
+  (while (< (length info) number)
+    (nconc info (list nil)))
+  ;; Set the entry.
+  (setcar (nthcdr number info) entry))
 
-Note that Gnus uses an aol machine as the default directory.  If this
-feels fundamentally unclean, just think of it as a way to finally get
-something of value back from them.
+(defmacro gnus-info-set-level (info level)
+  `(let ((rank (cdr ,info)))
+     (if (consp (car rank))
+        (setcar (car rank) ,level)
+       (setcar rank ,level))))
+(defmacro gnus-info-set-score (info score)
+  `(let ((rank (cdr ,info)))
+     (if (consp (car rank))
+        (setcdr (car rank) ,score)
+       (setcar rank (cons (car rank) ,score)))))
 
-If the default site is too slow, try one of these:
+(defmacro gnus-get-info (group)
+  `(nth 2 (gnus-gethash ,group gnus-newsrc-hashtb)))
 
-   North America: mirrors.aol.com               /pub/rtfm/usenet
-                 ftp.seas.gwu.edu               /pub/rtfm
-                 rtfm.mit.edu                   /pub/usenet
-   Europe:       ftp.uni-paderborn.de           /pub/FAQ
-                  src.doc.ic.ac.uk               /usenet/news-FAQS
-                 ftp.sunet.se                   /pub/usenet
-                 sunsite.auc.dk                 /pub/usenet
-   Asia:         nctuccca.edu.tw                /USENET/FAQ
-                 hwarang.postech.ac.kr          /pub/usenet
-                 ftp.hk.super.net               /mirror/faqs")
+;; Byte-compiler warning.
+(defvar gnus-visual)
+;; Find out whether the gnus-visual TYPE is wanted.
+(defun gnus-visual-p (&optional type class)
+  (and gnus-visual                     ; Has to be non-nil, at least.
+       (if (not type)                  ; We don't care about type.
+          gnus-visual
+        (if (listp gnus-visual)        ; It's a list, so we check it.
+            (or (memq type gnus-visual)
+                (memq class gnus-visual))
+          t))))
 
-(defvar gnus-group-archive-directory
-  "/ftp@ftp.hpc.uh.edu:/pub/emacs/ding-list/"
-  "*The address of the (ding) archives.")
+;;; Load the compatability functions.
 
-(defvar gnus-group-recent-archive-directory
-  "/ftp@ftp.hpc.uh.edu:/pub/emacs/ding-list-recent/"
-  "*The address of the most recent (ding) articles.")
+(require 'gnus-cus)
+(require 'gnus-ems)
 
-(defvar gnus-default-subscribed-newsgroups nil
-  "*This variable lists what newsgroups should be subscribed the first time Gnus is used.
-It should be a list of strings.
-If it is `t', Gnus will not do anything special the first time it is
-started; it'll just use the normal newsgroups subscription methods.")
+\f
+;;;
+;;; Shutdown
+;;;
 
-(defvar gnus-use-cross-reference t
-  "*Non-nil means that cross referenced articles will be marked as read.
-If nil, ignore cross references.  If t, mark articles as read in
-subscribed newsgroups. If neither t nor nil, mark as read in all
-newsgroups.")
+(defvar gnus-shutdown-alist nil)
 
-(defvar gnus-single-article-buffer t
-  "*If non-nil, display all articles in the same buffer.
-If nil, each group will get its own article buffer.")
+(defun gnus-add-shutdown (function &rest symbols)
+  "Run FUNCTION whenever one of SYMBOLS is shut down."
+  (push (cons function symbols) gnus-shutdown-alist))
 
-(defvar gnus-use-dribble-file t
-  "*Non-nil means that Gnus will use a dribble file to store user updates.
-If Emacs should crash without saving the .newsrc files, complete
-information can be restored from the dribble file.")
+(defun gnus-shutdown (symbol)
+  "Shut down everything that waits for SYMBOL."
+  (let ((alist gnus-shutdown-alist)
+       entry)
+    (while (setq entry (pop alist))
+      (when (memq symbol (cdr entry))
+       (funcall (car entry))))))
 
-(defvar gnus-dribble-directory nil
-  "*The directory where dribble files will be saved.
-If this variable is nil, the directory where the .newsrc files are
-saved will be used.")
+\f
+;;;
+;;; Gnus Utility Functions
+;;;
 
-(defvar gnus-asynchronous nil
-  "*If non-nil, Gnus will supply backends with data needed for async article fetching.")
+;; Add the current buffer to the list of buffers to be killed on exit.
+(defun gnus-add-current-to-buffer-list ()
+  (or (memq (current-buffer) gnus-buffer-list)
+      (setq gnus-buffer-list (cons (current-buffer) gnus-buffer-list))))
 
-(defvar gnus-kill-summary-on-exit t
-  "*If non-nil, kill the summary buffer when you exit from it.
-If nil, the summary will become a \"*Dead Summary*\" buffer, and
-it will be killed sometime later.")
+(defun gnus-version (&optional arg)
+  "Version number of this version of Gnus.
+If ARG, insert string at point."
+  (interactive "P")
+  (let ((methods gnus-valid-select-methods)
+       (mess gnus-version)
+       meth)
+    ;; Go through all the legal select methods and add their version
+    ;; numbers to the total version string.  Only the backends that are
+    ;; currently in use will have their message numbers taken into
+    ;; consideration.
+    (while methods
+      (setq meth (intern (concat (caar methods) "-version")))
+      (and (boundp meth)
+          (stringp (symbol-value meth))
+          (setq mess (concat mess "; " (symbol-value meth))))
+      (setq methods (cdr methods)))
+    (if arg
+       (insert (message mess))
+      (message mess))))
 
-(defvar gnus-large-newsgroup 200
-  "*The number of articles which indicates a large newsgroup.
-If the number of articles in a newsgroup is greater than this value,
-confirmation is required for selecting the newsgroup.")
+(defun gnus-continuum-version (version)
+  "Return VERSION as a floating point number."
+  (when (or (string-match "^\\([^ ]+\\)? ?Gnus v?\\([0-9.]+\\)$" version)
+           (string-match "^\\(.?\\)gnus-\\([0-9.]+\\)$" version))
+    (let* ((alpha (and (match-beginning 1) (match-string 1 version)))
+          (number (match-string 2 version))
+          major minor least)
+      (string-match "\\([0-9]\\)\\.\\([0-9]+\\)\\.?\\([0-9]+\\)?" number)
+      (setq major (string-to-number (match-string 1 number)))
+      (setq minor (string-to-number (match-string 2 number)))
+      (setq least (if (match-beginning 3)
+                     (string-to-number (match-string 3 number))
+                   0))
+      (string-to-number
+       (if (zerop major)
+          (format "%s00%02d%02d"
+                  (cond 
+                   ((member alpha '("(ding)" "d")) "4.99")
+                   ((member alpha '("September" "s")) "5.01")
+                   ((member alpha '("Red" "r")) "5.03"))
+                  minor least)
+        (format "%d.%02d%02d" major minor least))))))
 
-;; Suggested by Andrew Eskilsson <pi92ae@lelle.pt.hk-r.se>.
-(defvar gnus-no-groups-message "No news is horrible news"
-  "*Message displayed by Gnus when no groups are available.")
+(defun gnus-info-find-node ()
+  "Find Info documentation of Gnus."
+  (interactive)
+  ;; Enlarge info window if needed.
+  (let (gnus-info-buffer)
+    (Info-goto-node (cadr (assq major-mode gnus-info-nodes)))
+    (setq gnus-info-buffer (current-buffer))
+    (gnus-configure-windows 'info)))
 
-(defvar gnus-use-long-file-name (not (memq system-type '(usg-unix-v xenix)))
-  "*Non-nil means that the default name of a file to save articles in is the group name.
-If it's nil, the directory form of the group name is used instead.
+;;; More various functions.
 
-If this variable is a list, and the list contains the element
-`not-score', long file names will not be used for score files; if it
-contains the element `not-save', long file names will not be used for
-saving; and if it contains the element `not-kill', long file names
-will not be used for kill files.")
+(defun gnus-group-read-only-p (&optional group)
+  "Check whether GROUP supports editing or not.
+If GROUP is nil, `gnus-newsgroup-name' will be checked instead.         Note
+that that variable is buffer-local to the summary buffers."
+  (let ((group (or group gnus-newsgroup-name)))
+    (not (gnus-check-backend-function 'request-replace-article group))))
 
-(defvar gnus-article-save-directory gnus-directory
-  "*Name of the directory articles will be saved in (default \"~/News\").")
+(defun gnus-group-total-expirable-p (group)
+  "Check whether GROUP is total-expirable or not."
+  (let ((params (gnus-group-find-parameter group)))
+    (or (memq 'total-expire params)
+       (cdr (assq 'total-expire params)) ; (total-expire . t)
+       (and gnus-total-expirable-newsgroups ; Check var.
+            (string-match gnus-total-expirable-newsgroups group)))))
 
-(defvar gnus-kill-files-directory gnus-directory
-  "*Name of the directory where kill files will be stored (default \"~/News\").")
+(defun gnus-group-auto-expirable-p (group)
+  "Check whether GROUP is total-expirable or not."
+  (let ((params (gnus-group-find-parameter group)))
+    (or (memq 'auto-expire params)
+       (cdr (assq 'auto-expire params)) ; (auto-expire . t)
+       (and gnus-auto-expirable-newsgroups ; Check var.
+            (string-match gnus-auto-expirable-newsgroups group)))))
 
-(defvar gnus-default-article-saver 'gnus-summary-save-in-rmail
-  "*A function to save articles in your favorite format.
-The function must be interactively callable (in other words, it must
-be an Emacs command).
+(defun gnus-virtual-group-p (group)
+  "Say whether GROUP is virtual or not."
+  (memq 'virtual (assoc (symbol-name (car (gnus-find-method-for-group group)))
+                       gnus-valid-select-methods)))
 
-Gnus provides the following functions:
+(defun gnus-news-group-p (group &optional article)
+  "Return non-nil if GROUP (and ARTICLE) come from a news server."
+  (or (gnus-member-of-valid 'post group) ; Ordinary news group.
+      (and (gnus-member-of-valid 'post-mail group) ; Combined group.
+          (eq (gnus-request-type group article) 'news))))
 
-* gnus-summary-save-in-rmail (Rmail format)
-* gnus-summary-save-in-mail (Unix mail format)
-* gnus-summary-save-in-folder (MH folder)
-* gnus-summary-save-in-file (article format).
-* gnus-summary-save-in-vm (use VM's folder format).")
+;; Returns a list of writable groups.
+(defun gnus-writable-groups ()
+  (let ((alist gnus-newsrc-alist)
+       groups group)
+    (while (setq group (car (pop alist)))
+      (unless (gnus-group-read-only-p group)
+       (push group groups)))
+    (nreverse groups)))
 
-(defvar gnus-prompt-before-saving 'always
-  "*This variable says how much prompting is to be done when saving articles.
-If it is nil, no prompting will be done, and the articles will be
-saved to the default files.  If this variable is `always', each and
-every article that is saved will be preceded by a prompt, even when
-saving large batches of articles.  If this variable is neither nil not
-`always', there the user will be prompted once for a file name for
-each invocation of the saving commands.")
+;; Check&nb