changeset 257:e5ba2ba35226

Initial revision
author Jim Blandy <jimb@redhat.com>
date Thu, 09 May 1991 21:50:45 +0000
parents 7e4c7ef44243
children 1e0bc00dca7a
files lisp/=gnus.el lisp/compare-w.el lisp/emacs-lisp/cl-indent.el lisp/informat.el lisp/progmodes/awk-mode.el lisp/progmodes/cplus-md.el lisp/textmodes/bibtex.el
diffstat 7 files changed, 9160 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/=gnus.el	Thu May 09 21:50:45 1991 +0000
@@ -0,0 +1,6081 @@
+;;; GNUS: an NNTP-based News Reader for GNU Emacs
+;; Copyright (C) 1987, 1988, 1989 Fujitsu Laboratories LTD.
+;; Copyright (C) 1987, 1988, 1989, 1990 Masanobu UMEDA
+;; $Header: gnus.el,v 3.13 90/03/23 13:24:27 umerin Locked $
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY.  No author or distributor
+;; accepts responsibility to anyone for the consequences of using it
+;; or for whether it serves any particular purpose or works at all,
+;; unless he says so in writing.  Refer to the GNU Emacs General Public
+;; License for full details.
+
+;; Everyone is granted permission to copy, modify and redistribute
+;; GNU Emacs, but only under the conditions described in the
+;; GNU Emacs General Public License.   A copy of this license is
+;; supposed to have been given to you along with GNU Emacs so you
+;; can know your rights and responsibilities.  It should be in a
+;; file named COPYING.  Among other things, the copyright notice
+;; and this notice must be preserved on all copies.
+
+;; GNUS Mailing List:
+;; There are two mailing lists for GNUS lovers in the world:
+;;
+;;	info-gnus@flab.fujitsu.co.jp, and
+;;	info-gnus-english@tut.cis.ohio-state.edu.
+;;
+;; They are intended to exchange useful information about GNUS, such
+;; as bug fixes, useful hooks, and extensions.  The major difference
+;; between the lists is what the official language is.  Both Japanese
+;; and English are available in info-gnus, while English is only
+;; available in info-gnus-english. There is no need to subscribe to
+;; info-gnus if you cannot read Japanese messages, because most of the
+;; discussion and important announcements will be sent to
+;; info-gnus-english. Moreover, if you can read gnu.emacs.gnus
+;; newsgroup of USENET, you need not, either. info-gnus-english and
+;; gnu.emacs.gnus are linked each other.
+;;
+;; Please send subscription request to:
+;;
+;; 	info-gnus-request@flab.fujitsu.co.jp, or
+;;	info-gnus-english-request@cis.ohio-state.edu
+
+;; TO DO:
+;; (1) Incremental update of active info.
+;; (2) GNUS own poster.
+;; (3) Multi-GNUS (Talking to many hosts same time).
+;; (4) Asynchronous transmission of large messages.
+
+(provide 'gnus)
+(require 'nntp)
+(require 'mail-utils)
+
+(defvar gnus-nntp-server (or (getenv "NNTPSERVER") gnus-default-nntp-server)
+  "The name of the host running NNTP server.
+If it is a string such as `:DIRECTORY', the user's private DIRECTORY
+is used as a news spool.
+Initialized from the NNTPSERVER environment variable.")
+
+(defvar gnus-signature-file "~/.signature"
+  "*Your .signature file. Use `.signature-DISTRIBUTION' instead if exists.")
+
+(defvar gnus-use-cross-reference t
+  "Specifies what to do with cross references (Xref: field).
+If nil, ignore cross references.  If t, mark articles as read in subscribed
+newsgroups.  Otherwise, mark articles as read in all newsgroups.")
+
+(defvar gnus-use-followup-to t
+  "*Specifies what to do with Followup-To: field.
+If nil, ignore followup-to: field.  If t, use its value execpt for
+`poster'.  Otherewise, if not nil nor t, always use its value.")
+
+(defvar gnus-large-newsgroup 50
+  "*The number of articles which indicates a large newsgroup.
+If the number of articles in a newsgroup is greater than the value,
+confirmation is required for selecting the newsgroup.")
+
+(defvar gnus-author-copy (getenv "AUTHORCOPY")
+  "*Filename for saving a copy of an article posted using FCC: field.
+Initialized from the AUTHORCOPY environment variable.
+
+Articles are saved using a function specified by the the variable
+`gnus-author-copy-saver' (`rmail-output' is the default) if a file name
+is given.  Instead, if the first character of the name is `|', the
+contents of the article is piped out to the named program.  It is
+possible to save an article in an MH folder as follows:
+
+    (setq gnus-author-copy \"|/usr/local/lib/mh/rcvstore +Article\")")
+
+(defvar gnus-author-copy-saver (function rmail-output)
+  "*A function called with a file name to save an author copy to.
+The default function is `rmail-output' which saves in Unix mailbox format.")
+
+(defvar gnus-use-long-file-name
+  (not (memq system-type '(usg-unix-v xenix)))
+  "Non-nil means that a newsgroup name is used as a default file name
+to save articles to.  If nil, the directory form of a newsgroup is
+used instead.")
+
+(defvar gnus-article-save-directory (getenv "SAVEDIR")
+  "*The directory in which to save articles; defaults to ~/News.
+Initialized from the SAVEDIR environment variable.")
+
+(defvar gnus-default-article-saver (function gnus-Subject-save-in-rmail)
+  "A function used to save articles in your favorite format.
+The function must be interactively callable (in other words, it must
+be an Emacs command).
+
+GNUS provides the following functions:
+	gnus-Subject-save-in-rmail (in Rmail format)
+	gnus-Subject-save-in-mail (in Unix mail format)
+	gnus-Subject-save-in-folder (in an MH folder)
+	gnus-Subject-save-in-file (in article format).")
+
+(defvar gnus-rmail-save-name (function gnus-plain-save-name)
+  "A function generating a file name to save articles in Rmail format.
+The function is called with NEWSGROUP, HEADERS, and optional LAST-FILE.")
+
+(defvar gnus-mail-save-name (function gnus-plain-save-name)
+  "A function generating a file name to save articles in Unix mail format.
+The function is called with NEWSGROUP, HEADERS, and optional LAST-FILE.")
+
+(defvar gnus-folder-save-name (function gnus-folder-save-name)
+  "A function generating a file name to save articles in MH folder.
+The function is called with NEWSGROUP, HEADERS, and optional LAST-FOLDER.")
+
+(defvar gnus-file-save-name (function gnus-numeric-save-name)
+  "A function generating a file name to save articles in article format.
+The function is called with NEWSGROUP, HEADERS, and optional LAST-FILE.")
+
+(defvar gnus-kill-file-name "KILL"
+  "File name of a KILL file.")
+
+(defvar gnus-default-distribution "local"
+  "*Use this value as distribution if no distribution is specified.")
+
+(defvar gnus-novice-user t
+  "*Non-nil means that you are a novice to USENET.
+If non-nil, verbose messages may be displayed or your confirmation
+may be required.")
+
+(defvar gnus-interactive-post t
+  "*Newsgroup, subject, and distribution will be asked for if non-nil.")
+
+(defvar gnus-user-login-name nil
+  "*The login name of the user.
+Uses USER and LOGNAME environment variables if undefined.")
+
+(defvar gnus-user-full-name nil
+  "*The full name of the user.
+Uses from the NAME environment variable if undefined.")
+
+(defvar gnus-show-threads t
+  "*Show conversation threads in Subject Mode if non-nil.")
+
+(defvar gnus-thread-hide-subject t
+  "*Non-nil means hide subjects for thread subtrees.")
+
+(defvar gnus-thread-hide-subtree nil
+  "*Non-nil means hide thread subtrees initially.
+If non-nil, you have to run the command `gnus-Subject-show-thread' by
+hand or by using `gnus-Select-article-hook' to show hidden threads.")
+
+(defvar gnus-thread-hide-killed t
+  "*Non-nil means hide killed thread subtrees automatically.")
+
+(defvar gnus-thread-ignore-subject nil
+  "*Don't take care of subject differences, but only references if non-nil.
+If it is non-nil, some commands work with subjects do not work properly.")
+
+(defvar gnus-thread-indent-level 4
+  "Indentation of thread subtrees.")
+
+(defvar gnus-ignored-headers
+  "^Path:\\|^Posting-Version:\\|^Article-I.D.:\\|^Expires:\\|^Date-Received:\\|^References:\\|^Control:\\|^Xref:\\|^Lines:\\|^Posted:\\|^Relay-Version:\\|^Message-ID:\\|^Nf-ID:\\|^Nf-From:\\|^Approved:\\|^Sender:\\|^In-Reply-To:"
+  "Regexp matching headers not to display in messages.")
+
+(defvar gnus-show-all-headers nil
+  "*Show all headers of an article if non-nil.")
+
+(defvar gnus-save-all-headers nil
+  "*Save all headers of an article if non-nil.")
+
+(defvar gnus-optional-headers (function gnus-optional-lines-and-from)
+  "A function generating a optional string displayed in GNUS Subject
+mode buffer.  The function is called with an article HEADER. The
+result must be a string excluding `[' and `]'.")
+
+(defvar gnus-auto-extend-newsgroup t
+  "*Extend visible articles to forward and backward if non-nil.")
+
+(defvar gnus-auto-select-first t
+  "*Select the first unread article automagically if non-nil.
+If you want to prevent automatic selection of the first unread article
+in some newsgroups, set the variable to nil in `gnus-Select-group-hook'
+or `gnus-Apply-kill-hook'.")
+
+(defvar gnus-auto-select-next t
+  "*Select the next newsgroup automagically if non-nil.
+If the value is t and the next newsgroup is empty, GNUS will exit
+Subject mode and go back to Group mode.  If the value is neither nil
+nor t, GNUS will select the following unread newsgroup. Especially, if
+the value is the symbol `quietly', the next unread newsgroup will be
+selected without any confirmations.")
+
+(defvar gnus-auto-select-same nil
+  "*Select the next article with the same subject automagically if non-nil.")
+
+(defvar gnus-auto-center-subject t
+  "*Always center the current subject in GNUS Subject mode window if non-nil.")
+
+(defvar gnus-break-pages t
+  "*Break an article into pages if non-nil.
+Page delimiter is specified by the variable `gnus-page-delimiter'.")
+
+(defvar gnus-page-delimiter "^\^L"
+  "*Regexp describing line-beginnings that separate pages of news article.")
+
+(defvar gnus-digest-show-summary t
+  "*Show a summary of undigestified messages if non-nil.")
+
+(defvar gnus-digest-separator "^Subject:[ \t]"
+  "*Regexp that separates messages in a digest article.")
+
+(defvar gnus-use-full-window t
+  "*Non-nil means to take up the entire screen of Emacs.")
+
+(defvar gnus-window-configuration
+  '((SelectNewsgroup (0 1 0))
+    (ExitNewsgroup   (1 0 0))
+    (SelectArticle   (0 3 10))
+    (ExpandSubject   (0 1 0)))
+  "Specify window configurations for each action.
+The format of the variable is a list of (ACTION (G S A)), where
+G, S, and A are the relative height of Group, Subject, and Article
+windows, respectively.  ACTION is `SelectNewsgroup', `ExitNewsgroup',
+`SelectArticle', or `ExpandSubject'.")
+
+(defvar gnus-mail-reply-method
+  (function gnus-mail-reply-using-mail)
+  "A function to compose reply mail.
+The function `gnus-mail-reply-using-mail' uses usual the sendmail mail
+program.  The function `gnus-mail-reply-using-mhe' uses the mh-e mail
+program.  You can use yet another program by customizing this variable.")
+
+(defvar gnus-mail-other-window-method
+  (function gnus-mail-other-window-using-mail)
+  "A function to compose mail in other window.
+The function `gnus-mail-other-window-using-mail' uses usual sendmail
+mail program.  The function `gnus-mail-other-window-using-mhe' uses mh-e
+mail program.  You can use yet another program by customizing this variable.")
+
+(defvar gnus-subscribe-newsgroup-method
+  (function
+   (lambda (newsgroup)
+     (gnus-subscribe-newsgroup newsgroup
+			       (car (car gnus-newsrc-assoc)))))
+  "A function called with a newsgroup name when it is created.")
+
+(defvar gnus-Group-mode-hook nil
+  "A hook for GNUS Group Mode.")
+
+(defvar gnus-Subject-mode-hook nil
+  "A hook for GNUS Subject Mode.")
+
+(defvar gnus-Article-mode-hook nil
+  "A hook for GNUS Article Mode.")
+
+(defvar gnus-Kill-file-mode-hook nil
+  "A hook for GNUS KILL File Mode.")
+
+(defvar gnus-Open-server-hook nil
+  "A hook called just before opening connection to news server.")
+
+(defvar gnus-Startup-hook nil
+  "A hook called at start up time.
+This hook is called after GNUS is connected to the NNTP server.
+So, it is possible to change the behavior of GNUS according to the
+selected NNTP server.")
+
+(defvar gnus-Group-prepare-hook nil
+  "A hook called after newsgroup list is created in the Newsgroup buffer.
+If you want to modify the Newsgroup buffer, you can use this hook.")
+
+(defvar gnus-Subject-prepare-hook nil
+  "A hook called after subject list is created in the Subject buffer.
+If you want to modify the Subject buffer, you can use this hook.")
+
+(defvar gnus-Article-prepare-hook nil
+  "A hook called after an article is prepared in the Article buffer.
+If you want to run a special decoding program like nkf, use this hook.")
+
+(defvar gnus-Select-group-hook nil
+  "A hook called when a newsgroup is selected.
+If you want to sort Subject buffer by date and then by subject, you
+can use the following hook:
+
+(setq gnus-Select-group-hook
+      '(lambda ()
+	 ;; First of all, sort by date.
+	 (gnus-sort-headers
+	  '(lambda (a b)
+	     (gnus-date-lessp (gnus-header-date a)
+			      (gnus-header-date b))))
+	 ;; Then sort by subject string ignoring `Re:'.
+	 ;; If case-fold-search is non-nil, case of letters is ignored.
+	 (gnus-sort-headers
+	  '(lambda (a b)
+	     (gnus-string-lessp
+	      (gnus-simplify-subject (gnus-header-subject a) 're)
+	      (gnus-simplify-subject (gnus-header-subject b) 're)
+	      )))))
+
+If you'd like to simplify subjects like the `gnus-Subject-next-same-subject'
+command does, you can use the following hook:
+
+(setq gnus-Select-group-hook
+      '(lambda ()
+	 (mapcar (function
+		  (lambda (header)
+		    (nntp-set-header-subject
+		     header
+		     (gnus-simplify-subject
+		      (gnus-header-subject header) 're-only))))
+		 gnus-newsgroup-headers)))
+
+In some newsgroups author name is meaningless.  It is possible to
+prevent listing author names in the GNUS Subject buffer as follows:
+
+(setq gnus-Select-group-hook
+      '(lambda ()
+	 (cond ((string-equal \"comp.sources.unix\" gnus-newsgroup-name)
+		(setq gnus-optional-headers
+		      (function gnus-optional-lines)))
+	       (t
+		(setq gnus-optional-headers
+		      (function gnus-optional-lines-and-from))))))")
+
+(defvar gnus-Select-article-hook
+  (function (lambda () (gnus-Subject-show-thread)))
+  "Hook called when an article is selected.
+The default hook automatically shows conversation thread subtrees
+of the selected article as follows:
+
+(setq gnus-Select-article-hook
+      '(lambda ()
+	 (gnus-Subject-show-thread)))
+
+If you'd like to run RMAIL on a digest article automagically, you can
+use the following hook:
+
+(setq gnus-Select-article-hook
+      '(lambda ()
+	 (gnus-Subject-show-thread)
+	 (cond ((string-equal \"comp.sys.sun\" gnus-newsgroup-name)
+		(gnus-Subject-rmail-digest))
+	       ((and (string-equal \"comp.text\" gnus-newsgroup-name)
+		     (string-match \"^TeXhax Digest\"
+				   (gnus-header-subject gnus-current-headers)))
+		(gnus-Subject-rmail-digest)
+		))))")
+
+(defvar gnus-Select-digest-hook
+  (function
+   (lambda ()
+     ;; Reply-To: is required by `undigestify-rmail-message'.
+     (or (mail-position-on-field "Reply-to" t)
+	 (progn
+	   (mail-position-on-field "Reply-to")
+	   (insert (gnus-fetch-field "From"))))))
+  "A hook called when reading digest messages using Rmail.
+This hook can be used to modify incomplete digest articles as follows
+(this is the default):
+
+(setq gnus-Select-digest-hook
+      '(lambda ()
+	 ;; Reply-To: is required by `undigestify-rmail-message'.
+	 (or (mail-position-on-field \"Reply-to\" t)
+	     (progn
+	       (mail-position-on-field \"Reply-to\")
+	       (insert (gnus-fetch-field \"From\"))))))")
+
+(defvar gnus-Rmail-digest-hook nil
+  "A hook called when reading digest messages using Rmail.
+This hook is intended to customize Rmail mode for reading digest articles.")
+
+(defvar gnus-Apply-kill-hook (function gnus-apply-kill-file)
+  "A hook called when a newsgroup is selected and subject list is prepared.
+This hook is intended to apply a KILL file to the selected newsgroup.
+The function `gnus-apply-kill-file' is called defaultly.
+
+Since a general KILL file is too heavy to use for only a few
+newsgroups, we recommend you use a lighter hook function.  For
+example, if you'd like to apply a KILL file to articles which contains
+a string `rmgroup' in subject in newsgroup `control', you can use the
+following hook:
+
+(setq gnus-Apply-kill-hook
+      '(lambda ()
+	 (cond ((string-match \"control\" gnus-newsgroup-name)
+		(gnus-kill \"Subject\" \"rmgroup\")
+		(gnus-expunge \"X\")))))")
+
+(defvar gnus-Mark-article-hook
+  (function
+   (lambda ()
+     (or (memq gnus-current-article gnus-newsgroup-marked)
+	 (gnus-Subject-mark-as-read gnus-current-article))
+     (gnus-Subject-set-current-mark "+")))
+  "A hook called when an article is selected for the first time.
+The hook is intended to mark an article as read when it is selected.
+If you'd like to mark as unread (-) instead, use the following hook:
+
+(setq gnus-Mark-article-hook
+      '(lambda ()
+	 (gnus-Subject-mark-as-unread gnus-current-article)
+	 (gnus-Subject-set-current-mark \"+\")))")
+
+(defvar gnus-Inews-article-hook nil
+  "A hook called before posting an article.
+If you'd like to run a special encoding program, use this hook.")
+
+(defvar gnus-Exit-group-hook nil
+  "A hook called when exiting (not quitting) Subject mode.
+If your machine is so slow that exiting from Subject mode takes a
+long time, set the variable `gnus-newsgroup-headers' to nil.  This
+inhibits marking articles as read using cross-reference information.")
+
+(defvar gnus-Suspend-gnus-hook nil
+  "A hook called when suspending (not exiting) GNUS.")
+
+(defvar gnus-Exit-gnus-hook nil
+  "A hook called when exiting (not suspending) GNUS.")
+
+(defvar gnus-Save-newsrc-hook nil
+  "A hook called when saving the newsrc file.
+This hook is called before saving .newsrc file.")
+
+(defvar gnus-your-domain nil
+  "*Your domain name without your host name like: \"stars.flab.Fujitsu.CO.JP\"
+The environment variable DOMAINNAME is used instead if defined.  If
+the function `system-name' returns the full internet name, there is no
+need to define this variable.")
+
+(defvar gnus-your-organization nil
+  "*Your organization like: \"Fujitsu Laboratories Ltd., Kawasaki, Japan.\"
+The `ORGANIZATION' environment variable is used instead if defined.")
+
+(defvar gnus-use-generic-from nil
+  "*If nil, prepend local host name to the defined domain in the From:
+field; if stringp, use this; if non-nil, strip of the local host name.")
+
+(defvar gnus-use-generic-path nil
+  "*If nil, use the NNTP server name in the Path: field; if stringp,
+use this; if non-nil, use no host name (user name only)")
+
+;; Internal variables.
+
+(defconst gnus-version "GNUS 3.13"
+  "Version numbers of this version of GNUS.")
+
+(defvar gnus-Info-nodes
+  '((gnus-Group-mode . "(gnus)Newsgroup Commands")
+    (gnus-Subject-mode . "(gnus)Subject Commands")
+    (gnus-Article-mode . "(gnus)Article Commands")
+    (gnus-Kill-file-mode . "(gnus)KILL File")
+    (gnus-Browse-killed-mode . "(gnus)Maintenance"))
+  "Assoc list of major modes and related Info nodes.")
+
+(defvar gnus-access-methods
+  '((nntp
+     (gnus-retrieve-headers .	nntp-retrieve-headers)
+     (gnus-open-server .	nntp-open-server)
+     (gnus-close-server .	nntp-close-server)
+     (gnus-server-opened .	nntp-server-opened)
+     (gnus-status-message .	nntp-status-message)
+     (gnus-request-article .	nntp-request-article)
+     (gnus-request-group .	nntp-request-group)
+     (gnus-request-list .	nntp-request-list)
+     (gnus-request-post .	nntp-request-post))
+    (nnspool
+     (gnus-retrieve-headers .	nnspool-retrieve-headers)
+     (gnus-open-server .	nnspool-open-server)
+     (gnus-close-server .	nnspool-close-server)
+     (gnus-server-opened .	nnspool-server-opened)
+     (gnus-status-message .	nnspool-status-message)
+     (gnus-request-article .	nnspool-request-article)
+     (gnus-request-group .	nnspool-request-group)
+     (gnus-request-list .	nnspool-request-list)
+     (gnus-request-post .	nnspool-request-post))
+    (mhspool
+     (gnus-retrieve-headers .	mhspool-retrieve-headers)
+     (gnus-open-server .	mhspool-open-server)
+     (gnus-close-server .	mhspool-close-server)
+     (gnus-server-opened .	mhspool-server-opened)
+     (gnus-status-message .	mhspool-status-message)
+     (gnus-request-article .	mhspool-request-article)
+     (gnus-request-group .	mhspool-request-group)
+     (gnus-request-list .	mhspool-request-list)
+     (gnus-request-post .	mhspool-request-post)))
+  "Access method for NNTP, nnspool, and mhspool.")
+
+(defvar gnus-Group-buffer "*Newsgroup*")
+(defvar gnus-Subject-buffer "*Subject*")
+(defvar gnus-Article-buffer "*Article*")
+(defvar gnus-Digest-buffer "GNUS Digest")
+(defvar gnus-Digest-summary-buffer "GNUS Digest-summary")
+
+(defvar gnus-buffer-list
+  (list gnus-Group-buffer gnus-Subject-buffer gnus-Article-buffer
+	gnus-Digest-buffer gnus-Digest-summary-buffer)
+  "GNUS buffer names which should be killed when exiting.")
+
+(defvar gnus-variable-list
+  '(gnus-newsrc-options
+    gnus-newsrc-options-n-yes gnus-newsrc-options-n-no
+    gnus-newsrc-assoc gnus-killed-assoc gnus-marked-assoc)
+  "GNUS variables saved in the quick startup file.")
+
+(defvar gnus-overload-functions
+  '((news-inews gnus-inews-news "rnewspost")
+    (caesar-region gnus-caesar-region "rnews"))
+  "Functions overloaded by gnus.
+It is a list of `(original overload &optional file)'.")
+
+(defvar gnus-newsrc-options nil
+  "Options line in the .newsrc file.")
+
+(defvar gnus-newsrc-options-n-yes nil
+  "Regexp representing subscribed newsgroups.")
+
+(defvar gnus-newsrc-options-n-no nil
+  "Regexp representing unsubscribed newsgroups.")
+
+(defvar gnus-newsrc-assoc nil
+  "Assoc list of read articles.")
+
+(defvar gnus-killed-assoc nil
+  "Assoc list of newsgroups removed from `gnus-newsrc-assoc'.")
+
+(defvar gnus-marked-assoc nil
+  "Assoc list of articles marked as unread.")
+
+(defvar gnus-unread-hashtb nil
+  "Hashtable of unread articles.")
+
+(defvar gnus-active-hashtb nil
+  "Hashtable of active articles.")
+
+(defvar gnus-octive-hashtb nil
+  "Hashtable of OLD active articles.")
+
+(defvar gnus-current-startup-file nil
+  "Startup file for the current host.")
+
+(defvar gnus-last-search-regexp nil
+  "Default regexp for article search command.")
+
+(defvar gnus-last-shell-command nil
+  "Default shell command on article.")
+
+(defvar gnus-have-all-newsgroups nil)
+
+(defvar gnus-newsgroup-name nil)
+(defvar gnus-newsgroup-begin nil)
+(defvar gnus-newsgroup-end nil)
+(defvar gnus-newsgroup-last-rmail nil)
+(defvar gnus-newsgroup-last-mail nil)
+(defvar gnus-newsgroup-last-folder nil)
+(defvar gnus-newsgroup-last-file nil)
+
+(defvar gnus-newsgroup-unreads nil
+  "List of unread articles in the current newsgroup.")
+
+(defvar gnus-newsgroup-unselected nil
+  "List of unselected unread articles in the current newsgroup.")
+
+(defvar gnus-newsgroup-marked nil
+  "List of marked articles in the current newsgroup (a subset of unread art).")
+
+(defvar gnus-newsgroup-headers nil
+  "List of article headers in the current newsgroup.")
+
+(defvar gnus-current-article nil)
+(defvar gnus-current-headers nil)
+(defvar gnus-current-history nil)
+(defvar gnus-have-all-headers nil)
+(defvar gnus-last-article nil)
+(defvar gnus-current-kill-article nil)
+
+;; Save window configuration.
+(defvar gnus-winconf-kill-file nil)
+
+(defvar gnus-Group-mode-map nil)
+(defvar gnus-Subject-mode-map nil)
+(defvar gnus-Article-mode-map nil)
+(defvar gnus-Kill-file-mode-map nil)
+
+(defvar rmail-last-file (expand-file-name "~/XMBOX"))
+(defvar rmail-last-rmail-file (expand-file-name "~/XNEWS"))
+
+;; Define GNUS Subsystems.
+(autoload 'gnus-Group-post-news "gnuspost"
+	  "Post an article." t)
+(autoload 'gnus-Subject-post-news "gnuspost"
+	  "Post an article." t)
+(autoload 'gnus-Subject-post-reply "gnuspost"
+	  "Post a reply article." t)
+(autoload 'gnus-Subject-post-reply-with-original "gnuspost"
+	  "Post a reply article with original article." t)
+(autoload 'gnus-Subject-cancel-article "gnuspost"
+	  "Cancel an article you posted." t)
+
+(autoload 'gnus-Subject-mail-reply "gnusmail"
+	  "Reply mail to news author." t)
+(autoload 'gnus-Subject-mail-reply-with-original "gnusmail"
+	  "Reply mail to news author with original article." t)
+(autoload 'gnus-Subject-mail-other-window "gnusmail"
+	  "Compose mail in other window." t)
+
+(autoload 'gnus-Group-kill-group "gnusmisc"
+	  "Kill newsgroup on current line." t)
+(autoload 'gnus-Group-yank-group "gnusmisc"
+	  "Yank the last killed newsgroup on current line." t)
+(autoload 'gnus-Browse-killed-groups "gnusmisc"
+	  "Browse the killed newsgroups." t)
+
+(autoload 'rmail-output "rmailout"
+	  "Append this message to Unix mail file named FILE-NAME." t)
+(autoload 'mail-position-on-field "sendmail")
+(autoload 'mh-find-path "mh-e")
+(autoload 'mh-prompt-for-folder "mh-e")
+
+(put 'gnus-Group-mode 'mode-class 'special)
+(put 'gnus-Subject-mode 'mode-class 'special)
+(put 'gnus-Article-mode 'mode-class 'special)
+
+
+;;(put 'gnus-eval-in-buffer-window 'lisp-indent-function 1)
+
+(defmacro gnus-eval-in-buffer-window (buffer &rest forms)
+  "Pop to BUFFER, evaluate FORMS, and then returns to original window."
+  (` (let ((GNUSStartBufferWindow (selected-window)))
+       (unwind-protect
+	   (progn
+	     (pop-to-buffer (, buffer))
+	     (,@ forms))
+	 (select-window GNUSStartBufferWindow)))))
+
+(defmacro gnus-make-hashtable ()
+  '(make-abbrev-table))
+
+(defmacro gnus-gethash (string hashtable)
+  "Get hash value of STRING in HASHTABLE."
+  ;;(` (symbol-value (abbrev-symbol (, string) (, hashtable))))
+  (` (abbrev-expansion (, string) (, hashtable))))
+
+(defmacro gnus-sethash (string value hashtable)
+  "Set hash value. Arguments are STRING, VALUE, and HASHTABLE."
+  ;; We cannot use define-abbrev since it only accepts string as value.
+  (` (set (intern (, string) (, hashtable)) (, value))))
+
+;; Note: Macros defined here are also defined in nntp.el. I don't like
+;; to put them here, but many users got troubled with the old
+;; definitions in nntp.elc. These codes are NNTP 3.10 version.
+
+(defmacro nntp-header-number (header)
+  "Return article number in HEADER."
+  (` (aref (, header) 0)))
+
+(defmacro nntp-set-header-number (header number)
+  "Set article number of HEADER to NUMBER."
+  (` (aset (, header) 0 (, number))))
+
+(defmacro nntp-header-subject (header)
+  "Return subject string in HEADER."
+  (` (aref (, header) 1)))
+
+(defmacro nntp-set-header-subject (header subject)
+  "Set article subject of HEADER to SUBJECT."
+  (` (aset (, header) 1 (, subject))))
+
+(defmacro nntp-header-from (header)
+  "Return author string in HEADER."
+  (` (aref (, header) 2)))
+
+(defmacro nntp-set-header-from (header from)
+  "Set article author of HEADER to FROM."
+  (` (aset (, header) 2 (, from))))
+
+(defmacro nntp-header-xref (header)
+  "Return xref string in HEADER."
+  (` (aref (, header) 3)))
+
+(defmacro nntp-set-header-xref (header xref)
+  "Set article xref of HEADER to xref."
+  (` (aset (, header) 3 (, xref))))
+
+(defmacro nntp-header-lines (header)
+  "Return lines in HEADER."
+  (` (aref (, header) 4)))
+
+(defmacro nntp-set-header-lines (header lines)
+  "Set article lines of HEADER to LINES."
+  (` (aset (, header) 4 (, lines))))
+
+(defmacro nntp-header-date (header)
+  "Return date in HEADER."
+  (` (aref (, header) 5)))
+
+(defmacro nntp-set-header-date (header date)
+  "Set article date of HEADER to DATE."
+  (` (aset (, header) 5 (, date))))
+
+(defmacro nntp-header-id (header)
+  "Return Id in HEADER."
+  (` (aref (, header) 6)))
+
+(defmacro nntp-set-header-id (header id)
+  "Set article Id of HEADER to ID."
+  (` (aset (, header) 6 (, id))))
+
+(defmacro nntp-header-references (header)
+  "Return references in HEADER."
+  (` (aref (, header) 7)))
+
+(defmacro nntp-set-header-references (header ref)
+  "Set article references of HEADER to REF."
+  (` (aset (, header) 7 (, ref))))
+
+
+;;;
+;;; GNUS Group Mode
+;;;
+
+(if gnus-Group-mode-map
+    nil
+  (setq gnus-Group-mode-map (make-keymap))
+  (suppress-keymap gnus-Group-mode-map)
+  (define-key gnus-Group-mode-map " " 'gnus-Group-read-group)
+  (define-key gnus-Group-mode-map "=" 'gnus-Group-select-group)
+  (define-key gnus-Group-mode-map "j" 'gnus-Group-jump-to-group)
+  (define-key gnus-Group-mode-map "n" 'gnus-Group-next-unread-group)
+  (define-key gnus-Group-mode-map "p" 'gnus-Group-prev-unread-group)
+  (define-key gnus-Group-mode-map "\177" 'gnus-Group-prev-unread-group)
+  (define-key gnus-Group-mode-map "N" 'gnus-Group-next-group)
+  (define-key gnus-Group-mode-map "P" 'gnus-Group-prev-group)
+  (define-key gnus-Group-mode-map "\C-n" 'gnus-Group-next-group)
+  (define-key gnus-Group-mode-map "\C-p" 'gnus-Group-prev-group)
+  (define-key gnus-Group-mode-map "\r" 'next-line)
+  (define-key gnus-Group-mode-map "/" 'isearch-forward)
+  (define-key gnus-Group-mode-map "<" 'beginning-of-buffer)
+  (define-key gnus-Group-mode-map ">" 'end-of-buffer)
+  (define-key gnus-Group-mode-map "u" 'gnus-Group-unsubscribe-current-group)
+  (define-key gnus-Group-mode-map "U" 'gnus-Group-unsubscribe-group)
+  (define-key gnus-Group-mode-map "c" 'gnus-Group-catch-up)
+  (define-key gnus-Group-mode-map "C" 'gnus-Group-catch-up-all)
+  (define-key gnus-Group-mode-map "l" 'gnus-Group-list-groups)
+  (define-key gnus-Group-mode-map "L" 'gnus-Group-list-all-groups)
+  (define-key gnus-Group-mode-map "g" 'gnus-Group-get-new-news)
+  (define-key gnus-Group-mode-map "R" 'gnus-Group-restart)
+  (define-key gnus-Group-mode-map "b" 'gnus-Group-check-bogus-groups)
+  (define-key gnus-Group-mode-map "r" 'gnus-Group-restrict-groups)
+  (define-key gnus-Group-mode-map "a" 'gnus-Group-post-news)
+  (define-key gnus-Group-mode-map "\ek" 'gnus-Group-edit-local-kill)
+  (define-key gnus-Group-mode-map "\eK" 'gnus-Group-edit-global-kill)
+  (define-key gnus-Group-mode-map "\C-k" 'gnus-Group-kill-group)
+  (define-key gnus-Group-mode-map "\C-y" 'gnus-Group-yank-group)
+  (define-key gnus-Group-mode-map "\C-c\C-y" 'gnus-Browse-killed-groups)
+  (define-key gnus-Group-mode-map "V" 'gnus-version)
+  (define-key gnus-Group-mode-map "x" 'gnus-Group-force-update)
+  (define-key gnus-Group-mode-map "s" 'gnus-Group-force-update)
+  (define-key gnus-Group-mode-map "z" 'gnus-Group-suspend)
+  (define-key gnus-Group-mode-map "q" 'gnus-Group-exit)
+  (define-key gnus-Group-mode-map "Q" 'gnus-Group-quit)
+  (define-key gnus-Group-mode-map "?" 'gnus-Group-describe-briefly)
+  (define-key gnus-Group-mode-map "\C-c\C-i" 'gnus-Info-find-node))
+
+(defun gnus-Group-mode ()
+  "Major mode for reading network news.
+All normal editing commands are turned off.
+Instead, these commands are available:
+\\{gnus-Group-mode-map}
+
+  The name of the host running NNTP server is asked for if no default
+host is specified. It is also possible to choose another NNTP server
+even when the default server is defined by giving a prefix argument to
+the command `\\[gnus]'.
+
+  If an NNTP server is preceded by a colon such as `:Mail', the user's
+private directory `~/Mail' is used as a news spool. This makes it
+possible to read mail stored in MH folders or articles saved by GNUS.
+File names of mail or articles must consist of only numeric
+characters. Otherwise, they are ignored.
+
+  If there is a file named `~/.newsrc-SERVER', it is used as the
+startup file instead of standard one when talking to SERVER.  It is
+possible to talk to many hosts by using different startup files for
+each.
+
+  Option `-n' of the options line in the startup file is recognized
+properly the same as the Bnews system. For example, if the options
+line is `options -n !talk talk.rumors', newsgroups under the `talk'
+hierarchy except for `talk.rumors' are ignored while checking new
+newsgroups.
+
+  If there is a file named `~/.signature-DISTRIBUTION', it is used as
+signature file instead of standard one when posting a news in
+DISTRIBUTION.
+
+  If an Info file generated from `gnus.texinfo' is installed, you can
+read an appropriate Info node of the Info file according to the
+current major mode of GNUS by \\[gnus-Info-find-node].
+
+  The variable `gnus-version', `nntp-version', `nnspool-version', and
+`mhspool-version' have the version numbers of this version of gnus.el,
+nntp.el, nnspool.el, and mhspoo.el, respectively.
+
+User customizable variables:
+ gnus-nntp-server
+    Specifies the name of the host running the NNTP server. If its
+    value is a string such as `:DIRECTORY', the user's private
+    DIRECTORY is used as a news spool. The variable is initialized
+    from the NNTPSERVER environment variable.
+
+ gnus-nntp-service
+    Specifies a NNTP service name. It is usually \"nntp\" or 119.  Nil
+    forces GNUS to use a local news spool if the variable
+    `gnus-nntp-server' is set to the local host name.
+
+ gnus-startup-file
+    Specifies a startup file (.newsrc). If there is a file named
+    `.newsrc-SERVER', it's used instead when talking to SERVER. I
+    recommend you to use the server specific file, if you'd like to
+    talk to many servers.  Especially if you'd like to read your
+    private directory, the name of the file must be
+    `.newsrc-:DIRECTORY'.
+
+ gnus-signature-file
+    Specifies a signature file (.signature). If there is a file named
+    `.signature-DISTRIBUTION', it's used instead when posting an
+    article in DISTRIBUTION. Set the variable to nil to prevent
+    appending the file automatically. If you use an NNTP inews which
+    comes with the NNTP package, you may have to set the variable to
+    nil.
+
+ gnus-use-cross-reference
+    Specifies what to do with cross references (Xref: field).  If it
+    is nil, cross references are ignored.  If it is t, articles in
+    subscribed newsgroups are only marked as read.  Otherwise, if it
+    is not nil nor t, articles in all newsgroups are marked as read.
+
+ gnus-use-followup-to
+    Specifies what to do with followup-to: field.  If it is nil, its
+    value is ignored.  If it is non-nil, its value is used as followup
+    newsgroups.  Especially, if it is t and field value is `poster',
+    your confirmation is required.
+
+ gnus-author-copy
+    Specifies a file name to save a copy of article you posted using
+    FCC: field.  If the first character of the value is `|', the
+    contents of the article is piped out to a program specified by the
+    rest of the value.  The variable is initialized from the
+    AUTHORCOPY environment variable.
+
+ gnus-author-copy-saver
+    Specifies a function to save an author copy.  The function is
+    called with a file name.  The default function `rmail-output'
+    saves in Unix mail format.
+
+ gnus-kill-file-name
+    Use specified file name as a KILL file (default to `KILL').
+
+ gnus-novice-user
+    Non-nil means that you are a novice to USENET.  If non-nil,
+    verbose messages may be displayed or your confirmations may be
+    required.
+
+ gnus-interactive-post
+    Non-nil means that newsgroup, subject and distribution are asked
+    for interactively when posting a new article.
+
+ gnus-use-full-window
+    Non-nil means to take up the entire screen of Emacs.
+
+ gnus-window-configuration
+    Specifies the configuration of Group, Subject, and Article
+    windows.  It is a list of (ACTION (G S A)), where G, S, and A are
+    the relative height of Group, Subject, and Article windows,
+    respectively.  ACTION is `SelectNewsgroup', `ExitNewsgroup',
+    `SelectArticle', or `ExpandSubject'.
+
+ gnus-subscribe-newsgroup-method
+    Specifies a function called with a newsgroup name when new
+    newsgroup is found.  The default definition adds new newsgroup at
+    the beginning of other newsgroups.
+
+Various hooks for customization:
+ gnus-Group-mode-hook
+    Entry to this mode calls the value with no arguments, if that
+    value is non-nil. This hook is called before GNUS is connected to
+    the NNTP server. So, you can change or define the NNTP server in
+    this hook.
+
+ gnus-Startup-hook
+    Called with no arguments after the NNTP server is selected. It is
+    possible to change the behavior of GNUS or initialize the
+    variables according to the selected NNTP server.
+
+ gnus-Group-prepare-hook
+    Called with no arguments after a newsgroup list is created in the
+    Newsgroup buffer, if that value is non-nil.
+
+ gnus-Save-newsrc-hook
+    Called with no arguments when saving newsrc file if that value is
+    non-nil.
+
+ gnus-Inews-article-hook
+    Called with no arguments when posting an article if that value is
+    non-nil. This hook is called just before posting an article, while
+    `news-inews-hook' is called before preparing article headers. If
+    you'd like to convert kanji code of the article, this hook is recommended.
+
+ gnus-Suspend-gnus-hook
+    Called with no arguments when suspending (not exiting) GNUS, if
+    that value is non-nil.
+
+ gnus-Exit-gnus-hook
+    Called with no arguments when exiting (not suspending) GNUS, if
+    that value is non-nil."
+  (interactive)
+  (kill-all-local-variables)
+  ;; Gee.  Why don't you upgrade?
+  (cond ((boundp 'mode-line-modified)
+	 (setq mode-line-modified "--- "))
+	((listp (default-value 'mode-line-format))
+	 (setq mode-line-format
+	       (cons "--- " (cdr (default-value 'mode-line-format)))))
+	(t
+	 (setq mode-line-format
+	       "--- GNUS: List of Newsgroups  %[(%m)%]----%3p-%-")))
+  (setq major-mode 'gnus-Group-mode)
+  (setq mode-name "Newsgroup")
+  (setq mode-line-buffer-identification	"GNUS: List of Newsgroups")
+  (setq mode-line-process nil)
+  (use-local-map gnus-Group-mode-map)
+  (buffer-flush-undo (current-buffer))
+  (setq buffer-read-only t)		;Disable modification
+  (run-hooks 'gnus-Group-mode-hook))
+
+;;;###autoload
+(defun gnus (&optional confirm)
+  "Read network news.
+If optional argument CONFIRM is non-nil, ask NNTP server."
+  (interactive "P")
+  (unwind-protect
+      (progn
+	(switch-to-buffer (get-buffer-create gnus-Group-buffer))
+	(gnus-Group-mode)
+	(gnus-start-news-server confirm))
+    (if (not (gnus-server-opened))
+	(gnus-Group-quit)
+      ;; NNTP server is successfully open. 
+      (setq mode-line-process (format " {%s}" gnus-nntp-server))
+      (let ((buffer-read-only nil))
+	(erase-buffer)
+	(gnus-Group-startup-message)
+	(sit-for 0))
+      (run-hooks 'gnus-Startup-hook)
+      (gnus-setup-news-info)
+      (if gnus-novice-user
+	  (gnus-Group-describe-briefly)) ;Show brief help message.
+      (gnus-Group-list-groups nil)
+      )))
+
+(defun gnus-Group-startup-message ()
+  "Insert startup message in current buffer."
+  ;; Insert the message.
+  (insert "
+                   GNUS Version 3.13
+
+         NNTP-based News Reader for GNU Emacs
+
+
+If you have any trouble with this software, please let me
+know. I will fix your problems in the next release.
+
+Comments, suggestions, and bug fixes are welcome.
+
+Masanobu UMEDA
+umerin@tc.Nagasaki.GO.JP")
+  ;; And then hack it.
+  ;; 57 is the longest line.
+  (indent-rigidly (point-min) (point-max) (/ (max (- (window-width) 57) 0) 2))
+  (goto-char (point-min))
+  ;; +4 is fuzzy factor.
+  (insert-char ?\n (/ (max (- (window-height) 18) 0) 2)))
+
+(defun gnus-Group-list-groups (show-all)
+  "List newsgroups in the Newsgroup buffer.
+If argument SHOW-ALL is non-nil, unsubscribed groups are also listed."
+  (interactive "P")
+  (let ((last-group			;Current newsgroup.
+	 (gnus-Group-group-name))
+	(next-group			;Next possible newsgroup.
+	 (progn
+	   (gnus-Group-search-forward nil nil)
+	   (gnus-Group-group-name)))
+	(prev-group			;Previous possible newsgroup.
+	 (progn
+	   (gnus-Group-search-forward t nil)
+	   (gnus-Group-group-name))))
+    (gnus-Group-prepare show-all)
+    (if (zerop (buffer-size))
+	(message "No news is good news")
+      ;; Go to last newsgroup if possible.  If cannot, try next and
+      ;; previous.  If all fail, go to first unread newsgroup.
+      (goto-char (point-min))
+      (or (and last-group
+	       (re-search-forward
+		(concat "^.+: " (regexp-quote last-group) "$") nil t))
+	  (and next-group
+	       (re-search-forward
+		(concat "^.+: " (regexp-quote next-group) "$") nil t))
+	  (and prev-group
+	       (re-search-forward
+		(concat "^.+: " (regexp-quote prev-group) "$") nil t))
+	  (re-search-forward "^[ \t]+[1-9][0-9]*:" nil t))
+      ;; Adjust cursor point.
+      (beginning-of-line)
+      (search-forward ":" nil t)
+      )))
+
+(defun gnus-Group-prepare (&optional all)
+  "Prepare list of newsgroups in current buffer.
+If optional argument ALL is non-nil, unsubscribed groups are also listed."
+  (let ((buffer-read-only nil)
+	(newsrc gnus-newsrc-assoc)
+	(group-info nil)
+	(group-name nil)
+	(unread-count 0)
+	;; This specifies the format of Group buffer.
+	(cntl "%s%s%5d: %s\n"))
+    (erase-buffer)
+    ;; List newsgroups.
+    (while newsrc
+      (setq group-info (car newsrc))
+      (setq group-name (car group-info))
+      (setq unread-count (nth 1 (gnus-gethash group-name gnus-unread-hashtb)))
+      (if (or all
+	      (and (nth 1 group-info)	;Subscribed.
+		   (> unread-count 0)))	;There are unread articles.
+	  ;; Yes, I can use gnus-Group-prepare-line, but this is faster.
+	  (insert
+	   (format cntl
+		   ;; Subscribed or not.
+		   (if (nth 1 group-info) " " "U")
+		   ;; Has new news?
+		   (if (and (> unread-count 0)
+			    (>= 0
+				(- unread-count
+				   (length
+				    (cdr (assoc group-name
+						gnus-marked-assoc))))))
+		       "*" " ")
+		   ;; Number of unread articles.
+		   unread-count
+		   ;; Newsgroup name.
+		   group-name))
+	)
+      (setq newsrc (cdr newsrc))
+      )
+    (setq gnus-have-all-newsgroups all)
+    (goto-char (point-min))
+    (run-hooks 'gnus-Group-prepare-hook)
+    ))
+
+(defun gnus-Group-prepare-line (info)
+  "Return a string for the Newsgroup buffer from INFO.
+INFO is an element of gnus-newsrc-assoc or gnus-killed-assoc."
+  (let* ((group-name (car info))
+	 (unread-count
+	  (or (nth 1 (gnus-gethash group-name gnus-unread-hashtb))
+	      ;; Not in hash table, so compute it now.
+	      (gnus-number-of-articles
+	       (gnus-difference-of-range
+		(nth 2 (gnus-gethash group-name gnus-active-hashtb))
+		(nthcdr 2 info)))))
+	 ;; This specifies the format of Group buffer.
+	 (cntl "%s%s%5d: %s\n"))
+    (format cntl
+	    ;; Subscribed or not.
+	    (if (nth 1 info) " " "U")
+	    ;; Has new news?
+	    (if (and (> unread-count 0)
+		     (>= 0
+			 (- unread-count
+			    (length
+			     (cdr (assoc group-name gnus-marked-assoc))))))
+		"*" " ")
+	    ;; Number of unread articles.
+	    unread-count
+	    ;; Newsgroup name.
+	    group-name
+	    )))
+
+(defun gnus-Group-update-group (group &optional visible-only)
+  "Update newsgroup info of GROUP.
+If optional argument VISIBLE-ONLY is non-nil, non displayed group is ignored."
+  (let ((buffer-read-only nil)
+	(visible nil))
+    ;; Buffer may be narrowed.
+    (save-restriction
+      (widen)
+      ;; Search point to modify.
+      (goto-char (point-min))
+      (if (re-search-forward (concat "^.+: " (regexp-quote group) "$") nil t)
+	  ;; GROUP is listed in current buffer. So, delete old line.
+	  (progn
+	    (setq visible t)
+	    (beginning-of-line)
+	    (delete-region (point) (progn (forward-line 1) (point)))
+	    ))
+      (if (or visible (not visible-only))
+	  (progn
+	    (insert (gnus-Group-prepare-line (assoc group gnus-newsrc-assoc)))
+	    (forward-line -1)		;Move point on that line.
+	    ))
+      )))
+
+;; GNUS Group mode command
+
+(defun gnus-Group-group-name ()
+  "Get newsgroup name around point."
+  (save-excursion
+    (beginning-of-line)
+    (if (looking-at ".[* \t]*[0-9]+:[ \t]+\\([^ \t\n]+\\)$")
+	(buffer-substring (match-beginning 1) (match-end 1))
+      )))
+
+(defun gnus-Group-read-group (all &optional no-article)
+  "Read news in this newsgroup.
+If argument ALL is non-nil, already read articles become readable.
+If optional argument NO-ARTICLE is non-nil, no article body is displayed."
+  (interactive "P")
+  (let ((group (gnus-Group-group-name))) ;Newsgroup name to read.
+    (if group
+	(gnus-Subject-read-group
+	 group
+	 (or all
+	     ;;(not (nth 1 (assoc group gnus-newsrc-assoc)))	;Unsubscribed
+	     (zerop
+	      (nth 1 (gnus-gethash group gnus-unread-hashtb))))	;No unread
+	 no-article
+	 ))
+    ))
+
+(defun gnus-Group-select-group (all)
+  "Select this newsgroup.
+No article is selected automatically.
+If argument ALL is non-nil, already read articles become readable."
+  (interactive "P")
+  (gnus-Group-read-group all t))
+
+(defun gnus-Group-jump-to-group (group)
+  "Jump to newsgroup GROUP."
+  (interactive
+   (list (completing-read "Newsgroup: " gnus-newsrc-assoc nil 'require-match)))
+  (goto-char (point-min))
+  (or (re-search-forward (concat "^.+: " (regexp-quote group) "$") nil t)
+      (if (assoc group gnus-newsrc-assoc)
+	  ;; Add GROUP entry, then seach again.
+	  (gnus-Group-update-group group)))
+  ;; Adjust cursor point.
+  (beginning-of-line)
+  (search-forward ":" nil t))
+
+(defun gnus-Group-search-forward (backward any-group)
+  "Search for newsgroup forward.
+If first argument BACKWARD is non-nil, search backward instead.
+If second argument ANY-GROUP is non-nil, unsubscribed or empty
+group may be selected."
+  (let ((func (if backward 're-search-backward 're-search-forward))
+	(regexp 
+	 (format "^%s[ \t]*\\(%s\\):"
+		 (if any-group ".." " [ \t]")
+		 (if any-group "[0-9]+" "[1-9][0-9]*")))
+	(found nil))
+    (if backward
+	(beginning-of-line)
+      (end-of-line))
+    (setq found (funcall func regexp nil t))
+    ;; Adjust cursor point.
+    (beginning-of-line)
+    (search-forward ":" nil t)
+    ;; Return T if found.
+    found
+    ))
+
+(defun gnus-Group-next-group (n)
+  "Go to next N'th newsgroup."
+  (interactive "p")
+  (while (and (> n 1)
+	      (gnus-Group-search-forward nil t))
+    (setq n (1- n)))
+  (or (gnus-Group-search-forward nil t)
+      (message "No more newsgroups")))
+
+(defun gnus-Group-next-unread-group (n)
+  "Go to next N'th unread newsgroup."
+  (interactive "p")
+  (while (and (> n 1)
+	      (gnus-Group-search-forward nil nil))
+    (setq n (1- n)))
+  (or (gnus-Group-search-forward nil nil)
+      (message "No more unread newsgroups")))
+
+(defun gnus-Group-prev-group (n)
+  "Go to previous N'th newsgroup."
+  (interactive "p")
+  (while (and (> n 1)
+	      (gnus-Group-search-forward t t))
+    (setq n (1- n)))
+  (or (gnus-Group-search-forward t t)
+      (message "No more newsgroups")))
+
+(defun gnus-Group-prev-unread-group (n)
+  "Go to previous N'th unread newsgroup."
+  (interactive "p")
+  (while (and (> n 1)
+	      (gnus-Group-search-forward t nil))	      
+    (setq n (1- n)))
+  (or (gnus-Group-search-forward t nil)
+      (message "No more unread newsgroups")))
+
+(defun gnus-Group-catch-up (all &optional quietly)
+  "Mark all articles not marked as unread in current newsgroup as read.
+If prefix argument ALL is non-nil, all articles are marked as read.
+Cross references (Xref: field) of articles are ignored."
+  (interactive "P")
+  (let* ((group (gnus-Group-group-name))
+         (marked (if (not all)
+		     (cdr (assoc group gnus-marked-assoc)))))
+    (and group
+	 (or quietly
+	     (y-or-n-p
+	      (if all
+		  "Do you really want to mark everything as read? "
+		"Delete all articles not marked as read? ")))
+	 (progn
+	   (message "")			;Erase "Yes or No" question.
+	   ;; Any marked articles will be preserved.
+	   (gnus-update-unread-articles group marked marked)
+	   (gnus-Group-update-group group)
+	   (gnus-Group-next-group 1)))
+    ))
+
+(defun gnus-Group-catch-up-all (&optional quietly)
+  "Mark all articles in current newsgroup as read.
+Cross references (Xref: field) of articles are ignored."
+  (interactive)
+  (gnus-Group-catch-up t quietly))
+
+(defun gnus-Group-unsubscribe-current-group ()
+  "Toggle subscribe from/to unsubscribe current group."
+  (interactive)
+  (gnus-Group-unsubscribe-group (gnus-Group-group-name))
+  (gnus-Group-next-group 1))
+
+(defun gnus-Group-unsubscribe-group (group)
+  "Toggle subscribe from/to unsubscribe GROUP.
+New newsgroup is added to .newsrc automatically."
+  (interactive
+   (list (completing-read "Newsgroup: "
+			  gnus-active-hashtb nil 'require-match)))
+  (let ((newsrc (assoc group gnus-newsrc-assoc)))
+    (cond ((not (null newsrc))
+	   ;; Toggle subscription flag.
+	   (setcar (nthcdr 1 newsrc) (not (nth 1 newsrc)))
+	   (gnus-update-newsrc-buffer group)
+	   (gnus-Group-update-group group)
+	   ;; Adjust cursor point.
+	   (beginning-of-line)
+	   (search-forward ":" nil t))
+	  ((and (stringp group)
+		(gnus-gethash group gnus-active-hashtb))
+	   ;; Add new newsgroup.
+	   (gnus-add-newsgroup group)
+	   (gnus-Group-update-group group)
+	   ;; Adjust cursor point.
+	   (beginning-of-line)
+	   (search-forward ":" nil t))
+	  (t (error "No such newsgroup: %s" group)))
+    ))
+
+(defun gnus-Group-list-all-groups ()
+  "List all of newsgroups in the Newsgroup buffer."
+  (interactive)
+  (gnus-Group-list-groups t))
+
+(defun gnus-Group-get-new-news ()
+  "Get newly arrived articles.  In fact, read the active file again."
+  (interactive)
+  (gnus-setup-news-info)
+  (gnus-Group-list-groups gnus-have-all-newsgroups))
+
+(defun gnus-Group-restart ()
+  "Force GNUS to read the raw startup file."
+  (interactive)
+  (gnus-save-newsrc-file)
+  (gnus-setup-news-info t)		;Force to read the raw startup file.
+  (gnus-Group-list-groups gnus-have-all-newsgroups))
+
+(defun gnus-Group-check-bogus-groups ()
+  "Check bogus newsgroups."
+  (interactive)
+  (gnus-check-bogus-newsgroups t)	;Require confirmation.
+  (gnus-Group-list-groups gnus-have-all-newsgroups))
+
+(defun gnus-Group-restrict-groups (start end)
+  "Restrict visible newsgroups to the current region (START and END).
+Type \\[widen] to remove restriction."
+  (interactive "r")
+  (save-excursion
+    (narrow-to-region (progn
+			(goto-char start)
+			(beginning-of-line)
+			(point))
+		      (progn
+			(goto-char end)
+			(forward-line 1)
+			(point))))
+  (message (substitute-command-keys "Type \\[widen] to remove restriction")))
+
+(defun gnus-Group-edit-global-kill ()
+  "Edit a global KILL file."
+  (interactive)
+  (setq gnus-current-kill-article nil)	;No articles selected.
+  (gnus-Kill-file-edit-file nil) 	;Nil stands for global KILL file.
+  (message
+   (substitute-command-keys
+    "Editing a global KILL file (Type \\[gnus-Kill-file-exit] to exit)")))
+
+(defun gnus-Group-edit-local-kill ()
+  "Edit a local KILL file."
+  (interactive)
+  (setq gnus-current-kill-article nil)	;No articles selected.
+  (gnus-Kill-file-edit-file (gnus-Group-group-name))
+  (message
+   (substitute-command-keys
+    "Editing a local KILL file (Type \\[gnus-Kill-file-exit] to exit)")))
+
+(defun gnus-Group-force-update ()
+  "Update .newsrc file."
+  (interactive)
+  (gnus-save-newsrc-file))
+
+(defun gnus-Group-suspend ()
+  "Suspend the current GNUS session.
+In fact, cleanup buffers except for Group Mode buffer.
+The hook `gnus-Suspend-gnus-hook' is called before actually suspending."
+  (interactive)
+  (run-hooks 'gnus-Suspend-gnus-hook)
+  ;; Kill GNUS buffers except for Group Mode buffer.
+  (let ((buffers gnus-buffer-list))
+    (while buffers
+      (and (not (eq (car buffers) gnus-Group-buffer))
+	   (get-buffer (car buffers))
+	   (kill-buffer (car buffers)))
+      (setq buffers (cdr buffers))
+      ))
+  (bury-buffer))
+
+(defun gnus-Group-exit ()
+  "Quit reading news after updating .newsrc.
+The hook `gnus-Exit-gnus-hook' is called before actually quitting."
+  (interactive)
+  (if (or noninteractive		;For gnus-batch-kill
+	  (zerop (buffer-size))		;No news is good news.
+	  (not (gnus-server-opened))	;NNTP connection closed.
+	  (y-or-n-p "Are you sure you want to quit reading news? "))
+      (progn
+	(message "")			;Erase "Yes or No" question.
+	(run-hooks 'gnus-Exit-gnus-hook)
+	(gnus-save-newsrc-file)
+	(gnus-clear-system)
+	(gnus-close-server))
+    ))
+
+(defun gnus-Group-quit ()
+  "Quit reading news without updating .newsrc.
+The hook `gnus-Exit-gnus-hook' is called before actually quitting."
+  (interactive)
+  (if (or (zerop (buffer-size))
+	  (not (gnus-server-opened))
+	  (yes-or-no-p
+	   (format "Quit reading news without saving %s? "
+		   (file-name-nondirectory gnus-current-startup-file))))
+      (progn
+	(message "")			;Erase "Yes or No" question.
+	(run-hooks 'gnus-Exit-gnus-hook)
+	(gnus-clear-system)
+	(gnus-close-server))
+    ))
+
+(defun gnus-Group-describe-briefly ()
+  "Describe Group mode commands briefly."
+  (interactive)
+  (message
+   (concat
+    (substitute-command-keys "\\[gnus-Group-read-group]:Select  ")
+    (substitute-command-keys "\\[gnus-Group-next-unread-group]:Forward  ")
+    (substitute-command-keys "\\[gnus-Group-prev-unread-group]:Backward  ")
+    (substitute-command-keys "\\[gnus-Group-exit]:Exit  ")
+    (substitute-command-keys "\\[gnus-Info-find-node]:Run Info  ")
+    (substitute-command-keys "\\[gnus-Group-describe-briefly]:This help")
+    )))
+
+
+;;;
+;;; GNUS Subject Mode
+;;;
+
+(if gnus-Subject-mode-map
+    nil
+  (setq gnus-Subject-mode-map (make-keymap))
+  (suppress-keymap gnus-Subject-mode-map)
+  (define-key gnus-Subject-mode-map " " 'gnus-Subject-next-page)
+  (define-key gnus-Subject-mode-map "\177" 'gnus-Subject-prev-page)
+  (define-key gnus-Subject-mode-map "\r" 'gnus-Subject-scroll-up)
+  (define-key gnus-Subject-mode-map "n" 'gnus-Subject-next-unread-article)
+  (define-key gnus-Subject-mode-map "p" 'gnus-Subject-prev-unread-article)
+  (define-key gnus-Subject-mode-map "N" 'gnus-Subject-next-article)
+  (define-key gnus-Subject-mode-map "P" 'gnus-Subject-prev-article)
+  (define-key gnus-Subject-mode-map "\e\C-n" 'gnus-Subject-next-same-subject)
+  (define-key gnus-Subject-mode-map "\e\C-p" 'gnus-Subject-prev-same-subject)
+  ;;(define-key gnus-Subject-mode-map "\e\C-n" 'gnus-Subject-next-unread-same-subject)
+  ;;(define-key gnus-Subject-mode-map "\e\C-p" 'gnus-Subject-prev-unread-same-subject)
+  (define-key gnus-Subject-mode-map "\C-c\C-n" 'gnus-Subject-next-digest)
+  (define-key gnus-Subject-mode-map "\C-c\C-p" 'gnus-Subject-prev-digest)
+  (define-key gnus-Subject-mode-map "\C-n" 'gnus-Subject-next-subject)
+  (define-key gnus-Subject-mode-map "\C-p" 'gnus-Subject-prev-subject)
+  (define-key gnus-Subject-mode-map "\en" 'gnus-Subject-next-unread-subject)
+  (define-key gnus-Subject-mode-map "\ep" 'gnus-Subject-prev-unread-subject)
+  ;;(define-key gnus-Subject-mode-map "\C-cn" 'gnus-Subject-next-group)
+  ;;(define-key gnus-Subject-mode-map "\C-cp" 'gnus-Subject-prev-group)
+  (define-key gnus-Subject-mode-map "." 'gnus-Subject-first-unread-article)
+  (define-key gnus-Subject-mode-map "/" 'isearch-forward)
+  (define-key gnus-Subject-mode-map "s" 'gnus-Subject-isearch-article)
+  (define-key gnus-Subject-mode-map "\es" 'gnus-Subject-search-article-forward)
+  (define-key gnus-Subject-mode-map "\eS" 'gnus-Subject-search-article-backward)
+  (define-key gnus-Subject-mode-map "<" 'gnus-Subject-beginning-of-article)
+  (define-key gnus-Subject-mode-map ">" 'gnus-Subject-end-of-article)
+  (define-key gnus-Subject-mode-map "j" 'gnus-Subject-goto-subject)
+  (define-key gnus-Subject-mode-map "J" 'gnus-Subject-goto-article)
+  (define-key gnus-Subject-mode-map "l" 'gnus-Subject-goto-last-article)
+  (define-key gnus-Subject-mode-map "^" 'gnus-Subject-refer-parent-article)
+  (define-key gnus-Subject-mode-map "\er" 'gnus-Subject-refer-article)
+  (define-key gnus-Subject-mode-map "u" 'gnus-Subject-mark-as-unread-forward)
+  (define-key gnus-Subject-mode-map "U" 'gnus-Subject-mark-as-unread-backward)
+  (define-key gnus-Subject-mode-map "d" 'gnus-Subject-mark-as-read-forward)
+  (define-key gnus-Subject-mode-map "D" 'gnus-Subject-mark-as-read-backward)
+  (define-key gnus-Subject-mode-map "\eu" 'gnus-Subject-clear-mark-forward)
+  (define-key gnus-Subject-mode-map "\eU" 'gnus-Subject-clear-mark-backward)
+  (define-key gnus-Subject-mode-map "k" 'gnus-Subject-kill-same-subject-and-select)
+  (define-key gnus-Subject-mode-map "\C-k" 'gnus-Subject-kill-same-subject)
+  (define-key gnus-Subject-mode-map "\e\C-t" 'gnus-Subject-toggle-threads)
+  (define-key gnus-Subject-mode-map "\e\C-s" 'gnus-Subject-show-thread)
+  (define-key gnus-Subject-mode-map "\e\C-h" 'gnus-Subject-hide-thread)
+  (define-key gnus-Subject-mode-map "\e\C-f" 'gnus-Subject-next-thread)
+  (define-key gnus-Subject-mode-map "\e\C-b" 'gnus-Subject-prev-thread)
+  (define-key gnus-Subject-mode-map "\e\C-u" 'gnus-Subject-up-thread)
+  (define-key gnus-Subject-mode-map "\e\C-d" 'gnus-Subject-down-thread)
+  (define-key gnus-Subject-mode-map "\e\C-k" 'gnus-Subject-kill-thread)
+  (define-key gnus-Subject-mode-map "&" 'gnus-Subject-execute-command)
+  ;;(define-key gnus-Subject-mode-map "c" 'gnus-Subject-catch-up)
+  ;;(define-key gnus-Subject-mode-map "c" 'gnus-Subject-catch-up-all)
+  (define-key gnus-Subject-mode-map "c" 'gnus-Subject-catch-up-and-exit)
+  ;;(define-key gnus-Subject-mode-map "c" 'gnus-Subject-catch-up-all-and-exit)
+  (define-key gnus-Subject-mode-map "\C-t" 'gnus-Subject-toggle-truncation)
+  (define-key gnus-Subject-mode-map "x" 'gnus-Subject-delete-marked-as-read)
+  (define-key gnus-Subject-mode-map "X" 'gnus-Subject-delete-marked-with)
+  (define-key gnus-Subject-mode-map "\C-c\C-sn" 'gnus-Subject-sort-by-number)
+  (define-key gnus-Subject-mode-map "\C-c\C-sa" 'gnus-Subject-sort-by-author)
+  (define-key gnus-Subject-mode-map "\C-c\C-ss" 'gnus-Subject-sort-by-subject)
+  (define-key gnus-Subject-mode-map "\C-c\C-sd" 'gnus-Subject-sort-by-date)
+  (define-key gnus-Subject-mode-map "\C-c\C-s\C-n" 'gnus-Subject-sort-by-number)
+  (define-key gnus-Subject-mode-map "\C-c\C-s\C-a" 'gnus-Subject-sort-by-author)
+  (define-key gnus-Subject-mode-map "\C-c\C-s\C-s" 'gnus-Subject-sort-by-subject)
+  (define-key gnus-Subject-mode-map "\C-c\C-s\C-d" 'gnus-Subject-sort-by-date)
+  (define-key gnus-Subject-mode-map "=" 'gnus-Subject-expand-window)
+  (define-key gnus-Subject-mode-map "G" 'gnus-Subject-reselect-current-group)
+  (define-key gnus-Subject-mode-map "w" 'gnus-Subject-stop-page-breaking)
+  (define-key gnus-Subject-mode-map "\C-c\C-r" 'gnus-Subject-caesar-message)
+  (define-key gnus-Subject-mode-map "g" 'gnus-Subject-show-article)
+  (define-key gnus-Subject-mode-map "t" 'gnus-Subject-toggle-header)
+  (define-key gnus-Subject-mode-map "v" 'gnus-Subject-show-all-headers)
+  (define-key gnus-Subject-mode-map "\C-d" 'gnus-Subject-rmail-digest)
+  (define-key gnus-Subject-mode-map "a" 'gnus-Subject-post-news)
+  (define-key gnus-Subject-mode-map "f" 'gnus-Subject-post-reply)
+  (define-key gnus-Subject-mode-map "F" 'gnus-Subject-post-reply-with-original)
+  (define-key gnus-Subject-mode-map "C" 'gnus-Subject-cancel-article)
+  (define-key gnus-Subject-mode-map "r" 'gnus-Subject-mail-reply)
+  (define-key gnus-Subject-mode-map "R" 'gnus-Subject-mail-reply-with-original)
+  (define-key gnus-Subject-mode-map "m" 'gnus-Subject-mail-other-window)
+  (define-key gnus-Subject-mode-map "o" 'gnus-Subject-save-article)
+  (define-key gnus-Subject-mode-map "\C-o" 'gnus-Subject-save-in-mail)
+  (define-key gnus-Subject-mode-map "|" 'gnus-Subject-pipe-output)
+  (define-key gnus-Subject-mode-map "\ek" 'gnus-Subject-edit-local-kill)
+  (define-key gnus-Subject-mode-map "\eK" 'gnus-Subject-edit-global-kill)
+  (define-key gnus-Subject-mode-map "V" 'gnus-version)
+  (define-key gnus-Subject-mode-map "q" 'gnus-Subject-exit)
+  (define-key gnus-Subject-mode-map "Q" 'gnus-Subject-quit)
+  (define-key gnus-Subject-mode-map "?" 'gnus-Subject-describe-briefly)
+  (define-key gnus-Subject-mode-map "\C-c\C-i" 'gnus-Info-find-node))
+
+(defun gnus-Subject-mode ()
+  "Major mode for reading articles in this newsgroup.
+All normal editing commands are turned off.
+Instead, these commands are available:
+\\{gnus-Subject-mode-map}
+
+User customizable variables:
+ gnus-large-newsgroup
+    The number of articles which indicates a large newsgroup. If the
+    number of articles in a newsgroup is greater than the value, the
+    number of articles to be selected is asked for.  If the given value
+    N is positive, the last N articles is selected.  If N is negative,
+    the first N articles are selected.  An empty string means to select
+    all articles.
+
+ gnus-use-long-file-name
+    Non-nil means that a newsgroup name is used as a default file name
+    to save articles to.  If it's nil, the directory form of a
+    newsgroup is used instead.
+
+ gnus-default-article-saver
+    Specifies your favorite article saver which is interactively
+    funcallable.  Following functions are available:
+
+	gnus-Subject-save-in-rmail (in Rmail format)
+	gnus-Subject-save-in-mail (in Unix mail format)
+	gnus-Subject-save-in-folder (in MH folder)
+	gnus-Subject-save-in-file (in article format).
+
+ gnus-rmail-save-name
+ gnus-mail-save-name
+ gnus-folder-save-name
+ gnus-file-save-name
+    Specifies a function generating a file name to save articles in
+    specified format.  The function is called with NEWSGROUP, HEADERS,
+    and optional LAST-FILE.  Access macros to the headers are defined
+    as nntp-header-FIELD, and functions are defined as `gnus-header-FIELD'.
+
+ gnus-article-save-directory
+    Specifies a directory name to save articles to using the commands
+    `gnus-Subject-save-in-rmail', `gnus-Subject-save-in-mail' and
+    `gnus-Subject-save-in-file'.  The variable is initialized from the
+    SAVEDIR environment variable.
+
+ gnus-show-all-headers
+    Non-nil means that all headers of an article are shown.
+
+ gnus-save-all-headers
+    Non-nil means that all headers of an article are saved in a file.
+
+ gnus-show-threads
+    Non-nil means that conversation threads are shown in tree structure.
+
+ gnus-thread-hide-subject
+    Non-nil means that subjects for thread subtrees are hidden.
+
+ gnus-thread-hide-subtree
+    Non-nil means that thread subtrees are hidden initially.
+
+ gnus-thread-hide-killed
+    Non-nil means that killed thread subtrees are hidden automatically.
+
+ gnus-thread-ignore-subject
+    Non-nil means that subject differences are ignored in constructing
+    thread trees.
+
+ gnus-thread-indent-level
+    Indentation of thread subtrees.
+
+ gnus-optional-headers
+    Specifies a function which generates an optional string displayed
+    in the Subject buffer.  The function is called with an article
+    HEADERS.  The result must be a string excluding `[' and `]'.  The
+    default function returns a string like NNN:AUTHOR, where NNN is
+    the number of lines in an article and AUTHOR is the name of the
+    author.
+
+ gnus-auto-extend-newsgroup
+    Non-nil means visible articles are extended to forward and
+    backward automatically if possible.
+
+ gnus-auto-select-first
+    Non-nil means the first unread article is selected automagically
+    when a newsgroup is selected normally (by gnus-Group-read-group).
+    If you'd like to prevent automatic selection of the first unread
+    article in some newsgroups, set the variable to nil in
+    gnus-Select-group-hook or gnus-Apply-kill-hook.
+
+ gnus-auto-select-next
+    Non-nil means the next newsgroup is selected automagically at the
+    end of the newsgroup. If the value is t and the next newsgroup is
+    empty (no unread articles), GNUS will exit Subject mode and go
+    back to Group mode. If the value is neither nil nor t, GNUS won't
+    exit Subject mode but select the following unread newsgroup.
+    Especially, if the value is the symbol `quietly', the next unread
+    newsgroup will be selected without any confirmations.
+
+ gnus-auto-select-same
+    Non-nil means an article with the same subject as the current
+    article is selected automagically like `rn -S'.
+
+ gnus-auto-center-subject
+    Non-nil means the point of Subject Mode window is always kept
+    centered.
+
+ gnus-break-pages
+    Non-nil means an article is broken into pages at page delimiters.
+    This may not work with some versions of GNU Emacs earlier than
+    version 18.50.
+
+ gnus-page-delimiter
+    Specifies a regexp describing line-beginnings that separate pages
+    of news article.
+
+ [gnus-more-message is obsolete.  overlay-arrow-string interfares
+    with other subsystems, such as dbx mode.]
+
+ gnus-digest-show-summary
+    Non-nil means that a summary of digest messages is shown when
+    reading a digest article using `gnus-Subject-rmail-digest' command.
+
+ gnus-digest-separator
+    Specifies a regexp separating messages in a digest article.
+
+ gnus-mail-reply-method
+ gnus-mail-other-window-method
+    Specifies a function to begin composing mail message using
+    commands gnus-Subject-mail-reply and
+    gnus-Subject-mail-other-window.  Functions
+    gnus-mail-reply-using-mail and gnus-mail-reply-using-mhe are
+    available for the value of gnus-mail-reply-method.  And functions
+    gnus-mail-other-window-using-mail and
+    gnus-mail-other-window-using-mhe are available for the value of
+    gnus-mail-other-window-method.
+
+Various hooks for customization:
+ gnus-Subject-mode-hook
+    Entry to this mode calls the value with no arguments, if that
+    value is non-nil.
+
+ gnus-Select-group-hook
+    Called with no arguments when newsgroup is selected, if that value
+    is non-nil.  It is possible to sort subjects in this hook.  See the
+    documentation of this variable for more information.
+
+ gnus-Subject-prepare-hook
+    Called with no arguments after a subject list is created in the
+    Subject buffer, if that value is non-nil.  If you'd like to modify
+    the buffer, you can use this hook.
+
+ gnus-Select-article-hook
+    Called with no arguments when an article is selected, if that
+    value is non-nil.  See the documentation of this variable for
+    more information.
+
+ gnus-Select-digest-hook
+    Called with no arguments when reading digest messages using Rmail,
+    if that value is non-nil.  This hook can be used to modify an
+    article so that Rmail can work with it.  See the documentation of
+    the variable for more information.
+
+ gnus-Rmail-digest-hook
+    Called with no arguments when reading digest messages using Rmail,
+    if that value is non-nil.  This hook is intended to customize Rmail
+    mode.
+
+ gnus-Apply-kill-hook
+    Called with no arguments when a newsgroup is selected and the
+    Subject buffer is prepared.  This hook is intended to apply a KILL
+    file to the selected newsgroup.  The format of KILL file is
+    completely different from that of version 3.8. You have to rewrite
+    them in the new format.  See the documentation of Kill file mode
+    for more information.
+
+ gnus-Mark-article-hook
+    Called with no arguments when an article is selected at the first
+    time.  The hook is intended to mark an article as read (or unread)
+    automatically when it is selected.  See the documentation of the
+    variable for more information.
+
+ gnus-Exit-group-hook
+    Called with no arguments when exiting the current newsgroup, if
+    that value is non-nil.  If your machine is so slow that exiting
+    from Subject mode takes very long time, inhibit marking articles
+    as read using cross-references by setting the variable
+    `gnus-newsgroup-headers' to nil in this hook."
+  (interactive)
+  (kill-all-local-variables)
+  ;; Gee.  Why don't you upgrade?
+  (cond ((boundp 'mode-line-modified)
+	 (setq mode-line-modified "--- "))
+	((listp (default-value 'mode-line-format))
+	 (setq mode-line-format
+	       (cons "--- " (cdr (default-value 'mode-line-format))))))
+  (make-local-variable 'global-mode-string)
+  (setq global-mode-string nil)
+  (setq major-mode 'gnus-Subject-mode)
+  (setq mode-name "Subject")
+  ;;(setq mode-line-process '(" " gnus-newsgroup-name))
+  (make-local-variable 'minor-mode-alist)
+  (or (assq 'gnus-show-threads minor-mode-alist)
+      (setq minor-mode-alist
+	    (cons (list 'gnus-show-threads " Thread") minor-mode-alist)))
+  (gnus-Subject-set-mode-line)
+  (use-local-map gnus-Subject-mode-map)
+  (buffer-flush-undo (current-buffer))
+  (setq buffer-read-only t)		;Disable modification
+  (setq truncate-lines t)		;Stop line folding
+  (setq selective-display t)
+  (setq selective-display-ellipses t)	;Display `...'
+  ;;(setq case-fold-search t)
+  (run-hooks 'gnus-Subject-mode-hook))
+
+(defun gnus-Subject-setup-buffer ()
+  "Initialize subject display buffer."
+  (if (get-buffer gnus-Subject-buffer)
+      (set-buffer gnus-Subject-buffer)
+    (set-buffer (get-buffer-create gnus-Subject-buffer))
+    (gnus-Subject-mode)
+    ))
+
+(defun gnus-Subject-read-group (group &optional show-all no-article)
+  "Start reading news in newsgroup GROUP.
+If optional first argument SHOW-ALL is non-nil, already read articles are
+also listed.
+If optional second argument NO-ARTICLE is non-nil, no article is selected
+initially."
+  (message "Retrieving newsgroup: %s..." group)
+  (if (gnus-select-newsgroup group show-all)
+      (progn
+	;; Don't switch-to-buffer to prevent displaying old contents
+	;;  of the buffer until new subjects list is created.
+	;; Suggested by Juha Heinanen <jh@tut.fi>
+	(gnus-Subject-setup-buffer)
+	;; You can change the order of subjects in this hook.
+	(run-hooks 'gnus-Select-group-hook)
+	(gnus-Subject-prepare)
+	;; Function `gnus-apply-kill-file' must be called in this hook.
+	(run-hooks 'gnus-Apply-kill-hook)
+	(if (zerop (buffer-size))
+	    ;; This newsgroup is empty.
+	    (progn
+	      (gnus-Subject-catch-up-and-exit nil t) ;Without confirmations.
+	      (message "No unread news"))
+	  ;; Hide conversation thread subtrees.  We cannot do this in
+	  ;; gnus-Subject-prepare-hook since kill processing may not
+	  ;; work with hidden articles.
+	  (and gnus-show-threads
+	       gnus-thread-hide-subtree
+	       (gnus-Subject-hide-all-threads))
+	  ;; Show first unread article if requested.
+	  (goto-char (point-min))
+	  (if (and (not no-article)
+		   gnus-auto-select-first
+		   (gnus-Subject-first-unread-article))
+	      ;; Window is configured automatically.
+	      ;; Current buffer may be changed as a result of hook
+	      ;; evaluation, especially by gnus-Subject-rmail-digest
+	      ;; command, so we should adjust cursor point carefully.
+	      (if (eq (current-buffer) (get-buffer gnus-Subject-buffer))
+		  (progn
+		    ;; Adjust cursor point.
+		    (beginning-of-line)
+		    (search-forward ":" nil t)))
+	    (gnus-configure-windows 'SelectNewsgroup)
+	    (pop-to-buffer gnus-Subject-buffer)
+	    (gnus-Subject-set-mode-line)
+	    ;; I sometime get confused with the old Article buffer.
+	    (if (get-buffer gnus-Article-buffer)
+		(if (get-buffer-window gnus-Article-buffer)
+		    (save-excursion
+		      (set-buffer gnus-Article-buffer)
+		      (let ((buffer-read-only nil))
+			(erase-buffer)))
+		  (kill-buffer gnus-Article-buffer)))
+	    ;; Adjust cursor point.
+	    (beginning-of-line)
+	    (search-forward ":" nil t))
+	  ))
+    ;; Cannot select newsgroup GROUP.
+    (if (gnus-gethash group gnus-active-hashtb)
+	(progn
+	  ;; If NNTP is used, nntp_access file may not be installed
+	  ;; properly.  Otherwise, may be active file problem.
+	  (ding)
+	  (message "Cannot select %s.  May be security or active file problem." group)
+	  (sit-for 0))
+      ;; Check bogus newsgroups.
+      ;; We must be in Group Mode buffer.
+      (gnus-Group-check-bogus-groups))
+    ))
+
+(defun gnus-Subject-prepare ()
+  "Prepare subject list of current newsgroup in Subject mode buffer."
+  (let ((buffer-read-only nil))
+    ;; Note: The next codes are not actually used because the user who
+    ;; want it can define them in gnus-Select-group-hook.
+    ;; Print verbose messages if too many articles are selected.
+    ;;    (and (numberp gnus-large-newsgroup)
+    ;;       (> (length gnus-newsgroup-headers) gnus-large-newsgroup)
+    ;;       (message "Preparing headers..."))
+    (erase-buffer)
+    (gnus-Subject-prepare-threads
+     (if gnus-show-threads
+	 (gnus-make-threads gnus-newsgroup-headers)
+       gnus-newsgroup-headers) 0)
+    ;; Erase header retrieval message.
+    (message "")
+    ;; Call hooks for modifying Subject mode buffer.
+    ;; Suggested by sven@tde.LTH.Se (Sven Mattisson).
+    (goto-char (point-min))
+    (run-hooks 'gnus-Subject-prepare-hook)
+    ))
+
+;; Basic ideas by Paul Dworkin <paul@media-lab.media.mit.edu>
+
+(defun gnus-Subject-prepare-threads (threads level)
+  "Prepare Subject buffer from THREADS and indentation LEVEL.
+THREADS is a list of `(PARENT [(CHILD1 [(GRANDCHILD ...]...) ...]).'"
+  (let ((thread nil)
+	(header nil)
+	(number nil)
+	;; `M Indent NUM: [OPT] SUBJECT'
+	(cntl (format "%%s %%s%%%dd: [%%s] %%s\n"
+		      (length (prin1-to-string gnus-newsgroup-end)))))
+    (while threads
+      (setq thread (car threads))
+      (setq threads (cdr threads))
+      ;; If thread is a cons, hierarchical threads is given.
+      ;; Otherwise, thread itself is header.
+      (if (consp thread)
+	  (setq header (car thread))
+	(setq header thread))
+      ;; Print valid header only.
+      (if (vectorp header)		;Depends on nntp.el.
+	  (progn
+	    (setq number (nntp-header-number header))
+	    (insert
+	     (format cntl
+		     ;; Read or not.
+		     (cond ((memq number gnus-newsgroup-marked)  "-")
+			   ((memq number gnus-newsgroup-unreads) " ")
+			   (t "D"))
+		     ;; Thread level.
+		     (make-string (* level gnus-thread-indent-level) ? )
+		     ;; Article number.
+		     number
+		     ;; Optional headers.
+		     (or (and gnus-optional-headers
+			      (funcall gnus-optional-headers header)) "")
+		     ;; Its subject string.
+		     (concat (if (or (zerop level)
+				     (not gnus-thread-hide-subject))
+				 nil
+			       (make-string (window-width) ? ))
+			     (nntp-header-subject header))
+		     ))
+	    ))
+      ;; Print subthreads.
+      (and (consp thread)
+	   (cdr thread)
+	   (gnus-Subject-prepare-threads (cdr thread) (1+ level)))
+      )))
+
+(defun gnus-Subject-set-mode-line ()
+  "Set Subject mode line string."
+  ;; The value must be a string to escape %-constructs.
+  (let ((subject
+	 (if gnus-current-headers
+	     (nntp-header-subject gnus-current-headers) gnus-newsgroup-name)))
+    (setq mode-line-buffer-identification
+	  (concat "GNUS: "
+		  subject
+		  ;; Enough spaces to pad subject to 17 positions.
+		  (make-string (max 0 (- 17 (length subject))) ? ))))
+  (set-buffer-modified-p t))
+
+;; GNUS Subject mode command.
+
+(defun gnus-Subject-search-group (&optional backward)
+  "Search for next unread newsgroup.
+If optional argument BACKWARD is non-nil, search backward instead."
+  (save-excursion
+    (set-buffer gnus-Group-buffer)
+    (save-excursion
+      ;; We don't want to alter current point of Group mode buffer.
+      (if (gnus-Group-search-forward backward nil)
+	  (gnus-Group-group-name))
+      )))
+
+(defun gnus-Subject-search-subject (backward unread subject)
+  "Search for article forward.
+If first argument BACKWARD is non-nil, search backward.
+If second argument UNREAD is non-nil, only unread article is selected.
+If third argument SUBJECT is non-nil, the article which has
+the same subject will be searched for."
+  (let ((func (if backward 're-search-backward 're-search-forward))
+	(article nil)
+	;; We have to take care of hidden lines.
+	(regexp 
+	 (format "^%s[ \t]+\\([0-9]+\\):.\\[[^]\r\n]*\\][ \t]+%s"
+		 ;;(if unread " " ".")
+		 (cond ((eq unread t) " ") (unread "[ ---]") (t "."))
+		 (if subject
+		     (concat "\\([Rr][Ee]:[ \t]+\\)*"
+			     (regexp-quote (gnus-simplify-subject subject))
+			     ;; Ignore words in parentheses.
+			     "\\([ \t]*([^\r\n]*)\\)*[ \t]*\\(\r\\|$\\)")
+		   "")
+		 )))
+    (if backward
+	(beginning-of-line)
+      (end-of-line))
+    (if (funcall func regexp nil t)
+	(setq article
+	      (string-to-int
+	       (buffer-substring (match-beginning 1) (match-end 1)))))
+    ;; Adjust cursor point.
+    (beginning-of-line)
+    (search-forward ":" nil t)
+    ;; This is the result.
+    article
+    ))
+
+(defun gnus-Subject-search-forward (&optional unread subject)
+  "Search for article forward.
+If first optional argument UNREAD is non-nil, only unread article is selected.
+If second optional argument SUBJECT is non-nil, the article which has
+the same subject will be searched for."
+  (gnus-Subject-search-subject nil unread subject))
+
+(defun gnus-Subject-search-backward (&optional unread subject)
+  "Search for article backward.
+If first optional argument UNREAD is non-nil, only unread article is selected.
+If second optional argument SUBJECT is non-nil, the article which has
+the same subject will be searched for."
+  (gnus-Subject-search-subject t unread subject))
+
+(defun gnus-Subject-article-number ()
+  "Article number around point. If nothing, return current number."
+  (save-excursion
+    (beginning-of-line)
+    (if (looking-at ".[ \t]+\\([0-9]+\\):")
+	(string-to-int
+	 (buffer-substring (match-beginning 1) (match-end 1)))
+      ;; If search fail, return current article number.
+      gnus-current-article
+      )))
+
+(defun gnus-Subject-subject-string ()
+  "Return current subject string or nil if nothing."
+  (save-excursion
+    ;; It is possible to implement this function using
+    ;;  `gnus-Subject-article-number' and `gnus-newsgroup-headers'.
+    (beginning-of-line)
+    ;; We have to take care of hidden lines.
+    (if (looking-at ".[ \t]+[0-9]+:.\\[[^]\r\n]*\\][ \t]+\\([^\r\n]*\\)[\r\n]")
+	(buffer-substring (match-beginning 1) (match-end 1)))
+    ))
+
+(defun gnus-Subject-goto-subject (article)
+  "Move point to ARTICLE's subject."
+  (interactive
+   (list
+    (string-to-int
+     (completing-read "Article number: "
+		      (mapcar
+		       (function
+			(lambda (headers)
+			  (list
+			   (int-to-string (nntp-header-number headers)))))
+		       gnus-newsgroup-headers)
+		      nil 'require-match))))
+  (let ((current (point)))
+    (goto-char (point-min))
+    (or (and article (re-search-forward (format "^.[ \t]+%d:" article) nil t))
+	(progn (goto-char current) nil))
+    ))
+
+(defun gnus-Subject-recenter ()
+  "Center point in Subject mode window."
+  ;; Scroll window so as to cursor comes center of Subject mode window
+  ;;  only when article is displayed.
+  ;; Suggested by earle@mahendo.JPL.NASA.GOV (Greg Earle).
+  ;; Recenter only when requested.
+  ;; Suggested by popovich@park.cs.columbia.edu
+  (and gnus-auto-center-subject
+       (get-buffer-window gnus-Article-buffer)
+       (< (/ (- (window-height) 1) 2)
+	  (count-lines (point) (point-max)))
+       (recenter (/ (- (window-height) 2) 2))))
+
+;; Walking around Group mode buffer.
+
+(defun gnus-Subject-jump-to-group (newsgroup)
+  "Move point to NEWSGROUP in Group mode buffer."
+  ;; Keep update point of Group mode buffer if visible.
+  (if (eq (current-buffer)
+	  (get-buffer gnus-Group-buffer))
+      (save-window-excursion
+	;; Take care of tree window mode.
+	(if (get-buffer-window gnus-Group-buffer)
+	    (pop-to-buffer gnus-Group-buffer))
+	(gnus-Group-jump-to-group newsgroup))
+    (save-excursion
+      ;; Take care of tree window mode.
+      (if (get-buffer-window gnus-Group-buffer)
+	  (pop-to-buffer gnus-Group-buffer)
+	(set-buffer gnus-Group-buffer))
+      (gnus-Group-jump-to-group newsgroup))))
+
+(defun gnus-Subject-next-group (no-article)
+  "Exit current newsgroup and then select next unread newsgroup.
+If prefix argument NO-ARTICLE is non-nil, no article is selected initially."
+  (interactive "P")
+  ;; Make sure Group mode buffer point is on current newsgroup.
+  (gnus-Subject-jump-to-group gnus-newsgroup-name)
+  (let ((group (gnus-Subject-search-group)))
+    (if (null group)
+	(progn
+	  (message "Exiting %s..." gnus-newsgroup-name)  
+	  (gnus-Subject-exit)
+	  (message ""))
+      (message "Selecting %s..." group)
+      (gnus-Subject-exit t)		;Exit Subject mode temporary.
+      ;; We are now in Group mode buffer.
+      ;; Make sure Group mode buffer point is on GROUP.
+      (gnus-Subject-jump-to-group group)
+      (gnus-Subject-read-group group nil no-article)
+      (or (eq (current-buffer)
+	      (get-buffer gnus-Subject-buffer))
+	  (eq gnus-auto-select-next t)
+	  ;; Expected newsgroup has nothing to read since the articles
+	  ;; are marked as read by cross-referencing. So, try next
+	  ;; newsgroup. (Make sure we are in Group mode buffer now.)
+	  (and (eq (current-buffer)
+		   (get-buffer gnus-Group-buffer))
+	       (gnus-Group-group-name)
+	       (gnus-Subject-read-group
+		(gnus-Group-group-name) nil no-article))
+	  )
+      )))
+
+(defun gnus-Subject-prev-group (no-article)
+  "Exit current newsgroup and then select previous unread newsgroup.
+If prefix argument NO-ARTICLE is non-nil, no article is selected initially."
+  (interactive "P")
+  ;; Make sure Group mode buffer point is on current newsgroup.
+  (gnus-Subject-jump-to-group gnus-newsgroup-name)
+  (let ((group (gnus-Subject-search-group t)))
+    (if (null group)
+	(progn
+	  (message "Exiting %s..." gnus-newsgroup-name)  
+	  (gnus-Subject-exit)
+	  (message ""))
+      (message "Selecting %s..." group)
+      (gnus-Subject-exit t)		;Exit Subject mode temporary.
+      ;; We are now in Group mode buffer.
+      ;; We have to adjust point of Group mode buffer because current
+      ;; point is moved to next unread newsgroup by exiting.
+      (gnus-Subject-jump-to-group group)
+      (gnus-Subject-read-group group nil no-article)
+      (or (eq (current-buffer)
+	      (get-buffer gnus-Subject-buffer))
+	  (eq gnus-auto-select-next t)
+	  ;; Expected newsgroup has nothing to read since the articles
+	  ;; are marked as read by cross-referencing. So, try next
+	  ;; newsgroup. (Make sure we are in Group mode buffer now.)
+	  (and (eq (current-buffer)
+		   (get-buffer gnus-Group-buffer))
+	       (gnus-Subject-search-group t)
+	       (gnus-Subject-read-group
+		(gnus-Subject-search-group t) nil no-article))
+	  )
+      )))
+
+;; Walking around subject lines.
+
+(defun gnus-Subject-next-subject (n &optional unread)
+  "Go to next N'th subject line.
+If optional argument UNREAD is non-nil, only unread article is selected."
+  (interactive "p")
+  (while (and (> n 1)
+	      (gnus-Subject-search-forward unread))
+    (setq n (1- n)))
+  (cond ((gnus-Subject-search-forward unread)
+	 (gnus-Subject-recenter))
+	(unread
+	 (message "No more unread articles"))
+	(t
+	 (message "No more articles"))
+	))
+
+(defun gnus-Subject-next-unread-subject (n)
+  "Go to next N'th unread subject line."
+  (interactive "p")
+  (gnus-Subject-next-subject n t))
+
+(defun gnus-Subject-prev-subject (n &optional unread)
+  "Go to previous N'th subject line.
+If optional argument UNREAD is non-nil, only unread article is selected."
+  (interactive "p")
+  (while (and (> n 1)
+	      (gnus-Subject-search-backward unread))
+    (setq n (1- n)))
+  (cond ((gnus-Subject-search-backward unread)
+	 (gnus-Subject-recenter))
+	(unread
+	 (message "No more unread articles"))
+	(t
+	 (message "No more articles"))
+	))
+
+(defun gnus-Subject-prev-unread-subject (n)
+  "Go to previous N'th unread subject line."
+  (interactive "p")
+  (gnus-Subject-prev-subject n t))
+
+;; Walking around subject lines with displaying articles.
+
+(defun gnus-Subject-expand-window ()
+  "Expand Subject window to show headers full window."
+  (interactive)
+  (gnus-configure-windows 'ExpandSubject)
+  (pop-to-buffer gnus-Subject-buffer))
+
+(defun gnus-Subject-display-article (article &optional all-header)
+  "Display ARTICLE in Article buffer."
+  (if (null article)
+      nil
+    (gnus-configure-windows 'SelectArticle)
+    (pop-to-buffer gnus-Subject-buffer)
+    (gnus-Article-prepare article all-header)
+    (gnus-Subject-recenter)
+    (gnus-Subject-set-mode-line)
+    (run-hooks 'gnus-Select-article-hook)
+    ;; Successfully display article.
+    t
+    ))
+
+(defun gnus-Subject-select-article (&optional all-headers force)
+  "Select the current article.
+Optional argument ALL-HEADERS is non-nil, show all headers."
+  (let ((article (gnus-Subject-article-number)))
+    (if (or (null gnus-current-article)
+	    (/= article gnus-current-article)
+	    (and force (not (eq all-headers gnus-have-all-headers))))
+	;; The selected subject is different from that of the current article.
+	(gnus-Subject-display-article article all-headers)
+      (gnus-configure-windows 'SelectArticle)
+      (pop-to-buffer gnus-Subject-buffer))
+    ))
+
+(defun gnus-Subject-set-current-mark (&optional current-mark)
+  "Put `+' at the current article.
+Optional argument specifies CURRENT-MARK instead of `+'."
+  (save-excursion
+    (set-buffer gnus-Subject-buffer)
+    (let ((buffer-read-only nil))
+      (goto-char (point-min))
+      ;; First of all clear mark at last article.
+      (if (re-search-forward "^.[ \t]+[0-9]+:[^ \t]" nil t)
+	  (progn
+	    (delete-char -1)
+	    (insert " ")
+	    (goto-char (point-min))))
+      (if (re-search-forward (format "^.[ \t]+%d:" gnus-current-article) nil t)
+	  (progn
+	    (delete-char 1)
+	    (insert (or current-mark "+"))))
+      )))
+
+(defun gnus-Subject-next-article (unread &optional subject)
+  "Select article after current one.
+If argument UNREAD is non-nil, only unread article is selected."
+  (interactive "P")
+  (let ((header nil))
+    (cond ((gnus-Subject-display-article
+	    (gnus-Subject-search-forward unread subject)))
+	  ((and subject
+		gnus-auto-select-same
+		(gnus-set-difference gnus-newsgroup-unreads
+				     gnus-newsgroup-marked)
+		(memq this-command
+		      '(gnus-Subject-next-unread-article
+			gnus-Subject-next-page
+			gnus-Subject-kill-same-subject-and-select
+			;;gnus-Subject-next-article
+			;;gnus-Subject-next-same-subject
+			;;gnus-Subject-next-unread-same-subject
+			)))
+	   ;; Wrap article pointer if there are unread articles.
+	   ;; Hook function, such as gnus-Subject-rmail-digest, may
+	   ;; change current buffer, so need check.
+	   (let ((buffer (current-buffer))
+		 (last-point (point)))
+	     ;; No more articles with same subject, so jump to the first
+	     ;; unread article.
+	     (gnus-Subject-first-unread-article)
+	     ;;(and (eq buffer (current-buffer))
+	     ;;	(= (point) last-point)
+	     ;;	;; Ignore given SUBJECT, and try again.
+	     ;;	(gnus-Subject-next-article unread nil))
+	     (and (eq buffer (current-buffer))
+		  (< (point) last-point)
+		  (message "Wrapped"))
+	     ))
+	  ((and (not unread)
+		gnus-auto-extend-newsgroup
+		(setq header (gnus-more-header-forward)))
+	   ;; Extend to next article if possible.
+	   ;; Basic ideas by himacdonald@watdragon.waterloo.edu
+	   (gnus-extend-newsgroup header nil)
+	   ;; Threads feature must be turned off.
+	   (let ((buffer-read-only nil))
+	     (goto-char (point-max))
+	     (gnus-Subject-prepare-threads (list header) 0))
+	   (gnus-Subject-goto-article gnus-newsgroup-end))
+	  (t
+	   ;; Select next newsgroup automatically if requested.
+	   (let ((cmd (string-to-char (this-command-keys)))
+		 (group (gnus-Subject-search-group))
+		 (auto-select
+		  (and gnus-auto-select-next
+		       ;;(null (gnus-set-difference gnus-newsgroup-unreads
+		       ;;				gnus-newsgroup-marked))
+		       (memq this-command
+			     '(gnus-Subject-next-unread-article
+			       gnus-Subject-next-article
+			       gnus-Subject-next-page
+			       gnus-Subject-next-same-subject
+			       gnus-Subject-next-unread-same-subject
+			       gnus-Subject-kill-same-subject
+			       gnus-Subject-kill-same-subject-and-select
+			       ))
+		       ;; Ignore characters typed ahead.
+		       (not (input-pending-p))
+		       )))
+	     (message "No more%s articles%s"
+		      (if unread " unread" "")
+		      (if (and auto-select
+			       (not (eq gnus-auto-select-next 'quietly)))
+			  (if group
+			      (format " (Type %s to %s [%d])"
+				      (key-description (char-to-string cmd))
+				      group
+				      (nth 1 (gnus-gethash group
+							   gnus-unread-hashtb)))
+			    (format " (Type %s to exit %s)"
+				    (key-description (char-to-string cmd))
+				    gnus-newsgroup-name
+				    ))
+			""))
+	     ;; Select next unread newsgroup automagically.
+	     (cond ((and auto-select
+			 (eq gnus-auto-select-next 'quietly))
+		    ;; Select quietly.
+		    (gnus-Subject-next-group nil))
+		   (auto-select
+		    ;; Confirm auto selection.
+		    (let ((char (read-char)))
+		      (if (= char cmd)
+			  (gnus-Subject-next-group nil)
+			(setq unread-command-char char))))
+		   )
+	     ))
+	  )))
+
+(defun gnus-Subject-next-unread-article ()
+  "Select unread article after current one."
+  (interactive)
+  (gnus-Subject-next-article t (and gnus-auto-select-same
+				    (gnus-Subject-subject-string))))
+
+(defun gnus-Subject-prev-article (unread &optional subject)
+  "Select article before current one.
+If argument UNREAD is non-nil, only unread article is selected."
+  (interactive "P")
+  (let ((header nil))
+    (cond ((gnus-Subject-display-article
+	    (gnus-Subject-search-backward unread subject)))
+	  ((and subject
+		gnus-auto-select-same
+		(gnus-set-difference gnus-newsgroup-unreads
+				     gnus-newsgroup-marked)
+		(memq this-command
+		      '(gnus-Subject-prev-unread-article
+			;;gnus-Subject-prev-page
+			;;gnus-Subject-prev-article
+			;;gnus-Subject-prev-same-subject
+			;;gnus-Subject-prev-unread-same-subject
+			)))
+	   ;; Ignore given SUBJECT, and try again.
+	   (gnus-Subject-prev-article unread nil))
+	  (unread
+	   (message "No more unread articles"))
+	  ((and gnus-auto-extend-newsgroup
+		(setq header (gnus-more-header-backward)))
+	   ;; Extend to previous article if possible.
+	   ;; Basic ideas by himacdonald@watdragon.waterloo.edu
+	   (gnus-extend-newsgroup header t)
+	   (let ((buffer-read-only nil))
+	     (goto-char (point-min))
+	     (gnus-Subject-prepare-threads (list header) 0))
+	   (gnus-Subject-goto-article gnus-newsgroup-begin))
+	  (t
+	   (message "No more articles"))
+	  )))
+
+(defun gnus-Subject-prev-unread-article ()
+  "Select unred article before current one."
+  (interactive)
+  (gnus-Subject-prev-article t (and gnus-auto-select-same
+				    (gnus-Subject-subject-string))))
+
+(defun gnus-Subject-next-page (lines)
+  "Show next page of selected article.
+If end of artile, select next article.
+Argument LINES specifies lines to be scrolled up."
+  (interactive "P")
+  (let ((article (gnus-Subject-article-number))
+	(endp nil))
+    (if (or (null gnus-current-article)
+	    (/= article gnus-current-article))
+	;; Selected subject is different from current article's.
+	(gnus-Subject-display-article article)
+      (gnus-configure-windows 'SelectArticle)
+      (pop-to-buffer gnus-Subject-buffer)
+      (gnus-eval-in-buffer-window gnus-Article-buffer
+	(setq endp (gnus-Article-next-page lines)))
+      (cond ((and endp lines)
+	     (message "End of message"))
+	    ((and endp (null lines))
+	     (gnus-Subject-next-unread-article)))
+      )))
+
+(defun gnus-Subject-prev-page (lines)
+  "Show previous page of selected article.
+Argument LINES specifies lines to be scrolled down."
+  (interactive "P")
+  (let ((article (gnus-Subject-article-number)))
+    (if (or (null gnus-current-article)
+	    (/= article gnus-current-article))
+	;; Selected subject is different from current article's.
+	(gnus-Subject-display-article article)
+      (gnus-configure-windows 'SelectArticle)
+      (pop-to-buffer gnus-Subject-buffer)
+      (gnus-eval-in-buffer-window gnus-Article-buffer
+	(gnus-Article-prev-page lines))
+      )))
+
+(defun gnus-Subject-scroll-up (lines)
+  "Scroll up (or down) one line current article.
+Argument LINES specifies lines to be scrolled up (or down if negative)."
+  (interactive "p")
+  (gnus-Subject-select-article)
+  (gnus-eval-in-buffer-window gnus-Article-buffer
+    (cond ((> lines 0)
+	   (if (gnus-Article-next-page lines)
+	       (message "End of message")))
+	  ((< lines 0)
+	   (gnus-Article-prev-page (- 0 lines))))
+    ))
+
+(defun gnus-Subject-next-same-subject ()
+  "Select next article which has the same subject as current one."
+  (interactive)
+  (gnus-Subject-next-article nil (gnus-Subject-subject-string)))
+
+(defun gnus-Subject-prev-same-subject ()
+  "Select previous article which has the same subject as current one."
+  (interactive)
+  (gnus-Subject-prev-article nil (gnus-Subject-subject-string)))
+
+(defun gnus-Subject-next-unread-same-subject ()
+  "Select next unread article which has the same subject as current one."
+  (interactive)
+  (gnus-Subject-next-article t (gnus-Subject-subject-string)))
+
+(defun gnus-Subject-prev-unread-same-subject ()
+  "Select previous unread article which has the same subject as current one."
+  (interactive)
+  (gnus-Subject-prev-article t (gnus-Subject-subject-string)))
+
+(defun gnus-Subject-refer-parent-article (child)
+  "Refer parent article of current article.
+If a prefix argument CHILD is non-nil, go back to the child article
+using internally maintained articles history.
+NOTE: This command may not work with nnspool.el."
+  (interactive "P")
+  (gnus-Subject-select-article t t)	;Request all headers.
+  (let ((referenced-id nil))		;Message-id of parent or child article.
+    (if child
+	;; Go back to child article using history.
+	(gnus-Subject-refer-article nil)
+      (gnus-eval-in-buffer-window gnus-Article-buffer
+	;; Look for parent Message-ID.
+	;; We cannot use gnus-current-headers to get references
+	;; because we may be looking at parent or refered article.
+	(let ((references (gnus-fetch-field "References")))
+	  ;; Get the last message-id in the references.
+	  (and references
+	       (string-match "\\(<[^<>]+>\\)[^>]*\\'" references)
+	       (setq referenced-id
+		     (substring references
+				(match-beginning 1) (match-end 1))))
+	  ))
+      (if (stringp referenced-id)
+	  (gnus-Subject-refer-article referenced-id)
+	(error "No more parents"))
+      )))
+
+(defun gnus-Subject-refer-article (message-id)
+  "Refer article specified by MESSAGE-ID.
+If MESSAGE-ID is nil or an empty string, it is popped from an
+internally maintained articles history.
+NOTE: This command may not work with nnspool.el."
+  (interactive "sMessage-ID: ")
+  ;; Make sure that this command depends on the fact that article
+  ;; related information is not updated when an article is retrieved
+  ;; by Message-ID.
+  (gnus-Subject-select-article t t)	;Request all headers.
+  (if (and (stringp message-id)
+	   (> (length message-id) 0))
+      (gnus-eval-in-buffer-window gnus-Article-buffer
+	;; Construct the correct Message-ID if necessary.
+	;; Suggested by tale@pawl.rpi.edu.
+	(or (string-match "^<" message-id)
+	    (setq message-id (concat "<" message-id)))
+	(or (string-match ">$" message-id)
+	    (setq message-id (concat message-id ">")))
+	;; Push current message-id on history.
+	;; We cannot use gnus-current-headers to get current
+	;; message-id because we may be looking at parent or refered
+	;; article.
+	(let ((current (gnus-fetch-field "Message-ID")))
+	  (or (equal current message-id) ;Nothing to do.
+	      (equal current (car gnus-current-history))
+	      (setq gnus-current-history
+		    (cons current gnus-current-history)))
+	  ))
+    ;; Pop message-id from history.
+    (setq message-id (car gnus-current-history))
+    (setq gnus-current-history (cdr gnus-current-history)))
+  (if (stringp message-id)
+      ;; Retrieve article by message-id.  This may not work with nnspool.
+      (gnus-Article-prepare message-id t)
+    (error "No such references"))
+  )
+
+(defun gnus-Subject-next-digest (nth)
+  "Move to head of NTH next digested message."
+  (interactive "p")
+  (gnus-Subject-select-article)
+  (gnus-eval-in-buffer-window gnus-Article-buffer
+    (gnus-Article-next-digest (or nth 1))
+    ))
+
+(defun gnus-Subject-prev-digest (nth)
+  "Move to head of NTH previous digested message."
+  (interactive "p")
+  (gnus-Subject-select-article)
+  (gnus-eval-in-buffer-window gnus-Article-buffer
+    (gnus-Article-prev-digest (or nth 1))
+    ))
+
+(defun gnus-Subject-first-unread-article ()
+  "Select first unread article.  Return non-nil if successfully selected."
+  (interactive)
+  (let ((begin (point)))
+    (goto-char (point-min))
+    (if (re-search-forward "^ [ \t]+[0-9]+:" nil t)
+	(gnus-Subject-display-article (gnus-Subject-article-number))
+      ;; If there is no unread articles, stay there.
+      (goto-char begin)
+      ;;(gnus-Subject-display-article (gnus-Subject-article-number))
+      (message "No more unread articles")
+      nil
+      )
+    ))
+
+(defun gnus-Subject-isearch-article ()
+  "Do incremental search forward on current article."
+  (interactive)
+  (gnus-Subject-select-article)
+  (gnus-eval-in-buffer-window gnus-Article-buffer
+    (call-interactively 'isearch-forward)
+    ))
+
+(defun gnus-Subject-search-article-forward (regexp)
+  "Search for an article containing REGEXP forward.
+`gnus-Select-article-hook' is not called during the search."
+  (interactive
+   (list (read-string
+	  (concat "Search forward (regexp): "
+		  (if gnus-last-search-regexp
+		      (concat "(default " gnus-last-search-regexp ") "))))))
+  (if (string-equal regexp "")
+      (setq regexp (or gnus-last-search-regexp ""))
+    (setq gnus-last-search-regexp regexp))
+  (if (gnus-Subject-search-article regexp nil)
+      (gnus-eval-in-buffer-window gnus-Article-buffer
+	(recenter 0)
+	;;(sit-for 1)
+	)
+    (error "Search failed: \"%s\"" regexp)
+    ))
+
+(defun gnus-Subject-search-article-backward (regexp)
+  "Search for an article containing REGEXP backward.
+`gnus-Select-article-hook' is not called during the search."
+  (interactive
+   (list (read-string
+	  (concat "Search backward (regexp): "
+		  (if gnus-last-search-regexp
+		      (concat "(default " gnus-last-search-regexp ") "))))))
+  (if (string-equal regexp "")
+      (setq regexp (or gnus-last-search-regexp ""))
+    (setq gnus-last-search-regexp regexp))
+  (if (gnus-Subject-search-article regexp t)
+      (gnus-eval-in-buffer-window gnus-Article-buffer
+	(recenter 0)
+	;;(sit-for 1)
+	)
+    (error "Search failed: \"%s\"" regexp)
+    ))
+
+(defun gnus-Subject-search-article (regexp &optional backward)
+  "Search for an article containing REGEXP.
+Optional argument BACKWARD means do search for backward.
+`gnus-Select-article-hook' is not called during the search."
+  (let ((gnus-Select-article-hook nil)	;Disable hook.
+	(gnus-Mark-article-hook nil)	;Inhibit marking as read.
+	(re-search
+	 (if backward
+	     (function re-search-backward) (function re-search-forward)))
+	(found nil)
+	(last nil))
+    ;; Hidden thread subtrees must be searched for ,too.
+    (gnus-Subject-show-all-threads)
+    ;; First of all, search current article.
+    ;; We don't want to read article again from NNTP server nor reset
+    ;; current point.
+    (gnus-Subject-select-article)
+    (message "Searching article: %d..." gnus-current-article)
+    (setq last gnus-current-article)
+    (gnus-eval-in-buffer-window gnus-Article-buffer
+      (save-restriction
+	(widen)
+	;; Begin search from current point.
+	(setq found (funcall re-search regexp nil t))))
+    ;; Then search next articles.
+    (while (and (not found)
+		(gnus-Subject-display-article 
+		 (gnus-Subject-search-subject backward nil nil)))
+      (message "Searching article: %d..." gnus-current-article)
+      (gnus-eval-in-buffer-window gnus-Article-buffer
+	(save-restriction
+	  (widen)
+	  (goto-char (if backward (point-max) (point-min)))
+	  (setq found (funcall re-search regexp nil t)))
+	))
+    (message "")
+    ;; Adjust article pointer.
+    (or (eq last gnus-current-article)
+	(setq gnus-last-article last))
+    ;; Return T if found such article.
+    found
+    ))
+
+(defun gnus-Subject-execute-command (field regexp command &optional backward)
+  "If FIELD of article header matches REGEXP, execute COMMAND string.
+If FIELD is an empty string (or nil), entire article body is searched for.
+If optional (prefix) argument BACKWARD is non-nil, do backward instead."
+  (interactive
+   (list (let ((completion-ignore-case t))
+	   (completing-read "Field name: "
+			    '(("Number")("Subject")("From")
+			      ("Lines")("Date")("Id")
+			      ("Xref")("References"))
+			    nil 'require-match))
+	 (read-string "Regexp: ")
+	 (read-key-sequence "Command: ")
+	 current-prefix-arg))
+  ;; Hidden thread subtrees must be searched for ,too.
+  (gnus-Subject-show-all-threads)
+  ;; We don't want to change current point nor window configuration.
+  (save-excursion
+    (save-window-excursion
+      (message "Executing %s..." (key-description command))
+      ;; We'd like to execute COMMAND interactively so as to give arguments.
+      (gnus-execute field regexp
+		    (` (lambda ()
+			 (call-interactively '(, (key-binding command)))))
+		    backward)
+      (message "Executing %s... done" (key-description command)))))
+
+(defun gnus-Subject-beginning-of-article ()
+  "Go to beginning of article body"
+  (interactive)
+  (gnus-Subject-select-article)
+  (gnus-eval-in-buffer-window gnus-Article-buffer
+    (widen)
+    (beginning-of-buffer)
+    (if gnus-break-pages
+	(gnus-narrow-to-page))
+    ))
+
+(defun gnus-Subject-end-of-article ()
+  "Go to end of article body"
+  (interactive)
+  (gnus-Subject-select-article)
+  (gnus-eval-in-buffer-window gnus-Article-buffer
+    (widen)
+    (end-of-buffer)
+    (if gnus-break-pages
+	(gnus-narrow-to-page))
+    ))
+
+(defun gnus-Subject-goto-article (article &optional all-headers)
+  "Read ARTICLE if exists.
+Optional argument ALL-HEADERS means all headers are shown."
+  (interactive
+   (list
+    (string-to-int
+     (completing-read "Article number: "
+		      (mapcar
+		       (function
+			(lambda (headers)
+			  (list
+			   (int-to-string (nntp-header-number headers)))))
+		       gnus-newsgroup-headers)
+		      nil 'require-match))))
+  (if (gnus-Subject-goto-subject article)
+      (gnus-Subject-display-article article all-headers)))
+
+(defun gnus-Subject-goto-last-article ()
+  "Go to last subject line."
+  (interactive)
+  (if gnus-last-article
+      (gnus-Subject-goto-article gnus-last-article)))
+
+(defun gnus-Subject-show-article ()
+  "Force to show current article."
+  (interactive)
+  ;; The following is a trick to force to read the current article again.
+  (setq gnus-have-all-headers (not gnus-have-all-headers))
+  (gnus-Subject-select-article (not gnus-have-all-headers) t))
+
+(defun gnus-Subject-toggle-header (arg)
+  "Show original header if pruned header currently shown, or vice versa.
+With arg, show original header iff arg is positive."
+  (interactive "P")
+  ;; Variable gnus-show-all-headers must be NIL to toggle really.
+  (let ((gnus-show-all-headers nil)
+	(all-headers
+	 (if (null arg) (not gnus-have-all-headers)
+	   (> (prefix-numeric-value arg) 0))))
+    (gnus-Subject-select-article all-headers t)))
+
+(defun gnus-Subject-show-all-headers ()
+  "Show original article header."
+  (interactive)
+  (gnus-Subject-select-article t t))
+
+(defun gnus-Subject-stop-page-breaking ()
+  "Stop page breaking by linefeed temporary (Widen article buffer)."
+  (interactive)
+  (gnus-Subject-select-article)
+  (gnus-eval-in-buffer-window gnus-Article-buffer
+    (widen)))
+
+(defun gnus-Subject-kill-same-subject-and-select (unmark)
+  "Mark articles which has the same subject as read, and then select next.
+If argument UNMARK is positive, remove any kinds of marks.
+If argument UNMARK is negative, mark articles as unread instead."
+  (interactive "P")
+  (if unmark
+      (setq unmark (prefix-numeric-value unmark)))
+  (let ((count
+	 (gnus-Subject-mark-same-subject
+	  (gnus-Subject-subject-string) unmark)))
+    ;; Select next unread article. If auto-select-same mode, should
+    ;; select the first unread article.
+    (gnus-Subject-next-article t (and gnus-auto-select-same
+				      (gnus-Subject-subject-string)))
+    (message "%d articles are marked as %s"
+	     count (if unmark "unread" "read"))
+    ))
+
+(defun gnus-Subject-kill-same-subject (unmark)
+  "Mark articles which has the same subject as read. 
+If argument UNMARK is positive, remove any kinds of marks.
+If argument UNMARK is negative, mark articles as unread instead."
+  (interactive "P")
+  (if unmark
+      (setq unmark (prefix-numeric-value unmark)))
+  (let ((count
+	 (gnus-Subject-mark-same-subject
+	  (gnus-Subject-subject-string) unmark)))
+    ;; If marked as read, go to next unread subject.
+    (if (null unmark)
+	;; Go to next unread subject.
+	(gnus-Subject-next-subject 1 t))
+    (message "%d articles are marked as %s"
+	     count (if unmark "unread" "read"))
+    ))
+
+(defun gnus-Subject-mark-same-subject (subject &optional unmark)
+  "Mark articles with same SUBJECT as read, and return marked number.
+If optional argument UNMARK is positive, remove any kinds of marks.
+If optional argument UNMARK is negative, mark articles as unread instead."
+  (let ((count 1))
+    (save-excursion
+      (cond ((null unmark)
+	     (gnus-Subject-mark-as-read nil "K"))
+	    ((> unmark 0)
+	     (gnus-Subject-mark-as-unread nil t))
+	    (t
+	     (gnus-Subject-mark-as-unread)))
+      (while (and subject
+		  (gnus-Subject-search-forward nil subject))
+	(cond ((null unmark)
+	       (gnus-Subject-mark-as-read nil "K"))
+	      ((> unmark 0)
+	       (gnus-Subject-mark-as-unread nil t))
+	      (t
+	       (gnus-Subject-mark-as-unread)))
+	(setq count (1+ count))
+	))
+    ;; Hide killed thread subtrees.  Does not work properly always.
+    ;;(and (null unmark)
+    ;;     gnus-thread-hide-killed
+    ;;	   (gnus-Subject-hide-thread))
+    ;; Return number of articles marked as read.
+    count
+    ))
+
+(defun gnus-Subject-mark-as-unread-forward (count)
+  "Mark current article as unread, and then go forward.
+Argument COUNT specifies number of articles marked as unread."
+  (interactive "p")
+  (while (> count 0)
+    (gnus-Subject-mark-as-unread nil nil)
+    (gnus-Subject-next-subject 1 nil)
+    (setq count (1- count))))
+
+(defun gnus-Subject-mark-as-unread-backward (count)
+  "Mark current article as unread, and then go backward.
+Argument COUNT specifies number of articles marked as unread."
+  (interactive "p")
+  (while (> count 0)
+    (gnus-Subject-mark-as-unread nil nil)
+    (gnus-Subject-prev-subject 1 nil)
+    (setq count (1- count))))
+
+(defun gnus-Subject-mark-as-unread (&optional article clear-mark)
+  "Mark current article as unread.
+Optional first argument ARTICLE specifies article number to be
+marked as unread.  Optional second argument CLEAR-MARK removes
+any kind of mark."
+  (save-excursion
+    (set-buffer gnus-Subject-buffer)
+    ;; First of all, show hidden thread subtrees.
+    (gnus-Subject-show-thread)
+    (let* ((buffer-read-only nil)
+	   (current (gnus-Subject-article-number))
+	   (article (or article current)))
+      (gnus-mark-article-as-unread article clear-mark)
+      (if (or (eq article current)
+	      (gnus-Subject-goto-subject article))
+	  (progn
+	    (beginning-of-line)
+	    (delete-char 1)
+	    (insert (if clear-mark " " "-"))))
+      )))
+
+(defun gnus-Subject-mark-as-read-forward (count)
+  "Mark current article as read, and then go forward.
+Argument COUNT specifies number of articles marked as read"
+  (interactive "p")
+  (while (> count 0)
+    (gnus-Subject-mark-as-read)
+    (gnus-Subject-next-subject 1 'unread-only)
+    (setq count (1- count))))
+
+(defun gnus-Subject-mark-as-read-backward (count)
+  "Mark current article as read, and then go backward.
+Argument COUNT specifies number of articles marked as read"
+  (interactive "p")
+  (while (> count 0)
+    (gnus-Subject-mark-as-read)
+    (gnus-Subject-prev-subject 1 'unread-only)
+    (setq count (1- count))))
+
+(defun gnus-Subject-mark-as-read (&optional article mark)
+  "Mark current article as read.
+Optional first argument ARTICLE specifies article number to be marked as read.
+Optional second argument MARK specifies a string inserted at beginning of line.
+Any kind of string (length 1) except for a space and `-' is ok."
+  (save-excursion
+    (set-buffer gnus-Subject-buffer)
+    ;; First of all, show hidden thread subtrees.
+    (gnus-Subject-show-thread)
+    (let* ((buffer-read-only nil)
+	   (mark (or mark "D"))		;Default mark is `D'.
+	   (current (gnus-Subject-article-number))
+	   (article (or article current)))
+      (gnus-mark-article-as-read article)
+      (if (or (eq article current)
+	      (gnus-Subject-goto-subject article))
+	  (progn
+	    (beginning-of-line)
+	    (delete-char 1)
+	    (insert mark)))
+      )))
+
+(defun gnus-Subject-clear-mark-forward (count)
+  "Remove current article's mark, and go forward.
+Argument COUNT specifies number of articles unmarked"
+  (interactive "p")
+  (while (> count 0)
+    (gnus-Subject-mark-as-unread nil t)
+    (gnus-Subject-next-subject 1 nil)
+    (setq count (1- count))))
+
+(defun gnus-Subject-clear-mark-backward (count)
+  "Remove current article's mark, and go backward.
+Argument COUNT specifies number of articles unmarked"
+  (interactive "p")
+  (while (> count 0)
+    (gnus-Subject-mark-as-unread nil t)
+    (gnus-Subject-prev-subject 1 nil)
+    (setq count (1- count))))
+
+(defun gnus-Subject-delete-marked-as-read ()
+  "Delete lines which are marked as read."
+  (interactive)
+  (if gnus-newsgroup-unreads
+      (let ((buffer-read-only nil))
+	(save-excursion
+	  (goto-char (point-min))
+	  (delete-non-matching-lines "^[ ---]"))
+	;; Adjust point.
+	(if (eobp)
+	    (gnus-Subject-prev-subject 1)
+	  (beginning-of-line)
+	  (search-forward ":" nil t)))
+    ;; It is not so good idea to make the buffer empty.
+    (message "All articles are marked as read")
+    ))
+
+(defun gnus-Subject-delete-marked-with (marks)
+  "Delete lines which are marked with MARKS (e.g. \"DK\")."
+  (interactive "sMarks: ")
+  (let ((buffer-read-only nil))
+    (save-excursion
+      (goto-char (point-min))
+      (delete-matching-lines (concat "^[" marks "]")))
+    ;; Adjust point.
+    (or (zerop (buffer-size))
+	(if (eobp)
+	    (gnus-Subject-prev-subject 1)
+	  (beginning-of-line)
+	  (search-forward ":" nil t)))
+    ))
+
+;; Thread-based commands.
+
+(defun gnus-Subject-toggle-threads (arg)
+  "Toggle showing conversation threads.
+With arg, turn showing conversation threads on iff arg is positive."
+  (interactive "P")
+  (let ((current (gnus-Subject-article-number)))
+    (setq gnus-show-threads
+	  (if (null arg) (not gnus-show-threads)
+	    (> (prefix-numeric-value arg) 0)))
+    (gnus-Subject-prepare)
+    (gnus-Subject-goto-subject current)
+    ))
+
+(defun gnus-Subject-show-all-threads ()
+  "Show all thread subtrees."
+  (interactive)
+  (if gnus-show-threads
+      (save-excursion
+	(let ((buffer-read-only nil))
+	  (subst-char-in-region (point-min) (point-max) ?\^M ?\n t)
+	  ))))
+
+(defun gnus-Subject-show-thread ()
+  "Show thread subtrees."
+  (interactive)
+  (if gnus-show-threads
+      (save-excursion
+	(let ((buffer-read-only nil))
+	  (subst-char-in-region (progn
+				  (beginning-of-line) (point))
+				(progn
+				  (end-of-line) (point))
+				?\^M ?\n t)
+	  ))))
+
+(defun gnus-Subject-hide-all-threads ()
+  "Hide all thread subtrees."
+  (interactive)
+  (if gnus-show-threads
+      (save-excursion
+	;; Adjust cursor point.
+	(goto-char (point-min))
+	(search-forward ":" nil t)
+	(let ((level (current-column)))
+	  (gnus-Subject-hide-thread)
+	  (while (gnus-Subject-search-forward)
+	    (and (>= level (current-column))
+		 (gnus-Subject-hide-thread)))
+	  ))))
+
+(defun gnus-Subject-hide-thread ()
+  "Hide thread subtrees."
+  (interactive)
+  (if gnus-show-threads
+      (save-excursion
+	;; Adjust cursor point.
+	(beginning-of-line)
+	(search-forward ":" nil t)
+	(let ((buffer-read-only nil)
+	      (init (point))
+	      (last (point))
+	      (level (current-column)))
+	  (while (and (gnus-Subject-search-forward)
+		      (< level (current-column)))
+	    ;; Interested in lower levels.
+	    (if (< level (current-column))
+		(progn
+		  (setq last (point))
+		  ))
+	    )
+	  (subst-char-in-region init last ?\n ?\^M t)
+	  ))))
+
+(defun gnus-Subject-next-thread (n)
+  "Go to the same level next thread.
+Argument N specifies the number of threads."
+  (interactive "p")
+  ;; Adjust cursor point.
+  (beginning-of-line)
+  (search-forward ":" nil t)
+  (let ((init (point))
+	(last (point))
+	(level (current-column)))
+    (while (and (> n 0)
+		(gnus-Subject-search-forward)
+		(<= level (current-column)))
+      ;; We have to skip lower levels.
+      (if (= level (current-column))
+	  (progn
+	    (setq last (point))
+	    (setq n (1- n))
+	    ))
+      )
+    ;; Return non-nil if successfully move to the next.
+    (prog1 (not (= init last))
+      (goto-char last))
+    ))
+
+(defun gnus-Subject-prev-thread (n)
+  "Go to the same level previous thread.
+Argument N specifies the number of threads."
+  (interactive "p")
+  ;; Adjust cursor point.
+  (beginning-of-line)
+  (search-forward ":" nil t)
+  (let ((init (point))
+	(last (point))
+	(level (current-column)))
+    (while (and (> n 0)
+		(gnus-Subject-search-backward)
+		(<= level (current-column)))
+      ;; We have to skip lower levels.
+      (if (= level (current-column))
+	  (progn
+	    (setq last (point))
+	    (setq n (1- n))
+	    ))
+      )
+    ;; Return non-nil if successfully move to the previous.
+    (prog1 (not (= init last))
+      (goto-char last))
+    ))
+
+(defun gnus-Subject-down-thread (d)
+  "Go downward current thread.
+Argument D specifies the depth goes down."
+  (interactive "p")
+  ;; Adjust cursor point.
+  (beginning-of-line)
+  (search-forward ":" nil t)
+  (let ((last (point))
+	(level (current-column)))
+    (while (and (> d 0)
+		(gnus-Subject-search-forward)
+		(<= level (current-column))) ;<= can be <.  Which do you like?
+      ;; We have to skip the same levels.
+      (if (< level (current-column))
+	  (progn
+	    (setq last (point))
+	    (setq level (current-column))
+	    (setq d (1- d))
+	    ))
+      )
+    (goto-char last)
+    ))
+
+(defun gnus-Subject-up-thread (d)
+  "Go upward current thread.
+Argument D specifies the depth goes up."
+  (interactive "p")
+  ;; Adjust cursor point.
+  (beginning-of-line)
+  (search-forward ":" nil t)
+  (let ((last (point))
+	(level (current-column)))
+    (while (and (> d 0)
+		(gnus-Subject-search-backward))
+      ;; We have to skip the same levels.
+      (if (> level (current-column))
+	  (progn
+	    (setq last (point))
+	    (setq level (current-column))
+	    (setq d (1- d))
+	    ))
+      )
+    (goto-char last)
+    ))
+
+(defun gnus-Subject-kill-thread (unmark)
+  "Mark articles under current thread as read.
+If argument UNMARK is positive, remove any kinds of marks.
+If argument UNMARK is negative, mark articles as unread instead."
+  (interactive "P")
+  (if unmark
+      (setq unmark (prefix-numeric-value unmark)))
+  ;; Adjust cursor point.
+  (beginning-of-line)
+  (search-forward ":" nil t)
+  (save-excursion
+    (let ((level (current-column)))
+      ;; Mark current article.
+      (cond ((null unmark)
+	     (gnus-Subject-mark-as-read nil "K"))
+	    ((> unmark 0)
+	     (gnus-Subject-mark-as-unread nil t))
+	    (t
+	     (gnus-Subject-mark-as-unread))
+	    )
+      ;; Mark following articles.
+      (while (and (gnus-Subject-search-forward)
+		  (< level (current-column)))
+	(cond ((null unmark)
+	       (gnus-Subject-mark-as-read nil "K"))
+	      ((> unmark 0)
+	       (gnus-Subject-mark-as-unread nil t))
+	      (t
+	       (gnus-Subject-mark-as-unread))
+	      ))
+      ))
+  ;; Hide killed subtrees.
+  (and (null unmark)
+       gnus-thread-hide-killed
+       (gnus-Subject-hide-thread))
+  ;; If marked as read, go to next unread subject.
+  (if (null unmark)
+      ;; Go to next unread subject.
+      (gnus-Subject-next-subject 1 t))
+  )
+
+(defun gnus-Subject-toggle-truncation (arg)
+  "Toggle truncation of subject lines.
+With ARG, turn line truncation on iff ARG is positive."
+  (interactive "P")
+  (setq truncate-lines
+	(if (null arg) (not truncate-lines)
+	  (> (prefix-numeric-value arg) 0)))
+  (redraw-display))
+
+(defun gnus-Subject-sort-by-number (reverse)
+  "Sort subject display buffer by article number.
+Argument REVERSE means reverse order."
+  (interactive "P")
+  (gnus-Subject-sort-subjects
+   (function
+    (lambda (a b)
+      (< (nntp-header-number a) (nntp-header-number b))))
+   reverse
+   ))
+
+(defun gnus-Subject-sort-by-author (reverse)
+  "Sort subject display buffer by author name alphabetically.
+If case-fold-search is non-nil, case of letters is ignored.
+Argument REVERSE means reverse order."
+  (interactive "P")
+  (gnus-Subject-sort-subjects
+   (function
+    (lambda (a b)
+      (gnus-string-lessp (nntp-header-from a) (nntp-header-from b))))
+   reverse
+   ))
+
+(defun gnus-Subject-sort-by-subject (reverse)
+  "Sort subject display buffer by subject alphabetically. `Re:'s are ignored.
+If case-fold-search is non-nil, case of letters is ignored.
+Argument REVERSE means reverse order."
+  (interactive "P")
+  (gnus-Subject-sort-subjects
+   (function
+    (lambda (a b)
+      (gnus-string-lessp
+       (gnus-simplify-subject (nntp-header-subject a) 're-only)
+       (gnus-simplify-subject (nntp-header-subject b) 're-only))))
+   reverse
+   ))
+
+(defun gnus-Subject-sort-by-date (reverse)
+  "Sort subject display buffer by posted date.
+Argument REVERSE means reverse order."
+  (interactive "P")
+  (gnus-Subject-sort-subjects
+   (function
+    (lambda (a b)
+      (gnus-date-lessp (nntp-header-date a) (nntp-header-date b))))
+   reverse
+   ))
+
+(defun gnus-Subject-sort-subjects (predicate &optional reverse)
+  "Sort subject display buffer by PREDICATE.
+Optional argument REVERSE means reverse order."
+  (let ((current (gnus-Subject-article-number)))
+    (gnus-sort-headers predicate reverse)
+    (gnus-Subject-prepare)
+    (gnus-Subject-goto-subject current)
+    ))
+
+(defun gnus-Subject-reselect-current-group (show-all)
+  "Once exit and then reselect the current newsgroup.
+Prefix argument SHOW-ALL means to select all articles."
+  (interactive "P")
+  (let ((current-subject (gnus-Subject-article-number)))
+    (gnus-Subject-exit t)
+    ;; We have to adjust the point of Group mode buffer because the
+    ;; current point was moved to the next unread newsgroup by
+    ;; exiting.
+    (gnus-Subject-jump-to-group gnus-newsgroup-name)
+    (gnus-Group-read-group show-all t)
+    (gnus-Subject-goto-subject current-subject)
+    ))
+
+(defun gnus-Subject-caesar-message (rotnum)
+  "Caesar rotates all letters of current message by 13/47 places.
+With prefix arg, specifies the number of places to rotate each letter forward.
+Caesar rotates Japanese letters by 47 places in any case."
+  (interactive "P")
+  (gnus-Subject-select-article)
+  (gnus-overload-functions)
+  (gnus-eval-in-buffer-window gnus-Article-buffer
+    (save-restriction
+      (widen)
+      ;; We don't want to jump to the beginning of the message.
+      ;; `save-excursion' does not do its job.
+      (move-to-window-line 0)
+      (let ((last (point)))
+	(news-caesar-buffer-body rotnum)
+	(goto-char last)
+	(recenter 0)
+	))
+    ))
+
+(defun gnus-Subject-rmail-digest ()
+  "Run RMAIL on current digest article.
+`gnus-Select-digest-hook' will be called with no arguments, if that
+value is non-nil.  It is possible to modify the article so that Rmail
+can work with it.
+
+`gnus-Rmail-digest-hook' will be called with no arguments, if that value
+is non-nil.  The hook is intended to customize Rmail mode."
+  (interactive)
+  (gnus-Subject-select-article)
+  (require 'rmail)
+  (let ((artbuf gnus-Article-buffer)
+	(digbuf (get-buffer-create gnus-Digest-buffer))
+	(mail-header-separator ""))
+    (set-buffer digbuf)
+    (buffer-flush-undo (current-buffer))
+    (setq buffer-read-only nil)
+    (erase-buffer)
+    (insert-buffer-substring artbuf)
+    (run-hooks 'gnus-Select-digest-hook)
+    (gnus-convert-article-to-rmail)
+    (goto-char (point-min))
+    ;; Rmail initializations.
+    (rmail-insert-rmail-file-header)
+    (rmail-mode)
+    (rmail-set-message-counters)
+    (rmail-show-message)
+    (condition-case ()
+	(progn
+	  (undigestify-rmail-message)
+	  (rmail-expunge)		;Delete original message.
+	  ;; File name is meaningless but `save-buffer' requires it.
+	  (setq buffer-file-name "GNUS Digest")
+	  (setq mode-line-buffer-identification
+		(concat "Digest: "
+			(nntp-header-subject gnus-current-headers)))
+	  ;; There is no need to write this buffer to a file.
+	  (make-local-variable 'write-file-hooks)
+	  (setq write-file-hooks
+		(list (function
+		       (lambda ()
+			 (set-buffer-modified-p nil)
+			 (message "(No changes need to be saved)")
+			 'no-need-to-write-this-buffer))))
+	  ;; Default file name saving digest messages.
+	  (setq rmail-last-rmail-file
+		(funcall gnus-rmail-save-name
+			 gnus-newsgroup-name
+			 gnus-current-headers
+			 gnus-newsgroup-last-rmail
+			 ))
+	  (setq rmail-last-file
+		(funcall gnus-mail-save-name
+			 gnus-newsgroup-name
+			 gnus-current-headers
+			 gnus-newsgroup-last-mail
+			 ))
+	  ;; Prevent generating new buffer named ***<N> each time.
+	  (setq rmail-summary-buffer
+		(get-buffer-create gnus-Digest-summary-buffer))
+	  (run-hooks 'gnus-Rmail-digest-hook)
+	  ;; Take all windows safely.
+	  (gnus-configure-windows '(1 0 0))
+	  (pop-to-buffer gnus-Group-buffer)
+	  ;; Use Subject and Article windows for Digest summary and
+	  ;; Digest buffers.
+	  (if gnus-digest-show-summary
+	      (let ((gnus-Subject-buffer gnus-Digest-summary-buffer)
+		    (gnus-Article-buffer gnus-Digest-buffer))
+		(gnus-configure-windows 'SelectArticle)
+		(pop-to-buffer gnus-Digest-buffer)
+		(rmail-summary)
+		(pop-to-buffer gnus-Digest-summary-buffer)
+		(message (substitute-command-keys
+			  "Type \\[rmail-summary-quit] to return to GNUS")))
+	    (let ((gnus-Subject-buffer gnus-Digest-buffer))
+	      (gnus-configure-windows 'ExpandSubject)
+	      (pop-to-buffer gnus-Digest-buffer)
+	      (message (substitute-command-keys
+			"Type \\[rmail-quit] to return to GNUS")))
+	    )
+	  ;; Move the buffers to the end of buffer list.
+	  (bury-buffer gnus-Article-buffer)
+	  (bury-buffer gnus-Group-buffer)
+	  (bury-buffer gnus-Digest-summary-buffer)
+	  (bury-buffer gnus-Digest-buffer))
+      (error (set-buffer-modified-p nil)
+	     (kill-buffer digbuf)
+	     ;; This command should not signal an error because the
+	     ;; command is called from hooks.
+	     (ding) (message "Article is not a digest")))
+    ))
+
+(defun gnus-Subject-save-article ()
+  "Save this article using default saver function.
+Variable `gnus-default-article-saver' specifies the saver function."
+  (interactive)
+  (gnus-Subject-select-article
+   (not (null gnus-save-all-headers)) gnus-save-all-headers)
+  (if gnus-default-article-saver
+      (call-interactively gnus-default-article-saver)
+    (error "No default saver is defined.")))
+
+(defun gnus-Subject-save-in-rmail (&optional filename)
+  "Append this article to Rmail file.
+Optional argument FILENAME specifies file name.
+Directory to save to is default to `gnus-article-save-directory' which
+is initialized from the SAVEDIR environment variable."
+  (interactive)
+  (gnus-Subject-select-article
+   (not (null gnus-save-all-headers)) gnus-save-all-headers)
+  (gnus-eval-in-buffer-window gnus-Article-buffer
+    (save-excursion
+      (save-restriction
+	(widen)
+	(let ((default-name
+		(funcall gnus-rmail-save-name
+			 gnus-newsgroup-name
+			 gnus-current-headers
+			 gnus-newsgroup-last-rmail
+			 )))
+	  (or filename
+	      (setq filename
+		    (read-file-name
+		     (concat "Save article in Rmail file: (default "
+			     (file-name-nondirectory default-name)
+			     ") ")
+		     (file-name-directory default-name)
+		     default-name)))
+	  (gnus-make-directory (file-name-directory filename))
+	  (gnus-output-to-rmail filename)
+	  ;; Remember the directory name to save articles.
+	  (setq gnus-newsgroup-last-rmail filename)
+	  )))
+    ))
+
+(defun gnus-Subject-save-in-mail (&optional filename)
+  "Append this article to Unix mail file.
+Optional argument FILENAME specifies file name.
+Directory to save to is default to `gnus-article-save-directory' which
+is initialized from the SAVEDIR environment variable."
+  (interactive)
+  (gnus-Subject-select-article
+   (not (null gnus-save-all-headers)) gnus-save-all-headers)
+  (gnus-eval-in-buffer-window gnus-Article-buffer
+    (save-excursion
+      (save-restriction
+	(widen)
+	(let ((default-name
+		(funcall gnus-mail-save-name
+			 gnus-newsgroup-name
+			 gnus-current-headers
+			 gnus-newsgroup-last-mail
+			 )))
+	  (or filename
+	      (setq filename
+		    (read-file-name
+		     (concat "Save article in Unix mail file: (default "
+			     (file-name-nondirectory default-name)
+			     ") ")
+		     (file-name-directory default-name)
+		     default-name)))
+	  (gnus-make-directory (file-name-directory filename))
+	  (rmail-output filename)
+	  ;; Remember the directory name to save articles.
+	  (setq gnus-newsgroup-last-mail filename)
+	  )))
+    ))
+
+(defun gnus-Subject-save-in-file (&optional filename)
+  "Append this article to file.
+Optional argument FILENAME specifies file name.
+Directory to save to is default to `gnus-article-save-directory' which
+is initialized from the SAVEDIR environment variable."
+  (interactive)
+  (gnus-Subject-select-article
+   (not (null gnus-save-all-headers)) gnus-save-all-headers)
+  (gnus-eval-in-buffer-window gnus-Article-buffer
+    (save-excursion
+      (save-restriction
+	(widen)
+	(let ((default-name
+		(funcall gnus-file-save-name
+			 gnus-newsgroup-name
+			 gnus-current-headers
+			 gnus-newsgroup-last-file
+			 )))
+	  (or filename
+	      (setq filename
+		    (read-file-name
+		     (concat "Save article in file: (default "
+			     (file-name-nondirectory default-name)
+			     ") ")
+		     (file-name-directory default-name)
+		     default-name)))
+	  (gnus-make-directory (file-name-directory filename))
+	  (gnus-output-to-file filename)
+	  ;; Remember the directory name to save articles.
+	  (setq gnus-newsgroup-last-file filename)
+	  )))
+    ))
+
+(defun gnus-Subject-save-in-folder (&optional folder)
+  "Save this article to MH folder (using `rcvstore' in MH library).
+Optional argument FOLDER specifies folder name."
+  (interactive)
+  (gnus-Subject-select-article
+   (not (null gnus-save-all-headers)) gnus-save-all-headers)
+  (gnus-eval-in-buffer-window gnus-Article-buffer
+    (save-restriction
+      (widen)
+      ;; Thanks to yuki@flab.Fujitsu.JUNET and ohm@kaba.junet.
+      (mh-find-path)
+      (let ((folder
+	     (or folder
+		 (mh-prompt-for-folder "Save article in"
+				       (funcall gnus-folder-save-name
+						gnus-newsgroup-name
+						gnus-current-headers
+						gnus-newsgroup-last-folder
+						)
+				       t
+				       )))
+	    (errbuf (get-buffer-create " *GNUS rcvstore*")))
+	(unwind-protect
+	    (call-process-region (point-min) (point-max)
+				 (expand-file-name "rcvstore" mh-lib)
+				 nil errbuf nil folder)
+	  (set-buffer errbuf)
+	  (if (zerop (buffer-size))
+	      (message "Article saved in folder: %s" folder)
+	    (message "%s" (buffer-string)))
+	  (kill-buffer errbuf)
+	  (setq gnus-newsgroup-last-folder folder))
+	))
+    ))
+
+(defun gnus-Subject-pipe-output ()
+  "Pipe this article to subprocess."
+  (interactive)
+  ;; Ignore `gnus-save-all-headers' since this is not save command.
+  (gnus-Subject-select-article)
+  (gnus-eval-in-buffer-window gnus-Article-buffer
+    (save-restriction
+      (widen)
+      (let ((command (read-string "Shell command on article: "
+				  gnus-last-shell-command)))
+	(if (string-equal command "")
+	    (setq command gnus-last-shell-command))
+	(shell-command-on-region (point-min) (point-max) command nil)
+	(setq gnus-last-shell-command command)
+	))
+    ))
+
+(defun gnus-Subject-catch-up (all &optional quietly)
+  "Mark all articles not marked as unread in this newsgroup as read.
+If prefix argument ALL is non-nil, all articles are marked as read."
+  (interactive "P")
+  (if (or quietly
+	  (y-or-n-p
+	   (if all
+	       "Do you really want to mark everything as read? "
+	     "Delete all articles not marked as unread? ")))
+      (let ((unmarked
+	     (gnus-set-difference gnus-newsgroup-unreads
+				  (if (not all) gnus-newsgroup-marked))))
+        (message "")			;Erase "Yes or No" question.
+	(while unmarked
+          (gnus-Subject-mark-as-read (car unmarked) "C")
+	  (setq unmarked (cdr unmarked))
+	  ))
+    ))
+
+(defun gnus-Subject-catch-up-all (&optional quietly)
+  "Mark all articles in this newsgroup as read."
+  (interactive)
+  (gnus-Subject-catch-up t quietly))
+
+(defun gnus-Subject-catch-up-and-exit (all &optional quietly)
+  "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 (or quietly
+	  (y-or-n-p
+	   (if all
+	       "Do you really want to mark everything as read? "
+	     "Delete all articles not marked as unread? ")))
+      (let ((unmarked
+             (gnus-set-difference gnus-newsgroup-unreads
+                                  (if (not all) gnus-newsgroup-marked))))
+        (message "")			;Erase "Yes or No" question.
+	(while unmarked
+          (gnus-mark-article-as-read (car unmarked))
+	  (setq unmarked (cdr unmarked)))
+	;; Select next newsgroup or exit.
+	(cond ((eq gnus-auto-select-next 'quietly)
+	       ;; Select next newsgroup quietly.
+	       (gnus-Subject-next-group nil))
+	      (t
+	       (gnus-Subject-exit)))
+	)))
+
+(defun gnus-Subject-catch-up-all-and-exit (&optional quietly)
+  "Mark all articles in this newsgroup as read, and then exit."
+  (interactive)
+  (gnus-Subject-catch-up-and-exit t quietly))
+
+(defun gnus-Subject-edit-global-kill ()
+  "Edit a global KILL file."
+  (interactive)
+  (setq gnus-current-kill-article (gnus-Subject-article-number))
+  (gnus-Kill-file-edit-file nil)	;Nil stands for global KILL file.
+  (message
+   (substitute-command-keys
+    "Editing a global KILL file (Type \\[gnus-Kill-file-exit] to exit)")))
+
+(defun gnus-Subject-edit-local-kill ()
+  "Edit a local KILL file applied to the current newsgroup."
+  (interactive)
+  (setq gnus-current-kill-article (gnus-Subject-article-number))
+  (gnus-Kill-file-edit-file gnus-newsgroup-name)
+  (message
+   (substitute-command-keys
+    "Editing a local KILL file (Type \\[gnus-Kill-file-exit] to exit)")))
+
+(defun gnus-Subject-exit (&optional temporary)
+  "Exit reading current newsgroup, and then return to group selection mode.
+gnus-Exit-group-hook is called with no arguments if that value is non-nil."
+  (interactive)
+  (let ((updated nil)
+	(gnus-newsgroup-headers gnus-newsgroup-headers)
+	(gnus-newsgroup-unreads gnus-newsgroup-unreads)
+	(gnus-newsgroup-unselected gnus-newsgroup-unselected)
+	(gnus-newsgroup-marked gnus-newsgroup-marked))
+    ;; Important internal variables are save, so we can reenter
+    ;; Subject Mode buffer even if hook changes them.
+    (run-hooks 'gnus-Exit-group-hook)
+    (gnus-update-unread-articles gnus-newsgroup-name
+				 (append gnus-newsgroup-unselected
+					 gnus-newsgroup-unreads)
+				 gnus-newsgroup-marked)
+    ;; T means ignore unsubscribed newsgroups.
+    (if gnus-use-cross-reference
+	(setq updated
+	      (gnus-mark-as-read-by-xref gnus-newsgroup-name
+					 gnus-newsgroup-headers
+					 gnus-newsgroup-unreads
+					 (eq gnus-use-cross-reference t)
+					 )))
+    ;; Do not switch windows but change the buffer to work.
+    (set-buffer gnus-Group-buffer)
+    ;; Update cross referenced group info.
+    (while updated
+      (gnus-Group-update-group (car updated) t) ;Ignore invisible group.
+      (setq updated (cdr updated)))
+    (gnus-Group-update-group gnus-newsgroup-name))
+  ;; Make sure where I was, and go to next newsgroup.
+  (gnus-Group-jump-to-group gnus-newsgroup-name)
+  (gnus-Group-next-unread-group 1)
+  (if temporary
+      ;; If exiting temporary, caller should adjust Group mode
+      ;; buffer point by itself.
+      nil				;Nothing to do.
+    ;; Return to Group mode buffer.
+    (if (get-buffer gnus-Subject-buffer)
+	(bury-buffer gnus-Subject-buffer))
+    (if (get-buffer gnus-Article-buffer)
+	(bury-buffer gnus-Article-buffer))
+    (gnus-configure-windows 'ExitNewsgroup)
+    (pop-to-buffer gnus-Group-buffer)))
+
+(defun gnus-Subject-quit ()
+  "Quit reading current newsgroup without updating read article info."
+  (interactive)
+  (if (y-or-n-p "Do you really wanna quit reading this group? ")
+      (progn
+	(message "")			;Erase "Yes or No" question.
+	;; Return to Group selection mode.
+	(if (get-buffer gnus-Subject-buffer)
+	    (bury-buffer gnus-Subject-buffer))
+	(if (get-buffer gnus-Article-buffer)
+	    (bury-buffer gnus-Article-buffer))
+	(gnus-configure-windows 'ExitNewsgroup)
+	(pop-to-buffer gnus-Group-buffer)
+	(gnus-Group-jump-to-group gnus-newsgroup-name) ;Make sure where I was.
+	(gnus-Group-next-group 1)	;(gnus-Group-next-unread-group 1)
+	)))
+
+(defun gnus-Subject-describe-briefly ()
+  "Describe Subject mode commands briefly."
+  (interactive)
+  (message
+   (concat
+    (substitute-command-keys "\\[gnus-Subject-next-page]:Select  ")
+    (substitute-command-keys "\\[gnus-Subject-next-unread-article]:Forward  ")
+    (substitute-command-keys "\\[gnus-Subject-prev-unread-article]:Backward  ")
+    (substitute-command-keys "\\[gnus-Subject-exit]:Exit  ")
+    (substitute-command-keys "\\[gnus-Info-find-node]:Run Info  ")
+    (substitute-command-keys "\\[gnus-Subject-describe-briefly]:This help")
+    )))
+
+
+;;;
+;;; GNUS Article Mode
+;;;
+
+(if gnus-Article-mode-map
+    nil
+  (setq gnus-Article-mode-map (make-keymap))
+  (suppress-keymap gnus-Article-mode-map)
+  (define-key gnus-Article-mode-map " " 'gnus-Article-next-page)
+  (define-key gnus-Article-mode-map "\177" 'gnus-Article-prev-page)
+  (define-key gnus-Article-mode-map "r" 'gnus-Article-refer-article)
+  (define-key gnus-Article-mode-map "o" 'gnus-Article-pop-article)
+  (define-key gnus-Article-mode-map "h" 'gnus-Article-show-subjects)
+  (define-key gnus-Article-mode-map "s" 'gnus-Article-show-subjects)
+  (define-key gnus-Article-mode-map "?" 'gnus-Article-describe-briefly)
+  (define-key gnus-Article-mode-map "\C-c\C-i" 'gnus-Info-find-node))
+
+(defun gnus-Article-mode ()
+  "Major mode for browsing through an article.
+All normal editing commands are turned off.
+Instead, these commands are available:
+\\{gnus-Article-mode-map}
+
+Various hooks for customization:
+ gnus-Article-mode-hook
+    Entry to this mode calls the value with no arguments, if that
+    value is non-nil.
+
+ gnus-Article-prepare-hook
+    Called with no arguments after an article is prepared for reading,
+    if that value is non-nil."
+  (interactive)
+  (kill-all-local-variables)
+  ;; Gee.  Why don't you upgrade?
+  (cond ((boundp 'mode-line-modified)
+	 (setq mode-line-modified "--- "))
+	((listp (default-value 'mode-line-format))
+	 (setq mode-line-format
+	       (cons "--- " (cdr (default-value 'mode-line-format))))))
+  (make-local-variable 'global-mode-string)
+  (setq global-mode-string nil)
+  (setq major-mode 'gnus-Article-mode)
+  (setq mode-name "Article")
+  (gnus-Article-set-mode-line)
+  (use-local-map gnus-Article-mode-map)
+  (make-local-variable 'page-delimiter)
+  (setq page-delimiter gnus-page-delimiter)
+  (make-local-variable 'mail-header-separator)
+  (setq mail-header-separator "")	;For caesar function.
+  (buffer-flush-undo (current-buffer))
+  (setq buffer-read-only t)		;Disable modification
+  (run-hooks 'gnus-Article-mode-hook))
+
+(defun gnus-Article-setup-buffer ()
+  "Initialize Article mode buffer."
+  (or (get-buffer gnus-Article-buffer)
+      (save-excursion
+	(set-buffer (get-buffer-create gnus-Article-buffer))
+	(gnus-Article-mode))
+      ))
+
+(defun gnus-Article-prepare (article &optional all-headers)
+  "Prepare ARTICLE in Article mode buffer.
+If optional argument ALL-HEADERS is non-nil, all headers are inserted."
+  (save-excursion
+    (set-buffer gnus-Article-buffer)
+    (let ((buffer-read-only nil))
+      (erase-buffer)
+      (if (gnus-request-article article)
+	  (progn
+	    ;; Prepare article buffer
+	    (insert-buffer-substring nntp-server-buffer)
+	    (setq gnus-have-all-headers (or all-headers gnus-show-all-headers))
+	    (if (and (numberp article)
+		     (not (eq article gnus-current-article)))
+		;; Seems me that a new article is selected.
+		(progn
+		  ;; gnus-current-article must be an article number.
+		  (setq gnus-last-article gnus-current-article)
+		  (setq gnus-current-article article)
+		  (setq gnus-current-headers
+			(gnus-find-header-by-number gnus-newsgroup-headers
+						    gnus-current-article))
+		  ;; Clear articles history only when articles are
+		  ;; retrieved by article numbers.
+		  (setq gnus-current-history nil)
+		  (run-hooks 'gnus-Mark-article-hook)
+		  ))
+	    ;; Hooks for modifying contents of the article. This hook
+	    ;; must be called before being narrowed.
+	    (run-hooks 'gnus-Article-prepare-hook)
+	    ;; Delete unnecessary headers.
+	    (or gnus-have-all-headers
+		(gnus-Article-delete-headers))
+	    ;; Do page break.
+	    (goto-char (point-min))
+	    (if gnus-break-pages
+		(gnus-narrow-to-page))
+	    ;; Next function must be called after setting
+	    ;;  `gnus-current-article' variable and narrowed to page.
+	    (gnus-Article-set-mode-line)
+	    )
+	(if (numberp article)
+	    (gnus-Subject-mark-as-read article))
+	(ding) (message "No such article (may be canceled)"))
+      )))
+
+(defun gnus-Article-show-all-headers ()
+  "Show all article headers in Article mode buffer."
+  (or gnus-have-all-headers
+      (gnus-Article-prepare gnus-current-article t)))
+
+;;(defun gnus-Article-set-mode-line ()
+;;  "Set Article mode line string."
+;;  (setq mode-line-buffer-identification
+;;	(list 17
+;;	      (format "GNUS: %s {%d-%d} %d"
+;;		      gnus-newsgroup-name
+;;		      gnus-newsgroup-begin
+;;		      gnus-newsgroup-end
+;;		      gnus-current-article
+;;                    )))
+;;  (set-buffer-modified-p t))
+
+(defun gnus-Article-set-mode-line ()
+  "Set Article mode line string."
+  (let ((unmarked
+	 (- (length gnus-newsgroup-unreads)
+	    (length (gnus-intersection
+		     gnus-newsgroup-unreads gnus-newsgroup-marked))))
+	(unselected
+	 (- (length gnus-newsgroup-unselected)
+	    (length (gnus-intersection
+		     gnus-newsgroup-unselected gnus-newsgroup-marked)))))
+    (setq mode-line-buffer-identification
+	  (list 17
+		(format "GNUS: %s{%d} %s"
+			gnus-newsgroup-name
+			gnus-current-article
+			;; This is proposed by tale@pawl.rpi.edu.
+			(cond ((and (zerop unmarked)
+				    (zerop unselected))
+			       "      ")
+			      ((zerop unselected)
+			       (format "%d more" unmarked))
+			      (t
+			       (format "%d(+%d) more" unmarked unselected)))
+			))))
+  (set-buffer-modified-p t))
+
+(defun gnus-Article-delete-headers ()
+  "Delete unnecessary headers."
+  (save-excursion
+    (save-restriction
+      (goto-char (point-min))
+      (narrow-to-region (point-min)
+			(progn (search-forward "\n\n" nil 'move) (point)))
+      (goto-char (point-min))
+      (and (stringp gnus-ignored-headers)
+	   (while (re-search-forward gnus-ignored-headers nil t)
+	     (beginning-of-line)
+	     (delete-region (point)
+			    (progn (re-search-forward "\n[^ \t]")
+				   (forward-char -1)
+				   (point)))))
+      )))
+
+;; Working on article's buffer
+
+(defun gnus-Article-next-page (lines)
+  "Show next page of current article.
+If end of article, return non-nil. Otherwise return nil.
+Argument LINES specifies lines to be scrolled up."
+  (interactive "P")
+  (move-to-window-line -1)
+  ;; Fixed by enami@ptgd.sony.co.jp (enami tsugutomo)
+  (if (save-excursion
+	(end-of-line)
+	(and (pos-visible-in-window-p)	;Not continuation line.
+	     (eobp)))
+      ;; Nothing in this page.
+      (if (or (not gnus-break-pages)
+	      (save-excursion
+		(save-restriction
+		  (widen) (forward-line 1) (eobp)))) ;Real end-of-buffer?
+	  t				;Nothing more.
+	(gnus-narrow-to-page 1)		;Go to next page.
+	nil
+	)
+    ;; More in this page.
+    (condition-case ()
+	(scroll-up lines)
+      (end-of-buffer
+       ;; Long lines may cause an end-of-buffer error.
+       (goto-char (point-max))))
+    nil
+    ))
+
+(defun gnus-Article-prev-page (lines)
+  "Show previous page of current article.
+Argument LINES specifies lines to be scrolled down."
+  (interactive "P")
+  (move-to-window-line 0)
+  (if (and gnus-break-pages
+	   (bobp)
+	   (not (save-restriction (widen) (bobp)))) ;Real beginning-of-buffer?
+      (progn
+	(gnus-narrow-to-page -1) ;Go to previous page.
+	(goto-char (point-max))
+	(recenter -1))
+    (scroll-down lines)))
+
+(defun gnus-Article-next-digest (nth)
+  "Move to head of NTH next digested message.
+Set mark at end of digested message."
+  ;; Stop page breaking in digest mode.
+  (widen)
+  (end-of-line)
+  ;; Skip NTH - 1 digest.
+  ;; Suggested by Khalid Sattar <admin@cs.exeter.ac.uk>.
+  ;; Digest separator is customizable.
+  ;; Suggested by Skip Montanaro <montanaro@sprite.crd.ge.com>.
+  (while (and (> nth 1)
+	      (re-search-forward gnus-digest-separator nil 'move))
+    (setq nth (1- nth)))
+  (if (re-search-forward gnus-digest-separator nil t)
+      (let ((begin (point)))
+	;; Search for end of this message.
+	(end-of-line)
+	(if (re-search-forward gnus-digest-separator nil t)
+	    (progn
+	      (search-backward "\n\n")	;This may be incorrect.
+	      (forward-line 1))
+	  (goto-char (point-max)))
+	(push-mark)			;Set mark at end of digested message.
+	(goto-char begin)
+	(beginning-of-line)
+	;; Show From: and Subject: fields.
+	(recenter 1))
+    (message "End of message")
+    ))
+
+(defun gnus-Article-prev-digest (nth)
+  "Move to head of NTH previous digested message."
+  ;; Stop page breaking in digest mode.
+  (widen)
+  (beginning-of-line)
+  ;; Skip NTH - 1 digest.
+  ;; Suggested by Khalid Sattar <admin@cs.exeter.ac.uk>.
+  ;; Digest separator is customizable.
+  ;; Suggested by Skip Montanaro <montanaro@sprite.crd.ge.com>.
+  (while (and (> nth 1)
+	      (re-search-backward gnus-digest-separator nil 'move))
+    (setq nth (1- nth)))
+  (if (re-search-backward gnus-digest-separator nil t)
+      (let ((begin (point)))
+	;; Search for end of this message.
+	(end-of-line)
+	(if (re-search-forward gnus-digest-separator nil t)
+	    (progn
+	      (search-backward "\n\n")	;This may be incorrect.
+	      (forward-line 1))
+	  (goto-char (point-max)))
+	(push-mark)			;Set mark at end of digested message.
+	(goto-char begin)
+	;; Show From: and Subject: fields.
+	(recenter 1))
+    (goto-char (point-min))
+    (message "Top of message")
+    ))
+
+(defun gnus-Article-refer-article ()
+  "Read article specified by message-id around point."
+  (interactive)
+  (save-window-excursion
+    (save-excursion
+      (re-search-forward ">" nil t)	;Move point to end of "<....>".
+      (if (re-search-backward "\\(<[^<> \t\n]+>\\)" nil t)
+	  (let ((message-id
+		 (buffer-substring (match-beginning 1) (match-end 1))))
+	    (set-buffer gnus-Subject-buffer)
+	    (gnus-Subject-refer-article message-id))
+	(error "No references around point"))
+      )))
+
+(defun gnus-Article-pop-article ()
+  "Pop up article history."
+  (interactive)
+  (save-window-excursion
+    (set-buffer gnus-Subject-buffer)
+    (gnus-Subject-refer-article nil)))
+
+(defun gnus-Article-show-subjects ()
+  "Reconfigure windows to show headers."
+  (interactive)
+  (gnus-configure-windows 'SelectArticle)
+  (pop-to-buffer gnus-Subject-buffer)
+  (gnus-Subject-goto-subject gnus-current-article))
+
+(defun gnus-Article-describe-briefly ()
+  "Describe Article mode commands briefly."
+  (interactive)
+  (message
+   (concat
+    (substitute-command-keys "\\[gnus-Article-next-page]:Next page  ")
+    (substitute-command-keys "\\[gnus-Article-prev-page]:Prev page  ")
+    (substitute-command-keys "\\[gnus-Article-show-subjects]:Show headers  ")
+    (substitute-command-keys "\\[gnus-Info-find-node]:Run Info  ")
+    (substitute-command-keys "\\[gnus-Article-describe-briefly]:This help")
+    )))
+
+
+;;;
+;;; GNUS KILL-File Mode
+;;;
+
+(if gnus-Kill-file-mode-map
+    nil
+  (setq gnus-Kill-file-mode-map (copy-keymap emacs-lisp-mode-map))
+  (define-key gnus-Kill-file-mode-map "\C-c\C-k\C-s" 'gnus-Kill-file-kill-by-subject)
+  (define-key gnus-Kill-file-mode-map "\C-c\C-k\C-a" 'gnus-Kill-file-kill-by-author)
+  (define-key gnus-Kill-file-mode-map "\C-c\C-a" 'gnus-Kill-file-apply-buffer)
+  (define-key gnus-Kill-file-mode-map "\C-c\C-e" 'gnus-Kill-file-apply-last-sexp)
+  (define-key gnus-Kill-file-mode-map "\C-c\C-c" 'gnus-Kill-file-exit)
+  (define-key gnus-Kill-file-mode-map "\C-c\C-i" 'gnus-Info-find-node))
+
+(defun gnus-Kill-file-mode ()
+  "Major mode for editing KILL file.
+
+In addition to Emacs-Lisp Mode, the following commands are available:
+
+\\[gnus-Kill-file-kill-by-subject]	Insert KILL command for current subject.
+\\[gnus-Kill-file-kill-by-author]	Insert KILL command for current author.
+\\[gnus-Kill-file-apply-buffer]	Apply current buffer to selected newsgroup.
+\\[gnus-Kill-file-apply-last-sexp]	Apply sexp before point to selected newsgroup.
+\\[gnus-Kill-file-exit]	Save file and exit editing KILL file.
+\\[gnus-Info-find-node]	Read Info about KILL file.
+
+  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 Subject 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 Subject 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 Subject Mode or Lisp expression. COMMAND is default to
+'(gnus-Subject-mark-as-read nil \"X\"). Make sure that COMMAND is
+executed in the Subject 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-Subject-mark-as-read-forward' is assigned to `d' in Subject Mode.
+
+  It is possible to delete unnecessary headers which are marked with
+`X' in a KILL file as follows:
+
+	(gnus-expunge \"X\")
+
+  If the Subject 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-File")
+  (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 of 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 '(1 0 0)) ;Take all windows.
+	     (pop-to-buffer gnus-Group-buffer)
+	     (let ((gnus-Subject-buffer buffer))
+	       (gnus-configure-windows '(1 1 0)) ;Split into two.
+	       (pop-to-buffer buffer)))
+	    ((eq major-mode 'gnus-Subject-mode)
+	     (gnus-configure-windows 'SelectArticle)
+	     (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)
+    ))
+
+(defun gnus-Kill-file-kill-by-subject ()
+  "Insert KILL command for current subject."
+  (interactive)
+  (insert
+   (format "(gnus-kill \"Subject\" %s)\n"
+	   (prin1-to-string
+	    (if gnus-current-kill-article
+		(regexp-quote
+		 (nntp-header-subject
+		  (gnus-find-header-by-number gnus-newsgroup-headers
+					      gnus-current-kill-article)))
+	      "")))))
+
+(defun gnus-Kill-file-kill-by-author ()
+  "Insert KILL command for current author."
+  (interactive)
+  (insert
+   (format "(gnus-kill \"From\" %s)\n"
+	   (prin1-to-string
+	    (if gnus-current-kill-article
+		(regexp-quote
+		 (nntp-header-from
+		  (gnus-find-header-by-number gnus-newsgroup-headers
+					      gnus-current-kill-article)))
+	      "")))))
+
+(defun gnus-Kill-file-apply-buffer ()
+  "Apply current buffer to current newsgroup."
+  (interactive)
+  (if (and gnus-current-kill-article
+	   (get-buffer gnus-Subject-buffer))
+      ;; Assume newsgroup is selected.
+      (let ((string (concat "(progn \n" (buffer-string) "\n)" )))
+	(save-excursion
+	  (save-window-excursion
+	    (pop-to-buffer gnus-Subject-buffer)
+	    (eval (car (read-from-string string))))))
+    (ding) (message "No newsgroup is selected.")))
+
+(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-Subject-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-Subject-buffer)
+	    (eval (car (read-from-string string))))))
+    (ding) (message "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 (get-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)))
+
+
+;;;
+;;; Utility functions
+;;;
+
+;; Basic ideas by emv@math.lsa.umich.edu (Edward Vielmetti)
+
+(defun gnus-batch-kill ()
+  "Run batched KILL.
+Usage: emacs -batch -l gnus -f gnus-batch-kill NEWSGROUP ..."
+  (if (not noninteractive)
+      (error "gnus-batch-kill is to be used only with -batch"))
+  (let* ((group nil)
+	 (subscribed nil)
+	 (newsrc nil)
+	 (yes-and-no
+	  (gnus-parse-n-options
+	   (apply (function concat)
+		  (mapcar (function (lambda (g) (concat g " ")))
+			  command-line-args-left))))
+	 (yes (car yes-and-no))
+	 (no  (cdr yes-and-no))
+	 ;; Disable verbose message.
+	 (gnus-novice-user nil)
+	 (gnus-large-newsgroup nil)
+	 (nntp-large-newsgroup nil))
+    ;; Eat all arguments.
+    (setq command-line-args-left nil)
+    ;; Startup GNUS.
+    (gnus)
+    ;; Apply kills to specified newsgroups in command line arguments.
+    (setq newsrc (copy-sequence gnus-newsrc-assoc))
+    (while newsrc
+      (setq group (car (car newsrc)))
+      (setq subscribed (nth 1 (car newsrc)))
+      (setq newsrc (cdr newsrc))
+      (if (and subscribed
+	       (not (zerop (nth 1 (gnus-gethash group gnus-unread-hashtb))))
+	       (if yes
+		   (string-match yes group) t)
+	       (or (null no)
+		   (not (string-match no group))))
+	  (progn
+	    (gnus-Subject-read-group group nil t)
+	    (if (eq (current-buffer) (get-buffer gnus-Subject-buffer))
+		(gnus-Subject-exit t))
+	    ))
+      )
+    ;; Finally, exit Emacs.
+    (set-buffer gnus-Group-buffer)
+    (gnus-Group-exit)
+    ))
+
+(defun gnus-Numeric-save-name (newsgroup headers &optional last-file)
+  "Generate file name from NEWSGROUP, HEADERS, and optional LAST-FILE.
+If variable `gnus-use-long-file-name' is nil, it is ~/News/News.group/num.
+Otherwise, it is like ~/News/news/group/num."
+  (let ((default
+	  (expand-file-name
+	   (concat (if gnus-use-long-file-name
+		       (capitalize newsgroup)
+		     (gnus-newsgroup-directory-form newsgroup))
+		   "/" (int-to-string (nntp-header-number headers)))
+	   (or gnus-article-save-directory "~/News"))))
+    (if (and last-file
+	     (string-equal (file-name-directory default)
+			   (file-name-directory last-file))
+	     (string-match "^[0-9]+$" (file-name-nondirectory last-file)))
+	default
+      (or last-file default))))
+
+(defun gnus-numeric-save-name (newsgroup headers &optional last-file)
+  "Generate file name from NEWSGROUP, HEADERS, and optional LAST-FILE.
+If variable `gnus-use-long-file-name' is nil, it is ~/News/news.group/num.
+Otherwise, it is like ~/News/news/group/num."
+  (let ((default
+	  (expand-file-name
+	   (concat (if gnus-use-long-file-name
+		       newsgroup
+		     (gnus-newsgroup-directory-form newsgroup))
+		   "/" (int-to-string (nntp-header-number headers)))
+	   (or gnus-article-save-directory "~/News"))))
+    (if (and last-file
+	     (string-equal (file-name-directory default)
+			   (file-name-directory last-file))
+	     (string-match "^[0-9]+$" (file-name-nondirectory last-file)))
+	default
+      (or last-file default))))
+
+(defun gnus-Plain-save-name (newsgroup headers &optional last-file)
+  "Generate file name from NEWSGROUP, HEADERS, and optional LAST-FILE.
+If variable `gnus-use-long-file-name' is nil, it is ~/News/News.group.
+Otherwise, it is like ~/News/news/group/news."
+  (or last-file
+      (expand-file-name
+       (if gnus-use-long-file-name
+	   (capitalize newsgroup)
+	 (concat (gnus-newsgroup-directory-form newsgroup) "/news"))
+       (or gnus-article-save-directory "~/News"))))
+
+(defun gnus-plain-save-name (newsgroup headers &optional last-file)
+  "Generate file name from NEWSGROUP, HEADERS, and optional LAST-FILE.
+If variable `gnus-use-long-file-name' is nil, it is ~/News/news.group.
+Otherwise, it is like ~/News/news/group/news."
+  (or last-file
+      (expand-file-name
+       (if gnus-use-long-file-name
+	   newsgroup
+	 (concat (gnus-newsgroup-directory-form newsgroup) "/news"))
+       (or gnus-article-save-directory "~/News"))))
+
+(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
+		  (capitalize 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)))))
+
+(defun gnus-apply-kill-file ()
+  "Apply KILL file to the current newsgroup."
+  ;; Apply the global KILL file.
+  (load (gnus-newsgroup-kill-file nil) t nil t)
+  ;; And then apply the local KILL file.
+  (load (gnus-newsgroup-kill-file gnus-newsgroup-name) t nil t))
+
+(defun gnus-Newsgroup-kill-file (newsgroup)
+  "Return the name of a KILL file of 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
+			   (or gnus-article-save-directory "~/News")))
+	(gnus-use-long-file-name
+	 ;; Append ".KILL" to capitalized newsgroup name.
+	 (expand-file-name (concat (capitalize newsgroup)
+				   "." gnus-kill-file-name)
+			   (or gnus-article-save-directory "~/News")))
+	(t
+	 ;; Place "KILL" under the hierarchical directory.
+	 (expand-file-name (concat (gnus-newsgroup-directory-form newsgroup)
+				   "/" gnus-kill-file-name)
+			   (or gnus-article-save-directory "~/News")))
+	))
+
+(defun gnus-newsgroup-kill-file (newsgroup)
+  "Return the name of a KILL file of 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
+			   (or gnus-article-save-directory "~/News")))
+	(gnus-use-long-file-name
+	 ;; Append ".KILL" to newsgroup name.
+	 (expand-file-name (concat newsgroup "." gnus-kill-file-name)
+			   (or gnus-article-save-directory "~/News")))
+	(t
+	 ;; Place "KILL" under the hierarchical directory.
+	 (expand-file-name (concat (gnus-newsgroup-directory-form newsgroup)
+				   "/" gnus-kill-file-name)
+			   (or gnus-article-save-directory "~/News")))
+	))
+
+(defun gnus-newsgroup-directory-form (newsgroup)
+  "Make hierarchical directory name from NEWSGROUP name."
+  (let ((newsgroup (substring newsgroup 0)) ;Copy string.
+	(len (length newsgroup))
+	(idx 0))
+    ;; Replace all occurence of `.' with `/'.
+    (while (< idx len)
+      (if (= (aref newsgroup idx) ?.)
+	  (aset newsgroup idx ?/))
+      (setq idx (1+ idx)))
+    newsgroup
+    ))
+
+(defun gnus-make-directory (directory)
+  "Make DIRECTORY recursively."
+  (let ((directory (expand-file-name directory default-directory)))
+    (or (file-exists-p directory)
+	(gnus-make-directory-1 "" directory))
+    ))
+
+(defun gnus-make-directory-1 (head tail)
+  (cond ((string-match "^/\\([^/]+\\)" tail)
+	 (setq head
+	       (concat (file-name-as-directory head)
+		       (substring tail (match-beginning 1) (match-end 1))))
+	 (or (file-exists-p head)
+	     (call-process "mkdir" nil nil nil head))
+	 (gnus-make-directory-1 head (substring tail (match-end 1))))
+	((string-equal tail "") t)
+	))
+
+(defun gnus-simplify-subject (subject &optional re-only)
+  "Remove `Re:' and words in parentheses.
+If optional argument RE-ONLY is non-nil, strip `Re:' only."
+  (let ((case-fold-search t))		;Ignore case.
+    ;; Remove `Re:' and `Re^N:'.
+    (if (string-match "\\`\\(re\\(\\^[0-9]+\\)?:[ \t]+\\)+" subject)
+	(setq subject (substring subject (match-end 0))))
+    ;; Remove words in parentheses from end.
+    (or re-only
+	(while (string-match "[ \t\n]*([^()]*)[ \t\n]*\\'" subject)
+	  (setq subject (substring subject 0 (match-beginning 0)))))
+    ;; Return subject string.
+    subject
+    ))
+
+(defun gnus-optional-lines-and-from (header)
+  "Return a string like `NNN:AUTHOR' from HEADER."
+  (let ((name-length (length "umerin@photon")))
+    (substring (format "%3d:%s"
+		       ;; Lines of the article.
+		       ;; Suggested by dana@bellcore.com.
+		       (nntp-header-lines header)
+		       ;; Its author.
+		       (concat (mail-strip-quoted-names
+				(nntp-header-from header))
+			       (make-string name-length ? )))
+	       ;; 4 stands for length of `NNN:'.
+	       0 (+ 4 name-length))))
+
+(defun gnus-optional-lines (header)
+  "Return a string like `NNN' from HEADER."
+  (format "%4d" (nntp-header-lines header)))
+
+(defun gnus-sort-headers (predicate &optional reverse)
+  "Sort current group headers by PREDICATE safely.
+*Safely* means C-g quitting is disabled during sorting.
+Optional argument REVERSE means reverse order."
+  (let ((inhibit-quit t))
+    (setq gnus-newsgroup-headers
+	  (if reverse
+	      (nreverse (sort (nreverse gnus-newsgroup-headers) predicate))
+	    (sort gnus-newsgroup-headers predicate)))
+    ))
+
+(defun gnus-string-lessp (a b)
+  "Return T if first arg string is less than second in lexicographic order.
+If case-fold-search is non-nil, case of letters is ignored."
+  (if case-fold-search
+      (string-lessp (downcase a) (downcase b)) (string-lessp a b)))
+
+(defun gnus-date-lessp (date1 date2)
+  "Return T if DATE1 is earlyer than DATE2."
+  (string-lessp (gnus-comparable-date date1)
+		(gnus-comparable-date date2)))
+
+(defun gnus-comparable-date (date)
+  "Make comparable string by string-lessp from DATE."
+  (let ((month '(("JAN" . " 1")("FEB" . " 2")("MAR" . " 3")
+		 ("APR" . " 4")("MAY" . " 5")("JUN" . " 6")
+		 ("JUL" . " 7")("AUG" . " 8")("SEP" . " 9")
+		 ("OCT" . "10")("NOV" . "11")("DEC" . "12")))
+	(date (or date "")))
+    ;; Can understand the following styles:
+    ;; (1) 14 Apr 89 03:20:12 GMT
+    ;; (2) Fri, 17 Mar 89 4:01:33 GMT
+    (if (string-match
+	 "\\([0-9]+\\) \\([^ ,]+\\) \\([0-9]+\\) \\([0-9:]+\\)" date)
+	(concat
+	 ;; Year
+	 (substring date (match-beginning 3) (match-end 3))
+	 ;; Month
+	 (cdr
+	  (assoc
+	   (upcase (substring date (match-beginning 2) (match-end 2))) month))
+	 ;; Day
+	 (format "%2d" (string-to-int
+			(substring date
+				   (match-beginning 1) (match-end 1))))
+	 ;; Time
+	 (substring date (match-beginning 4) (match-end 4)))
+      ;; Cannot understand DATE string.
+      date
+      )
+    ))
+
+(defun gnus-fetch-field (field)
+  "Return the value of the header FIELD of current article."
+  (save-excursion
+    (save-restriction
+      (widen)
+      (goto-char (point-min))
+      (narrow-to-region (point-min)
+			(progn (search-forward "\n\n" nil 'move) (point)))
+      (mail-fetch-field field))))
+
+(fset 'gnus-expunge 'gnus-Subject-delete-marked-with)
+
+(defun gnus-kill (field regexp &optional command all)
+  "If FIELD of an article matches REGEXP, execute COMMAND.
+Optional third argument COMMAND is default to
+	(gnus-Subject-mark-as-read nil \"X\").
+If optional fourth 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.
+  (save-excursion
+    (save-window-excursion
+      ;; Selected window must be Subject mode buffer to execute
+      ;; keyboard macros correctly. See command_loop_1.
+      (switch-to-buffer gnus-Subject-buffer 'norecord)
+      (goto-char (point-min))		;From the beginning.
+      (if (null command)
+	  (setq command '(gnus-Subject-mark-as-read nil "X")))
+      (gnus-execute field regexp command nil (not all))
+      )))
+
+(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 fifth argument BACKWARD is non-nil, do backward instead.
+If optional sixth argument IGNORE-MARKED is non-nil, articles which are
+marked as read or unread are ignored."
+  (let ((function nil)
+	(header nil)
+	(article nil))
+    (if (string-equal field "")
+	(setq field nil))
+    (if (null field)
+	nil
+      (or (stringp field)
+	  (setq field (symbol-name field)))
+      ;; Get access function of header filed.
+      (setq function (intern-soft (concat "gnus-header-" (downcase field))))
+      (if (and function (fboundp function))
+	  (setq function (symbol-function function))
+	(error "Unknown header field: \"%s\"" field)))
+    ;; Make FORM funcallable.
+    (if (and (listp form) (not (eq (car form) 'lambda)))
+	(setq form (list 'lambda nil form)))
+    ;; Starting from the current article.
+    (or (and ignore-marked
+	     ;; Articles marked as read and unread should be ignored.
+	     (setq article (gnus-Subject-article-number))
+	     (or (not (memq article gnus-newsgroup-unreads)) ;Marked as read.
+		 (memq article gnus-newsgroup-marked) ;Marked as unread.
+		 ))
+	(gnus-execute-1 function regexp form))
+    (while (gnus-Subject-search-subject backward ignore-marked nil)
+      (gnus-execute-1 function regexp form))
+    ))
+
+(defun gnus-execute-1 (function regexp form)
+  (save-excursion
+    ;; The point of Subject mode buffer must be saved during execution.
+    (let ((article (gnus-Subject-article-number)))
+      (if (null article)
+	  nil				;Nothing to do.
+	(if function
+	    ;; Compare with header field.
+	    (let ((header (gnus-find-header-by-number
+			   gnus-newsgroup-headers article))
+		  (value nil))
+	      (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)))
+		     (string-match regexp value))
+		   (if (stringp form)	;Keyboard macro.
+		       (execute-kbd-macro form)
+		     (funcall form))))
+	  ;; Search article body.
+	  (let ((gnus-current-article nil) ;Save article pointer.
+		(gnus-last-article nil)
+		(gnus-break-pages nil)	;No need to break pages.
+		(gnus-Mark-article-hook nil)) ;Inhibit marking as read.
+	    (message "Searching for article: %d..." article)
+	    (gnus-Article-setup-buffer)
+	    (gnus-Article-prepare article t)
+	    (if (save-excursion
+		  (set-buffer gnus-Article-buffer)
+		  (goto-char (point-min))
+		  (re-search-forward regexp nil t))
+		(if (stringp form)	;Keyboard macro.
+		    (execute-kbd-macro form)
+		  (funcall form))))
+	  ))
+      )))
+
+;;; caesar-region written by phr@prep.ai.mit.edu  Nov 86
+;;; modified by tower@prep Nov 86
+;;; Modified by umerin@flab.flab.Fujitsu.JUNET for ROT47.
+
+(defun gnus-caesar-region (&optional n)
+  "Caesar rotation of region by N, default 13, for decrypting netnews.
+ROT47 will be performed for Japanese text in any case."
+  (interactive (if current-prefix-arg	; Was there a prefix arg?
+		   (list (prefix-numeric-value current-prefix-arg))
+		 (list nil)))
+  (cond ((not (numberp n)) (setq n 13))
+	((< n 0) (setq n (- 26 (% (- n) 26))))
+	(t (setq n (% n 26))))		;canonicalize N
+  (if (not (zerop n))		; no action needed for a rot of 0
+      (progn
+	(if (or (not (boundp 'caesar-translate-table))
+		(/= (aref caesar-translate-table ?a) (+ ?a n)))
+	    (let ((i 0) (lower "abcdefghijklmnopqrstuvwxyz") upper)
+	      (message "Building caesar-translate-table...")
+	      (setq caesar-translate-table (make-vector 256 0))
+	      (while (< i 256)
+		(aset caesar-translate-table i i)
+		(setq i (1+ i)))
+	      (setq lower (concat lower lower) upper (upcase lower) i 0)
+	      (while (< i 26)
+		(aset caesar-translate-table (+ ?a i) (aref lower (+ i n)))
+		(aset caesar-translate-table (+ ?A i) (aref upper (+ i n)))
+		(setq i (1+ i)))
+	      ;; ROT47 for Japanese text.
+	      ;; Thanks to ichikawa@flab.fujitsu.junet.
+	      (setq i 161)
+	      (let ((t1 (logior ?O 128))
+		    (t2 (logior ?! 128))
+		    (t3 (logior ?~ 128)))
+		(while (< i 256)
+		  (aset caesar-translate-table i
+			(let ((v (aref caesar-translate-table i)))
+			  (if (<= v t1) (if (< v t2) v (+ v 47))
+			    (if (<= v t3) (- v 47) v))))
+		  (setq i (1+ i))))
+	      (message "Building caesar-translate-table... done")))
+	(let ((from (region-beginning))
+	      (to (region-end))
+	      (i 0) str len)
+	  (setq str (buffer-substring from to))
+	  (setq len (length str))
+	  (while (< i len)
+	    (aset str i (aref caesar-translate-table (aref str i)))
+	    (setq i (1+ i)))
+	  (goto-char from)
+	  (delete-region from to)
+	  (insert str)))))
+
+;; Functions accessing headers.
+;; Functions are more convenient than macros in some case.
+
+(defun gnus-header-number (header)
+  "Return article number in HEADER."
+  (nntp-header-number header))
+
+(defun gnus-header-subject (header)
+  "Return subject string in HEADER."
+  (nntp-header-subject header))
+
+(defun gnus-header-from (header)
+  "Return author string in HEADER."
+  (nntp-header-from header))
+
+(defun gnus-header-xref (header)
+  "Return xref string in HEADER."
+  (nntp-header-xref header))
+
+(defun gnus-header-lines (header)
+  "Return lines in HEADER."
+  (nntp-header-lines header))
+
+(defun gnus-header-date (header)
+  "Return date in HEADER."
+  (nntp-header-date header))
+
+(defun gnus-header-id (header)
+  "Return Id in HEADER."
+  (nntp-header-id header))
+
+(defun gnus-header-references (header)
+  "Return references in HEADER."
+  (nntp-header-references header))
+
+
+;;;
+;;; Article savers.
+;;;
+
+(defun gnus-output-to-rmail (file-name)
+  "Append the current article to an Rmail file named FILE-NAME."
+  (require 'rmail)
+  ;; Most of these codes are borrowed from rmailout.el.
+  (setq file-name (expand-file-name file-name))
+  (setq rmail-last-rmail-file file-name)
+  (let ((artbuf (current-buffer))
+	(tmpbuf (get-buffer-create " *GNUS-output*")))
+    (save-excursion
+      (or (get-file-buffer file-name)
+	  (file-exists-p file-name)
+	  (if (yes-or-no-p
+	       (concat "\"" file-name "\" does not exist, create it? "))
+	      (let ((file-buffer (create-file-buffer file-name)))
+		(save-excursion
+		  (set-buffer file-buffer)
+		  (rmail-insert-rmail-file-header)
+		  (let ((require-final-newline nil))
+		    (write-region (point-min) (point-max) file-name t 1)))
+		(kill-buffer file-buffer))
+	    (error "Output file does not exist")))
+      (set-buffer tmpbuf)
+      (buffer-flush-undo (current-buffer))
+      (erase-buffer)
+      (insert-buffer-substring artbuf)
+      (gnus-convert-article-to-rmail)
+      ;; Decide whether to append to a file or to an Emacs buffer.
+      (let ((outbuf (get-file-buffer file-name)))
+	(if (not outbuf)
+	    (append-to-file (point-min) (point-max) file-name)
+	  ;; File has been visited, in buffer OUTBUF.
+	  (set-buffer outbuf)
+	  (let ((buffer-read-only nil)
+		(msg (and (boundp 'rmail-current-message)
+			  rmail-current-message)))
+	    ;; If MSG is non-nil, buffer is in RMAIL mode.
+	    (if msg
+		(progn (widen)
+		       (narrow-to-region (point-max) (point-max))))
+	    (insert-buffer-substring tmpbuf)
+	    (if msg
+		(progn
+		  (goto-char (point-min))
+		  (widen)
+		  (search-backward "\^_")
+		  (narrow-to-region (point) (point-max))
+		  (goto-char (1+ (point-min)))
+		  (rmail-count-new-messages t)
+		  (rmail-show-message msg))))))
+      )
+    (kill-buffer tmpbuf)
+    ))
+
+(defun gnus-output-to-file (file-name)
+  "Append the current article to a file named FILE-NAME."
+  (setq file-name (expand-file-name file-name))
+  (let ((artbuf (current-buffer))
+	(tmpbuf (get-buffer-create " *GNUS-output*")))
+    (save-excursion
+      (set-buffer tmpbuf)
+      (buffer-flush-undo (current-buffer))
+      (erase-buffer)
+      (insert-buffer-substring artbuf)
+      ;; Append newline at end of the buffer as separator, and then
+      ;; save it to file.
+      (goto-char (point-max))
+      (insert "\n")
+      (append-to-file (point-min) (point-max) file-name))
+    (kill-buffer tmpbuf)
+    ))
+
+(defun gnus-convert-article-to-rmail ()
+  "Convert article in current buffer to Rmail message format."
+  (let ((buffer-read-only nil))
+    ;; Convert article directly into Babyl format.
+    ;; Suggested by Rob Austein <sra@lcs.mit.edu>
+    (goto-char (point-min))
+    (insert "\^L\n0, unseen,,\n*** EOOH ***\n")
+    (while (search-forward "\n\^_" nil t) ;single char
+      (replace-match "\n^_"))		;2 chars: "^" and "_"
+    (goto-char (point-max))
+    (insert "\^_")))
+
+;;(defun gnus-convert-article-to-rmail ()
+;;  "Convert article in current buffer to Rmail message format."
+;;  (let ((buffer-read-only nil))
+;;    ;; Insert special header of Unix mail.
+;;    (goto-char (point-min))
+;;    (insert "From "
+;;	    (or (mail-strip-quoted-names (mail-fetch-field "from"))
+;;		"unknown")
+;;	    " " (current-time-string) "\n")
+;;    ;; Stop quoting `From' since this seems unnecessary in most cases.
+;;    ;; ``Quote'' "\nFrom " as "\n>From "
+;;    ;;(while (search-forward "\nFrom " nil t)
+;;    ;;  (forward-char -5)
+;;    ;;  (insert ?>))
+;;    ;; Convert article to babyl format.
+;;    (rmail-convert-to-babyl-format)
+;;    ))
+
+
+;;;
+;;; Internal functions.
+;;;
+
+(defun gnus-start-news-server (&optional confirm)
+  "Open network stream to remote NNTP server.
+If optional argument CONFIRM is non-nil, ask you host that NNTP server
+is running even if it is defined.
+Run gnus-Open-server-hook just before opening news server."
+  (if (gnus-server-opened)
+      ;; Stream is already opened.
+      nil
+    ;; Open NNTP server.
+    (if (or confirm
+	    (null gnus-nntp-server))
+	(if (and (boundp 'gnus-secondary-servers) gnus-secondary-servers)
+	    ;; Read server name with completion.
+	    (setq gnus-nntp-server
+		  (completing-read "NNTP server: "
+				   (cons (list gnus-nntp-server)
+					 gnus-secondary-servers)
+				   nil nil gnus-nntp-server))
+	  (setq gnus-nntp-server
+		(read-string "NNTP server: " gnus-nntp-server))))
+    ;; If no server name is given, local host is assumed.
+    (if (string-equal gnus-nntp-server "")
+	(setq gnus-nntp-server (system-name)))
+    (cond ((string-match ":" gnus-nntp-server)
+	   ;; :DIRECTORY
+	   (require 'mhspool)
+	   (gnus-define-access-method 'mhspool)
+	   (message "Looking up private directory..."))
+	  ((and (null gnus-nntp-service)
+	        (string-equal gnus-nntp-server (system-name)))
+	   (require 'nnspool)
+	   (gnus-define-access-method 'nnspool)
+	   (message "Looking up local news spool..."))
+	  (t
+	   (gnus-define-access-method 'nntp)
+	   (message "Connecting to NNTP server on %s..." gnus-nntp-server)))
+    (run-hooks 'gnus-Open-server-hook)
+    (cond ((gnus-open-server gnus-nntp-server gnus-nntp-service))
+	  ((and (stringp (gnus-status-message))
+		(> (length (gnus-status-message)) 0))
+	   ;; Show valuable message if available.
+	   (error (gnus-status-message)))
+	  (t (error "Cannot open NNTP server on %s" gnus-nntp-server)))
+    ))
+
+;; Dummy functions used only once. Should return nil.
+(defun gnus-server-opened () nil)
+(defun gnus-close-server () nil)
+
+(defun gnus-define-access-method (method &optional access-methods)
+  "Define access functions for the access METHOD.
+Methods defintion is taken from optional argument ACCESS-METHODS or
+the variable gnus-access-methods."
+  (let ((bindings
+	 (cdr (assoc method (or access-methods gnus-access-methods)))))
+    (if (null bindings)
+	(error "Unknown access method: %s" method)
+      ;; Should not use symbol-function here since overload does not work.
+      (while bindings
+	(fset (car (car bindings)) (cdr (car bindings)))
+	(setq bindings (cdr bindings)))
+      )))
+
+(defun gnus-select-newsgroup (group &optional show-all)
+  "Select newsgroup GROUP.
+If optional argument SHOW-ALL is non-nil, all of articles in the group
+are selected."
+  (if (gnus-request-group group)
+      (let ((articles nil))
+	(setq gnus-newsgroup-name group)
+	(setq gnus-newsgroup-unreads
+	      (gnus-uncompress-sequence
+	       (nthcdr 2 (gnus-gethash group gnus-unread-hashtb))))
+	(cond (show-all
+	       ;; Select all active articles.
+	       (setq articles
+		     (gnus-uncompress-sequence
+		      (nthcdr 2 (gnus-gethash group gnus-active-hashtb)))))
+	      (t
+	       ;; Select unread articles only.
+	       (setq articles gnus-newsgroup-unreads)))
+	;; Require confirmation if selecting large newsgroup.
+	(setq gnus-newsgroup-unselected nil)
+	(if (not (numberp gnus-large-newsgroup))
+	    nil
+	  (let ((selected nil)
+		(number (length articles)))
+	    (if (> number gnus-large-newsgroup)
+		(progn
+		  (condition-case ()
+		      (let ((input
+			     (read-string
+			      (format
+			       "How many articles from %s (default %d): "
+			       gnus-newsgroup-name number))))
+			(setq selected
+			      (if (string-equal input "")
+				  number (string-to-int input))))
+		    (quit
+		     (setq selected 0)))
+		  (cond ((and (> selected 0)
+			      (< selected number))
+			 ;; Select last N articles.
+			 (setq articles (nthcdr (- number selected) articles)))
+			((and (< selected 0)
+			      (< (- 0 selected) number))
+			 ;; Select first N articles.
+			 (setq selected (- 0 selected))
+			 (setq articles (copy-sequence articles))
+			 (setcdr (nthcdr (1- selected) articles) nil))
+			((zerop selected)
+			 (setq articles nil))
+			;; Otherwise select all.
+			)
+		  ;; Get unselected unread articles.
+		  (setq gnus-newsgroup-unselected
+			(gnus-set-difference gnus-newsgroup-unreads articles))
+		  ))
+	    ))
+	;; Get headers list.
+	(setq gnus-newsgroup-headers (gnus-retrieve-headers articles))
+	;; UNREADS may contain expired articles, so we have to remove
+	;;  them from the list.
+	(setq gnus-newsgroup-unreads
+	      (gnus-intersection gnus-newsgroup-unreads
+				 (mapcar
+				  (function
+				   (lambda (header)
+				     (nntp-header-number header)))
+				  gnus-newsgroup-headers)))
+	;; Marked article must be a subset of unread articles.
+	(setq gnus-newsgroup-marked
+	      (gnus-intersection (append gnus-newsgroup-unselected
+					 gnus-newsgroup-unreads)
+				 (cdr (assoc group gnus-marked-assoc))))
+	;; First and last article in this newsgroup.
+	(setq gnus-newsgroup-begin
+	      (if gnus-newsgroup-headers
+		  (nntp-header-number (car gnus-newsgroup-headers))
+		0
+		))
+	(setq gnus-newsgroup-end
+	      (if gnus-newsgroup-headers
+		  (nntp-header-number
+		   (gnus-last-element gnus-newsgroup-headers))
+		0
+		))
+	;; File name that an article was saved last.
+	(setq gnus-newsgroup-last-rmail nil)
+	(setq gnus-newsgroup-last-mail nil)
+	(setq gnus-newsgroup-last-folder nil)
+	(setq gnus-newsgroup-last-file nil)
+	;; Reset article pointer etc.
+	(setq gnus-current-article nil)
+	(setq gnus-current-headers nil)
+	(setq gnus-current-history nil)
+	(setq gnus-have-all-headers nil)
+	(setq gnus-last-article nil)
+	;; GROUP is successfully selected.
+	t
+	)
+    ))
+
+(defun gnus-more-header-backward ()
+  "Find new header backward."
+  (let ((first
+	 (car (nth 2 (gnus-gethash gnus-newsgroup-name gnus-active-hashtb))))
+	(artnum gnus-newsgroup-begin)
+	(header nil))
+    (while (and (not header)
+		(> artnum first))
+      (setq artnum (1- artnum))
+      (setq header (car (gnus-retrieve-headers (list artnum)))))
+    header
+    ))
+
+(defun gnus-more-header-forward ()
+  "Find new header forward."
+  (let ((last
+	 (cdr (nth 2 (gnus-gethash gnus-newsgroup-name gnus-active-hashtb))))
+	(artnum gnus-newsgroup-end)
+	(header nil))
+    (while (and (not header)
+		(< artnum last))
+      (setq artnum (1+ artnum))
+      (setq header (car (gnus-retrieve-headers (list artnum)))))
+    header
+    ))
+
+(defun gnus-extend-newsgroup (header &optional backward)
+  "Extend newsgroup selection with HEADER.
+Optional argument BACKWARD means extend toward backward."
+  (if header
+      (let ((artnum (nntp-header-number header)))
+	(setq gnus-newsgroup-headers
+	      (if backward
+		  (cons header gnus-newsgroup-headers)
+		(append gnus-newsgroup-headers (list header))))
+	;; We have to update unreads and unselected, but don't have to
+	;; care about gnus-newsgroup-marked.
+	(if (memq artnum gnus-newsgroup-unselected)
+	    (setq gnus-newsgroup-unreads
+		  (cons artnum gnus-newsgroup-unreads)))
+	(setq gnus-newsgroup-unselected
+	      (delq artnum gnus-newsgroup-unselected))
+	(setq gnus-newsgroup-begin (min gnus-newsgroup-begin artnum))
+	(setq gnus-newsgroup-end (max gnus-newsgroup-end artnum))
+	)))
+
+(defun gnus-mark-article-as-read (article)
+  "Remember that ARTICLE is marked as read."
+  ;; Remove from unread and marked list.
+  (setq gnus-newsgroup-unreads
+	(delq article gnus-newsgroup-unreads))
+  (setq gnus-newsgroup-marked
+	(delq article gnus-newsgroup-marked)))
+
+(defun gnus-mark-article-as-unread (article &optional clear-mark)
+  "Remember that ARTICLE is marked as unread.
+Optional argument CLEAR-MARK means ARTICLE should not be remembered
+that it was marked as read once."
+  ;; Add to unread list.
+  (or (memq article gnus-newsgroup-unreads)
+      (setq gnus-newsgroup-unreads
+	    (cons article gnus-newsgroup-unreads)))
+  ;; If CLEAR-MARK is non-nil, the article must be removed from marked
+  ;; list.  Otherwise, it must be added to the list.
+  (if clear-mark
+      (setq gnus-newsgroup-marked
+	    (delq article gnus-newsgroup-marked))
+    (or (memq article gnus-newsgroup-marked)
+	(setq gnus-newsgroup-marked
+	      (cons article gnus-newsgroup-marked)))))
+
+(defun gnus-clear-system ()
+  "Clear all variables and buffer."
+  ;; Clear GNUS variables.
+  (let ((variables gnus-variable-list))
+    (while variables
+      (set (car variables) nil)
+      (setq variables (cdr variables))))
+  ;; Clear other internal variables.
+  (setq gnus-active-hashtb nil)
+  (setq gnus-unread-hashtb nil)
+  ;; Kill the startup file.
+  (and gnus-current-startup-file
+       (get-file-buffer gnus-current-startup-file)
+       (kill-buffer (get-file-buffer gnus-current-startup-file)))
+  (setq gnus-current-startup-file nil)
+  ;; Kill GNUS buffers.
+  (let ((buffers gnus-buffer-list))
+    (while buffers
+      (if (get-buffer (car buffers))
+	  (kill-buffer (car buffers)))
+      (setq buffers (cdr buffers))
+      )))
+
+(defun gnus-configure-windows (action)
+  "Configure GNUS windows according to the next ACTION.
+The ACTION is either a symbol, such as `SelectNewsgroup', or a
+configuration list such as `(1 1 2)'.  If ACTION is not a list,
+configuration list is got from the variable gnus-window-configuration."
+  (let* ((windows
+	  (if (listp action)
+	      action (car (cdr (assq action gnus-window-configuration)))))
+	 (grpwin (get-buffer-window gnus-Group-buffer))
+	 (subwin (get-buffer-window gnus-Subject-buffer))
+	 (artwin (get-buffer-window gnus-Article-buffer))
+	 (winsum nil)
+	 (height nil)
+	 (grpheight 0)
+	 (subheight 0)
+	 (artheight 0))
+    (if (or (null windows)		;No configuration is specified.
+	    (and (eq (null grpwin)
+		     (zerop (nth 0 windows)))
+		 (eq (null subwin)
+		     (zerop (nth 1 windows)))
+		 (eq (null artwin)
+		     (zerop (nth 2 windows)))))
+	;; No need to change window configuration.
+	nil
+      (select-window (or grpwin subwin artwin (selected-window)))
+      ;; First of all, compute the height of each window.
+      (cond (gnus-use-full-window
+	     ;; Take up the entire screen.
+	     (delete-other-windows)
+	     (setq height (window-height (selected-window))))
+	    (t
+	     (setq height (+ (if grpwin (window-height grpwin) 0)
+			     (if subwin (window-height subwin) 0)
+			     (if artwin (window-height artwin) 0)))))
+      ;; The Newsgroup buffer exits always. So, use it to extend the
+      ;; Group window so as to get enough window space.
+      (switch-to-buffer gnus-Group-buffer 'norecord)
+      (and (get-buffer gnus-Subject-buffer)
+	   (delete-windows-on gnus-Subject-buffer))
+      (and (get-buffer gnus-Article-buffer)
+	   (delete-windows-on gnus-Article-buffer))
+      ;; Compute expected window height.
+      (setq winsum (apply (function +) windows))
+      (if (not (zerop (nth 0 windows)))
+	  (setq grpheight (max window-min-height
+			       (/ (* height (nth 0 windows)) winsum))))
+      (if (not (zerop (nth 1 windows)))
+	  (setq subheight (max window-min-height
+			       (/ (* height (nth 1 windows)) winsum))))
+      (if (not (zerop (nth 2 windows)))
+	  (setq artheight (max window-min-height
+			       (/ (* height (nth 2 windows)) winsum))))
+      (setq height (+ grpheight subheight artheight))
+      (enlarge-window (max 0 (- height (window-height (selected-window)))))
+      ;; Then split the window.
+      (and (not (zerop artheight))
+	   (or (not (zerop grpheight))
+	       (not (zerop subheight)))
+	   (split-window-vertically (+ grpheight subheight)))
+      (and (not (zerop grpheight))
+	   (not (zerop subheight))
+	   (split-window-vertically grpheight))
+      ;; Then select buffers in each window.
+      (and (not (zerop grpheight))
+	   (progn
+	     (switch-to-buffer gnus-Group-buffer 'norecord)
+	     (other-window 1)))
+      (and (not (zerop subheight))
+	   (progn
+	     (switch-to-buffer gnus-Subject-buffer 'norecord)
+	     (other-window 1)))
+      (and (not (zerop artheight))
+	   (progn
+	     ;; If Article buffer does not exist, it will be created
+	     ;; and initialized.
+	     (gnus-Article-setup-buffer)
+	     (switch-to-buffer gnus-Article-buffer 'norecord)))
+      )
+    ))
+
+(defun gnus-find-header-by-number (headers number)
+  "Return a header which is a element of HEADERS and has NUMBER."
+  (let ((found nil))
+    (while (and headers (not found))
+      ;; We cannot use `=' to accept non-numeric NUMBER.
+      (if (eq number (nntp-header-number (car headers)))
+	  (setq found (car headers)))
+      (setq headers (cdr headers)))
+    found
+    ))
+
+(defun gnus-find-header-by-id (headers id)
+  "Return a header which is a element of HEADERS and has Message-ID."
+  (let ((found nil))
+    (while (and headers (not found))
+      (if (string-equal id (nntp-header-id (car headers)))
+	  (setq found (car headers)))
+      (setq headers (cdr headers)))
+    found
+    ))
+
+(defun gnus-version ()
+  "Version numbers of this version of GNUS."
+  (interactive)
+  (cond ((and (boundp 'mhspool-version) (boundp 'nnspool-version))
+	 (message "%s; %s; %s; %s"
+		  gnus-version nntp-version nnspool-version mhspool-version))
+	((boundp 'mhspool-version)
+	 (message "%s; %s; %s"
+		  gnus-version nntp-version mhspool-version))
+	((boundp 'nnspool-version)
+	 (message "%s; %s; %s"
+		  gnus-version nntp-version nnspool-version))
+	(t
+	 (message "%s; %s" gnus-version nntp-version))))
+
+(defun gnus-Info-find-node ()
+  "Find Info documentation of GNUS."
+  (interactive)
+  (require 'info)
+  ;; Enlarge info window if needed.
+  (cond ((eq major-mode 'gnus-Group-mode)
+	 (gnus-configure-windows '(1 0 0)) ;Take all windows.
+	 (pop-to-buffer gnus-Group-buffer))
+	((eq major-mode 'gnus-Subject-mode)
+	 (gnus-configure-windows '(0 1 0)) ;Take all windows.
+	 (pop-to-buffer gnus-Subject-buffer)))
+  (Info-goto-node (cdr (assq major-mode gnus-Info-nodes))))
+
+(defun gnus-overload-functions (&optional overloads)
+  "Overload functions specified by optional argument OVERLOADS.
+If nothing is specified, use the variable gnus-overload-functions."
+  (let ((defs nil)
+	(overloads (or overloads gnus-overload-functions)))
+    (while overloads
+      (setq defs (car overloads))
+      (setq overloads (cdr overloads))
+      ;; Load file before overloading function if necessary.  Make
+      ;; sure we cannot use `requre' always.
+      (and (not (fboundp (car defs)))
+	   (car (cdr (cdr defs)))
+	   (load (car (cdr (cdr defs))) nil 'nomessage))
+      (fset (car defs) (car (cdr defs)))
+      )))
+
+(defun gnus-make-threads (newsgroup-headers)
+  "Make conversation threads tree from NEWSGROUP-HEADERS."
+  (let ((headers newsgroup-headers)
+	(h nil)
+	(d nil)
+	(roots nil)
+	(dependencies nil))
+    ;; Make message dependency alist.
+    (while headers
+      (setq h (car headers))
+      (setq headers (cdr headers))
+      ;; Ignore invalid headers.
+      (if (vectorp h)			;Depends on nntp.el.
+	  (progn
+	    ;; Ignore broken references, e.g "<123@a.b.c".
+	    (setq d (and (nntp-header-references h)
+			 (string-match "\\(<[^<>]+>\\)[^>]*$"
+				       (nntp-header-references h))
+			 (gnus-find-header-by-id
+			  newsgroup-headers
+			  (substring (nntp-header-references h)
+				     (match-beginning 1) (match-end 1)))))
+	    ;; Check subject equality.
+	    (or gnus-thread-ignore-subject
+		(null d)
+		(string-equal (gnus-simplify-subject
+			       (nntp-header-subject h) 're)
+			      (gnus-simplify-subject
+			       (nntp-header-subject d) 're))
+		;; H should be a thread root.
+		(setq d nil))
+	    ;; H depends on D.
+	    (setq dependencies
+		  (cons (cons h d) dependencies))
+	    ;; H is a thread root.
+	    (if (null d)
+		(setq roots (cons h roots)))
+	    ))
+      )
+    ;; Make complete threads from the roots.
+    ;; Note: dependencies are in reverse order, but
+    ;; gnus-make-threads-1 processes it in reverse order again.  So,
+    ;; we don't have to worry about it.
+    (mapcar
+     (function
+      (lambda (root)
+	(gnus-make-threads-1 root dependencies))) (nreverse roots))
+    ))
+
+(defun gnus-make-threads-1 (parent dependencies)
+  (let ((children nil)
+	(d nil)
+	(depends dependencies))
+    ;; Find children.
+    (while depends
+      (setq d (car depends))
+      (setq depends (cdr depends))
+      (and (cdr d)
+	   (eq (nntp-header-id parent) (nntp-header-id (cdr d)))
+	   (setq children (cons (car d) children))))
+    ;; Go down.
+    (cons parent
+	  (mapcar
+	   (function
+	    (lambda (child)
+	      (gnus-make-threads-1 child dependencies))) children))
+    ))
+
+(defun gnus-narrow-to-page (&optional arg)
+  "Make text outside current page invisible except for page delimiter.
+A numeric arg specifies to move forward or backward by that many pages,
+thus showing a page other than the one point was originally in."
+  (interactive "P")
+  (setq arg (if arg (prefix-numeric-value arg) 0))
+  (save-excursion
+    (forward-page -1)			;Beginning of current page.
+    (widen)
+    (if (> arg 0)
+	(forward-page arg)
+      (if (< arg 0)
+	  (forward-page (1- arg))))
+    ;; Find the end of the page.
+    (forward-page)
+    ;; If we stopped due to end of buffer, stay there.
+    ;; If we stopped after a page delimiter, put end of restriction
+    ;; at the beginning of that line.
+    ;; These are commented out.
+    ;;    (if (save-excursion (beginning-of-line)
+    ;;			(looking-at page-delimiter))
+    ;;	(beginning-of-line))
+    (narrow-to-region (point)
+		      (progn
+			;; Find the top of the page.
+			(forward-page -1)
+			;; If we found beginning of buffer, stay there.
+			;; If extra text follows page delimiter on same line,
+			;; include it.
+			;; Otherwise, show text starting with following line.
+			(if (and (eolp) (not (bobp)))
+			    (forward-line 1))
+			(point)))
+    ))
+
+(defun gnus-last-element (list)
+  "Return last element of LIST."
+  (let ((last nil))
+    (while list
+      (if (null (cdr list))
+	  (setq last (car list)))
+      (setq list (cdr list)))
+    last
+    ))
+
+(defun gnus-set-difference (list1 list2)
+  "Return a list of elements of LIST1 that do not appear in LIST2."
+  (let ((list1 (copy-sequence list1)))
+    (while list2
+      (setq list1 (delq (car list2) list1))
+      (setq list2 (cdr list2)))
+    list1
+    ))
+
+(defun gnus-intersection (list1 list2)
+  "Return a list of elements that appear in both LIST1 and LIST2."
+  (let ((result nil))
+    (while list2
+      (if (memq (car list2) list1)
+	  (setq result (cons (car list2) result)))
+      (setq list2 (cdr list2)))
+    result
+    ))
+
+
+;;;
+;;; Get information about active articles, already read articles, and
+;;;  still unread articles.
+;;;
+
+;; GNUS internal format of gnus-newsrc-assoc and gnus-killed-assoc:
+;; (("general" t (1 . 1))
+;;  ("misc"    t (1 . 10) (12 . 15))
+;;  ("test"  nil (1 . 99)) ...)
+;; GNUS internal format of gnus-marked-assoc:
+;; (("general" 1 2 3)
+;;  ("misc" 2) ...)
+;; GNUS internal format of gnus-active-hashtb:
+;; (("general" t (1 . 1))
+;;  ("misc"    t (1 . 10))
+;;  ("test"  nil (1 . 99)) ...)
+;; GNUS internal format of gnus-unread-hashtb:
+;; (("general" 1 (1 . 1))
+;;  ("misc"   14 (1 . 10) (12 . 15))
+;;  ("test"   99 (1 . 99)) ...)
+
+(defun gnus-setup-news-info (&optional rawfile)
+  "Setup news information.
+If optional argument RAWFILE is non-nil, force to read raw startup file."
+  (let ((init (not (and gnus-newsrc-assoc
+			gnus-active-hashtb
+			gnus-unread-hashtb
+			(not rawfile)
+			))))
+    ;; We have to clear some variables to re-initialize news info.
+    (if init
+	(setq gnus-newsrc-assoc nil
+	      gnus-active-hashtb nil
+	      gnus-unread-hashtb nil))
+    (if init
+	(gnus-read-newsrc-file rawfile))
+    (gnus-read-active-file)
+    (gnus-expire-marked-articles)
+    (gnus-get-unread-articles)
+    ;; Check new newsgroups and subscribe them.
+    (if init
+	(let ((new-newsgroups (gnus-find-new-newsgroups)))
+	  (while new-newsgroups
+	    (funcall gnus-subscribe-newsgroup-method (car new-newsgroups))
+	    (setq new-newsgroups (cdr new-newsgroups))
+	    )))
+    ))
+
+(defun gnus-subscribe-newsgroup (newsgroup &optional next)
+  "Subscribe new NEWSGROUP.
+If optional argument NEXT is non-nil, it is inserted before NEXT."
+  (gnus-insert-newsgroup (list newsgroup t) next)
+  (message "Newsgroup %s is subscribed" newsgroup))
+
+(defun gnus-add-newsgroup (newsgroup)
+  "Subscribe new NEWSGROUP safely and put it at top."
+  (and (null (assoc newsgroup gnus-newsrc-assoc)) ;Really new?
+       (gnus-gethash newsgroup gnus-active-hashtb) ;Really exist?
+       (gnus-insert-newsgroup (or (assoc newsgroup gnus-killed-assoc)
+				  (list newsgroup t))
+			      (car (car gnus-newsrc-assoc)))))
+
+(defun gnus-find-new-newsgroups ()
+  "Looking for new newsgroups and return names.
+`-n' option of options line in .newsrc file is recognized."
+  (let ((group nil)
+	(new-newsgroups nil))
+    (mapatoms
+     (function
+      (lambda (sym)
+	(setq group (symbol-name sym))
+	;; Taking account of `-n' option.
+	(and (or (null gnus-newsrc-options-n-no)
+		 (not (string-match gnus-newsrc-options-n-no group))
+		 (and gnus-newsrc-options-n-yes
+		      (string-match gnus-newsrc-options-n-yes group)))
+	     (null (assoc group gnus-killed-assoc)) ;Ignore killed.
+	     (null (assoc group gnus-newsrc-assoc)) ;Really new.
+	     ;; Find new newsgroup.
+	     (setq new-newsgroups
+		   (cons group new-newsgroups)))
+	))
+     gnus-active-hashtb)
+    ;; Return new newsgroups.
+    new-newsgroups
+    ))
+
+(defun gnus-kill-newsgroup (group)
+  "Kill GROUP from gnus-newsrc-assoc, .newsrc and gnus-unread-hashtb."
+  (let ((info (assoc group gnus-newsrc-assoc)))
+    (if (null info)
+	nil
+      ;; Delete from gnus-newsrc-assoc
+      (setq gnus-newsrc-assoc (delq info gnus-newsrc-assoc))
+      ;; Add to gnus-killed-assoc.
+      (setq gnus-killed-assoc
+	    (cons info
+		  (delq (assoc group gnus-killed-assoc) gnus-killed-assoc)))
+      ;; Clear unread hashtable.
+      ;; Thanks cwitty@csli.Stanford.EDU (Carl Witty).
+      (gnus-sethash group nil gnus-unread-hashtb)
+      ;; Then delete from .newsrc
+      (gnus-update-newsrc-buffer group 'delete)
+      ;; Return the deleted newsrc entry.
+      info
+      )))
+
+(defun gnus-insert-newsgroup (info &optional next)
+  "Insert newsrc INFO entry before NEXT.
+If optional argument NEXT is nil, appended to the last."
+  (if (null info)
+      (error "Invalid argument: %s" info))
+  (let* ((group (car info))		;Newsgroup name.
+	 (range
+	  (gnus-difference-of-range
+	   (nth 2 (gnus-gethash group gnus-active-hashtb)) (nthcdr 2 info))))
+    ;; Check duplication.
+    (if (assoc group gnus-newsrc-assoc)
+	(error "Duplicated: %s" group))
+    ;; Insert to gnus-newsrc-assoc.
+    (if (string-equal next (car (car gnus-newsrc-assoc)))
+	(setq gnus-newsrc-assoc
+	      (cons info gnus-newsrc-assoc))
+      (let ((found nil)
+	    (rest gnus-newsrc-assoc)
+	    (tail (cons nil gnus-newsrc-assoc)))
+	;; Seach insertion point.
+	(while (and (not found) rest)
+	  (if (string-equal next (car (car rest)))
+	      (setq found t)
+	    (setq rest (cdr rest))
+	    (setq tail (cdr tail))
+	    ))
+	;; Find it.
+	(setcdr tail nil)
+	(setq gnus-newsrc-assoc
+	      (append gnus-newsrc-assoc (cons info rest)))
+	))
+    ;; Delete from gnus-killed-assoc.
+    (setq gnus-killed-assoc
+	  (delq (assoc group gnus-killed-assoc) gnus-killed-assoc))
+    ;; Then insert to .newsrc.
+    (gnus-update-newsrc-buffer group nil next)
+    ;; Add to gnus-unread-hashtb.
+    (gnus-sethash group
+		  (cons group		;Newsgroup name.
+			(cons (gnus-number-of-articles range) range))
+		  gnus-unread-hashtb)
+    ))
+
+(defun gnus-check-killed-newsgroups ()
+  "Check consistency between gnus-newsrc-assoc and gnus-killed-assoc."
+  (let ((group nil)
+	(new-killed nil)
+	(old-killed gnus-killed-assoc))
+    (while old-killed
+      (setq group (car (car old-killed)))
+      (and (or (null gnus-newsrc-options-n-no)
+	       (not (string-match gnus-newsrc-options-n-no group))
+	       (and gnus-newsrc-options-n-yes
+		    (string-match gnus-newsrc-options-n-yes group)))
+	   (null (assoc group gnus-newsrc-assoc)) ;No duplication.
+	   ;; Subscribed in options line and not in gnus-newsrc-assoc.
+	   (setq new-killed
+		 (cons (car old-killed) new-killed)))
+      (setq old-killed (cdr old-killed))
+      )
+    (setq gnus-killed-assoc (nreverse new-killed))
+    ))
+
+(defun gnus-check-bogus-newsgroups (&optional confirm)
+  "Delete bogus newsgroups.
+If optional argument CONFIRM is non-nil, confirm deletion of newsgroups."
+  (let ((group nil)			;Newsgroup name temporary used.
+	(old-newsrc gnus-newsrc-assoc)
+	(new-newsrc nil)
+	(bogus nil)			;List of bogus newsgroups.
+	(old-killed gnus-killed-assoc)
+	(new-killed nil)
+	(old-marked gnus-marked-assoc)
+	(new-marked nil))
+    (message "Checking bogus newsgroups...")
+    ;; Update gnus-newsrc-assoc.
+    (while old-newsrc
+      (setq group (car (car old-newsrc)))
+      (if (or (gnus-gethash group gnus-active-hashtb)
+	      (and confirm
+		   (not (y-or-n-p
+			 (format "Delete bogus newsgroup: %s " group)))))
+	  ;; Active newsgroup.
+	  (setq new-newsrc (cons (car old-newsrc) new-newsrc))
+	;; Found a bogus newsgroup.
+	(setq bogus (cons group bogus)))
+      (setq old-newsrc (cdr old-newsrc))
+      )
+    (setq gnus-newsrc-assoc (nreverse new-newsrc))
+    ;; Update gnus-killed-assoc.
+    ;; The killed newsgroups are deleted without any confirmations.
+    (while old-killed
+      (setq group (car (car old-killed)))
+      (and (gnus-gethash group gnus-active-hashtb)
+	   (null (assoc group gnus-newsrc-assoc))
+	   ;; Active and really killed newsgroup.
+	   (setq new-killed (cons (car old-killed) new-killed)))
+      (setq old-killed (cdr old-killed))
+      )
+    (setq gnus-killed-assoc (nreverse new-killed))
+    ;; Remove BOGUS from .newsrc file.
+    (while bogus
+      (gnus-update-newsrc-buffer (car bogus) 'delete)
+      (setq bogus (cdr bogus)))
+    ;; Update gnus-marked-assoc.
+    (while old-marked
+      (setq group (car (car old-marked)))
+      (if (and (cdr (car old-marked))	;Non-empty?
+	       (assoc group gnus-newsrc-assoc))	;Not bogus?
+	  (setq new-marked (cons (car old-marked) new-marked)))
+      (setq old-marked (cdr old-marked)))
+    (setq gnus-marked-assoc new-marked)
+    (message "Checking bogus newsgroups... done")
+    ))
+
+(defun gnus-get-unread-articles ()
+  "Compute diffs between active and read articles."
+  (let ((read gnus-newsrc-assoc)
+	(group-info nil)
+	(group-name nil)
+	(active nil)
+	(range nil))
+    (message "Checking new news...")
+    (or gnus-unread-hashtb
+	(setq gnus-unread-hashtb (gnus-make-hashtable)))
+    (while read
+      (setq group-info (car read))	;About one newsgroup
+      (setq group-name (car group-info))
+      (setq active (nth 2 (gnus-gethash group-name gnus-active-hashtb)))
+      (if (and gnus-octive-hashtb
+	       ;; Is nothing changed?
+	       (equal active
+		      (nth 2 (gnus-gethash group-name gnus-octive-hashtb)))
+	       ;; Is this newsgroup in the unread hash table?
+	       (gnus-gethash group-name gnus-unread-hashtb)
+	       )
+	  nil				;Nothing to do.
+	(setq range (gnus-difference-of-range active (nthcdr 2 group-info)))
+	(gnus-sethash group-name
+		      (cons group-name	;Group name
+			    (cons (gnus-number-of-articles range)
+				  range)) ;Range of unread articles
+		      gnus-unread-hashtb)
+	)
+      (setq read (cdr read))
+      )
+    (message "Checking new news... done")
+    ))
+
+(defun gnus-expire-marked-articles ()
+  "Check expired article which is marked as unread."
+  (let ((marked-assoc gnus-marked-assoc)
+	(updated-assoc nil)
+	(marked nil)			;Current marked info.
+	(articles nil)			;List of marked articles.
+	(updated nil)			;List of real marked.
+	(begin nil))
+    (while marked-assoc
+      (setq marked (car marked-assoc))
+      (setq articles (cdr marked))
+      (setq updated nil)
+      (setq begin
+	    (car (nth 2 (gnus-gethash (car marked) gnus-active-hashtb))))
+      (while (and begin articles)
+	(if (>= (car articles) begin)
+	    ;; This article is still active.
+	    (setq updated (cons (car articles) updated)))
+	(setq articles (cdr articles)))
+      (if updated
+	  (setq updated-assoc
+		(cons (cons (car marked) updated) updated-assoc)))
+      (setq marked-assoc (cdr marked-assoc)))
+    (setq gnus-marked-assoc updated-assoc)
+    ))
+
+(defun gnus-mark-as-read-by-xref
+  (group headers unreads &optional subscribed-only)
+  "Mark articles as read using cross references and return updated newsgroups.
+Arguments are GROUP, HEADERS, UNREADS, and optional SUBSCRIBED-ONLY."
+  (let ((xref-list nil)
+	(header nil)
+	(xrefs nil)			;One Xref: field info.
+	(xref nil)			;(NEWSGROUP . ARTICLE)
+	(gname nil)			;Newsgroup name
+	(article nil))			;Article number
+    (while headers
+      (setq header (car headers))
+      (if (memq (nntp-header-number header) unreads)
+	  ;; This article is not yet marked as read.
+	  nil
+	(setq xrefs (gnus-parse-xref-field (nntp-header-xref header)))
+	;; For each cross reference info. in one Xref: field.
+	(while xrefs
+	  (setq xref (car xrefs))
+	  (setq gname (car xref))	;Newsgroup name
+	  (setq article (cdr xref))	;Article number
+	  (or (string-equal group gname) ;Ignore current newsgroup.
+	      ;; Ignore unsubscribed newsgroup if requested.
+	      (and subscribed-only
+		   (not (nth 1 (assoc gname gnus-newsrc-assoc))))
+	      ;; Ignore article marked as unread.
+	      (memq article (cdr (assoc gname gnus-marked-assoc)))
+	      (let ((group-xref (assoc gname xref-list)))
+		(if group-xref
+		    (if (memq article (cdr group-xref))
+			nil		;Alread marked.
+		      (setcdr group-xref (cons article (cdr group-xref))))
+		  ;; Create new assoc entry for GROUP.
+		  (setq xref-list (cons (list gname article) xref-list)))
+		))
+	  (setq xrefs (cdr xrefs))
+	  ))
+      (setq headers (cdr headers)))
+    ;; Mark cross referenced articles as read.
+    (gnus-mark-xrefed-as-read xref-list)
+    ;;(message "%s %s" (prin1-to-string unreads) (prin1-to-string xref-list))
+    ;; Return list of updated group name.
+    (mapcar (function car) xref-list)
+    ))
+
+(defun gnus-parse-xref-field (xref-value)
+  "Parse Xref: field value, and return list of `(group . article-id)'."
+  (let ((xref-list nil)
+	(xref-value (or xref-value "")))
+    ;; Remove server host name.
+    (if (string-match "^[ \t]*[^ \t,]+[ \t,]+\\(.*\\)$" xref-value)
+	(setq xref-value (substring xref-value (match-beginning 1)))
+      (setq xref-value nil))
+    ;; Process each xref info.
+    (while xref-value
+      (if (string-match
+	   "^[ \t,]*\\([^ \t,]+\\):\\([0-9]+\\)[^0-9]*" xref-value)
+	  (progn
+	    (setq xref-list
+		  (cons
+		   (cons
+		    ;; Group name
+		    (substring xref-value (match-beginning 1) (match-end 1))
+		    ;; Article-ID
+		    (string-to-int
+		     (substring xref-value (match-beginning 2) (match-end 2))))
+		   xref-list))
+	    (setq xref-value (substring xref-value (match-end 2))))
+	(setq xref-value nil)))
+    ;; Return alist.
+    xref-list
+    ))
+
+(defun gnus-mark-xrefed-as-read (xrefs)
+  "Update unread article information using XREFS alist."
+  (let ((group nil)
+	(idlist nil)
+	(unread nil))
+    (while xrefs
+      (setq group (car (car xrefs)))
+      (setq idlist (cdr (car xrefs)))
+      (setq unread (gnus-uncompress-sequence
+		    (nthcdr 2 (gnus-gethash group gnus-unread-hashtb))))
+      (while idlist
+	(setq unread (delq (car idlist) unread))
+	(setq idlist (cdr idlist)))
+      (gnus-update-unread-articles group unread 'ignore)
+      (setq xrefs (cdr xrefs))
+      )))
+
+(defun gnus-update-unread-articles (group unread-list marked-list)
+  "Update unread articles of GROUP using UNREAD-LIST and MARKED-LIST."
+  (let ((active (nth 2 (gnus-gethash group gnus-active-hashtb)))
+	(unread (gnus-gethash group gnus-unread-hashtb)))
+    (if (or (null active) (null unread))
+	;; Ignore unknown newsgroup.
+	nil
+      ;; Update gnus-unread-hashtb.
+      (if unread-list
+	  (setcdr (cdr unread)
+		  (gnus-compress-sequence unread-list))
+	;; All of the articles are read.
+	(setcdr (cdr unread) '((0 . 0))))
+      ;; Number of unread articles.
+      (setcar (cdr unread)
+	      (gnus-number-of-articles (nthcdr 2 unread)))
+      ;; Update gnus-newsrc-assoc.
+      (if (> (car active) 0)
+	  ;; Articles from 1 to N are not active.
+	  (setq active (cons 1 (cdr active))))
+      (setcdr (cdr (assoc group gnus-newsrc-assoc))
+	      (gnus-difference-of-range active (nthcdr 2 unread)))
+      ;; Update .newsrc buffer.
+      (gnus-update-newsrc-buffer group)
+      ;; Update gnus-marked-assoc.
+      (if (listp marked-list)		;Includes NIL.
+	  (let ((marked (assoc group gnus-marked-assoc)))
+	    (cond (marked
+		   (setcdr marked marked-list))
+		  (marked-list		;Non-NIL.
+		   (setq gnus-marked-assoc
+			 (cons (cons group marked-list)
+			       gnus-marked-assoc)))
+		  )))
+      )))
+
+(defun gnus-read-active-file ()
+  "Get active file from NNTP server."
+  (message "Reading active file...")
+  (if (gnus-request-list)		;Get active file from server
+      (save-excursion
+	(set-buffer nntp-server-buffer)
+	;; Save OLD active info.
+	(setq gnus-octive-hashtb gnus-active-hashtb)
+	(setq gnus-active-hashtb (gnus-make-hashtable))
+	(gnus-active-to-gnus-format)
+	(message "Reading active file... done"))
+    (error "Cannot read active file from NNTP server.")))
+
+(defun gnus-active-to-gnus-format ()
+  "Convert active file format to internal format."
+  ;; Delete unnecessary lines.
+  (goto-char (point-min))
+  (delete-matching-lines "^to\\..*$")
+  ;; Store active file in hashtable.
+  (goto-char (point-min))
+  (while
+      (re-search-forward
+       "^\\([^ \t]+\\)[ \t]+\\([0-9]+\\)[ \t]+\\([0-9]+\\)[ \t]+\\([ymn]\\).*$"
+       nil t)
+    (gnus-sethash
+     (buffer-substring (match-beginning 1) (match-end 1))
+     (list (buffer-substring (match-beginning 1) (match-end 1))
+	   (string-equal
+	    "y" (buffer-substring (match-beginning 4) (match-end 4)))
+	   (cons (string-to-int
+		  (buffer-substring (match-beginning 3) (match-end 3)))
+		 (string-to-int
+		  (buffer-substring (match-beginning 2) (match-end 2)))))
+     gnus-active-hashtb)))
+
+(defun gnus-read-newsrc-file (&optional rawfile)
+  "Read startup FILE.
+If optional argument RAWFILE is non-nil, the raw startup file is read."
+  (setq gnus-current-startup-file
+	(let* ((file (expand-file-name gnus-startup-file nil))
+	       (real-file (concat file "-" gnus-nntp-server)))
+	  (if (file-exists-p real-file)
+	      real-file file)))
+  ;; Reset variables which may be included in the quick startup file.
+  (let ((variables gnus-variable-list))
+    (while variables
+      (set (car variables) nil)
+      (setq variables (cdr variables))))
+  (let* ((newsrc-file gnus-current-startup-file)
+	 (quick-file (concat newsrc-file ".el"))
+	 (quick-loaded nil)
+	 (newsrc-mod (nth 5 (file-attributes newsrc-file)))
+	 (quick-mod (nth 5 (file-attributes quick-file))))
+    (save-excursion
+      ;; Prepare .newsrc buffer.
+      (set-buffer (find-file-noselect newsrc-file))
+      ;; It is not so good idea turning off undo.
+      ;;(buffer-flush-undo (current-buffer))
+      ;; Load quick .newsrc to restore gnus-marked-assoc and
+      ;; gnus-killed-assoc even if gnus-newsrc-assoc is out of date.
+      (condition-case nil
+	  (setq quick-loaded (load quick-file t t t))
+	(error nil))
+      (cond ((and (not rawfile)		;Not forced to read the raw file.
+		  (or (and (fboundp 'file-newer-than-file-p)
+			   (file-newer-than-file-p quick-file newsrc-file))
+		      (and newsrc-mod quick-mod
+			   ;; .newsrc.el is newer than .newsrc.
+			   ;; Some older version does not support function
+			   ;; `file-newer-than-file-p'.
+			   (or (< (car newsrc-mod) (car quick-mod))
+			       (and (= (car newsrc-mod) (car quick-mod))
+				    (<= (nth 1 newsrc-mod) (nth 1 quick-mod))))
+			   ))
+		  quick-loaded
+		  gnus-newsrc-assoc	;Really loaded?
+		  )
+	     ;; We don't have to read the raw startup file.
+	     )
+	    (t
+	     ;; Since .newsrc file is newer than quick file, read it.
+	     (message "Reading %s..." newsrc-file)
+	     (gnus-newsrc-to-gnus-format)
+	     (gnus-check-killed-newsgroups)
+	     (message "Reading %s... Done" newsrc-file)))
+      )))
+
+(defun gnus-make-newsrc-file (file)
+  "Make server dependent file name by catenating FILE and server host name."
+  (let* ((file (expand-file-name file nil))
+	 (real-file (concat file "-" gnus-nntp-server)))
+    (if (file-exists-p real-file)
+	real-file file)
+    ))
+
+(defun gnus-newsrc-to-gnus-format ()
+  "Parse current buffer as .newsrc file."
+  (let ((newsgroup nil)
+	(subscribe nil)
+	(ranges nil)
+	(subrange nil)
+	(read-list nil))
+    ;; We have to re-initialize these variable (except for
+    ;; gnus-marked-assoc and gnus-killed-assoc) because quick startup
+    ;; file may contain bogus values.
+    (setq gnus-newsrc-options nil)
+    (setq gnus-newsrc-options-n-yes nil)
+    (setq gnus-newsrc-options-n-no nil)
+    (setq gnus-newsrc-assoc nil)
+    ;; Save options line to variable.
+    ;; Lines beginning with white spaces are treated as continuation
+    ;; line.  Refer man page of newsrc(5).
+    (goto-char (point-min))
+    (if (re-search-forward
+	 "^[ \t]*options[ \t]*\\(.*\\(\n[ \t]+.*\\)*\\)[ \t]*$" nil t)
+	(progn
+	  ;; Save entire options line.
+	  (setq gnus-newsrc-options
+		(buffer-substring (match-beginning 1) (match-end 1)))
+	  ;; Compile "-n" option.
+	  (if (string-match "\\(^\\|[ \t\n]\\)-n" gnus-newsrc-options)
+	      (let ((yes-and-no
+		     (gnus-parse-n-options
+		      (substring gnus-newsrc-options (match-end 0)))))
+		(setq gnus-newsrc-options-n-yes (car yes-and-no))
+		(setq gnus-newsrc-options-n-no  (cdr yes-and-no))
+		))
+	  ))
+    ;; Parse body of .newsrc file
+    ;; Options line continuation lines must be also considered here.
+    ;; Before supporting continuation lines, " newsgroup ! 1-5" was
+    ;; okay, but now it is invalid.  It should be "newsgroup! 1-5".
+    (goto-char (point-min))
+    ;; Due to overflows in regex.c, change the following regexp:
+    ;; "^\\([^:! \t\n]+\\)\\([:!]\\)[ \t]*\\(.*\\)$"
+    ;; Suggested by composer@bucsf.bu.edu (Jeff Kellem).
+    (while (re-search-forward
+	    "^\\([^:! \t\n]+\\)\\([:!]\\)[ \t]*\\(\\(...\\)*.*\\)$" nil t)
+      (setq newsgroup (buffer-substring (match-beginning 1) (match-end 1)))
+      ;; Check duplications of newsgroups.
+      ;; Note: Checking the duplications takes very long time.
+      (if (assoc newsgroup gnus-newsrc-assoc)
+	  (message "Ignore duplicated newsgroup: %s" newsgroup)
+	(setq subscribe
+	      (string-equal
+	       ":" (buffer-substring (match-beginning 2) (match-end 2))))
+	(setq ranges (buffer-substring (match-beginning 3) (match-end 3)))
+	(setq read-list nil)
+	(while (string-match "^[, \t]*\\([0-9-]+\\)" ranges)
+	  (setq subrange (substring ranges (match-beginning 1) (match-end 1)))
+	  (setq ranges (substring ranges (match-end 1)))
+	  (cond ((string-match "^\\([0-9]+\\)-\\([0-9]+\\)$" subrange)
+		 (setq read-list
+		       (cons
+			(cons (string-to-int
+			       (substring subrange
+					  (match-beginning 1) (match-end 1)))
+			      (string-to-int
+			       (substring subrange
+					  (match-beginning 2) (match-end 2))))
+			read-list)))
+		((string-match "^[0-9]+$" subrange)
+		 (setq read-list
+		       (cons (cons (string-to-int subrange)
+				   (string-to-int subrange))
+			     read-list)))
+		(t
+		 (ding) (message "Ignoring bogus lines of %s" newsgroup)
+		 (sit-for 0))
+		))
+	(setq gnus-newsrc-assoc
+	      (cons (cons newsgroup (cons subscribe (nreverse read-list)))
+		    gnus-newsrc-assoc))
+	))
+    (setq gnus-newsrc-assoc
+	  (nreverse gnus-newsrc-assoc))
+    ))
+
+(defun gnus-parse-n-options (options)
+  "Parse -n NEWSGROUPS options and return a cons of YES and NO regexps."
+  (let ((yes nil)
+	(no nil)
+	(yes-or-no nil)			;`!' or not.
+	(newsgroup nil))
+    ;; Parse each newsgroup description such as "comp.all".  Commas
+    ;; and white spaces can be a newsgroup separator.
+    (while
+	(string-match "^[ \t\n,]*\\(!?\\)\\([^--- \t\n,][^ \t\n,]*\\)" options)
+      (setq yes-or-no
+	    (substring options (match-beginning 1) (match-end 1)))
+      (setq newsgroup
+	    (regexp-quote
+	     (substring options
+			(match-beginning 2) (match-end 2))))
+      (setq options (substring options (match-end 2)))
+      ;; Rewrite "all" to ".+" not ".*".  ".+" requires at least one
+      ;; character.
+      (while (string-match "\\(^\\|\\\\[.]\\)all\\(\\\\[.]\\|$\\)" newsgroup)
+	(setq newsgroup
+	      (concat (substring newsgroup 0 (match-end 1))
+		      ".+"
+		      (substring newsgroup (match-beginning 2)))))
+      (cond ((string-equal yes-or-no "!")
+	     (setq no (cons newsgroup no)))
+	    ((string-equal newsgroup ".+")) ;Ignore `all'.
+	    (t
+	     (setq yes (cons newsgroup yes)))
+	    ))
+    ;; Make a cons of regexps from parsing result.
+    (cons (if yes
+	      (concat "^\\("
+		      (apply (function concat)
+			     (mapcar
+			      (function
+			       (lambda (newsgroup)
+				 (concat newsgroup "\\|")))
+			      (cdr yes)))
+		      (car yes) "\\)"))
+	  (if no
+	      (concat "^\\("
+		      (apply (function concat)
+			     (mapcar
+			      (function
+			       (lambda (newsgroup)
+				 (concat newsgroup "\\|")))
+			      (cdr no)))
+		      (car no) "\\)")))
+    ))
+
+(defun gnus-save-newsrc-file ()
+  "Save to .newsrc FILE."
+  ;; Note: We cannot save .newsrc file if all newsgroups are removed
+  ;; from the variable gnus-newsrc-assoc.
+  (and (or gnus-newsrc-assoc gnus-killed-assoc)
+       gnus-current-startup-file
+       (save-excursion
+	 ;; A buffer containing .newsrc file may be deleted.
+	 (set-buffer (find-file-noselect gnus-current-startup-file))
+	 (if (not (buffer-modified-p))
+	     (message "(No changes need to be saved)")
+	   (message "Saving %s..." gnus-current-startup-file)
+	   (let ((make-backup-files t)
+		 (version-control nil)
+		 (require-final-newline t)) ;Don't ask even if requested.
+	     ;; Make backup file of master newsrc.
+	     ;; You can stop or change version control of backup file.
+	     ;; Suggested by jason@violet.berkeley.edu.
+	     (run-hooks 'gnus-Save-newsrc-hook)
+	     (save-buffer))
+	   ;; Quickly loadable .newsrc.
+	   (set-buffer (get-buffer-create " *GNUS-newsrc*"))
+	   (buffer-flush-undo (current-buffer))
+	   (erase-buffer)
+	   (gnus-gnus-to-quick-newsrc-format)
+	   (let ((make-backup-files nil)
+		 (version-control nil)
+		 (require-final-newline t)) ;Don't ask even if requested.
+	     (write-file (concat gnus-current-startup-file ".el")))
+	   (kill-buffer (current-buffer))
+	   (message "Saving %s... Done" gnus-current-startup-file)
+	   ))
+    ))
+
+(defun gnus-update-newsrc-buffer (group &optional delete next)
+  "Incrementally update .newsrc buffer about GROUP.
+If optional second argument DELETE is non-nil, delete the group.
+If optional third argument NEXT is non-nil, inserted before it."
+  (save-excursion
+    ;; Taking account of the killed startup file.
+    ;; Suggested by tale@pawl.rpi.edu.
+    (set-buffer (or (get-file-buffer gnus-current-startup-file)
+		    (find-file-noselect gnus-current-startup-file)))
+    ;; Options line continuation lines must be also considered here.
+    ;; Before supporting continuation lines, " newsgroup ! 1-5" was
+    ;; okay, but now it is invalid.  It should be "newsgroup! 1-5".
+    (let ((deleted nil)
+	  (buffer-read-only nil))	;May be not modifiable.
+      ;; Delete ALL entries which match for GROUP.
+      (goto-char (point-min))
+      (while (re-search-forward
+	      (concat "^" (regexp-quote group) "[:!]") nil t)
+	(beginning-of-line)
+	(delete-region (point) (progn (forward-line 1) (point)))
+	(setq deleted t)		;Old entry is deleted.
+	)
+      (if delete
+	  nil
+	;; Insert group entry.
+	(let ((newsrc (assoc group gnus-newsrc-assoc)))
+	  (if (null newsrc)
+	      nil
+	    ;; Find insertion point.
+	    (cond (deleted nil)		;Insert here.
+		  ((and (stringp next)
+			(progn
+			  (goto-char (point-min))
+			  (re-search-forward
+			   (concat "^" (regexp-quote next) "[:!]") nil t)))
+		   (beginning-of-line))
+		  (t
+		   (goto-char (point-max))
+		   (or (bolp)
+		       (insert "\n"))))
+	    ;; Insert after options line.
+	    (if (looking-at "^[ \t]*options\\([ \t]\\|$\\)")
+		(progn
+		  (forward-line 1)
+		  ;; Skip continuation lines.
+		  (while (and (not (eobp))
+			      (looking-at "^[ \t]+"))
+		    (forward-line 1))))
+	    (insert group		;Group name
+		    (if (nth 1 newsrc) ": " "! ")) ;Subscribed?
+	    (gnus-ranges-to-newsrc-format (nthcdr 2 newsrc)) ;Read articles
+	    (insert "\n")
+	    )))
+      )))
+
+(defun gnus-gnus-to-quick-newsrc-format ()
+  "Insert GNUS variables such as `gnus-newsrc-assoc' in Lisp format."
+  (insert ";; GNUS internal format of .newsrc.\n")
+  (insert ";; Touch .newsrc instead if you think to remove this file.\n")
+  (let ((variable nil)
+	(variables gnus-variable-list)
+	;; Temporary rebind to make changes invisible.
+	(gnus-killed-assoc gnus-killed-assoc))
+    ;; Remove duplicated or unsubscribed newsgroups in gnus-killed-assoc.
+    (gnus-check-killed-newsgroups)
+    ;; Then, insert lisp expressions.
+    (while variables
+      (setq variable (car variables))
+      (and (boundp variable)
+	   (symbol-value variable)
+	   (insert "(setq " (symbol-name variable) " '"
+		   (prin1-to-string (symbol-value variable))
+		   ")\n"))
+      (setq variables (cdr variables)))
+    ))
+
+(defun gnus-ranges-to-newsrc-format (ranges)
+  "Insert ranges of read articles."
+  (let ((range nil))			;Range is a pair of BEGIN and END.
+    (while ranges
+      (setq range (car ranges))
+      (setq ranges (cdr ranges))
+      (cond ((= (car range) (cdr range))
+	     (if (= (car range) 0)
+		 (setq ranges nil)	;No unread articles.
+	       (insert (int-to-string (car range)))
+	       (if ranges (insert ","))
+	       ))
+	    (t
+	     (insert (int-to-string (car range))
+		     "-"
+		     (int-to-string (cdr range)))
+	     (if ranges (insert ","))
+	     ))
+      )))
+
+(defun gnus-compress-sequence (numbers)
+  "Convert list of sorted numbers to ranges."
+  (let* ((numbers (sort (copy-sequence numbers) (function <)))
+	 (first (car numbers))
+	 (last (car numbers))
+	 (result nil))
+    (while numbers
+      (cond ((= last (car numbers)) nil) ;Omit duplicated number
+	    ((= (1+ last) (car numbers)) ;Still in sequence
+	     (setq last (car numbers)))
+	    (t				;End of one sequence
+	     (setq result (cons (cons first last) result))
+	     (setq first (car numbers))
+	     (setq last  (car numbers)))
+	    )
+      (setq numbers (cdr numbers))
+      )
+    (nreverse (cons (cons first last) result))
+    ))
+
+(defun gnus-uncompress-sequence (ranges)
+  "Expand compressed format of sequence."
+  (let ((first nil)
+	(last  nil)
+	(result nil))
+    (while ranges
+      (setq first (car (car ranges)))
+      (setq last  (cdr (car ranges)))
+      (while (< first last)
+	(setq result (cons first result))
+	(setq first (1+ first)))
+      (setq result (cons first result))
+      (setq ranges (cdr ranges))
+      )
+    (nreverse result)
+    ))
+
+(defun gnus-number-of-articles (range)
+  "Compute number of articles from RANGE `((beg1 . end1) (beg2 . end2) ...)'."
+  (let ((count 0))
+    (while range
+      (if (/= (cdr (car range)) 0)
+	  ;; If end1 is 0, it must be skipped. Usually no articles in
+	  ;;  this group.
+	  (setq count (+ count 1 (- (cdr (car range)) (car (car range))))))
+      (setq range (cdr range))
+      )
+    count				;Result
+    ))
+
+(defun gnus-difference-of-range (src obj)
+  "Compute (SRC - OBJ) on range.
+Range of SRC is expressed as `(beg . end)'.
+Range of OBJ is expressed as `((beg1 . end1) (beg2 . end2) ...)."
+  (let ((beg (car src))
+	(end (cdr src))
+	(range nil))			;This is result.
+    ;; Src may be nil.
+    (while (and src obj)
+      (let ((beg1 (car (car obj)))
+	    (end1 (cdr (car obj))))
+	(cond ((> beg end)
+	       (setq obj nil))		;Terminate loop
+	      ((< beg beg1)
+	       (setq range (cons (cons beg (min (1- beg1) end)) range))
+	       (setq beg (1+ end1)))
+	      ((>= beg beg1)
+	       (setq beg (max beg (1+ end1))))
+	      )
+	(setq obj (cdr obj))		;Next OBJ
+	))
+    ;; Src may be nil.
+    (if (and src (<= beg end))
+	(setq range (cons (cons beg end) range)))
+    ;; Result
+    (if range
+	(nreverse range)
+      (list (cons 0 0)))
+    ))
+
+
+;;Local variables:
+;;eval: (put 'gnus-eval-in-buffer-window 'lisp-indent-function 1)
+;;end:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/compare-w.el	Thu May 09 21:50:45 1991 +0000
@@ -0,0 +1,129 @@
+;; Compare text between windows for Emacs.
+;; Copyright (C) 1986, 1989 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 1, 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.
+
+(provide 'compare-w)
+
+(defvar compare-windows-whitespace " \t\n"
+  "*String of characters considered whitespace for \\[compare-windows].
+Changes in whitespace are optionally ignored.
+
+The value of `compare-windows-whitespace' may instead be a function; this
+function is called in each buffer, with point at the current scanning point.
+The function's job is to categorize any whitespace around (including before)
+point; it should also advance past any whitespace.
+
+The function is passed one argument, the point where `compare-windows'
+was originally called; it should not consider any text before that point.
+If the function returns the same value for both buffers, then the
+whitespace is considered to match, and is skipped.")
+
+(defvar compare-ignore-case nil
+  "*Non-nil means \\[compare-windows] ignores case differences.")
+
+;;;###autoload
+(defun compare-windows (ignore-whitespace)
+  "Compare text in current window with text in next window.
+Compares the text starting at point in each window,
+moving over text in each one as far as they match.
+
+A prefix arg means ignore changes in whitespace.
+The variable `compare-windows-whitespace' controls how whitespace is skipped.
+If `compare-ignore-case' is non-nil, changes in case are also ignored."
+  (interactive "P")
+  (let* (p1 p2 maxp1 maxp2 b1 b2 w2
+	    success size
+	    (opoint1 (point))
+	    opoint2
+	    (skip-whitespace (if ignore-whitespace
+				 compare-windows-whitespace))
+	    (skip-whitespace-regexp (concat "[" skip-whitespace "]+")))
+    (setq p1 (point) b1 (current-buffer))
+    (setq w2 (next-window (selected-window)))
+    (if (eq w2 (selected-window))
+	(error "No other window"))
+    (setq p2 (window-point w2)
+	  b2 (window-buffer w2))
+    (setq opoint2 p2)
+    (setq maxp1 (point-max))
+    (save-excursion
+      (set-buffer b2)
+      (setq maxp2 (point-max)))
+
+    (setq success t)
+    (while success
+      (setq success nil)
+      ;; if interrupted, show how far we've gotten
+      (goto-char p1)
+      (set-window-point w2 p2)
+
+      ;; If both buffers have whitespace next to point,
+      ;; optionally skip over it.
+
+      (and skip-whitespace
+	   (save-excursion
+	     (let (p1a p2a w1 w2 result1 result2)
+	       (if (stringp skip-whitespace)
+		   (progn
+		     (if (not (eobp))
+			 (skip-chars-backward skip-whitespace opoint1))
+		     (and (looking-at skip-whitespace-regexp)
+			  (setq p1a (match-end 0) result1 t)))
+		 (setq result1 (funcall skip-whitespace opoint1))
+		 (setq p1a (point)))
+	       (set-buffer b2)
+	       (goto-char p2)
+	       (if (stringp skip-whitespace)
+		   (progn
+		     (if (not (eobp))
+			 (skip-chars-backward skip-whitespace opoint2))
+		     (and (looking-at skip-whitespace-regexp)
+			  (setq p2a (match-end 0) result2 t)))
+		 (setq result2 (funcall skip-whitespace opoint2))
+		 (setq p2a (point)))
+	       (and result1 result2 (eq result1 result2)
+		    (setq p1 p1a
+			  p2 p2a)))))
+
+      ;; Try advancing comparing 1000 chars at a time.
+      ;; When that fails, go 500 chars at a time, and so on.
+      (let ((size 1000)
+	    success-1)
+	(while (> size 0)
+	  (setq success-1 t)
+	  (while success-1
+	    (setq size (min size (- maxp1 p1) (- maxp2 p2)))
+	    (save-excursion
+	      (set-buffer b2)
+	      (setq s2 (buffer-substring p2 (+ size p2))))
+	    (setq success-1
+		  (and (> size 0)
+		       (if compare-ignore-case
+			   (let ((case-fold-search t))
+			     (save-excursion
+			       (search-forward s2 (+ p1 size) t)))
+			 (equal (buffer-substring p1 (+ size p1)) s2))))
+	    (if success-1
+		(setq p1 (+ p1 size) p2 (+ p2 size)
+		      success t)))
+	  (setq size (/ size 2)))))
+
+    (goto-char p1)
+    (set-window-point w2 p2)
+    (if (= (point) opoint1)
+	(ding))))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/emacs-lisp/cl-indent.el	Thu May 09 21:50:45 1991 +0000
@@ -0,0 +1,466 @@
+;; Lisp mode, and its idiosyncratic commands.
+;; Copyright (C) 1987 Free Software Foundation, Inc.
+;; Written by Richard Mlynarik July 1987
+
+;; 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 1, 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.
+
+;;>> TODO
+;; :foo
+;;   bar
+;; :baz
+;;   zap
+;; &key (like &body)??
+
+;; &rest 1 in lambda-lists doesn't work
+;;  -- really want (foo bar
+;;                  baz)
+;;     not (foo bar
+;;              baz)
+;;  Need something better than &rest for such cases
+
+
+;;; Hairy lisp indentation.
+
+(defvar lisp-indent-maximum-backtracking 3
+  "*Maximum depth to backtrack out from a sublist for structured indentation.
+If this variable is  0, no backtracking will occur and forms such as flet
+may not be correctly indented.")
+
+(defvar lisp-tag-indentation 1
+  "*Indentation of tags relative to containing list.
+This variable is used by the function `lisp-indent-tagbody'.")
+
+(defvar lisp-tag-body-indentation 3
+  "*Indentation of non-tagged lines relative to containing list.
+This variable is used by the function `lisp-indent-tagbody' to indent normal
+lines (lines without tags).
+The indentation is relative to the indentation of the parenthesis enclosing
+the special form.  If the value is t, the body of tags will be indented
+as a block at the same indentation as the first s-expression following
+the tag.  In this case, any forms before the first tag are indented
+by `lisp-body-indent'.")
+
+
+;;;###autoload
+(defun common-lisp-indent-function (indent-point state)
+  (let ((normal-indent (current-column)))
+    ;; Walk up list levels until we see something
+    ;;  which does special things with subforms.
+    (let ((depth 0)
+          ;; Path describes the position of point in terms of
+          ;;  list-structure with respect to contining lists.
+          ;; `foo' has a path of (0 4 1) in `((a b c (d foo) f) g)'
+          (path ())
+          ;; set non-nil when somebody works out the indentation to use
+          calculated
+          (last-point indent-point)
+          ;; the position of the open-paren of the innermost containing list
+          (containing-form-start (elt state 1))
+          ;; the column of the above
+          sexp-column)
+      ;; Move to start of innermost containing list
+      (goto-char containing-form-start)
+      (setq sexp-column (current-column))
+      ;; Look over successively less-deep containing forms
+      (while (and (not calculated)
+                  (< depth lisp-indent-maximum-backtracking))
+        (let ((containing-sexp (point)))
+          (forward-char 1)
+          (parse-partial-sexp (point) indent-point 1 t)
+          ;; Move to the car of the relevant containing form
+          (let (tem function method)
+            (if (not (looking-at "\\sw\\|\\s_"))
+                ;; This form doesn't seem to start with a symbol
+                (setq function nil method nil)
+              (setq tem (point))
+              (forward-sexp 1)
+              (setq function (downcase (buffer-substring tem (point))))
+              (goto-char tem)
+              (setq tem (intern-soft function)
+                    method (get tem 'common-lisp-indent-function))
+              (cond ((and (null method)
+                          (string-match ":[^:]+" function))
+                     ;; The pleblisp package feature
+                     (setq function (substring function
+                                               (1+ (match-beginning 0)))
+                           method (get (intern-soft function)
+                                       'common-lisp-indent-function)))
+                    ((and (null method))
+                     ;; backwards compatibility
+                     (setq method (get tem 'lisp-indent-function)))))
+            (let ((n 0))
+              ;; How far into the containing form is the current form?
+              (if (< (point) indent-point)
+                  (while (condition-case ()
+                             (progn
+                               (forward-sexp 1)
+                               (if (>= (point) indent-point)
+                                   nil
+                                 (parse-partial-sexp (point)
+                                                     indent-point 1 t)
+                                 (setq n (1+ n))
+                                 t))
+                           (error nil))))
+              (setq path (cons n path)))
+
+            ;; backwards compatibility.
+            (cond ((null function))
+                  ((null method)
+                   (if (null (cdr path))
+                       ;; (package prefix was stripped off above)
+                       (setq method (cond ((string-match "\\`def"
+                                                         function)
+                                           '(4 (&whole 4 &rest 1) &body))
+                                          ((string-match "\\`\\(with\\|do\\)-"
+                                                         function)
+                                           '(4 &body))))))
+                  ;; backwards compatibility.  Bletch.
+                  ((eq method 'defun)
+                   (setq method '(4 (&whole 4 &rest 1) &body))))
+
+            (cond ((and (memq (char-after (1- containing-sexp)) '(?\' ?\`))
+                        (not (eql (char-after (- containing-sexp 2)) ?\#)))
+                   ;; No indentation for "'(...)" elements
+                   (setq calculated (1+ sexp-column)))
+		  ((or (eql (char-after (1- containing-sexp)) ?\,)
+		       (and (eql (char-after (1- containing-sexp)) ?\@)
+			    (eql (char-after (- containing-sexp 2)) ?\,)))
+		   ;; ",(...)" or ",@(...)"
+		   (setq calculated normal-indent))
+                  ((eql (char-after (1- containing-sexp)) ?\#)
+                   ;; "#(...)"
+                   (setq calculated (1+ sexp-column)))
+                  ((null method))
+                  ((integerp method)
+                   ;; convenient top-level hack.
+                   ;;  (also compatible with lisp-indent-function)
+                   ;; The number specifies how many `distinguished'
+                   ;;  forms there are before the body starts
+                   ;; Equivalent to (4 4 ... &body)
+                   (setq calculated (cond ((cdr path)
+                                           normal-indent)
+                                          ((<= (car path) method)
+                                           ;; `distinguished' form
+                                           (list (+ sexp-column 4)
+                                                 containing-form-start))
+                                          ((= (car path) (1+ method))
+                                           ;; first body form.
+                                           (+ sexp-column lisp-body-indent))
+                                          (t
+                                           ;; other body form
+                                           normal-indent))))
+                  ((symbolp method)
+                   (setq calculated (funcall method
+                                             path state indent-point
+                                             sexp-column normal-indent)))
+                  (t
+                   (setq calculated (lisp-indent-259
+                                      method path state indent-point
+                                      sexp-column normal-indent)))))
+          (goto-char containing-sexp)
+          (setq last-point containing-sexp)
+          (if (not calculated)
+              (condition-case ()
+                   (progn (backward-up-list 1)
+                          (setq depth (1+ depth)))
+                (error (setq depth lisp-indent-maximum-backtracking))))))
+      calculated)))
+
+
+(defun lisp-indent-report-bad-format (m)
+  (error "%s has a badly-formed %s property: %s"
+         ;; Love those free variable references!!
+         function 'common-lisp-indent-function m))
+
+;; Blame the crufty control structure on dynamic scoping
+;;  -- not on me!
+(defun lisp-indent-259 (method path state indent-point
+                        sexp-column normal-indent)
+  (catch 'exit
+    (let ((p path)
+          (containing-form-start (elt state 1))
+          n tem tail)
+      ;; Isn't tail-recursion wonderful?
+      (while p
+        ;; This while loop is for destructuring.
+        ;; p is set to (cdr p) each iteration.
+        (if (not (consp method)) (lisp-indent-report-bad-format method))
+        (setq n (1- (car p))
+              p (cdr p)
+              tail nil)
+        (while n
+          ;; This while loop is for advancing along a method
+          ;; until the relevant (possibly &rest/&body) pattern
+          ;; is reached.
+          ;; n is set to (1- n) and method to (cdr method)
+          ;; each iteration.
+          (setq tem (car method))
+
+          (or (eq tem 'nil)             ;default indentation
+;             (eq tem '&lambda)         ;abbrev for (&whole 4 (&rest 1))
+              (and (eq tem '&body) (null (cdr method)))
+              (and (eq tem '&rest)
+                   (consp (cdr method)) (null (cdr (cdr method))))
+              (integerp tem)            ;explicit indentation specified
+              (and (consp tem)          ;destructuring
+                   (eq (car tem) '&whole)
+                   (or (symbolp (car (cdr tem)))
+                       (integerp (car (cdr tem)))))
+              (and (symbolp tem)        ;a function to call to do the work.
+                   (null (cdr method)))
+              (lisp-indent-report-bad-format method))
+
+          (cond ((and tail (not (consp tem)))
+                 ;; indent tail of &rest in same way as first elt of rest
+                 (throw 'exit normal-indent))
+                ((eq tem '&body)
+                 ;; &body means (&rest <lisp-body-indent>)
+                 (throw 'exit
+                   (if (and (= n 0)     ;first body form
+                            (null p))   ;not in subforms
+                       (+ sexp-column
+                          lisp-body-indent)
+                       normal-indent)))
+                ((eq tem '&rest)
+                 ;; this pattern holds for all remaining forms
+                 (setq tail (> n 0)
+                       n 0
+                       method (cdr method)))
+                ((> n 0)
+                 ;; try next element of pattern
+                 (setq n (1- n)
+                       method (cdr method))
+                 (if (< n 0)
+                     ;; Too few elements in pattern.
+                     (throw 'exit normal-indent)))
+                ((eq tem 'nil)
+                 (throw 'exit (list normal-indent containing-form-start)))
+;               ((eq tem '&lambda)
+;                ;; abbrev for (&whole 4 &rest 1)
+;                (throw 'exit
+;                  (cond ((null p)
+;                         (list (+ sexp-column 4) containing-form-start))
+;                        ((null (cdr p))
+;                         (+ sexp-column 1))
+;                        (t normal-indent))))
+                ((integerp tem)
+                 (throw 'exit
+                   (if (null p)         ;not in subforms
+                       (list (+ sexp-column tem) containing-form-start)
+                       normal-indent)))
+                ((symbolp tem)          ;a function to call
+                 (throw 'exit
+                   (funcall tem path state indent-point
+                            sexp-column normal-indent)))
+                (t
+                 ;; must be a destructing frob
+                 (if (not (null p))
+                     ;; descend
+                     (setq method (cdr (cdr tem))
+                           n nil)
+                   (setq tem (car (cdr tem)))
+                   (throw 'exit
+                     (cond (tail
+                            normal-indent)
+                           ((eq tem 'nil)
+                            (list normal-indent
+                                  containing-form-start))
+                           ((integerp tem)
+                            (list (+ sexp-column tem)
+                                  containing-form-start))
+                           (t
+                            (funcall tem path state indent-point
+                                     sexp-column normal-indent))))))))))))
+
+(defun lisp-indent-tagbody (path state indent-point sexp-column normal-indent)
+  (if (not (null (cdr path)))
+      normal-indent
+    (save-excursion
+      (goto-char indent-point)
+      (beginning-of-line)
+      (skip-chars-forward " \t")
+      (list (cond ((looking-at "\\sw\\|\\s_")
+                   ;; a tagbody tag
+                   (+ sexp-column lisp-tag-indentation))
+                  ((integerp lisp-tag-body-indentation)
+                   (+ sexp-column lisp-tag-body-indentation))
+                  ((eq lisp-tag-body-indentation 't)
+                   (condition-case ()
+                       (progn (backward-sexp 1) (current-column))
+                     (error (1+ sexp-column))))
+                  (t (+ sexp-column lisp-body-indent)))
+;            (cond ((integerp lisp-tag-body-indentation)
+;                   (+ sexp-column lisp-tag-body-indentation))
+;                  ((eq lisp-tag-body-indentation 't)
+;                   normal-indent)
+;                  (t
+;                   (+ sexp-column lisp-body-indent)))
+            (elt state 1)
+            ))))
+
+(defun lisp-indent-do (path state indent-point sexp-column normal-indent)
+  (if (>= (car path) 3)
+      (let ((lisp-tag-body-indentation lisp-body-indent))
+        (funcall (function lisp-indent-tagbody)
+		 path state indent-point sexp-column normal-indent))
+    (funcall (function lisp-indent-259)
+	     '((&whole nil &rest
+ 		;; the following causes wierd indentation
+ 		;;(&whole 1 1 2 nil)
+		)
+	       (&whole nil &rest 1))
+	     path state indent-point sexp-column normal-indent)))
+
+(defun lisp-indent-function-lambda-hack (path state indent-point
+                                         sexp-column normal-indent)
+  ;; indent (function (lambda () <newline> <body-forms>)) kludgily.
+  (if (or (cdr path) ; wtf?
+          (> (car path) 3))
+      ;; line up under previous body form
+      normal-indent
+    ;; line up under function rather than under lambda in order to
+    ;;  conserve horizontal space.  (Which is what #' is for.)
+    (condition-case ()
+        (save-excursion
+          (backward-up-list 2)
+          (forward-char 1)
+          (if (looking-at "\\(lisp:+\\)?function\\(\\Sw\\|\\S_\\)")
+              (+ lisp-body-indent -1 (current-column))
+              (+ sexp-column lisp-body-indent)))
+       (error (+ sexp-column lisp-body-indent)))))
+
+
+(let ((l '((block 1)
+	   (catch 1)
+           (case        (4 &rest (&whole 2 &rest 1)))
+           (ccase . case) (ecase . case)
+           (typecase . case) (etypecase . case) (ctypecase . case)
+           (catch 1)
+           (cond        (&rest (&whole 2 &rest 1)))
+           (block 1)
+           (defvar      (4 2 2))
+           (defconstant . defvar) (defparameter . defvar)
+           (define-modify-macro
+                        (4 &body))
+           (define-setf-method
+                        (4 (&whole 4 &rest 1) &body))
+           (defsetf     (4 (&whole 4 &rest 1) 4 &body))
+           (defun       (4 (&whole 4 &rest 1) &body))
+           (defmacro . defun) (deftype . defun)
+           (defstruct   ((&whole 4 &rest (&whole 2 &rest 1))
+                         &rest (&whole 2 &rest 1)))
+           (destructuring-bind
+                        ((&whole 6 &rest 1) 4 &body))
+           (do          lisp-indent-do)
+           (do* . do)
+           (dolist      ((&whole 4 2 1) &body))
+           (dotimes . dolist)
+           (eval-when	1)
+           (flet        ((&whole 4 &rest (&whole 1 (&whole 4 &rest 1) &body))
+                         &body))
+           (labels . flet)
+           (macrolet . flet)
+           ;; `else-body' style
+           (if          (nil nil &body))
+           ;; single-else style (then and else equally indented)
+           (if          (&rest nil))
+           ;(lambda     ((&whole 4 &rest 1) &body))
+           (lambda      ((&whole 4 &rest 1)
+                         &rest lisp-indent-function-lambda-hack))
+           (let         ((&whole 4 &rest (&whole 1 1 2)) &body))
+           (let* . let)
+           (compiler-let . let) ;barf
+           (locally	1)
+           ;(loop ...)
+           (multiple-value-bind
+                        ((&whole 6 &rest 1) 4 &body))
+           (multiple-value-call
+			(4 &body))
+           (multiple-value-list 1)
+           (multiple-value-prog1 1)
+           (multiple-value-setq
+			(4 2))
+           ;; Combines the worst features of BLOCK, LET and TAGBODY
+           (prog        ((&whole 4 &rest 1) &rest lisp-indent-tagbody))
+           (prog* . prog)
+           (prog1 1)
+           (prog2 2)
+           (progn 0)
+           (progv       (4 4 &body))
+           (return 0)
+           (return-from (nil &body))
+           (tagbody     lisp-indent-tagbody)
+           (throw 1)
+           (unless 1)
+           (unwind-protect
+                        (5 &body))
+           (when 1))))
+  (while l
+    (put (car (car l)) 'common-lisp-indent-function
+         (if (symbolp (cdr (car l)))
+             (get (cdr (car l)) 'common-lisp-indent-function)
+             (car (cdr (car l)))))
+    (setq l (cdr l))))
+
+
+;(defun foo (x)
+;  (tagbody
+;   foo
+;     (bar)
+;   baz
+;     (when (losing)
+;       (with-big-loser
+;           (yow)
+;         ((lambda ()
+;            foo)
+;          big)))
+;     (flet ((foo (bar baz zap)
+;              (zip))
+;            (zot ()
+;              quux))
+;       (do ()
+;           ((lose)
+;            (foo 1))
+;         (quux)
+;        foo
+;         (lose))
+;       (cond ((x)
+;              (win 1 2
+;                   (foo)))
+;             (t
+;              (lose
+;                3))))))
+          
+
+;(put 'while    'common-lisp-indent-function 1)
+;(put 'defwrapper'common-lisp-indent-function ...)
+;(put 'def 'common-lisp-indent-function ...)
+;(put 'defflavor        'common-lisp-indent-function ...)
+;(put 'defsubst 'common-lisp-indent-function ...)
+
+;(put 'with-restart 'common-lisp-indent-function '((1 4 ((* 1))) (2 &body)))
+;(put 'restart-case 'common-lisp-indent-function '((1 4) (* 2 ((0 1) (* 1)))))
+;(put 'define-condition 'common-lisp-indent-function '((1 6) (2 6 ((* 1))) (3 4 ((* 1))) (4 &body)))
+;(put 'with-condition-handler 'common-lisp-indent-function '((1 4 ((* 1))) (2 &body)))
+;(put 'condition-case 'common-lisp-indent-function '((1 4) (* 2 ((0 1) (1 3) (2 &body)))))
+
+
+;;;; Turn it on.
+;(setq lisp-indent-function 'common-lisp-indent-function)
+
+;; To disable this stuff, (setq lisp-indent-function 'lisp-indent-function)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/informat.el	Thu May 09 21:50:45 1991 +0000
@@ -0,0 +1,415 @@
+;; Info support functions package for Emacs
+;; Copyright (C) 1986 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 1, 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.
+
+(require 'info)
+
+;;;###autoload
+(defun Info-tagify ()
+  "Create or update Info-file tag table in current buffer."
+  (interactive)
+  ;; Save and restore point and restrictions.
+  ;; save-restrictions would not work
+  ;; because it records the old max relative to the end.
+  ;; We record it relative to the beginning.
+  (message "Tagifying %s ..." (file-name-nondirectory (buffer-file-name)))
+  (let ((omin (point-min))
+	(omax (point-max))
+	(nomax (= (point-max) (1+ (buffer-size))))
+	(opoint (point)))
+    (unwind-protect
+	(progn
+	  (widen)
+	  (goto-char (point-min))
+	  (if (search-forward "\^_\nIndirect:\n" nil t)
+	      (message "Cannot tagify split info file")
+	    (let ((regexp "Node:[ \t]*\\([^,\n\t]\\)*[,\t\n]")
+		  (case-fold-search t)
+		  list)
+	      (while (search-forward "\n\^_" nil t)
+		(forward-line 1)
+		(let ((beg (point)))
+		  (forward-line 1)
+		  (if (re-search-backward regexp beg t)
+		      (setq list
+			    (cons (list (buffer-substring
+					  (match-beginning 1)
+					  (match-end 1))
+					beg)
+				  list)))))
+	      (goto-char (point-max))
+	      (forward-line -8)
+	      (let ((buffer-read-only nil))
+		(if (search-forward "\^_\nEnd tag table\n" nil t)
+		    (let ((end (point)))
+		      (search-backward "\nTag table:\n")
+		      (beginning-of-line)
+		      (delete-region (point) end)))
+		(goto-char (point-max))
+		(insert "\^_\f\nTag table:\n")
+		(move-marker Info-tag-table-marker (point))
+		(setq list (nreverse list))
+		(while list
+		  (insert "Node: " (car (car list)) ?\177)
+		  (princ (car (cdr (car list))) (current-buffer))
+		  (insert ?\n)
+		  (setq list (cdr list)))
+		(insert "\^_\nEnd tag table\n")))))
+      (goto-char opoint)
+      (narrow-to-region omin (if nomax (1+ (buffer-size))
+			       (min omax (point-max))))))
+  (message "Tagifying %s ... done" (file-name-nondirectory (buffer-file-name))))
+
+;;;###autoload
+(defun Info-split ()
+  "Split an info file into an indirect file plus bounded-size subfiles.
+Each subfile will be up to 50,000 characters plus one node.
+
+To use this command, first visit a large Info file that has a tag
+table.  The buffer is modified into a (small) indirect info file which
+should be saved in place of the original visited file.
+
+The subfiles are written in the same directory the original file is
+in, with names generated by appending `-' and a number to the original
+file name.  The indirect file still functions as an Info file, but it
+contains just the tag table and a directory of subfiles."
+
+  (interactive)
+  (if (< (buffer-size) 70000)
+      (error "This is too small to be worth splitting"))
+  (goto-char (point-min))
+  (search-forward "\^_")
+  (forward-char -1)
+  (let ((start (point))
+	(chars-deleted 0)
+	subfiles
+	(subfile-number 1)
+	(case-fold-search t)
+	(filename (file-name-sans-versions buffer-file-name)))
+    (goto-char (point-max))
+    (forward-line -8)
+    (setq buffer-read-only nil)
+    (or (search-forward "\^_\nEnd tag table\n" nil t)
+	(error "Tag table required; use M-x Info-tagify"))
+    (search-backward "\nTag table:\n")
+    (if (looking-at "\nTag table:\n\^_")
+	(error "Tag table is just a skeleton; use M-x Info-tagify"))
+    (beginning-of-line)
+    (forward-char 1)
+    (save-restriction
+      (narrow-to-region (point-min) (point))
+      (goto-char (point-min))
+      (while (< (1+ (point)) (point-max))
+	(goto-char (min (+ (point) 50000) (point-max)))
+	(search-forward "\^_" nil 'move)
+	(setq subfiles
+	      (cons (list (+ start chars-deleted)
+			  (concat (file-name-nondirectory filename)
+				  (format "-%d" subfile-number)))
+		    subfiles))
+	;; Put a newline at end of split file, to make Unix happier.
+	(insert "\n")
+	(write-region (point-min) (point)
+		      (concat filename (format "-%d" subfile-number)))
+	(delete-region (1- (point)) (point))
+	;; Back up over the final ^_.
+	(forward-char -1)
+	(setq chars-deleted (+ chars-deleted (- (point) start)))
+	(delete-region start (point))
+	(setq subfile-number (1+ subfile-number))))
+    (while subfiles
+      (goto-char start)
+      (insert (nth 1 (car subfiles))
+	      (format ": %d" (car (car subfiles)))
+	      "\n")
+      (setq subfiles (cdr subfiles)))
+    (goto-char start)
+    (insert "\^_\nIndirect:\n")
+    (search-forward "\nTag Table:\n")
+    (insert "(Indirect)\n")))
+
+;;;###autoload
+(defun Info-validate ()
+  "Check current buffer for validity as an Info file.
+Check that every node pointer points to an existing node."
+  (interactive)
+  (save-excursion
+    (save-restriction
+      (widen)
+      (goto-char (point-min))
+      (if (search-forward "\nTag table:\n(Indirect)\n" nil t)
+	  (error "Don't yet know how to validate indirect info files: \"%s\""
+		 (buffer-name (current-buffer))))
+      (goto-char (point-min))
+      (let ((allnodes '(("*")))
+	    (regexp "Node:[ \t]*\\([^,\n\t]*\\)[,\t\n]")
+	    (case-fold-search t)
+	    (tags-losing nil)
+	    (lossages ()))
+	(while (search-forward "\n\^_" nil t)
+	  (forward-line 1)
+	  (let ((beg (point)))
+	    (forward-line 1)
+	    (if (re-search-backward regexp beg t)
+		(let ((name (downcase
+			      (buffer-substring
+			        (match-beginning 1)
+				(progn
+				  (goto-char (match-end 1))
+				  (skip-chars-backward " \t")
+				  (point))))))
+		  (if (assoc name allnodes)
+		      (setq lossages
+			    (cons (list name "Duplicate node-name" nil)
+				  lossages))
+		      (setq allnodes
+			    (cons (list name
+					(progn
+					  (end-of-line)
+					  (and (re-search-backward
+						"prev[ious]*:" beg t)
+					       (progn
+						 (goto-char (match-end 0))
+						 (downcase
+						   (Info-following-node-name)))))
+					beg)
+				  allnodes)))))))
+	(goto-char (point-min))
+	(while (search-forward "\n\^_" nil t)
+	  (forward-line 1)
+	  (let ((beg (point))
+		thisnode next)
+	    (forward-line 1)
+	    (if (re-search-backward regexp beg t)
+		(save-restriction
+		  (search-forward "\n\^_" nil 'move)
+		  (narrow-to-region beg (point))
+		  (setq thisnode (downcase
+				   (buffer-substring
+				     (match-beginning 1)
+				     (progn
+				       (goto-char (match-end 1))
+				       (skip-chars-backward " \t")
+				       (point)))))
+		  (end-of-line)
+		  (and (search-backward "next:" nil t)
+		       (setq next (Info-validate-node-name "invalid Next"))
+		       (assoc next allnodes)
+		       (if (equal (car (cdr (assoc next allnodes)))
+				  thisnode)
+			   ;; allow multiple `next' pointers to one node
+			   (let ((tem lossages))
+			     (while tem
+			       (if (and (equal (car (cdr (car tem)))
+					       "should have Previous")
+					(equal (car (car tem))
+					       next))
+				   (setq lossages (delq (car tem) lossages)))
+			       (setq tem (cdr tem))))
+			 (setq lossages
+			       (cons (list next
+					   "should have Previous"
+					   thisnode)
+				     lossages))))
+		  (end-of-line)
+		  (if (re-search-backward "prev[ious]*:" nil t)
+		      (Info-validate-node-name "invalid Previous"))
+		  (end-of-line)
+		  (if (search-backward "up:" nil t)
+		      (Info-validate-node-name "invalid Up"))
+		  (if (re-search-forward "\n* Menu:" nil t)
+		      (while (re-search-forward "\n\\* " nil t)
+			(Info-validate-node-name
+			  (concat "invalid menu item "
+				  (buffer-substring (point)
+						    (save-excursion
+						      (skip-chars-forward "^:")
+						      (point))))
+			  (Info-extract-menu-node-name))))
+		  (goto-char (point-min))
+		  (while (re-search-forward "\\*note[ \n]*[^:\t]*:" nil t)
+		    (goto-char (+ (match-beginning 0) 5))
+		    (skip-chars-forward " \n")
+		    (Info-validate-node-name
+		     (concat "invalid reference "
+			     (buffer-substring (point)
+					       (save-excursion
+						 (skip-chars-forward "^:")
+						 (point))))
+		     (Info-extract-menu-node-name "Bad format cross-reference")))))))
+	(setq tags-losing (not (Info-validate-tags-table)))
+	(if (or lossages tags-losing)
+	    (with-output-to-temp-buffer " *problems in info file*"
+	      (while lossages
+		(princ "In node \"")
+		(princ (car (car lossages)))
+		(princ "\", ")
+		(let ((tem (nth 1 (car lossages))))
+		  (cond ((string-match "\n" tem)
+			 (princ (substring tem 0 (match-beginning 0)))
+			 (princ "..."))
+			(t
+			 (princ tem))))
+		(if (nth 2 (car lossages))
+		    (progn
+		      (princ ": ")
+		      (let ((tem (nth 2 (car lossages))))
+			(cond ((string-match "\n" tem)
+			       (princ (substring tem 0 (match-beginning 0)))
+			       (princ "..."))
+			      (t
+			       (princ tem))))))
+		(terpri)
+		(setq lossages (cdr lossages)))
+	      (if tags-losing (princ "\nTags table must be recomputed\n")))
+	  ;; Here if info file is valid.
+	  ;; If we already made a list of problems, clear it out.
+	  (save-excursion
+	    (if (get-buffer " *problems in info file*")
+		(progn
+		  (set-buffer " *problems in info file*")
+		  (kill-buffer (current-buffer)))))
+	  (message "File appears valid"))))))
+
+(defun Info-validate-node-name (kind &optional name)
+  (if name
+      nil
+    (goto-char (match-end 0))
+    (skip-chars-forward " \t")
+    (if (= (following-char) ?\()
+	nil
+      (setq name
+	    (buffer-substring
+	     (point)
+	     (progn
+	      (skip-chars-forward "^,\t\n")
+	      (skip-chars-backward " ")
+	      (point))))))
+  (if (null name)
+      nil
+    (setq name (downcase name))
+    (or (and (> (length name) 0) (= (aref name 0) ?\())
+	(assoc name allnodes)
+	(setq lossages
+	      (cons (list thisnode kind name) lossages))))
+  name)
+
+(defun Info-validate-tags-table ()
+  (goto-char (point-min))
+  (if (not (search-forward "\^_\nEnd tag table\n" nil t))
+      t
+    (not (catch 'losing
+	   (let* ((end (match-beginning 0))
+		  (start (progn (search-backward "\nTag table:\n")
+				(1- (match-end 0))))
+		  tem)
+	     (setq tem allnodes)
+	     (while tem
+	       (goto-char start)
+	       (or (equal (car (car tem)) "*")
+		   (search-forward (concat "Node: "
+					   (car (car tem))
+					   "\177")
+				   end t)
+		   (throw 'losing 'x))
+	       (setq tem (cdr tem)))
+	     (goto-char (1+ start))
+	     (while (looking-at ".*Node: \\(.*\\)\177\\([0-9]+\\)$")
+	       (setq tem (downcase (buffer-substring
+				     (match-beginning 1)
+				     (match-end 1))))
+	       (setq tem (assoc tem allnodes))
+	       (if (or (not tem)
+		       (< 1000 (progn
+				 (goto-char (match-beginning 2))
+				 (setq tem (- (car (cdr (cdr tem)))
+					      (read (current-buffer))))
+				 (if (> tem 0) tem (- tem)))))
+		   (throw 'losing 'y)))
+	     (forward-line 1))
+	   (or (looking-at "End tag table\n")
+	       (throw 'losing 'z))
+	   nil))))
+
+;;;###autoload
+(defun batch-info-validate ()
+  "Runs `Info-validate' on the files remaining on the command line.
+Must be used only with -batch, and kills Emacs on completion.
+Each file will be processed even if an error occurred previously.
+For example, invoke \"emacs -batch -f batch-info-validate $info/ ~/*.info\""
+  (if (not noninteractive)
+      (error "batch-info-validate may only be used -batch."))
+  (let ((version-control t)
+	(auto-save-default nil)
+	(find-file-run-dired nil)
+	(kept-old-versions 259259)
+	(kept-new-versions 259259))
+    (let ((error 0)
+	  file
+	  (files ()))
+      (while command-line-args-left
+	(setq file (expand-file-name (car command-line-args-left)))
+	(cond ((not (file-exists-p file))
+	       (message ">> %s does not exist!" file)
+	       (setq error 1
+		     command-line-args-left (cdr command-line-args-left))) 
+	      ((file-directory-p file)
+	       (setq command-line-args-left (nconc (directory-files file)
+					      (cdr command-line-args-left))))
+	      (t
+	       (setq files (cons file files)
+		     command-line-args-left (cdr command-line-args-left)))))
+      (while files
+	(setq file (car files)
+	      files (cdr files))
+	(let ((lose nil))
+	  (condition-case err
+	      (progn
+		(if buffer-file-name (kill-buffer (current-buffer)))
+		(find-file file)
+		(buffer-disable-undo (current-buffer))
+		(set-buffer-modified-p nil)
+		(fundamental-mode)
+		(let ((case-fold-search nil))
+		  (goto-char (point-max))
+		  (cond ((search-backward "\n\^_\^L\nTag table:\n" nil t)
+			 (message "%s already tagified" file))
+			((< (point-max) 30000)
+			 (message "%s too small to bother tagifying" file))
+			(t
+			 (Info-tagify file))))
+		(let ((loss-name " *problems in info file*"))
+		  (message "Checking validity of info file %s..." file)
+		  (if (get-buffer loss-name)
+		      (kill-buffer loss-name))
+		  (Info-validate)
+		  (if (not (get-buffer loss-name))
+		      nil ;(message "Checking validity of info file %s... OK" file)
+		    (message "----------------------------------------------------------------------")
+		    (message ">> PROBLEMS IN INFO FILE %s" file)
+		    (save-excursion
+		      (set-buffer loss-name)
+		      (princ (buffer-substring (point-min) (point-max))))
+		    (message "----------------------------------------------------------------------")
+		    (setq error 1 lose t)))
+		(if (and (buffer-modified-p)
+			 (not lose))
+		    (progn (message "Saving modified %s" file)
+			   (save-buffer))))
+	    (error (message ">> Error: %s" (prin1-to-string err))))))
+      (kill-emacs error))))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/progmodes/awk-mode.el	Thu May 09 21:50:45 1991 +0000
@@ -0,0 +1,83 @@
+;; C code editing commands for Emacs
+;; Copyright (C) 1988 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 1, 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.
+
+
+(defvar awk-mode-syntax-table nil
+  "Syntax table in use in Awk-mode buffers.")
+
+(if awk-mode-syntax-table
+    ()
+  (setq awk-mode-syntax-table (make-syntax-table))
+  (modify-syntax-entry ?\\ "\\" awk-mode-syntax-table)
+  (modify-syntax-entry ?\n ">   " emacs-lisp-mode-syntax-table)
+  (modify-syntax-entry ?\f ">   " emacs-lisp-mode-syntax-table)
+  (modify-syntax-entry ?\# "<   " emacs-lisp-mode-syntax-table)
+  (modify-syntax-entry ?/ "." awk-mode-syntax-table)
+  (modify-syntax-entry ?* "." awk-mode-syntax-table)
+  (modify-syntax-entry ?+ "." awk-mode-syntax-table)
+  (modify-syntax-entry ?- "." awk-mode-syntax-table)
+  (modify-syntax-entry ?= "." awk-mode-syntax-table)
+  (modify-syntax-entry ?% "." awk-mode-syntax-table)
+  (modify-syntax-entry ?< "." awk-mode-syntax-table)
+  (modify-syntax-entry ?> "." awk-mode-syntax-table)
+  (modify-syntax-entry ?& "." awk-mode-syntax-table)
+  (modify-syntax-entry ?| "." awk-mode-syntax-table)
+  (modify-syntax-entry ?\' "\"" awk-mode-syntax-table))
+
+(defvar awk-mode-abbrev-table nil
+  "Abbrev table in use in Awk-mode buffers.")
+(define-abbrev-table 'awk-mode-abbrev-table ())
+
+;;;###autoload
+(defun awk-mode ()
+  "Major mode for editing AWK code.
+This is much like C mode except for the syntax of comments.  It uses
+the same keymap as C mode and has the same variables for customizing
+indentation.  It has its own abbrev table and its own syntax table.
+
+Turning on AWK mode calls the value of the variable `awk-mode-hook'
+with no args, if that value is non-nil."
+  (interactive)
+  (kill-all-local-variables)
+  (use-local-map c-mode-map)
+  (setq major-mode 'awk-mode)
+  (setq mode-name "AWK")
+  (setq local-abbrev-table awk-mode-abbrev-table)
+  (set-syntax-table awk-mode-syntax-table)
+  (make-local-variable 'paragraph-start)
+  (setq paragraph-start (concat "^$\\|" page-delimiter))
+  (make-local-variable 'paragraph-separate)
+  (setq paragraph-separate paragraph-start)
+  (make-local-variable 'paragraph-ignore-fill-prefix)
+  (setq paragraph-ignore-fill-prefix t)
+  (make-local-variable 'indent-line-function)
+  (setq indent-line-function 'awk-indent-line)
+  (make-local-variable 'require-final-newline)
+  (setq require-final-newline t)
+  (make-local-variable 'comment-start)
+  (setq comment-start "# ")
+  (make-local-variable 'comment-end)
+  (setq comment-end "")
+  (make-local-variable 'comment-column)
+  (setq comment-column 32)
+  (make-local-variable 'comment-start-skip)
+  (setq comment-start-skip "#+ *")
+  (make-local-variable 'comment-indent-hook)
+  (setq comment-indent-hook 'c-comment-indent)
+  (run-hooks 'awk-mode-hook))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/progmodes/cplus-md.el	Thu May 09 21:50:45 1991 +0000
@@ -0,0 +1,966 @@
+;; C++ code editing commands for Emacs
+;; 1987 Dave Detlefs  (dld@cs.cmu.edu) 
+;; and  Stewart Clamen (clamen@cs.cmu.edu).
+;; Done by fairly faithful modification of:
+;; c-mode.el, Copyright (C) 1985 Richard M. Stallman.
+;;
+;; Feb, 1990 (Dave Detlefs, dld@cs.cmu.edu)
+;;   Fixed electric-c++-terminator to handle double colons, at the
+;;   request of John Hagerman.
+;; 
+;; Jan, 1990 (Doug Lea, dl@oswego.edu)
+;;   Replaced c++-comment-region and c++-uncomment-region with
+;;     versions from Igor Metz that avoid potential infinite loops.
+;;
+;; Oct, 1989 (Dave Detlefs, dld@cs.cmu.edu)
+;;   Added contribution from Igor Metz <metz@iam.unibe.ch>:
+;;     functions c++-comment-region and c++-uncomment-region and 
+;;     corresponding key-binding.
+;;   Also fixed bug in indentation of second line after an empty
+;;   arglist with empty-arglist non-null.
+;;   
+;; Sept, 1989 (Glen Ditchfield, gjditchfield@violet.uwaterloo.ca):
+;;   Textual changes to more closely imitate Emacs 18.55's c-mode.
+;;   Fixed handling of "default:", where ":" was the last character in the
+;;   buffer.  Fixed indentation of comments starting in column 0, and when
+;;   previous line contained more than one comment start string.  Fixed
+;;   handling of "friend class".
+;;
+;; Aug 7, 1989; John Hagerman (hagerman@ece.cmu.edu):
+;;   Changed calculate-c++-indent to handle member initializations
+;;   more flexibly. Two new variables are used to control behavior:
+;;   c++-member-init-indent and c++-continued-member-init-offset.
+;;   Note the assumption that member initializations and argument
+;;   declarations are not mixed in one function definition.
+;;
+;; June 1989 (Dave Detlefs, dld@cs.cmu.edu)
+;;   Fixed calculate-c++-indent to handle continued lines ending in
+;;   {'s.  (I wasn't following C-mode closely enough, or C-mode
+;;   changed.)  Made ' a quote character, at the behest of someone
+;;   whose mail I apparently deleted (if they send me mail I'll credit
+;;   them here in a future revision.)
+;;   Dan Weinreb (dlw@odi.com) pointed out that 'c++-mode successively
+;;   bound c++-indent-exp and c++-indent-defun to ESC-^q.  ESC-^q is
+;;   now bound to c++-indent-exp, while, c++-indent-defun is invoked
+;;   with ESC-^x.
+
+;; February 1989 (Dave Detlefs, dld@cs.cmu.edu)
+;;   Fixed some errors in c++-indent-defun, as pointed out by Sam
+;;   Haradhvala (odi!sam@talcott.harvard.edu).
+;; October 1988 (Dave Detlefs, dld@cs.cmu.edu)
+;;   It turns out I had only *thought* I had made
+;;   beginning(end)-of-defun work.  It should work better now -- you
+;;   can either attempt to match defun headers "strongly," using a
+;;   very complicated regexp, or "weakly," using a simple one.  This
+;;   is settable by a variable; the default is the cheaper weak
+;;   method.  (Stewart Clamen was intimately involved in this, too.)
+;;
+;;   I made "'" *not* be a string delimiter, because that was causing
+;;   comments containing contractions to ("// don't") to mess up paren
+;;   balancing.
+;;
+;;   I also incorporated another slight indentation fix from Glen
+;;   Ditchfield.
+;;
+;;   We hope this is will make into version 19 of gnu-emacs.
+;;
+;; September 1988: incorporated changes from Fred Calm at Schlumberger.
+;;   Also, made beginning(end)-of-defun, indent-defun work.
+;;
+;; August 1987: incorporated changes done by Glen Ditchfield of Waterloo.
+
+(defvar c++-mode-abbrev-table nil
+  "Abbrev table used in C++ mode.")
+(define-abbrev-table 'c++-mode-abbrev-table ())
+
+(defvar c++-mode-map ()
+  "Keymap used in C++ mode.")
+(if c++-mode-map
+    ()
+  (setq c++-mode-map (make-sparse-keymap))
+  (define-key c++-mode-map "\C-j" 'reindent-then-newline-and-indent)
+  (define-key c++-mode-map "{" 'electric-c++-brace)
+  (define-key c++-mode-map "}" 'electric-c++-brace)
+  (define-key c++-mode-map ";" 'electric-c++-semi)
+  (define-key c++-mode-map "\e\C-h" 'mark-c-function)
+  (define-key c++-mode-map "\e\C-q" 'indent-c++-exp)
+  (define-key c++-mode-map "\177" 'backward-delete-char-untabify)
+  (define-key c++-mode-map "\t" 'c++-indent-command)
+  (define-key c++-mode-map "\C-c\C-i" 'c++-insert-header)
+  (define-key c++-mode-map "\C-c\C-\\" 'c++-macroize-region)
+  (define-key c++-mode-map "\C-c\C-c" 'c++-comment-region)
+  (define-key c++-mode-map "\C-c\C-u" 'c++-uncomment-region)
+  (define-key c++-mode-map "\e\C-a" 'c++-beginning-of-defun)
+  (define-key c++-mode-map "\e\C-e" 'c++-end-of-defun)
+  (define-key c++-mode-map "\e\C-x" 'c++-indent-defun))
+
+(defvar c++-mode-syntax-table nil
+  "Syntax table used in C++ mode.")
+
+(if c++-mode-syntax-table
+    ()
+  (setq c++-mode-syntax-table (copy-syntax-table c-mode-syntax-table))
+  (modify-syntax-entry ?/ ". 12" c++-mode-syntax-table)
+  (modify-syntax-entry ?\n ">" c++-mode-syntax-table)
+  (modify-syntax-entry ?\' "." c++-mode-syntax-table))
+
+(defvar c++-continued-member-init-offset nil
+  "*Extra indent for continuation lines of member inits;
+NIL means to align with previous initializations rather than
+with the colon on the first line.")
+(defvar c++-member-init-indent 0
+  "*Indentation level of member initializations in function declarations.")
+(defvar c++-friend-offset -4
+  "*Offset of C++ friend class declarations relative to member declarations.")
+(defvar c++-electric-colon t
+  "*If t, colon is an electric terminator.")
+(defvar c++-empty-arglist-indent nil
+  "*Indicates how far to indent an line following an empty argument
+list.  Nil indicates to just after the paren.")
+
+
+;;;###autoload
+(defun c++-mode ()
+  "Major mode for editing C++ code.  Very much like editing C code.
+Expression and list commands understand all C++ brackets.
+Tab at left margin indents for C++ code
+Comments are delimited with /* ... */ {or with // ... <newline>}
+Paragraphs are separated by blank lines only.
+Delete converts tabs to spaces as it moves back.
+\\{c++-mode-map}
+Variables controlling indentation style:
+ c-tab-always-indent
+    Non-nil means TAB in C mode should always reindent the current line,
+    regardless of where in the line point is when the TAB command is used.
+    Default is t.
+ c-auto-newline
+    Non-nil means automatically newline before and after braces,
+    and after colons and semicolons, inserted in C code.
+ c-indent-level
+    Indentation of C statements within surrounding block.
+    The surrounding block's indentation is the indentation
+    of the line on which the open-brace appears.
+ c-continued-statement-offset
+    Extra indentation given to a substatement, such as the
+    then-clause of an if or body of a while.
+ c-continued-brace-offset
+    Extra indentation given to a brace that starts a substatement.
+    This is in addition to c-continued-statement-offset.
+ c-brace-offset
+    Extra indentation for line if it starts with an open brace.
+ c-brace-imaginary-offset
+    An open brace following other text is treated as if it were
+    this far to the right of the start of its line.
+ c-argdecl-indent
+    Indentation level of declarations of C function arguments.
+ c-label-offset
+    Extra indentation for line that is a label, or case or ``default:'', or
+    ``public:'' or ``private:'', or ``protected:''.
+ c++-electric-colon
+    If non-nil at invocation of c++-mode (t is the default) colon electricly
+    indents.
+ c++-empty-arglist-indent
+    If non-nil, a function declaration or invocation which ends a line with a
+    left paren is indented this many extra spaces, instead of flush with the
+    left paren.
+ c++-friend-offset
+    Offset of C++ friend class declarations relative to member declarations.
+ c++-member-init-indent
+    Indentation level of member initializations in function declarations,
+    if they are on a separate line beginning with a colon.
+ c++-continued-member-init-offset
+    Extra indentation for continuation lines of member initializations; NIL
+    means to align with previous initializations rather than with the colon.
+
+Settings for K&R, BSD, and Stroustrup indentation styles are
+  c-indent-level                5    8    4
+  c-continued-statement-offset  5    8    4
+  c-continued-brace-offset                0
+  c-brace-offset               -5   -8    0
+  c-brace-imaginary-offset                0
+  c-argdecl-indent              0    8    4
+  c-label-offset               -5   -8   -4
+  c++-empty-arglist-indent                4
+  c++-friend-offset                       0
+
+Turning on C++ mode calls the value of the variable `c++-mode-hook' with
+no args if that value is non-nil."
+  (interactive)
+  (kill-all-local-variables)
+  (use-local-map c++-mode-map)
+  (set-syntax-table c++-mode-syntax-table)
+  (setq major-mode 'c++-mode
+	mode-name "C++"
+	comment-column 32
+	local-abbrev-table c++-mode-abbrev-table)
+  (set (make-local-variable 'indent-line-function) 'c++-indent-line)
+  (set (make-local-variable 'comment-start) "// ")
+  (set (make-local-variable 'comment-end) "")
+  (set (make-local-variable 'comment-start-skip) "/\\*+ *\\|// *")
+  (set (make-local-variable 'comment-indent-hook) 'c++-comment-indent)
+  (set (make-local-variable 'paragraph-start) (concat "^$\\|" page-delimiter))
+  (set (make-local-variable 'paragraph-separate) paragraph-start)
+  (set (make-local-variable 'paragraph-ignore-fill-prefix) t)
+  (set (make-local-variable 'require-final-newline) t)
+  (set (make-local-variable 'parse-sexp-ignore-comments) nil)
+  (run-hooks 'c++-mode-hook)
+  (if c++-electric-colon
+      (define-key c++-mode-map ":" 'electric-c++-terminator)))
+
+;; This is used by indent-for-comment
+;; to decide how much to indent a comment in C++ code
+;; based on its context.
+(defun c++-comment-indent ()
+  (if (looking-at "^\\(/\\*\\|//\\)")
+      0					; Existing comment at bol stays there.
+    (save-excursion
+      (skip-chars-backward " \t")
+      (max
+       ;; leave at least one space on non-empty lines.
+       (if (zerop (current-column)) 0 (1+ (current-column)))
+       (let ((cur-pt (point)))
+	 (beginning-of-line 0)
+	 ;; If previous line had a comment, use it's indent
+	 (if (re-search-forward comment-start-skip cur-pt t)
+	     (progn
+	       (goto-char (match-beginning 0))
+	       (current-column))
+	   comment-column))))))		; otherwise indent at comment column.
+
+(defun electric-c++-brace (arg)
+  "Insert character and correct line's indentation."
+  (interactive "P")
+  (let (insertpos)
+    (if (and (not arg)
+	     (eolp)
+	     (or (save-excursion
+		   (skip-chars-backward " \t")
+		   (bolp))
+		 (if c-auto-newline (progn (c++-indent-line) (newline) t))))
+	(progn
+	  (insert last-command-char)
+	  (c++-indent-line)
+	  (if c-auto-newline
+	      (progn
+		(newline)
+		;; (newline) may have done auto-fill
+		(setq insertpos (- (point) 2))
+		(c++-indent-line)))
+	  (save-excursion
+	    (if insertpos (goto-char (1+ insertpos)))
+	    (delete-char -1))))
+    (if insertpos
+	(save-excursion
+	  (goto-char insertpos)
+	  (self-insert-command (prefix-numeric-value arg)))
+      (self-insert-command (prefix-numeric-value arg)))))
+
+(defun electric-c++-semi (arg)
+  "Insert character and correct line's indentation."
+  (interactive "P")
+  (if c-auto-newline
+      (electric-c++-terminator arg)
+    (self-insert-command (prefix-numeric-value arg))))
+
+(defun electric-c++-terminator (arg)
+  "Insert character and correct line's indentation."
+  (interactive "P")
+  (let (insertpos (end (point)))
+    (if (and (not arg) (eolp)
+	     (not (save-excursion
+		    (beginning-of-line)
+		    (skip-chars-forward " \t")
+		    (or (= (following-char) ?#)
+			;; Colon is special only after a label, or
+			;; case, or another colon.
+			;; So quickly rule out most other uses of colon
+			;; and do no indentation for them.
+			(and (eq last-command-char ?:)
+			     (not (looking-at "case[ \t]"))
+			     (save-excursion
+			       (forward-word 1)
+			       (skip-chars-forward " \t")
+			       (< (point) end))
+			     ;; Do re-indent double colons
+			     (save-excursion
+			       (end-of-line 1)
+			       (looking-at ":")))
+			(progn
+			  (beginning-of-defun)
+			  (let ((pps (parse-partial-sexp (point) end)))
+			    (or (nth 3 pps) (nth 4 pps) (nth 5 pps))))))))
+	(progn
+	  (insert last-command-char)
+	  (c++-indent-line)
+	  (and c-auto-newline
+	       (not (c-inside-parens-p))
+	       (progn
+		 ;; the new marker object, used to be just an integer
+		 (setq insertpos (make-marker))
+		 ;; changed setq to set-marker
+		 (set-marker insertpos (1- (point)))
+		 ;; do this before the newline, since in auto fill can break
+		 (newline)
+		 (c-indent-line)))
+	  (save-excursion
+	    (if insertpos (goto-char (1+ insertpos)))
+	    (delete-char -1))))
+    (if insertpos
+	(save-excursion
+	  (goto-char insertpos)
+	  (self-insert-command (prefix-numeric-value arg)))
+      (self-insert-command (prefix-numeric-value arg)))))
+
+(defun c++-indent-command (&optional whole-exp)
+  "Indent current line as C++ code, or in some cases insert a tab character.
+If `c-tab-always-indent' is non-nil (the default), always indent current
+line.  Otherwise, indent the current line only if point is at the left
+margin or in the line's indentation; otherwise insert a tab.
+
+A numeric argument, regardless of its value, means indent rigidly all means
+indent rigidly all the lines of the expression starting after point so that
+this line becomes properly indented.  The relative indentation among the
+lines of the expression are preserved."
+  (interactive "P")
+  (if whole-exp
+      ;; If arg, always indent this line as C
+      ;; and shift remaining lines of expression the same amount.
+      (let ((shift-amt (c++-indent-line))
+	    beg end)
+	(save-excursion
+	  (if c-tab-always-indent
+	      (beginning-of-line))
+	  (setq beg (point))
+	  (forward-sexp 1)
+	  (setq end (point))
+	  (goto-char beg)
+	  (forward-line 1)
+	  (setq beg (point)))
+	(if (> end beg)
+	    (indent-code-rigidly beg end shift-amt "#")))
+    (if (and (not c-tab-always-indent)
+	     (save-excursion
+	       (skip-chars-backward " \t")
+	       (not (bolp))))
+	(insert-tab)
+      (c++-indent-line))))
+
+(defun c++-indent-line ()
+  "Indent current line as C++ code.
+Return the amount the indentation changed by."
+  (let ((indent (calculate-c++-indent nil))
+	beg shift-amt
+	(case-fold-search nil)
+	(pos (- (point-max) (point))))
+    (beginning-of-line)
+    (setq beg (point))
+    (cond ((eq indent nil)
+	   (setq indent (current-indentation)))
+	  ((eq indent t)
+	   (setq indent (calculate-c-indent-within-comment)))
+	  ((looking-at "[ \t]*#")
+	   (setq indent 0))
+	  (t
+	   (skip-chars-forward " \t")
+	   (if (listp indent) (setq indent (car indent)))
+	   (cond ((looking-at "\\(default\\|public\\|private\\|protected\\):")
+		  (setq indent (+ indent c-label-offset)))
+		 ((or (looking-at "case\\b")
+		      (and (looking-at "[A-Za-z]")
+			   (save-excursion
+			     (forward-sexp 1)
+			     (looking-at ":[^:]"))))
+		  (setq indent (max 1 (+ indent c-label-offset))))
+		 ((and (looking-at "else\\b")
+		       (not (looking-at "else\\s_")))
+		  (setq indent (save-excursion
+				 (c-backward-to-start-of-if)
+				 (current-indentation))))
+		 ((looking-at "friend\[ \t]class[ \t]")
+		  (setq indent (+ indent c++-friend-offset)))
+		 ((= (following-char) ?})
+		  (setq indent (- indent c-indent-level)))
+		 ((= (following-char) ?{)
+		  (setq indent (+ indent c-brace-offset))))))
+    (skip-chars-forward " \t")
+    (setq shift-amt (- indent (current-column)))
+    (if (zerop shift-amt)
+	(if (> (- (point-max) pos) (point))
+	    (goto-char (- (point-max) pos)))
+      (delete-region beg (point))
+      (indent-to indent)
+      ;; If initial point was within line's indentation,
+      ;; position after the indentation.  Else stay at same point in text.
+      (if (> (- (point-max) pos) (point))
+	  (goto-char (- (point-max) pos))))
+    shift-amt))
+
+(defun calculate-c++-indent (&optional parse-start)
+  "Return appropriate indentation for current line as C++ code.
+In usual case returns an integer: the column to indent to.
+Returns nil if line starts inside a string, t if in a comment."
+  (save-excursion
+    (beginning-of-line)
+    (let ((indent-point (point))
+	  (case-fold-search nil)
+	  state
+	  containing-sexp)
+      (if parse-start
+	  (goto-char parse-start)
+	(beginning-of-defun))
+      (while (< (point) indent-point)
+	(setq parse-start (point))
+	(setq state (parse-partial-sexp (point) indent-point 0))
+	(setq containing-sexp (car (cdr state))))
+      (cond ((or (nth 3 state) (nth 4 state))
+	     ;; return nil or t if should not change this line
+	     (nth 4 state))
+	    ((null containing-sexp)
+	     ;; Line is at top level.  May be data or function definition, or
+	     ;; may be function argument declaration or member initialization.
+	     ;; Indent like the previous top level line unless
+	     ;; (1) the previous line ends in a closeparen without semicolon,
+	     ;; in which case this line is the first argument declaration or
+	     ;; member initialization, or
+	     ;; (2) the previous line begins with a colon,
+	     ;; in which case this is the second line of member inits.
+	     ;; It is assumed that arg decls and member inits are not mixed.
+	     (goto-char indent-point)
+	     (skip-chars-forward " \t")
+	     (if (= (following-char) ?{)
+		 0   ; Unless it starts a function body
+	       (c++-backward-to-noncomment (or parse-start (point-min)))
+	       (if (= (preceding-char) ?\))
+		   (progn		; first arg decl or member init
+		     (goto-char indent-point)
+		     (skip-chars-forward " \t")
+		     (if (= (following-char) ?:)
+			 c++-member-init-indent
+		       c-argdecl-indent))
+		 (if (= (preceding-char) ?\;)
+		     (backward-char 1))
+		 (if (= (preceding-char) ?})
+		     0
+		   (beginning-of-line)	; continued arg decls or member inits
+		   (skip-chars-forward " \t")
+		   (if (= (following-char) ?:)
+		       (if c++-continued-member-init-offset
+			   (+ (current-indentation)
+			      c++-continued-member-init-offset)
+			 (progn
+			   (forward-char 1)
+			   (skip-chars-forward " \t")
+			   (current-column)))
+		     (current-indentation)))
+		 )))
+	    ((/= (char-after containing-sexp) ?{)
+	     ;; line is expression, not statement:
+	     ;; indent to just after the surrounding open -- unless
+	     ;; empty arg list, in which case we do what
+	     ;; c++-empty-arglist-indent says to do.
+	     (if (and c++-empty-arglist-indent
+		      (or (null (nth 2 state))	;; indicates empty arg
+						;; list.
+			  ;; Use a heuristic: if the first
+			  ;; non-whitespace following left paren on
+			  ;; same line is not a comment,
+			  ;; is not an empty arglist.
+			  (save-excursion
+			    (goto-char (1+ containing-sexp))
+			    (not
+			     (looking-at "\\( \\|\t\\)*[^/\n]")))))
+		 (progn
+		   (goto-char containing-sexp)
+		   (beginning-of-line)
+		   (skip-chars-forward " \t")
+		   (goto-char (min (+ (point) c++-empty-arglist-indent)
+				   (1+ containing-sexp)))
+		   (current-column))
+	       ;; In C-mode, we would always indent to one after the
+	       ;; left paren.  Here, though, we may have an
+	       ;; empty-arglist, so we'll indent to the min of that
+	       ;; and the beginning of the first argument.
+	       (goto-char (1+ containing-sexp))
+	       (current-column)))
+	    (t
+	     ;; Statement.  Find previous non-comment character.
+	     (goto-char indent-point)
+	     (c++-backward-to-noncomment containing-sexp)
+	     (if (not (memq (preceding-char) '(nil ?\, ?\; ?} ?: ?\{)))
+		 ;; This line is continuation of preceding line's statement;
+		 ;; indent  c-continued-statement-offset  more than the
+		 ;; previous line of the statement.
+		 (progn
+		   (c-backward-to-start-of-continued-exp containing-sexp)
+		   (+ c-continued-statement-offset (current-column)))
+	       ;; This line starts a new statement.
+	       ;; Position following last unclosed open.
+	       (goto-char containing-sexp)
+	       ;; Is line first statement after an open-brace?
+	       (or
+		 ;; If no, find that first statement and indent like it.
+		 (save-excursion
+		   (forward-char 1)
+		   (while (progn (skip-chars-forward " \t\n")
+				 (looking-at
+				  (concat
+				   "#\\|/\\*\\|//"
+				   "\\|case[ \t]"
+				   "\\|[a-zA-Z0-9_$]*:[^:]"
+				   "\\|friend[ \t]class[ \t]")))
+		     ;; Skip over comments and labels following openbrace.
+		     (cond ((= (following-char) ?\#)
+			    (forward-line 1))
+			   ((looking-at "/\\*")
+			    (search-forward "*/" nil 'move))
+			   ((looking-at "//\\|friend[ \t]class[ \t]")
+			    (forward-line 1))
+			   (t
+			    (re-search-forward ":[^:]" nil 'move))))
+		      ;; The first following code counts
+		      ;; if it is before the line we want to indent.
+		      (and (< (point) indent-point)
+			   (current-column)))
+		 ;; If no previous statement,
+		 ;; indent it relative to line brace is on.
+		 ;; For open brace in column zero, don't let statement
+		 ;; start there too.  If c-indent-offset is zero,
+		 ;; use c-brace-offset + c-continued-statement-offset instead.
+		 ;; For open-braces not the first thing in a line,
+		 ;; add in c-brace-imaginary-offset.
+		 (+ (if (and (bolp) (zerop c-indent-level))
+			(+ c-brace-offset c-continued-statement-offset)
+		      c-indent-level)
+		    ;; Move back over whitespace before the openbrace.
+		    ;; If openbrace is not first nonwhite thing on the line,
+		    ;; add the c-brace-imaginary-offset.
+		    (progn (skip-chars-backward " \t")
+			   (if (bolp) 0 c-brace-imaginary-offset))
+		    ;; If the openbrace is preceded by a parenthesized exp,
+		    ;; move to the beginning of that;
+		    ;; possibly a different line
+		    (progn
+		      (if (eq (preceding-char) ?\))
+			  (forward-sexp -1))
+		      ;; Get initial indentation of the line we are on.
+		      (current-indentation))))))))))
+
+(defun c++-backward-to-noncomment (lim)
+  (let (opoint stop)
+    (while (not stop)
+      (skip-chars-backward " \t\n\r\f" lim)
+      (setq opoint (point))
+      (cond ((and (>= (point) (+ 2 lim))
+		  (save-excursion
+		    (forward-char -2)
+		    (looking-at "\\*/")))
+	     (search-backward "/*" lim 'move))
+	    ((and
+	      (search-backward "//" (max (point-bol) lim) 'move)
+	      (not (within-string-p (point) opoint))))
+	  (t (beginning-of-line)
+	     (skip-chars-forward " \t")
+	     (if (looking-at "#")
+		 (setq stop (<= (point) lim))
+	       (setq stop t)
+	       (goto-char opoint)))))))
+
+(defun indent-c++-exp ()
+  "Indent each line of the C++ grouping following point."
+  (interactive)
+  (let ((indent-stack (list nil))
+	(contain-stack (list (point)))
+	(case-fold-search nil)
+	restart outer-loop-done inner-loop-done state ostate
+	this-indent last-sexp
+	at-else at-brace
+	(opoint (point))
+	(next-depth 0))
+    (save-excursion
+      (forward-sexp 1))
+    (save-excursion
+      (setq outer-loop-done nil)
+      (while (and (not (eobp)) (not outer-loop-done))
+	(setq last-depth next-depth)
+	;; Compute how depth changes over this line
+	;; plus enough other lines to get to one that
+	;; does not end inside a comment or string.
+	;; Meanwhile, do appropriate indentation on comment lines.
+	(setq innerloop-done nil)
+	(while (and (not innerloop-done)
+		    (not (and (eobp) (setq outer-loop-done t))))
+	  (setq ostate state)
+	  (setq state (parse-partial-sexp (point) (progn (end-of-line) (point))
+					  nil nil state))
+	  (setq next-depth (car state))
+	  (if (and (car (cdr (cdr state)))
+		   (>= (car (cdr (cdr state))) 0))
+	      (setq last-sexp (car (cdr (cdr state)))))
+	  (if (or (nth 4 ostate))
+	      (c++-indent-line))
+	  (if (or (nth 3 state))
+	      (forward-line 1)
+	    (setq innerloop-done t)))
+	(if (<= next-depth 0)
+	    (setq outer-loop-done t))
+	(if outer-loop-done
+	    nil
+	  ;; If this line had ..))) (((.. in it, pop out of the levels
+	  ;; that ended anywhere in this line, even if the final depth
+	  ;; doesn't indicate that they ended.
+	  (while (> last-depth (nth 6 state))
+	    (setq indent-stack (cdr indent-stack)
+		  contain-stack (cdr contain-stack)
+		  last-depth (1- last-depth)))
+	  (if (/= last-depth next-depth)
+	      (setq last-sexp nil))
+	  ;; Add levels for any parens that were started in this line.
+	  (while (< last-depth next-depth)
+	    (setq indent-stack (cons nil indent-stack)
+		  contain-stack (cons nil contain-stack)
+		  last-depth (1+ last-depth)))
+	  (if (null (car contain-stack))
+	      (setcar contain-stack (or (car (cdr state))
+					(save-excursion (forward-sexp -1)
+							(point)))))
+	  (forward-line 1)
+	  (skip-chars-forward " \t")
+	  (if (eolp)
+	      nil
+	    (if (and (car indent-stack)
+		     (>= (car indent-stack) 0))
+		;; Line is on an existing nesting level.
+		;; Lines inside parens are handled specially.
+		(if (/= (char-after (car contain-stack)) ?{)
+		    (setq this-indent (car indent-stack))
+		  ;; Line is at statement level.
+		  ;; Is it a new statement?  Is it an else?
+		  ;; Find last non-comment character before this line
+		  (save-excursion
+		    (setq at-else (looking-at "else\\W"))
+		    (setq at-brace (= (following-char) ?{))
+		    (c++-backward-to-noncomment opoint)
+		    (if (not (memq (preceding-char) '(nil ?\, ?\; ?} ?: ?{)))
+			;; Preceding line did not end in comma or semi;
+			;; indent this line  c-continued-statement-offset
+			;; more than previous.
+			(progn
+			  (c-backward-to-start-of-continued-exp
+			   (car contain-stack))
+			  (setq this-indent
+				(+ c-continued-statement-offset
+				   (current-column)
+				   (if at-brace c-continued-brace-offset 0))))
+		      ;; Preceding line ended in comma or semi;
+		      ;; use the standard indent for this level.
+		      (if at-else
+			  (progn (c-backward-to-start-of-if opoint)
+				 (setq this-indent (current-indentation)))
+			(setq this-indent (car indent-stack))))))
+	      ;; Just started a new nesting level.
+	      ;; Compute the standard indent for this level.
+	      (let ((val (calculate-c++-indent
+			  (if (car indent-stack)
+			      (- (car indent-stack))))))
+		(setcar indent-stack
+			(setq this-indent val))))
+	    ;; Adjust line indentation according to its contents
+	    (if (looking-at "\\(public\\|private\\|protected\\):")
+		(setq this-indent (- this-indent c-indent-level)))
+	    (if (or (looking-at "case[ \t]")
+		    (and (looking-at "[A-Za-z]")
+			 (save-excursion
+			   (forward-sexp 1)
+			   (looking-at ":[^:]"))))
+		(setq this-indent (max 1 (+ this-indent c-label-offset))))
+	    (if (looking-at "friend[ \t]class[ \t]")
+		(setq this-indent (+ this-indent c++-friend-offset)))
+	    (if (= (following-char) ?})
+		(setq this-indent (- this-indent c-indent-level)))
+	    (if (= (following-char) ?{)
+		(setq this-indent (+ this-indent c-brace-offset)))
+	    ;; Put chosen indentation into effect.
+	    (or (= (current-column) this-indent)
+		(= (following-char) ?\#)
+		(progn
+		  (delete-region (point) (progn (beginning-of-line) (point)))
+		  (indent-to this-indent)))
+	    ;; Indent any comment following the text.
+	    (or (looking-at comment-start-skip)
+		(if (re-search-forward comment-start-skip
+				       (save-excursion (end-of-line)
+						       (point)) t)
+		    (progn
+		      (indent-for-comment)
+		      (beginning-of-line))))))))))
+
+(defun fill-C-comment ()
+  (interactive)
+  (save-excursion
+    (let ((save fill-prefix))
+      (beginning-of-line 1)
+      (save-excursion
+	(re-search-forward comment-start-skip
+			   (save-excursion (end-of-line) (point))
+			   t)
+	(goto-char (match-end 0))
+	(set-fill-prefix))
+      (while (looking-at fill-prefix)
+	(previous-line 1))
+      (next-line 1)
+      (insert-string "\n")
+      (fill-paragraph nil)
+      (delete-char -1)
+      (setq fill-prefix save))))
+
+(defun point-bol ()
+  "Returns the value of the point at the beginning of the current line."
+  (save-excursion
+    (beginning-of-line)
+    (point)))
+
+(defun c++-insert-header ()
+  "Insert header denoting C++ code at top of buffer."
+  (interactive)
+  (save-excursion
+    (goto-char (point-min))
+    (insert "// "
+	    "This may look like C code, but it is really "
+	    "-*- C++ -*-"
+	    "\n\n")))
+
+(defun within-string-p (point1 point2)
+  "Returns true if number of double quotes between two points is odd."
+  (let ((s (buffer-substring point1 point2)))
+    (not (zerop (mod (count-char-in-string ?\" s) 2)))))
+
+(defun count-char-in-string (c s)
+  (let ((count 0)
+	(pos 0))
+    (while (< pos (length s))
+      (setq count (+ count (if (\= (aref s pos) c) 1 0)))
+      (setq pos (1+ pos)))
+    count))
+
+;;; This page covers "macroization;" making C++ parameterized types
+;;; via macros.
+
+(defvar c++-default-macroize-column 78
+  "Place to insert backslashes.")
+
+(defun c++-macroize-region (from to arg)
+  "Insert backslashes at end of every line in region.  Useful for defining cpp
+macros.  If called with negative argument, will remove trailing backslashes,
+so that indentation will work right."
+  (interactive "r\np")
+  (save-excursion
+    (goto-char from)
+    (beginning-of-line 1)
+    (let ((line (count-lines (point-min) (point)))
+	  (to-line (save-excursion (goto-char to)
+				   (count-lines (point-min) (point)))))
+      (while (< line to-line)
+	(backslashify-current-line (> arg 0))
+	(next-line 1) (setq line (1+ line))))))
+
+(defun backslashify-current-line (doit)
+  (end-of-line 1)
+  (cond
+   (doit
+    ;; Note that "\\\\" is needed to get one backslash.
+    (if (not (save-excursion (forward-char -1) (looking-at "\\\\")))
+	(progn
+	  (if (>= (current-column) c++-default-macroize-column)
+	      (insert " \\")
+	    (while (<= (current-column) c++-default-macroize-column)
+	      (insert "\t") (end-of-line))
+	    (delete-char -1)
+	    (while (< (current-column) c++-default-macroize-column)
+	      (insert " ") (end-of-line))
+	    (insert "\\")))))
+   (t
+    (forward-char -1)
+    (if (looking-at "\\\\")
+	(progn (skip-chars-backward " \t")
+	       (kill-line))))))
+
+
+;;; This page covers commenting out multiple lines.
+
+(defun c++-comment-region ()
+  "Comment out all lines in a region between mark and current point.
+Inserts \"// \" (`comment-start') in front of each line."
+  (interactive)
+  (let* ((m      (if (eq (mark) nil) (error "Mark is not set!") (mark)))
+	 (start  (if (< (point) m) (point) m))
+	 (end    (if (> (point) m) (point) m))
+	 (mymark (copy-marker end)))
+    (save-excursion
+	(goto-char start)
+	(while (< (point) (marker-position mymark))
+	    (beginning-of-line)
+	    (insert comment-start)
+	    (beginning-of-line)
+	    (next-line 1)))))
+
+(defun c++-uncomment-region ()
+  "Uncomment all lines in region between mark and current point.
+Deletes the leading \"// \" (`comment-start') from each line, if any."
+  (interactive)
+  (let* ((m      (if (eq (mark) nil) (error "Mark is not set!") (mark)))
+	 (start  (if (< (point) m) (point) m))
+	 (end    (if (> (point) m) (point) m))
+	 (mymark (copy-marker end))
+	 (len    (length comment-start))
+	 (char   (string-to-char comment-start)))
+    (save-excursion
+	(goto-char start)
+	(while (< (point) (marker-position mymark))
+	    (beginning-of-line)
+	    (if (looking-at (concat " *" comment-start))
+		(progn
+		  (zap-to-char 1 char)
+		  (delete-char len)))
+	    (beginning-of-line)
+	    (next-line 1)))))
+
+;;; Below are two regular expressions that attempt to match defuns
+;;; "strongly" and "weakly."  The strong one almost reconstructs the
+;;; grammar of C++; the weak one just figures anything id or curly on
+;;; the left begins a defun.  The constant "c++-match-header-strongly"
+;;; determines which to use; the default is the weak one.
+
+(defvar c++-match-header-strongly nil
+  "*If nil, use `c++-defun-header-weak' to identify beginning of definitions.
+If non-nil, use `c++-defun-header-strong'.")
+
+(defvar c++-defun-header-strong-struct-equivs "\\(class\\|struct\\|enum\\)"
+  "Regexp to match names of structure declaration blocks in C++.")
+
+(defconst c++-defun-header-strong
+  (let*
+      (; valid identifiers
+       ;; There's a real wierdness here -- if I switch the below
+       (id "\\(\\w\\|_\\)+")
+       ;; to be
+       ;; (id "\\(_\\|\\w\\)+")
+       ;; things no longer work right.  Try it and see!
+
+       ; overloadable operators
+       (op-sym1
+	 "[---+*/%^&|~!=<>]\\|[---+*/%^&|<>=!]=\\|<<=?\\|>>=?")
+       (op-sym2
+	 "&&\\|||\\|\\+\\+\\|--\\|()\\|\\[\\]")	 
+       (op-sym (concat "\\(" op-sym1 "\\|" op-sym2 "\\)"))
+       ; whitespace
+       (middle "[^\\*]*\\(\\*+[^/\\*][^\\*]*\\)*")
+       (c-comment (concat "/\\*" middle "\\*+/"))
+       (wh (concat "\\(\\s \\|\n\\|//.*$\\|" c-comment "\\)"))
+       (wh-opt (concat wh "*"))
+       (wh-nec (concat wh "+"))
+       (oper (concat "\\(" "operator" "\\("
+		     wh-opt op-sym "\\|" wh-nec id "\\)" "\\)"))
+       (dcl-list "([^():]*)")
+       (func-name (concat "\\(" oper "\\|" id "::" id "\\|" id "\\)"))
+       (inits
+	 (concat "\\(:"
+		 "\\(" wh-opt id "(.*\\()" wh-opt "," "\\)\\)*"
+		 wh-opt id "(.*)" wh-opt "{"
+		 "\\|" wh-opt "{\\)"))
+       (type-name (concat
+		    "\\(" c++-defun-header-strong-struct-equivs wh-nec "\\)?"
+		    id))
+       (type (concat "\\(const" wh-nec "\\)?"
+		     "\\(" type-name "\\|" type-name wh-opt "\\*+" "\\|"
+		     type-name wh-opt "&" "\\)"))
+       (modifier "\\(inline\\|virtual\\|overload\\|auto\\|static\\)")
+       (modifiers (concat "\\(" modifier wh-nec "\\)*"))
+       (func-header
+	 ;;     type               arg-dcl
+	 (concat modifiers type wh-nec func-name wh-opt dcl-list wh-opt inits))
+       (inherit (concat "\\(:" wh-opt "\\(public\\|private\\)?"
+			wh-nec id "\\)"))
+       (cs-header (concat
+		    c++-defun-header-strong-struct-equivs
+		    wh-nec id wh-opt inherit "?" wh-opt "{")))
+    (concat "^\\(" func-header "\\|" cs-header "\\)"))
+  "Strongly-defined regexp to match beginning of structure or function def.")
+
+
+;; This part has to do with recognizing defuns.
+
+;; The weak convention we will use is that a defun begins any time
+;; there is a left curly brace, or some identifier on the left margin,
+;; followed by a left curly somewhere on the line.  (This will also
+;; incorrectly match some continued strings, but this is after all
+;; just a weak heuristic.)  Suggestions for improvement (short of the
+;; strong scheme shown above) are welcomed.
+
+(defconst c++-defun-header-weak "^{\\|^[_a-zA-Z].*{"
+  "Weakly-defined regexp to match beginning of structure or function def.")
+
+(defun c++-beginning-of-defun (arg)
+  (interactive "p")
+  (let ((c++-defun-header (if c++-match-header-strongly
+			      c++-defun-header-strong
+			    c++-defun-header-weak)))
+    (cond ((or (= arg 0) (and (> arg 0) (bobp))) nil)
+	  ((and (not (looking-at c++-defun-header))
+		(let ((curr-pos (point))
+		      (open-pos (if (search-forward "{" nil 'move)
+				    (point)))
+		      (beg-pos
+			(if (re-search-backward c++-defun-header nil 'move)
+			    (match-beginning 0))))
+		  (if (and open-pos beg-pos
+			   (< beg-pos curr-pos)
+			   (> open-pos curr-pos))
+		      (progn
+			(goto-char beg-pos)
+			(if (= arg 1) t nil));; Are we done?
+		    (goto-char curr-pos)
+		    nil))))
+	  (t
+	    (if (and (looking-at c++-defun-header) (not (bobp)))
+		(forward-char (if (< arg 0) 1 -1)))
+	    (and (re-search-backward c++-defun-header nil 'move (or arg 1))
+		 (goto-char (match-beginning 0)))))))
+
+
+(defun c++-end-of-defun (arg)
+  (interactive "p")
+  (let ((c++-defun-header (if c++-match-header-strongly
+			      c++-defun-header-strong
+			    c++-defun-header-weak)))
+    (if (and (eobp) (> arg 0))
+	nil
+      (if (and (> arg 0) (looking-at c++-defun-header)) (forward-char 1))
+      (let ((pos (point)))
+	(c++-beginning-of-defun 
+	  (if (< arg 0)
+	      (- (- arg (if (eobp) 0 1)))
+	    arg))
+	(if (and (< arg 0) (bobp))
+	    t
+	  (if (re-search-forward c++-defun-header nil 'move)
+	      (progn (forward-char -1)
+		     (forward-sexp)
+		     (beginning-of-line 2)))
+	  (if (and (= pos (point)) 
+		   (re-search-forward c++-defun-header nil 'move))
+	      (c++-end-of-defun 1))))
+      t)))
+
+(defun c++-indent-defun ()
+  "Indents the current function definition, struct or class declaration."
+  (interactive)
+  (let ((restore (point)))
+    (c++-end-of-defun 1)
+    (beginning-of-line 1)
+    (let ((end (point)))
+      (c++-beginning-of-defun 1)
+      (while (<= (point) end)
+	(c++-indent-line)
+	(next-line 1)
+	(beginning-of-line 1)))
+    (goto-char restore)))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/textmodes/bibtex.el	Thu May 09 21:50:45 1991 +0000
@@ -0,0 +1,1020 @@
+;;; BibTeX mode for GNU Emacs
+;; Copyright (C) 1985, 1986, 1987, 1990 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 1, 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.
+
+;;; Mike Newton (newton@gumby.cs.caltech.edu) 91.1.20
+;;;  * bibtex.el/bibtex-mode : updated comments to indicate new use of 
+;;;    address, add minor explanations and fix small omissions.
+;;;  * bibtex.el/bibtex-entry : fixed spelling of variable
+
+;;; Mike Newton (newton@gumby.cs.caltech.edu) 90.11.17
+;;;  * Handle items like
+;;;          title = poft # "Fifth Tri-quaterly" # random-conf,
+;;;    and   title = {This title is inside curlies}
+;;;  * added user settable, always present, optional fields
+;;;  * fixed 'bibtex-find-it's doc string's location
+;;;  * bibtex-field-text made more general (it wouldnt handle the # construct)
+;;;		and it now handles a small subset of the {} cases
+;;;  * put DEA thesis back in (why get rid of good code?) 
+;;;  * merged into release 19 version code
+;;;  * if cross-ref'ing is on, put 'pages' near top of OPTs, as the other
+;;;             entries are unlikely to be used.
+;;;  * skip-whitespace replaced by skip-chars-forward (also done 
+;;;             by Marc Shairo)
+
+;;; Bengt Martensson, March 6
+;;;   Adapted to Bibtex 0.99 by updating the optional fields according
+;;;   to the document BibTeXing, Oren Patashnik, dated January 31, 1988.
+;;;   Updated documentation strings accordingly.  Added (provide 'bibtex).
+;;;   If bibtex-include-OPT-crossref is non-nil, every entry will have
+;;;   an OPTcrossref field, analogously for bibtex-include-OPTkey and
+;;;   bibtex-include-OPTannote.  Added bibtex-preamble, bound to ^C^EP,
+;;;   and also found in X- and sun-menus.  Cleaned up the sun-menu
+;;;   stuff, and made it more uniform with the X-menu stuff.  Marc: I
+;;;   strongly suspect that I broke your parsing...  (Or, more
+;;;   correctly, BibTeX 0.99 broke it.)
+;;;   Added bibtex-clean-entry-zap-empty-opts, defvar'd to t.  If it
+;;;   is nil, bibtex-clean-entry will leave empty optional fields alone.
+  
+;;; Marc Shapiro 1-feb-89: integrated changes by Bengt Martensson 88-05-06:
+;;;   Added Sun menu support.  Locally bound to right mouse button in 
+;;;   bibtex-mode.  Emacs 18.49 allows local mouse bindings!!
+;;;   Commented out DEAthesis.
+
+;;; Marc Shapiro 6-oct-88
+;;;  * use indent-to-column instead of inserting tabs (changes to 
+;;;    bibtex-entry, bibtex-make-entry, bibtex-make-OPT-entry, renamed to
+;;;    bibtex-make-optional-entry)
+;;;  * C-c C-k deletes the current OPT entry entirely
+;;;  * C-c C-d replaces text of field with ""
+;;;  * renamed bibtex-find-it to bibtex-find-text.  With arg, now goes to
+;;;    start of text.  Fixed bugs in it.
+
+;;; Marc Shapiro 23-sep-88
+;;;  * bibtex-clean-entry moves past end of entry.
+;;;  * bibtex-clean-entry signals mandatory fields left empty.
+
+;;; Marc Shapiro 18-jul-88
+;;;  * Moved all the entry type keystrokes to "C-c C-e something" (instead of
+;;;    "C-c something" previously) to make room for more.  C-c C-e is
+;;;    supposed to stand for "entry" [idea taken from mail-mode].  Moved
+;;;    bibtex-pop-previous to C-c C-p and bibtex-pop-next to C-c C-n.
+;;;  * removed binding for "\e[25~"
+;;;  * replaced bibtex-clean-optionals by bibtex-clean-entry, bound to
+;;;    C-c C-c
+
+;;; Marc Shapiro 13-jul-88 [based on ideas by Sacha Krakowiak of IMAG]
+;;;  * bibtex-pop-previous replaces current field with value of
+;;;    similar field in previous entry.  May be called n times in a row
+;;;    (or with arg n) to pop similar field of n'th previous entry.
+;;;    There is also a bibtex-pop-next to get similar field of next
+;;;    entry.
+;;;  * C-c C-k now kills all empty optional fields of current entry, and
+;;;    removes "OPT" for those optional fields which have text. 
+
+;;; Marc Shapiro 14-dec-87
+;;;   Cosmetic fixes.  Fixed small bug in bibtex-move-outside-of-entry.
+;;; Skip Montanaro <steinmetz!sprite!montanaro> 7-dec-87, Shapiro 10-dec-87
+;;;   before inserting an entry, make sure we are outside of a bib entry
+;;; Marc Shapiro 3-nov-87
+;;;   addition for France: DEAthesis
+;;; Marc Shapiro 19-oct-1987
+;;;   add X window menu option; bug fixes. TAB, LFD, C-c " and C-c C-o now
+;;;   behave consistently; deletion never occurs blindly.
+;;; Marc Shapiro <shapiro@inria.inria.fr> 15-oct-1986
+;;;    align long lines nicely; C-c C-o checks for the "OPT" string;
+;;;    TAB goes to the end of the string; use lower case; use
+;;;    run-hooks
+
+;;; Bengt Martensson <ubrinf!mond!bengt> 87-06-28
+;;; (Bengt Martensson <bengt@mathematik.uni-Bremen.de> 87-06-28)
+;;;   Original version
+
+;;; NOTE by Marc Shapiro, 14-dec-87:
+;;; (bibtex-x-environment) binds an X menu for bibtex mode to x-button-c-right.
+;;; Trouble is, in Emacs 18.44 you can't have a mode-specific mouse binding,
+;;; so it will remain active in all windows.  Yuck!
+
+(provide 'bibtex)
+
+(defvar bibtex-mode-syntax-table nil "")
+(defvar bibtex-mode-abbrev-table nil "")
+(define-abbrev-table 'bibtex-mode-abbrev-table ())
+(defvar bibtex-mode-map (make-sparse-keymap) "")
+(defvar bibtex-pop-previous-search-point nil
+  "Next point where `bibtex-pop-previous' should start looking for a similar
+entry.")
+(defvar bibtex-pop-next-search-point nil
+  "Next point where `bibtex-pop-next' should start looking for a similar
+entry.")
+
+(defvar bibtex-clean-entry-zap-empty-opts t
+  "*If non-nil, `bibtex-clean-entry' will delete all empty optional fields.")
+(defvar bibtex-include-OPTcrossref t
+  "*If non-nil, all entries will have an `OPTcrossref' field.")
+(defvar bibtex-include-OPTkey t
+  "*If non-nil, all entries will have an `OPTkey' field.")
+(defvar bibtex-include-OPTannote t
+  "*If non-nil, all entries will have an `OPTannote' field.")
+
+;; note: the user should be allowed to have their own list of always
+;;       available optional fields.  exs: "keywords" "categories"
+(defvar bibtex-mode-user-optional-fields nil		;no default value
+  "*List of optional fields that user always wants present in a bibtex entry.
+One possibility is for ``keywords''")
+
+
+;;; A bibtex file is a sequence of entries, either string definitions
+;;; or reference entries.  A reference entry has a type part, a
+;;; key part, and a comma-separated sequence of fields.  A string
+;;; entry has a single field.  A field has a left and right part,
+;;; separated by a '='.  The left part is the name, the right part is
+;;; the text.  Here come the definitions allowing to create and/or parse
+;;; entries and fields:
+
+;;; fields
+(defun bibtex-cfield (name text)
+  "Create a regexp for a bibtex field of name NAME and text TEXT."
+  (concat ",[ \t\n]*\\("
+	  name
+	  "\\)[ \t\n]*=[ \t\n]*\\("
+	  text
+	  "\\)"))
+(defconst bibtex-name-in-cfield 1
+  "The regexp subexpression number of the name part in `bibtex-cfield'.")
+(defconst bibtex-text-in-cfield 2
+  "The regexp subexpression number of the text part in `bibtex-cfield'.")
+
+(defconst bibtex-field-name "[A-Za-z][---A-Za-z0-9:_+]*"
+  "Regexp defining the name part of a bibtex field.")
+
+;; bibtex-field-text must be able to handle
+;;   title = "Proc. Fifteenth Annual" # STOC,
+;;   month = "10~" # jan,
+;;   year = "{\noopsort{1973c}}1981",
+;;   month = apr # "-" # may,
+;;   key = {Volume-2},
+;;   note = "Volume~2 is listed under Knuth \cite{book-full}"
+;; i have added a few of these, but not all! -- MON
+
+(defconst bibtex-field-const
+  "[0-9A-Za-z][---A-Za-z0-9:_+]*"
+  "Format of a bibtex field constant.")
+(defconst bibtex-field-string
+  (concat
+    "\"[^\"]*[^\\\\]\"\\|\"\"")
+  "Match either a string or an empty string.")
+(defconst bibtex-field-string-or-const
+  (concat bibtex-field-const "\\|" bibtex-field-string)
+  "Match either `bibtex-field-string' or `bibtex-field-const'.")
+
+;(defconst bibtex-field-text
+;  "\"[^\"]*[^\\\\]\"\\|\"\"\\|[0-9A-Za-z][---A-Za-z0-9:_+]*"
+;  "Regexp defining the text part of a bibtex field: either a string, or an empty string, or a constant.")
+
+(defconst bibtex-field-text
+  (concat
+    "\\(" bibtex-field-string-or-const "\\)"
+        "\\([ \t\n]+#[ \t\n]+\\(" bibtex-field-string-or-const "\\)\\)*\\|"
+    "{[^{}]*[^\\\\]}")
+  "Regexp defining the text part of a bibtex field: either a string, or
+an empty string, or a constant followed by one or more # / constant pairs.
+Also matches simple {...} patterns.")
+
+(defconst bibtex-field
+  (bibtex-cfield bibtex-field-name bibtex-field-text)
+  "Regexp defining the format of a bibtex field")
+
+(defconst bibtex-name-in-field bibtex-name-in-cfield
+  "The regexp subexpression number of the name part in `bibtex-field'.")
+(defconst bibtex-text-in-field bibtex-text-in-cfield
+  "The regexp subexpression number of the text part in `bibtex-field'.")
+
+;;; references
+(defconst bibtex-reference-type
+  "@[A-Za-z]+"
+  "Regexp defining the type part of a bibtex reference entry.")
+(defconst bibtex-reference-head
+  (concat "^[ \t]*\\("
+	  bibtex-reference-type
+	  "\\)[ \t]*[({]\\("
+	  bibtex-field-name
+	  "\\)")
+  "Regexp defining format of the header line of a bibtex reference entry.")
+(defconst bibtex-type-in-head 1
+  "The regexp subexpression number of the type part in `bibtex-reference-head'.")
+(defconst bibtex-key-in-head 2
+  "The regexp subexpression number of the key part in `bibtex-reference-head'.")
+
+(defconst bibtex-reference
+  (concat bibtex-reference-head
+	  "\\([ \t\n]*" bibtex-field "\\)*"
+	  "[ \t\n]*[})]")
+  "Regexp defining the format of a bibtex reference entry.")
+(defconst bibtex-type-in-reference bibtex-type-in-head
+  "The regexp subexpression number of the type part in `bibtex-reference'.")
+(defconst bibtex-key-in-reference bibtex-key-in-head
+  "The regexp subexpression number of the key part in `bibtex-reference'.")
+
+;;; strings
+(defconst bibtex-string
+  (concat "^[ \t]*@[sS][tT][rR][iI][nN][gG][ \t\n]*[({][ \t\n]*\\("
+	  bibtex-field-name
+	  "\\)[ \t\n]*=[ \t\n]*\\("
+	  bibtex-field-text
+	  "\\)[ \t\n]*[})]")
+  "Regexp defining the format of a bibtex string entry.")
+(defconst bibtex-name-in-string 1
+  "The regexp subexpression of the name part in `bibtex-string'.")
+(defconst bibtex-text-in-string 2
+  "The regexp subexpression of the text part in `bibtex-string'.")
+
+(defconst bibtex-name-alignement 2
+  "Alignment for the name part in BibTeX fields.
+Chosen on aesthetic grounds only.")
+
+(defconst bibtex-text-alignment (length "  organization = ")
+  "Alignment for the text part in BibTeX fields.
+Equal to the space needed for the longest name part.")
+
+;;; bibtex mode:
+
+;;;###autoload
+(defun bibtex-mode () 
+  "Major mode for editing bibtex files.
+
+\\{bibtex-mode-map}
+
+A command such as \\[bibtex-Book] will outline the fields for a BibTeX book entry.
+
+The optional fields start with the string OPT, and thus ignored by BibTeX.
+The OPT string may be removed from a field with \\[bibtex-remove-OPT].
+\\[bibtex-kill-optional-field] kills the current optional field entirely.
+\\[bibtex-remove-double-quotes] removes the double-quotes around the text of
+the current field.  \\[bibtex-empty-field] replaces the text of the current
+field with the default \"\".
+
+The command \\[bibtex-clean-entry] cleans the current entry, i.e. (i) removes
+double-quotes from entirely numerical fields, (ii) removes OPT from all
+non-empty optional fields, (iii) removes all empty optional fields, and (iv)
+checks that no non-optional fields are empty.
+
+Use \\[bibtex-find-text] to position the dot at the end of the current field.
+Use \\[bibtex-next-field] to move to end of the next field.
+
+\\[bibtex-x-environment] binds a mode-specific X menu to control+right
+mouse button.
+\\[bibtex-sun-environment] binds a mode-specific Sun menu to right
+mouse button.
+
+Fields:
+    address
+           Publisher's address, or for conference, location held
+    annote
+           Long annotation used for annotated bibliographies (begins sentence)
+    author
+           Name(s) of author(s), in BibTeX name format
+    booktitle
+           Book title when the thing being referenced isn't the whole book.
+           For book entries, the title field should be used instead.
+    chapter
+           Chapter number (or section or whatever).
+    crossref
+	   The database key of the entry being cross referenced.
+    edition
+           Edition of a book (e.g., \"second\")
+    editor
+           Name(s) of editor(s), in BibTeX name format.
+           If there is also an author field, then the editor field should be
+           for the book or collection that the work appears in
+    howpublished
+            How something strange has been published (begins sentence)
+    institution
+           Sponsoring institution
+    journal
+           Journal name (macros are provided for many)
+    key
+           Alphabetizing, labeling and cross-refing key (needed when no 
+	   author or editor)
+    month
+           Month (macros are provided)
+    note
+           To help the reader find a reference (begins sentence)
+    number
+           Number of a journal or technical report
+    organization
+           Organization (sponsoring a conference)
+    pages
+           Page number or numbers (use `--' to separate a range)
+    publisher
+           Publisher name
+    school
+           School name (for theses)
+    series
+           The name of a series or set of books.
+           An individual book will will also have it's own title
+    title
+           The title of the thing being referenced
+    type
+           Type of a technical report (e.g., \"Research Note\") to be used
+           instead of the default \"Technical Report\"
+    volume
+           Volume of a journal or multivolume work
+    year
+           Year---should contain only numerals
+---------------------------------------------------------
+Entry to this mode calls the value of bibtex-mode-hook if that value is
+non-nil."
+  (interactive)
+  (kill-all-local-variables)
+  (if bibtex-mode-syntax-table
+      (set-syntax-table bibtex-mode-syntax-table)
+     (setq bibtex-mode-syntax-table (make-syntax-table))
+     (set-syntax-table bibtex-mode-syntax-table)
+     (modify-syntax-entry ?\" ".")
+     (modify-syntax-entry ?$ "$$  ")
+     (modify-syntax-entry ?% "<   ")
+     (modify-syntax-entry ?'  "w   ")
+     (modify-syntax-entry ?@  "w   ")
+     (modify-syntax-entry ?\\ "\\")
+     (modify-syntax-entry ?\f ">   ")
+     (modify-syntax-entry ?\n ">   ")
+     (modify-syntax-entry ?~ " "))
+  (use-local-map bibtex-mode-map)
+  (setq major-mode 'bibtex-mode)
+
+
+  (setq mode-name "BibTeX")
+  (set-syntax-table bibtex-mode-syntax-table)
+  (setq local-abbrev-table bibtex-mode-abbrev-table)
+  (make-local-variable 'paragraph-start)
+  (setq paragraph-start "^[ \f\n\t]*$")
+
+  (define-key bibtex-mode-map "\t" 'bibtex-find-text)
+  (define-key bibtex-mode-map "\n" 'bibtex-next-field)
+  (define-key bibtex-mode-map "\C-c\"" 'bibtex-remove-double-quotes)
+  (define-key bibtex-mode-map "\C-c\C-c" 'bibtex-clean-entry)
+  (define-key bibtex-mode-map "\C-c?" 'describe-mode)
+  (define-key bibtex-mode-map "\C-c\C-p" 'bibtex-pop-previous)
+  (define-key bibtex-mode-map "\C-c\C-n" 'bibtex-pop-next)
+  (define-key bibtex-mode-map "\C-c\C-k" 'bibtex-kill-optional-field)
+  (define-key bibtex-mode-map "\C-c\C-d" 'bibtex-empty-field)
+
+  (define-key bibtex-mode-map "\C-c\C-e\C-a" 'bibtex-Article)
+  (define-key bibtex-mode-map "\C-c\C-e\C-b" 'bibtex-Book)
+  (define-key bibtex-mode-map "\C-c\C-e\C-d" 'bibtex-DEAthesis)
+  (define-key bibtex-mode-map "\C-c\C-e\C-c" 'bibtex-InProceedings)
+  (define-key bibtex-mode-map "\C-c\C-e\C-i" 'bibtex-InBook)
+  (define-key bibtex-mode-map "\C-c\C-ei" 'bibtex-InCollection)
+  (define-key bibtex-mode-map "\C-c\C-eI" 'bibtex-InProceedings)
+  (define-key bibtex-mode-map "\C-c\C-e\C-m" 'bibtex-Manual)
+  (define-key bibtex-mode-map "\C-c\C-em" 'bibtex-MastersThesis)
+  (define-key bibtex-mode-map "\C-c\C-eM" 'bibtex-Misc)
+  (define-key bibtex-mode-map "\C-c\C-o" 'bibtex-remove-OPT)
+  (define-key bibtex-mode-map "\C-c\C-e\C-p" 'bibtex-PhdThesis)
+  (define-key bibtex-mode-map "\C-c\C-ep" 'bibtex-Proceedings)
+  (define-key bibtex-mode-map "\C-c\C-eP" 'bibtex-preamble)
+  (define-key bibtex-mode-map "\C-c\C-e\C-t" 'bibtex-TechReport)
+  (define-key bibtex-mode-map "\C-c\C-e\C-s" 'bibtex-string)
+  (define-key bibtex-mode-map "\C-c\C-e\C-u" 'bibtex-Unpublished)
+
+  (auto-fill-mode 1)			; nice alignements
+  (setq left-margin (+ bibtex-text-alignment 1))
+
+  (run-hooks 'bibtex-mode-hook))
+
+(defun bibtex-move-outside-of-entry ()
+  "Make sure we are outside of a bib entry"
+  (cond ((or
+	  (= (point) (point-max))
+	  (= (point) (point-min))
+	  (looking-at "[ \n]*@")
+	  )
+	 t)
+	(t
+	 (backward-paragraph)
+	 (forward-paragraph)))
+  (re-search-forward "[ \t\n]*" (point-max) t))
+
+;;
+;; note: this should really take lists of strings OR of lists.  in the
+;;       second case, one can use either list.  (ie:
+;;                "name" (("crossref") ("journal" "year"))     )
+;;
+
+(defun bibtex-entry (entry-type required optional)
+  (bibtex-move-outside-of-entry)
+  (insert "@" entry-type "{")
+  (mapcar 'bibtex-make-field required)
+  (if bibtex-include-OPTcrossref
+      (bibtex-make-optional-field "crossref"))
+  (if bibtex-include-OPTkey
+      (bibtex-make-optional-field "key"))
+  (mapcar 'bibtex-make-optional-field optional)
+  (if bibtex-mode-user-optional-fields		;MON...
+      (mapcar 'bibtex-make-optional-field
+	      bibtex-mode-user-optional-fields))
+  (if bibtex-include-OPTannote
+      (bibtex-make-optional-field "annote"))
+  (insert "\n}\n\n")
+  (forward-char -3)
+  (up-list -1)
+  (forward-char 1))  
+
+(defun bibtex-make-field (str)
+  (interactive "sBibTeX entry type: ")
+  (insert ",\n")
+  (indent-to-column bibtex-name-alignement)
+  (insert str " = ")
+  (indent-to-column bibtex-text-alignment)
+  (insert "\"\"")
+  nil)
+
+(defun bibtex-make-optional-field (str)
+  (interactive "sOptional BibTeX entry type: ")
+  (insert ",\n")
+  (indent-to-column bibtex-name-alignement)
+  (insert "OPT" str " = ")
+  (indent-to-column bibtex-text-alignment)
+  (insert "\"\"")
+  nil)
+
+;; What to do about crossref?  if present, journal and year are 
+;; both optional.  Due to this, i move all of them into optional. -- MON
+
+(defun bibtex-Article ()
+  (interactive)
+  (if bibtex-include-OPTcrossref
+      (bibtex-entry "Article" '("author" "title")
+		    '("journal" "year" "volume" "number" "pages"
+		      "month" "note"))
+    (bibtex-entry "Article" '("author" "title" "journal" "year")
+		  '("volume" "number" "pages" "month" "note"))))
+
+(defun bibtex-Book ()
+  (interactive)
+  (bibtex-entry "Book" '("author" "title" "publisher" "year")
+ 		'("editor" "volume" "number" "series" "address"
+		  "edition" "month" "note")))
+
+(defun bibtex-Booklet ()
+  (interactive)
+  (bibtex-entry "Booklet" '("title")
+		'("author" "howpublished" "address" "month" "year" "note")))
+
+;; France: Dipl\^{o}me d'Etudes Approfondies (similar to Master's)
+(defun bibtex-DEAthesis ()
+  (interactive)
+  (bibtex-entry "DEAthesis" '("author" "title" "school" "year")
+		'("address" "month" "note")))
+
+(defun bibtex-InBook ()
+  (interactive)
+  (if bibtex-include-OPTcrossref
+      (bibtex-entry "InBook" '("author" "title" "chapter")
+		    '("publisher" "year" "editor" "pages" "volume" "number"
+		      "series" "address" "edition" "month" "type" "note"))
+    (bibtex-entry "InBook" '("author" "title" "chapter" "publisher" "year")
+		  '("editor" "pages" "volume" "number" "series" "address"
+		    "edition" "month" "type" "note"))))
+
+;; In next 2, for crossref case, put pages near beginning of 
+;; optionals as it will be used most often  -- MON
+(defun bibtex-InCollection ()
+  (interactive)
+  (if bibtex-include-OPTcrossref
+      (bibtex-entry "InCollection" '("author" "title")
+		    '("pages" "booktitle" "publisher" "year"
+		      "editor" "volume" "number" "series" "type" "chapter"
+		      "address" "edition" "month" "note"))
+    (bibtex-entry "InCollection" '("author" "title"
+				   "booktitle" "publisher" "year")
+		  '("editor" "volume" "number" "series" "type" "chapter"
+		    "pages" "address" "edition" "month" "note"))))
+
+(defun bibtex-InProceedings ()
+  (interactive)
+  (if bibtex-include-OPTcrossref
+      (bibtex-entry "InProceedings" '("author" "title")
+		    '( "pages" "editor" "volume" "number" "series"
+		       "booktitle" "year"
+		       "organization" "publisher" "address" "month" "note"))
+    (bibtex-entry "InProceedings" '("author" "title" "booktitle" "year")
+		  '("editor" "volume" "number" "series" "pages"
+		    "organization" "publisher" "address" "month" "note"))))
+
+(defun bibtex-Manual ()
+  (interactive)
+  (bibtex-entry "Manual" '("title")
+		'("author" "organization" "address" "edition" "year"
+			   "month" "note")))
+
+(defun bibtex-MastersThesis ()
+  (interactive)
+  (bibtex-entry "MastersThesis" '("author" "title" "school" "year")
+		'("address" "month" "note" "type")))
+
+(defun bibtex-Misc ()
+  (interactive)
+  (bibtex-entry "Misc" '()
+		'("author" "title" "howpublished" "year" "month" "note")))
+
+(defun bibtex-PhdThesis ()
+  (interactive)
+  (bibtex-entry "PhdThesis" '("author" "title" "school" "year")
+		'("address" "month" "type" "note")))
+
+(defun bibtex-Proceedings ()
+  (interactive)
+  (bibtex-entry "Proceedings" '("title" "year")
+		'("editor" "volume" "number" "series" "publisher"
+		  "organization" "address" "month" "note")))
+
+(defun bibtex-TechReport ()
+  (interactive)
+  (bibtex-entry "TechReport" '("author" "title" "institution" "year")
+		'("type" "number" "address" "month" "note")))
+
+
+(defun bibtex-Unpublished ()
+  (interactive)
+  (bibtex-entry "Unpublished" '("author" "title" "note")
+		'("year" "month")))
+
+(defun bibtex-string ()
+  (interactive)
+  (bibtex-move-outside-of-entry)
+  (insert "@string{ = """"}\n")
+  (previous-line 1)
+  (forward-char 8))
+
+(defun bibtex-preamble ()
+  (interactive)
+  (bibtex-move-outside-of-entry)
+  (insert "@Preamble{}\n")
+  (previous-line 1)
+  (forward-char 10))
+
+(defun bibtex-next-field (arg)
+  "Finds end of text of next BibTeX field; with arg, to its beginning"
+  (interactive "P")
+  (bibtex-inside-field)
+  (let ((start (point)))
+    (condition-case ()
+	(progn
+	  (bibtex-enclosing-field)
+	  (goto-char (match-end 0))
+	  (forward-char 2))
+      (error
+       (goto-char start)
+       (end-of-line)
+       (forward-char 1))))
+  (bibtex-find-text arg))
+
+(defun bibtex-find-text (arg)
+  "Go to end of text of current field; with arg, go to beginning."
+  (interactive "P")
+  (bibtex-inside-field)
+  (bibtex-enclosing-field)
+  (if arg
+      (progn
+	(goto-char (match-beginning bibtex-text-in-field))
+	(if (looking-at "\"")
+	    (forward-char 1)))
+    (goto-char (match-end bibtex-text-in-field))
+    (if (= (preceding-char) ?\")
+	(forward-char -1))))
+
+(defun bibtex-remove-OPT ()
+  "Removes the 'OPT' starting optional arguments and goes to end of text"
+  (interactive)
+  (bibtex-inside-field)
+  (bibtex-enclosing-field)
+  (save-excursion
+    (goto-char (match-beginning bibtex-name-in-field))
+    (if (looking-at "OPT")
+	(delete-char (length "OPT"))))
+  (bibtex-inside-field))
+
+(defun bibtex-inside-field ()
+  "Try to avoid point being at end of a bibtex field."
+  (interactive)
+  (end-of-line)
+  (skip-chars-backward " \t")		;delete these chars? -- MON
+  (cond ((= (preceding-char) ?,)
+	 (forward-char -1)))
+  (cond ((= (preceding-char) ?\")
+	 (forward-char -1))))		;only go back if quote
+
+
+(defun bibtex-remove-double-quotes ()
+  "Removes """" around string."
+  (interactive)
+  (save-excursion
+    (bibtex-inside-field)
+    (bibtex-enclosing-field)
+    (let ((start (match-beginning bibtex-text-in-field))
+	  (stop (match-end  bibtex-text-in-field)))
+      (goto-char stop)
+      (forward-char -1)
+      (if (looking-at "\"")
+	  (delete-char 1))
+      (goto-char start)
+      (if (looking-at "\"")
+	  (delete-char 1)))))
+
+(defun bibtex-kill-optional-field ()
+  "Kill the entire enclosing optional BibTeX field"
+  (interactive)
+  (bibtex-inside-field)
+  (bibtex-enclosing-field)
+  (goto-char (match-beginning bibtex-name-in-field))
+  (let ((the-end (match-end 0))
+	(the-beginning (match-beginning 0)))
+    (if (looking-at "OPT")
+	(progn
+	  (goto-char the-end)
+	  (skip-chars-forward " \t\n,")
+	  (kill-region the-beginning the-end))
+      (error "Mandatory fields can't be killed"))))
+
+(defun bibtex-empty-field ()
+  "Delete the text part of the current field, replace with empty text"
+  (interactive)
+  (bibtex-inside-field)
+  (bibtex-enclosing-field)
+  (goto-char (match-beginning bibtex-text-in-field))
+  (kill-region (point) (match-end bibtex-text-in-field))
+  (insert "\"\"")
+  (bibtex-find-text t))
+
+
+(defun bibtex-pop-previous (arg)
+  "Replace text of current field with the text of similar field in previous entry.
+With arg, go up ARG entries.  Repeated, goes up so many times.  May be
+intermixed with \\[bibtex-pop-next] (bibtex-pop-next)."
+  (interactive "p")
+  (bibtex-inside-field)
+  (save-excursion
+    ; parse current field
+    (bibtex-enclosing-field)
+    (let ((start-old-text (match-beginning bibtex-text-in-field))
+	  (stop-old-text  (match-end bibtex-text-in-field))
+	  (start-name (match-beginning bibtex-name-in-field))
+	  (stop-name (match-end bibtex-name-in-field))
+	  (new-text))
+      (goto-char start-name)
+      ; construct regexp for previous field with same name as this one
+      (let ((matching-entry
+	     (bibtex-cfield
+	      (buffer-substring (if (looking-at "OPT")
+				    (+ (point) (length "OPT"))
+				  (point))
+				stop-name)
+	      bibtex-field-text)))
+	
+	; if executed several times in a row, start each search where the
+	; last one finished
+	(cond ((or (eq last-command 'bibtex-pop-previous)
+		   (eq last-command 'bibtex-pop-next))
+	       t
+	       )
+	      (t
+	       (bibtex-enclosing-reference)
+	       (setq bibtex-pop-previous-search-point (match-beginning 0))
+	       (setq bibtex-pop-next-search-point (match-end 0))))
+	(goto-char bibtex-pop-previous-search-point)
+	
+	; Now search for arg'th previous similar field
+	(cond
+	 ((re-search-backward matching-entry (point-min) t arg)
+	  (setq new-text
+		(buffer-substring (match-beginning bibtex-text-in-cfield)
+				  (match-end bibtex-text-in-cfield)))
+	  ; Found a matching field. Remember boundaries.
+	  (setq bibtex-pop-next-search-point (match-end 0))
+	  (setq bibtex-pop-previous-search-point (match-beginning 0))
+	  (bibtex-flash-head)
+	  ; Go back to where we started, delete old text, and pop new.
+	  (goto-char stop-old-text)
+	  (delete-region start-old-text stop-old-text)
+	  (insert new-text))
+	 (t				; search failed
+	  (error "No previous matching BibTeX field."))))))
+  (setq this-command 'bibtex-pop-previous))
+
+(defun bibtex-pop-next (arg)
+  "Replace text of current field with the text of similar field in next entry.
+With arg, go up ARG entries.  Repeated, goes up so many times.  May be
+intermixed with \\[bibtex-pop-previous] (bibtex-pop-previous)."
+  (interactive "p")
+  (bibtex-inside-field)
+  (save-excursion
+    ; parse current field
+    (bibtex-enclosing-field)
+    (let ((start-old-text (match-beginning bibtex-text-in-field))
+	  (stop-old-text  (match-end bibtex-text-in-field))
+	  (start-name (match-beginning bibtex-name-in-field))
+	  (stop-name (match-end bibtex-name-in-field))
+	  (new-text))
+      (goto-char start-name)
+      ; construct regexp for next field with same name as this one,
+      ; ignoring possible OPT's
+      (let ((matching-entry
+	     (bibtex-cfield
+	      (buffer-substring (if (looking-at "OPT")
+				    (+ (point) (length "OPT"))
+				  (point))
+				stop-name)
+	      bibtex-field-text)))
+	
+	; if executed several times in a row, start each search where the
+	; last one finished
+	(cond ((or (eq last-command 'bibtex-pop-next)
+		   (eq last-command 'bibtex-pop-previous))
+	       t
+	       )
+	      (t
+	       (bibtex-enclosing-reference)
+	       (setq bibtex-pop-previous-search-point (match-beginning 0))
+	       (setq bibtex-pop-next-search-point (match-end 0))))
+	(goto-char bibtex-pop-next-search-point)
+	
+	; Now search for arg'th next similar field
+	(cond
+	 ((re-search-forward matching-entry (point-max) t arg)
+	  (setq new-text
+		(buffer-substring (match-beginning bibtex-text-in-cfield)
+				  (match-end bibtex-text-in-cfield)))
+	  ; Found a matching field. Remember boundaries.
+	  (setq bibtex-pop-next-search-point (match-end 0))
+	  (setq bibtex-pop-previous-search-point (match-beginning 0))
+	  (bibtex-flash-head)
+	  ; Go back to where we started, delete old text, and pop new.
+	  (goto-char stop-old-text)
+	  (delete-region start-old-text stop-old-text)
+	  (insert new-text))
+	 (t				; search failed
+	  (error "No next matching BibTeX field."))))))
+  (setq this-command 'bibtex-pop-next))
+
+(defun bibtex-flash-head ()
+  "Flash at BibTeX reference head before point, if exists.  (Moves point)."
+  (let ((flash))
+    (cond ((re-search-backward bibtex-reference-head (point-min) t)
+	   (goto-char (match-beginning bibtex-type-in-head))
+	   (setq flash (match-end bibtex-key-in-reference)))
+	  (t
+	   (end-of-line)
+	   (skip-chars-backward " \t")
+	   (setq flash (point))
+	   (beginning-of-line)
+	   (skip-chars-forward " \t")))
+    (if (pos-visible-in-window-p (point))
+	(sit-for 1)
+      (message "From: %s"
+	       (buffer-substring (point) flash)))))
+
+
+
+(defun bibtex-enclosing-field ()
+  "Search for BibTeX field enclosing point.
+Point moves to end of field; also, use match-beginning and match-end
+to parse the field."
+  (condition-case errname
+      (bibtex-enclosing-regexp bibtex-field)
+    (search-failed
+     (error "Can't find enclosing BibTeX field."))))
+
+(defun bibtex-enclosing-reference ()
+  "Search for BibTeX reference enclosing point.
+Point moves to end of reference; also, use match-beginning and match-end
+to parse the reference."
+  (condition-case errname
+      (bibtex-enclosing-regexp bibtex-reference)
+    (search-failed
+     (error "Can't find enclosing BibTeX reference."))))
+
+(defun bibtex-enclosing-regexp (regexp)
+  "Search for REGEXP enclosing point.
+Point moves to end of REGEXP.  See also match-beginning and match-end.
+If an enclosing REGEXP is not found, signals search-failed; point is left in
+an undefined location.
+
+[Doesn't something like this exist already?]"
+  
+  (interactive "sRegexp: ")
+  ; compute reasonable limits for the loop
+  (let* ((initial (point))
+	 (right (if (re-search-forward regexp (point-max) t)
+		    (match-end 0)
+		  (point-max)))
+	 (left
+	  (progn
+	    (goto-char initial)
+	    (if (re-search-backward regexp (point-min) t)
+		(match-beginning 0)
+	      (point-min)))))
+    ; within the prescribed limits, loop until a match is found
+    (goto-char left)
+    (re-search-forward regexp right nil 1)
+    (if (> (match-beginning 0) initial)
+	(signal 'search-failed (list regexp)))	  
+    (while (<= (match-end 0) initial)
+      (re-search-forward regexp right nil 1)
+      (if (> (match-beginning 0) initial)
+	  (signal 'search-failed (list regexp))))
+    ))
+
+(defun bibtex-clean-entry ()
+  "For all optional fields of current BibTeX entry: if empty, kill the whole field; otherwise, remove the \"OPT\" string in the name; if text numerical, remove double-quotes.  For all mandatory fields: if empty, signal error."
+  (interactive)
+  (bibtex-enclosing-reference)
+  (goto-char (match-beginning 0))
+  (let ((start (point)))
+    (save-restriction
+      (narrow-to-region start (match-end 0))
+      (while (re-search-forward bibtex-field (point-max) t 1)
+	(let ((begin-field (match-beginning 0))
+	      (end-field (match-end 0))
+	      (begin-name (match-beginning bibtex-name-in-field))
+	      (end-name (match-end  bibtex-name-in-field))
+	      (begin-text (match-beginning bibtex-text-in-field))
+	      (end-text (match-end bibtex-text-in-field))
+	      )
+	  (goto-char begin-name)
+	  (cond ((and
+		  (looking-at "OPT")
+		  bibtex-clean-entry-zap-empty-opts)
+		 (goto-char begin-text)
+		 (if (looking-at "\"\"") ; empty: delete whole field
+		     (delete-region begin-field end-field)
+		   ; otherwise: not empty, delete "OPT"
+		   (goto-char begin-name)
+		   (delete-char (length "OPT"))
+		   (goto-char begin-field) ; and loop to go through next test
+		   ))
+		(t
+		 (goto-char begin-text)
+		 (cond ((looking-at "\"[0-9]+\"") ; if numerical,
+			(goto-char end-text)
+			(delete-char -1) ; delete enclosing double-quotes
+			(goto-char begin-text)
+			(delete-char 1)
+			(goto-char end-field) ; go to end for next search
+			(forward-char -2) ; to compensate for the 2 quotes deleted
+			)
+		       ((looking-at "\"\"") ; if empty quotes, complain
+			(forward-char 1)
+			(if (not (or (equal (buffer-substring
+					     begin-name
+					     (+ begin-name 3))
+					    "OPT")
+				     (equal (buffer-substring
+					     begin-name
+					     (+ begin-name 3))
+					    "opt")))
+			    (error "Mandatory field ``%s'' is empty"
+				   (buffer-substring begin-name end-name))))
+		       (t
+			(goto-char end-field))))))))
+    (goto-char start)
+    (skip-chars-forward "@a-zA-Z")
+    (bibtex-enclosing-reference)
+    (goto-char (match-end 0))
+    (skip-chars-forward " \t\n")))
+
+
+
+;;; X window menus for bibtex mode
+
+(defun bibtex-x-help (arg)
+  "Mouse commands for BibTeX mode"
+  
+  (let ((selection
+	 (x-popup-menu
+	  arg
+	  '("BibTeX commands"
+	    ("BibTeX entry types"
+	     (" article in conference Proceedings " . bibtex-InProceedings)
+	     ("        Article in journal         " . bibtex-Article)
+	     ("               Book                " . bibtex-Book)
+	     ("             Booklet               " . bibtex-Booklet)
+	     ("            Conference	          " . bibtex-InProceedings)
+	     ("         Master's Thesis           " . bibtex-MastersThesis)
+	     ("            DEA Thesis             " . bibtex-DEAthesis)
+	     ("            Phd. Thesis            " . bibtex-PhdThesis)
+	     ("         Technical Report          " . bibtex-TechReport)
+	     ("         technical Manual          " . bibtex-Manual)
+	     ("      conference Proceedings       " . bibtex-Proceedings)
+	     ("        a chapter in a Book        " . bibtex-InBook)
+	     ("    an article in a Collection     " . bibtex-InCollection)
+	     ("           miscellaneous           " . bibtex-Misc)
+	     ("            unpublished            " . bibtex-Unpublished)
+	     ("              string               " . bibtex-string)
+	     ("             preamble              " . bibtex-preamble)
+	     )
+	    ("Moving around and editing"
+	     ("            next field             " . bibtex-next-field)
+	     ("          to end of field          " . bibtex-find-text)
+	     ("snatch from similar preceding field" . bibtex-pop-previous)
+	     ("snatch from similar following field" . bibtex-pop-next)
+	     ("            remove OPT             " . bibtex-remove-OPT)
+	     ("           remove quotes           "
+	      . bibtex-remove-double-quotes)
+	     ("          clean up entry           " . bibtex-clean-entry)
+	     )
+	    ("help"
+	     ("       describe BibTeX mode        " . describe-mode)
+	     )))))
+    (and selection (call-interactively selection))))
+
+(defun bibtex-x-environment ()
+  "Set up X menus for BibTeX mode.  Call it as bibtex-mode-hook, or interactively"
+  (interactive)
+  (require 'x-mouse)
+  (define-key mouse-map x-button-c-right 'bibtex-x-help)
+  )
+
+
+
+;; Please don't send anything to bug-gnu-emacs about these Sunwindows functions
+;; since we aren't interested.  See etc/SUN-SUPPORT for the reasons why
+;; we consider this nothing but a distraction from our work.
+
+(if (fboundp 'defmenu)
+    (progn
+
+(defmenu bibtex-sun-entry-menu 
+  ("Article In Conf. Proc."
+   (lambda () (eval-in-window *menu-window* (bibtex-InProceedings))))
+  ("Article In Journal"
+   (lambda () (eval-in-window *menu-window* (bibtex-Article))))
+  ("Book"
+   (lambda () (eval-in-window *menu-window* (bibtex-Book))))
+  ("Booklet"
+   (lambda () (eval-in-window *menu-window* (bibtex-Booklet))))
+  ("Master's Thesis"
+   (lambda () (eval-in-window *menu-window* (bibtex-MastersThesis))))
+  ;;("DEA Thesis" bibtex-DEAthesis)
+  ("PhD. Thesis"
+   (lambda () (eval-in-window *menu-window* (bibtex-PhdThesis))))
+  ("Technical Report"
+   (lambda () (eval-in-window *menu-window* (bibtex-TechReport))))
+  ("Technical Manual"
+   (lambda () (eval-in-window *menu-window* (bibtex-Manual))))
+  ("Conference Proceedings"
+   (lambda () (eval-in-window *menu-window* (bibtex-Proceedings))))
+  ("In A Book"
+   (lambda () (eval-in-window *menu-window* (bibtex-InBook))))
+  ("In A Collection"
+   (lambda () (eval-in-window *menu-window* (bibtex-InCollection))))
+  ("Miscellaneous"
+   (lambda () (eval-in-window *menu-window* (bibtex-Misc))))
+  ("Unpublished"
+   (lambda () (eval-in-window *menu-window* (bibtex-Unpublished)))))
+
+(defmenu bibtex-sun-menu
+  ("BibTeX menu")
+  ("add entry" . bibtex-sun-entry-menu)
+  ("add string"
+   (lambda () (eval-in-window *menu-window* (bibtex-string))))
+  ;("next field" bibtex-next-position)
+  ;("to end of field" bibtex-find-it)
+;  ("remove OPT"
+;   (lambda () (eval-in-window *menu-window* (bibtex-remove-opt))))
+;  ("remove quotes"
+;   (lambda () (eval-in-window *menu-window* (bibtex-remove-double-quotes))))
+;  ("remove this line"
+;   (lambda () (eval-in-window *menu-window* (kill-current-line))))
+  ("describe BibTeX mode"
+   (lambda () (eval-in-window *menu-window* (describe-mode))))
+  ("Main Emacs menu" . emacs-menu))
+ 
+(defun bibtex-sun-menu-eval (window x y)
+  "Pop-up menu of BibTeX commands."
+  (sun-menu-evaluate window (1+ x) (1- y) 'bibtex-sun-menu))
+
+(defun bibtex-sun-environment ()
+  "Set up sun menus for BibTeX mode.  Call it as bibtex-mode-hook, or interactively"
+  (interactive)
+  (local-set-mouse  '(text right) 'bibtex-sun-menu-eval))
+
+))  ; matches (if...
+
+;;; ------------- end bibtex-mode.el -------------------------------