*** empty log message ***
[bpt/emacs.git] / lisp / textmodes / bibtex.el
index 1544e4f..362aea1 100644 (file)
 
 ;; 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 +25,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:
 
@@ -124,7 +122,7 @@ 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
@@ -185,7 +183,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
@@ -201,15 +199,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
   :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)
@@ -464,8 +462,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.
@@ -481,7 +479,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")
@@ -935,7 +933,7 @@ 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."
   :group 'bibtex
   :type '(repeat (cons (regexp :tag "Regexp")
                        (integer :tag "Number"))))
@@ -978,8 +976,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)
@@ -1035,8 +1033,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]
@@ -1209,7 +1207,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]*\\("
@@ -1291,19 +1289,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
@@ -1546,7 +1531,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))))))))
@@ -1887,254 +1872,285 @@ Formats current entry according to variable `bibtex-entry-format'."
       (setq bibtex-field-strings-opt
             (bibtex-field-re-init bibtex-field-strings-alist 'strings)))
 
-  (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
-                                  page-dashes whitespace inherit-booktitle
-                                  last-comma delimiters unify-case braces
-                                  strings)
-                      bibtex-entry-format))
-            bounds crossref-key req-field-list default-field-list field-list)
-
-        ;; 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 many times 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 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)))
-
-          ;; Do we have a crossref key?
-          (goto-char (point-min))
-          (if (setq bounds (bibtex-search-forward-field "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)
-
-            (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 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 "[}\"]")
+  (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))
+        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))))
-
-              ;; if empty field is a required 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
-                                                  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))
-                       (error "Mandatory field `%s' is missing" (car fname)))))
-              (if alt-list
-                  (cond ((= found 0)
-                         (error "Alternative mandatory field `%s' is missing"
-                                alt-list))
-                        ((> found 1)
-                         (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))))))
+                  (insert (bibtex-entry-left-delimiter)))
+
+                ;; Do we have a crossref key?
+                (goto-char (point-min))
+                (if (setq bounds (bibtex-search-forward-field "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 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 "[}\"]")
+                        (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-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 occured 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.
@@ -2425,61 +2441,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.
@@ -2700,7 +2717,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)
@@ -2856,7 +2873,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))
@@ -2869,7 +2886,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))))
@@ -2884,9 +2901,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)
@@ -3368,23 +3385,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)."
@@ -3394,13 +3411,13 @@ 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
@@ -3452,7 +3469,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.
@@ -3497,7 +3514,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)
@@ -3514,13 +3531,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."
@@ -3528,26 +3547,30 @@ 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-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)))))
+            (if (and (listp bibtex-reference-keys)
+                     (cdr (assoc-string key bibtex-reference-keys)))
+                ;; `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)))
@@ -3572,10 +3595,12 @@ 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,
@@ -3590,7 +3615,7 @@ Return t if preparation was successful or nil if entry KEY already exists."
     (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
@@ -3708,7 +3733,7 @@ Return t if test was successful, nil otherwise."
             (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))))
@@ -3830,7 +3855,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)))
@@ -3897,8 +3922,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.
@@ -4143,15 +4168,13 @@ 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 (undone if error occurs)
-    (atomic-change-group
-      (cond ((bibtex-string= entry-type "preamble")
-             ;; (bibtex-format-preamble)
-             (error "No clean up of @Preamble entries"))
-            ((bibtex-string= entry-type "string")
-             (setq entry-type 'string))
-             ;; (bibtex-format-string)
-            (t (bibtex-format-entry))))
+    (cond ((bibtex-string= entry-type "preamble")
+           ;; (bibtex-format-preamble)
+           (error "No clean up of @Preamble entries"))
+          ((bibtex-string= entry-type "string")
+           (setq entry-type 'string))
+          ;; (bibtex-format-string)
+          (t (bibtex-format-entry)))
     ;; set key
     (when (or new-key (not key))
       (setq key (bibtex-generate-autokey))
@@ -4190,9 +4213,9 @@ 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-initialize))
@@ -4389,14 +4412,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.")))