Function: bibtex-validate
bibtex-validate is an interactive and byte-compiled function defined
in bibtex.el.gz.
Signature
(bibtex-validate &optional TEST-THOROUGHLY)
Documentation
Validate if buffer or region is syntactically correct.
Check also for duplicate keys and correct sort order provided
bibtex-maintain-sorted-entries is non-nil.
With optional argument TEST-THOROUGHLY non-nil check also for
the absence of required fields and for questionable month fields.
If mark is active, validate current region, if not the whole buffer.
Only check known entry types, so you can put comments outside of entries.
Return t if test was successful, nil otherwise.
Probably introduced at or before Emacs version 20.1.
Key Bindings
Source Code
;; Defined in /usr/src/emacs/lisp/textmodes/bibtex.el.gz
(defun bibtex-validate (&optional test-thoroughly)
"Validate if buffer or region is syntactically correct.
Check also for duplicate keys and correct sort order provided
`bibtex-maintain-sorted-entries' is non-nil.
With optional argument TEST-THOROUGHLY non-nil check also for
the absence of required fields and for questionable month fields.
If mark is active, validate current region, if not the whole buffer.
Only check known entry types, so you can put comments outside of entries.
Return t if test was successful, nil otherwise."
(interactive "P")
(let* ((case-fold-search t)
error-list syntax-error)
(save-excursion
(save-restriction
(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 (bounds end)
(while (setq end (re-search-forward "^[ \t]*@" nil t))
(bibtex-progress-message)
(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
;; Continue only if there were no syntax errors.
(setq syntax-error t)
;; Check for duplicate keys and correct sort order
(bibtex-init-sort t) ; Needed by `bibtex-lessp' and global key check.
(let (previous current key-list)
(bibtex-progress-message "Checking for duplicate keys")
(bibtex-map-entries
(lambda (key _beg _end)
(bibtex-progress-message)
(setq current (bibtex-entry-index))
(cond ((not previous))
((member key key-list)
(push (cons (bibtex-current-line)
(format-message "Duplicate key `%s'" key))
error-list))
((and bibtex-maintain-sorted-entries
(not (bibtex-lessp previous current)))
(push (cons (bibtex-current-line)
"Entries out of order")
error-list)))
(push key key-list)
(setq previous current)))
(bibtex-progress-message 'done))
;; Check for duplicate keys in `bibtex-files'.
;; `bibtex-validate' only compares keys in current buffer with keys
;; in `bibtex-files'. `bibtex-validate-globally' compares keys for
;; each file in `bibtex-files' with keys of all other files in
;; `bibtex-files'.
;; We don't want to be fooled by outdated `bibtex-reference-keys'.
(dolist (buffer (bibtex-initialize nil t))
(dolist (key (with-current-buffer buffer bibtex-reference-keys))
(when (and (cdr key)
(cdr (assoc-string (car key) bibtex-reference-keys)))
(bibtex-search-entry (car key))
(push (cons (bibtex-current-line)
(format-message
"Duplicate key `%s' in %s" (car key)
(abbreviate-file-name (buffer-file-name buffer))))
error-list))))
(when test-thoroughly
(bibtex-progress-message
"Checking required fields and month fields")
(let ((bibtex-sort-ignore-string-entries t))
(bibtex-map-entries
(lambda (_key beg end)
(bibtex-progress-message)
(bibtex-beginning-first-field beg)
(let* ((beg-line (save-excursion (goto-char beg)
(bibtex-current-line)))
(entry-list (assoc-string (bibtex-type-in-head)
bibtex-entry-alist t))
(crossref (bibtex-search-forward-field "crossref" end))
(req (append (nth 2 entry-list)
(unless crossref
(copy-sequence (nth 3 entry-list)))))
(opt (append (if crossref (nth 3 entry-list))
(nth 4 entry-list)))
(default (append req opt))
(num-alt (let ((n 0))
(mapc (lambda (x)
(if (nth 3 x)
(setq n (max n (abs (nth 3 x))))))
default)
(1+ n)))
(alt-fields (make-vector num-alt nil))
bounds field idx)
(while (setq bounds (bibtex-parse-field))
(let ((field-name (bibtex-name-in-field bounds)))
(if (and (string-equal-ignore-case field-name "month")
;; Check only abbreviated month fields.
(let ((month (bibtex-text-in-field-bounds bounds)))
(not (or (string-match "\\`[\"{].+[\"}]\\'" month)
(assoc-string
month
bibtex-predefined-month-strings t)))))
(push (cons (bibtex-current-line)
"Questionable month field")
error-list))
(setq field (assoc-string field-name default t)
req (delete field req))
(if (setq idx (nth 3 field))
(if (aref alt-fields idx)
(push (cons (bibtex-current-line)
"More than one non-empty alternative")
error-list)
(aset alt-fields idx t))))
(goto-char (bibtex-end-of-field bounds)))
(let ((alt-expect (make-vector num-alt nil)))
(dolist (field req) ; absent required fields
(if (setq idx (nth 3 field))
(bibtex-vec-push alt-expect idx (car field))
(push (cons beg-line
(format-message
"Required field `%s' missing"
(car field)))
error-list)))
(dotimes (idx num-alt)
(if (and (aref alt-expect idx)
(not (aref alt-fields idx)))
(push (cons beg-line
(format-message
"Alternative fields `%s' missing"
(aref alt-expect idx)))
error-list))))))))
(bibtex-progress-message 'done)))))
(if error-list
(let* ((is-file (buffer-file-name))
(file (if is-file (file-name-nondirectory is-file) (buffer-name)))
(dir default-directory)
(err-buf "*BibTeX validation errors*"))
(setq error-list (sort error-list #'car-less-than-car))
(with-current-buffer (get-buffer-create err-buf)
(setq default-directory dir)
(unless (eq major-mode 'compilation-mode) (compilation-mode))
(unless is-file
(setq-local compilation-parse-errors-filename-function
#'get-buffer))
(let ((inhibit-read-only t))
(delete-region (point-min) (point-max))
(insert (substitute-command-keys
"BibTeX mode command `bibtex-validate'\n")
(if syntax-error
"Maybe undetected errors due to syntax errors. \
Correct and validate again.\n"
"\n"))
(dolist (err error-list)
(insert (format "%s:%d: %s\n" file (car err) (cdr err))))
(set-buffer-modified-p nil))
(goto-char (point-min))
(forward-line 2)) ; first error message
(display-buffer err-buf)
nil) ; return nil (i.e., buffer is invalid)
(message "%s is syntactically correct"
(if mark-active "Region" "Buffer"))
t))) ; return t (i.e., buffer is valid)