]> git.bundesbrandschatzamt.de Git - emacsconfig.git/commitdiff
install parsebib
authorAndreas Gerler <baronNOSPAMbundesbrandschatzamt.de>
Fri, 22 Nov 2019 14:10:46 +0000 (15:10 +0100)
committerAndreas Gerler <baronNOSPAMbundesbrandschatzamt.de>
Fri, 22 Nov 2019 14:10:46 +0000 (15:10 +0100)
elpa/parsebib-20181219.928/parsebib-autoloads.el [new file with mode: 0644]
elpa/parsebib-20181219.928/parsebib-pkg.el [new file with mode: 0644]
elpa/parsebib-20181219.928/parsebib.el [new file with mode: 0644]

diff --git a/elpa/parsebib-20181219.928/parsebib-autoloads.el b/elpa/parsebib-20181219.928/parsebib-autoloads.el
new file mode 100644 (file)
index 0000000..e62905f
--- /dev/null
@@ -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))))
+
+\f
+;;;### (autoloads nil "parsebib" "parsebib.el" (0 0 0 0))
+;;; Generated autoloads from parsebib.el
+
+(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "parsebib" '("parsebib-")))
+
+;;;***
+\f
+;; 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 (file)
index 0000000..e1e13da
--- /dev/null
@@ -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" . "joostkremersNOSPAMfastmail.fm")) :maintainer '("Joost Kremers" . "joostkremersNOSPAMfastmail.fm"))
diff --git a/elpa/parsebib-20181219.928/parsebib.el b/elpa/parsebib-20181219.928/parsebib.el
new file mode 100644 (file)
index 0000000..aff0dee
--- /dev/null
@@ -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 <joostkremersNOSPAMfastmail.fm>
+;; Maintainer: Joost Kremers <joostkremersNOSPAMfastmail.fm>
+;; 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 (<abbrev> . <expansion>).  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 (<field> .
+<contents>) 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