]> code.delx.au - gnu-emacs/blobdiff - lisp/textmodes/bibtex.el
Merge from emacs--rel--22
[gnu-emacs] / lisp / textmodes / bibtex.el
index 161b5fbc126fda6d091c2e480254505c4e797956..377c90b7bed90d922cf7631abba9b88e3e61146f 100644 (file)
@@ -1,11 +1,11 @@
 ;;; bibtex.el --- BibTeX mode for GNU Emacs
 
-;; Copyright (C) 1992, 1994, 1995, 1996, 1997, 1998, 1999, 2002, 2003,
-;;   2004, 2005 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>
@@ -34,7 +34,7 @@
 ;;  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:
@@ -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")
@@ -178,6 +209,17 @@ to all entries not explicitly mentioned."
   :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'.")
@@ -595,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
@@ -604,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.
@@ -666,7 +743,7 @@ 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
@@ -675,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.
@@ -847,13 +926,25 @@ The following is a complex example, see http://link.aps.org/linkfaq.html.
                                (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"))))
 
 (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 as well, but since the
+;; `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.
 
@@ -1019,6 +1110,17 @@ The following is a complex example, see http://link.aps.org/linkfaq.html.
 \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.")
 
@@ -1049,6 +1151,7 @@ At most `bibtex-entry-kill-ring-max' items are kept here.")
   "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
@@ -1056,6 +1159,7 @@ Initialized from `bibtex-predefined-strings' and `bibtex-string-files'.")
   "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.")
@@ -1091,7 +1195,7 @@ The CDRs of the elements are t for header keys and nil for crossref keys.")
   "Regexp matching the name of a BibTeX field.")
 
 (defconst bibtex-name-part
-  (concat ",[ \t\n]*\\(" bibtex-field-name "\\)[ \t\n]*=")
+  (concat ",[ \t\n]*\\(" bibtex-field-name "\\)")
   "Regexp matching the name part of a BibTeX field.")
 
 (defconst bibtex-reference-key "[][[:alnum:].:;?!`'/*@+|()<>&_^$-]+"
@@ -1105,16 +1209,6 @@ The CDRs of the elements are t for header keys and nil for crossref keys.")
           (regexp-opt (mapcar 'car bibtex-entry-field-alist)) "\\)")
   "Regexp matching the name of a BibTeX entry.")
 
-(defvar bibtex-entry-type-whitespace
-  (concat "[ \t]*" bibtex-entry-type)
-  "Regexp matching the name of a BibTeX entry preceded by whitespace.")
-
-(defvar bibtex-entry-type-str
-  (concat "@[ \t]*\\(?:"
-          (regexp-opt (append '("String")
-                              (mapcar 'car bibtex-entry-field-alist))) "\\)")
-  "Regexp matching the name of a BibTeX entry (including @String).")
-
 (defvar bibtex-entry-head
   (concat "^[ \t]*\\("
           bibtex-entry-type
@@ -1132,15 +1226,18 @@ The CDRs of the elements are t for header keys and nil for crossref keys.")
           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-empty-field-re "\\`\\(\"\"\\|{}\\)\\'"
-  "Regexp matching the text part (as a string) of an empty field.")
-
 (defconst bibtex-string-type "^[ \t]*\\(@[ \t]*String\\)[ \t]*[({][ \t\n]*"
    "Regexp matching the name of a BibTeX String entry.")
 
@@ -1148,8 +1245,9 @@ The CDRs of the elements are t for header keys and nil for crossref keys.")
   (concat bibtex-string-type "\\(" bibtex-reference-key "\\)?")
   "Regexp matching the header line of a BibTeX String entry.")
 
-(defconst bibtex-preamble-prefix "[ \t]*@[ \t]*Preamble[ \t]*"
-  "Regexp matching the prefix part of a preamble.")
+(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) "\\)"
@@ -1168,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
@@ -1176,18 +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'.")
+  "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
-  (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))))
+(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'.")
@@ -1229,12 +1325,9 @@ 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 preceding
 BibTeX field as necessary."
-  (cond ((looking-at ",[ \t\n]*")
-         (let ((start (point)))
-           (goto-char (match-end 0))
-           (when (looking-at bibtex-field-name)
-             (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
@@ -1334,60 +1427,71 @@ the boundaries of the name and text parts of the field.  Do not move point."
   "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 or if nil by the end of the current
-entry.  Do not move point."
+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 bound
-          ;; If the search is bounded we need not worry we could overshoot.
-          ;; This is indeed the case 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)))
-        (let ((regexp (concat bibtex-name-part "\\|"
-                              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)))))))))
+      (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 nil the search is
+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
-    (save-excursion
-      (let ((name-part (concat ",[ \t\n]*\\(?:" name "\\)[ \t\n]*="))
-            (case-fold-search t)
-            bounds)
-        (unless bound (setq bound (save-excursion (bibtex-beginning-of-entry))))
-        (while (and (not bounds)
-                    (search-backward "," bound t)
-                    (looking-at name-part))
-          (setq bounds (bibtex-parse-field)))
-        bounds))))
+    (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.
@@ -1407,25 +1511,22 @@ 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 temp)
-          (goto-char (bibtex-start-of-text-in-field bounds))
+              content opoint)
           (while (< (setq opoint (point)) epoint)
-            (cond ((looking-at bibtex-field-const)
-                   (let ((mtch (match-string-no-properties 0)))
-                     (goto-char (match-end 0))
-                     (setq temp (if bibtex-expand-strings
-                                    (cdr (assoc-string mtch (bibtex-strings) t)))
-                           content (concat content (or temp mtch)))))
-
-                  ((setq temp (bibtex-parse-field-string))
-                   (setq content (concat content (buffer-substring-no-properties
-                                                  (1+ (car temp))
-                                                  (1- (cdr temp)))))
-                   (goto-char (cdr temp)))
-                  (t (error "Malformed text field")))
+            (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))
-          content))
+          (apply 'concat (nreverse content))))
     (buffer-substring-no-properties (bibtex-start-of-text-in-field bounds)
                                     (bibtex-end-of-text-in-field bounds))))
 
