4747-- stylua: ignore end
4848--- @diagnostic enable : undefined-global , unused-local , lowercase-global
4949
50- local parseBibtex = function (fn )
50+ local bibcompat = require (" packages.bibtex.bibmaps" )
51+ local crossrefmap , fieldmap = bibcompat .crossrefmap , bibcompat .fieldmap
52+
53+ local function consolidateEntry (entry , label )
54+ local consolidated = {}
55+ for field , value in pairs (entry .attributes ) do
56+ consolidated [field ] = value
57+ local alias = fieldmap [field ]
58+ if alias then
59+ if entry .attributes [alias ] then
60+ SU .warn (" Duplicate field '" .. field .. " ' and alias '" .. alias .. " ' in entry '" .. label .. " '" )
61+ else
62+ consolidated [alias ] = value
63+ end
64+ end
65+ end
66+ entry .attributes = consolidated
67+ return entry
68+ end
69+
70+ --- Parse a BibTeX file and populate a bibliography table.
71+ -- @tparam string fn Filename
72+ -- @tparam table biblio Table of entries
73+ local function parseBibtex (fn , biblio )
5174 fn = SILE .resolveFile (fn ) or SU .error (" Unable to resolve Bibtex file " .. fn )
5275 local fh , e = io.open (fn )
5376 if e then
@@ -58,14 +81,45 @@ local parseBibtex = function (fn)
5881 if not t or not t [1 ] or t .id ~= " document" then
5982 SU .error (" Error parsing bibtex" )
6083 end
61- local entries = {}
6284 for i = 1 , # t do
6385 if t [i ].id == " entry" then
6486 local ent = t [i ][1 ]
65- entries [ent .label ] = { type = ent .type , attributes = ent [1 ] }
87+ local entry = { type = ent .type , attributes = ent [1 ] }
88+ if biblio [ent .label ] then
89+ SU .warn (" Duplicate entry key '" .. ent .label .. " ', picking the last one" )
90+ end
91+ biblio [ent .label ] = consolidateEntry (entry , ent .label )
92+ end
93+ end
94+ end
95+
96+ --- Copy fields from the parent entry to the child entry.
97+ -- BibLaTeX/Biber have a complex inheritance system for fields.
98+ -- This implementation is more naive, but should be sufficient for reasonable
99+ -- use cases.
100+ -- @tparam table parent Parent entry
101+ -- @tparam table entry Child entry
102+ local function fieldsInherit (parent , entry )
103+ local map = crossrefmap [parent .type ] and crossrefmap [parent .type ][entry .type ]
104+ if not map then
105+ -- @xdata and any other unknown types: inherit all missing fields
106+ for field , value in pairs (parent .attributes ) do
107+ if not entry .attributes [field ] then
108+ entry .attributes [field ] = value
109+ end
110+ end
111+ return -- done
112+ end
113+ for field , value in pairs (parent .attributes ) do
114+ if map [field ] == nil and not entry .attributes [field ] then
115+ entry .attributes [field ] = value
116+ end
117+ for childfield , parentfield in pairs (map ) do
118+ if parentfield and not entry .attributes [parentfield ] then
119+ entry .attributes [parentfield ] = parent .attributes [childfield ]
120+ end
66121 end
67122 end
68- return entries
69123end
70124
71125--- Resolve the 'crossref' and 'xdata' fields on a bibliography entry.
@@ -104,11 +158,7 @@ local function crossrefAndXDataResolve (bib, entry)
104158 local parent = bib [ref ]
105159 if parent then
106160 crossrefAndXDataResolve (bib , parent )
107- for k , v in pairs (parent .attributes ) do
108- if not entry .attributes [k ] then
109- entry .attributes [k ] = v
110- end
111- end
161+ fieldsInherit (parent , entry )
112162 else
113163 SU .warn (" Unknown crossref " .. ref .. " in bibliography entry " .. entry .label )
114164 end
133183function package :registerCommands ()
134184 self :registerCommand (" loadbibliography" , function (options , _ )
135185 local file = SU .required (options , " file" , " loadbibliography" )
136- SILE .scratch .bibtex .bib = parseBibtex ( file ) -- Later we'll do multiple bibliogs, but not now
186+ parseBibtex ( file , SILE .scratch .bibtex .bib )
137187 end )
138188
139189 self :registerCommand (" bibstyle" , function (_ , _ )
0 commit comments