]> code.delx.au - gnu-emacs/blobdiff - lisp/textmodes/bibtex.el
Merge from emacs--rel--22
[gnu-emacs] / lisp / textmodes / bibtex.el
index c5209456f83c7726d35c5b2767726c2a4d7edc07..377c90b7bed90d922cf7631abba9b88e3e61146f 100644 (file)
@@ -1,11 +1,11 @@
 ;;; bibtex.el --- BibTeX mode for GNU Emacs
 
-;; Copyright (C) 1992, 1994, 1995, 1996, 1997, 1998, 1999, 2003, 2004
-;;           Free Software Foundation, Inc.
+;; Copyright (C) 1992, 1994, 1995, 1996, 1997, 1998, 1999, 2001, 2002,
+;;   2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
 
 ;; Author: Stefan Schoef <schoef@offis.uni-oldenburg.de>
 ;;      Bengt Martensson <bengt@mathematik.uni-Bremen.de>
-;;      Mark Shapiro <shapiro@corto.inria.fr>
+;;      Marc Shapiro <marc.shapiro@acm.org>
 ;;      Mike Newton <newton@gumby.cs.caltech.edu>
 ;;      Aaron Larson <alarson@src.honeywell.com>
 ;;      Dirk Herrmann <D.Herrmann@tu-bs.de>
 
 ;; 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, Inc., 59 Temple Place - Suite 330,
-;; Boston, MA 02111-1307, USA.
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
 
 ;;; Commentary:
 
 ;;  Major mode for editing and validating BibTeX files.
 
 ;;  Usage:
-;;  See documentation for function bibtex-mode or type "\M-x describe-mode"
+;;  See documentation for `bibtex-mode' or type "M-x describe-mode"
 ;;  when you are in BibTeX mode.
 
 ;;  Todo:
 ;; User Options:
 
 (defgroup bibtex nil
-  "BibTeX mode"
+  "BibTeX mode."
   :group 'tex
   :prefix "bibtex-")
 
 (defgroup bibtex-autokey nil
-  "Generate automatically a key from the author/editor and the title field"
+  "Generate automatically a key from the author/editor and the title field."
   :group 'bibtex
   :prefix "bibtex-autokey-")
 
@@ -86,8 +86,9 @@ If this is a function, call it to generate the initial field text."
   :group 'bibtex
   :type '(choice (const :tag "None" nil)
                  (string :tag "Initial text")
-                 (function :tag "Initialize Function" :value fun)
-                 (other :tag "Default" t)))
+                 (function :tag "Initialize Function")
+                 (const :tag "Default" t)))
+(put 'bibtex-include-OPTkey 'risky-local-variable t)
 
 (defcustom bibtex-user-optional-fields
   '(("annote" "Personal annotation (ignored)"))
@@ -97,11 +98,9 @@ CROSSREF-OPTIONAL lists in `bibtex-entry-field-alist' (which see)."
   :group 'bibtex
   :type '(repeat (group (string :tag "Field")
                         (string :tag "Comment")
-                        (option (group :inline t
-                                       :extra-offset -4
-                                       (choice :tag "Init" :value ""
-                                               string
-                                               function))))))
+                        (option (choice :tag "Init"
+                                        (const nil) string function)))))
+(put 'bibtex-user-optional-fields 'risky-local-variable t)
 
 (defcustom bibtex-entry-format
   '(opts-or-alts required-fields numerical-fields)
@@ -113,6 +112,7 @@ required-fields     Signal an error if a required field is missing.
 numerical-fields    Delete delimiters around numeral fields.
 page-dashes         Change double dashes in page field to single dash
                       (for scribe compatibility).
+whitespace          Delete whitespace at the beginning and end of fields.
 inherit-booktitle   If entry contains a crossref field and the booktitle
                       field is empty, set the booktitle field to the content
                       of the title field of the crossreferenced entry.
@@ -124,6 +124,10 @@ last-comma          Add or delete comma on end of last field in entry,
 delimiters          Change delimiters according to variables
                       `bibtex-field-delimiters' and `bibtex-entry-delimiters'.
 unify-case          Change case of entry and field names.
+braces              Enclose parts of field entries by braces according to
+                      `bibtex-field-braces-alist'.
+strings             Replace parts of field entries by string constants
+                      according to `bibtex-field-strings-alist'.
 
 The value t means do all of the above formatting actions.
 The value nil means do no formatting at all."
@@ -135,11 +139,35 @@ The value nil means do no formatting at all."
                       (const required-fields)
                       (const numerical-fields)
                       (const page-dashes)
+                      (const whitespace)
                       (const inherit-booktitle)
                       (const realign)
                       (const last-comma)
                       (const delimiters)
-                      (const unify-case))))
+                      (const unify-case)
+                      (const braces)
+                      (const strings))))
+
+(defcustom bibtex-field-braces-alist nil
+ "Alist of field regexps that \\[bibtex-clean-entry] encloses by braces.
+Each element has the form (FIELDS REGEXP), where FIELDS is a list
+of BibTeX field names and REGEXP is a regexp.
+Whitespace in REGEXP will be replaced by \"[ \\t\\n]+\"."
+  :group 'bibtex
+  :type '(repeat (list (repeat (string :tag "field name"))
+                       (choice (regexp :tag "regexp")
+                               (sexp :tag "sexp")))))
+
+(defcustom bibtex-field-strings-alist nil
+ "Alist of regexps that \\[bibtex-clean-entry] replaces by string constants.
+Each element has the form (FIELDS REGEXP TO-STR), where FIELDS is a list
+of BibTeX field names.  In FIELDS search for REGEXP, which are replaced
+by the BibTeX string constant TO-STR.
+Whitespace in REGEXP will be replaced by \"[ \\t\\n]+\"."
+  :group 'bibtex
+  :type '(repeat (list (repeat (string :tag "field name"))
+                       (regexp :tag "From regexp")
+                       (regexp :tag "To string constant"))))
 
 (defcustom bibtex-clean-entry-hook nil
   "List of functions to call when entry has been cleaned.
@@ -151,7 +179,7 @@ narrowed to just the entry."
 (defcustom bibtex-maintain-sorted-entries nil
   "If non-nil, BibTeX mode maintains all entries in sorted order.
 Allowed non-nil values are:
-plain        All entries are sorted alphabetically.
+plain or t   All entries are sorted alphabetically.
 crossref     All entries are sorted alphabetically unless an entry has a
              crossref field.  These crossrefed entries are placed in
              alphabetical order immediately preceding the main entry.
@@ -163,7 +191,10 @@ See also `bibtex-sort-ignore-string-entries'."
   :type '(choice (const nil)
                  (const plain)
                  (const crossref)
-                 (const entry-class)))
+                 (const entry-class)
+                 (const t)))
+(put 'bibtex-maintain-sorted-entries 'safe-local-variable
+     '(lambda (a) (memq a '(nil t plain crossref entry-class))))
 
 (defcustom bibtex-sort-entry-class
   '(("String")
@@ -173,11 +204,22 @@ See also `bibtex-sort-ignore-string-entries'."
 If value of `bibtex-maintain-sorted-entries' is `entry-class'
 entries are ordered according to the classes they belong to.  Each
 class contains a list of entry names.  An entry `catch-all' applies
-to all entries not explicitely mentioned."
+to all entries not explicitly mentioned."
   :group 'BibTeX
   :type '(repeat (choice :tag "Class"
                          (const :tag "catch-all" (catch-all))
                          (repeat :tag "Entry name" string))))
+(put 'bibtex-sort-entry-class 'safe-local-variable
+     (lambda (x) (let ((OK t))
+              (while (consp x)
+                (let ((y (pop x)))
+                  (while (consp y)
+                    (let ((z (pop y)))
+                      (unless (or (stringp z) (eq z 'catch-all))
+                        (setq OK nil))))
+                  (unless (null y) (setq OK nil))))
+              (unless (null x) (setq OK nil))
+              OK)))
 
 (defcustom bibtex-sort-ignore-string-entries t
   "If non-nil, BibTeX @String entries are not sort-significant.
@@ -208,7 +250,7 @@ If parsing fails, try to set this variable to nil."
   :group 'bibtex
   :type 'boolean)
 
-(defvar bibtex-entry-field-alist
+(defcustom bibtex-entry-field-alist
   '(("Article"
      ((("author" "Author1 [and Author2 ...] [and others]")
        ("title" "Title of the article (BibTeX converts it to lowercase)")
@@ -436,7 +478,37 @@ appears in the echo area, INIT is either the initial content of the
 field or a function, which is called to determine the initial content
 of the field, and ALTERNATIVE-FLAG (either nil or t) marks if the
 field is an alternative.  ALTERNATIVE-FLAG may be t only in the
-REQUIRED or CROSSREF-REQUIRED lists.")
+REQUIRED or CROSSREF-REQUIRED lists."
+  :group 'bibtex
+  :type '(repeat (group (string :tag "Entry name")
+                        (group (repeat :tag "Required fields"
+                                       (group (string :tag "Field")
+                                              (string :tag "Comment")
+                                              (option (choice :tag "Init" :value nil
+                                                              (const nil) string function))
+                                              (option (choice :tag "Alternative"
+                                                              (const :tag "No" nil)
+                                                              (const :tag "Yes" t)))))
+                               (repeat :tag "Optional fields"
+                                       (group (string :tag "Field")
+                                              (string :tag "Comment")
+                                              (option (choice :tag "Init" :value nil
+                                                              (const nil) string function)))))
+                        (option :extra-offset -4
+                         (group (repeat :tag "Crossref: required fields"
+                                        (group (string :tag "Field")
+                                               (string :tag "Comment")
+                                               (option (choice :tag "Init" :value nil
+                                                               (const nil) string function))
+                                               (option (choice :tag "Alternative"
+                                                               (const :tag "No" nil)
+                                                               (const :tag "Yes" t)))))
+                                (repeat :tag "Crossref: optional fields"
+                                        (group (string :tag "Field")
+                                               (string :tag "Comment")
+                                               (option (choice :tag "Init" :value nil
+                                                               (const nil) string function)))))))))
+(put 'bibtex-entry-field-alist 'risky-local-variable t)
 
 (defcustom bibtex-comment-start "@Comment"
   "String starting a BibTeX comment."
@@ -513,7 +585,8 @@ directories specified in `bibtex-file-path'.  If an element is a directory,
 check all BibTeX files in this directory.  If an element is the symbol
 `bibtex-file-path', check all BibTeX files in `bibtex-file-path'."
   :group 'bibtex
-  :type '(repeat file))
+  :type '(repeat (choice (const :tag "bibtex-file-path" bibtex-file-path)
+                         directory file)))
 
 (defvar bibtex-file-path (getenv "BIBINPUTS")
   "*Colon separated list of paths to search for `bibtex-files'.")
@@ -551,6 +624,12 @@ See `bibtex-generate-autokey' for details."
   :group 'bibtex-autokey
   :type 'string)
 
+(defcustom bibtex-autokey-expand-strings nil
+  "If non-nil, expand strings when extracting the content of a BibTeX field.
+See `bibtex-generate-autokey' for details."
+  :group 'bibtex-autokey
+  :type 'boolean)
+
 (defvar bibtex-autokey-transcriptions
   '(;; language specific characters
     ("\\\\aa" . "a")                      ; \aa           -> a
@@ -589,7 +668,7 @@ See `bibtex-generate-autokey' for details."
   :type '(repeat (cons (regexp :tag "Old")
                        (string :tag "New"))))
 
-(defcustom bibtex-autokey-name-case-convert 'downcase
+(defcustom bibtex-autokey-name-case-convert-function 'downcase
   "Function called for each name to perform case conversion.
 See `bibtex-generate-autokey' for details."
   :group 'bibtex-autokey
@@ -598,6 +677,10 @@ See `bibtex-generate-autokey' for details."
                  (const :tag "Capitalize" capitalize)
                  (const :tag "Upcase" upcase)
                  (function :tag "Conversion function")))
+(put 'bibtex-autokey-name-case-convert-function 'safe-local-variable
+     (lambda (x) (memq x '(upcase downcase capitalize identity))))
+(defvaralias 'bibtex-autokey-name-case-convert
+  'bibtex-autokey-name-case-convert-function)
 
 (defcustom bibtex-autokey-name-length 'infty
   "Number of characters from name to incorporate into key.
@@ -652,15 +735,15 @@ See `bibtex-generate-autokey' for details."
 
 (defcustom bibtex-autokey-titleword-ignore
   '("A" "An" "On" "The" "Eine?" "Der" "Die" "Das"
-    "[^A-Z].*" ".*[^A-Z0-9].*")
+    "[^[:upper:]].*" ".*[^[:upper:]0-9].*")
   "Determines words from the title that are not to be used in the key.
-Each item of the list is a regexp.  If a word of the title matchs a
+Each item of the list is a regexp.  If a word of the title matches a
 regexp from that list, it is not included in the title part of the key.
 See `bibtex-generate-autokey' for details."
   :group 'bibtex-autokey
   :type '(repeat regexp))
 
-(defcustom bibtex-autokey-titleword-case-convert 'downcase
+(defcustom bibtex-autokey-titleword-case-convert-function 'downcase
   "Function called for each titleword to perform case conversion.
 See `bibtex-generate-autokey' for details."
   :group 'bibtex-autokey
@@ -669,6 +752,8 @@ See `bibtex-generate-autokey' for details."
                  (const :tag "Capitalize" capitalize)
                  (const :tag "Upcase" upcase)
                  (function :tag "Conversion function")))
+(defvaralias 'bibtex-autokey-titleword-case-convert
+  'bibtex-autokey-titleword-case-convert-function)
 
 (defcustom bibtex-autokey-titleword-abbrevs nil
   "Determines exceptions to the usual abbreviation mechanism.
@@ -730,7 +815,7 @@ and must return a string (the key to use)."
 
 (defcustom bibtex-entry-offset 0
   "Offset for BibTeX entries.
-Added to the value of all other variables which determine colums."
+Added to the value of all other variables which determine columns."
   :group 'bibtex
   :type 'integer)
 
@@ -776,61 +861,95 @@ If non-nil, the column for the equal sign is the value of
   :type '(repeat string))
 
 (defcustom bibtex-summary-function 'bibtex-summary
-  "Function to call for generating a one-line summary of a BibTeX entry.
-It takes one argument, the key of the entry.
+  "Function to call for generating a summary of current BibTeX entry.
+It takes no arguments.  Point must be at beginning of entry.
 Used by `bibtex-complete-crossref-cleanup' and `bibtex-copy-summary-as-kill'."
   :group 'bibtex
   :type '(choice (const :tag "Default" bibtex-summary)
                  (function :tag "Personalized function")))
 
 (defcustom bibtex-generate-url-list
-  '((("url" . ".*:.*"))
-    ;; Example of a complex setup.
-    (("journal" . "\\<\\(PR[ABCDEL]?\\|RMP\\)\\>")
-     "http://link.aps.org/abstract/"
-     ("journal" ".*" downcase)
-     "/v"
-     ("volume" ".*" 0)
-     "/p"
-     ("pages" "\\`\\([0-9]+\\)" 1)))
+  '((("url" . ".*:.*")))
   "List of schemes for generating the URL of a BibTeX entry.
 These schemes are used by `bibtex-url'.
 
-Each scheme is of the form ((FIELD . REGEXP) STEP...).
-
-FIELD is a field name as returned by `bibtex-parse-entry'.
-REGEXP is matched against the text of FIELD.  If the match succeeds, then
-this scheme is used.  If no STEPs are specified the matched text is used
-as the URL, otherwise the URL is built by concatenating the STEPs.
+Each scheme should have one of these forms:
 
-A STEP can be a string or a list (FIELD REGEXP REPLACE) in which case
-the text of FIELD is matched against REGEXP, and is replaced with REPLACE.
-REPLACE can be a string, or a number (which selects the corresponding submatch)
-or a function called with the field's text as argument and with the
-`match-data' properly set.
+  ((FIELD . REGEXP))
+  ((FIELD . REGEXP) STEP...)
+  ((FIELD . REGEXP) STRING STEP...)
 
-Case is always ignored.  Always remove the field delimiters."
+FIELD is a field name as returned by `bibtex-parse-entry'.
+REGEXP is matched against the text of FIELD.  If the match succeeds,
+then this scheme is used.  If no STRING and STEPs are specified
+the matched text is used as the URL, otherwise the URL is built
+by evaluating STEPs.  If no STRING is specified the STEPs must result
+in strings which are concatenated.  Otherwise the resulting objects
+are passed through `format' using STRING as format control string.
+
+A STEP is a list (FIELD REGEXP REPLACE).  The text of FIELD
+is matched against REGEXP, and is replaced with REPLACE.
+REPLACE can be a string, or a number (which selects the corresponding
+submatch), or a function called with the field's text as argument
+and with the `match-data' properly set.
+
+Case is always ignored.  Always remove the field delimiters.
+If `bibtex-expand-strings' is non-nil, BibTeX strings are expanded
+for generating the URL.
+
+The following is a complex example, see http://link.aps.org/linkfaq.html.
+
+   (((\"journal\" . \"\\\\=<\\(PR[ABCDEL]?\\|RMP\\)\\\\=>\")
+     \"http://link.aps.org/abstract/%s/v%s/p%s\"
+     (\"journal\" \".*\" downcase)
+     (\"volume\" \".*\" 0)
+     (\"pages\" \"\\`[A-Z]?[0-9]+\" 0)))"
   :group 'bibtex
   :type '(repeat