@@ -1434,19 +1535,15 @@ If `bibtex-expand-strings' is non-nil, also expand BibTeX strings."
 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 (point-max)))
-            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" (point-max)))))
-               (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.
@@ -1487,45 +1584,31 @@ character of the string entry.  Move point past BibTeX string entry."
               (nth 1 bounds)
               (match-end 0))))))
 
-(defun bibtex-parse-string ()
+(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.
-Do not move point."
-  (bibtex-parse-association 'bibtex-parse-string-prefix
-                            'bibtex-parse-string-postfix))
+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)
+      (let ((case-fold-search t) bounds)
+        (while (and (not bounds)
                     (search-forward-regexp bibtex-string-type 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 bibtex-string-type nil t))
-          (goto-char (match-beginning 0))
-          (setq boundaries (bibtex-parse-string)))
-        boundaries))))
+          (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"
+  "Return the key part of a BibTeX string defined via BOUNDS."
   (buffer-substring-no-properties (nth 1 (car bounds))
                                   (nth 2 (car bounds))))
 
@@ -1554,14 +1637,15 @@ If `bibtex-expand-strings' is non-nil, also expand BibTeX strings."
   (or (match-string-no-properties bibtex-key-in-head)
       empty))
 
-(defun bibtex-preamble-prefix (&optional delim)
-  "Parse the prefix part of a BibTeX Preamble.
-Point must be at beginning of prefix part.  If prefix is found move point
-to its end and return position of point.  If optional arg DELIM is non-nil,
-move past the opening delimiter.  If no preamble is found return nil."
+(defun bibtex-parse-preamble ()
+  "Parse BibTeX preamble.
+Point must be at beginning of preamble.  Do not move point."
   (let ((case-fold-search t))
-    (re-search-forward (concat "\\=" bibtex-preamble-prefix
-                               (if delim "[({][ \t\n]*")) nil 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))))))
 
 ;; Helper Functions
 
