diff lisp/modes/bibtex.el @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children ac2d302a0011
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/modes/bibtex.el	Mon Aug 13 08:45:50 2007 +0200
@@ -0,0 +1,1530 @@
+;;; bibtex.el --- BibTeX mode for GNU Emacs
+
+;; Copyright (C) 1992 Free Software Foundation, Inc.
+
+;; Author: Bengt Martensson <ubrinf!mond!bengt>
+;;	Mark Shapiro <shapiro@corto.inria.fr>
+;;	Mike Newton <newton@gumby.cs.caltech.edu>
+;;	Aaron Larson <alarson@src.honeywell.com>
+;; Version: 1.3.1
+;; Maintainer:Aaron Larson <alarson@src.honeywell.com>
+;; Adapted-By: ESR
+;; Keywords: tex, bib
+
+;; This file is part of XEmacs.
+
+;; XEmacs is free software; you can redistribute it and/or modify it
+;; under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; XEmacs 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 XEmacs; see the file COPYING.  If not, write to the Free
+;; Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+;;; TODO distribute texinfo file.
+
+;;; LCD Archive Entry:
+;;; bibtex-mode|Bengt Martensson, Marc Shapiro, Aaron Larson|
+;;; alarson@src.honeywell.com|
+;;; Support for maintaining BibTeX format bibliography databases|
+;;; 93-03-29|version 1.3|~/modes/bibtex-mode.el.Z|
+
+;;; Commentary:
+
+;;; BUGS:
+;;;   1. using regular expressions to match the entire bibtex entry dies
+;;;      on long bibtex entires (e.g. those containing abstracts) since
+;;;      the length of regular expression matches is fairly limited.
+;;;   2. When inserting a string (with \C-C\C-E\s) hitting a TAB results
+;;;      in the error message "Can't find enclosing Bibtex field" instead
+;;;      of moving to the empty string. [reported by gernot@cs.unsw.oz.au]
+;;;   3. Function string-equalp should be in a library file, not in this
+;;;      file. 
+
+;;; (current keeper: alarson@src.honeywell.com
+;;;  previous: shapiro@corto.inria.fr)
+
+;;; Change Log:
+
+;; Mon Mar 29 14:06:06 1993  Aaron Larson  (alarson at gendibal)
+;;
+;;	* bibtex.el: V1.3 released Mar 30, 1993
+;;	(bibtex-field-name):  Fix to match definition if latex manual,
+;;	specifically letters, digits, and punctuation other than comma.
+;;	Underscore is retained for historical reasons. 
+;;	(bibtex-make-field):  Fix to work around bug in Lucid prin1-to-string
+;;	function as reported by Martin Sjolin <marsj@ida.liu.se>. 
+;;	(bibtex-entry):  minor code cleanup.
+;;	(bibtex-mode-map): Remove key binding (C-c n) for
+;;	narrow-to-bibtex-entry, previous binding violated emacs policy of
+;;	reserving C-c followed by a letter for user customization.
+;;	revise modification history to better conform to FSF changelog
+;;	standards.
+;;	(bibtex-refile-entry): Removed.  Would need disclaimer papers to
+;;	incorporate it into official sources, and unable to contact author.
+;;	Fix minor "syntax" errors in documentation strings and such found
+;;	by new byte compiler.  Funs bibtex-mode, bibtex-remove-double-quotes
+;;
+;;
+;; Fri Jan 15 14:06:06 1993  Aaron Larson  (alarson at gendibal)
+;;
+;;	* bibtex.el: V1.2 released Feb 15 1993
+;;	(find-bibtex-entry-location bibtex-make-field): Fixed placement of
+;;	"interactive specification".  [Bug report from
+;;	mernst@theory.lcs.mit.edu] 
+;;	Fixed problem where bibtex-entry would fail if user typed entry
+;;	name in wrong case.
+;;	(bibtex-inside-field) Position the cursor _before_ the last comma
+;;	on a line (the comma is not necessarily "inside" the field); this
+;;	does not seem to break any existing code. ref sct@dcs.edinburgh.ac.uk
+;;	(bibtex-enclosing-field, bibtex-enclosing-reference): leave
+;;	point unmoved if no enclosing field/reference is found.  As a
+;;	result of changes (3) and (4) bibtex-next-field works properly,
+;;	even when called from the entry key position. 
+;;	(bibtex-remove-OPT): realign the '=' after removing the 'opt'.
+;;	(bibtex-clean-entry): always remove any trailing comma from the
+;;	end of a bibtex entry (these commas get stripped automatically when
+;;	optional fields are killed by bibtex-kill-optional-field, but can be
+;;	left if optional fields are removed by other means).
+;;	(bibtex-x-help) Replace tab with spaces in X menu as noted by
+;;	khera@cs.duke.edu
+;;	(bibtex-refile-entry): Added (from brannon@jove.cs.caltech.edu)
+;;	(bibtex-sort-ignore-string-entries sort-bibtex-entries,
+;;	map-bibtex-entries): Added variable as requested by  
+;;	gernot@cs.unsw.oz.au, required changes to funs.
+;;	(bibtex-current-entry-label): Added at request of
+;;	yasuro@maekawa.is.uec.ac.jp
+;;	(bibtex-DEAthesis:) Deleted along with corresponding entry from
+;;	bibtex-x-help  per shapiro@corto.inria.fr 
+;;	Moved narrow-to-bibtex-entry from C-c C-n to C-c n (the previous
+;;	binding was in conflict with the binding for bibtex-pop-next.
+;;	bug report from [shapiro@corto.inria.fr]
+;;
+
+;;; 
+;;; alarson@src.honeywell.com 92-Feb-13
+;;;   1. Made bibtex-entry user callable, now prompts for entry type (e.g.
+;;;      Article), with completion, and bound it to a key.  This is now my
+;;;      preferred way to add most entries. 
+;;;   2. Made fields of a bibtex entry derived from the alist bibtex-entry-
+;;;      field-alist.
+;;;   3. Fixed handling of escaped double quotes, e.g. "Schr{\"o}dinger".
+;;;   4. Fixed bug where unhiding bibtex entries moved point.
+;;;   5. Made "field name" specs permit (name . value) for defaulting.  E.g. 
+;;;       (setq bibtex-mode-user-optional-fields '(("library" . "alarson")))
+;;;      will generate the field:
+;;;       library	= "alarson",
+;;;   6. Added binding for narrow-to-bibtex-entry
+;;;   7. Adding a bibtex entry now runs hook: bibtex-add-entry-hook
+;;;   8. Made bibtex-clean-entry fixup text alignment, and eliminated the
+;;;      dependency on bibtex-enclosing-reference which has a problem with
+;;;      long entries (e.g. those containing abstracts).
+;;; 
+;;; alarson@src.honeywell.com 92-Jan-31
+;;;   Added support for: ispell, beginning/end of entry movement, a simple
+;;;   outline like mode (hide the bodies of bibtex entries), support for
+;;;   sorting bibtex entries, and maintaining them in sorted order, and
+;;;   simple buffer validation.
+;;;   User visible functions added:
+;;;      ispell-{abstract,bibtex-entry}, {beginning,end}-of-bibtex-entry
+;;;      hide-bibtex-entry-bodies, sort-bibtex-entries, validate-bibtex-
+;;;      buffer, find-bibtex-duplicates
+;;;   user visible variables added:
+;;; 	 bibtex-maintain-sorted-entries
+;;;   new local keybindings:
+;;; 	"	tex-insert-quote
+;;; 	C-c$   ispell-bibtex-entry
+;;; 	M-C-a  beginning-of-bibtex-entry
+;;; 	M-C-e  end-of-bibtex-entry
+;;; Mike Newton (newton@gumby.cs.caltech.edu) 90.11.17
+;;;  * Handle items like
+;;;          title = poft # "Fifth Tri-quarterly" # 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 wouldn't handle the # construct)
+;;;		and it now handles a small subset of the {} cases
+
+;;; 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
+;;;  * skip-whitespace replaced by skip-chars-forward
+;;;  * 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
+;;;  * Fixed bug in bibtex-flash-entry
+;;;  * 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
+
+;;; Code:
+
+;;; 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)
+
+;;; these guys typically don't have autoloads...[alarson:19920131.1548CST]
+;;; Check for fboundp first so that if user autoloads them from non standard 
+;;; places, the users bindings will take precedence.
+(if (not (fboundp 'tex-insert-quote))
+    (autoload 'tex-insert-quote "tex-mode"))
+(if (not (fboundp 'sort-subr))
+    (autoload 'sort-subr "sort"))
+
+;;; These should be in a more generally accessible location. 
+
+(defun string-equalp (s1 s2)
+  "Like string= except differences in case are ignored."
+  (let ((ss1 (if (symbolp s1) (symbol-name s1) s1))
+	(ss2 (if (symbolp s2) (symbol-name s2) s2)))
+    (and (= (length ss1) (length ss2))
+	 (string-equal (upcase ss1) (upcase ss2)))))
+
+;;; This should be moved into simple.el, and the functions there modified
+;;; to call it rather than doing it themselves.
+(defun put-string-on-kill-ring (string)
+  "Make STRING be the first element of the kill ring."
+  (setq kill-ring (cons string kill-ring))
+  (if (> (length kill-ring) kill-ring-max)
+      (setcdr (nthcdr (1- kill-ring-max) kill-ring) nil))
+  (setq kill-ring-yank-pointer kill-ring))
+
+
+
+
+(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 want to have as always present 
+when making a bibtex entry.  One possibility is for ``keywords''.  
+Entries can be either strings or conses, in which case the car should be 
+string and the cdr the value to be inserted.")
+
+(defvar bibtex-mode-syntax-table
+  (let ((st (make-syntax-table)))
+    ;; [alarson:19920214.1004CST] make double quote a string quote
+    (modify-syntax-entry ?\" "\"" st)
+    (modify-syntax-entry ?$ "$$  " st)
+    (modify-syntax-entry ?% "<   " st)
+    (modify-syntax-entry ?'  "w   " st)
+    (modify-syntax-entry ?@  "w   " st)
+    (modify-syntax-entry ?\\ "\\" st)
+    (modify-syntax-entry ?\f ">   " st)
+    (modify-syntax-entry ?\n ">   " st)
+    (modify-syntax-entry ?~ " " st)
+    st))
+
+(defvar bibtex-mode-abbrev-table nil "")
+(define-abbrev-table 'bibtex-mode-abbrev-table ())
+(defvar bibtex-mode-map
+  (let ((km (make-sparse-keymap)))
+    
+    (define-key km "\t" 'bibtex-find-text)
+    (define-key km "\n" 'bibtex-next-field)
+    (define-key km "\C-c\"" 'bibtex-remove-double-quotes)
+    (define-key km "\C-c\C-c" 'bibtex-clean-entry)
+    (define-key km "\C-c?" 'describe-mode)
+    (define-key km "\C-c\C-p" 'bibtex-pop-previous)
+    (define-key km "\C-c\C-n" 'bibtex-pop-next)
+    (define-key km "\C-c\C-k" 'bibtex-kill-optional-field)
+    (define-key km "\C-c\C-d" 'bibtex-empty-field)
+
+    ;; [alarson:19920131.1543CST]
+    (define-key km "\""   'tex-insert-quote)
+    (define-key km "\C-c$"   'ispell-bibtex-entry)
+    (define-key km "\M-\C-a"   'beginning-of-bibtex-entry)
+    (define-key km "\M-\C-e"   'end-of-bibtex-entry)
+    (define-key km "\C-c\C-b"   'bibtex-entry)
+;    (define-key km "\C-cn" 'narrow-to-bibtex-entry)
+
+    (define-key km "\C-c\C-e\C-a" 'bibtex-Article)
+    (define-key km "\C-c\C-e\C-b" 'bibtex-Book)
+;    (define-key km "\C-c\C-e\C-d" 'bibtex-DEAthesis)
+    (define-key km "\C-c\C-e\C-c" 'bibtex-InProceedings)
+    (define-key km "\C-c\C-e\C-i" 'bibtex-InBook)
+    (define-key km "\C-c\C-ei" 'bibtex-InCollection)
+    (define-key km "\C-c\C-eI" 'bibtex-InProceedings)
+    (define-key km "\C-c\C-e\C-m" 'bibtex-Manual)
+    (define-key km "\C-c\C-em" 'bibtex-MastersThesis)
+    (define-key km "\C-c\C-eM" 'bibtex-Misc)
+    (define-key km "\C-c\C-o" 'bibtex-remove-OPT)
+    (define-key km "\C-c\C-e\C-p" 'bibtex-PhdThesis)
+    (define-key km "\C-c\C-ep" 'bibtex-Proceedings)
+    (define-key km "\C-c\C-eP" 'bibtex-preamble)
+    (define-key km "\C-c\C-e\C-t" 'bibtex-TechReport)
+    (define-key km "\C-c\C-e\C-s" 'bibtex-string)
+    (define-key km "\C-c\C-e\C-u" 'bibtex-Unpublished)
+
+    (define-key km 'button3 'bibtex-menu)
+    km))
+
+(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-entry-field-alist
+  '(
+    ("Article" . ((("author" "title" "journal" "year")
+		   ("volume" "number" "pages" "month" "note"))
+		  (("author" "title")
+		   ("journal" "year" "volume" "number" "pages"
+		    "month" "note"))))
+    ("Book" . ((("author" "title" "publisher" "year")
+		("editor" "volume" "number" "series" "address"
+		 "edition" "month" "note"))))
+    ("Booklet" . ((("title")
+		   ("author" "howpublished" "address" "month" "year" "note"))))
+    
+    ;; France: Dipl\^{o}me d'Etudes Approfondies (similar to Master's)
+;    ("DEAthesis" . ((("author" "title" "school" "year")
+;		     ("address" "month" "note"))))
+    
+    ("InBook" . ((("author" "title" "chapter" "publisher" "year")
+		  ("editor" "pages" "volume" "number" "series" "address"
+		   "edition" "month" "type" "note"))
+		 (("author" "title" "chapter")
+		  ("publisher" "year" "editor" "pages" "volume" "number"
+		   "series" "address" "edition" "month" "type" "note"))))
+
+
+    ("InCollection" . ((("author" "title"
+			 "booktitle" "publisher" "year")
+			("editor" "volume" "number" "series" "type" "chapter"
+			 "pages" "address" "edition" "month" "note"))
+		       (("author" "title")
+			("booktitle" "publisher" "year"
+			 "editor" "volume" "number" "series" "type" "chapter"
+			 "pages" "address" "edition" "month" "note"))))
+
+
+    ("InProceedings" . ((("author" "title" "booktitle" "year")
+			 ("editor" "volume" "number" "series" "pages"
+			  "organization" "publisher" "address" "month" "note"))
+			(("author" "title")
+			 ("editor" "volume" "number" "series" "pages"
+			  "booktitle" "year"
+			  "organization" "publisher" "address" "month" "note"))))
+
+
+    ("Manual" . ((("title")
+		  ("author" "organization" "address" "edition" "year"
+		   "month" "note"))))
+
+    ("MastersThesis" . ((("author" "title" "school" "year")
+			 ("address" "month" "note" "type"))))
+
+    ("Misc" . ((()
+		("author" "title" "howpublished" "year" "month" "note"))))
+
+    ("PhdThesis" . ((("author" "title" "school" "year")
+		     ("address" "month" "type" "note"))))
+
+    ("Proceedings" . ((("title" "year")
+		       ("editor" "volume" "number" "series" "publisher"
+			"organization" "address" "month" "note"))))
+
+    ("TechReport" . ((("author" "title" "institution" "year")
+		      ("type" "number" "address" "month" "note"))))
+
+    ("Unpublished" . ((("author" "title" "note")
+		       ("year" "month"))))
+    )
+
+  "List of (entry-name (required optional) (crossref-required crossref-optional))
+tripples.  If the third element is nil, then the first pair can be used.  Required
+and optional are lists of strings.  All entry creation functions use this variable
+to generate entries, and bibtex-entry ensures the entry type is valid.  This 
+variable can be used for example to make bibtex manipulate a different set of entry
+types, e.g. a crossreference document of organization types.")
+
+
+;;; 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.")
+
+;;; KAWATA Yasuro <yasuro@qqqq.maekawa.is.uec.ac.jp> reported bug that "/"
+;;; was not premitted in field names.  The old value of this var was:
+;;;    "[A-Za-z][---A-Za-z0-9:_+]*"
+;;; According to the LaTeX manual, page 71, the legal values are letters,
+;;; digits, and punctuation other than comma.  Section 2.1 defines
+;;; punctuation as:
+;;;     .:;,?!`'()[]-/*@
+;;; and says that += can be used in normal text.  Specifically #$%&~_^\{}
+;;; are called out as special chars.  Some experimentation with LaTeX
+;;; indicates that # and ~ definitely don't work, but that the following
+;;; citation does! \cite{a0.:;?!`'()[]-/*@_&$^+=|<>}.  I chose here to
+;;; permit _ since it was previously allowed, but otherwise to only handle
+;;; punc and +=
+;;; Amendment:  I couldn't get a regexp with both "[]"'s and hyphen to
+;;; work.  It looks like you need them both to be the first entries in a
+;;; regexp pattern. [alarson:19930315.0900CST]
+
+(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
+  (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-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
+  (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-alignment 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.")
+
+(defun bibtex-current-entry-label (&optional include-cite kill)
+  "Return the label of the bibtex entry containing, or preceding point.
+Optional argument INCLUDE-CITE, if true means put a '\\cite{}' around the
+returned value.  Second optional argument KILL, if true, means place the
+returned value in the kill buffer.  Interactively; providing prefix
+argument makes INCLUDE-CITE true, and kill is true by default.
+
+Rationale:
+The intention is that someone will write a function that can be bound to
+a mouse key so that people entering TeX can just mouse on the bibtex entry
+and have the citation key inserted at the current point (which will almost
+certainly be in some other buffer).  In the interim this function is 
+marginally useful for keyboard binding and is not bound by default.  
+Suggested binding is ^C-k."
+  (interactive (list current-prefix-arg t))
+  (save-excursion
+    (beginning-of-bibtex-entry)
+    (re-search-forward bibtex-reference-head (save-excursion (end-of-bibtex-entry) (point)))
+    (let* ((key (buffer-substring (match-beginning bibtex-key-in-head)
+				  (match-end bibtex-key-in-head)))
+	   (val (if include-cite
+		    (format "\\cite{%s}" key)
+		    key)))
+      (if kill
+	  (put-string-on-kill-ring val))
+      val)))
+
+;;; 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.
+
+The following may be of interest as well:
+
+  Functions:
+    find-bibtex-duplicates
+    find-bibtex-entry-location
+    hide-bibtex-entry-bodies
+    sort-bibtex-entries
+    validate-bibtex-buffer
+
+  Variables:
+    bibtex-clean-entry-zap-empty-opts
+    bibtex-entry-field-alist
+    bibtex-include-OPTannote
+    bibtex-include-OPTcrossref
+    bibtex-include-OPTkey
+    bibtex-maintain-sorted-entries
+    bibtex-mode-user-optional-fields
+
+Fields:
+    address
+           Publisher's address
+    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
+    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 and labeling 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)
+  (set-syntax-table bibtex-mode-syntax-table)
+  (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]*$")
+  (auto-fill-mode 1)			; nice alignments
+  (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))
+
+(defun ispell-abstract ()
+  (interactive)
+  (beginning-of-bibtex-entry)
+  (re-search-forward "^[ \t]*[OPT]*abstract[ \t]*=")
+  (ispell-region (point)
+		 (save-excursion (forward-sexp) (point))))
+
+(defun beginning-of-bibtex-entry ()
+  (interactive)
+  (re-search-backward "^@" nil 'move))
+
+(defun skip-whitespace-and-comments ()
+  ;; It might be a good idea to have forward-sexp with argument 0 do what
+  ;; this function tries to do, namely skip whitespace and comments.
+  ;; Maybe a better name for this would be skip-to-next-sexp.
+  ;; alternative implementation:
+  ;;   (let ((parse-sexp-ignore-comments t))
+  ;;     (forward-sexp 1)
+  ;;     (forward-sexp -1))
+  ;; but I've had problems with this not getting the parse of comments
+  ;; right going backward if they contain unbalanced expressions or string
+  ;; quotes. [alarson:19920217.1021CST]
+  (let ((md (match-data)))
+    (unwind-protect
+	(while (cond ((looking-at "\\s>+\\|\\s +")
+		      ;; was whitespace
+		      ;; NOTE: also checked end-comment.  In latex and
+		      ;; lisp modes, newline is an end comment, but it
+		      ;; should also be a whitespace char.
+		      (goto-char (match-end 0)))
+		     ;; If looking at beginning of comment, skip to end.
+		     ((looking-at "\\s<")
+		      (re-search-forward "\\s>"))))		      
+      (store-match-data md))))
+
+;;; [alarson:19920214.1007CST]
+(defun end-of-bibtex-entry ()
+  "If inside an entry, move to the end of it, otherwise move to the end
+of the next entry."
+  (interactive)
+  ;; if point was previously at the end of an entry, this puts us
+  ;; inside the next entry, otherwise we remain in the current one.
+  (progn
+    (skip-whitespace-and-comments)
+;;;     (skip-chars-forward " \t\n") 
+    (end-of-line))
+  (beginning-of-bibtex-entry)
+  (let ((parse-sexp-ignore-comments t))
+    (forward-sexp) ; skip entry type
+    (forward-sexp) ; skip entry body
+    ))
+;(defun end-of-bibtex-entry ()
+;  (interactive)
+;  (re-search-forward "}$" nil 'move))
+  
+(defun ispell-bibtex-entry ()
+  (interactive)
+  (ispell-region (progn (beginning-of-bibtex-entry) (point))
+		 (progn (end-of-bibtex-entry) (point))))
+
+(defun narrow-to-bibtex-entry ()
+  (interactive)
+  (save-excursion
+    (narrow-to-region (progn (beginning-of-bibtex-entry) (point))
+		      (progn (end-of-bibtex-entry) (point)))))
+
+
+(defun beginning-of-first-bibtex-entry ()
+  (goto-char (point-min))
+  (cond
+   ((re-search-forward "^@" nil 'move)
+    (beginning-of-line))
+   ((and (bobp) (eobp))
+    nil)
+   (t
+    (message "Warning: No bibtex entries found!"))))
+
+(defun hide-bibtex-entry-bodies (&optional arg)
+  "Hide all lines between first and last bibtex entries not beginning with @.
+With argument, show all text."
+  (interactive "P")
+  (save-excursion
+    (beginning-of-first-bibtex-entry)
+    ;; subst-char-in-region modifies the buffer, despite what the
+    ;; documentation says...
+    (let ((modifiedp (buffer-modified-p))
+	  (buffer-read-only nil))
+      (if arg
+	  (subst-char-in-region (point) (point-max) ?\r ?\n t)
+	(while (save-excursion (re-search-forward "\n[^@]" (point-max) t))
+	  (save-excursion (replace-regexp "\n\\([^@]\\)" "\r\\1"))))
+      (setq selective-display (not arg))
+      (set-buffer-modified-p modifiedp))))
+
+(defvar bibtex-sort-ignore-string-entries nil
+  "*If true, bibtex @STRING entries are ignored when determining ordering
+of the buffer (e.g. sorting, locating alphabetical position for new entries,
+etc.)")
+
+(defun sort-bibtex-entries ()
+  "Sort bibtex entries alphabetically by key.
+Text before the first bibtex entry, and following the last is not affected.
+If bibtex-sort-ignore-string-entries is true, @string entries will be ignored.
+
+Bugs:
+  1. Text between the closing brace ending one bibtex entry, and the @ starting 
+     the next, is considered part of the PRECEDING entry.  Perhaps it should be
+     part of the following entry."
+  (interactive)
+  (save-restriction
+    (beginning-of-first-bibtex-entry)
+    (narrow-to-region (point)
+		      (save-excursion
+			(goto-char (point-max))
+			(beginning-of-bibtex-entry)
+			(end-of-bibtex-entry)
+			(point)))
+    (sort-subr nil			; reversep
+	       ;; beginning of record function
+	       'forward-line
+	       ;; end of record function
+	       (function (lambda () (and (re-search-forward "}\\s-*\n[\n \t]*@" nil 'move)
+					 (forward-char -2))))
+	       ;; start of key function
+	       (if bibtex-sort-ignore-string-entries
+		   (function (lambda ()
+			       (while (and (re-search-forward "^\\s-*\\([@a-zA-Z]*\\)\\s-*{\\s-*")
+					   (string-equalp "@string"
+							  (buffer-substring (match-beginning 1)
+									    (match-end 1)))))
+			       nil))
+		   (function (lambda () (re-search-forward "{\\s-*") nil)))
+	       ;; end of key function
+	       (function (lambda () (search-forward ",")))
+	       )))
+  
+(defun map-bibtex-entries (fun)
+  "Call FUN for each bibtex entry starting with the current, to the end of the file.
+FUN is called with one argument, the key of the entry, and with point inside the entry.
+If bibtex-sort-ignore-string-entries is true, FUN will not be called for @string entries."
+  (beginning-of-bibtex-entry)
+  (while (re-search-forward "^@[^{]*{[ \t]*\\([^, ]*\\)" nil t)
+    (if (and bibtex-sort-ignore-string-entries
+	     (string-equalp "@string{"
+			    (buffer-substring (match-beginning 0)
+					      (match-beginning 1))))
+	nil ; ignore the @string entry.
+      (funcall fun (buffer-substring (match-beginning 1) (match-end 1))))))
+  
+(defun find-bibtex-entry-location (entry-name)
+  "Searches from beginning of current buffer looking for place to put the
+bibtex entry named ENTRY-NAME.  Buffer is assumed to be in sorted order,
+without duplicates (see \\[sort-bibtex-entries]), if it is not, an error will
+be signalled."
+  (interactive "sBibtex entry key: ")
+  (let ((previous nil)
+	point)
+    (beginning-of-first-bibtex-entry)
+    (or (catch 'done
+	  (map-bibtex-entries (function (lambda (current)
+				 (cond
+				  ((string-equal entry-name current)
+				   (error "Entry duplicates existing!"))
+				  ((or (null previous)
+				       (string< previous current))
+				   (setq previous current
+					 point (point))
+				   (if (string< entry-name current)
+				       (progn
+					 (beginning-of-bibtex-entry)
+					 ;; Many schemes append strings to
+					 ;; existing entries to resolve them,
+					 ;; so initial substring matches may
+					 ;; indicate a duplicate entry.  
+					 (let ((idx (string-match (regexp-quote entry-name) current)))
+					   (if (and (integerp idx)
+						    (zerop idx))
+					       (progn
+						 (message "Warning: Entry %s may be a duplicate of %s!"
+							  entry-name current)
+						 (ding t))))
+					 (throw 'done t))))
+				  ((string-equal previous current)
+				   (error "Duplicate here with previous!"))
+				  (t (error "Entries out of order here!")))))))
+	(end-of-bibtex-entry))))
+
+(defun validate-bibtex-buffer ()
+  "Find some typical errors in bibtex files.
+  1. At signs (@) not as first char of a line.
+  2. Double quotes (\") inside strings.
+  3. Closing braces (}) not the last character of a line."
+  (interactive)
+  (let ((point (point)))
+    (while (re-search-forward ".@" nil t)
+      (let* ((foo (parse-partial-sexp (save-excursion (beginning-of-bibtex-entry)
+						      (point))
+				      (point)))
+	     (in-a-string (nth 3 foo)))
+	(if (not in-a-string)
+	    (error "At sign (@) out of place!"))))
+    (goto-char point)
+    (while (search-forward "\"" nil t)
+      (or (looking-at "[,}][ \t]*$")
+	  (char-equal (preceding-char) ?\")
+	  ;; some versions put closing brace on separate line.
+	  (looking-at "[ \t]*\n}")
+	  (save-excursion
+	    (save-restriction
+	      (narrow-to-region (point)
+				(progn (beginning-of-line) (point)))
+	      (looking-at "^[ \t]*[a-zA-Z]+[ \t]*=[ \t]*\"$")))
+	  (error "Quote out of place, or missing \",\" or \"}\"!")))
+    (goto-char point)
+    ;; This is only approximate, should actually search for close braces,
+    ;; then see if they are inside a string, or at the end of a line.
+    ;; This just gets the typical case of whitespace after a closing brace.
+    (while (search-forward "}[ \t]+$" nil t)
+      (error "Brace not last char of line!"))
+    (goto-char point)
+    (message "Bibtex buffer appears o.k.")))
+
+(defun find-bibtex-duplicates ()
+  "Searches forward in current buffer looking for duplicate bibtex entries.
+Buffer is assumed to be sorted, see \\[sort-bibtex-entries]"
+  (interactive)
+  (let ((point (point)))
+    ;; errors if things are not right...
+    (find-bibtex-entry-location (make-string 10 255))
+    (goto-char point)
+    (message "No duplicates found!")))
+
+
+;;; assoc doesn't ignore case, so we need an assoc that does...
+(defun assoc-string-equalp (thing alist)
+  (or (assoc thing alist)
+      (while (and alist
+		  (not (string-equalp thing (car (car alist)))))
+	(setq alist (cdr alist)))
+      (car alist)))
+
+(defvar bibtex-maintain-sorted-entries nil
+  "*If true, bibtex-mode will attempt to maintain all bibtex entries in 
+sorted order.  
+
+Note that this is more a property of a file than a personal preference and
+as such should normally be set via a file local variable entry.")
+
+(defun bibtex-entry (entry-type &optional required optional)
+  (interactive (let* ((completion-ignore-case t)
+		      (e-t (completing-read "Entry Type: " bibtex-entry-field-alist
+					    nil t)))
+		 (list e-t)))
+  (if (and (null required) (null optional))
+      (let* ((e (assoc-string-equalp entry-type bibtex-entry-field-alist))
+	     (r-n-o (elt e 1))
+	     (c-ref (elt e 2)))
+	(if (null e)
+	  (error "Bibtex entry type %s not defined!"))
+	(if (and bibtex-include-OPTcrossref c-ref)
+	    (setq required (elt c-ref 0)
+		  optional (elt c-ref 1))
+	  (setq required (elt r-n-o 0)
+		optional (elt r-n-o 1)))))
+  (let ((key (if bibtex-maintain-sorted-entries
+		 (read-string (format "%s key: " entry-type)))))
+    (if key
+	(find-bibtex-entry-location key))	
+    (bibtex-move-outside-of-entry)
+    (insert "@" entry-type "{")
+    (if key
+	(insert key))
+    (save-excursion
+      (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)
+      (mapcar 'bibtex-make-optional-field 
+	      bibtex-mode-user-optional-fields)
+      (if bibtex-include-OPTannote
+	  (bibtex-make-optional-field "annote"))
+      (insert "\n}\n\n"))
+    (if key
+	(bibtex-next-field t))
+    (run-hooks 'bibtex-add-entry-hook)))
+
+;; (defun bibtex-entry (entry-type required optional)
+;;   (bibtex-move-outside-of-entry)
+;;   (insert (concat "@" entry-type "{,\n\n}\n\n"))
+;;   (previous-line 3)
+;;   (insert (mapconcat 'bibtex-make-entry required ",\n"))
+;;   (if required
+;;       (if optional
+;; 	  (insert ",\n")))
+;;   (insert (mapconcat 'bibtex-make-OPT-entry optional ",\n"))
+;;   (if bibtex-mode-user-optional-fields		;MON...
+;;       (progn
+;; 	(if optional
+;; 	    (insert ",\n"))
+;; 	(insert (mapconcat 'bibtex-make-OPT-entry
+;; 			   bibtex-mode-user-optional-fields
+;; 			   ",\n"))))		;MON
+;;  (up-list -1)
+;;  (forward-char 1))
+
+
+(defun bibtex-make-field (e-t)
+  (interactive "sBibTeX entry type: ")
+  (let ((name  (if (consp e-t) (car e-t) e-t))
+	(value (if (consp e-t) (cdr e-t) "")))
+    (insert ",\n")
+    (indent-to-column bibtex-name-alignment)
+    (insert name " = ")
+    (indent-to-column bibtex-text-alignment)
+    ;; lucid emacs prin1-to-string breaks the undo chain.  When they fix
+    ;; that, the  hack can be removed. [alarson:19930316.0805CST]
+;    (insert (prin1-to-string value))
+    ;; begin hack
+    (insert (format (if (stringp value) "\"%s\"" "%s")
+		    value))
+    ;; end hack
+    nil))
+
+(defun bibtex-make-optional-field (e-t)
+  (interactive "sOptional BibTeX entry type: ")
+  (if (consp e-t)
+      (setq e-t (cons (concat "OPT" (car e-t)) (cdr e-t)))
+    (setq e-t (concat "OPT" e-t)))
+  (bibtex-make-field e-t))
+
+;; 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)
+  (bibtex-entry "Article"))
+
+(defun bibtex-Book ()
+  (interactive)
+  (bibtex-entry "Book"))
+
+(defun bibtex-Booklet ()
+  (interactive)
+  (bibtex-entry "Booklet"))
+
+;(defun bibtex-DEAthesis ()
+;  (interactive)
+;  (bibtex-entry "DEAthesis"))
+
+(defun bibtex-InBook ()
+  (interactive)
+  (bibtex-entry "InBook"))
+
+(defun bibtex-InCollection ()
+  (interactive)
+  (bibtex-entry "InCollection"))
+
+(defun bibtex-InProceedings ()
+  (interactive)
+  (bibtex-entry "InProceedings"))
+
+(defun bibtex-Manual ()
+  (interactive)
+  (bibtex-entry "Manual"))
+
+(defun bibtex-MastersThesis ()
+  (interactive)
+  (bibtex-entry "MastersThesis"))
+
+(defun bibtex-Misc ()
+  (interactive)
+  (bibtex-entry "Misc"))
+
+(defun bibtex-PhdThesis ()
+  (interactive)
+  (bibtex-entry "PhdThesis"))
+
+(defun bibtex-Proceedings ()
+  (interactive)
+  (bibtex-entry "Proceedings"))
+
+(defun bibtex-TechReport ()
+  (interactive)
+  (bibtex-entry "TechReport"))
+
+(defun bibtex-Unpublished ()
+  (interactive)
+  (bibtex-entry "Unpublished"))
+
+(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-next-field ()
+;;   "Finds end of text of next field."
+;;   (interactive)
+;;   (condition-case ()
+;;       (progn
+;; 	(bibtex-inside-field)
+;; 	(re-search-forward ",[ \t\n]*" (point-max) 1)
+;; 	(bibtex-enclosing-field)
+;; 	(bibtex-inside-field))
+;;     (error nil)))
+
+(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-find-text ()
+;;   "Go to end of text of current field."
+;;   (interactive)
+;;   (condition-case ()
+;;       (progn
+;; 	(bibtex-inside-field)
+;; 	(bibtex-enclosing-field)
+;; 	(goto-char (match-end bibtex-text-in-field))
+;; 	(bibtex-inside-field))
+;;     (error nil)))
+
+(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")
+	;; sct@dcs.edinburgh.ac.uk
+ 	(progn
+ 	  (delete-char (length "OPT"))
+ 	  (search-forward "=")
+ 	  (delete-horizontal-space)
+ 	  (indent-to-column bibtex-text-alignment))))
+  (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")		;MON - maybe delete these chars?
+  (cond ((= (preceding-char) ?,)
+	 (forward-char -2))) ; -1 --> -2 sct@dcs.edinburgh.ac.uk
+  (cond ((= (preceding-char) ?\")
+	 (forward-char -1))))		;MON - 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."
+  ;; sct@dcs.edinburgh.ac.uk
+  (let ((old-point (point)))
+    (condition-case errname
+ 	(bibtex-enclosing-regexp bibtex-field)
+      (search-failed
+       (goto-char old-point)
+       (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."
+  ;; sct@dcs.edinburgh.ac.uk
+  (let ((old-point (point)))
+    (condition-case errname
+ 	(bibtex-enclosing-regexp bibtex-reference)
+      (search-failed
+       (goto-char old-point)
+       (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)
+  (beginning-of-bibtex-entry)
+  (let ((start (point)))
+    (save-restriction
+      (narrow-to-region start (save-excursion (end-of-bibtex-entry) (point)))
+      (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"))
+		   (progn
+		     ;; fixup alignment. [alarson:19920309.2047CST]
+		     (search-forward "=")
+		     (delete-horizontal-space)
+		     (indent-to-column bibtex-text-alignment))
+		   (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)
+    (end-of-bibtex-entry)
+    ;; sct@dcs.edinburgh.ac.uk
+    (save-excursion
+      (previous-line 1)
+      (end-of-line)
+      (if (eq (preceding-char) ?,)
+ 	  (backward-delete-char 1)))
+    (skip-whitespace-and-comments)))
+
+
+;;; Menus for bibtex mode
+
+(defconst bibtex-menu
+  '("BibTeX Commands"
+    "Entry Types"
+    "---"
+    ["Article in Conference Proceedings" bibtex-InProceedings		t]
+    ["Article in Journal"		bibtex-Article			t]
+    ["Book"				bibtex-Book			t]
+    ["Booklet"				bibtex-Booklet			t]
+    ["Conference"			bibtex-InProceedings		t]
+    ["Master's Thesis"			bibtex-MastersThesis		t]
+    ["DEA Thesis"			bibtex-DEAthesis		t]
+    ["Phd. Thesis"			bibtex-PhdThesis		t]
+    ["Technical Report"			bibtex-TechReport		t]
+    ["Technical Manual"			bibtex-Manual			t]
+    ["Conference Proceedings"		bibtex-Proceedings		t]
+    ["A Chapter in a Book"		bibtex-InBook			t]
+    ["An Article in a Collection"	bibtex-InCollection		t]
+    ["Miscellaneous"			bibtex-Misc			t]
+    ["Unpublished"			bibtex-Unpublished		t]
+    ["String"				bibtex-string			t]
+    ["Preamble"				bibtex-preamble			t]
+    "---"
+    "Bibtex Edit"
+    "---"
+    ["Next Field"			bibtex-next-field		t]
+    ["To End of Field"			bibtex-find-text		t]
+    ["Snatch From Similar Preceding Field"	bibtex-pop-previous	t]
+    ["Snatch From Similar Following Field"	bibtex-pop-next		t]
+    ["Remove OPT"			bibtex-remove-OPT		t]
+    ["Remove Quotes"			bibtex-remove-double-quotes	t]
+    ["Clean Up Entry"			bibtex-clean-entry		t]
+    ["Find Duplicates"			find-bibtex-duplicates		t]
+    ["Sort Entries"			sort-bibtex-entries		t]
+    ["Validate Entries"			validate-bibtex-buffer		t]
+    ))
+
+(defun bibtex-menu ()
+  (interactive)
+  (let ((popup-menu-titles nil))
+    (popup-menu bibtex-menu)))
+
+;;; bibtex.el ends here