- (insert (bibtex-field-right-delimiter))))
-
- ;; update page dashes
- (if (and (memq 'page-dashes format)
- (bibtex-string= field-name "pages")
- (progn (goto-char beg-text)
- (looking-at
- "\\([\"{][0-9]+\\)[ \t\n]*--?[ \t\n]*\\([0-9]+[\"}]\\)")))
- (replace-match "\\1-\\2"))
-
- ;; remove whitespace at beginning and end of field
- (when (memq 'whitespace format)
- (goto-char beg-text)
- (if (looking-at "\\([{\"]\\)[ \t\n]+")
- (replace-match "\\1"))
- (goto-char end-text)
- (if (looking-back "[ \t\n]+\\([}\"]\\)" beg-text t)
- (replace-match "\\1")))
-
- ;; enclose field text by braces according to
- ;; `bibtex-field-braces-alist'.
- (let (case-fold-search temp) ; Case-sensitive search
- (when (and (memq 'braces format)
- (setq temp (cdr (assoc-string field-name
- bibtex-field-braces-opt t))))
- (goto-char beg-text)
- (while (re-search-forward temp end-text t)
- (let ((beg (match-beginning 0))
- (bounds (bibtex-find-text-internal nil t)))
- (unless (or (nth 4 bounds) ; string constant
- ;; match already surrounded by braces
- ;; (braces are inside field delimiters)
- (and (< (point) (1- (nth 2 bounds)))
- (< (1+ (nth 1 bounds)) beg)
- (looking-at "}")
- (save-excursion (goto-char (1- beg))
- (looking-at "{"))))
- (insert "}")
- (goto-char beg)
- (insert "{")))))
-
- ;; replace field text by BibTeX string constants according to
- ;; `bibtex-field-strings-alist'.
- (when (and (memq 'strings format)
- (setq temp (cdr (assoc-string field-name
- bibtex-field-strings-opt t))))
- (goto-char beg-text)
- (dolist (re temp)
- (while (re-search-forward (car re) end-text t)
- (let ((bounds (save-match-data
- (bibtex-find-text-internal nil t))))
- (unless (nth 4 bounds)
- ;; if match not at right subfield boundary...
- (if (< (match-end 0) (1- (nth 2 bounds)))
- (insert " # " (bibtex-field-left-delimiter))
- (delete-char 1))
- (replace-match (cdr re))
- (goto-char (match-beginning 0))
- ;; if match not at left subfield boundary...
- (if (< (1+ (nth 1 bounds)) (match-beginning 0))
- (insert (bibtex-field-right-delimiter) " # ")
- (delete-backward-char 1))))))))
-
- ;; use book title of crossref'd entry
- (if (and (memq 'inherit-booktitle format)
- empty-field
- (bibtex-string= field-name "booktitle")
- crossref-key)
- (let ((title (save-excursion
- (save-restriction
- (widen)
- (if (bibtex-find-entry crossref-key t)
- (bibtex-text-in-field "title"))))))
- (when title
- (setq empty-field nil)
- (goto-char (1+ beg-text))
- (insert title))))
-
- ;; Use booktitle to set a missing title.
- (if (and empty-field
- (bibtex-string= field-name "title"))
- (let ((booktitle (bibtex-text-in-field "booktitle")))
- (when booktitle
- (setq empty-field nil)
- (goto-char (1+ beg-text))
- (insert booktitle))))
-
- ;; if empty field, complain
- (if (and empty-field
- (memq 'required-fields format)
- (assoc-string field-name req-field-list t))
- (error "Mandatory field `%s' is empty" field-name))
-
- ;; unify case of field name
- (if (memq 'unify-case format)
- (let ((fname (car (assoc-string
- field-name
- (append (nth 0 (nth 1 entry-list))
- (nth 1 (nth 1 entry-list))
- bibtex-user-optional-fields)
- t))))
- (if fname
- (progn
- (delete-region beg-name end-name)
- (goto-char beg-name)
- (insert fname))
- ;; there are no rules we could follow
- (downcase-region beg-name end-name))))
-
- ;; update point
- (goto-char end-field))))
-
- ;; check whether all required fields are present
- (if (memq 'required-fields format)
- (let ((found 0) altlist)
- (dolist (fname req-field-list)
- (if (nth 3 fname)
- (push (car fname) altlist))
- (unless (or (member (car fname) field-list)
- (nth 3 fname))
- (error "Mandatory field `%s' is missing" (car fname))))
- (when altlist
- (dolist (fname altlist)
- (if (member fname field-list)
- (setq found (1+ found))))
- (cond ((= found 0)
- (error "Alternative mandatory field `%s' is missing"
- altlist))
- ((> found 1)
- (error "Alternative fields `%s' are defined %s times"
- altlist found))))))
-
- ;; update comma after last field
- (if (memq 'last-comma format)
- (cond ((and bibtex-comma-after-last-field
- (not (looking-at ",")))
- (insert ","))
- ((and (not bibtex-comma-after-last-field)
- (looking-at ","))
- (delete-char 1))))
-
- ;; update right entry delimiter
- (if (looking-at ",")
- (forward-char))
- (when (memq 'delimiters format)
- (skip-chars-forward " \t\n")
- (delete-char 1)
- (insert (bibtex-entry-right-delimiter)))
-
- ;; fill entry
- (if (memq 'realign format)
- (bibtex-fill-entry))))))
+ (insert (bibtex-entry-left-delimiter)))
+
+ ;; Do we have a crossref key?
+ (goto-char (point-min))
+ (if (setq bounds (bibtex-search-forward-field
+ "\\(OPT\\)?crossref"))
+ (let ((text (bibtex-text-in-field-bounds bounds t)))
+ (unless (equal "" text)
+ (setq crossref-key text))))
+
+ ;; list of required fields appropriate for an entry with
+ ;; or without crossref key.
+ (setq req-field-list (if (and crossref-key (nth 2 entry-list))
+ (car (nth 2 entry-list))
+ (car (nth 1 entry-list)))
+ ;; default list of fields that may appear in this entry
+ default-field-list (append (nth 0 (nth 1 entry-list))
+ (nth 1 (nth 1 entry-list))
+ bibtex-user-optional-fields)))
+
+ ;; process all fields
+ (bibtex-beginning-first-field (point-min))
+ (while (setq bounds (bibtex-parse-field))
+ (let* ((beg-field (copy-marker (bibtex-start-of-field bounds)))
+ (end-field (copy-marker (bibtex-end-of-field bounds) t))
+ (beg-name (copy-marker (bibtex-start-of-name-in-field bounds)))
+ (end-name (copy-marker (bibtex-end-of-name-in-field bounds)))
+ (beg-text (copy-marker (bibtex-start-of-text-in-field bounds)))
+ (end-text (copy-marker (bibtex-end-of-text-in-field bounds) t))
+ (opt-alt (string-match "OPT\\|ALT"
+ (buffer-substring-no-properties
+ beg-name (+ beg-name 3))))
+ (field-name (buffer-substring-no-properties
+ (if opt-alt (+ beg-name 3) beg-name) end-name))
+ (empty-field (equal "" (bibtex-text-in-field-bounds bounds t)))
+ deleted)
+
+ ;; keep track of alternatives
+ (if (nth 3 (assoc-string field-name req-field-list t))
+ (push field-name alt-fields))
+
+ (if (memq 'opts-or-alts format)
+ ;; delete empty optional and alternative fields
+ ;; (but keep empty required fields)
+ (cond ((and empty-field
+ (or opt-alt
+ (let ((field (assoc-string
+ field-name req-field-list t)))
+ (or (not field) ; OPT field
+ (nth 3 field))))) ; ALT field
+ (delete-region beg-field end-field)
+ (setq deleted t))
+ ;; otherwise nonempty field: delete "OPT" or "ALT"
+ (opt-alt
+ (goto-char beg-name)
+ (delete-char 3))))
+
+ (unless deleted
+ (push field-name field-list)
+
+ ;; Remove whitespace at beginning and end of field.
+ ;; We do not look at individual parts of the field
+ ;; as {foo } # bar # { baz} is a fine field.
+ (when (memq 'whitespace format)
+ (goto-char beg-text)
+ (if (looking-at "\\([{\"]\\)[ \t\n]+")
+ (replace-match "\\1"))
+ (goto-char end-text)
+ (if (looking-back "[ \t\n]+\\([}\"]\\)" beg-text t)
+ (replace-match "\\1")))
+
+ ;; remove delimiters from purely numerical fields
+ (when (and (memq 'numerical-fields format)
+ (progn (goto-char beg-text)
+ (looking-at "\\(\"[0-9]+\"\\)\\|\\({[0-9]+}\\)")))
+ (goto-char end-text)
+ (delete-char -1)
+ (goto-char beg-text)
+ (delete-char 1))
+
+ ;; update delimiters
+ (when (memq 'delimiters format)
+ (goto-char beg-text)
+ ;; simplified from `bibtex-parse-field-text', as we
+ ;; already checked that the field format is correct
+ (while (< (point) end-text)
+ (if (looking-at bibtex-field-const)
+ (goto-char (match-end 0))
+ (let ((boundaries (bibtex-parse-field-string)))
+ (if (looking-at left-delim-re)
+ (goto-char (cdr boundaries))
+ (delete-char 1)
+ (insert (bibtex-field-left-delimiter))
+ (goto-char (1- (cdr boundaries)))
+ (delete-char 1)
+ (insert (bibtex-field-right-delimiter)))))
+ (if (looking-at "[ \t\n]*#[ \t\n]*")
+ (goto-char (match-end 0)))))
+
+ ;; update page dashes
+ (if (and (memq 'page-dashes format)
+ (bibtex-string= field-name "pages")
+ (progn (goto-char beg-text)
+ (looking-at
+ "\\([\"{][0-9]+\\)[ \t\n]*--?[ \t\n]*\\([0-9]+[\"}]\\)")))
+ (replace-match "\\1-\\2"))
+
+ ;; enclose field text by braces according to
+ ;; `bibtex-field-braces-alist'.
+ (let (case-fold-search temp) ; Case-sensitive search
+ (when (and (memq 'braces format)
+ (setq temp (cdr (assoc-string field-name
+ bibtex-field-braces-opt t))))
+ (goto-char beg-text)
+ (while (re-search-forward temp end-text t)
+ (let ((beg (match-beginning 0))
+ (bounds (bibtex-find-text-internal nil t)))
+ (unless (or (nth 4 bounds) ; string constant
+ ;; match already surrounded by braces
+ ;; (braces are inside field delimiters)
+ (and (< (point) (1- (nth 2 bounds)))
+ (< (1+ (nth 1 bounds)) beg)
+ (looking-at "}")
+ (save-excursion (goto-char (1- beg))
+ (looking-at "{"))))
+ (insert "}")
+ (goto-char beg)
+ (insert "{")))))
+
+ ;; replace field text by BibTeX string constants
+ ;; according to `bibtex-field-strings-alist'.
+ (when (and (memq 'strings format)
+ (setq temp (cdr (assoc-string field-name
+ bibtex-field-strings-opt t))))
+ (goto-char beg-text)
+ (dolist (re temp)
+ (while (re-search-forward (car re) end-text t)
+ (let ((bounds (save-match-data
+ (bibtex-find-text-internal nil t))))
+ (unless (nth 4 bounds)
+ ;; if match not at right subfield boundary...
+ (if (< (match-end 0) (1- (nth 2 bounds)))
+ (insert " # " (bibtex-field-left-delimiter))
+ (delete-char 1))
+ (replace-match (cdr re))
+ (goto-char (match-beginning 0))
+ ;; if match not at left subfield boundary...
+ (if (< (1+ (nth 1 bounds)) (match-beginning 0))
+ (insert (bibtex-field-right-delimiter) " # ")
+ (delete-char -1))))))))
+
+ ;; use book title of crossref'd entry
+ (if (and (memq 'inherit-booktitle format)
+ empty-field
+ (bibtex-string= field-name "booktitle")
+ crossref-key)
+ (let ((title (save-excursion
+ (save-restriction
+ (widen)
+ (if (bibtex-search-entry crossref-key t)
+ (bibtex-text-in-field "title"))))))
+ (when title
+ (setq empty-field nil)
+ (goto-char (1+ beg-text))
+ (insert title))))
+
+ ;; if empty field is a required field, complain
+ (when (and empty-field
+ (memq 'required-fields format)
+ (assoc-string field-name req-field-list t))
+ (setq error-field-name field-name)
+ (error "Mandatory field `%s' is empty" field-name))
+
+ ;; unify case of field name
+ (if (memq 'unify-case format)
+ (let ((fname (car (assoc-string field-name
+ default-field-list t))))
+ (if fname
+ (progn
+ (delete-region beg-name end-name)
+ (goto-char beg-name)
+ (insert fname))
+ ;; there are no rules we could follow
+ (downcase-region beg-name end-name))))
+
+ ;; update point
+ (goto-char end-field))))
+
+ ;; check whether all required fields are present
+ (if (memq 'required-fields format)
+ (let ((found 0) alt-list)
+ (dolist (fname req-field-list)
+ (cond ((nth 3 fname) ; t if field has alternative flag
+ (push (car fname) alt-list)
+ (if (member-ignore-case (car fname) field-list)
+ (setq found (1+ found))))
+ ((not (member-ignore-case (car fname) field-list))
+ ;; If we use the crossref field, a required field
+ ;; can have the OPT prefix. So if it was empty,
+ ;; we have deleted by now. Nonetheless we can
+ ;; move point on this empty field.
+ (setq error-field-name (car fname))
+ (error "Mandatory field `%s' is missing" (car fname)))))
+ (if alt-list
+ (cond ((= found 0)
+ (if alt-fields
+ (setq error-field-name (car (last alt-fields))))
+ (error "Alternative mandatory field `%s' is missing"
+ alt-list))
+ ((> found 1)
+ (if alt-fields
+ (setq error-field-name (car (last alt-fields))))
+ (error "Alternative fields `%s' are defined %s times"
+ alt-list found))))))
+
+ ;; update comma after last field
+ (if (memq 'last-comma format)
+ (cond ((and bibtex-comma-after-last-field
+ (not (looking-at ",")))
+ (insert ","))
+ ((and (not bibtex-comma-after-last-field)
+ (looking-at ","))
+ (delete-char 1))))
+
+ ;; update right entry delimiter
+ (if (looking-at ",")
+ (forward-char))
+ (when (memq 'delimiters format)
+ (skip-chars-forward " \t\n")
+ (delete-char 1)
+ (insert (bibtex-entry-right-delimiter)))
+
+ ;; realign and fill entry
+ (if (memq 'realign format)
+ (bibtex-fill-entry)))))
+
+ ;; Unwindform: move point to location where error occurred if possible
+ (if error-field-name
+ (let (bounds)
+ (when (save-excursion
+ (bibtex-beginning-of-entry)
+ (setq bounds
+ (bibtex-search-forward-field
+ ;; If we use the crossref field, a required field
+ ;; can have the OPT prefix
+ (concat "\\(OPT\\|ALT\\)?" error-field-name) t)))
+ (goto-char (bibtex-start-of-text-in-field bounds))
+ (bibtex-find-text)))))))