changeset 67894:d742983a2136

(bibtex-entry-type-whitespace) (bibtex-entry-type-str, bibtex-empty-field-re) (bibtex-search-backward-string, bibtex-preamble-prefix) (bibtex-search-entry, bibtex-enclosing-entry-maybe-empty-head): Removed. (bibtex-any-valid-entry-type): New variable. (bibtex-parse-field-name): Simplify. (bibtex-parse-string, bibtex-search-forward-string): New arg empty-key. (bibtex-preamble-prefix): Include left delimiter. (bibtex-search-forward-field, bibtex-search-backward-field): Allow unbounded search past entry boundaries (required by bibtex-pop). (bibtex-text-in-field-bounds): Use push. (bibtex-text-in-field): Do not use bibtex-narrow-to-entry. (bibtex-parse-preamble, bibtex-valid-entry) (bibtex-beginning-first-field): New functions. (bibtex-skip-to-valid-entry): Use bibtex-valid-entry. Fix regexp. (bibtex-map-entries): Fix docstring. (bibtex-flash-head): New arg prompt. Simplify. (bibtex-enclosing-field): Include code of bibtex-inside-field. (bibtex-insert-kill): Simplify. Always insert text past the current field or entry. (bibtex-format-entry): Use bibtex-parse-field. (bibtex-pop): Use bibtex-beginning-of-entry and bibtex-end-of-entry to initiate the search. Insert empty field if we found ourselves. (bibtex-print-help-message): New args field and comma. Handle entry keys. (bibtex-make-field): Use bibtex-beginning-of-entry. (bibtex-end-of-entry): Use bibtex-valid-entry. Recognize any invalid entry. (bibtex-validate): Use bibtex-valid-entry and bibtex-parse-string. Handle preambles. Simplify code for thorough test. (bibtex-next-field, bibtex-find-text, bibtex-find-text-internal): New arg comma. Handle entry heads. (bibtex-remove-OPT-or-ALT, bibtex-remove-delimiters) (bibtex-kill-field, bibtex-copy-field-as-kil, bibtex-empty-field): New arg comma. (bibtex-kill-entry): Use bibtex-any-entry-maybe-empty-head. (bibtex-fill-field): Simplify. (bibtex-fill-entry): Use bibtex-beginning-first-field and bibtex-parse-field. (bibtex-convert-alien): Do not wait before calling bibtex-validate. (bibtex-complete): Use bibtex-parse-preamble.
author Roland Winkler <Roland.Winkler@physik.uni-erlangen.de>
date Thu, 29 Dec 2005 15:23:52 +0000
parents 6af57e0a5d45
children 38a44c9a355f
files lisp/textmodes/bibtex.el
diffstat 1 files changed, 499 insertions(+), 556 deletions(-) [+]
line wrap: on
line diff
--- a/lisp/textmodes/bibtex.el	Thu Dec 29 14:20:23 2005 +0000
+++ b/lisp/textmodes/bibtex.el	Thu Dec 29 15:23:52 2005 +0000
@@ -853,7 +853,7 @@
   :group 'bibtex
   :type 'boolean)
 
-;; `bibtex-font-lock-keywords' is a user option as well, but since the
+;; `bibtex-font-lock-keywords' is a user option, too.  But since the
 ;; patterns used to define this variable are defined in a later
 ;; section of this file, it is defined later.
 