@@ -1579,6 +1663,35 @@ move past the opening delimiter.  If no preamble is found return nil."
   (+ (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.
@@ -1590,32 +1703,27 @@ 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-entry-type-whitespace)
-                        (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 (re-search-backward "^[ \t]*@" nil 'move))
-              (t (re-search-forward "\\=[ \t]*@" nil t) ;; don't be stuck
-                 (if (re-search-forward "^[ \t]*@" nil 'move)
-                     (goto-char (match-beginning 0)))))))
+      (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."
+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
@@ -1673,75 +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 a call to this function `match-data' corresponds to the head part
-of the entry, see regexp `bibtex-entry-head'.
-Ignore @String and @Preamble entries."
-  (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)))
-          (cond (found
-                 (goto-char (match-beginning 0))
-                 found)
-                ((not noerror)  ;; yell
-                 (error "Backward search of BibTeX entry failed"))
-                (t (if (eq noerror t) (goto-char pnt)) ;; don't move
-                   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]*("))
-                       ",?[ \t\n]*)" ;; entry opened with `('
-                     ",?[ \t\n]*}")) ;; entry opened with `{'
-                  bounds)
-              (skip-chars-forward " \t\n" bound)
-              ;; loop over all BibTeX fields
-              (while (and (setq bounds (bibtex-parse-field))
-                          (<= (bibtex-end-of-field bounds) bound))
-                (goto-char (bibtex-end-of-field bounds)))
-              ;; This matches the infix* part.
-              (when (and (looking-at entry-closer)
-                         (<= (match-end 0) bound))
-                (goto-char (match-end 0))
-                (setq found t)))))
-        (cond (found
-               (cons (match-beginning 0) (point)))
-              ((not noerror) ;; yell
-               (error "Search of BibTeX entry failed"))
-              (t (if (eq noerror t) (goto-char pnt)) ;; don't move
-                 nil))))))
-
-(defun bibtex-flash-head ()
+(defun bibtex-flash-head (prompt)
   "Flash at BibTeX entry head before point, if exists."
   (let ((case-fold-search t)
-        (pnt (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))
-        (setq flash (match-end bibtex-key-in-head))
         (if (pos-visible-in-window-p (point))
-            (sit-for 1)
-          (message "From: %s"
-                   (buffer-substring (point) flash)))))))
+            (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."
@@ -1772,66 +1824,53 @@ are ignored.  Return point"
   (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)))
-    (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)
-          (progn
-            (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'.
@@ -1849,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
@@ -1900,9 +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 (point-max)))
+        (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)))
@@ -1918,7 +1966,7 @@ Formats current entry according to variable `bibtex-entry-format'."
                  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.
 
@@ -1971,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
@@ -2040,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
@@ -2065,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.
@@ -2117,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)))
@@ -2149,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 ()
@@ -2179,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)
@@ -2191,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)
@@ -2211,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 ()
@@ -2231,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
@@ -2263,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.
 
@@ -2361,7 +2483,7 @@ for parsing BibTeX keys.  If parsing fails, try to set this variable to nil."
                              (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
@@ -2417,7 +2539,7 @@ Return alist of strings if parsing was completed, `aborted' otherwise."
           (setq bibtex-strings strings))))))
 
 (defun bibtex-strings ()
-  "Return `bibtex-strings'. Initialize this variable if necessary."
+  "Return `bibtex-strings'.  Initialize this variable if necessary."
   (if (listp bibtex-strings) bibtex-strings
     (bibtex-parse-strings (bibtex-string-files-init))))
 
