From: Andreas Gerler Date: Fri, 22 Nov 2019 14:10:46 +0000 (+0100) Subject: install parsebib X-Git-Url: http://git.bundesbrandschatzamt.de/?a=commitdiff_plain;h=d28dc77e71b5eaa23cb158173a4cf05eb8d8539d;p=emacsconfig.git install parsebib --- diff --git a/elpa/parsebib-20181219.928/parsebib-autoloads.el b/elpa/parsebib-20181219.928/parsebib-autoloads.el new file mode 100644 index 00000000..e62905fe --- /dev/null +++ b/elpa/parsebib-20181219.928/parsebib-autoloads.el @@ -0,0 +1,22 @@ +;;; parsebib-autoloads.el --- automatically extracted autoloads +;; +;;; Code: + +(add-to-list 'load-path (directory-file-name + (or (file-name-directory #$) (car load-path)))) + + +;;;### (autoloads nil "parsebib" "parsebib.el" (0 0 0 0)) +;;; Generated autoloads from parsebib.el + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "parsebib" '("parsebib-"))) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; coding: utf-8 +;; End: +;;; parsebib-autoloads.el ends here diff --git a/elpa/parsebib-20181219.928/parsebib-pkg.el b/elpa/parsebib-20181219.928/parsebib-pkg.el new file mode 100644 index 00000000..e1e13da8 --- /dev/null +++ b/elpa/parsebib-20181219.928/parsebib-pkg.el @@ -0,0 +1,2 @@ +;;; -*- no-byte-compile: t -*- +(define-package "parsebib" "20181219.928" "A library for parsing bib files" '((emacs "24.3")) :commit "9a5f1730b8ef1fb6c29262a8ba79f8136e5548d4" :keywords '("text" "bibtex") :authors '(("Joost Kremers" . "joostkremers@fastmail.fm")) :maintainer '("Joost Kremers" . "joostkremers@fastmail.fm")) diff --git a/elpa/parsebib-20181219.928/parsebib.el b/elpa/parsebib-20181219.928/parsebib.el new file mode 100644 index 00000000..aff0dee6 --- /dev/null +++ b/elpa/parsebib-20181219.928/parsebib.el @@ -0,0 +1,676 @@ +;;; parsebib.el --- A library for parsing bib files -*- lexical-binding: t -*- + +;; Copyright (c) 2014-2017 Joost Kremers +;; All rights reserved. + +;; Author: Joost Kremers +;; Maintainer: Joost Kremers +;; Created: 2014 +;; Version: 2.3 +;; Package-Version: 20181219.928 +;; Keywords: text bibtex +;; Package-Requires: ((emacs "24.3")) + +;; Redistribution and use in source and binary forms, with or without +;; modification, are permitted provided that the following conditions +;; are met: +;; +;; 1. Redistributions of source code must retain the above copyright +;; notice, this list of conditions and the following disclaimer. +;; 2. Redistributions in binary form must reproduce the above copyright +;; notice, this list of conditions and the following disclaimer in the +;; documentation and/or other materials provided with the distribution. +;; 3. The name of the author may not be used to endorse or promote products +;; derived from this software without specific prior written permission. +;; +;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +;; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +;; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +;; IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +;; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +;; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE, +;; DATA, OR PROFITS ; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +;; THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +;;; Commentary: + +;; + +;;; Code: + +(require 'bibtex) +(require 'cl-lib) +(eval-when-compile (require 'subr-x)) ; for `string-join'. + +(defvar parsebib--biblatex-inheritances '(("all" + "all" + (("ids" . none) + ("crossref" . none) + ("xref" . none) + ("entryset" . none) + ("entrysubtype" . none) + ("execute" . none) + ("label" . none) + ("options" . none) + ("presort" . none) + ("related" . none) + ("relatedoptions" . none) + ("relatedstring" . none) + ("relatedtype" . none) + ("shorthand" . none) + ("shorthandintro" . none) + ("sortkey" . none))) + + ("mvbook, book" + "inbook, bookinbook, suppbook" + (("author" . "author") + ("author" . "bookauthor"))) + + ("mvbook" + "book, inbook, bookinbook, suppbook" + (("title" . "maintitle") + ("subtitle" . "mainsubtitle") + ("titleaddon" . "maintitleaddon") + ("shorttitle" . none) + ("sorttitle" . none) + ("indextitle" . none) + ("indexsorttitle" . none))) + + ("mvcollection, mvreference" + "collection, reference, incollection, inreference, suppcollection" + (("title" . "maintitle") + ("subtitle" . "mainsubtitle") + ("titleaddon" . "maintitleaddon") + ("shorttitle" . none) + ("sorttitle" . none) + ("indextitle" . none) + ("indexsorttitle" . none))) + + ("mvproceedings" + "proceedings, inproceedings" + (("title" . "maintitle") + ("subtitle" . "mainsubtitle") + ("titleaddon" . "maintitleaddon") + ("shorttitle" . none) + ("sorttitle" . none) + ("indextitle" . none) + ("indexsorttitle" . none))) + + ("book" + "inbook, bookinbook, suppbook" + (("title" . "booktitle") + ("subtitle" . "booksubtitle") + ("titleaddon" . "booktitleaddon") + ("shorttitle" . none) + ("sorttitle" . none) + ("indextitle" . none) + ("indexsorttitle" . none))) + + ("collection, reference" + "incollection, inreference, suppcollection" + (("title" . "booktitle") + ("subtitle" . "booksubtitle") + ("titleaddon" . "booktitleaddon") + ("shorttitle" . none) + ("sorttitle" . none) + ("indextitle" . none) + ("indexsorttitle" . none))) + + ("proceedings" + "inproceedings" + (("title" . "booktitle") + ("subtitle" . "booksubtitle") + ("titleaddon" . "booktitleaddon") + ("shorttitle" . none) + ("sorttitle" . none) + ("indextitle" . none) + ("indexsorttitle" . none))) + + ("periodical" + "article, suppperiodical" + (("title" . "journaltitle") + ("subtitle" . "journalsubtitle") + ("shorttitle" . none) + ("sorttitle" . none) + ("indextitle" . none) + ("indexsorttitle" . none)))) + "Inheritance scheme for BibLaTeX cross-referencing. +Inheritances are specified for pairs of source and target entry +type, where the target is the cross-referencing entry and the +source the cross-referenced entry. Each pair specifies the +fields in the source and the fields in the target that they +correspond with. + +Inheritances valid for all entry types are defined by specifying +the entry type as \"all\". The entry type may also be a +comma-separated list of entry types. + +If no inheritance rule is set up for a given entry type+field +combination, the field inherits from the same-name field in the +cross-referenced entry. If no inheritance should take place, the +target field is set to the symbol `none'.") + +;; Regexes describing BibTeX identifiers and keys. Note that while $ ^ & are +;; valid in BibTeX keys, they may nonetheless be problematic, because they are +;; special for TeX. The difference between `parsebib--bibtex-identifier' and +;; `parsebib--key-regexp' are the parentheses (), which are valid in keys. It may in +;; fact not be necessary (or desirable) to distinguish the two, but until +;; someone complains, I'll keep it this way. +(defconst parsebib--bibtex-identifier "[^\"@\\#%',={}() \t\n\f]+" "Regexp describing a licit BibTeX identifier.") +(defconst parsebib--key-regexp "[^\"@\\#%',={} \t\n\f]+" "Regexp describing a licit key.") +(defconst parsebib--entry-start "^[ \t]*@" "Regexp describing the start of an entry.") + +;; Emacs 24.3 compatibility code. +(unless (fboundp 'define-error) + ;; This definition is simply copied from the Emacs 24.4 sources + (defun define-error (name message &optional parent) + "Define NAME as a new error signal. +MESSAGE is a string that will be output to the echo area if such an error +is signaled without being caught by a `condition-case'. +PARENT is either a signal or a list of signals from which it inherits. +Defaults to `error'." + (unless parent (setq parent 'error)) + (let ((conditions + (if (consp parent) + (apply #'nconc + (mapcar (lambda (parent) + (cons parent + (or (get parent 'error-conditions) + (error "Unknown signal `%s'" parent)))) + parent)) + (cons parent (get parent 'error-conditions))))) + (put name 'error-conditions + (delete-dups (copy-sequence (cons name conditions)))) + (when message (put name 'error-message message))))) + +(define-error 'parsebib-entry-type-error "Illegal entry type" 'error) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; matching and parsing stuff ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defun parsebib--looking-at-goto-end (str &optional match) + "Like `looking-at' but move point to the end of the matching string STR. +MATCH acts just like the argument to MATCH-END, and defaults to +0. Comparison is done case-insensitively." + (or match (setq match 0)) + (let ((case-fold-search t)) + (if (looking-at str) + (goto-char (match-end match))))) + +(defun parsebib--match-paren-forward () + "Move forward to the closing paren matching the opening paren at point. +This function handles parentheses () and braces {}. Return t if +a matching parenthesis was found. This function puts point +immediately after the matching parenthesis." + (cond + ((eq (char-after) ?\{) + (parsebib--match-brace-forward)) + ((eq (char-after) ?\() + (bibtex-end-of-entry)))) + +(defun parsebib--match-delim-forward () + "Move forward to the closing delimiter matching the delimiter at point. +This function handles braces {} and double quotes \"\". Return t +if a matching delimiter was found." + (let ((result (cond + ((eq (char-after) ?\{) + (parsebib--match-brace-forward)) + ((eq (char-after) ?\") + (parsebib--match-quote-forward))))) + result)) + +(defun parsebib--match-brace-forward () + "Move forward to the closing brace matching the opening brace at point." + (with-syntax-table bibtex-braced-string-syntax-table + (forward-sexp 1) + ;; if forward-sexp does not result in an error, we want to return t + t)) + +(defun parsebib--match-quote-forward () + "Move to the closing double quote matching the quote at point." + (with-syntax-table bibtex-quoted-string-syntax-table + (forward-sexp 1) + ;; if forward-sexp does not result in an error, we want to return t + t)) + +(defun parsebib--parse-value (limit &optional strings) + "Parse value at point. +A value is either a field value or a @String expansion. Return +the value as a string. No parsing is done beyond LIMIT, but note +that parsing may stop well before LIMIT. + +STRINGS, if non-nil, is a hash table of @String definitions. +@String abbrevs in the value to be parsed are then replaced with +their expansions. Additionally, newlines in field values are +removed, white space is reduced to a single space and braces or +double quotes around field values are removed." + (let (res) + (while (and (< (point) limit) + (not (looking-at-p ","))) + (cond + ((looking-at-p "[{\"]") + (let ((beg (point))) + (parsebib--match-delim-forward) + (push (buffer-substring-no-properties beg (point)) res))) + ((looking-at parsebib--bibtex-identifier) + (push (buffer-substring-no-properties (point) (match-end 0)) res) + (goto-char (match-end 0))) + ((looking-at "[[:space:]]*#[[:space:]]*") + (goto-char (match-end 0))) + (t (forward-char 1)))) ; so as not to get stuck in an infinite loop. + (if strings + (string-join (parsebib--expand-strings (nreverse res) strings)) + (string-join (nreverse res) " # ")))) + +;;;;;;;;;;;;;;;;;;;;; +;; expanding stuff ;; +;;;;;;;;;;;;;;;;;;;;; + +(defun parsebib--expand-strings (strings abbrevs) + "Expand strings in STRINGS using expansions in ABBREVS. +STRINGS is a list of strings. If a string in STRINGS has an +expansion in hash table ABBREVS, replace it with its expansion. +Otherwise, if the string is enclosed in braces {} or double +quotes \"\", remove the delimiters. In addition, newlines and +multiple spaces in the string are replaced with a single space." + (mapcar (lambda (str) + (setq str (replace-regexp-in-string "[ \t\n\f]+" " " str)) + (cond + ((gethash str abbrevs)) + ((string-match "\\`[\"{]\\(.*?\\)[\"}]\\'" str) + (match-string 1 str)) + (t str))) + strings)) + +(defun parsebib-expand-xrefs (entries inheritance) + "Expand cross-referencing items in ENTRIES. +BibTeX entries in ENTRIES that have a `crossref' field are +expanded with the fields in the cross-referenced entry. ENTRIES +is a hash table with entries. This hash table is updated with +the new fields. The return value of this function is always nil. + +INHERITANCE indicates the inheritance schema. It can be a symbol +`BibTeX' or `biblatex', or it can be an explicit inheritance +schema. See the variable `parsebib--biblatex-inheritances' for +details on the structure of such an inheritance schema." + (maphash (lambda (key fields) + (let ((xref (cdr (assoc-string "crossref" fields)))) + (when xref + (if (string-match-p (concat "\\b[\"{]" parsebib--key-regexp "[\"}]\\b") xref) + (setq xref (substring xref 1 -1))) + (let* ((source (gethash xref entries)) + (updated-entry (parsebib--get-xref-fields fields source inheritance))) + (when updated-entry + (puthash key updated-entry entries)))))) + entries)) + +(defun parsebib--get-xref-fields (target-entry source-entry inheritance) + "Return TARGET-ENTRY supplemented with fields inherited from SOURCE-ENTRY. +TARGET-ENTRY and SOURCE-ENTRY are entry alists. Fields in +SOURCE-ENTRY for which TARGET-ENTRY has no value are added to +TARGET-ENTRY. Return value is the modified TARGET-ENTRY. + +INHERITANCE is an inheritance schema. It can either be one of +the symbols `BibTeX' or `biblatex', or it can be an explicit +inheritance schema. See the variable +`parsebib--biblatex-inheritances' for details on the structure of +such an inheritance schema." + (when (and target-entry source-entry) + (when (eq inheritance 'biblatex) + (setq inheritance parsebib--biblatex-inheritances)) + (let* ((inheritable-fields (unless (eq inheritance 'BibTeX) + (append (cl-third (cl-find-if (lambda (elem) + (and (string-match-p (concat "\\b" (cdr (assoc-string "=type=" source-entry)) "\\b") (cl-first elem)) + (string-match-p (concat "\\b" (cdr (assoc-string "=type=" target-entry)) "\\b") (cl-second elem)))) + inheritance)) + (cl-third (assoc-string "all" inheritance))))) + (new-fields (delq nil (mapcar (lambda (field) + (let ((target-field (parsebib--get-target-field (car field) inheritable-fields))) + (if (and target-field + (not (assoc-string target-field target-entry 'case-fold))) + (cons target-field (cdr field))))) + source-entry)))) + (append target-entry new-fields)))) + +(defun parsebib--get-target-field (source-field inheritances) + "Return the target field for inheritance from SOURCE-FIELD. +Inheritance is determined by INHERITANCES, which is an alist of +source/target pairs. If no inheritance should take place for +SOURCE-FIELD, the target in the relevant item in INHERITANCES is +the symbol `none'. If there is no item for SOURCE-FIELD in +INHERITANCES, SOURCE-FIELD is returned. Note that it is valid +for INHERITANCES to be nil." + ;; Note: the argument INHERITANCES differs from the INHERITANCE argument in + ;; the previous two functions. It is a simple alist of (source-field + ;; . target-field) pairs. + (let ((target-field (cdr (assoc-string source-field inheritances 'case-fold)))) + (cond + ((null target-field) + source-field) + ((eq target-field 'none) + nil) + (t target-field)))) + +;;;;;;;;;;;;;;;;;;; +;; low-level API ;; +;;;;;;;;;;;;;;;;;;; + +(defun parsebib-find-next-item (&optional pos) + "Find the first (potential) BibTeX item following POS. +This function simply searches for an @ at the start of a line, +possibly preceded by spaces or tabs, followed by a string of +characters as defined by `parsebib--bibtex-identifier'. When +successful, point is placed right after the item's type, i.e., +generally on the opening brace or parenthesis following the entry +type, \"@Comment\", \"@Preamble\" or \"@String\". + +The return value is the name of the item as a string, either +\"Comment\", \"Preamble\" or \"String\", or the entry +type (without the @). If an item name is found that includes an +illegal character, an error of type `parsebib-entry-type-error' +is raised. If no item is found, nil is returned and point is left +at the end of the buffer. + +POS can be a number or a marker and defaults to point." + (when pos (goto-char pos)) + (when (re-search-forward parsebib--entry-start nil 0) + (if (parsebib--looking-at-goto-end (concat "\\(" parsebib--bibtex-identifier "\\)" "[[:space:]]*[\(\{]?") 1) + (match-string-no-properties 1) + (signal 'parsebib-entry-type-error (list (point)))))) + +(defun parsebib-read-comment (&optional pos) + "Read the @Comment beginning at the line POS is on. +Return value is the text of the @Comment including the braces. +For comments that last until the end of the line (i.e., comments +that are not delimited by braces), the return value includes the +whitespace between `@comment' and the actual comment text. + +If no comment could be found, return nil. + +POS can be a number or a marker. It does not have to be at the +beginning of a line, but the @Comment entry must start at the +beginning of the line POS is on. If POS is nil, it defaults to +point." + (when pos (goto-char pos)) + (beginning-of-line) + (when (parsebib--looking-at-goto-end (concat parsebib--entry-start "\\(comment\\)[[:space:]]*[\(\{]?") 1) + (let ((beg (point))) + (if (looking-at-p "[[:space:]]*[\(\{]") + (progn (skip-chars-forward "[:space:]") + (parsebib--match-paren-forward)) + (goto-char (point-at-eol))) + (buffer-substring-no-properties beg (point))))) + +(defun parsebib-read-string (&optional pos strings) + "Read the @String definition beginning at the line POS is on. +If a proper abbreviation and expansion are found, they are +returned as a cons cell ( . ). Otherwise, nil +is returned. + +POS can be a number or a marker. It does not have to be at the +beginning of a line, but the @String entry must start at the +beginning of the line POS is on. If POS is nil, it defaults to +point. + +If STRINGS is provided it should be a hash table with string +abbreviations, which are used to expand abbrevs in the string's +expansion." + (interactive) + (when pos (goto-char pos)) + (beginning-of-line) + (when (parsebib--looking-at-goto-end (concat parsebib--entry-start "\\(string[[:space:]]*\\)[\(\{]") 1) + (let ((limit (save-excursion + (parsebib--match-paren-forward) + (point)))) + (parsebib--looking-at-goto-end (concat "[({]\\(" parsebib--bibtex-identifier "\\)[[:space:]]*=[[:space:]]*")) + (let ((abbr (match-string-no-properties 1))) + (when (and abbr (> (length abbr) 0)) ; if we found an abbrev + (let ((expansion (parsebib--parse-value limit strings))) + (goto-char limit) + (cons abbr expansion))))))) + +(defun parsebib-read-preamble (&optional pos) + "Read the @Preamble definition at the line POS is on. +Return the preamble as a string (including the braces surrounding +the preamble text), or nil if no preamble was found. + +POS can be a number or a marker. It does not have to be at the +beginning of a line, but the @Preamble must start at the +beginning of the line POS is on. If POS is nil, it defaults to +point." + (when pos (goto-char pos)) + (beginning-of-line) + (when (parsebib--looking-at-goto-end (concat parsebib--entry-start "\\(preamble[[:space:]]*\\)[\(\{]") 1) + (let ((beg (point))) + (when (parsebib--match-paren-forward) + (buffer-substring-no-properties beg (point)))))) + +(defun parsebib-read-entry (type &optional pos strings) + "Read a BibTeX entry of type TYPE at the line POS is on. +TYPE should be a string and should not contain the @ +sign. The return value is the entry as an alist of ( . +) cons pairs, or nil if no entry was found. In this +alist, the entry key is provided in the field \"=key=\" and the +entry type in the field \"=type=\". + +POS can be a number or a marker. It does not have to be at the +beginning of a line, but the entry must start at the beginning of +the line POS is on. If POS is nil, it defaults to point. + +ENTRY should not be \"Comment\", \"Preamble\" or \"String\", but +is otherwise not limited to any set of possible entry types. If +so required, the calling function has to ensure that the entry +type is valid. + +If STRINGS is provided, it should be a hash table with string +abbreviations, which are used to expand abbrevs in the entry's +fields." + (unless (member-ignore-case type '("comment" "preamble" "string")) + (when pos (goto-char pos)) + (beginning-of-line) + (when (parsebib--looking-at-goto-end (concat parsebib--entry-start type "[[:space:]]*[\(\{]")) + ;; find the end of the entry and the beginning of the entry key + (let* ((limit (save-excursion + (backward-char) + (parsebib--match-paren-forward) + (point))) + (beg (progn + (skip-chars-forward " \n\t\f") ; note the space! + (point))) + (key (when (parsebib--looking-at-goto-end (concat "\\(" parsebib--key-regexp "\\)[ \t\n\f]*,") 1) + (buffer-substring-no-properties beg (point))))) + (or key (setq key "")) ; if no key was found, we pretend it's empty and try to read the entry anyway + (skip-chars-forward "^," limit) ; move to the comma after the entry key + (let ((fields (cl-loop for field = (parsebib--find-bibtex-field limit strings) + while field collect field))) + (push (cons "=type=" type) fields) + (push (cons "=key=" key) fields) + (nreverse fields)))))) + +(defun parsebib--find-bibtex-field (limit &optional strings) + "Find the field after point. +Do not search beyond LIMIT (a buffer position). Return a +cons (FIELD . VALUE), or nil if no field was found. + +If STRINGS is provided it should be a hash table with string +abbreviations, which are used to expand abbrevs in the field's +value." + (skip-chars-forward "\"#%'(),={} \n\t\f" limit) ; move to the first char of the field name + (unless (>= (point) limit) ; if we haven't reached the end of the entry + (let ((beg (point))) + (if (parsebib--looking-at-goto-end (concat "\\(" parsebib--bibtex-identifier "\\)[[:space:]]*=[[:space:]]*") 1) + (let ((field-type (buffer-substring-no-properties beg (point)))) + (let ((field-contents (parsebib--parse-value limit strings))) + (cons field-type field-contents))))))) + +;;;;;;;;;;;;;;;;;;;; +;; high-level API ;; +;;;;;;;;;;;;;;;;;;;; + +(defun parsebib-collect-preambles () + "Collect all @Preamble definitions in the current buffer. +Return a list of strings, each string a separate @Preamble." + (save-excursion + (goto-char (point-min)) + (let (res) + (cl-loop for item = (parsebib-find-next-item) + while item do + (when (cl-equalp item "preamble") + (push (parsebib-read-preamble) res))) + (nreverse res)))) + +(defun parsebib-collect-comments () + "Collect all @Comment definitions in the current buffer. +Return a list of strings, each string a separate @Comment." + (save-excursion + (goto-char (point-min)) + (let (res) + (cl-loop for item = (parsebib-find-next-item) + while item do + (when (cl-equalp item "comment") + (push (parsebib-read-comment) res))) + (nreverse (delq nil res))))) + +(defun parsebib-collect-strings (&optional hash expand-strings) + "Collect all @String definitions in the current buffer. +Return value is a hash with the abbreviations as keys and the +expansions as values. If HASH is a hash table with test function +`equal', it is used to store the @String definitions. If +EXPAND-STRINGS is non-nil, @String expansions are expanded +themselves using the @String definitions already stored in HASH." + (or (and (hash-table-p hash) + (eq 'equal (hash-table-test hash))) + (setq hash (make-hash-table :test #'equal))) + (save-excursion + (goto-char (point-min)) + (cl-loop with string = nil + for item = (parsebib-find-next-item) + while item do + (when (cl-equalp item "string") + (setq string (parsebib-read-string nil (if expand-strings hash))) + (puthash (car string) (cdr string) hash))) + hash)) + +(defun parsebib-collect-entries (&optional hash strings inheritance) + "Collect all entries in the current buffer. +Return value is a hash table containing the entries. If HASH is +a hash table, with test function `equal', it is used to store the +entries. If STRINGS is non-nil, it should be a hash table of +string definitions, which are used to expand abbreviations used +in the entries. + +If INHERITANCE is non-nil, cross-references in the entries are +resolved: if the crossref field of an entry points to an entry +already in HASH, the fields of the latter that do not occur in +the entry are added to it. INHERITANCE indicates the inheritance +schema used for determining which fields inherit from which +fields. It can be a symbol `BibTeX' or `biblatex', or it can be +an explicit inheritance schema. (See the variable +`parsebib--biblatex-inheritances' for details on the structure of +such an inheritance schema.) It can also be the symbol t, in +which case the local variable block is checked for a +dialect (using the variable `bibtex-dialect'), or, if no such +local variable is found, the value of the variable +`bibtex-dialect'." + (or (and (hash-table-p hash) + (eq 'equal (hash-table-test hash))) + (setq hash (make-hash-table :test #'equal))) + (if (eq inheritance t) + (setq inheritance (or (parsebib-find-bibtex-dialect) + bibtex-dialect + 'BibTeX))) + (save-excursion + (goto-char (point-min)) + (cl-loop with entry = nil + for entry-type = (parsebib-find-next-item) + while entry-type do + (unless (member-ignore-case entry-type '("preamble" "string" "comment")) + (setq entry (parsebib-read-entry entry-type nil strings)) + (if entry + (puthash (cdr (assoc-string "=key=" entry)) entry hash)))) + (when inheritance + (parsebib-expand-xrefs hash inheritance)) + hash)) + +(defun parsebib-find-bibtex-dialect () + "Find the BibTeX dialect of a file if one is set. +This function looks for a local value of the variable +`bibtex-dialect' in the local variable block at the end of the +file. Return nil if no dialect is found." + (save-excursion + (goto-char (point-max)) + (let ((case-fold-search t)) + (when (re-search-backward (concat parsebib--entry-start "comment") (- (point-max) 3000) t) + (let ((comment (parsebib-read-comment))) + (when (and comment + (string-match-p "\\`{[ \n\t\r]*Local Variables:" comment) + (string-match-p "End:[ \n\t\r]*}\\'" comment) + (string-match (concat "bibtex-dialect: " (regexp-opt (mapcar #'symbol-name bibtex-dialect-list) t)) comment)) + (intern (match-string 1 comment)))))))) + +(defun parsebib-parse-buffer (&optional entries strings expand-strings inheritance) + "Parse the current buffer and return all BibTeX data. +Return list of five elements: a hash table with the entries, a +hash table with the @String definitions, a list of @Preamble +definitions, a list of @Comments and the BibTeX dialect, if +present in the file. + +If ENTRIES is a hash table with test function `equal', it is used +to store the entries. Any existing entries with identical keys +are overwritten. Similarly, if STRINGS is a hash table with test +function `equal', the @String definitions are stored in it. + +If EXPAND-STRINGS is non-nil, abbreviations in the entries and +@String definitions are expanded using the @String definitions +already in STRINGS. + +If INHERITANCE is non-nil, cross-references in the entries are +resolved: if the crossref field of an entry points to an entry +already in ENTRIES, the fields of the latter that do not occur in +the entry are added to it. INHERITANCE indicates the inheritance +schema used for determining which fields inherit from which +fields. It can be a symbol `BibTeX' or `biblatex', which means +to use the default inheritance schema for either dialect, or it +can be an explicit inheritance schema. (See the variable +`parsebib--biblatex-inheritances' for details on the structure of +such an inheritance schema.) It can also be the symbol t, in +which case the local variable block is checked for a +dialect (using the variable `bibtex-dialect'), or, if no such +local variable is found, the value of the variable +`bibtex-dialect'." + (save-excursion + (goto-char (point-min)) + (or (and (hash-table-p entries) + (eq (hash-table-test entries) 'equal)) + (setq entries (make-hash-table :test #'equal))) + (or (and (hash-table-p strings) + (eq (hash-table-test strings) 'equal)) + (setq strings (make-hash-table :test #'equal))) + (let ((dialect (or (parsebib-find-bibtex-dialect) + bibtex-dialect + 'BibTeX)) + preambles comments) + (cl-loop for item = (parsebib-find-next-item) + while item do + (cond + ((cl-equalp item "string") ; `cl-equalp' compares strings case-insensitively. + (let ((string (parsebib-read-string nil (if expand-strings strings)))) + (if string + (puthash (car string) (cdr string) strings)))) + ((cl-equalp item "preamble") + (push (parsebib-read-preamble) preambles)) + ((cl-equalp item "comment") + (push (parsebib-read-comment) comments)) + ((stringp item) + (let ((entry (parsebib-read-entry item nil (if expand-strings strings)))) + (when entry + (puthash (cdr (assoc-string "=key=" entry)) entry entries)))))) + (when inheritance (parsebib-expand-xrefs entries (if (eq inheritance t) dialect inheritance))) + (list entries strings (nreverse preambles) (nreverse comments) dialect)))) + +(provide 'parsebib) + +;;; parsebib.el ends here