Add 2011 to FSF/AIST copyright years.
[bpt/emacs.git] / lisp / textmodes / bibtex.el
index 6d2d8ae..191ceed 100644 (file)
@@ -1,7 +1,8 @@
 ;;; bibtex.el --- BibTeX mode for GNU Emacs
 
 ;; Copyright (C) 1992, 1994, 1995, 1996, 1997, 1998, 1999, 2001, 2002,
-;;   2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+;;   2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
+;;   Free Software Foundation, Inc.
 
 ;; Author: Stefan Schoef <schoef@offis.uni-oldenburg.de>
 ;;      Bengt Martensson <bengt@mathematik.uni-Bremen.de>
 ;;      Mike Newton <newton@gumby.cs.caltech.edu>
 ;;      Aaron Larson <alarson@src.honeywell.com>
 ;;      Dirk Herrmann <D.Herrmann@tu-bs.de>
-;; Maintainer: Roland Winkler <roland.winkler@physik.uni-erlangen.de>
+;; Maintainer: Roland Winkler <winkler@gnu.org>
 ;; Keywords: BibTeX, LaTeX, TeX
 
 ;; This file is part of GNU Emacs.
 
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; GNU Emacs is free software: you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 3, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -25,9 +26,7 @@
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING.  If not, write to the
-;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
 
 ;;; Commentary:
 
@@ -119,11 +118,12 @@ inherit-booktitle   If entry contains a crossref field and the booktitle
 realign             Realign entries, so that field texts and perhaps equal
                       signs (depending on the value of
                       `bibtex-align-at-equal-sign') begin in the same column.
+                      Also fill fields.
 last-comma          Add or delete comma on end of last field in entry,
                       according to value of `bibtex-comma-after-last-field'.
 delimiters          Change delimiters according to variables
                       `bibtex-field-delimiters' and `bibtex-entry-delimiters'.
-unify-case          Change case of entry and field names.
+unify-case          Change case of entry types 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
@@ -147,12 +147,24 @@ The value nil means do no formatting at all."
                       (const unify-case)
                       (const braces)
                       (const strings))))
+(put 'bibtex-entry-format 'safe-local-variable
+     (lambda (x)
+       (or (eq x t)
+           (let ((OK t))
+             (while (consp x)
+               (unless (memq (pop x)
+                             '(opts-or-alts required-fields numerical-fields
+                               page-dashes whitespace inherit-booktitle realign
+                               last-comma delimiters unify-case braces strings))
+                 (setq OK nil)))
+             (unless (null x) (setq OK nil))
+             OK))))
 
 (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]+\"."
+Space characters in REGEXP will be replaced by \"[ \\t\\n]+\"."
   :group 'bibtex
   :type '(repeat (list (repeat (string :tag "field name"))
                        (choice (regexp :tag "regexp")
@@ -163,7 +175,7 @@ Whitespace in REGEXP will be replaced by \"[ \\t\\n]+\"."
 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]+\"."
+Space characters in REGEXP will be replaced by \"[ \\t\\n]+\"."
   :group 'bibtex
   :type '(repeat (list (repeat (string :tag "field name"))
                        (regexp :tag "From regexp")
@@ -184,7 +196,7 @@ 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.
 entry-class  The entries are divided into classes according to their
-             entry name, see `bibtex-sort-entry-class'.  Within each class
+             entry type, see `bibtex-sort-entry-class'.  Within each class
              the entries are sorted alphabetically.
 See also `bibtex-sort-ignore-string-entries'."
   :group 'bibtex
@@ -200,15 +212,15 @@ See also `bibtex-sort-ignore-string-entries'."
   '(("String")
     (catch-all)
     ("Book" "Proceedings"))
-  "List of classes of BibTeX entry names, used for sorting entries.
+  "List of classes of BibTeX entry types, used for sorting entries.
 If value of `bibtex-maintain-sorted-entries' is `entry-class'
 entries are ordered according to the classes they belong to.  Each
-class contains a list of entry names.  An entry `catch-all' applies
+class contains a list of entry types.  An entry `catch-all' applies
 to all entries not explicitly mentioned."
-  :group 'BibTeX
+  :group 'bibtex
   :type '(repeat (choice :tag "Class"
                          (const :tag "catch-all" (catch-all))