-          (list :tag "Scheme"
+          (cons :tag "Scheme"
                 (cons :tag "Matcher" :extra-offset 4
                       (string :tag "BibTeX field")
                      (regexp :tag "Regexp"))
-                (repeat :tag "Steps to generate URL" :inline t
-                        (choice
-                         (string :tag "Literal text")
+                (choice
+                 (const :tag "Take match as is" nil)
+                 (cons :tag "Formatted"
+                  (string :tag "Format control string")
+                  (repeat :tag "Steps to generate URL"
+                          (list (string :tag "BibTeX field")
+                                (regexp :tag "Regexp")
+                                (choice (string :tag "Replacement")
+                                        (integer :tag "Sub-match")
+                                        (function :tag "Filter")))))
+                 (repeat :tag "Concatenated"
                          (list (string :tag "BibTeX field")
                               (regexp :tag "Regexp")
                                (choice (string :tag "Replacement")
                                       (integer :tag "Sub-match")
                                       (function :tag "Filter"))))))))
+(put 'bibtex-generate-url-list 'risky-local-variable t)
+
+(defcustom bibtex-cite-matcher-alist
+  '(("\\\\cite[ \t\n]*{\\([^}]+\\)}" . 1))
+  "Alist of rules to identify cited keys in a BibTeX entry.
+Each rule should be of the form (REGEXP . SUBEXP), where SUBEXP
+specifies which parenthesized expression in REGEXP is a cited key.
+Case is significant.
+Used by `bibtex-find-crossref' and for font-locking."
+  :group 'bibtex
+  :type '(repeat (cons (regexp :tag "Regexp")
+                       (integer :tag "Number"))))
 
-;; bibtex-font-lock-keywords is a user option as well, but since the
+(defcustom bibtex-expand-strings nil
+  "If non-nil, expand strings when extracting the content of a BibTeX field."
+  :group 'bibtex
+  :type 'boolean)
+
+;; `bibtex-font-lock-keywords' is a user option, too.  But since the
 ;; patterns used to define this variable are defined in a later
 ;; section of this file, it is defined later.
 
 \f
-;; Syntax Table, Keybindings and BibTeX Entry List
+;; Syntax Table and Keybindings
 (defvar bibtex-mode-syntax-table
   (let ((st (make-syntax-table)))
     (modify-syntax-entry ?\" "\"" st)
@@ -991,6 +1110,17 @@ Case is always ignored.  Always remove the field delimiters."
 \f
 ;; Internal Variables
 
+(defvar bibtex-field-braces-opt nil
+  "Optimized value of `bibtex-field-braces-alist'.
+Created by `bibtex-field-re-init'.
+It is a an alist with elements (FIELD . REGEXP).")
+
+(defvar bibtex-field-strings-opt nil
+  "Optimized value of `bibtex-field-strings-alist'.
+Created by `bibtex-field-re-init'.
+It is a an alist with elements (FIELD RULE1 RULE2 ...),
+where each RULE is (REGEXP . TO-STR).")
+
 (defvar bibtex-pop-previous-search-point nil
   "Next point where `bibtex-pop-previous' starts looking for a similar entry.")
 
@@ -1016,16 +1146,20 @@ At most `bibtex-entry-kill-ring-max' items are kept here.")
 
 (defvar bibtex-strings
   (lazy-completion-table bibtex-strings
-                         bibtex-parse-strings (bibtex-string-files-init))
+                         (lambda ()
+                           (bibtex-parse-strings (bibtex-string-files-init))))
   "Completion table for BibTeX string keys.
 Initialized from `bibtex-predefined-strings' and `bibtex-string-files'.")
 (make-variable-buffer-local 'bibtex-strings)
+(put 'bibtex-strings 'risky-local-variable t)
 
 (defvar bibtex-reference-keys
-  (lazy-completion-table bibtex-reference-keys bibtex-parse-keys nil t)
+  (lazy-completion-table bibtex-reference-keys
+                         (lambda () (bibtex-parse-keys nil t)))
   "Completion table for BibTeX reference keys.
 The CDRs of the elements are t for header keys and nil for crossref keys.")
 (make-variable-buffer-local 'bibtex-reference-keys)
+(put 'bibtex-reference-keys 'risky-local-variable t)
 
 (defvar bibtex-buffer-last-parsed-tick nil
   "Value of `buffer-modified-tick' last time buffer was parsed for keys.")
@@ -1058,66 +1192,62 @@ The CDRs of the elements are t for header keys and nil for crossref keys.")
   "Last reformat reference keys option given.")
 
 (defconst bibtex-field-name "[^\"#%'(),={} \t\n0-9][^\"#%'(),={} \t\n]*"
-  "Regexp matching the name part of a BibTeX field.")
+  "Regexp matching the name of a BibTeX field.")
 
-(defconst bibtex-entry-type (concat "@" bibtex-field-name)
-  "Regexp matching the type part of a BibTeX entry.")
+(defconst bibtex-name-part
+  (concat ",[ \t\n]*\\(" bibtex-field-name "\\)")
+  "Regexp matching the name part of a BibTeX field.")
 
-(defconst bibtex-reference-key "[][a-zA-Z0-9.:;?!`'/*@+|()<>&_^$-]+"
+(defconst bibtex-reference-key "[][[:alnum:].:;?!`'/*@+|()<>&_^$-]+"
   "Regexp matching the reference key part of a BibTeX entry.")
 
-(defconst bibtex-field-const "[][a-zA-Z0-9.:;?!`'/*@+=|<>&_^$-]+"
+(defconst bibtex-field-const "[][[:alnum:].:;?!`'/*@+=|<>&_^$-]+"
   "Regexp matching a BibTeX field constant.")
 
-(defconst bibtex-entry-head
+(defvar bibtex-entry-type
+  (concat "@[ \t]*\\(?:"
+          (regexp-opt (mapcar 'car bibtex-entry-field-alist)) "\\)")
+  "Regexp matching the name of a BibTeX entry.")
+
+(defvar bibtex-entry-head
   (concat "^[ \t]*\\("
           bibtex-entry-type
           "\\)[ \t]*[({][ \t\n]*\\("
           bibtex-reference-key
           "\\)")
-  "Regexp matching the header line of a BibTeX entry.")
+  "Regexp matching the header line of a BibTeX entry (including key).")
 
-(defconst bibtex-entry-maybe-empty-head
+(defvar bibtex-entry-maybe-empty-head
   (concat bibtex-entry-head "?")
   "Regexp matching the header line of a BibTeX entry (possibly without key).")
 
+(defconst bibtex-any-entry-maybe-empty-head
+  (concat "^[ \t]*\\(@[ \t]*" bibtex-field-name "\\)[ \t]*[({][ \t\n]*\\("
+          bibtex-reference-key "\\)?")
+  "Regexp matching the header line of any BibTeX entry (possibly without key).")
+
+(defvar bibtex-any-valid-entry-type
+  (concat "^[ \t]*@[ \t]*\\(?:"
+          (regexp-opt (append '("String" "Preamble")
+                              (mapcar 'car bibtex-entry-field-alist))) "\\)")
+  "Regexp matching any valid BibTeX entry (including String and Preamble).")
+
 (defconst bibtex-type-in-head 1
   "Regexp subexpression number of the type part in `bibtex-entry-head'.")
 
 (defconst bibtex-key-in-head 2
   "Regexp subexpression number of the key part in `bibtex-entry-head'.")
 
+(defconst bibtex-string-type "^[ \t]*\\(@[ \t]*String\\)[ \t]*[({][ \t\n]*"
+   "Regexp matching the name of a BibTeX String entry.")
+
 (defconst bibtex-string-maybe-empty-head
-  (concat "^[ \t]*\\(@String\\)[ \t]*[({]\\("
-          bibtex-reference-key
-          "\\)?")
+  (concat bibtex-string-type "\\(" bibtex-reference-key "\\)?")
   "Regexp matching the header line of a BibTeX String entry.")
 
-(defconst bibtex-entry-postfix "[ \t\n]*,?[ \t\n]*[})]"
-  "Regexp matching the postfix of a BibTeX entry.")
-
-(defvar bibtex-known-entry-type-re
-  (regexp-opt (mapcar 'car bibtex-entry-field-alist))
-  "Regexp matching the name of a BibTeX entry.")
-
-(defvar bibtex-valid-entry-re
-  (concat "@[ \t]*\\(" bibtex-known-entry-type-re "\\)")
-  "Regexp matching the name of a valid BibTeX entry.")
-
-(defvar bibtex-valid-entry-whitespace-re
-  (concat "[ \t\n]*\\(" bibtex-valid-entry-re "\\)")
-  "Regexp matching the name of a valid BibTeX entry preceded by whitespace.")
-
-(defvar bibtex-any-valid-entry-re
-  (concat "@[ \t]*"
-          (regexp-opt (append '("String")
-                              (mapcar 'car bibtex-entry-field-alist))
-                      t))
-  "Regexp matching the name of any valid BibTeX entry (including string).")
-
-
-(defconst bibtex-empty-field-re "\"\"\\|{}"
-  "Regexp matching an empty field.")
+(defconst bibtex-preamble-prefix
+  "[ \t]*\\(@[ \t]*Preamble\\)[ \t]*[({][ \t\n]*"
+  "Regexp matching the prefix part of a BibTeX Preamble entry.")
 
 (defconst bibtex-font-lock-syntactic-keywords
   `((,(concat "^[ \t]*\\(" (substring bibtex-comment-start 0 1) "\\)"
@@ -1126,7 +1256,7 @@ The CDRs of the elements are t for header keys and nil for crossref keys.")
 
 (defvar bibtex-font-lock-keywords
   ;; entry type and reference key
-  `((,bibtex-entry-maybe-empty-head
+  `((,bibtex-any-entry-maybe-empty-head
      (,bibtex-type-in-head font-lock-function-name-face)
      (,bibtex-key-in-head font-lock-constant-face nil t))
     ;; optional field names (treated as comments)
@@ -1136,7 +1266,11 @@ The CDRs of the elements are t for header keys and nil for crossref keys.")
     (,(concat "^[ \t]*\\(" bibtex-field-name "\\)[ \t]*=")
      1 font-lock-variable-name-face)
     ;; url
-    bibtex-font-lock-url bibtex-font-lock-crossref)
+    (bibtex-font-lock-url) (bibtex-font-lock-crossref)
+    ;; cite
+    ,@(mapcar (lambda (matcher)
+                `((lambda (bound) (bibtex-font-lock-cite ',matcher bound))))
+              bibtex-cite-matcher-alist))
   "*Default expressions to highlight in BibTeX mode.")
 
 (defvar bibtex-font-lock-url-regexp
@@ -1144,19 +1278,12 @@ The CDRs of the elements are t for header keys and nil for crossref keys.")
   (concat "^[ \t]*"
           (regexp-opt (delete-dups (mapcar 'caar bibtex-generate-url-list)) t)
           "[ \t]*=[ \t]*")
-  "Regexp for `bibtex-font-lock-url'.")
-
-(defvar bibtex-field-name-for-parsing nil
-  "Regexp of field name to be parsed by function `bibtex-parse-field-name'.
-Passed by dynamic scoping.")
-
-(defvar bibtex-sort-entry-class-alist
-  (let ((i -1) alist)
-    (dolist (class bibtex-sort-entry-class alist)
-      (setq i (1+ i))
-      (dolist (entry class)
-        ;; all entry names should be downcase (for ease of comparison)
-        (push (cons (if (stringp entry) (downcase entry) entry) i) alist))))
+  "Regexp for `bibtex-font-lock-url' derived from `bibtex-generate-url-list'.")
+
+(defvar bibtex-string-empty-key nil
+  "If non-nil, `bibtex-parse-string' accepts empty key.")
+
+(defvar bibtex-sort-entry-class-alist nil
   "Alist mapping entry types to their sorting index.
 Auto-generated from `bibtex-sort-entry-class'.
 Used when `bibtex-maintain-sorted-entries' is `entry-class'.")
@@ -1179,8 +1306,8 @@ ARG is ignored."
   "Parse a string of the format <left-hand-side = right-hand-side>.
 The functions PARSE-LHS and PARSE-RHS are used to parse the corresponding
 substrings.  These functions are expected to return nil if parsing is not
-successfull.  If both functions return non-nil, a pair containing the returned
-values of the functions PARSE-LHS and PARSE-RHS is returned."
+successful.  If the returned values of both functions are non-nil,
+return a cons pair of these values.  Do not move point."
   (save-match-data
     (save-excursion
       (let ((left (funcall parse-lhs))
@@ -1192,23 +1319,24 @@ values of the functions PARSE-LHS and PARSE-RHS is returned."
             (cons left right))))))
 
 (defun bibtex-parse-field-name ()
-  "Parse the field name stored in `bibtex-field-name-for-parsing'.
+  "Parse the name part of a BibTeX field.
 If the field name is found, return a triple consisting of the position of the
 very first character of the match, the actual starting position of the name
 part and end position of the match.  Move point to end of field name.
-If `bibtex-autoadd-commas' is non-nil add missing comma at end of preceeding
+If `bibtex-autoadd-commas' is non-nil add missing comma at end of preceding
 BibTeX field as necessary."
-  (cond ((looking-at ",[ \t\n]*")
-         (let ((start (point)))
-           (goto-char (match-end 0))
-           (when (looking-at bibtex-field-name-for-parsing)
-             (goto-char (match-end 0))
-             (list start (match-beginning 0) (match-end 0)))))
+  (cond ((looking-at bibtex-name-part)
+         (goto-char (match-end 0))
+         (list (match-beginning 0) (match-beginning 1) (match-end 0)))
         ;; Maybe add a missing comma.
         ((and bibtex-autoadd-commas
-              (looking-at (concat "[ \t\n]*\\(?:" bibtex-field-name-for-parsing
+              (looking-at (concat "[ \t\n]*\\(?:" bibtex-field-name
                                   "\\)[ \t\n]*=")))
          (skip-chars-backward " \t\n")
+         ;; It can be confusing if non-editing commands try to
+         ;; modify the buffer.
+         (if buffer-read-only
+             (error "Comma missing at buffer position %s" (point)))
          (insert ",")
          (forward-char -1)
          ;; Now try again.
@@ -1237,7 +1365,8 @@ BibTeX field as necessary."
 (defun bibtex-parse-field-string ()
   "Parse a BibTeX field string enclosed by braces or quotes.
 If a syntactically correct string is found, a pair containing the start and
-end position of the field string is returned, nil otherwise."
+end position of the field string is returned, nil otherwise.
+Do not move point."
   (let ((end-point
          (or (and (eq (following-char) ?\")
                   (save-excursion
@@ -1269,68 +1398,17 @@ returned, nil otherwise.  Move point to end of field text."
       (if (looking-at "[ \t\n]*#[ \t\n]*")
           (goto-char (match-end 0))
         (setq end-point (point))))
+    (skip-chars-forward " \t\n")
     (if (and (not failure)
              end-point)
-        (cons starting-point end-point))))
-
-(defun bibtex-parse-field (name)
-  "Parse a BibTeX field of regexp NAME.
-If a syntactically correct field is found, a pair containing the boundaries of
-the name and text parts of the field is returned."
-  (let ((bibtex-field-name-for-parsing name))
-    (bibtex-parse-association 'bibtex-parse-field-name
-                              'bibtex-parse-field-text)))
+        (list starting-point end-point (point)))))
 
-(defun bibtex-search-forward-field (name &optional bound)
-  "Search forward to find a BibTeX field of name NAME.
-If a syntactically correct field is found, a pair containing the boundaries of
-the name and text parts of the field is returned.  The search is limited by
-optional arg BOUND.  If BOUND is t the search is limited by the end of the
-current entry.  Do not move point."
-  (save-match-data
-    (save-excursion
-      (unless (integer-or-marker-p bound)
-        (setq bound (if bound
-                        (save-excursion (bibtex-end-of-entry))
-                      (point-max))))
-      (let ((case-fold-search t)
-            (bibtex-field-name-for-parsing name)
-            boundaries temp-boundaries)
-        (while (and (not boundaries)
-                    (< (point) bound)
-                    (search-forward "," bound t))
-          (goto-char (match-beginning 0))
-          (if (and (setq temp-boundaries
-                         (bibtex-parse-association 'bibtex-parse-field-name
-                                                   'bibtex-parse-field-text))
-                   (<= (cddr temp-boundaries) bound))
-              (setq boundaries temp-boundaries)
-            (forward-char 1)))
-        boundaries))))
-
-(defun bibtex-search-backward-field (name &optional bound)
-  "Search backward to find a BibTeX field of name NAME.
-If a syntactically correct field is found, a pair containing the boundaries of
-the name and text parts of the field is returned.  The search is limited by
-optional arg BOUND.  If BOUND is t the search is limited by the beginning of the
-current entry.  Do not move point."
-  (save-match-data
-    (save-excursion
-      (unless (integer-or-marker-p bound)
-        (setq bound (if bound
-                        (save-excursion (bibtex-beginning-of-entry))
-                      (point-min))))
-      (let ((case-fold-search t)
-            (bibtex-field-name-for-parsing name)
-            boundaries temp-boundaries)
-        (while (and (not boundaries)
-                    (>= (point) bound)
-                    (search-backward "," bound t))
-          (if (setq temp-boundaries
-                    (bibtex-parse-association 'bibtex-parse-field-name
-                                              'bibtex-parse-field-text))
-              (setq boundaries temp-boundaries)))
-        boundaries))))
+(defun bibtex-parse-field ()
+  "Parse the BibTeX field beginning at the position of point.
+If a syntactically correct field is found, return a cons pair containing
+the boundaries of the name and text parts of the field.  Do not move point."
+  (bibtex-parse-association 'bibtex-parse-field-name
+                            'bibtex-parse-field-text))
 
 (defsubst bibtex-start-of-field (bounds)
   (nth 0 (car bounds)))
@@ -1338,51 +1416,134 @@ current entry.  Do not move point."
   (nth 1 (car bounds)))
 (defsubst bibtex-end-of-name-in-field (bounds)
   (nth 2 (car bounds)))
-(defsubst bibtex-end-of-field (bounds)
-  (cddr bounds))
 (defsubst bibtex-start-of-text-in-field (bounds)
-  (cadr bounds))
+  (nth 1 bounds))
 (defsubst bibtex-end-of-text-in-field (bounds)
-  (cddr bounds))
+  (nth 2 bounds))
+(defsubst bibtex-end-of-field (bounds)
+  (nth 3 bounds))
+
+(defun bibtex-search-forward-field (name &optional bound)
+  "Search forward to find a BibTeX field of name NAME.
+If a syntactically correct field is found, return a pair containing
+the boundaries of the name and text parts of the field.  The search
+is limited by optional arg BOUND.  If BOUND is t the search is limited
+by the end of the current entry.  Do not move point."
+  (save-match-data
+    (save-excursion
+      (if (eq bound t)
+          (let ((regexp (concat bibtex-name-part "[ \t\n]*=\\|"
+                                bibtex-any-entry-maybe-empty-head))
+                (case-fold-search t) bounds)
+            (catch 'done
+              (if (looking-at "[ \t]*@") (goto-char (match-end 0)))
+              (while (and (not bounds)
+                          (re-search-forward regexp nil t))
+                (if (match-beginning 2)
+                    ;; We found a new entry
+                    (throw 'done nil)
+                  ;; We found a field
+                  (goto-char (match-beginning 0))
+                  (setq bounds (bibtex-parse-field))))
+              ;; Step through all fields so that we cannot overshoot.
+              (while bounds
+                (goto-char (bibtex-start-of-name-in-field bounds))
+                (if (looking-at name) (throw 'done bounds))
+                (goto-char (bibtex-end-of-field bounds))
+                (setq bounds (bibtex-parse-field)))))
+        ;; Bounded search or bound is nil (i.e. we cannot overshoot).
+        ;; Indeed, the search is bounded when `bibtex-search-forward-field'
+        ;; is called many times.  So we optimize this part of this function.
+        (let ((name-part (concat ",[ \t\n]*\\(" name "\\)[ \t\n]*=[ \t\n]*"))
+              (case-fold-search t) left right)
+          (while (and (not right)
+                      (re-search-forward name-part bound t))
+            (setq left (list (match-beginning 0) (match-beginning 1)
+                             (match-end 1))
+                  ;; Don't worry that the field text could be past bound.
+                  right (bibtex-parse-field-text)))
+          (if right (cons left right)))))))
+
+(defun bibtex-search-backward-field (name &optional bound)
+  "Search backward to find a BibTeX field of name NAME.
+If a syntactically correct field is found, return a pair containing
+the boundaries of the name and text parts of the field.  The search
+is limited by the optional arg BOUND.  If BOUND is t the search is
+limited by the beginning of the current entry.  Do not move point."
+  (save-match-data
+    (if (eq bound t)
+        (setq bound (save-excursion (bibtex-beginning-of-entry))))
+    (let ((name-part (concat ",[ \t\n]*\\(" name "\\)[ \t\n]*=[ \t\n]*"))
+          (case-fold-search t) left right)
+      (save-excursion
+        ;; the parsing functions are not designed for parsing backwards :-(
+        (when (search-backward "," bound t)
+          (or (save-excursion
+                (when (looking-at name-part)
+                  (setq left (list (match-beginning 0) (match-beginning 1)
+                                   (match-end 1)))
+                  (goto-char (match-end 0))
+                  (setq right (bibtex-parse-field-text))))
+              (while (and (not right)
+                          (re-search-backward name-part bound t))
+                (setq left (list (match-beginning 0) (match-beginning 1)
+                                 (match-end 1)))
+                (save-excursion
+                  (goto-char (match-end 0))
+                  (setq right (bibtex-parse-field-text)))))
+          (if right (cons left right)))))))
 
 (defun bibtex-name-in-field (bounds &optional remove-opt-alt)
   "Get content of name in BibTeX field defined via BOUNDS.
 If optional arg REMOVE-OPT-ALT is non-nil remove \"OPT\" and \"ALT\"."
-  (let ((name (buffer-substring-no-properties (nth 1 (car bounds))
-                                              (nth 2 (car bounds)))))
+  (let ((name (buffer-substring-no-properties
+               (bibtex-start-of-name-in-field bounds)
+               (bibtex-end-of-name-in-field bounds))))
     (if (and remove-opt-alt
              (string-match "\\`\\(OPT\\|ALT\\)" name))
         (substring name 3)
       name)))
 
-(defun bibtex-text-in-field-bounds (bounds &optional remove-delim)
-  "Get content of text in BibTeX field defined via BOUNDS.
-If optional arg REMOVE-DELIM is non-nil remove enclosing field delimiters
-if present."
-  (let ((content (buffer-substring-no-properties (cadr bounds)
-                                                 (cddr bounds))))
-    (if remove-delim
-        (bibtex-remove-delimiters-string content)
-      content)))
+(defun bibtex-text-in-field-bounds (bounds &optional content)
+  "Get text in BibTeX field defined via BOUNDS.
+If optional arg CONTENT is non-nil extract content of field
+by removing field delimiters and concatenating the resulting string.
+If `bibtex-expand-strings' is non-nil, also expand BibTeX strings."
+  (if content
+      (save-excursion
+        (goto-char (bibtex-start-of-text-in-field bounds))
+        (let ((epoint (bibtex-end-of-text-in-field bounds))
+              content opoint)
+          (while (< (setq opoint (point)) epoint)
+            (if (looking-at bibtex-field-const)
+                (let ((mtch (match-string-no-properties 0)))
+                  (push (or (if bibtex-expand-strings
+                                (cdr (assoc-string mtch (bibtex-strings) t)))
+                            mtch) content)
+                  (goto-char (match-end 0)))
+              (let ((bounds (bibtex-parse-field-string)))
+                (push (buffer-substring-no-properties
+                       (1+ (car bounds)) (1- (cdr bounds))) content)
+                (goto-char (cdr bounds))))
+            (re-search-forward "\\=[ \t\n]*#[ \t\n]*" nil t))
+          (apply 'concat (nreverse content))))
+    (buffer-substring-no-properties (bibtex-start-of-text-in-field bounds)
+                                    (bibtex-end-of-text-in-field bounds))))
 
 (defun bibtex-text-in-field (field &optional follow-crossref)
   "Get content of field FIELD of current BibTeX entry.
 Return nil if not found.
 If optional arg FOLLOW-CROSSREF is non-nil, follow crossref."
   (save-excursion
-    (save-restriction
-      ;; We want to jump back and forth while searching FIELD
-      (bibtex-narrow-to-entry)
-      (goto-char (point-min))
-      (let ((bounds (bibtex-search-forward-field field))
-            crossref-field)
-        (cond (bounds (bibtex-text-in-field-bounds bounds t))
-              ((and follow-crossref
-                    (progn (goto-char (point-min))
-                           (setq bounds (bibtex-search-forward-field
-                                         "\\(OPT\\)?crossref"))))
-               (setq crossref-field (bibtex-text-in-field-bounds bounds t))
-               (widen)
+    (let* ((end (if follow-crossref (bibtex-end-of-entry) t))
+           (beg (bibtex-beginning-of-entry)) ; move point
+           (bounds (bibtex-search-forward-field field end)))
+      (cond (bounds (bibtex-text-in-field-bounds bounds t))
+            ((and follow-crossref
+                  (progn (goto-char beg)
+                         (setq bounds (bibtex-search-forward-field
+                                       "\\(OPT\\)?crossref" end))))
+             (let ((crossref-field (bibtex-text-in-field-bounds bounds t)))
                (if (bibtex-find-crossref crossref-field)
                    ;; Do not pass FOLLOW-CROSSREF because we want
                    ;; to follow crossrefs only one level of recursion.
@@ -1392,16 +1553,21 @@ If optional arg FOLLOW-CROSSREF is non-nil, follow crossref."
   "Parse the prefix part of a BibTeX string entry, including reference key.
 If the string prefix is found, return a triple consisting of the position of
 the very first character of the match, the actual starting position of the
-reference key and the end position of the match."
+reference key and the end position of the match.
+If `bibtex-string-empty-key' is non-nil accept empty string key."
   (let ((case-fold-search t))
-    (if (looking-at "^[ \t]*@string[ \t\n]*[({][ \t\n]*")
+    (if (looking-at bibtex-string-type)
         (let ((start (point)))
           (goto-char (match-end 0))
-          (when (looking-at bibtex-reference-key)
-            (goto-char (match-end 0))
-            (list start
-                  (match-beginning 0)
-                  (match-end 0)))))))
+          (cond ((looking-at bibtex-reference-key)
+                 (goto-char (match-end 0))
+                 (list start
+                       (match-beginning 0)
+                       (match-end 0)))
+                ((and bibtex-string-empty-key
+                      (looking-at "="))
+                 (skip-chars-backward " \t\n")
+                 (list start (point) (point))))))))
 
 (defun bibtex-parse-string-postfix ()
   "Parse the postfix part of a BibTeX string entry, including the text.
@@ -1411,65 +1577,47 @@ character of the string entry.  Move point past BibTeX string entry."
   (let* ((case-fold-search t)
          (bounds (bibtex-parse-field-text)))
     (when bounds
-      (goto-char (cdr bounds))
+      (goto-char (nth 1 bounds))
       (when (looking-at "[ \t\n]*[})]")
         (goto-char (match-end 0))
         (list (car bounds)
-              (cdr bounds)
+              (nth 1 bounds)
               (match-end 0))))))
 
-(defun bibtex-parse-string ()
-  "Parse a BibTeX string entry.
-If a syntactically correct entry is found, a pair containing the boundaries of
-the reference key and text parts of the entry is returned.
-Move point past BibTeX string entry."
-  (bibtex-parse-association 'bibtex-parse-string-prefix
-                            'bibtex-parse-string-postfix))
+(defun bibtex-parse-string (&optional empty-key)
+  "Parse a BibTeX string entry beginning at the position of point.
+If a syntactically correct entry is found, return a cons pair containing
+the boundaries of the reference key and text parts of the entry.
+If EMPTY-KEY is non-nil, key may be empty.  Do not move point."
+  (let ((bibtex-string-empty-key empty-key))
+    (bibtex-parse-association 'bibtex-parse-string-prefix
+                              'bibtex-parse-string-postfix)))
 
-(defun bibtex-search-forward-string ()
+(defun bibtex-search-forward-string (&optional empty-key)
   "Search forward to find a BibTeX string entry.
 If a syntactically correct entry is found, a pair containing the boundaries of
-the reference key and text parts of the string is returned.  Do not move point."
+the reference key and text parts of the string is returned.
+If EMPTY-KEY is non-nil, key may be empty.  Do not move point."
   (save-excursion
     (save-match-data
-      (let ((case-fold-search t)
-            boundaries)
-        (while (and (not boundaries)
-                    (search-forward-regexp
-                     "^[ \t]*@string[ \t\n]*[({][ \t\n]*" nil t))
-          (goto-char (match-beginning 0))
-          (unless (setq boundaries (bibtex-parse-string))
-            (forward-char 1)))
-        boundaries))))
-
-(defun bibtex-search-backward-string ()
-  "Search backward to find a BibTeX string entry.
-If a syntactically correct entry is found, a pair containing the boundaries of
-the reference key and text parts of the field is returned.  Do not move point."
-  (save-excursion
-    (save-match-data
-      (let ((case-fold-search t)
-            boundaries)
-        (while (and (not boundaries)
-                    (search-backward-regexp
-                     "^[ \t]*@string[ \t\n]*[({][ \t\n]*" nil t))
-          (goto-char (match-beginning 0))
-          (setq boundaries (bibtex-parse-string)))
-        boundaries))))
+      (let ((case-fold-search t) bounds)
+        (while (and (not bounds)
+                    (search-forward-regexp bibtex-string-type nil t))
+          (save-excursion (goto-char (match-beginning 0))
+                          (setq bounds (bibtex-parse-string empty-key))))
+        bounds))))
 
 (defun bibtex-reference-key-in-string (bounds)
+  "Return the key part of a BibTeX string defined via BOUNDS."
   (buffer-substring-no-properties (nth 1 (car bounds))
                                   (nth 2 (car bounds))))
 
-(defun bibtex-text-in-string (bounds &optional remove-delim)
-  "Get content of text in BibTeX string field defined via BOUNDS.
-If optional arg REMOVE-DELIM is non-nil remove enclosing field
-delimiters if present."
-  (let ((content (buffer-substring-no-properties (nth 0 (cdr bounds))
-                                                 (nth 1 (cdr bounds)))))
-    (if remove-delim
-        (bibtex-remove-delimiters-string content)
-      content)))
+(defun bibtex-text-in-string (bounds &optional content)
+  "Get text in BibTeX string field defined via BOUNDS.
+If optional arg CONTENT is non-nil extract content
+by removing field delimiters and concatenating the resulting string.
+If `bibtex-expand-strings' is non-nil, also expand BibTeX strings."
+  (bibtex-text-in-field-bounds bounds content))
 
 (defsubst bibtex-start-of-text-in-string (bounds)
   (nth 0 (cdr bounds)))
@@ -1489,14 +1637,17 @@ delimiters if present."
   (or (match-string-no-properties bibtex-key-in-head)
       empty))
 
-;; Helper Functions
+(defun bibtex-parse-preamble ()
+  "Parse BibTeX preamble.
+Point must be at beginning of preamble.  Do not move point."
+  (let ((case-fold-search t))
+    (when (looking-at bibtex-preamble-prefix)
+      (let ((start (match-beginning 0)) (pref-start (match-beginning 1))
+            (bounds (save-excursion (goto-char (match-end 0))
+                                    (bibtex-parse-string-postfix))))
+        (if bounds (cons (list start pref-start) bounds))))))
 
-(defun bibtex-remove-delimiters-string (str)
-  "Remove delimiters of string STR."
-  (if (and (memq (aref str 0) '(?\{ ?\"))
-           (memq (aref str (1- (length str))) '(?\} ?\")))
-      (substring str 1 -1)
-    str))
+;; Helper Functions
 
 (defsubst bibtex-string= (str1 str2)
   "Return t if STR1 and STR2 are equal, ignoring case."
@@ -1512,6 +1663,35 @@ delimiters if present."
   (+ (count-lines 1 (point))
      (if (bolp) 1 0)))
 
+(defun bibtex-valid-entry (&optional empty-key)
+  "Parse a valid BibTeX entry (maybe without key if EMPTY-KEY is t).
+A valid entry is a syntactical correct one with type contained in
+`bibtex-entry-field-alist'.  Ignore @String and @Preamble entries.
+Return a cons pair with buffer positions of beginning and end of entry
+if a valid entry is found, nil otherwise.  Do not move point.
+After a call to this function `match-data' corresponds to the header
+of the entry, see regexp `bibtex-entry-head'."
+  (let ((case-fold-search t) end)
+    (if (looking-at (if empty-key bibtex-entry-maybe-empty-head
+                    bibtex-entry-head))
+        (save-excursion
+          (save-match-data
+            (goto-char (match-end 0))
+            (let ((entry-closer
+                   (if (save-excursion
+                         (goto-char (match-end bibtex-type-in-head))
+                         (looking-at "[ \t]*("))
+                       ",?[ \t\n]*)" ; entry opened with `('
+                     ",?[ \t\n]*}")) ; entry opened with `{'
+                  bounds)
+              (skip-chars-forward " \t\n")
+              ;; loop over all BibTeX fields
+              (while (setq bounds (bibtex-parse-field))
+                (goto-char (bibtex-end-of-field bounds)))
+              ;; This matches the infix* part.
+              (if (looking-at entry-closer) (setq end (match-end 0)))))
+          (if end (cons (match-beginning 0) end))))))
+
 (defun bibtex-skip-to-valid-entry (&optional backward)
   "Move point to beginning of the next valid BibTeX entry.
 Do not move if we are already at beginning of a valid BibTeX entry.
@@ -1519,49 +1699,39 @@ With optional argument BACKWARD non-nil, move backward to
 beginning of previous valid one.  A valid entry is a syntactical correct one
 with type contained in `bibtex-entry-field-alist' or, if
 `bibtex-sort-ignore-string-entries' is nil, a syntactical correct string
-entry.  Return buffer position of beginning and ending of entry if a valid
+entry.  Return buffer position of beginning and end of entry if a valid
 entry is found, nil otherwise."
   (interactive "P")
   (let ((case-fold-search t)
-        found)
+        found bounds)
+    (beginning-of-line)
+    ;; Loop till we look at a valid entry.
     (while (not (or found (if backward (bobp) (eobp))))
-      (let ((pnt (point))
-            bounds)
-        (cond ((or (and (looking-at bibtex-valid-entry-re)
-                        (setq found (bibtex-search-entry nil nil t))
-                        (equal (match-beginning 0) pnt))
-                   (and (not bibtex-sort-ignore-string-entries)
-                        (setq bounds (bibtex-parse-string))
-                        (setq found (cons (bibtex-start-of-field bounds)
-                                          (bibtex-end-of-string bounds)))))
-               (goto-char pnt))
-              (backward
-               (if (re-search-backward "^[ \t]*\\(@\\)" nil 'move)
-                   (goto-char (match-beginning 1))))
-              (t (if (re-search-forward "\n[ \t]*@" nil 'move)
-                     (forward-char -1))))))
+      (cond ((setq found (or (bibtex-valid-entry)
+                             (and (not bibtex-sort-ignore-string-entries)
+                                  (setq bounds (bibtex-parse-string))
+                                  (cons (bibtex-start-of-field bounds)
+                                        (bibtex-end-of-string bounds))))))
+            (backward (re-search-backward "^[ \t]*@" nil 'move))
+            (t (if (re-search-forward "\n\\([ \t]*@\\)" nil 'move)
+                   (goto-char (match-beginning 1))))))
     found))
 
 (defun bibtex-map-entries (fun)
   "Call FUN for each BibTeX entry in buffer (possibly narrowed).
 FUN is called with three arguments, the key of the entry and the buffer
-positions (marker) of beginning and end of entry.  Point is inside the entry.
-If `bibtex-sort-ignore-string-entries' is non-nil, FUN is not called for
-@String entries."
-  (let ((case-fold-search t))
+positions of beginning and end of entry.  Also, point is at beginning of
+entry and `match-data' corresponds to the header of the entry,
+see regexp `bibtex-entry-head'.  If `bibtex-sort-ignore-string-entries'
+is non-nil, FUN is not called for @String entries."
+  (let ((case-fold-search t)
+        found)
     (save-excursion
       (goto-char (point-min))
-      (while (re-search-forward bibtex-entry-head nil t)
-        (let ((entry-type (bibtex-type-in-head))
-              (key (bibtex-key-in-head ""))
-              (beg (copy-marker (match-beginning 0)))
-              (end (copy-marker (save-excursion (bibtex-end-of-entry)))))
-          (save-excursion
-            (if (or (and (not bibtex-sort-ignore-string-entries)
-                         (bibtex-string= entry-type "string"))
-                    (assoc-string entry-type bibtex-entry-field-alist t))
-                (funcall fun key beg end)))
-          (goto-char end))))))
+      (while (setq found (bibtex-skip-to-valid-entry))
+        (looking-at bibtex-any-entry-maybe-empty-head)
+        (funcall fun (bibtex-key-in-head "") (car found) (cdr found))
+        (goto-char (cdr found))))))
 
 (defun bibtex-progress-message (&optional flag interval)
   "Echo a message about progress of current buffer.
@@ -1611,89 +1781,19 @@ If FLAG is nil, a message is echoed if point was incremented at least
       "}"
     ")"))
 
-(defun bibtex-search-entry (empty-head &optional bound noerror backward)
-  "Search for a BibTeX entry (maybe without reference key if EMPTY-HEAD is t).
-BOUND and NOERROR are exactly as in `re-search-forward'.  If BACKWARD
-is non-nil, search in reverse direction.  Move point past the closing
-delimiter (at the beginning of entry if BACKWARD is non-nil).
-Return a cons pair with buffer positions of beginning and end of entry.
-After call to this function MATCH-BEGINNING and MATCH-END functions
-are defined, but only for the head part of the entry
-\(especially (match-end 0) just gives the end of the head part)."
-  (let ((pnt (point))
-        (entry-head-re (if empty-head
-                           bibtex-entry-maybe-empty-head
-                         bibtex-entry-head)))
-    (if backward
-        (let (found)
-          (while (and (not found)
-                      (re-search-backward entry-head-re bound noerror))
-            (setq found (bibtex-search-entry empty-head pnt t)))
-          (if found
-              (progn (goto-char (match-beginning 0))
-                     found)
-            (cond ((not noerror)
-                   ;; yell
-                   (error "Backward search of BibTeX entry failed"))
-                  ((eq noerror t)
-                   ;; don't move
-                   (goto-char pnt)))
-            nil))
-      (let (found)
-        (unless bound (setq bound (point-max)))
-        (while (and (not found)
-                    (re-search-forward entry-head-re bound noerror))
-          (save-match-data
-            (let ((entry-closer
-                   (if (save-excursion
-                         (goto-char (match-end bibtex-type-in-head))
-                         (looking-at "[ \t]*("))
-                       ;; entry opened with parenthesis
-                       ?\)
-                     ?\}))
-                  (infix-start (point))
-                  finished bounds)
-              (while (not finished)
-                (skip-chars-forward " \t\n" bound)
-                (if (and (setq bounds (bibtex-parse-field bibtex-field-name))
-                         (<= (bibtex-end-of-field bounds) bound))
-                    (setq infix-start (bibtex-end-of-field bounds))
-                  (setq finished t))
-                (goto-char infix-start))
-              ;; This matches the infix* part.  The AND construction assures
-              ;; that BOUND is respected.
-              (when (and (looking-at bibtex-entry-postfix)
-                         (eq (char-before (match-end 0)) entry-closer)
-                         (<= (match-end 0) bound))
-                (goto-char (match-end 0))
-                (setq found t)))))
-        (if found
-            (cons (match-beginning 0) (point))
-          (cond ((not noerror)
-                 ;; yell
-                 (error "Search of BibTeX entry failed"))
-                ((eq noerror t)
-                 ;; don't move
-                 (goto-char pnt)))
-          nil)))))
-
-(defun bibtex-flash-head ()
+(defun bibtex-flash-head (prompt)
   "Flash at BibTeX entry head before point, if exists."
   (let ((case-fold-search t)
-        flash)
-    (cond ((re-search-backward bibtex-entry-head nil t)
-           (goto-char (match-beginning bibtex-type-in-head))
-           (setq flash (match-end bibtex-key-in-head)))
-          (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)))))
+        (pnt (point)))
+    (save-excursion
+      (bibtex-beginning-of-entry)
+      (when (and (looking-at bibtex-any-entry-maybe-empty-head)
+                 (< (point) pnt))
+        (goto-char (match-beginning bibtex-type-in-head))
+        (if (pos-visible-in-window-p (point))
+            (sit-for blink-matching-delay)
+          (message "%s%s" prompt (buffer-substring-no-properties
+                                  (point) (match-end bibtex-key-in-head))))))))
 
 (defun bibtex-make-optional-field (field)
   "Make an optional field named FIELD in current BibTeX entry."
@@ -1717,79 +1817,60 @@ are defined, but only for the head part of the entry
     (skip-chars-forward " \t\n")))
 
 (defun bibtex-beginning-of-first-entry ()
-  "Go to the beginning of the first BibTeX entry in buffer.  Return point."
+  "Go to beginning of line of first BibTeX entry in buffer.
+If `bibtex-sort-ignore-string-entries' is non-nil, @String entries
+are ignored.  Return point"
   (goto-char (point-min))
-  (if (re-search-forward "^[ \t]*@" nil 'move)
-      (beginning-of-line))
-  (point))
-
-(defun bibtex-beginning-of-last-entry ()
-  "Go to the beginning of the last BibTeX entry in buffer."
-  (goto-char (point-max))
-  (if (re-search-backward "^[ \t]*@" nil 'move)
-      (beginning-of-line))
+  (bibtex-skip-to-valid-entry)
   (point))
 
-(defun bibtex-inside-field ()
-  "Try to avoid point being at end of a BibTeX field."
-  (end-of-line)
-  (skip-chars-backward " \t")
-  (if (= (preceding-char) ?,)
-      (forward-char -2))
-  (if (or (= (preceding-char) ?})
-          (= (preceding-char) ?\"))
-      (forward-char -1)))
-
-(defun bibtex-enclosing-field (&optional noerr)
+(defun bibtex-enclosing-field (&optional comma noerr)
   "Search for BibTeX field enclosing point.
+For `bibtex-mode''s internal algorithms, a field begins at the comma
+following the preceding field.  Usually, this is not what the user expects.
+Thus if COMMA is non-nil, the \"current field\" includes the terminating comma.
 Unless NOERR is non-nil, signal an error if no enclosing field is found.
 On success return bounds, nil otherwise.  Do not move point."
-  (let ((bounds (bibtex-search-backward-field bibtex-field-name t)))
-    (if (and bounds
-             (<= (bibtex-start-of-field bounds) (point))
-             (>= (bibtex-end-of-field bounds) (point)))
-        bounds
-      (unless noerr
-        (error "Can't find enclosing BibTeX field")))))
-
-(defun bibtex-enclosing-entry-maybe-empty-head ()
-  "Search for BibTeX entry enclosing point.  Move point to end of entry.
-Beginning (but not end) of entry is given by (`match-beginning' 0)."
-  (let ((case-fold-search t)
-        (old-point (point)))
-    (unless (re-search-backward bibtex-entry-maybe-empty-head nil t)
-      (goto-char old-point)
-      (error "Can't find beginning of enclosing BibTeX entry"))
-    (goto-char (match-beginning bibtex-type-in-head))
-    (unless (bibtex-search-entry t nil t)
-      (goto-char old-point)
-      (error "Can't find end of enclosing BibTeX entry"))))
-
-(defun bibtex-insert-kill (n)
-  "Reinsert the Nth stretch of killed BibTeX text."
-  (if (not bibtex-last-kill-command)
-      (error "BibTeX kill ring is empty")
-    (let* ((kr (if (eq bibtex-last-kill-command 'field)
-                   'bibtex-field-kill-ring
-                 'bibtex-entry-kill-ring))
-           (kryp (if (eq bibtex-last-kill-command 'field)
-                     'bibtex-field-kill-ring-yank-pointer
-                   'bibtex-entry-kill-ring-yank-pointer))
-           (current (car (set kryp (nthcdr (mod (- n (length (eval kryp)))
-                                                (length (eval kr)))
-                                           (eval kr))))))
-      (if (eq bibtex-last-kill-command 'field)
-          (let (bibtex-help-message)
-            (bibtex-find-text)
-            (if (looking-at "[}\"]")
-                (forward-char))
-            (set-mark (point))
-            (message "Mark set")
-            (bibtex-make-field current t))
-        (unless (eobp) (bibtex-beginning-of-entry))
-        (set-mark (point))
-        (message "Mark set")
-        (insert current)))))
+  (save-excursion
+    (when comma
+      (end-of-line)
+      (skip-chars-backward " \t")
+      (if (= (preceding-char) ?,) (forward-char -1)))
+
+    (let ((bounds (bibtex-search-backward-field bibtex-field-name t)))
+      (cond ((and bounds
+                  (<= (bibtex-start-of-field bounds) (point))
+                  (>= (bibtex-end-of-field bounds) (point)))
+             bounds)
+            ((not noerr)
+             (error "Can't find enclosing BibTeX field"))))))
+
+(defun bibtex-beginning-first-field (&optional beg)
+  "Move point to beginning of first field.
+Optional arg BEG is beginning of entry."
+  (if beg (goto-char beg) (bibtex-beginning-of-entry))
+  (looking-at bibtex-any-entry-maybe-empty-head)
+  (goto-char (match-end 0)))
+
+(defun bibtex-insert-kill (n &optional comma)
+  "Reinsert the Nth stretch of killed BibTeX text (field or entry).
+Optional arg COMMA is as in `bibtex-enclosing-field'."
+  (unless bibtex-last-kill-command (error "BibTeX kill ring is empty"))
+  (let ((fun (lambda (kryp kr) ; adapted from `current-kill'
+               (car (set kryp (nthcdr (mod (- n (length (eval kryp)))
+                                           (length kr)) kr))))))
+    (if (eq bibtex-last-kill-command 'field)
+        (progn
+          ;; insert past the current field
+          (goto-char (bibtex-end-of-field (bibtex-enclosing-field comma)))
+          (push-mark)
+          (bibtex-make-field (funcall fun 'bibtex-field-kill-ring-yank-pointer
+                                      bibtex-field-kill-ring) t nil t))
+      ;; insert past the current entry
+      (bibtex-skip-to-valid-entry)
+      (push-mark)
+      (insert (funcall fun 'bibtex-entry-kill-ring-yank-pointer
+                       bibtex-entry-kill-ring)))))
 
 (defun bibtex-format-entry ()
   "Helper function for `bibtex-clean-entry'.
@@ -1807,9 +1888,19 @@ Formats current entry according to variable `bibtex-entry-format'."
             crossref-key bounds alternatives-there non-empty-alternative
             entry-list req-field-list field-list)
 
+        ;; Initialize `bibtex-field-braces-opt' and `bibtex-field-strings-opt'
+        ;; if necessary.
+        (unless bibtex-field-braces-opt
+          (setq bibtex-field-braces-opt
+                (bibtex-field-re-init bibtex-field-braces-alist 'braces)))
+        (unless bibtex-field-strings-opt
+          (setq bibtex-field-strings-opt
+                (bibtex-field-re-init bibtex-field-strings-alist 'strings)))
+
         ;; identify entry type
         (goto-char (point-min))
-        (re-search-forward bibtex-entry-type)
+        (or (re-search-forward bibtex-entry-type nil t)
+            (error "Not inside a BibTeX entry"))
         (let ((beg-type (1+ (match-beginning 0)))
               (end-type (match-end 0)))
           (setq entry-list (assoc-string (buffer-substring-no-properties
@@ -1832,12 +1923,11 @@ Formats current entry according to variable `bibtex-entry-format'."
         ;; determine if entry has crossref field and if at least
         ;; one alternative is non-empty
         (goto-char (point-min))
-        (let* ((fields-alist (bibtex-parse-entry))
+        (let* ((fields-alist (bibtex-parse-entry t))
                (field (assoc-string "crossref" fields-alist t)))
           (setq crossref-key (and field
-                                  (not (string-match bibtex-empty-field-re
-                                                     (cdr field)))
-                                  (bibtex-remove-delimiters-string (cdr field)))
+                                  (not (equal "" (cdr field)))
+                                  (cdr field))
                 req-field-list (if crossref-key
                                    (nth 0 (nth 2 entry-list)) ; crossref part
                                  (nth 0 (nth 1 entry-list)))) ; required part
@@ -1847,8 +1937,7 @@ Formats current entry according to variable `bibtex-entry-format'."
               (setq alternatives-there t
                     field (assoc-string (car rfield) fields-alist t))
               (if (and field
-                       (not (string-match bibtex-empty-field-re
-                                          (cdr field))))
+                       (not (equal "" (cdr field))))
                   (cond ((not non-empty-alternative)
                          (setq non-empty-alternative t))
                         ((memq 'required-fields format)
@@ -1860,8 +1949,8 @@ Formats current entry according to variable `bibtex-entry-format'."
             (error "All alternatives are empty"))
 
         ;; process all fields
-        (goto-char (point-min))
-        (while (setq bounds (bibtex-search-forward-field bibtex-field-name))
+        (bibtex-beginning-first-field (point-min))
+        (while (setq bounds (bibtex-parse-field))
           (let* ((beg-field (copy-marker (bibtex-start-of-field bounds)))
                  (end-field (copy-marker (bibtex-end-of-field bounds) t))
                  (beg-name  (copy-marker (bibtex-start-of-name-in-field bounds)))
@@ -1873,13 +1962,11 @@ Formats current entry according to variable `bibtex-entry-format'."
                                            beg-name (+ beg-name 3))))
                  (field-name (buffer-substring-no-properties
                               (if opt-alt (+ beg-name 3) beg-name) end-name))
-                 (empty-field (string-match bibtex-empty-field-re
-                                            (buffer-substring-no-properties
-                                             beg-field end-field)))
+                 (empty-field (equal "" (bibtex-text-in-field-bounds bounds t)))
                  deleted)
 
             ;; We have more elegant high-level functions for several
-            ;; tasks done by bibtex-format-entry.  However, they contain
+            ;; tasks done by `bibtex-format-entry'.  However, they contain
             ;; quite some redundancy compared with what we need to do
             ;; anyway.  So for speed-up we avoid using them.
 
@@ -1932,6 +2019,59 @@ Formats current entry according to variable `bibtex-entry-format'."
                                "\\([\"{][0-9]+\\)[ \t\n]*--?[ \t\n]*\\([0-9]+[\"}]\\)")))
                   (replace-match "\\1-\\2"))
 
+              ;; remove whitespace at beginning and end of field
+              (when (memq 'whitespace format)
+                (goto-char beg-text)
+                (if (looking-at "\\([{\"]\\)[ \t\n]+")
+                    (replace-match "\\1"))
+                (goto-char end-text)
+                (if (looking-back "[ \t\n]+\\([}\"]\\)" beg-text t)
+                    (replace-match "\\1")))
+
+              ;; enclose field text by braces according to
+              ;; `bibtex-field-braces-alist'.
+              (let (case-fold-search temp) ; Case-sensitive search
+                (when (and (memq 'braces format)
+                           (setq temp (cdr (assoc-string field-name
+                                                         bibtex-field-braces-opt t))))
+                  (goto-char beg-text)
+                  (while (re-search-forward temp end-text t)
+                    (let ((beg (match-beginning 0))
+                          (bounds (bibtex-find-text-internal nil t)))
+                      (unless (or (nth 4 bounds) ; string constant
+                                  ;; match already surrounded by braces
+                                  ;; (braces are inside field delimiters)
+                                  (and (< (point) (1- (nth 2 bounds)))
+                                       (< (1+ (nth 1 bounds)) beg)
+                                       (looking-at "}")
+                                       (save-excursion (goto-char (1- beg))
+                                                       (looking-at "{"))))
+                        (insert "}")
+                        (goto-char beg)
+                        (insert "{")))))
+
+                ;; replace field text by BibTeX string constants according to
+                ;; `bibtex-field-strings-alist'.
+                (when (and (memq 'strings format)
+                           (setq temp (cdr (assoc-string field-name
+                                                         bibtex-field-strings-opt t))))
+                  (goto-char beg-text)
+                  (dolist (re temp)
+                    (while (re-search-forward (car re) end-text t)
+                      (let ((bounds (save-match-data
+                                      (bibtex-find-text-internal nil t))))
+                        (unless (nth 4 bounds)
+                          ;; if match not at right subfield boundary...
+                          (if (< (match-end 0) (1- (nth 2 bounds)))
+                              (insert " # " (bibtex-field-left-delimiter))
+                            (delete-char 1))
+                          (replace-match (cdr re))
+                          (goto-char (match-beginning 0))
+                          ;; if match not at left subfield boundary...
+                          (if (< (1+ (nth 1 bounds)) (match-beginning 0))
+                              (insert (bibtex-field-right-delimiter) " # ")
+                            (delete-backward-char 1))))))))
+
               ;; use book title of crossref'd entry
               (if (and (memq 'inherit-booktitle format)
                        empty-field
@@ -2001,10 +2141,6 @@ Formats current entry according to variable `bibtex-entry-format'."
                        (error "Alternative fields `%s' are defined %s times"
                               altlist found))))))
 
-        ;; update point
-        (if (looking-at (bibtex-field-right-delimiter))
-            (forward-char))
-
         ;; update comma after last field
         (if (memq 'last-comma format)
             (cond ((and bibtex-comma-after-last-field
@@ -2026,6 +2162,31 @@ Formats current entry according to variable `bibtex-entry-format'."
         (if (memq 'realign format)
             (bibtex-fill-entry))))))
 
+(defun bibtex-field-re-init (regexp-alist type)
+  "Calculate optimized value for bibtex-regexp-TYPE-opt.
+This value is based on bibtex-regexp-TYPE-alist.  TYPE is 'braces or 'strings.
+Return optimized value to be used by `bibtex-format-entry'."
+  (setq regexp-alist
+        (mapcar (lambda (e)
+                  (list (car e)
+                        (replace-regexp-in-string "[ \t\n]+" "[ \t\n]+" (nth 1 e))
+                        (nth 2 e))) ; nil for 'braces'.
+                regexp-alist))
+  (let (opt-list)
+    ;; Loop over field names
+    (dolist (field (delete-dups (apply 'append (mapcar 'car regexp-alist))))
+      (let (rules)
+        ;; Collect all matches we have for this field name
+        (dolist (e regexp-alist)
+          (if (assoc-string field (car e) t)
+              (push (cons (nth 1 e) (nth 2 e)) rules)))
+        (if (eq type 'braces)
+            ;; concatenate all regexps to a single regexp
+            (setq rules (concat "\\(?:" (mapconcat 'car rules "\\|") "\\)")))
+        ;; create list of replacement rules.
+        (push (cons field rules) opt-list)))
+    opt-list))
+
 \f
 (defun bibtex-autokey-abbrev (string len)
   "Return an abbreviation of STRING with at least LEN characters.
@@ -2051,7 +2212,8 @@ is returned unchanged."
 Optional arg CHANGE-LIST is a list of substitution patterns that is
 applied to the content of FIELD.  It is an alist with pairs
 \(OLD-REGEXP . NEW-STRING\)."
-  (let ((content (bibtex-text-in-field field bibtex-autokey-use-crossref))
+  (let* ((bibtex-expand-strings bibtex-autokey-expand-strings)
+         (content (bibtex-text-in-field field bibtex-autokey-use-crossref))
         case-fold-search)
     (unless content (setq content ""))
     (dolist (pattern change-list content)
@@ -2077,7 +2239,7 @@ and `bibtex-autokey-names-stretch'."
                     (<= (length name-list)
                         (+ bibtex-autokey-names
                            bibtex-autokey-names-stretch)))
-          ;; Take bibtex-autokey-names elements from beginning of name-list
+          ;; Take `bibtex-autokey-names' elements from beginning of name-list
           (setq name-list (nreverse (nthcdr (- (length name-list)
                                                bibtex-autokey-names)
                                             (nreverse name-list)))
@@ -2089,7 +2251,7 @@ and `bibtex-autokey-names-stretch'."
 (defun bibtex-autokey-demangle-name (fullname)
   "Get the last part from a well-formed FULLNAME and perform abbreviations."
   (let* (case-fold-search
-         (name (cond ((string-match "\\([A-Z][^, ]*\\)[^,]*," fullname)
+         (name (cond ((string-match "\\([[:upper:]][^, ]*\\)[^,]*," fullname)
                       ;; Name is of the form "von Last, First" or
                       ;; "von Last, Jr, First"
                       ;; --> Take the first capital part before the comma
@@ -2098,7 +2260,7 @@ and `bibtex-autokey-names-stretch'."
                       ;; Strange name: we have a comma, but nothing capital
                       ;; So we accept even lowercase names
                       (match-string 1 fullname))
-                     ((string-match "\\(\\<[a-z][^ ]* +\\)+\\([A-Z][^ ]*\\)"
+                     ((string-match "\\(\\<[[:lower:]][^ ]* +\\)+\\([[:upper:]][^ ]*\\)"
                                     fullname)
                       ;; name is of the form "First von Last", "von Last",
                       ;; "First von von Last", or "d'Last"
@@ -2109,7 +2271,7 @@ and `bibtex-autokey-names-stretch'."
                       ;; --> take the last token
                       (match-string 1 fullname))
                      (t (error "Name `%s' is incorrectly formed" fullname)))))
-    (funcall bibtex-autokey-name-case-convert
+    (funcall bibtex-autokey-name-case-convert-function
              (bibtex-autokey-abbrev name bibtex-autokey-name-length))))
 
 (defun bibtex-autokey-get-year ()
@@ -2139,7 +2301,7 @@ Return the result as a string"
         (setq word (match-string 0 titlestring)
               titlestring (substring titlestring (match-end 0)))
         ;; Ignore words matched by one of the elements of
-        ;; bibtex-autokey-titleword-ignore
+        ;; `bibtex-autokey-titleword-ignore'
         (unless (let ((lst bibtex-autokey-titleword-ignore))
                   (while (and lst
                               (not (string-match (concat "\\`\\(?:" (car lst)
@@ -2151,9 +2313,9 @@ Return the result as a string"
                   (<= counter bibtex-autokey-titlewords))
               (push word titlewords)
             (push word titlewords-extra))))
-      ;; Obey bibtex-autokey-titlewords-stretch:
+      ;; Obey `bibtex-autokey-titlewords-stretch':
       ;; If by now we have processed all words in titlestring, we include
-      ;; titlewords-extra in titlewords. Otherwise, we ignore titlewords-extra.
+      ;; titlewords-extra in titlewords.  Otherwise, we ignore titlewords-extra.
       (unless (string-match "\\b\\w+" titlestring)
         (setq titlewords (append titlewords-extra titlewords)))
       (mapconcat 'bibtex-autokey-demangle-title (nreverse titlewords)
@@ -2171,7 +2333,7 @@ and `bibtex-autokey-titleword-length'."
       (setq alist (cdr alist)))
     (if alist
         (cdar alist)
-      (funcall bibtex-autokey-titleword-case-convert
+      (funcall bibtex-autokey-titleword-case-convert-function
                (bibtex-autokey-abbrev titleword bibtex-autokey-titleword-length)))))
 
 (defun bibtex-generate-autokey ()
@@ -2181,6 +2343,7 @@ The algorithm works as follows.
 
 The name part:
  1. Use the author or editor field to generate the name part of the key.
+    Expand BibTeX strings if `bibtex-autokey-expand-strings' is non-nil.
  2. Change the content of the name field according to
     `bibtex-autokey-name-change-strings' (see there for further detail).
  3. Use the first `bibtex-autokey-names' names in the name field.  If there
@@ -2190,7 +2353,7 @@ The name part:
     take at least `bibtex-autokey-name-length' characters (truncate only
     after a consonant or at a word end).
  5. Convert all last names using the function
-    `bibtex-autokey-name-case-convert'.
+    `bibtex-autokey-name-case-convert-function'.
  6. Build the name part of the key by concatenating all abbreviated last
     names with the string `bibtex-autokey-name-separator' between any two.
     If there are more names in the name field than names used in the name
@@ -2222,7 +2385,7 @@ The title part
     `bibtex-autokey-titleword-length' characters (truncate only after
     a consonant or at a word end).
  5. Convert all title words using the function
-    `bibtex-autokey-titleword-case-convert'.
+    `bibtex-autokey-titleword-case-convert-function'.
  6. Build the title part by concatenating all abbreviated title words with
     the string `bibtex-autokey-titleword-separator' between any two.
 
@@ -2285,7 +2448,9 @@ If optional arg GLOBAL is non-nil, completion is based on the keys in
   "Set `bibtex-reference-keys' to the keys used in the whole buffer.
 Find both entry keys and crossref entries.  If ABORTABLE is non-nil abort
 on user input.  If VERBOSE is non-nil give messages about progress.
-Return alist of keys if parsing was completed, `aborted' otherwise."
+Return alist of keys if parsing was completed, `aborted' otherwise.
+If `bibtex-parse-keys-fast' is non-nil, use fast but simplified algorithm
+for parsing BibTeX keys.  If parsing fails, try to set this variable to nil."
   (let (ref-keys crossref-keys)
     (save-excursion
       (save-match-data
@@ -2318,7 +2483,7 @@ Return alist of keys if parsing was completed, `aborted' otherwise."
                              (push (cons key t) ref-keys)))))))
 
             (let (;; ignore @String entries because they are handled
-                  ;; separately by bibtex-parse-strings
+                  ;; separately by `bibtex-parse-strings'
                   (bibtex-sort-ignore-string-entries t)
                   bounds)
               (bibtex-map-entries
@@ -2373,6 +2538,11 @@ Return alist of strings if parsing was completed, `aborted' otherwise."
           ;; successful operation --> return `bibtex-strings'
           (setq bibtex-strings strings))))))
 
+(defun bibtex-strings ()
+  "Return `bibtex-strings'.  Initialize this variable if necessary."
+  (if (listp bibtex-strings) bibtex-strings
+    (bibtex-parse-strings (bibtex-string-files-init))))
+
 (defun bibtex-string-files-init ()
   "Return initialization for `bibtex-strings'.
 Use `bibtex-predefined-strings' and BibTeX files `bibtex-string-files'."
@@ -2426,10 +2596,10 @@ Parsing initializes `bibtex-reference-keys' and `bibtex-strings'."
                           bibtex-buffer-last-parsed-tick)))
             (save-restriction
               (widen)
-              ;; Output no progress messages in bibtex-parse-keys
-              ;; because when in y-or-n-p that can hide the question.
+              ;; Output no progress messages in `bibtex-parse-keys'
+              ;; because when in `y-or-n-p' that can hide the question.
               (if (and (listp (bibtex-parse-keys t))
-                       ;; update bibtex-strings
+                       ;; update `bibtex-strings'
                        (listp (bibtex-parse-strings strings-init t)))
 
                   ;; remember that parsing was successful
@@ -2488,47 +2658,48 @@ already set."
   "Complete word fragment before point to longest prefix of COMPLETIONS.
 COMPLETIONS is an alist of strings.  If point is not after the part
 of a word, all strings are listed.  Return completion."
+  ;; Return value is used by cleanup functions.
+  ;; Code inspired by `lisp-complete-symbol'.
   (let* ((case-fold-search t)
          (beg (save-excursion
                 (re-search-backward "[ \t{\"]")
                 (forward-char)
                 (point)))
          (end (point))
-         (part-of-word (buffer-substring-no-properties beg end))
-         (completion (try-completion part-of-word completions)))
+         (pattern (buffer-substring-no-properties beg end))
+         (completion (try-completion pattern completions)))
     (cond ((not completion)
-           (error "Can't find completion for `%s'" part-of-word))
+           (error "Can't find completion for `%s'" pattern))
           ((eq completion t)
-           part-of-word)
-          ((not (string= part-of-word completion))
+           pattern)
+          ((not (string= pattern completion))
            (delete-region beg end)
            (insert completion)
+           ;; Don't leave around a completions buffer that's out of date.
+           (let ((win (get-buffer-window "*Completions*" 0)))
+             (if win (with-selected-window win (bury-buffer))))
            completion)
           (t
-           (message "Making completion list...")
-           (with-output-to-temp-buffer "*Completions*"
-             (display-completion-list (all-completions part-of-word
-                                                       completions)))
-           (message "Making completion list...done")
-           ;; return value is handled by choose-completion-string-functions
+           (let ((minibuf-is-in-use
+                  (eq (minibuffer-window) (selected-window))))
+             (unless minibuf-is-in-use (message "Making completion list..."))
+             (with-output-to-temp-buffer "*Completions*"
+               (display-completion-list
+                (sort (all-completions pattern completions) 'string<) pattern))
+             (unless minibuf-is-in-use
+               (message "Making completion list...done")))
            nil))))
 
 (defun bibtex-complete-string-cleanup (str compl)
   "Cleanup after inserting string STR.
 Remove enclosing field delimiters for STR.  Display message with
 expansion of STR using expansion list COMPL."
+  ;; point is at position inside field where completion was requested
   (save-excursion
-    (bibtex-inside-field)
-    (let ((bounds (bibtex-enclosing-field))
-          (abbr (cdr (if (stringp str)
+    (let ((abbr (cdr (if (stringp str)
                          (assoc-string str compl t)))))
       (if abbr (message "Abbreviation for `%s'" abbr))
-      (goto-char (bibtex-start-of-text-in-field bounds))
-      (let ((boundaries (bibtex-parse-field-string)))
-        (if (and boundaries
-                 (equal (cdr boundaries)
-                        (bibtex-end-of-text-in-field bounds)))
-            (bibtex-remove-delimiters))))))
+      (bibtex-remove-delimiters))))
 
 (defun bibtex-complete-crossref-cleanup (key)
   "Display summary message on entry KEY after completion of a crossref key.
@@ -2536,101 +2707,104 @@ Use `bibtex-summary-function' to generate summary."
   (save-excursion
     (if (and (stringp key)
              (bibtex-find-entry key t))
-        (message "Ref: %s" (funcall bibtex-summary-function key)))))
+        (message "Ref: %s" (funcall bibtex-summary-function)))))
 
-(defun bibtex-copy-summary-as-kill ()
+(defun bibtex-copy-summary-as-kill (&optional arg)
   "Push summery of current BibTeX entry to kill ring.
-Use `bibtex-summary-function' to generate summary."
-  (interactive)
-  (let ((key (save-excursion
-               (bibtex-beginning-of-entry)
-               (if (looking-at bibtex-entry-maybe-empty-head)
-                   (bibtex-key-in-head)
-                 (error "No key found")))))
-    (kill-new (message "%s" (funcall bibtex-summary-function key)))))
-
-(defun bibtex-summary (key)
-  "Return summary of BibTeX entry KEY.
+Use `bibtex-summary-function' to generate summary.
+If prefix ARG is non-nil push BibTeX entry's URL to kill ring
+that is generated by calling `bibtex-url'."
+  (interactive "P")
+  (if arg (let ((url (bibtex-url nil t)))
+            (if url (kill-new (message "%s" url))
+              (message "No URL known")))
+    (save-excursion
+      (bibtex-beginning-of-entry)
+      (if (looking-at bibtex-entry-maybe-empty-head)
+          (kill-new (message "%s" (funcall bibtex-summary-function)))
+        (error "No entry found")))))
+
+(defun bibtex-summary ()
+  "Return summary of current BibTeX entry.
 Used as default value of `bibtex-summary-function'."
-  ;; It would be neat to customize this function.  How?
-  (save-excursion
-    (if (bibtex-find-entry key t)
-        (let* ((bibtex-autokey-name-case-convert 'identity)
-               (bibtex-autokey-name-length 'infty)
-               (bibtex-autokey-names 1)
-               (bibtex-autokey-names-stretch 0)
-               (bibtex-autokey-name-separator " ")
-               (bibtex-autokey-additional-names " etal")
-               (names (bibtex-autokey-get-names))
-               (bibtex-autokey-year-length 4)
-               (year (bibtex-autokey-get-year))
-               (bibtex-autokey-titlewords 5)
-               (bibtex-autokey-titlewords-stretch 2)
-               (bibtex-autokey-titleword-case-convert 'identity)
-               (bibtex-autokey-titleword-length 5)
-               (bibtex-autokey-titleword-separator " ")
-               (title (bibtex-autokey-get-title))
-               (journal (bibtex-autokey-get-field
-                         "journal" bibtex-autokey-transcriptions))
-               (volume (bibtex-autokey-get-field "volume"))
-               (pages (bibtex-autokey-get-field "pages" '(("-.*\\'" . "")))))
-          (mapconcat (lambda (arg)
-                       (if (not (string= "" (cdr arg)))
-                           (concat (car arg) (cdr arg))))
-                     `((" " . ,names) (" " . ,year) (": " . ,title)
-                       (", " . ,journal) (" " . ,volume) (":" . ,pages))
-                     ""))
-      (error "Key `%s' not found" key))))
+  ;; It would be neat to make this function customizable.  How?
+  (if (looking-at bibtex-entry-maybe-empty-head)
+      (let* ((bibtex-autokey-name-case-convert-function 'identity)
+             (bibtex-autokey-name-length 'infty)
+             (bibtex-autokey-names 1)
+             (bibtex-autokey-names-stretch 0)
+             (bibtex-autokey-name-separator " ")
+             (bibtex-autokey-additional-names " etal")
+             (names (bibtex-autokey-get-names))
+             (bibtex-autokey-year-length 4)
+             (year (bibtex-autokey-get-year))
+             (bibtex-autokey-titlewords 5)
+             (bibtex-autokey-titlewords-stretch 2)
+             (bibtex-autokey-titleword-case-convert-function 'identity)
+             (bibtex-autokey-titleword-length 5)
+             (bibtex-autokey-titleword-separator " ")
+             (title (bibtex-autokey-get-title))
+             (journal (bibtex-autokey-get-field
+                       "journal" bibtex-autokey-transcriptions))
+             (volume (bibtex-autokey-get-field "volume"))
+             (pages (bibtex-autokey-get-field "pages" '(("-.*\\'" . "")))))
+        (mapconcat (lambda (arg)
+                     (if (not (string= "" (cdr arg)))
+                         (concat (car arg) (cdr arg))))
+                   `((" " . ,names) (" " . ,year) (": " . ,title)
+                     (", " . ,journal) (" " . ,volume) (":" . ,pages))
+                   ""))
+    (error "Entry not found")))
 
 (defun bibtex-pop (arg direction)
   "Fill current field from the ARGth same field's text in DIRECTION.
 Generic function used by `bibtex-pop-previous' and `bibtex-pop-next'."
-  (let (bibtex-help-message)
-    (bibtex-find-text))
-  (save-excursion
-    ;; parse current field
-    (bibtex-inside-field)
-    (let* ((case-fold-search t)
-           (bounds (bibtex-enclosing-field))
-           (start-old-text (bibtex-start-of-text-in-field bounds))
-           (stop-old-text (bibtex-end-of-text-in-field bounds))
-           (field-name (bibtex-name-in-field bounds t)))
+  ;; parse current field
+  (let* ((bounds (bibtex-enclosing-field t))
+         (start-old-field (bibtex-start-of-field bounds))
+         (start-old-text (bibtex-start-of-text-in-field bounds))
+         (end-old-text (bibtex-end-of-text-in-field bounds))
+         (field-name (bibtex-name-in-field bounds t))
+         failure)
+    (save-excursion
       ;; if executed several times in a row, start each search where
       ;; the last one was finished
-      (unless (eq last-command 'bibtex-pop)
-        (bibtex-enclosing-entry-maybe-empty-head)
-        (setq bibtex-pop-previous-search-point (match-beginning 0)
-              bibtex-pop-next-search-point (point)))
-      (if (eq direction 'previous)
-          (goto-char bibtex-pop-previous-search-point)
-        (goto-char bibtex-pop-next-search-point))
-      ;; Now search for arg'th previous/next similar field
-      (let (bounds failure new-text)
-        (while (and (not failure)
-                    (> arg 0))
-          (cond ((eq direction 'previous)
-                 (if (setq bounds (bibtex-search-backward-field field-name))
-                     (goto-char (bibtex-start-of-field bounds))
-                   (setq failure t)))
-                ((eq direction 'next)
-                 (if (setq bounds (bibtex-search-forward-field field-name))
-                     (goto-char (bibtex-end-of-field bounds))
-                   (setq failure t))))
-          (setq arg (- arg 1)))
-        (if failure
-            (error "No %s matching BibTeX field"
-                   (if (eq direction 'previous) "previous" "next"))
-          ;; Found a matching field.  Remember boundaries.
-          (setq bibtex-pop-previous-search-point (bibtex-start-of-field bounds)
-                bibtex-pop-next-search-point (bibtex-end-of-field bounds)
-                new-text (bibtex-text-in-field-bounds bounds))
-          (bibtex-flash-head)
+      (cond ((eq last-command 'bibtex-pop)
+             (goto-char (if (eq direction 'previous)
+                            bibtex-pop-previous-search-point
+                          bibtex-pop-next-search-point)))
+            ((eq direction 'previous)
+             (bibtex-beginning-of-entry))
+            (t (bibtex-end-of-entry)))
+      ;; Search for arg'th previous/next similar field
+      (while (and (not failure)
+                  (>= (setq arg (1- arg)) 0))
+        ;; The search of BibTeX fields is not bounded by entry boundaries
+        (if (eq direction 'previous)
+            (if (setq bounds (bibtex-search-backward-field field-name))
+                (goto-char (bibtex-start-of-field bounds))
+              (setq failure t))
+          (if (setq bounds (bibtex-search-forward-field field-name))
+              (goto-char (bibtex-end-of-field bounds))
+            (setq failure t))))
+      (if failure
+          (error "No %s matching BibTeX field"
+                 (if (eq direction 'previous) "previous" "next"))
+        ;; Found a matching field.  Remember boundaries.
+        (let ((new-text (bibtex-text-in-field-bounds bounds))
+              (nbeg (copy-marker (bibtex-start-of-field bounds)))
+              (nend (copy-marker (bibtex-end-of-field bounds))))
+          (bibtex-flash-head "From: ")
           ;; 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)))))
-  (let (bibtex-help-message)
-    (bibtex-find-text))
+          (goto-char end-old-text)
+          (delete-region start-old-text end-old-text)
+          (if (= nbeg start-old-field)
+              (insert (bibtex-field-left-delimiter)
+                      (bibtex-field-right-delimiter))
+            (insert new-text))
+          (setq bibtex-pop-previous-search-point (marker-position nbeg)
+                bibtex-pop-next-search-point (marker-position nend))))))
+  (bibtex-find-text nil nil nil t)
   (setq this-command 'bibtex-pop))
 
 (defun bibtex-beginning-of-field ()
@@ -2642,35 +2816,37 @@ begins at the beginning of a line.  We use this function for font-locking."
     (unless (looking-at field-reg)
       (re-search-backward field-reg nil t))))
 
-(defun bibtex-font-lock-url (bound)
-  "Font-lock for URLs.  BOUND limits the search."
+(defun bibtex-font-lock-url (bound &optional no-button)
+  "Font-lock for URLs.  BOUND limits the search.
+If NO-BUTTON is non-nil do not generate buttons."
   (let ((case-fold-search t)
         (pnt (point))
-        field bounds start end found)
+        name bounds start end found)
     (bibtex-beginning-of-field)
     (while (and (not found)
+                (<= (point) bound)
                (prog1 (re-search-forward bibtex-font-lock-url-regexp bound t)
-                 (setq field (match-string-no-properties 1)))
+                 (setq name (match-string-no-properties 1)))
                (setq bounds (bibtex-parse-field-text))
                 (progn
-                  (setq start (car bounds) end (cdr bounds))
+                  (setq start (car bounds) end (nth 1 bounds))
                   ;; Always ignore field delimiters
                   (if (memq (char-before end) '(?\} ?\"))
                       (setq end (1- end)))
                   (if (memq (char-after start) '(?\{ ?\"))
                       (setq start (1+ start)))
-                  (>= bound start)))
-      (let ((lst bibtex-generate-url-list) url)
-        (goto-char start)
-       (while (and (not found)
-                   (setq url (caar lst)))
-         (setq found (and (bibtex-string= field (car url))
-                           (re-search-forward (cdr url) end t)
-                           (>= (match-beginning 0) pnt))
-                lst (cdr lst))))
-      (goto-char end))
-    (if found (bibtex-button (match-beginning 0) (match-end 0)
-                             'bibtex-url (match-beginning 0)))
+                  (if (< start pnt) (setq start (min pnt end)))
+                  (<= start bound)))
+      (if (<= pnt start)
+          (let ((lst bibtex-generate-url-list) url)
+            (while (and (not found) (setq url (car (pop lst))))
+              (goto-char start)
+              (setq found (and (bibtex-string= name (car url))
+                               (re-search-forward (cdr url) end t))))))
+      (unless found (goto-char end)))
+    (if (and found (not no-button))
+        (bibtex-button (match-beginning 0) (match-end 0)
+                       'bibtex-url (match-beginning 0)))
     found))
 
 (defun bibtex-font-lock-crossref (bound)
@@ -2691,6 +2867,19 @@ begins at the beginning of a line.  We use this function for font-locking."
                              start t))
     found))
 
+(defun bibtex-font-lock-cite (matcher bound)
+  "Font-lock for cited keys.
+MATCHER identifies the cited key, see `bibtex-cite-matcher-alist'.
+BOUND limits the search."
+  (let (case-fold-search)
+    (if (re-search-forward (car matcher) bound t)
+        (let ((start (match-beginning (cdr matcher)))
+              (end (match-end (cdr matcher))))
+          (bibtex-button start end 'bibtex-find-crossref
+                         (buffer-substring-no-properties start end)
+                         start t t)
+          t))))
+
 (defun bibtex-button-action (button)
   "Call BUTTON's BibTeX function."
   (apply (button-get button 'bibtex-function)
@@ -2730,7 +2919,7 @@ works only with buffers containing valid (syntactical correct) and sorted
 entries.  This is usually the case, if you have created a buffer completely
 with BibTeX mode and finished every new entry with \\[bibtex-clean-entry].
 
-For third party BibTeX files, call the function `bibtex-convert-alien'
+For third party BibTeX files, call the command \\[bibtex-convert-alien]
 to fully take advantage of all features of BibTeX mode.
 
 
@@ -2783,12 +2972,12 @@ if that value is non-nil.
   (set (make-local-variable 'comment-start-skip)
        (concat (regexp-quote bibtex-comment-start) "\\>[ \t]*"))
   (set (make-local-variable 'comment-column) 0)
-  (set (make-local-variable 'defun-prompt-regexp) "^[ \t]*@[a-zA-Z0-9]+[ \t]*")
+  (set (make-local-variable 'defun-prompt-regexp) "^[ \t]*@[[:alnum:]]+[ \t]*")
   (set (make-local-variable 'outline-regexp) "[ \t]*@")
   (set (make-local-variable 'fill-paragraph-function) 'bibtex-fill-field)
   (set (make-local-variable 'fill-prefix) (make-string (+ bibtex-entry-offset
                                                           bibtex-contline-indentation)
-                                                       ? ))
+                                                       ?\s))
   (set (make-local-variable 'font-lock-defaults)
        '(bibtex-font-lock-keywords
          nil t ((?$ . "\"")
@@ -2809,10 +2998,10 @@ if that value is non-nil.
         (list (list nil bibtex-entry-head bibtex-key-in-head))
         imenu-case-fold-search t)
   (make-local-variable 'choose-completion-string-functions)
-  ;; XEmacs needs easy-menu-add, Emacs does not care
+  ;; XEmacs needs `easy-menu-add', Emacs does not care
   (easy-menu-add bibtex-edit-menu)
   (easy-menu-add bibtex-entry-menu)
-  (run-hooks 'bibtex-mode-hook))
+  (run-mode-hooks 'bibtex-mode-hook))
 
 (defun bibtex-field-list (entry-type)
   "Return list of allowed fields for entry ENTRY-TYPE.
@@ -2824,7 +3013,7 @@ and `bibtex-user-optional-fields'."
   (let ((e (assoc-string entry-type bibtex-entry-field-alist t))
         required optional)
     (unless e
-      (error "Bibtex entry type %s not defined" entry-type))
+      (error "Fields for BibTeX entry type %s not defined" entry-type))
     (if (and (member-ignore-case entry-type bibtex-include-OPTcrossref)
              (nth 2 e))
         (setq required (nth 0 (nth 2 e))
@@ -2835,7 +3024,7 @@ and `bibtex-user-optional-fields'."
         (push (list "key"
                     "Used for reference key creation if author and editor fields are missing"
                     (if (or (stringp bibtex-include-OPTkey)
-                            (fboundp bibtex-include-OPTkey))
+                            (functionp bibtex-include-OPTkey))
                         bibtex-include-OPTkey))
               optional))
     (if (member-ignore-case entry-type bibtex-include-OPTcrossref)
@@ -2873,39 +3062,61 @@ is non-nil."
        (bibtex-autofill-entry))
     (run-hooks 'bibtex-add-entry-hook)))
 
-(defun bibtex-entry-update ()
+(defun bibtex-entry-update (&optional entry-type)
   "Update an existing BibTeX entry.
 In the BibTeX entry at point, make new fields for those items that may occur
-according to `bibtex-field-list', but are not yet present."
-  (interactive)
+according to `bibtex-field-list', but are not yet present.
+Also, add field delimiters to numerical fields if they are not present.
+If ENTRY-TYPE is non-nil, change first the entry type to ENTRY-TYPE.
+When called interactively with a prefix arg, query for a value of ENTRY-TYPE."
+  (interactive
+   (list (if current-prefix-arg
+             (let ((completion-ignore-case t))
+               (completing-read "New entry type: " bibtex-entry-field-alist
+                                nil t nil 'bibtex-entry-type-history)))))
   (save-excursion
     (bibtex-beginning-of-entry)
-    ;; For inserting new fields, we use the fact that
-    ;; bibtex-parse-entry moves point to the end of the last field.
-    (let* ((fields-alist (bibtex-parse-entry))
-           (field-list (bibtex-field-list
-                        (cdr (assoc "=type=" fields-alist)))))
-      (dolist (field (car field-list))
-        (unless (assoc-string (car field) fields-alist t)
-          (bibtex-make-field field)))
-      (dolist (field (cdr field-list))
-        (unless (assoc-string (car field) fields-alist t)
-          (bibtex-make-optional-field field))))))
-
-(defun bibtex-parse-entry ()
+    (when (looking-at bibtex-entry-maybe-empty-head)
+      (goto-char (match-end 0))
+      (if entry-type
+          (save-excursion
+            (replace-match (concat "@" entry-type) nil nil nil 1))
+        (setq entry-type (bibtex-type-in-head)))
+      (let* ((field-list (bibtex-field-list entry-type))
+             (required (copy-tree (car field-list)))
+             (optional (copy-tree (cdr field-list)))
+             bounds)
+        (while (setq bounds (bibtex-parse-field))
+          (let ((fname (bibtex-name-in-field bounds t))
+                (end (copy-marker (bibtex-end-of-field bounds) t)))
+            (setq required (delete (assoc-string fname required t) required)
+                  optional (delete (assoc-string fname optional t) optional))
+            (when (string-match "\\`[0-9]+\\'"
+                                (bibtex-text-in-field-bounds bounds))
+              (goto-char (bibtex-end-of-text-in-field bounds))
+              (insert (bibtex-field-right-delimiter))
+              (goto-char (bibtex-start-of-text-in-field bounds))
+              (insert (bibtex-field-left-delimiter)))
+            (goto-char end)))
+        (skip-chars-backward " \t\n")
+        (dolist (field required) (bibtex-make-field field))
+        (dolist (field optional) (bibtex-make-optional-field field))))))
+
+(defun bibtex-parse-entry (&optional content)
   "Parse entry at point, return an alist.
 The alist elements have the form (FIELD . TEXT), where FIELD can also be
 the special strings \"=type=\" and \"=key=\".  For the FIELD \"=key=\"
 TEXT may be nil.  Remove \"OPT\" and \"ALT\" from FIELD.
-Move point to the end of the last field."
+Move point to the end of the last field.
+If optional arg CONTENT is non-nil extract content of text fields."
   (let (alist bounds)
     (when (looking-at bibtex-entry-maybe-empty-head)
       (push (cons "=type=" (bibtex-type-in-head)) alist)
       (push (cons "=key=" (bibtex-key-in-head)) alist)
       (goto-char (match-end 0))
-      (while (setq bounds (bibtex-parse-field bibtex-field-name))
+      (while (setq bounds (bibtex-parse-field))
        (push (cons (bibtex-name-in-field bounds t)
-                   (bibtex-text-in-field-bounds bounds))
+                   (bibtex-text-in-field-bounds bounds content))
              alist)
        (goto-char (bibtex-end-of-field bounds))))
     alist))
@@ -2926,6 +3137,7 @@ entry (for example, the year parts of the keys)."
          (key (bibtex-key-in-head))
          (key-end (match-end bibtex-key-in-head))
           (case-fold-search t)
+          (bibtex-sort-ignore-string-entries t)
          tmp other-key other bounds)
       ;; The fields we want to change start right after the key.
       (goto-char key-end)
@@ -2957,12 +3169,13 @@ entry (for example, the year parts of the keys)."
       (when other
        (setq other (save-excursion (goto-char other) (bibtex-parse-entry)))
        (setq key-end (point))      ;In case parse-entry changed the buffer.
-       (while (setq bounds (bibtex-parse-field bibtex-field-name))
+       (while (setq bounds (bibtex-parse-field))
          (let ((text (assoc-string (bibtex-name-in-field bounds t)
                                     other t)))
-           (goto-char (bibtex-start-of-text-in-field bounds))
-           (if (not (and (looking-at bibtex-empty-field-re) text))
+           (if (not (and text
+                          (equal "" (bibtex-text-in-field-bounds bounds t))))
                (goto-char (bibtex-end-of-field bounds))
+              (goto-char (bibtex-start-of-text-in-field bounds))
              (delete-region (point) (bibtex-end-of-text-in-field bounds))
              (insert (cdr text)))))
        ;; Finally try to update the text based on the difference between
@@ -2977,24 +3190,30 @@ entry (for example, the year parts of the keys)."
          (while (re-search-backward (regexp-quote other-suffix) key-end 'move)
            (replace-match suffix)))))))
 
-(defun bibtex-print-help-message ()
-  "Print helpful information about current field in current BibTeX entry."
-  (interactive)
-  (save-excursion
+(defun bibtex-print-help-message (&optional field comma)
+  "Print helpful information about current FIELD in current BibTeX entry.
+Optional arg COMMA is as in `bibtex-enclosing-field'.  It is t for
+interactive calls."
+  (interactive (list nil t))
+  (unless field (setq field (car (bibtex-find-text-internal nil nil comma))))
+  (if (string-match "@" field)
+      (cond ((bibtex-string= field "@string")
+             (message "String definition"))
+            ((bibtex-string= field "@preamble")
+             (message "Preamble definition"))
+            (t (message "Entry key")))
     (let* ((case-fold-search t)
-           (field-name (bibtex-name-in-field (bibtex-enclosing-field) t))
-           (field-list (bibtex-field-list (progn (re-search-backward
-                                                  bibtex-entry-maybe-empty-head nil t)
-                                                 (bibtex-type-in-head))))
-           (comment (assoc-string field-name
-                                 (append (car field-list)
-                                         (cdr field-list))
-                                 t)))
-      (if comment
-          (message (nth 1 comment))
+           (type (save-excursion
+                   (bibtex-beginning-of-entry)
+                   (looking-at bibtex-entry-maybe-empty-head)
+                   (bibtex-type-in-head)))
+           (field-list (bibtex-field-list type))
+           (comment (assoc-string field (append (car field-list)
+                                                (cdr field-list)) t)))
+      (if comment (message "%s" (nth 1 comment))
         (message "No comment available")))))
 
-(defun bibtex-make-field (field &optional move interactive)
+(defun bibtex-make-field (field &optional move interactive nodelim)
   "Make a field named FIELD in current BibTeX entry.
 FIELD is either a string or a list of the form
 \(FIELD-NAME COMMENT-STRING INIT ALTERNATIVE-FLAG) as in
@@ -3002,12 +3221,14 @@ FIELD is either a string or a list of the form
 If MOVE is non-nil, move point past the present field before making
 the new field.  If INTERACTIVE is non-nil, move point to the end of
 the new field.  Otherwise move point past the new field.
-MOVE and INTERACTIVE are t when called interactively."
+MOVE and INTERACTIVE are t when called interactively.
+INIT is surrounded by field delimiters, unless NODELIM is non-nil."
   (interactive
    (list (let ((completion-ignore-case t)
                (field-list (bibtex-field-list
                             (save-excursion
-                              (bibtex-enclosing-entry-maybe-empty-head)
+                              (bibtex-beginning-of-entry)
+                              (looking-at bibtex-any-entry-maybe-empty-head)
                               (bibtex-type-in-head)))))
            (completing-read "BibTeX field name: "
                             (append (car field-list) (cdr field-list))
@@ -3015,11 +3236,10 @@ MOVE and INTERACTIVE are t when called interactively."
          t t))
   (unless (consp field)
     (setq field (list field)))
-  (if move
-      (let (bibtex-help-message)
-        (bibtex-find-text)
-        (if (looking-at "[}\"]")
-            (forward-char))))
+  (when move
+    (bibtex-find-text)
+    (if (looking-at "[}\"]")
+        (forward-char)))
   (insert ",\n")
   (indent-to-column (+ bibtex-entry-offset bibtex-field-indentation))
   (if (nth 3 field) (insert "ALT"))
@@ -3032,13 +3252,17 @@ MOVE and INTERACTIVE are t when called interactively."
     (indent-to-column (+ bibtex-entry-offset
                          bibtex-text-indentation)))
   (let ((init (nth 2 field)))
-    (insert (cond ((stringp init) init)
-                  ((fboundp init) (funcall init))
-                  (t (concat (bibtex-field-left-delimiter)
-                             (bibtex-field-right-delimiter))))))
+    (if (not init) (setq init "")
+      (if (functionp init) (setq init (funcall init)))
+      (unless (stringp init) (error "`%s' is not a string" init)))
+    ;; NODELIM is required by `bibtex-insert-kill'
+    (if nodelim (insert init)
+      (insert (bibtex-field-left-delimiter) init
+              (bibtex-field-right-delimiter))))
   (when interactive
-    (forward-char -1)
-    (bibtex-print-help-message)))
+    ;; (bibtex-find-text nil nil bibtex-help-message)
+    (if (memq (preceding-char) '(?} ?\")) (forward-char -1))
+    (if bibtex-help-message (bibtex-print-help-message (car field)))))
 
 (defun bibtex-beginning-of-entry ()
   "Move to beginning of BibTeX entry (beginning of line).
@@ -3059,28 +3283,19 @@ of the previous entry.  Do not move if ahead of first entry.
 Return the new location of point."
   (interactive)
   (let ((case-fold-search t)
-        (org (point))
-        (pnt (bibtex-beginning-of-entry))
-        err bounds)
-    (cond ((looking-at bibtex-valid-entry-whitespace-re)
-           (bibtex-search-entry t nil t)
-           (unless (equal (match-beginning 0) pnt)
-             (setq err t)))
-          ((setq bounds (bibtex-parse-string))
+        (pnt (point))
+        (_ (bibtex-beginning-of-entry))
+        (bounds (bibtex-valid-entry t)))
+    (cond (bounds (goto-char (cdr bounds))) ; regular entry
+          ;; @String or @Preamble
+          ((setq bounds (or (bibtex-parse-string t) (bibtex-parse-preamble)))
            (goto-char (bibtex-end-of-string bounds)))
-          ((looking-at "[ \t]*@[ \t]*preamble[ \t\n]*")
-           (goto-char (match-end 0))
-           (if (looking-at "[({]")
-               (forward-sexp 1)
-             (setq err t)))
-          (t
-           (if (interactive-p)
-               (message "Not on a known BibTeX entry."))
-           (goto-char org)))
-    (when err
-      (goto-char pnt)
-      (error "Syntactically incorrect BibTeX entry starts here")))
-  (point))
+          ((looking-at bibtex-any-valid-entry-type)
+           ;; Parsing of entry failed
+           (error "Syntactically incorrect BibTeX entry starts here"))
+          (t (if (interactive-p) (message "Not on a known BibTeX entry."))
+             (goto-char pnt)))
+    (point)))
 
 (defun bibtex-goto-line (arg)
   "Goto line ARG, counting from beginning of (narrowed) buffer."
@@ -3115,7 +3330,7 @@ Otherwise display the beginning of entry."
 (defun bibtex-mark-entry ()
   "Put mark at beginning, point at end of current BibTeX entry."
   (interactive)
-  (set-mark (bibtex-beginning-of-entry))
+  (push-mark (bibtex-beginning-of-entry))
   (bibtex-end-of-entry))
 
 (defun bibtex-count-entries (&optional count-string-entries)
@@ -3125,15 +3340,10 @@ otherwise count all entries except @String entries.
 If mark is active count entries in region, if not in whole buffer."
   (interactive "P")
   (let ((number 0)
-        (bibtex-sort-ignore-string-entries
-         (not count-string-entries)))
-    (save-excursion
-      (save-restriction
-        (narrow-to-region (if mark-active (region-beginning)
-                            (bibtex-beginning-of-first-entry))
-                          (if mark-active (region-end) (point-max)))
-        (bibtex-map-entries (lambda (key beg end)
-                              (setq number (1+ number))))))
+        (bibtex-sort-ignore-string-entries (not count-string-entries)))
+    (save-restriction
+      (if mark-active (narrow-to-region (region-beginning) (region-end)))
+      (bibtex-map-entries (lambda (key beg end) (setq number (1+ number)))))
     (message "%s contains %d entries."
              (if mark-active "Region" "Buffer")
              number)))
@@ -3183,6 +3393,18 @@ of the head of the entry found.  Return nil if no entry found."
                       entry-name))
             (list key nil entry-name))))))
 
+(defun bibtex-init-sort-entry-class-alist ()
+  "Initialize `bibtex-sort-entry-class-alist' (buffer-local)."
+  (unless (local-variable-p 'bibtex-sort-entry-class-alist)
+    (set (make-local-variable 'bibtex-sort-entry-class-alist)
+         (let ((i -1) alist)
+           (dolist (class bibtex-sort-entry-class alist)
+             (setq i (1+ i))
+             (dolist (entry class)
+               ;; All entry names should be downcase (for ease of comparison).
+               (push (cons (if (stringp entry) (downcase entry) entry) i)
+                     alist)))))))
+
 (defun bibtex-lessp (index1 index2)
   "Predicate for sorting BibTeX entries with indices INDEX1 and INDEX2.
 Each index is a list (KEY CROSSREF-KEY ENTRY-NAME).
@@ -3220,39 +3442,58 @@ If its value is nil use plain sorting.  Text outside of BibTeX entries is not
 affected.  If `bibtex-sort-ignore-string-entries' is non-nil, @String entries
 are ignored."
   (interactive)
-  (save-restriction
-    (narrow-to-region (bibtex-beginning-of-first-entry)
-                      (save-excursion (goto-char (point-max))
-                                      (bibtex-end-of-entry)))
-    (bibtex-skip-to-valid-entry)
-    (sort-subr nil
-               'bibtex-skip-to-valid-entry ; NEXTREC function
-               'bibtex-end-of-entry        ; ENDREC function
-               'bibtex-entry-index         ; STARTKEY function
-               nil                         ; ENDKEY function
-               'bibtex-lessp)))            ; PREDICATE
-
-(defun bibtex-find-crossref (crossref-key &optional pnt split)
+  (bibtex-beginning-of-first-entry)     ; Needed by `sort-subr'
+  (bibtex-init-sort-entry-class-alist)  ; Needed by `bibtex-lessp'.
+  (sort-subr nil
+             'bibtex-skip-to-valid-entry   ; NEXTREC function
+             'bibtex-end-of-entry          ; ENDREC function
+             'bibtex-entry-index           ; STARTKEY function
+             nil                           ; ENDKEY function
+             'bibtex-lessp))               ; PREDICATE
+
+(defun bibtex-find-crossref (crossref-key &optional pnt split noerror)
   "Move point to the beginning of BibTeX entry CROSSREF-KEY.
 If `bibtex-files' is non-nil, search all these files.
 Otherwise the search is limited to the current buffer.
 Return position of entry if CROSSREF-KEY is found or nil otherwise.
 If CROSSREF-KEY is in the same buffer like current entry but before it
-an error is signaled.  Optional arg PNT is the position of the referencing
-entry. It defaults to position of point.  If optional arg SPLIT is non-nil,
-split window so that both the referencing and the crossrefed entry are
-displayed.
-If called interactively, CROSSREF-KEY defaults to crossref key of current
-entry and SPLIT is t."
+an error is signaled.  If NOERRER is non-nil this error is suppressed.
+Optional arg PNT is the position of the referencing entry.  It defaults
+to position of point.  If optional arg SPLIT is non-nil, split window
+so that both the referencing and the crossrefed entry are displayed.
+
+If called interactively, CROSSREF-KEY defaults to either the crossref key
+of current entry or a key matched by `bibtex-cite-matcher-alist',
+whatever is nearer to the position of point.  SPLIT is t.  NOERROR is nil
+for a crossref key, t otherwise."
   (interactive
-   (let ((crossref-key
-          (save-excursion
-            (bibtex-beginning-of-entry)
-            (let ((bounds (bibtex-search-forward-field "crossref" t)))
-              (if bounds
-                  (bibtex-text-in-field-bounds bounds t))))))
-     (list (bibtex-read-key "Find crossref key: " crossref-key t)
-           (point) t)))
+   (save-excursion
+     (let* ((pnt (point))
+            (_ (bibtex-beginning-of-entry))
+            (end (cdr (bibtex-valid-entry t)))
+            (_ (unless end (error "Not inside valid entry")))
+            (beg (match-end 0)) ; set by `bibtex-valid-entry'
+            (bounds (bibtex-search-forward-field "crossref" end))
+            case-fold-search best temp crossref-key)
+       (if bounds
+           (setq crossref-key (bibtex-text-in-field-bounds bounds t)
+                 best (cons (bibtex-dist pnt (bibtex-end-of-field bounds)
+                                         (bibtex-start-of-field bounds))
+                            crossref-key)))
+       (dolist (matcher bibtex-cite-matcher-alist)
+         (goto-char beg)
+         (while (re-search-forward (car matcher) end t)
+           (setq temp (bibtex-dist pnt (match-end (cdr matcher))
+                                   (match-beginning (cdr matcher))))
+           ;; Accept the key closest to the position of point.
+           (if (or (not best) (< temp (car best)))
+               (setq best (cons temp (match-string-no-properties
+                                      (cdr matcher)))))))
+       (goto-char pnt)
+       (setq temp (bibtex-read-key "Find crossref key: " (cdr best) t))
+       (list temp (point) t (not (and crossref-key
+                                      (string= temp crossref-key)))))))
+
   (let (buffer pos eqb)
     (save-excursion
       (setq pos (bibtex-find-entry crossref-key t)
@@ -3263,13 +3504,15 @@ entry and SPLIT is t."
           (split ; called (quasi) interactively
            (unless pnt (setq pnt (point)))
            (goto-char pnt)
-           (if eqb (select-window (split-window))
-             (pop-to-buffer buffer))
-           (goto-char pos)
-           (bibtex-reposition-window)
-           (beginning-of-line)
-           (if (and eqb (> pnt pos))
-               (error "The referencing entry must preceed the crossrefed entry!")))
+           (if (and eqb (= pos (save-excursion (bibtex-beginning-of-entry))))
+               (message "Key `%s' is current entry" crossref-key)
+             (if eqb (select-window (split-window))
+               (pop-to-buffer buffer))
+             (goto-char pos)
+             (bibtex-reposition-window)
+             (beginning-of-line)
+             (if (and eqb (> pnt pos) (not noerror))
+                 (error "The referencing entry must precede the crossrefed entry!"))))
           ;; `bibtex-find-crossref' is called noninteractively during
           ;; clean-up of an entry.  Then it is not possible to check
           ;; whether the current entry and the crossrefed entry have
@@ -3278,6 +3521,12 @@ entry and SPLIT is t."
           (t (set-buffer buffer) (goto-char pos)))
     pos))
 
+(defun bibtex-dist (pos beg end)
+  "Return distance between POS and region delimited by BEG and END."
+  (cond ((and (<= beg pos) (<= pos end)) 0)
+        ((< pos beg) (- beg pos))
+        (t (- pos end))))
+
 (defun bibtex-find-entry (key &optional global start display)
   "Move point to the beginning of BibTeX entry named KEY.
 Return position of entry if KEY is found or nil if not found.
@@ -3307,7 +3556,7 @@ Otherwise, use `set-buffer'.  DISPLAY is t when called interactively."
               (display (message "Key `%s' not found" key)))
         found)
 
-    (let* (case-fold-search
+    (let* ((case-fold-search t)
            (pnt (save-excursion
                   (goto-char (or start (point-min)))
                   (if (re-search-forward (concat "^[ \t]*\\("
@@ -3329,8 +3578,9 @@ INDEX is a list (KEY CROSSREF-KEY ENTRY-NAME).
 Move point where the entry KEY should be placed.
 If `bibtex-maintain-sorted-entries' is non-nil, perform a binary
 search to look for place for KEY.  This requires that buffer is sorted,
-see \\[bibtex-validate].)
+see `bibtex-validate'.
 Return t if preparation was successful or nil if entry KEY already exists."
+  (bibtex-init-sort-entry-class-alist)  ; Needed by `bibtex-lessp'.
   (let ((key (nth 0 index))
         key-exist)
     (cond ((or (null key)
@@ -3342,13 +3592,12 @@ Return t if preparation was successful or nil if entry KEY already exists."
           ;; if key-exist is non-nil due to the previous cond clause
           ;; then point will be at beginning of entry named key.
           (key-exist)
-          (t             ; bibtex-maintain-sorted-entries is non-nil
+          (t             ; `bibtex-maintain-sorted-entries' is non-nil
            (let* ((case-fold-search t)
-                  (left (save-excursion (bibtex-beginning-of-first-entry)
-                                        (bibtex-skip-to-valid-entry)
-                                        (point)))
-                  (right (save-excursion (bibtex-beginning-of-last-entry)
-                                         (bibtex-end-of-entry)))
+                  (left (save-excursion (bibtex-beginning-of-first-entry)))
+                  (bounds (save-excursion (goto-char (point-max))
+                                          (bibtex-skip-to-valid-entry t)))
+                  (right (if bounds (cdr bounds) (point-min)))
                   (found (if (>= left right) left))
                   actual-index new)
              (save-excursion
@@ -3395,41 +3644,38 @@ Return t if test was successful, nil otherwise."
          error-list syntax-error)
     (save-excursion
       (save-restriction
-        (narrow-to-region (if mark-active (region-beginning)
-                            (bibtex-beginning-of-first-entry))
-                          (if mark-active (region-end) (point-max)))
+        (if mark-active (narrow-to-region (region-beginning) (region-end)))
 
-        ;; looking if entries fit syntactical structure
+        ;; Check syntactical structure of entries
         (goto-char (point-min))
         (bibtex-progress-message "Checking syntactical structure")
-        (let (bibtex-sort-ignore-string-entries)
-          (while (re-search-forward "^[ \t]*@" nil t)
+        (let (bounds end)
+          (while (setq end (re-search-forward "^[ \t]*@" nil t))
             (bibtex-progress-message)
-            (forward-char -1)
-            (let ((pnt (point)))
-              (if (not (looking-at bibtex-any-valid-entry-re))
-                  (forward-char)
-                (bibtex-skip-to-valid-entry)
-                (if (equal (point) pnt)
-                    (forward-char)
-                  (goto-char pnt)
-                  (push (cons (bibtex-current-line)
-                              "Syntax error (check esp. commas, braces, and quotes)")
-                        error-list)
-                  (forward-char))))))
+            (goto-char (match-beginning 0))
+            (cond ((setq bounds (bibtex-valid-entry))
+                   (goto-char (cdr bounds)))
+                  ((setq bounds (or (bibtex-parse-string)
+                                    (bibtex-parse-preamble)))
+                   (goto-char (bibtex-end-of-string bounds)))
+                  ((looking-at bibtex-any-valid-entry-type)
+                   (push (cons (bibtex-current-line)
+                               "Syntax error (check esp. commas, braces, and quotes)")
+                         error-list)
+                   (goto-char (match-end 0)))
+                  (t (goto-char end)))))
         (bibtex-progress-message 'done)
 
         (if error-list
-            ;; proceed only if there were no syntax errors.
+            ;; Continue only if there were no syntax errors.
             (setq syntax-error t)
 
-          ;; looking for duplicate keys and correct sort order
+          ;; Check for duplicate keys and correct sort order
           (let (previous current key-list)
             (bibtex-progress-message "Checking for duplicate keys")
             (bibtex-map-entries
              (lambda (key beg end)
                (bibtex-progress-message)
-               (goto-char beg)
                (setq current (bibtex-entry-index))
                (cond ((not previous))
                      ((member key key-list)
@@ -3465,18 +3711,13 @@ Return t if test was successful, nil otherwise."
               (bibtex-map-entries
                (lambda (key beg end)
                  (bibtex-progress-message)
-                 (let* ((entry-list (progn
-                                      (goto-char beg)
-                                      (bibtex-search-entry nil end)
-                                      (assoc-string (bibtex-type-in-head)
-                                                   bibtex-entry-field-alist t)))
+                 (let* ((entry-list (assoc-string (bibtex-type-in-head)
+                                                  bibtex-entry-field-alist t))
                         (req (copy-sequence (elt (elt entry-list 1) 0)))
                         (creq (copy-sequence (elt (elt entry-list 2) 0)))
                         crossref-there bounds alt-there field)
-                   (goto-char beg)
-                   (while (setq bounds (bibtex-search-forward-field
-                                        bibtex-field-name end))
-                     (goto-char (bibtex-start-of-text-in-field bounds))
+                   (bibtex-beginning-first-field beg)
+                   (while (setq bounds (bibtex-parse-field))
                      (let ((field-name (bibtex-name-in-field bounds)))
                        (if (and (bibtex-string= field-name "month")
                                 ;; Check only abbreviated month fields.
@@ -3488,18 +3729,19 @@ Return t if test was successful, nil otherwise."
                            (push (cons (bibtex-current-line)
                                        "Questionable month field")
                                  error-list))
-                       (setq field (assoc-string field-name req t))
+                       (setq field (assoc-string field-name req t)
+                             req (delete field req)
+                             creq (delete (assoc-string field-name creq t) creq))
                        (if (nth 3 field)
-                           (if alt-there (push (cons (bibtex-current-line)
-                                                     "More than one non-empty alternative")
-                                               error-list)
+                           (if alt-there
+                               (push (cons (bibtex-current-line)
+                                           "More than one non-empty alternative")
+                                     error-list)
                              (setq alt-there t)))
-                       (setq req (delete field req)
-                             creq (delete (assoc-string field-name creq t) creq))
                        (if (bibtex-string= field-name "crossref")
-                           (setq crossref-there t))))
-                   (if crossref-there
-                       (setq req creq))
+                           (setq crossref-there t)))
+                     (goto-char (bibtex-end-of-field bounds)))
+                   (if crossref-there (setq req creq))
                    (let (alt)
                      (dolist (field req)
                        (if (nth 3 field)
@@ -3532,7 +3774,7 @@ Return t if test was successful, nil otherwise."
             (delete-region (point-min) (point-max))
             (insert "BibTeX mode command `bibtex-validate'\n"
                     (if syntax-error
-                        "Maybe undetected errors due to syntax errors. Correct and validate again.\n"
+                        "Maybe undetected errors due to syntax errors.  Correct and validate again.\n"
                       "\n"))
             (dolist (err error-list)
               (insert (format "%s:%d: %s\n" file (car err) (cdr err))))
@@ -3540,11 +3782,10 @@ Return t if test was successful, nil otherwise."
             (toggle-read-only 1)
             (goto-line 3)) ; first error message
           (display-buffer err-buf)
-          ;; return nil
-          nil)
+          nil) ; return `nil' (i.e., buffer is invalid)
       (message "%s is syntactically correct"
                (if mark-active "Region" "Buffer"))
-      t)))
+      t))) ; return `t' (i.e., buffer is valid)
 
 (defun bibtex-validate-globally (&optional strings)
   "Check for duplicate keys in `bibtex-files'.
@@ -3580,7 +3821,7 @@ Return t if test was successful, nil otherwise."
           (dolist (key (cdr (assq buffer buffer-key-list)))
             (when (assoc-string key current-keys)
               (bibtex-find-entry key)
-              (push (format "%s:%d: Duplicat key `%s' in %s\n"
+              (push (format "%s:%d: Duplicate key `%s' in %s\n"
                             (buffer-file-name) (bibtex-current-line) key
                             (abbreviate-file-name (buffer-file-name buffer)))
                     error-list))))))
@@ -3598,67 +3839,136 @@ Return t if test was successful, nil otherwise."
             (toggle-read-only 1)
             (goto-line 3)) ; first error message
           (display-buffer err-buf)
-          ;; return nil
-          nil)
+          nil) ; return `nil' (i.e., buffer is invalid)
       (message "No duplicate keys.")
-      t)))
-
-(defun bibtex-next-field (begin)
-  "Move point to end of text of next BibTeX field.
-With prefix BEGIN non-nil, move point to its beginning."
-  (interactive "P")
-  (bibtex-inside-field)
-  (let ((start (point)))
-    (condition-case ()
-        (let ((bounds (bibtex-enclosing-field)))
-          (goto-char (bibtex-end-of-field bounds))
-          (forward-char 2))
-      (error
-       (goto-char start)
-       (end-of-line)
-       (forward-char))))
-  (bibtex-find-text begin))
-
-(defun bibtex-find-text (&optional begin noerror)
-  "Move point to end of text of current BibTeX field.
+      t))) ; return `t' (i.e., buffer is valid)
+
+(defun bibtex-next-field (begin &optional comma)
+  "Move point to end of text of next BibTeX field or entry head.
+With prefix BEGIN non-nil, move point to its beginning.  Optional arg COMMA
+is as in `bibtex-enclosing-field'.  It is t for interactive calls."
+  (interactive (list current-prefix-arg t))
+  (let ((bounds (bibtex-find-text-internal t nil comma))
+        end-of-entry)
+    (if (not bounds)
+        (setq end-of-entry t)
+      (goto-char (nth 3 bounds))
+      (if (assoc-string (car bounds) '("@String" "@Preamble") t)
+          (setq end-of-entry t)
+        ;; BibTeX key or field
+        (if (looking-at ",[ \t\n]*") (goto-char (match-end 0)))
+        ;; end of entry
+        (if (looking-at "[)}][ \t\n]*") (setq end-of-entry t))))
+    (if (and end-of-entry
+             (re-search-forward bibtex-any-entry-maybe-empty-head nil t))
+      (goto-char (match-beginning 0)))
+    (bibtex-find-text begin nil bibtex-help-message)))
+
+(defun bibtex-find-text (&optional begin noerror help comma)
+  "Move point to end of text of current BibTeX field or entry head.
 With optional prefix BEGIN non-nil, move point to its beginning.
 Unless NOERROR is non-nil, an error is signaled if point is not
-on a BibTeX field."
-  (interactive "P")
-  (let* ((pnt (point))
-         (_ (bibtex-inside-field))
-         (bounds (bibtex-enclosing-field t)))
-    (beginning-of-line)
+on a BibTeX field.  If optional arg HELP is non-nil print help message.
+When called interactively, the value of HELP is `bibtex-help-message'.
+Optional arg COMMA is as in `bibtex-enclosing-field'.  It is t for
+interactive calls."
+  (interactive (list current-prefix-arg nil bibtex-help-message t))
+  (let ((bounds (bibtex-find-text-internal t nil comma)))
     (cond (bounds
            (if begin
-               (progn (goto-char (bibtex-start-of-text-in-field bounds))
+               (progn (goto-char (nth 1 bounds))
                       (if (looking-at "[{\"]")
                           (forward-char)))
-             (goto-char (bibtex-end-of-text-in-field bounds))
-             (if (or (= (preceding-char) ?})
-                     (= (preceding-char) ?\"))
+             (goto-char (nth 2 bounds))
+             (if (memq (preceding-char) '(?} ?\"))
                  (forward-char -1)))
-           (if bibtex-help-message
-               (bibtex-print-help-message)))
-          ((setq bounds (bibtex-parse-string))
-           (goto-char (if begin
-                          (1+ (bibtex-start-of-text-in-string bounds))
-                        (1- (bibtex-end-of-text-in-string bounds)))))
-          ((looking-at bibtex-entry-maybe-empty-head)
-           (goto-char (if begin
-                          (match-beginning bibtex-key-in-head)
-                        (match-end 0))))
-          (t
-           (goto-char pnt)
-           (unless noerror (error "Not on BibTeX field"))))))
-
-(defun bibtex-remove-OPT-or-ALT ()
+           (if help (bibtex-print-help-message (car bounds))))
+          ((not noerror) (error "Not on BibTeX field")))))
+
+(defun bibtex-find-text-internal (&optional noerror subfield comma)
+  "Find text part of current BibTeX field or entry head.
+Return list (NAME START-TEXT END-TEXT END STRING-CONST) with field
+or entry name, start and end of text, and end of field or entry head.
+STRING-CONST is a flag which is non-nil if current subfield delimited by #
+is a BibTeX string constant.  Return value is nil if field or entry head
+are not found.
+If optional arg NOERROR is non-nil, an error message is suppressed
+if text is not found.  If optional arg SUBFIELD is non-nil START-TEXT
+and END-TEXT correspond to the current subfield delimited by #.
+Optional arg COMMA is as in `bibtex-enclosing-field'."
+  (save-excursion
+    (let ((pnt (point))
+          (bounds (bibtex-enclosing-field comma t))
+          (case-fold-search t)
+          name start-text end-text end failure done no-sub string-const)
+      (bibtex-beginning-of-entry)
+      (cond (bounds
+             (setq name (bibtex-name-in-field bounds t)
+                   start-text (bibtex-start-of-text-in-field bounds)
+                   end-text (bibtex-end-of-text-in-field bounds)
+                   end (bibtex-end-of-field bounds)))
+            ;; @String
+            ((setq bounds (bibtex-parse-string t))
+             (if (<= pnt (bibtex-end-of-string bounds))
+                 (setq name "@String" ;; not a field name!
+                       start-text (bibtex-start-of-text-in-string bounds)
+                       end-text (bibtex-end-of-text-in-string bounds)
+                       end (bibtex-end-of-string bounds))
+               (setq failure t)))
+            ;; @Preamble
+            ((setq bounds (bibtex-parse-preamble))
+             (if (<= pnt (bibtex-end-of-string bounds))
+                 (setq name "@Preamble" ;; not a field name!
+                       start-text (bibtex-start-of-text-in-string bounds)
+                       end-text (bibtex-end-of-text-in-string bounds)
+                       end (bibtex-end-of-string bounds))
+               (setq failure t)))
+            ;; BibTeX head
+            ((looking-at bibtex-entry-maybe-empty-head)
+             (goto-char (match-end 0))
+             (if comma (save-match-data
+                         (re-search-forward "\\=[ \t\n]*," nil t)))
+             (if (<= pnt (point))
+                 (setq name (match-string-no-properties bibtex-type-in-head)
+                       start-text (or (match-beginning bibtex-key-in-head)
+                                 (match-end 0))
+                       end-text (or (match-end bibtex-key-in-head)
+                               (match-end 0))
+                       end end-text
+                       no-sub t) ; subfields do not make sense
+               (setq failure t)))
+            (t (setq failure t)))
+      (when (and subfield (not failure))
+        (setq failure no-sub)
+        (unless failure
+          (goto-char start-text)
+          (while (not done)
+            (if (or (prog1 (looking-at bibtex-field-const)
+                      (setq end-text (match-end 0)
+                            string-const t))
+                    (prog1 (setq bounds (bibtex-parse-field-string))
+                      (setq end-text (cdr bounds)
+                            string-const nil)))
+                (progn
+                  (if (and (<= start-text pnt) (<= pnt end-text))
+                      (setq done t)
+                    (goto-char end-text))
+                  (if (looking-at "[ \t\n]*#[ \t\n]*")
+                      (setq start-text (goto-char (match-end 0)))))
+              (setq done t failure t)))))
+      (cond ((not failure)
+             (list name start-text end-text end string-const))
+            ((and no-sub (not noerror))
+             (error "Not on text part of BibTeX field"))
+            ((not noerror) (error "Not on BibTeX field"))))))
+
+(defun bibtex-remove-OPT-or-ALT (&optional comma)
   "Remove the string starting optional/alternative fields.
-Align text and go thereafter to end of text."
-  (interactive)
-  (bibtex-inside-field)
+Align text and go thereafter to end of text.  Optional arg COMMA
+is as in `bibtex-enclosing-field'.  It is t for interactive calls."
+  (interactive (list t))
   (let ((case-fold-search t)
-        (bounds (bibtex-enclosing-field)))
+        (bounds (bibtex-enclosing-field comma)))
     (save-excursion
       (goto-char (bibtex-start-of-name-in-field bounds))
       (when (looking-at "OPT\\|ALT")
@@ -3674,35 +3984,31 @@ Align text and go thereafter to end of text."
         (delete-horizontal-space)
         (if bibtex-align-at-equal-sign
             (insert " ")
-          (indent-to-column bibtex-text-indentation))))
-    (bibtex-inside-field)))
-
-(defun bibtex-remove-delimiters ()
-  "Remove \"\" or {} around string."
-  (interactive)
-  (save-excursion
-    (bibtex-inside-field)
-    (let* ((bounds (bibtex-enclosing-field))
-          (end (bibtex-end-of-text-in-field bounds))
-          (start (bibtex-start-of-text-in-field bounds)))
-      (if (memq (char-before end) '(?\} ?\"))
-         (delete-region (1- end) end))
-      (if (memq (char-after start) '(?\{ ?\"))
-         (delete-region start (1+ start))))))
-
-(defun bibtex-kill-field (&optional copy-only)
+          (indent-to-column bibtex-text-indentation))))))
+
+(defun bibtex-remove-delimiters (&optional comma)
+  "Remove \"\" or {} around current BibTeX field text.
+Optional arg COMMA is as in `bibtex-enclosing-field'.  It is t for
+interactive calls."
+  (interactive (list t))
+  (let ((bounds (bibtex-find-text-internal nil t comma)))
+    (unless (nth 4 bounds)
+      (delete-region (1- (nth 2 bounds)) (nth 2 bounds))
+      (delete-region (nth 1 bounds) (1+ (nth 1 bounds))))))
+
+(defun bibtex-kill-field (&optional copy-only comma)
   "Kill the entire enclosing BibTeX field.
 With prefix arg COPY-ONLY, copy the current field to `bibtex-field-kill-ring',
-but do not actually kill it."
-  (interactive "P")
+but do not actually kill it.  Optional arg COMMA is as in
+`bibtex-enclosing-field'.  It is t for interactive calls."
+  (interactive (list current-prefix-arg t))
   (save-excursion
-    (bibtex-inside-field)
     (let* ((case-fold-search t)
-           (bounds (bibtex-enclosing-field))
+           (bounds (bibtex-enclosing-field comma))
            (end (bibtex-end-of-field bounds))
            (beg (bibtex-start-of-field bounds)))
       (goto-char end)
-      (skip-chars-forward " \t\n,")
+      (skip-chars-forward ",")
       (push (list (bibtex-name-in-field bounds) nil
                   (bibtex-text-in-field-bounds bounds))
             bibtex-field-kill-ring)
@@ -3715,10 +4021,12 @@ but do not actually kill it."
         (delete-region beg end))))
   (setq bibtex-last-kill-command 'field))
 
-(defun bibtex-copy-field-as-kill ()
-  "Copy the BibTeX field at point to the kill ring."
-  (interactive)
-  (bibtex-kill-field t))
+(defun bibtex-copy-field-as-kill (&optional comma)
+  "Copy the BibTeX field at point to the kill ring.
+Optional arg COMMA is as in `bibtex-enclosing-field'.  It is t for
+interactive calls."
+  (interactive (list t))
+  (bibtex-kill-field t comma))
 
 (defun bibtex-kill-entry (&optional copy-only)
   "Kill the entire enclosing BibTeX entry.
@@ -3730,7 +4038,7 @@ but do not actually kill it."
            (beg (bibtex-beginning-of-entry))
            (end (progn (bibtex-end-of-entry)
                        (if (re-search-forward
-                            bibtex-entry-maybe-empty-head nil 'move)
+                            bibtex-any-entry-maybe-empty-head nil 'move)
                            (goto-char (match-beginning 0)))
                        (point))))
       (push (buffer-substring-no-properties beg end)
@@ -3753,15 +4061,15 @@ but do not actually kill it."
   "Reinsert the last BibTeX item.
 More precisely, reinsert the field or entry killed or yanked most recently.
 With argument N, reinsert the Nth most recently killed BibTeX item.
-See also the command \\[bibtex-yank-pop]]."
+See also the command \\[bibtex-yank-pop]."
   (interactive "*p")
-  (bibtex-insert-kill (1- n))
+  (bibtex-insert-kill (1- n) t)
   (setq this-command 'bibtex-yank))
 
 (defun bibtex-yank-pop (n)
   "Replace just-yanked killed BibTeX item with a different item.
 This command is allowed only immediately after a `bibtex-yank' or a
-`bibtex-yank-pop'.  At such a time, the region contains a reinserted
+`bibtex-yank-pop'.  In this case, the region contains a reinserted
 previously killed BibTeX item.  `bibtex-yank-pop' deletes that item
 and inserts in its place a different killed BibTeX item.
 
@@ -3777,18 +4085,19 @@ comes the newest one."
   (setq this-command 'bibtex-yank)
   (let ((inhibit-read-only t))
     (delete-region (point) (mark t))
-    (bibtex-insert-kill n)))
-
-(defun bibtex-empty-field ()
-  "Delete the text part of the current field, replace with empty text."
-  (interactive)
-  (bibtex-inside-field)
-  (let ((bounds (bibtex-enclosing-field)))
+    (bibtex-insert-kill n t)))
+
+(defun bibtex-empty-field (&optional comma)
+  "Delete the text part of the current field, replace with empty text.
+Optional arg COMMA is as in `bibtex-enclosing-field'.  It is t for
+interactive calls."
+  (interactive (list t))
+  (let ((bounds (bibtex-enclosing-field comma)))
     (goto-char (bibtex-start-of-text-in-field bounds))
     (delete-region (point) (bibtex-end-of-text-in-field bounds))
-    (insert (concat (bibtex-field-left-delimiter)
-                    (bibtex-field-right-delimiter)) )
-    (bibtex-find-text t)))
+    (insert (bibtex-field-left-delimiter)
+            (bibtex-field-right-delimiter))
+    (bibtex-find-text t nil bibtex-help-message)))
 
 (defun bibtex-pop-previous (arg)
   "Replace text of current field with the similar field in previous entry.
@@ -3815,12 +4124,13 @@ begin on separate lines prior to calling `bibtex-clean-entry' or if
 Don't call `bibtex-clean-entry' on @Preamble entries.
 At end of the cleaning process, the functions in
 `bibtex-clean-entry-hook' are called with region narrowed to entry."
-  ;; Opt. arg called-by-reformat is t if bibtex-clean-entry
-  ;; is called by bibtex-reformat
+  ;; Opt. arg CALLED-BY-REFORMAT is t if `bibtex-clean-entry'
+  ;; is called by `bibtex-reformat'
   (interactive "P")
   (let ((case-fold-search t)
         (start (bibtex-beginning-of-entry))
-        (_ (looking-at bibtex-entry-maybe-empty-head))
+        (_ (or (looking-at bibtex-any-entry-maybe-empty-head)
+              (error "Not inside a BibTeX entry")))
         (entry-type (bibtex-type-in-head))
         (key (bibtex-key-in-head)))
     ;; formatting
@@ -3834,7 +4144,7 @@ At end of the cleaning process, the functions in
     ;; set key
     (when (or new-key (not key))
       (setq key (bibtex-generate-autokey))
-      ;; Sometimes bibtex-generate-autokey returns an empty string
+      ;; Sometimes `bibtex-generate-autokey' returns an empty string
       (if (or bibtex-autokey-edit-before-use (string= "" key))
           (setq key (if (eq entry-type 'string)
                         (bibtex-read-string-key key)
@@ -3884,7 +4194,7 @@ At end of the cleaning process, the functions in
                (if (and (listp bibtex-strings)
                         (not (assoc key bibtex-strings)))
                    (push (cons key (bibtex-text-in-string
-                                    (save-excursion (bibtex-parse-string)) t))
+                                    (bibtex-parse-string) t))
                            bibtex-strings)))
               ;; We have a normal entry.
               ((listp bibtex-reference-keys)
@@ -3912,43 +4222,41 @@ At end of the cleaning process, the functions in
 If JUSTIFY is non-nil justify as well.
 If optional arg MOVE is non-nil move point to end of field."
   (let ((end-field (copy-marker (bibtex-end-of-field bounds))))
-    (goto-char (bibtex-start-of-field bounds))
-    (if justify
-        (progn
-          (forward-char)
-          (bibtex-delete-whitespace)
-          (open-line 1)
-          (forward-char)
-          (indent-to-column (+ bibtex-entry-offset
-                               bibtex-field-indentation))
-          (re-search-forward "[ \t\n]*=" end-field)
-          (replace-match "=")
-          (forward-char -1)
-          (if bibtex-align-at-equal-sign
-              (indent-to-column
-               (+ bibtex-entry-offset (- bibtex-text-indentation 2)))
-            (insert " "))
-          (forward-char)
-          (bibtex-delete-whitespace)
-          (if bibtex-align-at-equal-sign
-              (insert " ")
-            (indent-to-column bibtex-text-indentation)))
-      (re-search-forward "[ \t\n]*=[ \t\n]*" end-field))
-    (while (re-search-forward "[ \t\n]+" end-field 'move)
-      (replace-match " "))
-    (do-auto-fill)
+    (if (not justify)
+        (goto-char (bibtex-start-of-text-in-field bounds))
+      (goto-char (bibtex-start-of-field bounds))
+      (forward-char) ; leading comma
+      (bibtex-delete-whitespace)
+      (open-line 1)
+      (forward-char)
+      (indent-to-column (+ bibtex-entry-offset
+                           bibtex-field-indentation))
+      (re-search-forward "[ \t\n]*=" end-field)
+      (replace-match "=")
+      (forward-char -1)
+      (if bibtex-align-at-equal-sign
+          (indent-to-column
+           (+ bibtex-entry-offset (- bibtex-text-indentation 2)))
+        (insert " "))
+      (forward-char)
+      (bibtex-delete-whitespace)
+      (if bibtex-align-at-equal-sign
+          (insert " ")
+        (indent-to-column bibtex-text-indentation)))
+    ;; Paragraphs within fields are not preserved.  Bother?
+    (fill-region-as-paragraph (line-beginning-position) end-field
+                              default-justification nil (point))
     (if move (goto-char end-field))))
 
 (defun bibtex-fill-field (&optional justify)
   "Like \\[fill-paragraph], but fill current BibTeX field.
-Optional prefix arg JUSTIFY non-nil means justify as well.
+If optional prefix JUSTIFY is non-nil justify as well.
 In BibTeX mode this function is bound to `fill-paragraph-function'."
   (interactive "*P")
   (let ((pnt (copy-marker (point)))
-        (bounds (bibtex-enclosing-field)))
-    (when bounds
-      (bibtex-fill-field-bounds bounds justify)
-      (goto-char pnt))))
+        (bounds (bibtex-enclosing-field t)))
+    (bibtex-fill-field-bounds bounds justify)
+    (goto-char pnt)))
 
 (defun bibtex-fill-entry ()
   "Fill current BibTeX entry.
@@ -3959,14 +4267,16 @@ If `bibtex-align-at-equal-sign' is non-nil, align equal signs, too."
   (interactive "*")
   (let ((pnt (copy-marker (point)))
         (end (copy-marker (bibtex-end-of-entry)))
+        (beg (bibtex-beginning-of-entry)) ; move point
         bounds)
-    (bibtex-beginning-of-entry)
     (bibtex-delete-whitespace)
     (indent-to-column bibtex-entry-offset)
-    (while (setq bounds (bibtex-search-forward-field bibtex-field-name end))
+    (bibtex-beginning-first-field beg)
+    (while (setq bounds (bibtex-parse-field))
       (bibtex-fill-field-bounds bounds t t))
     (if (looking-at ",")
         (forward-char))
+    (skip-chars-backward " \t\n")
     (bibtex-delete-whitespace)
     (open-line 1)
     (forward-char)
@@ -3976,18 +4286,19 @@ If `bibtex-align-at-equal-sign' is non-nil, align equal signs, too."
 (defun bibtex-realign ()
   "Realign BibTeX entries such that they are separated by one blank line."
   (goto-char (point-min))
-  (let ((case-fold-search t))
-    ;; No blank lines prior to the first valid entry if there no
+  (let ((case-fold-search t)
+        (entry-type (concat "[ \t\n]*\\(" bibtex-entry-type "\\)")))
+    ;; No blank lines prior to the first entry if there no
     ;; non-white characters in front of it.
-    (when (looking-at bibtex-valid-entry-whitespace-re)
+    (when (looking-at entry-type)
       (replace-match "\\1"))
-    ;; Valid entries are separated by one blank line.
-    (while (re-search-forward bibtex-valid-entry-whitespace-re nil t)
+    ;; Entries are separated by one blank line.
+    (while (re-search-forward entry-type nil t)
       (replace-match "\n\n\\1"))
-    ;; One blank line past the last valid entry if it is followed by
+    ;; One blank line past the last entry if it is followed by
     ;; non-white characters, no blank line otherwise.
     (beginning-of-line)
-    (when (re-search-forward bibtex-valid-entry-re nil t)
+    (when (re-search-forward bibtex-entry-type nil t)
       (bibtex-end-of-entry)
       (bibtex-delete-whitespace)
       (open-line (if (eobp) 1 2)))))
@@ -4017,15 +4328,19 @@ If mark is active reformat entries in region, if not in whole buffer."
                                    (,(concat (if bibtex-comma-after-last-field "Insert" "Remove")
                                              " comma at end of entry? ") . 'last-comma)
                                    ("Replace double page dashes by single ones? " . 'page-dashes)
+                                   ("Delete whitespace at the beginning and end of fields? " . 'whitespace)
                                    ("Inherit booktitle? " . 'inherit-booktitle)
                                    ("Force delimiters? " . 'delimiters)
-                                   ("Unify case of entry types and field names? " . 'unify-case))))))
+                                   ("Unify case of entry types and field names? " . 'unify-case)
+                                   ("Enclose parts of field entries by braces? " . 'braces)
+                                   ("Replace parts of field entries by string constants? " . 'strings))))))
                 ;; Do not include required-fields because `bibtex-reformat'
                 ;; cannot handle the error messages of `bibtex-format-entry'.
                 ;; Use `bibtex-validate' to check for required fields.
                 ((eq t bibtex-entry-format)
                  '(realign opts-or-alts numerical-fields delimiters
-                           last-comma page-dashes unify-case inherit-booktitle))
+                           last-comma page-dashes unify-case inherit-booktitle
+                           whitespace braces strings))
                 (t
                  (remove 'required-fields (push 'realign bibtex-entry-format)))))
          (reformat-reference-keys
@@ -4038,8 +4353,7 @@ If mark is active reformat entries in region, if not in whole buffer."
          bibtex-autokey-edit-before-use)
 
     (save-restriction
-      (narrow-to-region (if mark-active (region-beginning) (point-min))
-                        (if mark-active (region-end) (point-max)))
+      (if mark-active (narrow-to-region (region-beginning) (region-end)))
       (if (memq 'realign bibtex-entry-format)
           (bibtex-realign))
       (bibtex-progress-message "Formatting" 1)
@@ -4066,31 +4380,30 @@ entries from minibuffer."
   (message "Starting to validate buffer...")
   (sit-for 1 nil t)
   (bibtex-realign)
-  (message
-   "If errors occur, correct them and call `bibtex-convert-alien' again")
-  (sit-for 5 nil t)
-  (deactivate-mark)  ; So bibtex-validate works on the whole buffer.
-  (when (let (bibtex-maintain-sorted-entries)
-          (bibtex-validate))
+  (deactivate-mark)  ; So `bibtex-validate' works on the whole buffer.
+  (if (not (let (bibtex-maintain-sorted-entries)
+             (bibtex-validate)))
+      (message "Correct errors and call `bibtex-convert-alien' again")
     (message "Starting to reformat entries...")
     (sit-for 2 nil t)
     (bibtex-reformat read-options)
     (goto-char (point-max))
-    (message "Buffer is now parsable. Please save it.")))
+    (message "Buffer is now parsable.  Please save it.")))
 
 (defun bibtex-complete ()
   "Complete word fragment before point according to context.
 If point is inside key or crossref field perform key completion based on
 `bibtex-reference-keys'.  Inside a month field perform key completion
 based on `bibtex-predefined-month-strings'.  Inside any other field
-perform string completion based on `bibtex-strings'.  An error is
-signaled if point is outside key or BibTeX field."
+\(including a String or Preamble definition) perform string completion
+based on `bibtex-strings'.
+An error is signaled if point is outside key or BibTeX field."
   (interactive)
   (let ((pnt (point))
         (case-fold-search t)
         bounds name compl)
     (save-excursion
-      (if (and (setq bounds (bibtex-enclosing-field t))
+      (if (and (setq bounds (bibtex-enclosing-field nil t))
                (>= pnt (bibtex-start-of-text-in-field bounds))
                (<= pnt (bibtex-end-of-text-in-field bounds)))
           (setq name (bibtex-name-in-field bounds t)
@@ -4101,22 +4414,22 @@ signaled if point is outside key or BibTeX field."
                              ;; point is in month field
                              bibtex-predefined-month-strings)
                             ;; point is in other field
-                            (t (if (listp bibtex-strings)
-                                   bibtex-strings
-                                 ;; so that bibtex-complete-string-cleanup
-                                 ;; can do its job
-                                 (bibtex-parse-strings
-                                  (bibtex-string-files-init))))))
+                            (t (bibtex-strings))))
         (bibtex-beginning-of-entry)
-        (cond ((and (looking-at bibtex-string-maybe-empty-head)
-                    ;; point is inside a string key
-                    (or (and (match-beginning bibtex-key-in-head)
-                             (>= pnt (match-beginning bibtex-key-in-head))
-                             (<= pnt (match-end bibtex-key-in-head)))
-                        ;; or point is on empty string key
-                        (and (not (match-beginning bibtex-key-in-head))
-                             (= pnt (match-end 0)))))
-               (setq compl 'string))
+        (cond ((setq bounds (bibtex-parse-string t))
+               ;; point is inside a @String key
+               (cond ((and (>= pnt (nth 1 (car bounds)))
+                           (<= pnt (nth 2 (car bounds))))
+                      (setq compl 'string))
+                     ;; point is inside a @String field
+                     ((and (>= pnt (bibtex-start-of-text-in-string bounds))
+                           (<= pnt (bibtex-end-of-text-in-string bounds)))
+                      (setq compl (bibtex-strings)))))
+              ;; point is inside a @Preamble field
+              ((setq bounds (bibtex-parse-preamble))
+               (if (and (>= pnt (bibtex-start-of-text-in-string bounds))
+                        (<= pnt (bibtex-end-of-text-in-string bounds)))
+                   (setq compl (bibtex-strings))))
               ((and (looking-at bibtex-entry-maybe-empty-head)
                     ;; point is inside a key
                     (or (and (match-beginning bibtex-key-in-head)
@@ -4129,24 +4442,31 @@ signaled if point is outside key or BibTeX field."
 
     (cond ((eq compl 'key)
            ;; key completion: no cleanup needed
+           (setq choose-completion-string-functions nil)
            (let (completion-ignore-case)
              (bibtex-complete-internal (bibtex-global-key-alist))))
 
           ((eq compl 'crossref-key)
            ;; crossref key completion
+           ;;
+           ;; If we quit the *Completions* buffer without requesting
+           ;; a completion, `choose-completion-string-functions' is still
+           ;; non-nil.  Therefore, `choose-completion-string-functions' is
+           ;; always set (either to non-nil or nil) when a new completion
+           ;; is requested.
            (let (completion-ignore-case)
              (setq choose-completion-string-functions
                    (lambda (choice buffer mini-p base-size)
-                     (let ((choose-completion-string-functions nil))
-                       (choose-completion-string choice buffer base-size))
+                     (setq choose-completion-string-functions nil)
+                     (choose-completion-string choice buffer base-size)
                      (bibtex-complete-crossref-cleanup choice)
-                     ;; return t (needed by choose-completion-string-functions)
-                     t))
-             (bibtex-complete-crossref-cleanup (bibtex-complete-internal
-                                                (bibtex-global-key-alist)))))
+                     t)) ; needed by choose-completion-string-functions
+             (bibtex-complete-crossref-cleanup
+              (bibtex-complete-internal (bibtex-global-key-alist)))))
 
           ((eq compl 'string)
            ;; string key completion: no cleanup needed
+           (setq choose-completion-string-functions nil)
            (let ((completion-ignore-case t))
              (bibtex-complete-internal bibtex-strings)))
 
@@ -4155,15 +4475,15 @@ signaled if point is outside key or BibTeX field."
            (let ((completion-ignore-case t))
              (setq choose-completion-string-functions
                    `(lambda (choice buffer mini-p base-size)
-                      (let ((choose-completion-string-functions nil))
-                        (choose-completion-string choice buffer base-size))
+                      (setq choose-completion-string-functions nil)
+                      (choose-completion-string choice buffer base-size)
                       (bibtex-complete-string-cleanup choice ',compl)
-                      ;; return t (needed by choose-completion-string-functions)
-                      t))
+                      t)) ; needed by `choose-completion-string-functions'
              (bibtex-complete-string-cleanup (bibtex-complete-internal compl)
                                              compl)))
 
-          (t (error "Point outside key or BibTeX field")))))
+          (t (setq choose-completion-string-functions nil)
+             (error "Point outside key or BibTeX field")))))
 
 (defun bibtex-Article ()
   "Insert a new BibTeX @Article entry; see also `bibtex-entry'."
@@ -4261,59 +4581,108 @@ signaled if point is outside key or BibTeX field."
   (bibtex-move-outside-of-entry)
   (indent-to-column bibtex-entry-offset)
   (insert "@Preamble"
-          (bibtex-entry-left-delimiter))
+          (bibtex-entry-left-delimiter)
+          (bibtex-field-left-delimiter))
   (let ((endpos (point)))
-    (insert (bibtex-entry-right-delimiter)
+    (insert (bibtex-field-right-delimiter)
+            (bibtex-entry-right-delimiter)
             "\n")
     (goto-char endpos)))
 
-(defun bibtex-url (&optional pos)
+(defun bibtex-url (&optional pos no-browse)
   "Browse a URL for the BibTeX entry at point.
 Optional POS is the location of the BibTeX entry.
 The URL is generated using the schemes defined in `bibtex-generate-url-list'
-\(see there\).  Then the URL is passed to `browse-url'."
+\(see there\).  If multiple schemes match for this entry, or the same scheme
+matches more than once, use the one for which the first step's match is the
+closest to POS.  The URL is passed to `browse-url' unless NO-BROWSE is t.
+Return the URL or nil if none can be generated."
   (interactive)
+  (unless pos (setq pos (point)))
   (save-excursion
-    (if pos (goto-char pos))
+    (goto-char pos)
     (bibtex-beginning-of-entry)
-    (let ((fields-alist (bibtex-parse-entry))
+    (let ((end (save-excursion (bibtex-end-of-entry)))
+          (fields-alist (save-excursion (bibtex-parse-entry t)))
           ;; Always ignore case,
           (case-fold-search t)
-          (lst bibtex-generate-url-list)
-          field url scheme)
-      (while (setq scheme (pop lst))
-        (when (and (setq field (cdr (assoc-string (caar scheme)
-                                                 fields-alist t)))
-                   ;; Always remove field delimiters
-                   (progn (setq field (bibtex-remove-delimiters-string field))
-                          (string-match (cdar scheme) field)))
-          (setq lst nil)
-         (if (null (cdr scheme))
-             (setq url (match-string 0 field)))
-          (dolist (step (cdr scheme))
-            (cond ((stringp step)
-                   (setq url (concat url step)))
-                  ((setq field (cdr (assoc-string (car step) fields-alist t)))
-                   ;; Always remove field delimiters
-                   (setq field (bibtex-remove-delimiters-string field))
-                   (if (string-match (nth 1 step) field)
-                       (setq field (cond
-                                    ((functionp (nth 2 step))
-                                     (funcall (nth 2 step) field))
-                                    ((numberp (nth 2 step))
-                                     (match-string (nth 2 step) field))
-                                    (t
-                                     (replace-match (nth 2 step) t nil field))))
-                     ;; If the scheme is set up correctly,
-                     ;; we should never reach this point
-                     (error "Match failed: %s" field))
-                   (setq url (concat url field)))
-                  ;; If the scheme is set up correctly,
-                  ;; we should never reach this point
-                  (t (error "Step failed: %s" step))))
-          (message "%s" url)
-          (browse-url url)))
-      (unless url (message "No URL known.")))))
+          text url scheme obj fmt fl-match step)
+      ;; The return value of `bibtex-parse-entry' (i.e., FIELDS-ALIST)
+      ;; is always used to generate the URL.  However, if the BibTeX
+      ;; entry contains more than one URL, we have multiple matches
+      ;; for the first step defining the generation of the URL.
+      ;; Therefore, we try to initiate the generation of the URL
+      ;; based on the match of `bibtex-font-lock-url' that is the
+      ;; closest to POS.  If that fails (no match found) we try to
+      ;; initiate the generation of the URL based on the properly
+      ;; concatenated CONTENT of the field as returned by
+      ;; `bibtex-text-in-field-bounds'.  The latter approach can
+      ;; differ from the former because `bibtex-font-lock-url' uses
+      ;; the buffer itself.
+      (while (bibtex-font-lock-url end t)
+        (push (list (bibtex-dist pos (match-beginning 0) (match-end 0))
+                    (match-beginning 0)
+                    (buffer-substring-no-properties
+                     (match-beginning 0) (match-end 0)))
+              fl-match)
+        ;; `bibtex-font-lock-url' moves point to end of match.
+        (forward-char))
+      (when fl-match
+        (setq fl-match (car (sort fl-match (lambda (x y) (< (car x) (car y))))))
+        (goto-char (nth 1 fl-match))
+        (bibtex-beginning-of-field) (re-search-backward ",")
+        (let* ((bounds (bibtex-parse-field))
+               (name (bibtex-name-in-field bounds))
+               (content (bibtex-text-in-field-bounds bounds t))
+               (lst bibtex-generate-url-list))
+          ;; This match can fail when CONTENT differs from text in buffer.
+          (when (string-match (regexp-quote (nth 2 fl-match)) content)
+            ;; TEXT is the part of CONTENT that starts with the match
+            ;; of `bibtex-font-lock-url' we are looking for.
+            (setq text (substring content (match-beginning 0)))
+            (while (and (not url) (setq scheme (pop lst)))
+              ;; Verify the match of `bibtex-font-lock-url' by
+              ;; comparing with TEXT.
+              (when (and (bibtex-string= (caar scheme) name)
+                         (string-match (cdar scheme) text))
+                (setq url t scheme (cdr scheme)))))))
+
+      ;; If the match of `bibtex-font-lock-url' was not approved
+      ;; parse FIELDS-ALIST, i.e., the output of `bibtex-parse-entry'.
+      (unless url
+        (let ((lst bibtex-generate-url-list))
+          (while (and (not url) (setq scheme (pop lst)))
+            (when (and (setq text (cdr (assoc-string (caar scheme)
+                                                      fields-alist t)))
+                       (string-match (cdar scheme) text))
+              (setq url t scheme (cdr scheme))))))
+
+      (when url
+        (setq url (if (null scheme) (match-string 0 text)
+                    (if (stringp (car scheme))
+                        (setq fmt (pop scheme)))
+                    (dotimes (i (length scheme))
+                      (setq step (nth i scheme))
+                      ;; The first step shall use TEXT as obtained earlier.
+                      (unless (= i 0)
+                        (setq text (cdr (assoc-string (car step) fields-alist t))))
+                      (if (string-match (nth 1 step) text)
+                          (push (cond ((functionp (nth 2 step))
+                                       (funcall (nth 2 step) text))
+                                      ((numberp (nth 2 step))
+                                       (match-string (nth 2 step) text))
+                                      (t
+                                       (replace-match (nth 2 step) t nil text)))
+                                obj)
+                        ;; If SCHEME is set up correctly,
+                        ;; we should never reach this point
+                        (error "Match failed: %s" text)))
+                    (if fmt (apply 'format fmt (nreverse obj))
+                      (apply 'concat (nreverse obj)))))
+        (if (interactive-p) (message "%s" url))
+        (unless no-browse (browse-url url)))
+      (if (and (not url) (interactive-p)) (message "No URL known."))
+      url)))
 
 \f
 ;; Make BibTeX a Feature