@@ -1091,7 +1091,7 @@
   "Regexp matching the name of a BibTeX field.")
 
 (defconst bibtex-name-part
-  (concat ",[ \t\n]*\\(" bibtex-field-name "\\)[ \t\n]*=")
+  (concat ",[ \t\n]*\\(" bibtex-field-name "\\)")
   "Regexp matching the name part of a BibTeX field.")
 
 (defconst bibtex-reference-key "[][[:alnum:].:;?!`'/*@+|()<>&_^$-]+"
@@ -1105,16 +1105,6 @@
           (regexp-opt (mapcar 'car bibtex-entry-field-alist)) "\\)")
   "Regexp matching the name of a BibTeX entry.")
 
-(defvar bibtex-entry-type-whitespace
-  (concat "[ \t]*" bibtex-entry-type)
-  "Regexp matching the name of a BibTeX entry preceded by whitespace.")
-
-(defvar bibtex-entry-type-str
-  (concat "@[ \t]*\\(?:"
-          (regexp-opt (append '("String")
-                              (mapcar 'car bibtex-entry-field-alist))) "\\)")
-  "Regexp matching the name of a BibTeX entry (including @String).")
-
 (defvar bibtex-entry-head
   (concat "^[ \t]*\\("
           bibtex-entry-type
@@ -1132,15 +1122,18 @@
           bibtex-reference-key "\\)?")
   "Regexp matching the header line of any BibTeX entry (possibly without key).")
 
+(defvar bibtex-any-valid-entry-type
+  (concat "^[ \t]*@[ \t]*\\(?:"
+          (regexp-opt (append '("String" "Preamble")
+                              (mapcar 'car bibtex-entry-field-alist))) "\\)")
+  "Regexp matching any valid BibTeX entry (including String and Preamble).")
+
 (defconst bibtex-type-in-head 1
   "Regexp subexpression number of the type part in `bibtex-entry-head'.")
 
 (defconst bibtex-key-in-head 2
   "Regexp subexpression number of the key part in `bibtex-entry-head'.")
 
-(defconst bibtex-empty-field-re "\\`\\(\"\"\\|{}\\)\\'"
-  "Regexp matching the text part (as a string) of an empty field.")
-
 (defconst bibtex-string-type "^[ \t]*\\(@[ \t]*String\\)[ \t]*[({][ \t\n]*"
    "Regexp matching the name of a BibTeX String entry.")
 
@@ -1148,8 +1141,9 @@
   (concat bibtex-string-type "\\(" bibtex-reference-key "\\)?")
   "Regexp matching the header line of a BibTeX String entry.")
 
-(defconst bibtex-preamble-prefix "[ \t]*@[ \t]*Preamble[ \t]*"
-  "Regexp matching the prefix part of a preamble.")
+(defconst bibtex-preamble-prefix
+  "[ \t]*\\(@[ \t]*Preamble\\)[ \t]*[({][ \t\n]*"
+  "Regexp matching the prefix part of a BibTeX Preamble entry.")
 
 (defconst bibtex-font-lock-syntactic-keywords
   `((,(concat "^[ \t]*\\(" (substring bibtex-comment-start 0 1) "\\)"
@@ -1229,12 +1223,9 @@
 part and end position of the match.  Move point to end of field name.
 If `bibtex-autoadd-commas' is non-nil add missing comma at end of preceding
 BibTeX field as necessary."
-  (cond ((looking-at ",[ \t\n]*")
-         (let ((start (point)))
-           (goto-char (match-end 0))
-           (when (looking-at bibtex-field-name)
-             (goto-char (match-end 0))
-             (list start (match-beginning 0) (match-end 0)))))
+  (cond ((looking-at bibtex-name-part)
+         (goto-char (match-end 0))
+         (list (match-beginning 0) (match-beginning 1) (match-end 0)))
         ;; Maybe add a missing comma.
         ((and bibtex-autoadd-commas
               (looking-at (concat "[ \t\n]*\\(?:" bibtex-field-name
@@ -1334,60 +1325,71 @@
   "Search forward to find a BibTeX field of name NAME.
 If a syntactically correct field is found, return a pair containing
 the boundaries of the name and text parts of the field.  The search
-is limited by optional arg BOUND or if nil by the end of the current
-entry.  Do not move point."
+is limited by optional arg BOUND.  If BOUND is t the search is limited
+by the end of the current entry.  Do not move point."
   (save-match-data
     (save-excursion
-      (if bound
-          ;; If the search is bounded we need not worry we could overshoot.
-          ;; This is indeed the case when `bibtex-search-forward-field' is
-          ;; called many times.  So we optimize this part of this function.
-          (let ((name-part (concat ",[ \t\n]*\\(" name "\\)[ \t\n]*=[ \t\n]*"))
-                (case-fold-search t) left right)
-            (while (and (not right)
-                        (re-search-forward name-part bound t))
-              (setq left (list (match-beginning 0) (match-beginning 1)
-                               (match-end 1))
-                    ;; Don't worry that the field text could be past bound.
-                    right (bibtex-parse-field-text)))
-            (if right (cons left right)))
-        (let ((regexp (concat bibtex-name-part "\\|"
-                              bibtex-any-entry-maybe-empty-head))
-              (case-fold-search t) bounds)
-          (catch 'done
-            (if (looking-at "[ \t]*@") (goto-char (match-end 0)))
-            (while (and (not bounds)
-                        (re-search-forward regexp nil t))
-              (if (match-beginning 2)
-                  ;; We found a new entry
-                  (throw 'done nil)
-                ;; We found a field
-                (goto-char (match-beginning 0))
-                (setq bounds (bibtex-parse-field))))
-            ;; Step through all fields so that we cannot overshoot.
-            (while bounds
-              (goto-char (bibtex-start-of-name-in-field bounds))
-              (if (looking-at name) (throw 'done bounds))
-              (goto-char (bibtex-end-of-field bounds))
-              (setq bounds (bibtex-parse-field)))))))))
+      (if (eq bound t)
+          (let ((regexp (concat bibtex-name-part "[ \t\n]*=\\|"
+                                bibtex-any-entry-maybe-empty-head))
+                (case-fold-search t) bounds)
+            (catch 'done
+              (if (looking-at "[ \t]*@") (goto-char (match-end 0)))
+              (while (and (not bounds)
+                          (re-search-forward regexp nil t))
+                (if (match-beginning 2)
+                    ;; We found a new entry
+                    (throw 'done nil)
+                  ;; We found a field
+                  (goto-char (match-beginning 0))
+                  (setq bounds (bibtex-parse-field))))
+              ;; Step through all fields so that we cannot overshoot.
+              (while bounds
+                (goto-char (bibtex-start-of-name-in-field bounds))
+                (if (looking-at name) (throw 'done bounds))
+                (goto-char (bibtex-end-of-field bounds))
+                (setq bounds (bibtex-parse-field)))))
+        ;; Bounded search or bound is nil (i.e. we cannot overshoot).
+        ;; Indeed, the search is bounded when `bibtex-search-forward-field'
+        ;; is called many times.  So we optimize this part of this function.
+        (let ((name-part (concat ",[ \t\n]*\\(" name "\\)[ \t\n]*=[ \t\n]*"))
+              (case-fold-search t) left right)
+          (while (and (not right)
+                      (re-search-forward name-part bound t))
+            (setq left (list (match-beginning 0) (match-beginning 1)
+                             (match-end 1))
+                  ;; Don't worry that the field text could be past bound.
+                  right (bibtex-parse-field-text)))
+          (if right (cons left right)))))))
 
 (defun bibtex-search-backward-field (name &optional bound)
   "Search backward to find a BibTeX field of name NAME.
 If a syntactically correct field is found, return a pair containing
 the boundaries of the name and text parts of the field.  The search
-is limited by the optional arg BOUND.  If BOUND is nil the search is
+is limited by the optional arg BOUND.  If BOUND is t the search is
 limited by the beginning of the current entry.  Do not move point."
   (save-match-data
-    (save-excursion
-      (let ((name-part (concat ",[ \t\n]*\\(?:" name "\\)[ \t\n]*="))
-            (case-fold-search t)
-            bounds)
-        (unless bound (setq bound (save-excursion (bibtex-beginning-of-entry))))
-        (while (and (not bounds)
-                    (search-backward "," bound t)
-                    (looking-at name-part))
-          (setq bounds (bibtex-parse-field)))
-        bounds))))
+    (if (eq bound t)
+        (setq bound (save-excursion (bibtex-beginning-of-entry))))
+    (let ((name-part (concat ",[ \t\n]*\\(" name "\\)[ \t\n]*=[ \t\n]*"))
+          (case-fold-search t) left right)
+      (save-excursion
+        ;; the parsing functions are not designed for parsing backwards :-(
+        (when (search-backward "," bound t)
+          (or (save-excursion
+                (when (looking-at name-part)
+                  (setq left (list (match-beginning 0) (match-beginning 1)
+                                   (match-end 1)))
+                  (goto-char (match-end 0))
+                  (setq right (bibtex-parse-field-text))))
+              (while (and (not right)
+                          (re-search-backward name-part bound t))
+                (setq left (list (match-beginning 0) (match-beginning 1)
+                                 (match-end 1)))
+                (save-excursion
+                  (goto-char (match-end 0))
+                  (setq right (bibtex-parse-field-text)))))
+          (if right (cons left right)))))))
 
 (defun bibtex-name-in-field (bounds &optional remove-opt-alt)
   "Get content of name in BibTeX field defined via BOUNDS.
@@ -1407,25 +1409,22 @@
 If `bibtex-expand-strings' is non-nil, also expand BibTeX strings."
   (if content
       (save-excursion
+        (goto-char (bibtex-start-of-text-in-field bounds))
         (let ((epoint (bibtex-end-of-text-in-field bounds))
-              content opoint temp)
-          (goto-char (bibtex-start-of-text-in-field bounds))
+              content opoint)
           (while (< (setq opoint (point)) epoint)
-            (cond ((looking-at bibtex-field-const)
-                   (let ((mtch (match-string-no-properties 0)))
-                     (goto-char (match-end 0))
-                     (setq temp (if bibtex-expand-strings
-                                    (cdr (assoc-string mtch (bibtex-strings) t)))
-                           content (concat content (or temp mtch)))))
-
-                  ((setq temp (bibtex-parse-field-string))
-                   (setq content (concat content (buffer-substring-no-properties
-                                                  (1+ (car temp))
-                                                  (1- (cdr temp)))))
-                   (goto-char (cdr temp)))
-                  (t (error "Malformed text field")))
+            (if (looking-at bibtex-field-const)
+                (let ((mtch (match-string-no-properties 0)))
+                  (push (if bibtex-expand-strings
+                            (cdr (assoc-string mtch (bibtex-strings) t))
+                          mtch) content)
+                  (goto-char (match-end 0)))
+              (let ((bounds (bibtex-parse-field-string)))
+                (push (buffer-substring-no-properties
+                       (1+ (car bounds)) (1- (cdr bounds))) content)
+                (goto-char (cdr bounds))))
             (re-search-forward "\\=[ \t\n]*#[ \t\n]*" nil t))
-          content))
+          (apply 'concat (nreverse content))))
     (buffer-substring-no-properties (bibtex-start-of-text-in-field bounds)
                                     (bibtex-end-of-text-in-field bounds))))
 
@@ -1434,19 +1433,15 @@
 Return nil if not found.
 If optional arg FOLLOW-CROSSREF is non-nil, follow crossref."
   (save-excursion
-    (save-restriction
-      ;; We want to jump back and forth while searching FIELD
-      (bibtex-narrow-to-entry)
-      (goto-char (point-min))
-      (let ((bounds (bibtex-search-forward-field field (point-max)))
-            crossref-field)
-        (cond (bounds (bibtex-text-in-field-bounds bounds t))
-              ((and follow-crossref
-                    (progn (goto-char (point-min))
-                           (setq bounds (bibtex-search-forward-field
-                                         "\\(OPT\\)?crossref" (point-max)))))
-               (setq crossref-field (bibtex-text-in-field-bounds bounds t))
-               (widen)
+    (let* ((end (if follow-crossref (bibtex-end-of-entry) t))
+           (beg (bibtex-beginning-of-entry)) ; move point
+           (bounds (bibtex-search-forward-field field end)))
+      (cond (bounds (bibtex-text-in-field-bounds bounds t))
+            ((and follow-crossref
+                  (progn (goto-char beg)
+                         (setq bounds (bibtex-search-forward-field
+                                       "\\(OPT\\)?crossref" end))))
+             (let ((crossref-field (bibtex-text-in-field-bounds bounds t)))
                (if (bibtex-find-crossref crossref-field)
                    ;; Do not pass FOLLOW-CROSSREF because we want
                    ;; to follow crossrefs only one level of recursion.
@@ -1487,42 +1482,28 @@
               (nth 1 bounds)
               (match-end 0))))))
 
-(defun bibtex-parse-string ()
+(defun bibtex-parse-string (&optional empty-key)
   "Parse a BibTeX string entry beginning at the position of point.
 If a syntactically correct entry is found, return a cons pair containing
 the boundaries of the reference key and text parts of the entry.
-Do not move point."
-  (bibtex-parse-association 'bibtex-parse-string-prefix
-                            'bibtex-parse-string-postfix))
-
-(defun bibtex-search-forward-string ()
+If EMPTY-KEY is non-nil, key may be empty.  Do not move point."
+  (let ((bibtex-string-empty-key empty-key))
+    (bibtex-parse-association 'bibtex-parse-string-prefix
+                              'bibtex-parse-string-postfix)))
+
+(defun bibtex-search-forward-string (&optional empty-key)
   "Search forward to find a BibTeX string entry.
 If a syntactically correct entry is found, a pair containing the boundaries of
-the reference key and text parts of the string is returned.  Do not move point."
+the reference key and text parts of the string is returned.
+If EMPTY-KEY is non-nil, key may be empty.  Do not move point."
   (save-excursion
     (save-match-data
-      (let ((case-fold-search t)
-            boundaries)
-        (while (and (not boundaries)
+      (let ((case-fold-search t) bounds)
+        (while (and (not bounds)
                     (search-forward-regexp bibtex-string-type nil t))
-          (goto-char (match-beginning 0))
-          (unless (setq boundaries (bibtex-parse-string))
-            (forward-char 1)))
-        boundaries))))
-
-(defun bibtex-search-backward-string ()
-  "Search backward to find a BibTeX string entry.
-If a syntactically correct entry is found, a pair containing the boundaries of
-the reference key and text parts of the field is returned.  Do not move point."
-  (save-excursion
-    (save-match-data
-      (let ((case-fold-search t)
-            boundaries)
-        (while (and (not boundaries)
-                    (search-backward-regexp bibtex-string-type nil t))
-          (goto-char (match-beginning 0))
-          (setq boundaries (bibtex-parse-string)))
-        boundaries))))
+          (save-excursion (goto-char (match-beginning 0))
+                          (setq bounds (bibtex-parse-string empty-key))))
+        bounds))))
 
 (defun bibtex-reference-key-in-string (bounds)
   "Return the key part of a BibTeX string defined via BOUNDS"
@@ -1554,14 +1535,15 @@
   (or (match-string-no-properties bibtex-key-in-head)
       empty))
 
-(defun bibtex-preamble-prefix (&optional delim)
-  "Parse the prefix part of a BibTeX Preamble.
-Point must be at beginning of prefix part.  If prefix is found move point
-to its end and return position of point.  If optional arg DELIM is non-nil,
-move past the opening delimiter.  If no preamble is found return nil."
+(defun bibtex-parse-preamble ()
+  "Parse BibTeX preamble.
+Point must be at beginning of preamble.  Do not move point."
   (let ((case-fold-search t))
-    (re-search-forward (concat "\\=" bibtex-preamble-prefix
-                               (if delim "[({][ \t\n]*")) nil t)))
+    (when (looking-at bibtex-preamble-prefix)
+      (let ((start (match-beginning 0)) (pref-start (match-beginning 1))
+            (bounds (save-excursion (goto-char (match-end 0))
+                                    (bibtex-parse-string-postfix))))
+        (if bounds (cons (list start pref-start) bounds))))))
 
 ;; Helper Functions
 
@@ -1579,6 +1561,35 @@
   (+ (count-lines 1 (point))
      (if (bolp) 1 0)))
 
+(defun bibtex-valid-entry (&optional empty-key)
+  "Parse a valid BibTeX entry (maybe without key if EMPTY-KEY is t).
+A valid entry is a syntactical correct one with type contained in
+`bibtex-entry-field-alist'.  Ignore @String and @Preamble entries.
+Return a cons pair with buffer positions of beginning and end of entry
+if a valid entry is found, nil otherwise.  Do not move point.
+After a call to this function `match-data' corresponds to the header
+of the entry, see regexp `bibtex-entry-head'."
+  (let ((case-fold-search t) end)
+    (if (looking-at (if empty-key bibtex-entry-maybe-empty-head
+                    bibtex-entry-head))
+        (save-excursion
+          (save-match-data
+            (goto-char (match-end 0))
+            (let ((entry-closer
+                   (if (save-excursion
+                         (goto-char (match-end bibtex-type-in-head))
+                         (looking-at "[ \t]*("))
+                       ",?[ \t\n]*)" ;; entry opened with `('
+                     ",?[ \t\n]*}")) ;; entry opened with `{'
+                  bounds)
+              (skip-chars-forward " \t\n")
+              ;; loop over all BibTeX fields
+              (while (setq bounds (bibtex-parse-field))
+                (goto-char (bibtex-end-of-field bounds)))
+              ;; This matches the infix* part.
+              (if (looking-at entry-closer) (setq end (match-end 0)))))
+          (if end (cons (match-beginning 0) end))))))
+
 (defun bibtex-skip-to-valid-entry (&optional backward)
   "Move point to beginning of the next valid BibTeX entry.
 Do not move if we are already at beginning of a valid BibTeX entry.
@@ -1590,32 +1601,27 @@
 entry is found, nil otherwise."
   (interactive "P")
   (let ((case-fold-search t)
-        found)
+        found bounds)
     (beginning-of-line)
     ;; Loop till we look at a valid entry.
     (while (not (or found (if backward (bobp) (eobp))))
-      (let ((pnt (point))
-            bounds)
-        (cond ((or (and (looking-at bibtex-entry-type-whitespace)
-                        (setq found (bibtex-search-entry nil nil t))
-                        (equal (match-beginning 0) pnt))
-                   (and (not bibtex-sort-ignore-string-entries)
-                        (setq bounds (bibtex-parse-string))
-                        (setq found (cons (bibtex-start-of-field bounds)
-                                          (bibtex-end-of-string bounds)))))
-               (goto-char pnt))
-              (backward (re-search-backward "^[ \t]*@" nil 'move))
-              (t (re-search-forward "\\=[ \t]*@" nil t) ;; don't be stuck
-                 (if (re-search-forward "^[ \t]*@" nil 'move)
-                     (goto-char (match-beginning 0)))))))
+      (cond ((setq found (or (bibtex-valid-entry)
+                             (and (not bibtex-sort-ignore-string-entries)
+                                  (setq bounds (bibtex-parse-string))
+                                  (cons (bibtex-start-of-field bounds)
+                                        (bibtex-end-of-string bounds))))))
+            (backward (re-search-backward "^[ \t]*@" nil 'move))
+            (t (if (re-search-forward "\n\\([ \t]*@\\)" nil 'move)
+                   (goto-char (match-beginning 1))))))
     found))
 
 (defun bibtex-map-entries (fun)
   "Call FUN for each BibTeX entry in buffer (possibly narrowed).
 FUN is called with three arguments, the key of the entry and the buffer
-positions (marker) of beginning and end of entry.  Point is inside the entry.
-If `bibtex-sort-ignore-string-entries' is non-nil, FUN is not called for
-@String entries."
+positions of beginning and end of entry.  Also, point is at beginning of
+entry and `match-data' corresponds to the header of the entry,
+see regexp `bibtex-entry-head'.  If `bibtex-sort-ignore-string-entries'
+is non-nil, FUN is not called for @String entries."
   (let ((case-fold-search t)
         found)
     (save-excursion
@@ -1673,75 +1679,19 @@
       "}"
     ")"))
 
-(defun bibtex-search-entry (empty-head &optional bound noerror backward)
-  "Search for a BibTeX entry (maybe without reference key if EMPTY-HEAD is t).
-BOUND and NOERROR are exactly as in `re-search-forward'.  If BACKWARD
-is non-nil, search in reverse direction.  Move point past the closing
-delimiter (at the beginning of entry if BACKWARD is non-nil).
-Return a cons pair with buffer positions of beginning and end of entry.
-After a call to this function `match-data' corresponds to the head part
-of the entry, see regexp `bibtex-entry-head'.
-Ignore @String and @Preamble entries."
-  (let ((pnt (point))
-        (entry-head-re (if empty-head
-                           bibtex-entry-maybe-empty-head
-                         bibtex-entry-head)))
-    (if backward
-        (let (found)
-          (while (and (not found)
-                      (re-search-backward entry-head-re bound noerror))
-            (setq found (bibtex-search-entry empty-head pnt t)))
-          (cond (found
-                 (goto-char (match-beginning 0))
-                 found)
-                ((not noerror)  ;; yell
-                 (error "Backward search of BibTeX entry failed"))
-                (t (if (eq noerror t) (goto-char pnt)) ;; don't move
-                   nil)))
-      (let (found)
-        (unless bound (setq bound (point-max)))
-        (while (and (not found)
-                    (re-search-forward entry-head-re bound noerror))
-          (save-match-data
-            (let ((entry-closer
-                   (if (save-excursion
-                         (goto-char (match-end bibtex-type-in-head))
-                         (looking-at "[ \t]*("))
-                       ",?[ \t\n]*)" ;; entry opened with `('
-                     ",?[ \t\n]*}")) ;; entry opened with `{'
-                  bounds)
-              (skip-chars-forward " \t\n" bound)
-              ;; loop over all BibTeX fields
-              (while (and (setq bounds (bibtex-parse-field))
-                          (<= (bibtex-end-of-field bounds) bound))
-                (goto-char (bibtex-end-of-field bounds)))
-              ;; This matches the infix* part.
-              (when (and (looking-at entry-closer)
-                         (<= (match-end 0) bound))
-                (goto-char (match-end 0))
-                (setq found t)))))
-        (cond (found
-               (cons (match-beginning 0) (point)))
-              ((not noerror) ;; yell
-               (error "Search of BibTeX entry failed"))
-              (t (if (eq noerror t) (goto-char pnt)) ;; don't move
-                 nil))))))
-
-(defun bibtex-flash-head ()
+(defun bibtex-flash-head (prompt)
   "Flash at BibTeX entry head before point, if exists."
   (let ((case-fold-search t)
-        (pnt (point))
-        flash)
+        (pnt (point)))
     (save-excursion
       (bibtex-beginning-of-entry)
       (when (and (looking-at bibtex-any-entry-maybe-empty-head)
                  (< (point) pnt))
         (goto-char (match-beginning bibtex-type-in-head))
-        (setq flash (match-end bibtex-key-in-head))
         (if (pos-visible-in-window-p (point))
             (sit-for 1)
-          (message "From: %s"
-                   (buffer-substring (point) flash)))))))
+          (message "%s%s" prompt (buffer-substring-no-properties
+                                  (point) (match-end bibtex-key-in-head))))))))
 
 (defun bibtex-make-optional-field (field)
   "Make an optional field named FIELD in current BibTeX entry."
@@ -1772,66 +1722,55 @@
   (bibtex-skip-to-valid-entry)
   (point))
 
-(defun bibtex-inside-field ()
-  "Try to avoid point being at end of a BibTeX field."
-  (end-of-line)
-  (skip-chars-backward " \t")
-  (if (= (preceding-char) ?,)
-      (forward-char -2))
-  (if (or (= (preceding-char) ?})
-          (= (preceding-char) ?\"))
-      (forward-char -1)))
-
-(defun bibtex-enclosing-field (&optional noerr)
+(defun bibtex-enclosing-field (&optional comma noerr)
   "Search for BibTeX field enclosing point.
+For `bibtex-mode''s internal algorithms, a field begins at the comma
+following the preceding field.  Usually, this is not what the user expects.
+Thus if COMMA is non-nil, the \"current field\" includes the terminating comma.
 Unless NOERR is non-nil, signal an error if no enclosing field is found.
 On success return bounds, nil otherwise.  Do not move point."
-  (let ((bounds (bibtex-search-backward-field bibtex-field-name)))
-    (if (and bounds
-             (<= (bibtex-start-of-field bounds) (point))
-             (>= (bibtex-end-of-field bounds) (point)))
-        bounds
-      (unless noerr
-        (error "Can't find enclosing BibTeX field")))))
-
-(defun bibtex-enclosing-entry-maybe-empty-head ()
-  "Search for BibTeX entry enclosing point.  Move point to end of entry.
-Beginning (but not end) of entry is given by (`match-beginning' 0)."
-  (let ((case-fold-search t)
-        (old-point (point)))
-    (unless (re-search-backward bibtex-entry-maybe-empty-head nil t)
-      (goto-char old-point)
-      (error "Can't find beginning of enclosing BibTeX entry"))
-    (goto-char (match-beginning bibtex-type-in-head))
-    (unless (bibtex-search-entry t nil t)
-      (goto-char old-point)
-      (error "Can't find end of enclosing BibTeX entry"))))
-
-(defun bibtex-insert-kill (n)
-  "Reinsert the Nth stretch of killed BibTeX text."
-  (if (not bibtex-last-kill-command)
-      (error "BibTeX kill ring is empty")
-    (let* ((kr (if (eq bibtex-last-kill-command 'field)
-                   'bibtex-field-kill-ring
-                 'bibtex-entry-kill-ring))
-           (kryp (if (eq bibtex-last-kill-command 'field)
-                     'bibtex-field-kill-ring-yank-pointer
-                   'bibtex-entry-kill-ring-yank-pointer))
-           (current (car (set kryp (nthcdr (mod (- n (length (eval kryp)))
-                                                (length (eval kr)))
-                                           (eval kr))))))
-      (if (eq bibtex-last-kill-command 'field)
-          (progn
-            (bibtex-find-text)
-            (if (looking-at "[}\"]")
-                (forward-char))
-            (set-mark (point))
-            (message "Mark set")
-            (bibtex-make-field current t))
-        (unless (eobp) (bibtex-beginning-of-entry))
-        (set-mark (point))
-        (message "Mark set")
-        (insert current)))))
+  (save-excursion
+    (when comma
+      (end-of-line)
+      (skip-chars-backward " \t")
+      (if (= (preceding-char) ?,) (forward-char -1)))
+
+    (let ((bounds (bibtex-search-backward-field bibtex-field-name t)))
+      (cond ((and bounds
+                  (<= (bibtex-start-of-field bounds) (point))
+                  (>= (bibtex-end-of-field bounds) (point)))
+             bounds)
+            ((not noerr)
+             (error "Can't find enclosing BibTeX field"))))))
+
+(defun bibtex-beginning-first-field (&optional beg)
+  "Move point to beginning of first field.
+Optional arg BEG is beginning of entry."
+  (if beg (goto-char beg) (bibtex-beginning-of-entry))
+  (looking-at bibtex-any-entry-maybe-empty-head)
+  (goto-char (match-end 0)))
+
+(defun bibtex-insert-kill (n &optional comma)
+  "Reinsert the Nth stretch of killed BibTeX text (field or entry).
+Optional arg COMMA is as in `bibtex-enclosing-field'."
+  (unless bibtex-last-kill-command (error "BibTeX kill ring is empty"))
+  (let ((fun (lambda (kryp kr) ;; adapted from `current-kill'
+               (car (set kryp (nthcdr (mod (- n (length (eval kryp)))
+                                           (length kr)) kr))))))
+    (if (eq bibtex-last-kill-command 'field)
+        (progn
+          ;; insert past the current field
+          (goto-char (bibtex-end-of-field (bibtex-enclosing-field comma)))
+          (set-mark (point))
+          (message "Mark set")
+          (bibtex-make-field (funcall fun 'bibtex-field-kill-ring-yank-pointer
+                                      bibtex-field-kill-ring) t))
+      ;; insert past the current entry
+      (bibtex-skip-to-valid-entry)
+      (set-mark (point))
+      (message "Mark set")
+      (insert (funcall fun 'bibtex-entry-kill-ring-yank-pointer
+                       bibtex-entry-kill-ring)))))
 
 (defun bibtex-format-entry ()
   "Helper function for `bibtex-clean-entry'.
@@ -1900,9 +1839,8 @@
             (error "All alternatives are empty"))
 
         ;; process all fields
-        (goto-char (point-min))
-        (while (setq bounds (bibtex-search-forward-field
-                             bibtex-field-name (point-max)))
+        (bibtex-beginning-first-field (point-min))
+        (while (setq bounds (bibtex-parse-field))
           (let* ((beg-field (copy-marker (bibtex-start-of-field bounds)))
                  (end-field (copy-marker (bibtex-end-of-field bounds) t))
                  (beg-name  (copy-marker (bibtex-start-of-name-in-field bounds)))
@@ -2040,10 +1978,6 @@
                        (error "Alternative fields `%s' are defined %s times"
                               altlist found))))))
 
-        ;; update point
-        (if (looking-at (bibtex-field-right-delimiter))
-            (forward-char))
-
         ;; update comma after last field
         (if (memq 'last-comma format)
             (cond ((and bibtex-comma-after-last-field
@@ -2565,6 +2499,7 @@
   "Cleanup after inserting string STR.
 Remove enclosing field delimiters for STR.  Display message with
 expansion of STR using expansion list COMPL."
+  ;; point is at position inside field where completion was requested
   (save-excursion
     (let ((abbr (cdr (if (stringp str)
                          (assoc-string str compl t)))))
@@ -2624,50 +2559,52 @@
 (defun bibtex-pop (arg direction)
   "Fill current field from the ARGth same field's text in DIRECTION.
 Generic function used by `bibtex-pop-previous' and `bibtex-pop-next'."
-  (bibtex-find-text)
-  (save-excursion
-    ;; parse current field
-    (bibtex-inside-field)
-    (let* ((case-fold-search t)
-           (bounds (bibtex-enclosing-field))
-           (start-old-text (bibtex-start-of-text-in-field bounds))
-           (stop-old-text (bibtex-end-of-text-in-field bounds))
-           (field-name (bibtex-name-in-field bounds t)))
+  ;; parse current field
+  (let* ((bounds (bibtex-enclosing-field t))
+         (start-old-field (bibtex-start-of-field bounds))
+         (start-old-text (bibtex-start-of-text-in-field bounds))
+         (end-old-text (bibtex-end-of-text-in-field bounds))
+         (field-name (bibtex-name-in-field bounds t))
+         failure)
+    (save-excursion
       ;; if executed several times in a row, start each search where
       ;; the last one was finished
-      (unless (eq last-command 'bibtex-pop)
-        (bibtex-enclosing-entry-maybe-empty-head)
-        (setq bibtex-pop-previous-search-point (match-beginning 0)
-              bibtex-pop-next-search-point (point)))
-      (if (eq direction 'previous)
-          (goto-char bibtex-pop-previous-search-point)
-        (goto-char bibtex-pop-next-search-point))
-      ;; Now search for arg'th previous/next similar field
-      (let (bounds failure new-text)
-        (while (and (not failure)
-                    (> arg 0))
-          (cond ((eq direction 'previous)
-                 (if (setq bounds (bibtex-search-backward-field field-name))
-                     (goto-char (bibtex-start-of-field bounds))
-                   (setq failure t)))
-                ((eq direction 'next)
-                 (if (setq bounds (bibtex-search-forward-field field-name))
-                     (goto-char (bibtex-end-of-field bounds))
-                   (setq failure t))))
-          (setq arg (- arg 1)))
-        (if failure
-            (error "No %s matching BibTeX field"
-                   (if (eq direction 'previous) "previous" "next"))
-          ;; Found a matching field.  Remember boundaries.
-          (setq bibtex-pop-previous-search-point (bibtex-start-of-field bounds)
-                bibtex-pop-next-search-point (bibtex-end-of-field bounds)
-                new-text (bibtex-text-in-field-bounds bounds))
-          (bibtex-flash-head)
+      (cond ((eq last-command 'bibtex-pop)
+             (goto-char (if (eq direction 'previous)
+                            bibtex-pop-previous-search-point
+                          bibtex-pop-next-search-point)))
+            ((eq direction 'previous)
+             (bibtex-beginning-of-entry))
+            (t (bibtex-end-of-entry)))
+      ;; Search for arg'th previous/next similar field
+      (while (and (not failure)
+                  (>= (setq arg (1- arg)) 0))
+        ;; The search of BibTeX fields is not bounded by entry boundaries
+        (if (eq direction 'previous)
+            (if (setq bounds (bibtex-search-backward-field field-name))
+                (goto-char (bibtex-start-of-field bounds))
+              (setq failure t))
+          (if (setq bounds (bibtex-search-forward-field field-name))
+              (goto-char (bibtex-end-of-field bounds))
+            (setq failure t))))
+      (if failure
+          (error "No %s matching BibTeX field"
+                 (if (eq direction 'previous) "previous" "next"))
+        ;; Found a matching field.  Remember boundaries.
+        (let ((new-text (bibtex-text-in-field-bounds bounds))
+              (nbeg (copy-marker (bibtex-start-of-field bounds)))
+              (nend (copy-marker (bibtex-end-of-field bounds))))
+          (bibtex-flash-head "From: ")
           ;; Go back to where we started, delete old text, and pop new.
-          (goto-char stop-old-text)
-          (delete-region start-old-text stop-old-text)
-          (insert new-text)))))
-  (bibtex-find-text)
+          (goto-char end-old-text)
+          (delete-region start-old-text end-old-text)
+          (if (= nbeg start-old-field)
+              (insert (bibtex-field-left-delimiter)
+                      (bibtex-field-right-delimiter))
+            (insert new-text))
+          (setq bibtex-pop-previous-search-point (marker-position nbeg)
+                bibtex-pop-next-search-point (marker-position nend))))))
+  (bibtex-find-text nil nil nil t)
   (setq this-command 'bibtex-pop))
 
 (defun bibtex-beginning-of-field ()
@@ -2861,7 +2798,7 @@
   (let ((e (assoc-string entry-type bibtex-entry-field-alist t))
         required optional)
     (unless e
-      (error "BibTeX entry type %s not defined" entry-type))
+      (error "Fields for BibTeX entry type %s not defined" entry-type))
     (if (and (member-ignore-case entry-type bibtex-include-OPTcrossref)
              (nth 2 e))
         (setq required (nth 0 (nth 2 e))
@@ -2918,10 +2855,11 @@
   (save-excursion
     (bibtex-beginning-of-entry)
     ;; For inserting new fields, we use the fact that
-    ;; bibtex-parse-entry moves point to the end of the last field.
+    ;; `bibtex-parse-entry' moves point to the end of the last field.
     (let* ((fields-alist (bibtex-parse-entry))
            (field-list (bibtex-field-list
                         (cdr (assoc "=type=" fields-alist)))))
+      (skip-chars-backward " \t\n")
       (dolist (field (car field-list))
         (unless (assoc-string (car field) fields-alist t)
           (bibtex-make-field field)))
@@ -2964,6 +2902,7 @@
 	  (key (bibtex-key-in-head))
 	  (key-end (match-end bibtex-key-in-head))
           (case-fold-search t)
+          (bibtex-sort-ignore-string-entries t)
 	  tmp other-key other bounds)
       ;; The fields we want to change start right after the key.
       (goto-char key-end)
@@ -3016,28 +2955,28 @@
 	  (while (re-search-backward (regexp-quote other-suffix) key-end 'move)
 	    (replace-match suffix)))))))
 
-(defun bibtex-print-help-message ()
-  "Print helpful information about current field in current BibTeX entry."
-  (interactive)
-  (let* ((case-fold-search t)
-         (type (save-excursion
-                 (bibtex-beginning-of-entry)
-                 (looking-at bibtex-any-entry-maybe-empty-head)
-                 (bibtex-type-in-head)))
-         comment field-list)
-    (cond ((bibtex-string= type "string")
-           (message "String definition"))
-          ((bibtex-string= type "preamble")
-           (message "Preamble definition"))
-          (t
-           (setq field-list (bibtex-field-list type)
-                 comment
-                 (assoc-string (bibtex-name-in-field (bibtex-enclosing-field) t)
-                               (append (car field-list) (cdr field-list))
-                               t))
-           (if comment
-               (message "%s" (nth 1 comment))
-             (message "No comment available"))))))
+(defun bibtex-print-help-message (&optional field comma)
+  "Print helpful information about current FIELD in current BibTeX entry.
+Optional arg COMMA is as in `bibtex-enclosing-field'.  It is t for
+interactive calls."
+  (interactive (list nil t))
+  (unless field (setq field (car (bibtex-find-text-internal nil nil comma))))
+  (if (string-match "@" field)
+      (cond ((bibtex-string= field "@string")
+             (message "String definition"))
+            ((bibtex-string= field "@preamble")
+             (message "Preamble definition"))
+            (t (message "Entry key")))
+    (let* ((case-fold-search t)
+           (type (save-excursion
+                   (bibtex-beginning-of-entry)
+                   (looking-at bibtex-entry-maybe-empty-head)
+                   (bibtex-type-in-head)))
+           (field-list (bibtex-field-list type))
+           (comment (assoc-string field (append (car field-list)
+                                                (cdr field-list)) t)))
+      (if comment (message "%s" (nth 1 comment))
+        (message "No comment available")))))
 
 (defun bibtex-make-field (field &optional move interactive)
   "Make a field named FIELD in current BibTeX entry.
@@ -3052,7 +2991,8 @@
    (list (let ((completion-ignore-case t)
                (field-list (bibtex-field-list
                             (save-excursion
-                              (bibtex-enclosing-entry-maybe-empty-head)
+                              (bibtex-beginning-of-entry)
+                              (looking-at bibtex-any-entry-maybe-empty-head)
                               (bibtex-type-in-head)))))
            (completing-read "BibTeX field name: "
                             (append (car field-list) (cdr field-list))
@@ -3081,8 +3021,9 @@
                   (t (concat (bibtex-field-left-delimiter)
                              (bibtex-field-right-delimiter))))))
   (when interactive
-    (forward-char -1)
-    (bibtex-print-help-message)))
+    ;; (bibtex-find-text nil nil bibtex-help-message)
+    (if (memq (preceding-char) '(?} ?\")) (forward-char -1))
+    (if bibtex-help-message (bibtex-print-help-message (car field)))))
 
 (defun bibtex-beginning-of-entry ()
   "Move to beginning of BibTeX entry (beginning of line).
@@ -3103,28 +3044,19 @@
 Return the new location of point."
   (interactive)
   (let ((case-fold-search t)
-        (org (point))
-        (pnt (bibtex-beginning-of-entry))
-        err bounds)
-    (cond ((looking-at bibtex-entry-type-whitespace)
-           (bibtex-search-entry t nil t)
-           (unless (equal (match-beginning 0) pnt)
-             (setq err t)))
-          ;; @String
-          ((setq bounds (bibtex-parse-string))
+        (pnt (point))
+        (_ (bibtex-beginning-of-entry))
+        (bounds (bibtex-valid-entry t)))
+    (cond (bounds (goto-char (cdr bounds))) ; regular entry
+          ;; @String or @Preamble
+          ((setq bounds (or (bibtex-parse-string t) (bibtex-parse-preamble)))
            (goto-char (bibtex-end-of-string bounds)))
-          ;; @Preamble
-          ((bibtex-preamble-prefix t)
-           (unless (bibtex-parse-string-postfix) ;; @String postfix OK
-             (setq err t)))
-          (t
-           (if (interactive-p)
-               (message "Not on a known BibTeX entry."))
-           (goto-char org)))
-    (when err
-      (goto-char pnt)
-      (error "Syntactically incorrect BibTeX entry starts here")))
-  (point))
+          ((looking-at bibtex-any-valid-entry-type)
+           ;; Parsing of entry failed
+           (error "Syntactically incorrect BibTeX entry starts here."))
+          (t (if (interactive-p) (message "Not on a known BibTeX entry."))
+             (goto-char pnt)))
+    (point)))
 
 (defun bibtex-goto-line (arg)
   "Goto line ARG, counting from beginning of (narrowed) buffer."
@@ -3188,7 +3120,7 @@
   (interactive)
   (let ((bounds (save-excursion
                   (bibtex-beginning-of-entry)
-                  (bibtex-search-forward-field "abstract"))))
+                  (bibtex-search-forward-field "abstract" t))))
     (if bounds
         (ispell-region (bibtex-start-of-text-in-field bounds)
                        (bibtex-end-of-text-in-field bounds))
@@ -3216,7 +3148,7 @@
           ;; Don't search CROSSREF-KEY if we don't need it.
           (if (eq bibtex-maintain-sorted-entries 'crossref)
               (let ((bounds (bibtex-search-forward-field
-                             "\\(OPT\\)?crossref")))
+                             "\\(OPT\\)?crossref" t)))
                 (list key
                       (if bounds (bibtex-text-in-field-bounds bounds t))
                       entry-name))
@@ -3283,7 +3215,7 @@
    (let ((crossref-key
           (save-excursion
             (bibtex-beginning-of-entry)
-            (let ((bounds (bibtex-search-forward-field "crossref")))
+            (let ((bounds (bibtex-search-forward-field "crossref" t)))
               (if bounds
                   (bibtex-text-in-field-bounds bounds t))))))
      (list (bibtex-read-key "Find crossref key: " crossref-key t)
@@ -3429,40 +3361,38 @@
          error-list syntax-error)
     (save-excursion
       (save-restriction
-        (if mark-active
-            (narrow-to-region (region-beginning) (region-end)))
-
-        ;; looking if entries fit syntactical structure
+        (if mark-active (narrow-to-region (region-beginning) (region-end)))
+
+        ;; Check syntactical structure of entries
         (goto-char (point-min))
         (bibtex-progress-message "Checking syntactical structure")
-        (let (bibtex-sort-ignore-string-entries)
-          (while (re-search-forward "^[ \t]*@" nil t)
+        (let (bounds end)
+          (while (setq end (re-search-forward "^[ \t]*@" nil t))
             (bibtex-progress-message)
-            (forward-char -1)
-            (let ((pnt (point)))
-              (if (not (looking-at bibtex-entry-type-str))
-                  (forward-char)
-                (bibtex-skip-to-valid-entry)
-                (if (equal (point) pnt)
-                    (forward-char)
-                  (goto-char pnt)
-                  (push (cons (bibtex-current-line)
-                              "Syntax error (check esp. commas, braces, and quotes)")
-                        error-list)
-                  (forward-char))))))
+            (goto-char (match-beginning 0))
+            (cond ((setq bounds (bibtex-valid-entry))
+                   (goto-char (cdr bounds)))
+                  ((setq bounds (or (bibtex-parse-string)
+                                    (bibtex-parse-preamble)))
+                   (goto-char (bibtex-end-of-string bounds)))
+                  ((looking-at bibtex-any-valid-entry-type)
+                   (push (cons (bibtex-current-line)
+                               "Syntax error (check esp. commas, braces, and quotes)")
+                         error-list)
+                   (goto-char (match-end 0)))
+                  (t (goto-char end)))))
         (bibtex-progress-message 'done)
 
         (if error-list
-            ;; proceed only if there were no syntax errors.
+            ;; Continue only if there were no syntax errors.
             (setq syntax-error t)
 
-          ;; looking for duplicate keys and correct sort order
+          ;; Check for duplicate keys and correct sort order
           (let (previous current key-list)
             (bibtex-progress-message "Checking for duplicate keys")
             (bibtex-map-entries
              (lambda (key beg end)
                (bibtex-progress-message)
-               (goto-char beg)
                (setq current (bibtex-entry-index))
                (cond ((not previous))
                      ((member key key-list)
@@ -3498,18 +3428,13 @@
               (bibtex-map-entries
                (lambda (key beg end)
                  (bibtex-progress-message)
-                 (let* ((entry-list (progn
-                                      (goto-char beg)
-                                      (bibtex-search-entry nil end)
-                                      (assoc-string (bibtex-type-in-head)
-						    bibtex-entry-field-alist t)))
+                 (let* ((entry-list (assoc-string (bibtex-type-in-head)
+                                                  bibtex-entry-field-alist t))
                         (req (copy-sequence (elt (elt entry-list 1) 0)))
                         (creq (copy-sequence (elt (elt entry-list 2) 0)))
                         crossref-there bounds alt-there field)
-                   (goto-char beg)
-                   (while (setq bounds (bibtex-search-forward-field
-                                        bibtex-field-name end))
-                     (goto-char (bibtex-start-of-text-in-field bounds))
+                   (bibtex-beginning-first-field beg)
+                   (while (setq bounds (bibtex-parse-field))
                      (let ((field-name (bibtex-name-in-field bounds)))
                        (if (and (bibtex-string= field-name "month")
                                 ;; Check only abbreviated month fields.
@@ -3521,18 +3446,19 @@
                            (push (cons (bibtex-current-line)
                                        "Questionable month field")
                                  error-list))
-                       (setq field (assoc-string field-name req t))
+                       (setq field (assoc-string field-name req t)
+                             req (delete field req)
+                             creq (delete (assoc-string field-name creq t) creq))
                        (if (nth 3 field)
-                           (if alt-there (push (cons (bibtex-current-line)
-                                                     "More than one non-empty alternative")
-                                               error-list)
+                           (if alt-there
+                               (push (cons (bibtex-current-line)
+                                           "More than one non-empty alternative")
+                                     error-list)
                              (setq alt-there t)))
-                       (setq req (delete field req)
-                             creq (delete (assoc-string field-name creq t) creq))
                        (if (bibtex-string= field-name "crossref")
-                           (setq crossref-there t))))
-                   (if crossref-there
-                       (setq req creq))
+                           (setq crossref-there t)))
+                     (goto-char (bibtex-end-of-field bounds)))
+                   (if crossref-there (setq req creq))
                    (let (alt)
                      (dolist (field req)
                        (if (nth 3 field)
@@ -3573,11 +3499,10 @@
             (toggle-read-only 1)
             (goto-line 3)) ; first error message
           (display-buffer err-buf)
-          ;; return nil
-          nil)
+          nil) ; return `nil' (i.e., buffer is invalid)
       (message "%s is syntactically correct"
                (if mark-active "Region" "Buffer"))
-      t)))
+      t))) ; return `t' (i.e., buffer is valid)
 
 (defun bibtex-validate-globally (&optional strings)
   "Check for duplicate keys in `bibtex-files'.
@@ -3631,37 +3556,41 @@
             (toggle-read-only 1)
             (goto-line 3)) ; first error message
           (display-buffer err-buf)
-          ;; return nil
-          nil)
+          nil) ; return `nil' (i.e., buffer is invalid)
       (message "No duplicate keys.")
-      t)))
-
-(defun bibtex-next-field (begin)
-  "Move point to end of text of next BibTeX field.
-With prefix BEGIN non-nil, move point to its beginning."
-  (interactive "P")
-  (bibtex-inside-field)
-  (let ((start (point)))
-    (condition-case ()
-        (let ((bounds (bibtex-enclosing-field)))
-          (goto-char (bibtex-end-of-field bounds))
-          (forward-char 2))
-      (error
-       (goto-char start)
-       (end-of-line)
-       (forward-char))))
-  (bibtex-find-text begin nil bibtex-help-message))
-
-(defun bibtex-find-text (&optional begin noerror help)
-  "Move point to end of text of current BibTeX field.
+      t))) ; return `t' (i.e., buffer is valid)
+
+(defun bibtex-next-field (begin &optional comma)
+  "Move point to end of text of next BibTeX field or entry head.
+With prefix BEGIN non-nil, move point to its beginning.  Optional arg COMMA
+is as in `bibtex-enclosing-field'.  It is t for interactive calls."
+  (interactive (list current-prefix-arg t))
+  (let ((bounds (bibtex-find-text-internal t nil comma))
+        end-of-entry)
+    (if (not bounds)
+        (setq end-of-entry t)
+      (goto-char (nth 3 bounds))
+      (if (assoc-string (car bounds) '("@String" "@Preamble") t)
+          (setq end-of-entry t)
+        ;; BibTeX key or field
+        (if (looking-at ",[ \t\n]*") (goto-char (match-end 0)))
+        ;; end of entry
+        (if (looking-at "[)}][ \t\n]*") (setq end-of-entry t))))
+    (if (and end-of-entry
+             (re-search-forward bibtex-any-entry-maybe-empty-head nil t))
+      (goto-char (match-beginning 0)))
+    (bibtex-find-text begin nil bibtex-help-message)))
+
+(defun bibtex-find-text (&optional begin noerror help comma)
+  "Move point to end of text of current BibTeX field or entry head.
 With optional prefix BEGIN non-nil, move point to its beginning.
 Unless NOERROR is non-nil, an error is signaled if point is not
 on a BibTeX field.  If optional arg HELP is non-nil print help message.
-When called interactively, the value of HELP is `bibtex-help-message'."
-  (interactive (list current-prefix-arg nil bibtex-help-message))
-  (let ((pnt (point))
-        (bounds (bibtex-find-text-internal)))
-    (beginning-of-line)
+When called interactively, the value of HELP is `bibtex-help-message'.
+Optional arg COMMA is as in `bibtex-enclosing-field'.  It is t for
+interactive calls."
+  (interactive (list current-prefix-arg nil bibtex-help-message t))
+  (let ((bounds (bibtex-find-text-internal t nil comma)))
     (cond (bounds
            (if begin
                (progn (goto-char (nth 1 bounds))
@@ -3670,72 +3599,88 @@
              (goto-char (nth 2 bounds))
              (if (memq (preceding-char) '(?} ?\"))
                  (forward-char -1)))
-           (if help (bibtex-print-help-message)))
-          ((looking-at bibtex-entry-maybe-empty-head)
-           (goto-char (if begin
-                          (match-beginning bibtex-key-in-head)
-                        (match-end 0))))
-          (t
-           (goto-char pnt)
-           (unless noerror (error "Not on BibTeX field"))))))
-
-(defun bibtex-find-text-internal (&optional noerror subfield)
-  "Find text part of current BibTeX field, @String or @Preamble.
-Return list (NAME START END) with field name, start and end of text
-or nil if not found.
+           (if help (bibtex-print-help-message (car bounds))))
+          ((not noerror) (error "Not on BibTeX field")))))
+
+(defun bibtex-find-text-internal (&optional noerror subfield comma)
+  "Find text part of current BibTeX field or entry head.
+Return list (NAME START-TEXT END-TEXT END) with field or entry name,
+start and end of text and end of field or entry head, or nil if not found.
 If optional arg NOERROR is non-nil, an error message is suppressed if text
-is not found.  If optional arg SUBFIELD is non-nil START and END correspond
-to the current subfield delimited by #."
+is not found.  If optional arg SUBFIELD is non-nil START-TEXT and END-TEXT
+correspond to the current subfield delimited by #.
+Optional arg COMMA is as in `bibtex-enclosing-field'."
   (save-excursion
     (let ((pnt (point))
-          (_ (bibtex-inside-field))
-          (bounds (bibtex-enclosing-field t))
+          (bounds (bibtex-enclosing-field comma t))
           (case-fold-search t)
-          (bibtex-string-empty-key t)
-          name start end)
+          name start-text end-text end failure done no-sub)
       (bibtex-beginning-of-entry)
       (cond (bounds
              (setq name (bibtex-name-in-field bounds t)
-                   start (bibtex-start-of-text-in-field bounds)
-                   end (bibtex-end-of-text-in-field bounds)))
+                   start-text (bibtex-start-of-text-in-field bounds)
+                   end-text (bibtex-end-of-text-in-field bounds)
+                   end (bibtex-end-of-field bounds)))
             ;; @String
-            ((setq bounds (bibtex-parse-string))
-             (setq name "@String" ;; not a field name!
-                   start (bibtex-start-of-text-in-string bounds)
-                   end (bibtex-end-of-text-in-string bounds)))
+            ((setq bounds (bibtex-parse-string t))
+             (if (<= pnt (bibtex-end-of-string bounds))
+                 (setq name "@String" ;; not a field name!
+                       start-text (bibtex-start-of-text-in-string bounds)
+                       end-text (bibtex-end-of-text-in-string bounds)
+                       end (bibtex-end-of-string bounds))
+               (setq failure t)))
             ;; @Preamble
-            ((and (bibtex-preamble-prefix t)
-                  (setq bounds (bibtex-parse-field-text)))
-             (setq name "@Preamble" ;; not a field name!
-                   start (car bounds)
-                   end (nth 1 bounds)))
-            (t (unless noerror (error "Not on BibTeX field"))))
-      (when (and start end subfield)
-        (goto-char start)
-        (let (done)
+            ((setq bounds (bibtex-parse-preamble))
+             (if (<= pnt (bibtex-end-of-string bounds))
+                 (setq name "@Preamble" ;; not a field name!
+                       start-text (bibtex-start-of-text-in-string bounds)
+                       end-text (bibtex-end-of-text-in-string bounds)
+                       end (bibtex-end-of-string bounds))
+               (setq failure t)))
+            ;; BibTeX head
+            ((looking-at bibtex-entry-maybe-empty-head)
+             (goto-char (match-end 0))
+             (if comma (save-match-data
+                         (re-search-forward "\\=[ \t\n]*," nil t)))
+             (if (<= pnt (point))
+                 (setq name (match-string-no-properties bibtex-type-in-head)
+                       start-text (or (match-beginning bibtex-key-in-head)
+                                 (match-end 0))
+                       end-text (or (match-end bibtex-key-in-head)
+                               (match-end 0))
+                       end end-text
+                       no-sub t) ;; subfields do not make sense
+               (setq failure t)))
+            (t (setq failure t)))
+      (when (and subfield (not failure))
+        (setq failure no-sub)
+        (unless failure
+          (goto-char start-text)
           (while (not done)
             (if (or (prog1 (looking-at bibtex-field-const)
-                      (setq end (match-end 0)))
+                      (setq end-text (match-end 0)))
                     (prog1 (setq bounds (bibtex-parse-field-string))
-                      (setq end (cdr bounds))))
+                      (setq end-text (cdr bounds))))
                 (progn
-                  (if (and (<= start pnt) (<= pnt end))
+                  (if (and (<= start-text pnt) (<= pnt end-text))
                       (setq done t)
-                    (goto-char end))
+                    (goto-char end-text))
                   (if (looking-at "[ \t\n]*#[ \t\n]*")
-                      (setq start (goto-char (match-end 0)))))
-              (unless noerror (error "Not on text part of BibTeX field"))
-              (setq done t start nil end nil)))))
-      (if (and start end)
-          (list name start end)))))
-
-(defun bibtex-remove-OPT-or-ALT ()
+                      (setq start-text (goto-char (match-end 0)))))
+              (setq done t failure t)))))
+      (cond ((not failure)
+             (list name start-text end-text end))
+            ((and no-sub (not noerror))
+             (error "Not on text part of BibTeX field"))
+            ((not noerror) (error "Not on BibTeX field"))))))
+
+(defun bibtex-remove-OPT-or-ALT (&optional comma)
   "Remove the string starting optional/alternative fields.
-Align text and go thereafter to end of text."
-  (interactive)
-  (bibtex-inside-field)
+Align text and go thereafter to end of text.  Optional arg COMMA
+is as in `bibtex-enclosing-field'.  It is t for interactive calls."
+  (interactive (list t))
   (let ((case-fold-search t)
-        (bounds (bibtex-enclosing-field)))
+        (bounds (bibtex-enclosing-field comma)))
     (save-excursion
       (goto-char (bibtex-start-of-name-in-field bounds))
       (when (looking-at "OPT\\|ALT")
@@ -3751,14 +3696,14 @@
         (delete-horizontal-space)
         (if bibtex-align-at-equal-sign
             (insert " ")
-          (indent-to-column bibtex-text-indentation))))
-    (bibtex-inside-field)))
-
-(defun bibtex-remove-delimiters ()
-  "Remove \"\" or {} around current BibTeX field text."
-  (interactive)
-  ;; `bibtex-find-text-internal' issues an error message if bounds is nil.
-  (let* ((bounds (bibtex-find-text-internal nil t))
+          (indent-to-column bibtex-text-indentation))))))
+
+(defun bibtex-remove-delimiters (&optional comma)
+  "Remove \"\" or {} around current BibTeX field text.
+Optional arg COMMA is as in `bibtex-enclosing-field'.  It is t for
+interactive calls."
+  (interactive (list t))
+  (let* ((bounds (bibtex-find-text-internal nil t comma))
          (start (nth 1 bounds))
          (end (nth 2 bounds)))
     (if (memq (char-before end) '(?\} ?\"))
@@ -3766,15 +3711,15 @@
     (if (memq (char-after start) '(?\{ ?\"))
         (delete-region start (1+ start)))))
 
-(defun bibtex-kill-field (&optional copy-only)
+(defun bibtex-kill-field (&optional copy-only comma)
   "Kill the entire enclosing BibTeX field.
 With prefix arg COPY-ONLY, copy the current field to `bibtex-field-kill-ring',
-but do not actually kill it."
-  (interactive "P")
+but do not actually kill it.  Optional arg COMMA is as in
+`bibtex-enclosing-field'.  It is t for interactive calls."
+  (interactive (list current-prefix-arg t))
   (save-excursion
-    (bibtex-inside-field)
     (let* ((case-fold-search t)
-           (bounds (bibtex-enclosing-field))
+           (bounds (bibtex-enclosing-field comma))
            (end (bibtex-end-of-field bounds))
            (beg (bibtex-start-of-field bounds)))
       (goto-char end)
@@ -3791,10 +3736,12 @@
         (delete-region beg end))))
   (setq bibtex-last-kill-command 'field))
 
-(defun bibtex-copy-field-as-kill ()
-  "Copy the BibTeX field at point to the kill ring."
-  (interactive)
-  (bibtex-kill-field t))
+(defun bibtex-copy-field-as-kill (&optional comma)
+  "Copy the BibTeX field at point to the kill ring.
+Optional arg COMMA is as in `bibtex-enclosing-field'.  It is t for
+interactive calls."
+  (interactive (list t))
+  (bibtex-kill-field t comma))
 
 (defun bibtex-kill-entry (&optional copy-only)
   "Kill the entire enclosing BibTeX entry.
@@ -3806,7 +3753,7 @@
            (beg (bibtex-beginning-of-entry))
            (end (progn (bibtex-end-of-entry)
                        (if (re-search-forward
-                            bibtex-entry-maybe-empty-head nil 'move)
+                            bibtex-any-entry-maybe-empty-head nil 'move)
                            (goto-char (match-beginning 0)))
                        (point))))
       (push (buffer-substring-no-properties beg end)
@@ -3831,13 +3778,13 @@
 With argument N, reinsert the Nth most recently killed BibTeX item.
 See also the command \\[bibtex-yank-pop]."
   (interactive "*p")
-  (bibtex-insert-kill (1- n))
+  (bibtex-insert-kill (1- n) t)
   (setq this-command 'bibtex-yank))
 
 (defun bibtex-yank-pop (n)
   "Replace just-yanked killed BibTeX item with a different item.
 This command is allowed only immediately after a `bibtex-yank' or a
-`bibtex-yank-pop'.  At such a time, the region contains a reinserted
+`bibtex-yank-pop'.  In this case, the region contains a reinserted
 previously killed BibTeX item.  `bibtex-yank-pop' deletes that item
 and inserts in its place a different killed BibTeX item.
 
@@ -3853,13 +3800,14 @@
   (setq this-command 'bibtex-yank)
   (let ((inhibit-read-only t))
     (delete-region (point) (mark t))
-    (bibtex-insert-kill n)))
-
-(defun bibtex-empty-field ()
-  "Delete the text part of the current field, replace with empty text."
-  (interactive)
-  (bibtex-inside-field)
-  (let ((bounds (bibtex-enclosing-field)))
+    (bibtex-insert-kill n t)))
+
+(defun bibtex-empty-field (&optional comma)
+  "Delete the text part of the current field, replace with empty text.
+Optional arg COMMA is as in `bibtex-enclosing-field'.  It is t for
+interactive calls."
+  (interactive (list t))
+  (let ((bounds (bibtex-enclosing-field comma)))
     (goto-char (bibtex-start-of-text-in-field bounds))
     (delete-region (point) (bibtex-end-of-text-in-field bounds))
     (insert (bibtex-field-left-delimiter)
@@ -3960,7 +3908,7 @@
                (if (and (listp bibtex-strings)
                         (not (assoc key bibtex-strings)))
                    (push (cons key (bibtex-text-in-string
-                                    (save-excursion (bibtex-parse-string)) t))
+                                    (bibtex-parse-string) t))
                            bibtex-strings)))
               ;; We have a normal entry.
               ((listp bibtex-reference-keys)
@@ -3988,28 +3936,27 @@
 If JUSTIFY is non-nil justify as well.
 If optional arg MOVE is non-nil move point to end of field."
   (let ((end-field (copy-marker (bibtex-end-of-field bounds))))
-    (goto-char (bibtex-start-of-field bounds))
-    (if justify
-        (progn
-          (forward-char)
-          (bibtex-delete-whitespace)
-          (open-line 1)
-          (forward-char)
-          (indent-to-column (+ bibtex-entry-offset
-                               bibtex-field-indentation))
-          (re-search-forward "[ \t\n]*=" end-field)
-          (replace-match "=")
-          (forward-char -1)
-          (if bibtex-align-at-equal-sign
-              (indent-to-column
-               (+ bibtex-entry-offset (- bibtex-text-indentation 2)))
-            (insert " "))
-          (forward-char)
-          (bibtex-delete-whitespace)
-          (if bibtex-align-at-equal-sign
-              (insert " ")
-            (indent-to-column bibtex-text-indentation)))
-      (re-search-forward "[ \t\n]*=[ \t\n]*" end-field))
+    (if (not justify)
+        (goto-char (bibtex-start-of-text-in-field bounds))
+      (goto-char (bibtex-start-of-field bounds))
+      (forward-char) ;; leading comma
+      (bibtex-delete-whitespace)
+      (open-line 1)
+      (forward-char)
+      (indent-to-column (+ bibtex-entry-offset
+                           bibtex-field-indentation))
+      (re-search-forward "[ \t\n]*=" end-field)
+      (replace-match "=")
+      (forward-char -1)
+      (if bibtex-align-at-equal-sign
+          (indent-to-column
+           (+ bibtex-entry-offset (- bibtex-text-indentation 2)))
+        (insert " "))
+      (forward-char)
+      (bibtex-delete-whitespace)
+      (if bibtex-align-at-equal-sign
+          (insert " ")
+        (indent-to-column bibtex-text-indentation)))
     ;; Paragraphs within fields are not preserved. Bother?
     (fill-region-as-paragraph (line-beginning-position) end-field
                               default-justification nil (point))
@@ -4017,14 +3964,13 @@
 
 (defun bibtex-fill-field (&optional justify)
   "Like \\[fill-paragraph], but fill current BibTeX field.
-Optional prefix arg JUSTIFY non-nil means justify as well.
+If optional prefix JUSTIFY is non-nil justify as well.
 In BibTeX mode this function is bound to `fill-paragraph-function'."
   (interactive "*P")
   (let ((pnt (copy-marker (point)))
-        (bounds (bibtex-enclosing-field)))
-    (when bounds
-      (bibtex-fill-field-bounds bounds justify)
-      (goto-char pnt))))
+        (bounds (bibtex-enclosing-field t)))
+    (bibtex-fill-field-bounds bounds justify)
+    (goto-char pnt)))
 
 (defun bibtex-fill-entry ()
   "Fill current BibTeX entry.
@@ -4035,14 +3981,16 @@
   (interactive "*")
   (let ((pnt (copy-marker (point)))
         (end (copy-marker (bibtex-end-of-entry)))
+        (beg (bibtex-beginning-of-entry)) ; move point
         bounds)
-    (bibtex-beginning-of-entry)
     (bibtex-delete-whitespace)
     (indent-to-column bibtex-entry-offset)
-    (while (setq bounds (bibtex-search-forward-field bibtex-field-name end))
+    (bibtex-beginning-first-field beg)
+    (while (setq bounds (bibtex-parse-field))
       (bibtex-fill-field-bounds bounds t t))
     (if (looking-at ",")
         (forward-char))
+    (skip-chars-backward " \t\n")
     (bibtex-delete-whitespace)
     (open-line 1)
     (forward-char)
@@ -4115,8 +4063,7 @@
          bibtex-autokey-edit-before-use)
 
     (save-restriction
-      (narrow-to-region (if mark-active (region-beginning) (point-min))
-                        (if mark-active (region-end) (point-max)))
+      (if mark-active (narrow-to-region (region-beginning) (region-end)))
       (if (memq 'realign bibtex-entry-format)
           (bibtex-realign))
       (bibtex-progress-message "Formatting" 1)
@@ -4143,12 +4090,10 @@
   (message "Starting to validate buffer...")
   (sit-for 1 nil t)
   (bibtex-realign)
-  (message
-   "If errors occur, correct them and call `bibtex-convert-alien' again")
-  (sit-for 5 nil t)
   (deactivate-mark)  ; So bibtex-validate works on the whole buffer.
-  (when (let (bibtex-maintain-sorted-entries)
-          (bibtex-validate))
+  (if (not (let (bibtex-maintain-sorted-entries)
+             (bibtex-validate)))
+      (message "Correct errors and call `bibtex-convert-alien' again")
     (message "Starting to reformat entries...")
     (sit-for 2 nil t)
     (bibtex-reformat read-options)
@@ -4166,10 +4111,9 @@
   (interactive)
   (let ((pnt (point))
         (case-fold-search t)
-        (bibtex-string-empty-key t)
         bounds name compl)
     (save-excursion
-      (if (and (setq bounds (bibtex-enclosing-field t))
+      (if (and (setq bounds (bibtex-enclosing-field nil t))
                (>= pnt (bibtex-start-of-text-in-field bounds))
                (<= pnt (bibtex-end-of-text-in-field bounds)))
           (setq name (bibtex-name-in-field bounds t)
@@ -4182,7 +4126,7 @@
                             ;; point is in other field
                             (t (bibtex-strings))))
         (bibtex-beginning-of-entry)
-        (cond ((setq bounds (bibtex-parse-string))
+        (cond ((setq bounds (bibtex-parse-string t))
                ;; point is inside a @String key
                (cond ((and (>= pnt (nth 1 (car bounds)))
                            (<= pnt (nth 2 (car bounds))))
@@ -4192,11 +4136,10 @@
                            (<= pnt (bibtex-end-of-text-in-string bounds)))
                       (setq compl (bibtex-strings)))))
               ;; point is inside a @Preamble field
-              ((and (bibtex-preamble-prefix t)
-                    (setq bounds (bibtex-parse-field-text))
-                    (>= pnt (car bounds))
-                    (<= pnt (nth 1 bounds)))
-               (setq compl (bibtex-strings)))
+              ((setq bounds (bibtex-parse-preamble))
+               (if (and (>= pnt (bibtex-start-of-text-in-string bounds))
+                        (<= pnt (bibtex-end-of-text-in-string bounds)))
+                   (setq compl (bibtex-strings))))
               ((and (looking-at bibtex-entry-maybe-empty-head)
                     ;; point is inside a key
                     (or (and (match-beginning bibtex-key-in-head)