-                         (repeat :tag "Entry name" string))))
+                         (repeat :tag "Entry type" string))))
 (put 'bibtex-sort-entry-class 'safe-local-variable
      (lambda (x) (let ((OK t))
               (while (consp x)
@@ -463,8 +475,8 @@ If parsing fails, try to set this variable to nil."
 
   "List of BibTeX entry types and their associated fields.
 List elements are triples
-\(ENTRY-NAME (REQUIRED OPTIONAL) (CROSSREF-REQUIRED CROSSREF-OPTIONAL)).
-ENTRY-NAME is the name of a BibTeX entry.  The remaining pairs contain
+\(ENTRY-TYPE (REQUIRED OPTIONAL) (CROSSREF-REQUIRED CROSSREF-OPTIONAL)).
+ENTRY-TYPE is the type of a BibTeX entry.  The remaining pairs contain
 the required and optional fields of the BibTeX entry.
 The second pair is used if a crossref field is present
 and the first pair is used if a crossref field is absent.
@@ -480,7 +492,7 @@ 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."
   :group 'bibtex
-  :type '(repeat (group (string :tag "Entry name")
+  :type '(repeat (group (string :tag "Entry type")
                         (group (repeat :tag "Required fields"
                                        (group (string :tag "Field")
                                               (string :tag "Comment")
@@ -735,11 +747,11 @@ See `bibtex-generate-autokey' for details."
 
 (defcustom bibtex-autokey-titleword-ignore
   '("A" "An" "On" "The" "Eine?" "Der" "Die" "Das"
-    "[^[:upper:]].*" ".*[^[:upper:]0-9].*")
+    "[^[:upper:]].*" ".*[^[:upper:][:lower:]0-9].*")
   "Determines words from the title that are not to be used in the key.
 Each item of the list is a regexp.  If a word of the title matches a
 regexp from that list, it is not included in the title part of the key.
-See `bibtex-generate-autokey' for details."
+Case is significant.  See `bibtex-generate-autokey' for details."
   :group 'bibtex-autokey
   :type '(repeat regexp))
 
@@ -796,7 +808,7 @@ See `bibtex-generate-autokey' for details."
   :type 'string)
 
 (defcustom bibtex-autokey-year-title-separator ":_"
-  "String to be put between name part and year part of key.
+  "String to be put between year part and title part of key.
 See `bibtex-generate-autokey' for details."
   :group 'bibtex-autokey
   :type 'string)
@@ -896,8 +908,9 @@ and with the `match-data' properly set.
 Case is always ignored.  Always remove the field delimiters.
 If `bibtex-expand-strings' is non-nil, BibTeX strings are expanded
 for generating the URL.
+Set this variable before loading BibTeX mode.
 
-The following is a complex example, see http://link.aps.org/linkfaq.html.
+The following is a complex example, see URL `http://link.aps.org/'.
 
    (((\"journal\" . \"\\\\=<\\(PR[ABCDEL]?\\|RMP\\)\\\\=>\")
      \"http://link.aps.org/abstract/%s/v%s/p%s\"
@@ -934,10 +947,12 @@ The following is a complex example, see http://link.aps.org/linkfaq.html.
 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."
+Used by `bibtex-search-crossref' and for font-locking.
+Set this variable before loading BibTeX mode."
   :group 'bibtex
   :type '(repeat (cons (regexp :tag "Regexp")
-                       (integer :tag "Number"))))
+                       (integer :tag "Number")))
+  :version "23.1")
 
 (defcustom bibtex-expand-strings nil
   "If non-nil, expand strings when extracting the content of a BibTeX field."
@@ -977,8 +992,8 @@ Used by `bibtex-find-crossref' and for font-locking."
     (define-key km "\C-c}" 'bibtex-remove-delimiters)
     (define-key km "\C-c\C-c" 'bibtex-clean-entry)
     (define-key km "\C-c\C-q" 'bibtex-fill-entry)
-    (define-key km "\C-c\C-s" 'bibtex-find-entry)
-    (define-key km "\C-c\C-x" 'bibtex-find-crossref)
+    (define-key km "\C-c\C-s" 'bibtex-search-entry)
+    (define-key km "\C-c\C-x" 'bibtex-search-crossref)
     (define-key km "\C-c\C-t" 'bibtex-copy-summary-as-kill)
     (define-key km "\C-c?" 'bibtex-print-help-message)
     (define-key km "\C-c\C-p" 'bibtex-pop-previous)
@@ -1034,8 +1049,8 @@ Used by `bibtex-find-crossref' and for font-locking."
     "--"
      ["Make Entry Visible" bibtex-reposition-window t])
     ("Moving in BibTeX Buffers"
-     ["Find Entry" bibtex-find-entry t]
-     ["Find Crossref Entry" bibtex-find-crossref t])
+     ["Search Entry" bibtex-search-entry t]
+     ["Search Crossref Entry" bibtex-search-crossref t])
     "--"
     ("Operating on Current Field"
      ["Fill Field" fill-paragraph t]
@@ -1085,6 +1100,7 @@ Used by `bibtex-find-crossref' and for font-locking."
      "--"
      ["Convert Alien Buffer" bibtex-convert-alien t])
     ("Operating on Multiple Buffers"
+     ["(Re)Initialize BibTeX Buffers" bibtex-initialize t]
      ["Validate Entries" bibtex-validate-globally t])))
 
 (easy-menu-define
@@ -1207,7 +1223,7 @@ The CDRs of the elements are t for header keys and nil for crossref keys.")
 (defvar bibtex-entry-type
   (concat "@[ \t]*\\(?:"
           (regexp-opt (mapcar 'car bibtex-entry-field-alist)) "\\)")
-  "Regexp matching the name of a BibTeX entry.")
+  "Regexp matching the type of a BibTeX entry.")
 
 (defvar bibtex-entry-head
   (concat "^[ \t]*\\("
@@ -1289,19 +1305,6 @@ Auto-generated from `bibtex-sort-entry-class'.
 Used when `bibtex-maintain-sorted-entries' is `entry-class'.")
 
 \f
-;; Support for hideshow minor mode
-(defun bibtex-hs-forward-sexp (arg)
-  "Replacement for `forward-sexp' to be used by `hs-minor-mode'.
-ARG is ignored."
-  (if (looking-at "@\\S(*\\s(")
-      (goto-char (1- (match-end 0))))
-  (forward-sexp 1))
-
-(add-to-list
- 'hs-special-modes-alist
- '(bibtex-mode "@\\S(*\\s(" "\\s)" nil bibtex-hs-forward-sexp nil))
-
-\f
 (defun bibtex-parse-association (parse-lhs parse-rhs)
   "Parse a string of the format <left-hand-side = right-hand-side>.
 The functions PARSE-LHS and PARSE-RHS are used to parse the corresponding
@@ -1513,8 +1516,8 @@ If `bibtex-expand-strings' is non-nil, also expand BibTeX strings."
       (save-excursion
         (goto-char (bibtex-start-of-text-in-field bounds))
         (let ((epoint (bibtex-end-of-text-in-field bounds))
-              content opoint)
-          (while (< (setq opoint (point)) epoint)
+              content)
+          (while (< (point) epoint)
             (if (looking-at bibtex-field-const)
                 (let ((mtch (match-string-no-properties 0)))
                   (push (or (if bibtex-expand-strings
@@ -1544,7 +1547,7 @@ If optional arg FOLLOW-CROSSREF is non-nil, follow crossref."
                          (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)
+               (if (bibtex-search-crossref crossref-field)
                    ;; Do not pass FOLLOW-CROSSREF because we want
                    ;; to follow crossrefs only one level of recursion.
                    (bibtex-text-in-field field))))))))
@@ -1725,13 +1728,18 @@ 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)
+        (end-marker (make-marker))
         found)
+    ;; Use marker to keep track of the buffer position of the end of
+    ;; a BibTeX entry as this position may change during reformatting.
+    (set-marker-insertion-type end-marker t)
     (save-excursion
       (goto-char (point-min))
       (while (setq found (bibtex-skip-to-valid-entry))
+        (set-marker end-marker (cdr found))
         (looking-at bibtex-any-entry-maybe-empty-head)
-        (funcall fun (bibtex-key-in-head "") (car found) (cdr found))
-        (goto-char (cdr found))))))
+        (funcall fun (bibtex-key-in-head "") (car found) end-marker)
+        (goto-char end-marker)))))
 
 (defun bibtex-progress-message (&optional flag interval)
   "Echo a message about progress of current buffer.
@@ -1782,7 +1790,7 @@ If FLAG is nil, a message is echoed if point was incremented at least
     ")"))
 
 (defun bibtex-flash-head (prompt)
-  "Flash at BibTeX entry head before point, if exists."
+  "Flash at BibTeX entry head before point, if it exists."
   (let ((case-fold-search t)
         (pnt (point)))
     (save-excursion
@@ -1790,7 +1798,8 @@ If FLAG is nil, a message is echoed if point was incremented at least
       (when (and (looking-at bibtex-any-entry-maybe-empty-head)
                  (< (point) pnt))
         (goto-char (match-beginning bibtex-type-in-head))
-        (if (pos-visible-in-window-p (point))
+        (if (and (< 0 blink-matching-delay)
+                 (pos-visible-in-window-p (point)))
             (sit-for blink-matching-delay)
           (message "%s%s" prompt (buffer-substring-no-properties
                                   (point) (match-end bibtex-key-in-head))))))))
@@ -1828,13 +1837,16 @@ are ignored.  Return point"
   "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.
+Thus if COMMA is non-nil, the \"current field\" includes the terminating comma
+as well as the entry delimiter if it appears on the same line.
 Unless NOERR is non-nil, signal an error if no enclosing field is found.
 On success return bounds, nil otherwise.  Do not move point."
   (save-excursion
     (when comma
       (end-of-line)
       (skip-chars-backward " \t")
+      ;; Ignore entry delimiter and comma at end of line.
+      (if (memq (preceding-char) '(?} ?\))) (forward-char -1))
       (if (= (preceding-char) ?,) (forward-char -1)))
 
     (let ((bounds (bibtex-search-backward-field bibtex-field-name t)))
@@ -1870,297 +1882,318 @@ Optional arg COMMA is as in `bibtex-enclosing-field'."
       (bibtex-skip-to-valid-entry)
       (push-mark)
       (insert (funcall fun 'bibtex-entry-kill-ring-yank-pointer
-                       bibtex-entry-kill-ring)))))
+                       bibtex-entry-kill-ring))
+      (unless (functionp bibtex-reference-keys)
+        ;; update `bibtex-reference-keys'
+        (save-excursion
+          (goto-char (mark t))
+          (looking-at bibtex-any-entry-maybe-empty-head)
+          (let ((key (bibtex-key-in-head)))
+            (if key (push (cons key t) bibtex-reference-keys))))))))
 
 (defun bibtex-format-entry ()
   "Helper function for `bibtex-clean-entry'.
 Formats current entry according to variable `bibtex-entry-format'."
-  (save-excursion
-    (save-restriction
-      (bibtex-narrow-to-entry)
-      (let ((case-fold-search t)
-            (format (if (eq bibtex-entry-format t)
-                        '(realign opts-or-alts required-fields
-                                  numerical-fields
-                                  last-comma page-dashes delimiters
-                                  unify-case inherit-booktitle)
-                      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))
-        (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
-                                         beg-type end-type)
-                                        bibtex-entry-field-alist
-                                        t))
-
-          ;; unify case of entry name
-          (when (memq 'unify-case format)
-            (delete-region beg-type end-type)
-            (insert (car entry-list)))
-
-          ;; update left entry delimiter
-          (when (memq 'delimiters format)
-            (goto-char end-type)
-            (skip-chars-forward " \t\n")
-            (delete-char 1)
-            (insert (bibtex-entry-left-delimiter))))
-
-        ;; determine if entry has crossref field and if at least
-        ;; one alternative is non-empty
-        (goto-char (point-min))
-        (let* ((fields-alist (bibtex-parse-entry t))
-               (field (assoc-string "crossref" fields-alist t)))
-          (setq crossref-key (and field
-                                  (not (equal "" (cdr field)))
-                                  (cdr field))
-                req-field-list (if crossref-key
-                                   (nth 0 (nth 2 entry-list)) ; crossref part
-                                 (nth 0 (nth 1 entry-list)))) ; required part
-
-          (dolist (rfield req-field-list)
-            (when (nth 3 rfield) ; we should have an alternative
-              (setq alternatives-there t
-                    field (assoc-string (car rfield) fields-alist t))
-              (if (and field
-                       (not (equal "" (cdr field))))
-                  (cond ((not non-empty-alternative)
-                         (setq non-empty-alternative t))
-                        ((memq 'required-fields format)
-                         (error "More than one non-empty alternative")))))))
-
-        (if (and alternatives-there
-                 (not non-empty-alternative)
-                 (memq 'required-fields format))
-            (error "All alternatives are empty"))
-
-        ;; process all fields
-        (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)))
-                 (end-name  (copy-marker (bibtex-end-of-name-in-field bounds)))
-                 (beg-text  (copy-marker (bibtex-start-of-text-in-field bounds)))
-                 (end-text  (copy-marker (bibtex-end-of-text-in-field bounds) t))
-                 (opt-alt   (string-match "OPT\\|ALT"
-                                          (buffer-substring-no-properties
-                                           beg-name (+ beg-name 3))))
-                 (field-name (buffer-substring-no-properties
-                              (if opt-alt (+ beg-name 3) beg-name) end-name))
-                 (empty-field (equal "" (bibtex-text-in-field-bounds bounds t)))
-                 deleted)
-
-            ;; We have more elegant high-level functions for several
-            ;; tasks done by `bibtex-format-entry'.  However, they contain
-            ;; quite some redundancy compared with what we need to do
-            ;; anyway.  So for speed-up we avoid using them.
-
-            (if (memq 'opts-or-alts format)
-                (cond ((and empty-field
-                            (or opt-alt
-                                (let ((field (assoc-string
-                                              field-name req-field-list t)))
-                                  (or (not field)       ; OPT field
-                                      (nth 3 field))))) ; ALT field
-                       ;; Either it is an empty ALT field.  Then we have checked
-                       ;; already that we have one non-empty alternative.  Or it
-                       ;; is an empty OPT field that we do not miss anyway.
-                       ;; So we can safely delete this field.
-                       (delete-region beg-field end-field)
-                       (setq deleted t))
-                      ;; otherwise: not empty, delete "OPT" or "ALT"
-                      (opt-alt
-                       (goto-char beg-name)
-                       (delete-char 3))))
-
-            (unless deleted
-              (push field-name field-list)
-
-              ;; remove delimiters from purely numerical fields
-              (when (and (memq 'numerical-fields format)
-                         (progn (goto-char beg-text)
-                                (looking-at "\\(\"[0-9]+\"\\)\\|\\({[0-9]+}\\)")))
-                (goto-char end-text)
-                (delete-char -1)
-                (goto-char beg-text)
-                (delete-char 1))
-
-              ;; update delimiters
-              (when (memq 'delimiters format)
-                (goto-char beg-text)
-                (when (looking-at "[{\"]")
-                  (delete-char 1)
-                  (insert (bibtex-field-left-delimiter)))
-                (goto-char (1- (marker-position end-text)))
-                (when (looking-at "[}\"]")
+  ;; initialize `bibtex-field-braces-opt' if necessary
+  (if (and bibtex-field-braces-alist (not bibtex-field-braces-opt))
+      (setq bibtex-field-braces-opt
+            (bibtex-field-re-init bibtex-field-braces-alist 'braces)))
+  ;; initialize `bibtex-field-strings-opt' if necessary
+  (if (and bibtex-field-strings-alist (not bibtex-field-strings-opt))
+      (setq bibtex-field-strings-opt
+            (bibtex-field-re-init bibtex-field-strings-alist 'strings)))
+
+  (let ((case-fold-search t)
+        (format (if (eq bibtex-entry-format t)
+                    '(realign opts-or-alts required-fields numerical-fields
+                              page-dashes whitespace inherit-booktitle
+                              last-comma delimiters unify-case braces
+                              strings)
+                  bibtex-entry-format))
+        (left-delim-re (regexp-quote (bibtex-field-left-delimiter)))
+        bounds crossref-key req-field-list default-field-list field-list
+        alt-fields error-field-name)
+    (unwind-protect
+        ;; formatting (undone if error occurs)
+        (atomic-change-group
+          (save-excursion
+            (save-restriction
+              (bibtex-narrow-to-entry)
+
+              ;; There are more elegant high-level functions for several tasks
+              ;; done by `bibtex-format-entry'.  However, they contain some
+              ;; redundancy compared with what we need to do anyway.
+              ;; So for speed-up we avoid using them.
+              ;; (`bibtex-format-entry' is called often by `bibtex-reformat'.)
+
+              ;; identify entry type
+              (goto-char (point-min))
+              (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))
+                     (entry-list (assoc-string (buffer-substring-no-properties
+                                                beg-type end-type)
+                                               bibtex-entry-field-alist t)))
+
+                ;; unify case of entry type
+                (when (memq 'unify-case format)
+                  (delete-region beg-type end-type)
+                  (insert (car entry-list)))
+
+                ;; update left entry delimiter
+                (when (memq 'delimiters format)
+                  (goto-char end-type)
+                  (skip-chars-forward " \t\n")
                   (delete-char 1)
-                  (insert (bibtex-field-right-delimiter))))
-
-              ;; update page dashes
-              (if (and (memq 'page-dashes format)
-                       (bibtex-string= field-name "pages")
-                       (progn (goto-char beg-text)
-                              (looking-at
-                               "\\([\"{][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
-                       (bibtex-string= field-name "booktitle")
-                       crossref-key)
-                  (let ((title (save-excursion
-                                 (save-restriction
-                                   (widen)
-                                   (if (bibtex-find-entry crossref-key t)
-                                       (bibtex-text-in-field "title"))))))
-                    (when title
-                      (setq empty-field nil)
-                      (goto-char (1+ beg-text))
-                      (insert title))))
-
-             ;; Use booktitle to set a missing title.
-             (if (and empty-field
-                      (bibtex-string= field-name "title"))
-                 (let ((booktitle (bibtex-text-in-field "booktitle")))
-                   (when booktitle
-                     (setq empty-field nil)
-                     (goto-char (1+ beg-text))
-                     (insert booktitle))))
-
-              ;; if empty field, complain
-              (if (and empty-field
-                       (memq 'required-fields format)
-                       (assoc-string field-name req-field-list t))
-                  (error "Mandatory field `%s' is empty" field-name))
-
-              ;; unify case of field name
-              (if (memq 'unify-case format)
-                  (let ((fname (car (assoc-string
-                                     field-name
-                                    (append (nth 0 (nth 1 entry-list))
-                                            (nth 1 (nth 1 entry-list))
-                                            bibtex-user-optional-fields)
-                                    t))))
-                    (if fname
-                        (progn
-                          (delete-region beg-name end-name)
-                          (goto-char beg-name)
-                          (insert fname))
-                      ;; there are no rules we could follow
-                      (downcase-region beg-name end-name))))
-
-              ;; update point
-              (goto-char end-field))))
-
-        ;; check whether all required fields are present
-        (if (memq 'required-fields format)
-            (let ((found 0) altlist)
-              (dolist (fname req-field-list)
-                (if (nth 3 fname)
-                    (push (car fname) altlist))
-                (unless (or (member (car fname) field-list)
-                            (nth 3 fname))
-                  (error "Mandatory field `%s' is missing" (car fname))))
-              (when altlist
-                (dolist (fname altlist)
-                  (if (member fname field-list)
-                      (setq found (1+ found))))
-                (cond ((= found 0)
-                       (error "Alternative mandatory field `%s' is missing"
-                              altlist))
-                      ((> found 1)
-                       (error "Alternative fields `%s' are defined %s times"
-                              altlist found))))))
-
-        ;; update comma after last field
-        (if (memq 'last-comma format)
-            (cond ((and bibtex-comma-after-last-field
-                        (not (looking-at ",")))
-                   (insert ","))
-                  ((and (not bibtex-comma-after-last-field)
-                        (looking-at ","))
-                   (delete-char 1))))
-
-        ;; update right entry delimiter
-        (if (looking-at ",")
-            (forward-char))
-        (when (memq 'delimiters format)
-          (skip-chars-forward " \t\n")
-          (delete-char 1)
-          (insert (bibtex-entry-right-delimiter)))
-
-        ;; fill entry
-        (if (memq 'realign format)
-            (bibtex-fill-entry))))))
+                  (insert (bibtex-entry-left-delimiter)))
+
+                ;; Do we have a crossref key?
+                (goto-char (point-min))
+                (if (setq bounds (bibtex-search-forward-field
+                                  "\\(OPT\\)?crossref"))
+                    (let ((text (bibtex-text-in-field-bounds bounds t)))
+                      (unless (equal "" text)
+                        (setq crossref-key text))))
+
+                ;; list of required fields appropriate for an entry with
+                ;; or without crossref key.
+                (setq req-field-list (if (and crossref-key (nth 2 entry-list))
+                                         (car (nth 2 entry-list))
+                                       (car (nth 1 entry-list)))
+                      ;; default list of fields that may appear in this entry
+                      default-field-list (append (nth 0 (nth 1 entry-list))
+                                                 (nth 1 (nth 1 entry-list))
+                                                 bibtex-user-optional-fields)))
+
+              ;; process all fields
+              (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)))
+                       (end-name  (copy-marker (bibtex-end-of-name-in-field bounds)))
+                       (beg-text  (copy-marker (bibtex-start-of-text-in-field bounds)))
+                       (end-text  (copy-marker (bibtex-end-of-text-in-field bounds) t))
+                       (opt-alt   (string-match "OPT\\|ALT"
+                                                (buffer-substring-no-properties
+                                                 beg-name (+ beg-name 3))))
+                       (field-name (buffer-substring-no-properties
+                                    (if opt-alt (+ beg-name 3) beg-name) end-name))
+                       (empty-field (equal "" (bibtex-text-in-field-bounds bounds t)))
+                       deleted)
+
+                  ;; keep track of alternatives
+                  (if (nth 3 (assoc-string field-name req-field-list t))
+                      (push field-name alt-fields))
+
+                  (if (memq 'opts-or-alts format)
+                      ;; delete empty optional and alternative fields
+                      ;; (but keep empty required fields)
+                      (cond ((and empty-field
+                                  (or opt-alt
+                                      (let ((field (assoc-string
+                                                    field-name req-field-list t)))
+                                        (or (not field) ; OPT field
+                                            (nth 3 field))))) ; ALT field
+                             (delete-region beg-field end-field)
+                             (setq deleted t))
+                            ;; otherwise nonempty field: delete "OPT" or "ALT"
+                            (opt-alt
+                             (goto-char beg-name)
+                             (delete-char 3))))
+
+                  (unless deleted
+                    (push field-name field-list)
+
+                    ;; Remove whitespace at beginning and end of field.
+                    ;; We do not look at individual parts of the field
+                    ;; as {foo } # bar # { baz} is a fine 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")))
+
+                    ;; remove delimiters from purely numerical fields
+                    (when (and (memq 'numerical-fields format)
+                               (progn (goto-char beg-text)
+                                      (looking-at "\\(\"[0-9]+\"\\)\\|\\({[0-9]+}\\)")))
+                      (goto-char end-text)
+                      (delete-char -1)
+                      (goto-char beg-text)
+                      (delete-char 1))
+
+                    ;; update delimiters
+                    (when (memq 'delimiters format)
+                      (goto-char beg-text)
+                      ;; simplified from `bibtex-parse-field-text', as we
+                      ;; already checked that the field format is correct
+                      (while (< (point) end-text)
+                        (if (looking-at bibtex-field-const)
+                            (goto-char (match-end 0))
+                          (let ((boundaries (bibtex-parse-field-string)))
+                            (if (looking-at left-delim-re)
+                                (goto-char (cdr boundaries))
+                              (delete-char 1)
+                              (insert (bibtex-field-left-delimiter))
+                              (goto-char (1- (cdr boundaries)))
+                              (delete-char 1)
+                              (insert (bibtex-field-right-delimiter)))))
+                        (if (looking-at "[ \t\n]*#[ \t\n]*")
+                            (goto-char (match-end 0)))))
+
+                    ;; update page dashes
+                    (if (and (memq 'page-dashes format)
+                             (bibtex-string= field-name "pages")
+                             (progn (goto-char beg-text)
+                                    (looking-at
+                                     "\\([\"{][0-9]+\\)[ \t\n]*--?[ \t\n]*\\([0-9]+[\"}]\\)")))
+                        (replace-match "\\1-\\2"))
+
+                    ;; 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
+                             (bibtex-string= field-name "booktitle")
+                             crossref-key)
+                        (let ((title (save-excursion
+                                       (save-restriction
+                                         (widen)
+                                         (if (bibtex-search-entry crossref-key t)
+                                             (bibtex-text-in-field "title"))))))
+                          (when title
+                            (setq empty-field nil)
+                            (goto-char (1+ beg-text))
+                            (insert title))))
+
+                    ;; if empty field is a required field, complain
+                    (when (and empty-field
+                               (memq 'required-fields format)
+                               (assoc-string field-name req-field-list t))
+                      (setq error-field-name field-name)
+                      (error "Mandatory field `%s' is empty" field-name))
+
+                    ;; unify case of field name
+                    (if (memq 'unify-case format)
+                        (let ((fname (car (assoc-string field-name
+                                                        default-field-list t))))
+                          (if fname
+                              (progn
+                                (delete-region beg-name end-name)
+                                (goto-char beg-name)
+                                (insert fname))
+                            ;; there are no rules we could follow
+                            (downcase-region beg-name end-name))))
+
+                    ;; update point
+                    (goto-char end-field))))
+
+              ;; check whether all required fields are present
+              (if (memq 'required-fields format)
+                  (let ((found 0) alt-list)
+                    (dolist (fname req-field-list)
+                      (cond ((nth 3 fname) ; t if field has alternative flag
+                             (push (car fname) alt-list)
+                             (if (member-ignore-case (car fname) field-list)
+                                 (setq found (1+ found))))
+                            ((not (member-ignore-case (car fname) field-list))
+                             ;; If we use the crossref field, a required field
+                             ;; can have the OPT prefix.  So if it was empty,
+                             ;; we have deleted by now.  Nonetheless we can
+                             ;; move point on this empty field.
+                             (setq error-field-name (car fname))
+                             (error "Mandatory field `%s' is missing" (car fname)))))
+                    (if alt-list
+                        (cond ((= found 0)
+                               (if alt-fields
+                                   (setq error-field-name (car (last alt-fields))))
+                               (error "Alternative mandatory field `%s' is missing"
+                                      alt-list))
+                              ((> found 1)
+                               (if alt-fields
+                                   (setq error-field-name (car (last alt-fields))))
+                               (error "Alternative fields `%s' are defined %s times"
+                                      alt-list found))))))
+
+              ;; update comma after last field
+              (if (memq 'last-comma format)
+                  (cond ((and bibtex-comma-after-last-field
+                              (not (looking-at ",")))
+                         (insert ","))
+                        ((and (not bibtex-comma-after-last-field)
+                              (looking-at ","))
+                         (delete-char 1))))
+
+              ;; update right entry delimiter
+              (if (looking-at ",")
+                  (forward-char))
+              (when (memq 'delimiters format)
+                (skip-chars-forward " \t\n")
+                (delete-char 1)
+                (insert (bibtex-entry-right-delimiter)))
+
+              ;; realign and fill entry
+              (if (memq 'realign format)
+                  (bibtex-fill-entry)))))
+
+      ;; Unwindform: move point to location where error occurred if possible
+      (if error-field-name
+          (let (bounds)
+            (when (save-excursion
+                    (bibtex-beginning-of-entry)
+                    (setq bounds
+                          (bibtex-search-forward-field
+                           ;; If we use the crossref field, a required field
+                           ;; can have the OPT prefix
+                           (concat "\\(OPT\\|ALT\\)?" error-field-name) t)))
+              (goto-char (bibtex-start-of-text-in-field bounds))
+              (bibtex-find-text)))))))
 
 (defun bibtex-field-re-init (regexp-alist type)
   "Calculate optimized value for bibtex-regexp-TYPE-opt.
@@ -2169,7 +2202,7 @@ 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))
+                        (replace-regexp-in-string " +" "[ \t\n]+" (nth 1 e))
                         (nth 2 e))) ; nil for 'braces'.
                 regexp-alist))
   (let (opt-list)
@@ -2293,6 +2326,10 @@ Return the result as a string"
     ;; gather words from titlestring into a list.  Ignore
     ;; specific words and use only a specific amount of words.
     (let ((counter 0)
+         (ignore-re (concat "\\`\\(?:"
+                             (mapconcat 'identity
+                                        bibtex-autokey-titleword-ignore "\\|")
+                             "\\)\\'"))
           titlewords titlewords-extra word)
       (while (and (or (not (numberp bibtex-autokey-titlewords))
                       (< counter (+ bibtex-autokey-titlewords
@@ -2301,13 +2338,9 @@ 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'
-        (unless (let ((lst bibtex-autokey-titleword-ignore))
-                  (while (and lst
-                              (not (string-match (concat "\\`\\(?:" (car lst)
-                                                         "\\)\\'") word)))
-                    (setq lst (cdr lst)))
-                  lst)
+        ;; `bibtex-autokey-titleword-ignore'.  Case is significant.
+        (unless (let (case-fold-search)
+                 (string-match ignore-re word))
           (setq counter (1+ counter))
           (if (or (not (numberp bibtex-autokey-titlewords))
                   (<= counter bibtex-autokey-titlewords))
@@ -2426,8 +2459,10 @@ Concatenate the key:
       (apply 'append
              (mapcar (lambda (buf)
                        (with-current-buffer buf bibtex-reference-keys))
-                     (bibtex-files-expand t)))
-    bibtex-reference-keys))
+                     ;; include current buffer only if it uses `bibtex-mode'
+                     (bibtex-initialize (eq major-mode 'bibtex-mode))))
+    (if (eq major-mode 'bibtex-mode)
+        bibtex-reference-keys)))
 
 (defun bibtex-read-key (prompt &optional key global)
   "Read BibTeX key from minibuffer using PROMPT and default KEY.
@@ -2451,61 +2486,62 @@ on user input.  If VERBOSE is non-nil give messages about progress.
 Return alist of keys if parsing was completed, `aborted' otherwise.
 If `bibtex-parse-keys-fast' is non-nil, use fast but simplified algorithm
 for parsing BibTeX keys.  If parsing fails, try to set this variable to nil."
-  (let (ref-keys crossref-keys)
-    (save-excursion
-      (save-match-data
-        (if verbose
-            (bibtex-progress-message
-             (concat (buffer-name) ": parsing reference keys")))
-        (catch 'userkey
-          (goto-char (point-min))
-          (if bibtex-parse-keys-fast
-              (let ((case-fold-search t)
-                    (re (concat bibtex-entry-head "\\|"
-                                ",[ \t\n]*crossref[ \t\n]*=[ \t\n]*"
-                                "\\(\"[^\"]*\"\\|{[^}]*}\\)[ \t\n]*[,})]")))
-                (while (re-search-forward re nil t)
-                  (if (and abortable (input-pending-p))
-                      ;; user has aborted by typing a key --> return `aborted'
-                      (throw 'userkey 'aborted))
-                  (cond ((match-end 3)
-                         ;; This is a crossref.
-                         (let ((key (buffer-substring-no-properties
-                                     (1+ (match-beginning 3)) (1- (match-end 3)))))
+  (if (eq major-mode 'bibtex-mode)
+      (let (ref-keys crossref-keys)
+        (save-excursion
+          (save-match-data
+            (if verbose
+                (bibtex-progress-message
+                 (concat (buffer-name) ": parsing reference keys")))
+            (catch 'userkey
+              (goto-char (point-min))
+              (if bibtex-parse-keys-fast
+                  (let ((case-fold-search t)
+                        (re (concat bibtex-entry-head "\\|"
+                                    ",[ \t\n]*crossref[ \t\n]*=[ \t\n]*"
+                                    "\\(\"[^\"]*\"\\|{[^}]*}\\)[ \t\n]*[,})]")))
+                    (while (re-search-forward re nil t)
+                      (if (and abortable (input-pending-p))
+                          ;; user has aborted by typing a key: return `aborted'
+                          (throw 'userkey 'aborted))
+                      (cond ((match-end 3)
+                             ;; This is a crossref.
+                             (let ((key (buffer-substring-no-properties
+                                         (1+ (match-beginning 3)) (1- (match-end 3)))))
                                (unless (assoc key crossref-keys)
                                  (push (list key) crossref-keys))))
-                        ;; only keys of known entries
-                        ((assoc-string (bibtex-type-in-head)
-                                       bibtex-entry-field-alist t)
-                         ;; This is an entry.
-                         (let ((key (bibtex-key-in-head)))
-                           (unless (assoc key ref-keys)
-                             (push (cons key t) ref-keys)))))))
-
-            (let (;; ignore @String entries because they are handled
-                  ;; separately by `bibtex-parse-strings'
-                  (bibtex-sort-ignore-string-entries t)
-                  bounds)
-              (bibtex-map-entries
-               (lambda (key beg end)
-                 (if (and abortable
-                          (input-pending-p))
-                     ;; user has aborted by typing a key --> return `aborted'
-                     (throw 'userkey 'aborted))
-                 (if verbose (bibtex-progress-message))
-                 (unless (assoc key ref-keys)
-                   (push (cons key t) ref-keys))
-                 (if (and (setq bounds (bibtex-search-forward-field "crossref" end))
-                          (setq key (bibtex-text-in-field-bounds bounds t))
-                          (not (assoc key crossref-keys)))
-                     (push (list key) crossref-keys))))))
-
-          (dolist (key crossref-keys)
-            (unless (assoc (car key) ref-keys) (push key ref-keys)))
-          (if verbose
-              (bibtex-progress-message 'done))
-          ;; successful operation --> return `bibtex-reference-keys'
-          (setq bibtex-reference-keys ref-keys))))))
+                            ;; only keys of known entries
+                            ((assoc-string (bibtex-type-in-head)
+                                           bibtex-entry-field-alist t)
+                             ;; This is an entry.
+                             (let ((key (bibtex-key-in-head)))
+                               (unless (assoc key ref-keys)
+                                 (push (cons key t) ref-keys)))))))
+
+                (let (;; ignore @String entries because they are handled
+                      ;; separately by `bibtex-parse-strings'
+                      (bibtex-sort-ignore-string-entries t)
+                      bounds)
+                  (bibtex-map-entries
+                   (lambda (key beg end)
+                     (if (and abortable
+                              (input-pending-p))
+                         ;; user has aborted by typing a key: return `aborted'
+                         (throw 'userkey 'aborted))
+                     (if verbose (bibtex-progress-message))
+                     (unless (assoc key ref-keys)
+                       (push (cons key t) ref-keys))
+                     (if (and (setq bounds (bibtex-search-forward-field "crossref" end))
+                              (setq key (bibtex-text-in-field-bounds bounds t))
+                              (not (assoc key crossref-keys)))
+                         (push (list key) crossref-keys))))))
+
+              (dolist (key crossref-keys)
+                (unless (assoc (car key) ref-keys) (push key ref-keys)))
+              (if verbose
+                  (bibtex-progress-message 'done))
+              ;; successful operation --> return `bibtex-reference-keys'
+              (setq bibtex-reference-keys ref-keys)))))))
 
 (defun bibtex-parse-strings (&optional add abortable)
   "Set `bibtex-strings' to the string definitions in the whole buffer.
@@ -2516,8 +2552,7 @@ Return alist of strings if parsing was completed, `aborted' otherwise."
   (save-excursion
     (save-match-data
       (goto-char (point-min))
-      (let ((strings (if (and add
-                              (listp bibtex-strings))
+      (let ((strings (if (and add (not (functionp bibtex-strings)))
                          bibtex-strings))
             bounds key)
         (if (listp add)
@@ -2540,8 +2575,9 @@ Return alist of strings if parsing was completed, `aborted' otherwise."
 
 (defun bibtex-strings ()
   "Return `bibtex-strings'.  Initialize this variable if necessary."
-  (if (listp bibtex-strings) bibtex-strings
-    (bibtex-parse-strings (bibtex-string-files-init))))
+  (if (functionp bibtex-strings)
+      (bibtex-parse-strings (bibtex-string-files-init))
+    bibtex-strings))
 
 (defun bibtex-string-files-init ()
   "Return initialization for `bibtex-strings'.
@@ -2606,14 +2642,22 @@ Parsing initializes `bibtex-reference-keys' and `bibtex-strings'."
                   (setq bibtex-buffer-last-parsed-tick (buffer-modified-tick)))))
         (setq buffers (cdr buffers))))))
 
-(defun bibtex-files-expand (&optional current force)
-  "Return an expanded list of BibTeX buffers based on `bibtex-files'.
+;;;###autoload
+(defun bibtex-initialize (&optional current force select)
+  "(Re)Initialize BibTeX buffers.
+Visit the BibTeX files defined by `bibtex-files' and return a list
+of corresponding buffers.
 Initialize in these buffers `bibtex-reference-keys' if not yet set.
 List of BibTeX buffers includes current buffer if CURRENT is non-nil.
 If FORCE is non-nil, (re)initialize `bibtex-reference-keys' even if
-already set."
+already set.  If SELECT is non-nil interactively select a BibTeX buffer.
+When called interactively, FORCE is t, CURRENT is t if current buffer uses
+`bibtex-mode', and SELECT is t if current buffer does not use `bibtex-mode',"
+  (interactive (list (eq major-mode 'bibtex-mode) t
+                     (not (eq major-mode 'bibtex-mode))))
   (let ((file-path (split-string (or bibtex-file-path default-directory) ":+"))
         file-list dir-list buffer-list)
+    ;; generate list of BibTeX files
     (dolist (file bibtex-files)
       (cond ((eq file 'bibtex-file-path)
              (setq dir-list (append dir-list file-path)))
@@ -2624,34 +2668,52 @@ already set."
                     (file-name-absolute-p file))
              (push file file-list))
             (t
-             (let (fullfilename found)
+             (let (expanded-file-name found)
                (dolist (dir file-path)
                  (when (file-readable-p
-                        (setq fullfilename (expand-file-name file dir)))
-                   (push fullfilename file-list)
+                        (setq expanded-file-name (expand-file-name file dir)))
+                   (push expanded-file-name file-list)
                    (setq found t)))
                (unless found
-                 (error "File %s not in paths defined via bibtex-file-path"
+                 (error "File `%s' not in paths defined via bibtex-file-path"
                         file))))))
     (dolist (file file-list)
       (unless (file-readable-p file)
-        (error "BibTeX file %s not found" file)))
+        (error "BibTeX file `%s' not found" file)))
     ;; expand dir-list
     (dolist (dir dir-list)
       (setq file-list
             (append file-list (directory-files dir t "\\.bib\\'" t))))
     (delete-dups file-list)
+    ;; visit files in FILE-LIST
     (dolist (file file-list)
-      (when (file-readable-p file)
-        (push (find-file-noselect file) buffer-list)
-        (with-current-buffer (car buffer-list)
-          (if (or force (not (listp bibtex-reference-keys)))
-            (bibtex-parse-keys)))))
+      (if (file-readable-p file)
+        (push (find-file-noselect file) buffer-list)))
+    ;; Include current buffer iff we want it.
+    ;; Exclude current buffer if it doesn't use `bibtex-mode'.
+    ;; Thus calling `bibtex-initialize' gives meaningful results for
+    ;; any current buffer.
+    (unless (and current (eq major-mode 'bibtex-mode)) (setq current nil))
     (cond ((and current (not (memq (current-buffer) buffer-list)))
-           (push (current-buffer) buffer-list)
-           (if force (bibtex-parse-keys)))
+           (push (current-buffer) buffer-list))
           ((and (not current) (memq (current-buffer) buffer-list))
            (setq buffer-list (delq (current-buffer) buffer-list))))
+    ;; parse keys
+    (dolist (buffer buffer-list)
+      (with-current-buffer buffer
+        (if (or force (functionp bibtex-reference-keys))
+            (bibtex-parse-keys))
+        (unless (functionp bibtex-strings)
+          (bibtex-parse-strings (bibtex-string-files-init)))))
+    ;; select BibTeX buffer
+    (if select
+        (if buffer-list
+            (switch-to-buffer
+             (completing-read "Switch to BibTeX buffer: "
+                              (mapcar 'buffer-name buffer-list)
+                              nil t
+                              (if current (buffer-name (current-buffer)))))
+          (message "No BibTeX buffers defined")))
     buffer-list))
 
 (defun bibtex-complete-internal (completions)
@@ -2660,35 +2722,13 @@ 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
+  (let ((beg (save-excursion
                 (re-search-backward "[ \t{\"]")
                 (forward-char)
                 (point)))
-         (end (point))
-         (pattern (buffer-substring-no-properties beg end))
-         (completion (try-completion pattern completions)))
-    (cond ((not completion)
-           (error "Can't find completion for `%s'" pattern))
-          ((eq completion t)
-           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
-           (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))))
+        (end (point)))
+    (when (completion-in-region beg end completions)
+      (buffer-substring beg (point)))))
 
 (defun bibtex-complete-string-cleanup (str compl)
   "Cleanup after inserting string STR.
@@ -2706,7 +2746,7 @@ expansion of STR using expansion list COMPL."
 Use `bibtex-summary-function' to generate summary."
   (save-excursion
     (if (and (stringp key)
-             (bibtex-find-entry key t))
+             (bibtex-search-entry key t))
         (message "Ref: %s" (funcall bibtex-summary-function)))))
 
 (defun bibtex-copy-summary-as-kill (&optional arg)
@@ -2862,7 +2902,7 @@ If NO-BUTTON is non-nil do not generate buttons."
       (setq start (1+ (match-beginning 1))
             end (1- (match-end 1))
             found (>= start pnt)))
-    (if found (bibtex-button start end 'bibtex-find-crossref
+    (if found (bibtex-button start end 'bibtex-search-crossref
                              (buffer-substring-no-properties start end)
                              start t))
     found))
@@ -2875,7 +2915,7 @@ BOUND limits the 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
+          (bibtex-button start end 'bibtex-search-crossref
                          (buffer-substring-no-properties start end)
                          start t t)
           t))))
@@ -2890,9 +2930,9 @@ BOUND limits the search."
   'bibtex-function 'bibtex-url
   'help-echo (purecopy "mouse-2, RET: follow URL"))
 
-(define-button-type 'bibtex-find-crossref
+(define-button-type 'bibtex-search-crossref
   'action 'bibtex-button-action
-  'bibtex-function 'bibtex-find-crossref
+  'bibtex-function 'bibtex-search-crossref
   'help-echo (purecopy "mouse-2, RET: follow crossref"))
 
 (defun bibtex-button (beg end type &rest args)
@@ -2908,7 +2948,7 @@ BOUND limits the search."
 
 General information on working with BibTeX mode:
 
-Use commands such as \\[bibtex-Book] to get a template for a specific entry.
+Use commands such as \\<bibtex-mode-map>\\[bibtex-Book] to get a template for a specific entry.
 Then fill in all desired fields using \\[bibtex-next-field] to jump from field
 to field.  After having filled in all desired fields in the entry, clean the
 new entry with the command \\[bibtex-clean-entry].
@@ -3130,7 +3170,6 @@ field contents of the neighboring entry.  Finally try to update the text
 based on the difference between the keys of the neighboring and the current
 entry (for example, the year parts of the keys)."
   (interactive)
-  (undo-boundary)      ;So you can easily undo it, if it didn't work right.
   (bibtex-beginning-of-entry)
   (when (looking-at bibtex-entry-head)
     (let ((type (bibtex-type-in-head))
@@ -3293,7 +3332,8 @@ Return the new location of 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."))
+          (t (if (called-interactively-p 'interactive)
+                (message "Not on a known BibTeX entry."))
              (goto-char pnt)))
     (point)))
 
@@ -3375,23 +3415,23 @@ If mark is active count entries in region, if not in whole buffer."
 
 (defun bibtex-entry-index ()
   "Return index of BibTeX entry head at or past position of point.
-The index is a list (KEY CROSSREF-KEY ENTRY-NAME) that is used for sorting
+The index is a list (KEY CROSSREF-KEY ENTRY-TYPE) that is used for sorting
 the entries of the BibTeX buffer.  CROSSREF-KEY is nil unless the value
 of `bibtex-maintain-sorted-entries' is `crossref'.  Move point to the end
 of the head of the entry found.  Return nil if no entry found."
   (let ((case-fold-search t))
     (if (re-search-forward bibtex-entry-maybe-empty-head nil t)
         (let ((key (bibtex-key-in-head))
-              ;; all entry names should be downcase (for ease of comparison)
-              (entry-name (downcase (bibtex-type-in-head))))
+              ;; all entry types should be downcase (for ease of comparison)
+              (entry-type (downcase (bibtex-type-in-head))))
           ;; 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" t)))
                 (list key
                       (if bounds (bibtex-text-in-field-bounds bounds t))
-                      entry-name))
-            (list key nil entry-name))))))
+                      entry-type))
+            (list key nil entry-type))))))
 
 (defun bibtex-init-sort-entry-class-alist ()
   "Initialize `bibtex-sort-entry-class-alist' (buffer-local)."
@@ -3401,25 +3441,30 @@ of the head of the entry found.  Return nil if no entry found."
            (dolist (class bibtex-sort-entry-class alist)
              (setq i (1+ i))
              (dolist (entry class)
-               ;; All entry names should be downcase (for ease of comparison).
+               ;; All entry types 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).
+Each index is a list (KEY CROSSREF-KEY ENTRY-TYPE).
 The predicate depends on the variable `bibtex-maintain-sorted-entries'.
 If its value is nil use plain sorting."
   (cond ((not index1) (not index2)) ; indices can be nil
         ((not index2) nil)
         ((eq bibtex-maintain-sorted-entries 'crossref)
-         (if (nth 1 index1)
-             (if (nth 1 index2)
+         ;; CROSSREF-KEY may be nil or it can point to an entry
+         ;; in another BibTeX file.  In both cases we ignore CROSSREF-KEY.
+         (if (and (nth 1 index1)
+                  (cdr (assoc-string (nth 1 index1) bibtex-reference-keys)))
+             (if (and (nth 1 index2)
+                      (cdr (assoc-string (nth 1 index2) bibtex-reference-keys)))
                  (or (string-lessp (nth 1 index1) (nth 1 index2))
                      (and (string-equal (nth 1 index1) (nth 1 index2))
                           (string-lessp (nth 0 index1) (nth 0 index2))))
                (not (string-lessp (nth 0 index2) (nth 1 index1))))
-           (if (nth 1 index2)
+           (if (and (nth 1 index2)
+                    (cdr (assoc-string (nth 1 index2) bibtex-reference-keys)))
                (string-lessp (nth 0 index1) (nth 1 index2))
              (string-lessp (nth 0 index1) (nth 0 index2)))))
         ((eq bibtex-maintain-sorted-entries 'entry-class)
@@ -3444,6 +3489,9 @@ are ignored."
   (interactive)
   (bibtex-beginning-of-first-entry)     ; Needed by `sort-subr'
   (bibtex-init-sort-entry-class-alist)  ; Needed by `bibtex-lessp'.
+  (if (and (eq bibtex-maintain-sorted-entries 'crossref)
+           (functionp bibtex-reference-keys))
+      (bibtex-parse-keys))              ; Needed by `bibtex-lessp'.
   (sort-subr nil
              'bibtex-skip-to-valid-entry   ; NEXTREC function
              'bibtex-end-of-entry          ; ENDREC function
@@ -3451,7 +3499,7 @@ are ignored."
              nil                           ; ENDKEY function
              'bibtex-lessp))               ; PREDICATE
 
-(defun bibtex-find-crossref (crossref-key &optional pnt split noerror)
+(defun bibtex-search-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.
@@ -3473,7 +3521,7 @@ for a crossref key, t otherwise."
             (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))
+            (bounds (bibtex-search-forward-field "\\(OPT\\)?crossref" end))
             case-fold-search best temp crossref-key)
        (if bounds
            (setq crossref-key (bibtex-text-in-field-bounds bounds t)
@@ -3496,7 +3544,7 @@ for a crossref key, t otherwise."
 
   (let (buffer pos eqb)
     (save-excursion
-      (setq pos (bibtex-find-entry crossref-key t)
+      (setq pos (bibtex-search-entry crossref-key t)
             buffer (current-buffer)))
     (setq eqb (eq buffer (current-buffer)))
     (cond ((not pos)
@@ -3513,13 +3561,15 @@ for a crossref key, t otherwise."
              (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
+          ;; `bibtex-search-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
           ;; the correct sorting order.
           (eqb (goto-char pos))
           (t (set-buffer buffer) (goto-char pos)))
     pos))
+;; backward compatibility
+(defalias 'bibtex-find-crossref 'bibtex-search-crossref)
 
 (defun bibtex-dist (pos beg end)
   "Return distance between POS and region delimited by BEG and END."
@@ -3527,26 +3577,29 @@ for a crossref key, t otherwise."
         ((< pos beg) (- beg pos))
         (t (- pos end))))
 
-(defun bibtex-find-entry (key &optional global start display)
+;;;###autoload
+(defun bibtex-search-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.
-With prefix arg GLOBAL non-nil, search KEY in `bibtex-files'.
-Otherwise the search is limited to the current buffer.
-Optional arg START is buffer position where the search starts.
-If it is nil, start search at beginning of buffer.
+With GLOBAL non-nil, search KEY in `bibtex-files'.  Otherwise the search
+is limited to the current buffer.  Optional arg START is buffer position
+where the search starts.  If it is nil, start search at beginning of buffer.
 If DISPLAY is non-nil, display the buffer containing KEY.
-Otherwise, use `set-buffer'.  DISPLAY is t when called interactively."
-  (interactive (list (bibtex-read-key "Find key: " nil current-prefix-arg)
-                     current-prefix-arg nil t))
+Otherwise, use `set-buffer'.
+When called interactively, GLOBAL is t if there is a prefix arg or the current
+mode is not `bibtex-mode', START is nil, and DISPLAY is t."
+  (interactive
+   (let ((global (or current-prefix-arg (not (eq major-mode 'bibtex-mode)))))
+     (list (bibtex-read-key "Find key: " nil global) global nil t)))
   (if (and global bibtex-files)
-      (let ((buffer-list (bibtex-files-expand t))
+      (let ((buffer-list (bibtex-initialize t))
             buffer found)
         (while (and (not found)
                     (setq buffer (pop buffer-list)))
           (with-current-buffer buffer
             (if (cdr (assoc-string key bibtex-reference-keys))
-                ;; `bibtex-find-entry' moves point if key found
-                (setq found (bibtex-find-entry key)))))
+                ;; `bibtex-search-entry' moves point if key found
+                (setq found (bibtex-search-entry key)))))
         (cond ((and found display)
                (let ((same-window-buffer-names
                       (cons (buffer-name buffer) same-window-buffer-names)))
@@ -3571,22 +3624,27 @@ Otherwise, use `set-buffer'.  DISPLAY is t when called interactively."
              (if display (bibtex-reposition-window)))
             (display (message "Key `%s' not found" key)))
       pnt)))
+;; backward compatibility
+(defalias 'bibtex-find-entry 'bibtex-search-entry)
 
 (defun bibtex-prepare-new-entry (index)
   "Prepare a new BibTeX entry with index INDEX.
-INDEX is a list (KEY CROSSREF-KEY ENTRY-NAME).
+INDEX is a list (KEY CROSSREF-KEY ENTRY-TYPE).
 Move point where the entry KEY should be placed.
 If `bibtex-maintain-sorted-entries' is non-nil, perform a binary
 search to look for place for KEY.  This requires that buffer is sorted,
 see `bibtex-validate'.
 Return t if preparation was successful or nil if entry KEY already exists."
   (bibtex-init-sort-entry-class-alist)  ; Needed by `bibtex-lessp'.
+  (if (and (eq bibtex-maintain-sorted-entries 'crossref)
+           (functionp bibtex-reference-keys))
+      (bibtex-parse-keys))              ; Needed by `bibtex-lessp'.
   (let ((key (nth 0 index))
         key-exist)
     (cond ((or (null key)
                (and (stringp key)
                     (string-equal key ""))
-               (and (not (setq key-exist (bibtex-find-entry key)))
+               (and (not (setq key-exist (bibtex-search-entry key)))
                     (not bibtex-maintain-sorted-entries)))
            (bibtex-move-outside-of-entry))
           ;; if key-exist is non-nil due to the previous cond clause
@@ -3671,6 +3729,9 @@ Return t if test was successful, nil otherwise."
             (setq syntax-error t)
 
           ;; Check for duplicate keys and correct sort order
+          (bibtex-init-sort-entry-class-alist)  ; Needed by `bibtex-lessp'.
+          (bibtex-parse-keys) ; Possibly needed by `bibtex-lessp'.
+                              ; Always needed by subsequent global key check.
           (let (previous current key-list)
             (bibtex-progress-message "Checking for duplicate keys")
             (bibtex-map-entries
@@ -3692,13 +3753,16 @@ Return t if test was successful, nil otherwise."
             (bibtex-progress-message 'done))
 
           ;; Check for duplicate keys in `bibtex-files'.
-          (bibtex-parse-keys)
+          ;; `bibtex-validate' only compares keys in current buffer with keys
+          ;; in `bibtex-files'. `bibtex-validate-globally' compares keys for
+          ;; each file in `bibtex-files' with keys of all other files in
+          ;; `bibtex-files'.
           ;; We don't want to be fooled by outdated `bibtex-reference-keys'.
-          (dolist (buffer (bibtex-files-expand nil t))
+          (dolist (buffer (bibtex-initialize nil t))
             (dolist (key (with-current-buffer buffer bibtex-reference-keys))
               (when (and (cdr key)
                          (cdr (assoc-string (car key) bibtex-reference-keys)))
-                (bibtex-find-entry (car key))
+                (bibtex-search-entry (car key))
                 (push (cons (bibtex-current-line)
                             (format "Duplicate key `%s' in %s" (car key)
                                     (abbreviate-file-name (buffer-file-name buffer))))
@@ -3770,17 +3834,18 @@ Return t if test was successful, nil otherwise."
           (with-current-buffer (get-buffer-create err-buf)
             (setq default-directory dir)
             (unless (eq major-mode 'compilation-mode) (compilation-mode))
-            (toggle-read-only -1)
-            (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"
-                      "\n"))
-            (dolist (err error-list)
-              (insert (format "%s:%d: %s\n" file (car err) (cdr err))))
-            (set-buffer-modified-p nil)
-            (toggle-read-only 1)
-            (goto-line 3)) ; first error message
+            (let ((inhibit-read-only t))
+              (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"
+                        "\n"))
+              (dolist (err error-list)
+                (insert (format "%s:%d: %s\n" file (car err) (cdr err))))
+              (set-buffer-modified-p nil))
+            (goto-char (point-min))
+            (forward-line 2)) ; first error message
           (display-buffer err-buf)
           nil) ; return `nil' (i.e., buffer is invalid)
       (message "%s is syntactically correct"
@@ -3792,12 +3857,11 @@ Return t if test was successful, nil otherwise."
 With optional prefix arg STRINGS, check for duplicate strings, too.
 Return t if test was successful, nil otherwise."
   (interactive "P")
-  (let ((buffer-list (bibtex-files-expand t))
+  (let ((buffer-list (bibtex-initialize t))
         buffer-key-list current-buf current-keys error-list)
     ;; Check for duplicate keys within BibTeX buffer
     (dolist (buffer buffer-list)
-      (save-excursion
-        (set-buffer buffer)
+      (with-current-buffer buffer
         (let (entry-type key key-list)
           (goto-char (point-min))
           (while (re-search-forward bibtex-entry-head nil t)
@@ -3820,7 +3884,7 @@ Return t if test was successful, nil otherwise."
         (dolist (buffer buffer-list)
           (dolist (key (cdr (assq buffer buffer-key-list)))
             (when (assoc-string key current-keys)
-              (bibtex-find-entry key)
+              (bibtex-search-entry key)
               (push (format "%s:%d: Duplicate key `%s' in %s\n"
                             (buffer-file-name) (bibtex-current-line) key
                             (abbreviate-file-name (buffer-file-name buffer)))
@@ -3831,13 +3895,13 @@ Return t if test was successful, nil otherwise."
         (let ((err-buf "*BibTeX validation errors*"))
           (with-current-buffer (get-buffer-create err-buf)
             (unless (eq major-mode 'compilation-mode) (compilation-mode))
-            (toggle-read-only -1)
-            (delete-region (point-min) (point-max))
-            (insert "BibTeX mode command `bibtex-validate-globally'\n\n")
-            (dolist (err (sort error-list 'string-lessp)) (insert err))
-            (set-buffer-modified-p nil)
-            (toggle-read-only 1)
-            (goto-line 3)) ; first error message
+            (let ((inhibit-read-only t))
+              (delete-region (point-min) (point-max))
+              (insert "BibTeX mode command `bibtex-validate-globally'\n\n")
+              (dolist (err (sort error-list 'string-lessp)) (insert err))
+              (set-buffer-modified-p nil))
+            (goto-char (point-min))
+            (forward-line 2)) ; first error message
           (display-buffer err-buf)
           nil) ; return `nil' (i.e., buffer is invalid)
       (message "No duplicate keys.")
@@ -3887,8 +3951,8 @@ interactive calls."
 
 (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.
+Return list (NAME START-TEXT END-TEXT END STRING-CONST) with field name
+or entry type, 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.
@@ -4008,7 +4072,11 @@ but do not actually kill it.  Optional arg COMMA is as in
            (end (bibtex-end-of-field bounds))
            (beg (bibtex-start-of-field bounds)))
       (goto-char end)
-      (skip-chars-forward ",")
+      ;; Preserve white space at end of BibTeX entry
+      (if (looking-at "[ \t\n]*[)}]")
+          (progn (skip-chars-backward " \t\n")
+                 (setq end (point)))
+        (skip-chars-forward ","))
       (push (list (bibtex-name-in-field bounds) nil
                   (bibtex-text-in-field-bounds bounds))
             bibtex-field-kill-ring)
@@ -4022,7 +4090,7 @@ but do not actually kill it.  Optional arg COMMA is as in
   (setq bibtex-last-kill-command 'field))
 
 (defun bibtex-copy-field-as-kill (&optional comma)
-  "Copy the BibTeX field at point to the kill ring.
+  "Copy the BibTeX field at point to `bibtex-field-kill-ring'.
 Optional arg COMMA is as in `bibtex-enclosing-field'.  It is t for
 interactive calls."
   (interactive (list t))
@@ -4036,6 +4104,8 @@ but do not actually kill it."
   (save-excursion
     (let* ((case-fold-search t)
            (beg (bibtex-beginning-of-entry))
+           (key (progn (looking-at bibtex-any-entry-maybe-empty-head)
+                       (bibtex-key-in-head)))
            (end (progn (bibtex-end-of-entry)
                        (if (re-search-forward
                             bibtex-any-entry-maybe-empty-head nil 'move)
@@ -4049,7 +4119,11 @@ but do not actually kill it."
                   nil))
       (setq bibtex-entry-kill-ring-yank-pointer bibtex-entry-kill-ring)
       (unless copy-only
-        (delete-region beg end))))
+        (delete-region beg end)
+        ;; remove key from `bibtex-reference-keys'.
+        (unless (functionp bibtex-reference-keys)
+          (setq bibtex-reference-keys
+                (delete (cons key t) bibtex-reference-keys))))))
   (setq bibtex-last-kill-command 'entry))
 
 (defun bibtex-copy-entry-as-kill ()
@@ -4083,7 +4157,16 @@ comes the newest one."
   (unless (eq last-command 'bibtex-yank)
     (error "Previous command was not a BibTeX yank"))
   (setq this-command 'bibtex-yank)
-  (let ((inhibit-read-only t))
+  (let ((inhibit-read-only t) key)
+    ;; point is at end of yanked entry
+    (unless (functionp bibtex-reference-keys)
+      ;; remove key of yanked entry from `bibtex-reference-keys'
+      (save-excursion
+        (goto-char (mark t))
+        (if (and (looking-at bibtex-any-entry-maybe-empty-head)
+                 (setq key (bibtex-key-in-head)))
+            (setq bibtex-reference-keys
+                  (delete (cons key t) bibtex-reference-keys)))))
     (delete-region (point) (mark t))
     (bibtex-insert-kill n t)))
 
@@ -4115,7 +4198,7 @@ intermixed with \\[bibtex-pop-previous] (bibtex-pop-previous)."
 
 (defun bibtex-clean-entry (&optional new-key called-by-reformat)
   "Finish editing the current BibTeX entry and clean it up.
-Check that no required fields are empty and formats entry dependent
+Check that no required fields are empty and format entry dependent
 on the value of `bibtex-entry-format'.
 If the reference key of the entry is empty or a prefix argument is given,
 calculate a new reference key.  (Note: this works only if fields in entry
@@ -4133,7 +4216,6 @@ At end of the cleaning process, the functions in
               (error "Not inside a BibTeX entry")))
         (entry-type (bibtex-type-in-head))
         (key (bibtex-key-in-head)))
-    ;; formatting
     (cond ((bibtex-string= entry-type "preamble")
            ;; (bibtex-format-preamble)
            (error "No clean up of @Preamble entries"))
@@ -4179,33 +4261,37 @@ At end of the cleaning process, the functions in
               (setq error (not (bibtex-prepare-new-entry index))
                     start (point)) ; update start
               (save-excursion (insert entry)))
-          (bibtex-find-entry key)
+          (bibtex-search-entry key)
           (setq error (or (/= (point) start)
-                          (bibtex-find-entry key nil end))))
+                          (bibtex-search-entry key nil end))))
         (if error
             (error "New inserted entry yields duplicate key"))
-        (dolist (buffer (bibtex-files-expand))
+        (dolist (buffer (bibtex-initialize))
           (with-current-buffer buffer
             (if (cdr (assoc-string key bibtex-reference-keys))
                 (error "Duplicate key in %s" (buffer-file-name)))))
 
-        ;; Only update the list of keys if it has been built already.
+        ;; Only update `bibtex-strings' and `bibtex-reference-keys'
+        ;; if they have been built already.
         (cond ((eq entry-type 'string)
-               (if (and (listp bibtex-strings)
-                        (not (assoc key bibtex-strings)))
-                   (push (cons key (bibtex-text-in-string
-                                    (bibtex-parse-string) t))
-                           bibtex-strings)))
+               ;; We have a @String entry.
+               (unless (or (functionp bibtex-strings)
+                           (assoc key bibtex-strings))
+                 (push (cons key (bibtex-text-in-string
+                                  (bibtex-parse-string) t))
+                       bibtex-strings)))
               ;; We have a normal entry.
-              ((listp bibtex-reference-keys)
-               (cond ((not (assoc key bibtex-reference-keys))
-                      (push (cons key t) bibtex-reference-keys))
-                     ((not (cdr (assoc key bibtex-reference-keys)))
-                      ;; Turn a crossref key into a header key
-                      (setq bibtex-reference-keys
-                            (cons (cons key t)
-                                  (delete (list key) bibtex-reference-keys)))))
-               ;; Handle crossref key.
+              ((not (functionp bibtex-reference-keys))
+               (let ((found (assoc key bibtex-reference-keys)))
+                 (cond ((not found)
+                        (push (cons key t) bibtex-reference-keys))
+                       ((not (cdr found))
+                        ;; Turn a crossref key into a header key
+                        (setq bibtex-reference-keys
+                              (cons (cons key t)
+                                    (delete (list key) bibtex-reference-keys))))))
+               ;; If entry has a crossref key, it goes into the list
+               ;; `bibtex-reference-keys', too.
                (if (and (nth 1 index)
                         (not (assoc (nth 1 index) bibtex-reference-keys)))
                    (push (list (nth 1 index)) bibtex-reference-keys)))))
@@ -4342,7 +4428,7 @@ If mark is active reformat entries in region, if not in whole buffer."
                            last-comma page-dashes unify-case inherit-booktitle
                            whitespace braces strings))
                 (t
-                 (remove 'required-fields (push 'realign bibtex-entry-format)))))
+                 (cons 'realign (remove 'required-fields bibtex-entry-format)))))
          (reformat-reference-keys
           (if read-options
               (if use-previous-options
@@ -4378,14 +4464,14 @@ With prefix argument READ-OPTIONS non-nil, read options for reformatting
 entries from minibuffer."
   (interactive "*P")
   (message "Starting to validate buffer...")
-  (sit-for 1 nil t)
+  (sit-for 1)
   (bibtex-realign)
   (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)
+    (sit-for 2)
     (bibtex-reformat read-options)
     (goto-char (point-max))
     (message "Buffer is now parsable.  Please save it.")))
@@ -4456,9 +4542,9 @@ An error is signaled if point is outside key or BibTeX field."
            ;; is requested.
            (let (completion-ignore-case)
              (setq choose-completion-string-functions
-                   (lambda (choice buffer mini-p base-size)
+                   (lambda (choice buffer base-position &rest ignored)
                      (setq choose-completion-string-functions nil)
-                     (choose-completion-string choice buffer base-size)
+                     (choose-completion-string choice buffer base-position)
                      (bibtex-complete-crossref-cleanup choice)
                      t)) ; needed by choose-completion-string-functions
              (bibtex-complete-crossref-cleanup
@@ -4474,9 +4560,9 @@ An error is signaled if point is outside key or BibTeX field."
            ;; string completion
            (let ((completion-ignore-case t))
              (setq choose-completion-string-functions
-                   `(lambda (choice buffer mini-p base-size)
+                   `(lambda (choice buffer base-position &rest ignored)
                       (setq choose-completion-string-functions nil)
-                      (choose-completion-string choice buffer base-size)
+                      (choose-completion-string choice buffer base-position)
                       (bibtex-complete-string-cleanup choice ',compl)
                       t)) ; needed by `choose-completion-string-functions'
              (bibtex-complete-string-cleanup (bibtex-complete-internal compl)
@@ -4679,9 +4765,10 @@ Return the URL or nil if none can be generated."
                         (error "Match failed: %s" text)))
                     (if fmt (apply 'format fmt (nreverse obj))
                       (apply 'concat (nreverse obj)))))
-        (if (interactive-p) (message "%s" url))
+        (if (called-interactively-p 'interactive) (message "%s" url))
         (unless no-browse (browse-url url)))
-      (if (and (not url) (interactive-p)) (message "No URL known."))
+      (if (and (not url) (called-interactively-p 'interactive))
+         (message "No URL known."))
       url)))
 
 \f
@@ -4689,5 +4776,4 @@ Return the URL or nil if none can be generated."
 
 (provide 'bibtex)
 
-;; arch-tag: ee2be3af-caad-427f-b42a-d20fad630d04
 ;;; bibtex.el ends here