@@ -2474,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
@@ -2536,35 +2658,43 @@ 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)
-                                     part-of-word))
-           (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
     (let ((abbr (cdr (if (stringp str)
                          (assoc-string str compl t)))))
@@ -2579,22 +2709,27 @@ Use `bibtex-summary-function' to generate summary."
              (bibtex-find-entry key t))
         (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)
-  (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"))))
+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?
+  ;; It would be neat to make this function customizable.  How?
   (if (looking-at bibtex-entry-maybe-empty-head)
-      (let* ((bibtex-autokey-name-case-convert 'identity)
+      (let* ((bibtex-autokey-name-case-convert-function 'identity)
              (bibtex-autokey-name-length 'infty)
              (bibtex-autokey-names 1)
              (bibtex-autokey-names-stretch 0)
@@ -2605,7 +2740,7 @@ Used as default value of `bibtex-summary-function'."
              (year (bibtex-autokey-get-year))
              (bibtex-autokey-titlewords 5)
              (bibtex-autokey-titlewords-stretch 2)
-             (bibtex-autokey-titleword-case-convert 'identity)
+             (bibtex-autokey-titleword-case-convert-function 'identity)
              (bibtex-autokey-titleword-length 5)
              (bibtex-autokey-titleword-separator " ")
              (title (bibtex-autokey-get-title))
@@ -2624,50 +2759,52 @@ Used as default value of `bibtex-summary-function'."
 (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'."
-  (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)))))
-  (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 ()
@@ -2679,16 +2816,17 @@ 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 (nth 1 bounds))
@@ -2697,17 +2835,18 @@ begins at the beginning of a line.  We use this function for font-locking."
                       (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 (car (pop lst))))
-         (setq found (and (bibtex-string= field (car url))
-                           (re-search-forward (cdr url) end t)
-                           (>= (match-beginning 0) pnt)))))
-      (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)
@@ -2728,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)
@@ -2846,7 +2998,7 @@ 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-mode-hooks 'bibtex-mode-hook))
@@ -2861,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))
@@ -2872,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)
@@ -2910,24 +3062,45 @@ 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))))))
+    (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.
@@ -2964,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)
@@ -3016,30 +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)
-  (let* ((case-fold-search t)
-         (type (save-excursion
-                 (bibtex-beginning-of-entry)
-                 (looking-at bibtex-any-entry-maybe-empty-head)
-                 (bibtex-type-in-head)))
-         comment field-list)
-    (cond ((bibtex-string= type "string")
-           (message "String definition"))
-          ((bibtex-string= type "preamble")
-           (message "Preamble definition"))
-          (t
-           (setq field-list (bibtex-field-list type)
-                 comment
-                 (assoc-string (bibtex-name-in-field (bibtex-enclosing-field) t)
-                               (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-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)
+           (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 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
@@ -3047,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))
@@ -3076,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).
@@ -3103,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-entry-type-whitespace)
-           (bibtex-search-entry t nil t)
-           (unless (equal (match-beginning 0) pnt)
-             (setq err t)))
-          ;; @String
-          ((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)))
-          ;; @Preamble
-          ((bibtex-preamble-prefix t)
-           (unless (bibtex-parse-string-postfix) ;; @String postfix OK
-             (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."
@@ -3159,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)
@@ -3188,7 +3359,7 @@ If mark is active count entries in region, if not in whole buffer."
   (interactive)
   (let ((bounds (save-excursion
                   (bibtex-beginning-of-entry)
-                  (bibtex-search-forward-field "abstract"))))
+                  (bibtex-search-forward-field "abstract" t))))
     (if bounds
         (ispell-region (bibtex-start-of-text-in-field bounds)
                        (bibtex-end-of-text-in-field bounds))
@@ -3216,12 +3387,24 @@ of the head of the entry found.  Return nil if no entry found."
           ;; Don't search CROSSREF-KEY if we don't need it.
           (if (eq bibtex-maintain-sorted-entries 'crossref)
               (let ((bounds (bibtex-search-forward-field
-                             "\\(OPT\\)?crossref")))
+                             "\\(OPT\\)?crossref" t)))
                 (list key
                       (if bounds (bibtex-text-in-field-bounds bounds t))
                       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).
@@ -3259,35 +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)
-    (bibtex-beginning-of-first-entry) ;; needed by `sort-subr'
-    (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")))
-              (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)
@@ -3298,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 precede 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
@@ -3313,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.
@@ -3342,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]*\\("
@@ -3366,6 +3580,7 @@ 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'.
 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)
@@ -3377,7 +3592,7 @@ 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)))
                   (bounds (save-excursion (goto-char (point-max))
@@ -3429,40 +3644,38 @@ Return t if test was successful, nil otherwise."
          error-list syntax-error)
     (save-excursion
       (save-restriction
-        (if mark-active
-            (narrow-to-region (region-beginning) (region-end)))
+        (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-entry-type-str))
-                  (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)
@@ -3498,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.
@@ -3521,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)
@@ -3565,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))))
@@ -3573,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'.
@@ -3613,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))))))
@@ -3631,37 +3839,41 @@ 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 nil bibtex-help-message))
-
-(defun bibtex-find-text (&optional begin noerror help)
-  "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.  If optional arg HELP is non-nil print help message.
-When called interactively, the value of HELP is `bibtex-help-message'."
-  (interactive (list current-prefix-arg nil bibtex-help-message))
-  (let ((pnt (point))
-        (bounds (bibtex-find-text-internal)))
-    (beginning-of-line)
+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 (nth 1 bounds))
@@ -3670,72 +3882,93 @@ When called interactively, the value of HELP is `bibtex-help-message'."
              (goto-char (nth 2 bounds))
              (if (memq (preceding-char) '(?} ?\"))
                  (forward-char -1)))
-           (if help (bibtex-print-help-message)))
-          ((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-find-text-internal (&optional noerror subfield)
-  "Find text part of current BibTeX field, @String or @Preamble.
-Return list (NAME START END) with field name, start and end of text
-or nil if 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 and END correspond
-to the current subfield delimited by #."
+           (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))
-          (_ (bibtex-inside-field))
-          (bounds (bibtex-enclosing-field t))
+          (bounds (bibtex-enclosing-field comma t))
           (case-fold-search t)
-          (bibtex-string-empty-key t)
-          name start end)
+          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 (bibtex-start-of-text-in-field bounds)
-                   end (bibtex-end-of-text-in-field bounds)))
+                   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))
-             (setq name "@String" ;; not a field name!
-                   start (bibtex-start-of-text-in-string bounds)
-                   end (bibtex-end-of-text-in-string bounds)))
+            ((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
-            ((and (bibtex-preamble-prefix t)
-                  (setq bounds (bibtex-parse-field-text)))
-             (setq name "@Preamble" ;; not a field name!
-                   start (car bounds)
-                   end (nth 1 bounds)))
-            (t (unless noerror (error "Not on BibTeX field"))))
-      (when (and start end subfield)
-        (goto-char start)
-        (let (done)
+            ((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 (match-end 0)))
+                      (setq end-text (match-end 0)
+                            string-const t))
                     (prog1 (setq bounds (bibtex-parse-field-string))
-                      (setq end (cdr bounds))))
+                      (setq end-text (cdr bounds)
+                            string-const nil)))
                 (progn
-                  (if (and (<= start pnt) (<= pnt end))
+                  (if (and (<= start-text pnt) (<= pnt end-text))
                       (setq done t)
-                    (goto-char end))
+                    (goto-char end-text))
                   (if (looking-at "[ \t\n]*#[ \t\n]*")
-                      (setq start (goto-char (match-end 0)))))
-              (unless noerror (error "Not on text part of BibTeX field"))
-              (setq done t start nil end nil)))))
-      (if (and start end)
-          (list name start end)))))
-
-(defun bibtex-remove-OPT-or-ALT ()
+                      (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")
@@ -3751,30 +3984,27 @@ 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 current BibTeX field text."
-  (interactive)
-  ;; `bibtex-find-text-internal' issues an error message if bounds is nil.
-  (let* ((bounds (bibtex-find-text-internal nil t))
-         (start (nth 1 bounds))
-         (end (nth 2 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)
@@ -3791,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.
@@ -3806,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)
@@ -3831,13 +4063,13 @@ 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]."
   (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.
 
@@ -3853,13 +4085,14 @@ 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 (bibtex-field-left-delimiter)
@@ -3891,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-any-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
@@ -3910,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)
@@ -3960,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)
@@ -3988,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))
-    ;; Paragraphs within fields are not preserved. Bother?
+    (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.
@@ -4035,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)
@@ -4094,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
@@ -4115,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)
@@ -4143,17 +4380,15 @@ 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.
@@ -4166,10 +4401,9 @@ An error is signaled if point is outside key or BibTeX field."
   (interactive)
   (let ((pnt (point))
         (case-fold-search t)
-        (bibtex-string-empty-key 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)
@@ -4182,7 +4416,7 @@ An error is signaled if point is outside key or BibTeX field."
                             ;; point is in other field
                             (t (bibtex-strings))))
         (bibtex-beginning-of-entry)
-        (cond ((setq bounds (bibtex-parse-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))))
@@ -4192,11 +4426,10 @@ An error is signaled if point is outside key or BibTeX field."
                            (<= pnt (bibtex-end-of-text-in-string bounds)))
                       (setq compl (bibtex-strings)))))
               ;; point is inside a @Preamble field
-              ((and (bibtex-preamble-prefix t)
-                    (setq bounds (bibtex-parse-field-text))
-                    (>= pnt (car bounds))
-                    (<= pnt (nth 1 bounds)))
-               (setq compl (bibtex-strings)))
+              ((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)
@@ -4209,24 +4442,31 @@ An error is 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)))
 
@@ -4235,15 +4475,15 @@ An error is 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'."
@@ -4349,47 +4589,100 @@ An error is signaled if point is outside key or BibTeX field."
             "\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)
-    ;; Always remove field delimiters
-    (let ((fields-alist (bibtex-parse-entry t))
+    (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 obj fmt)
-      (while (setq scheme (pop lst))
-        (when (and (setq field (cdr (assoc-string (caar scheme)
-                                                 fields-alist t)))
-                   (string-match (cdar scheme) field))
-          (setq lst nil
-                scheme (cdr scheme)
-                url (if (null scheme) (match-string 0 field)
-                      (if (stringp (car scheme))
-                          (setq fmt (pop scheme)))
-                      (dolist (step scheme)
-                        (setq field (cdr (assoc-string (car step) fields-alist t)))
-                        (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))
-                        (push field obj))
-                      (if fmt (apply 'format fmt (nreverse obj))
-                        (apply 'concat (nreverse obj)))))
-          (browse-url (message "%s" 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