summaryrefslogtreecommitdiff
path: root/tex/context/base/publ-ini.lua
diff options
context:
space:
mode:
Diffstat (limited to 'tex/context/base/publ-ini.lua')
-rw-r--r--tex/context/base/publ-ini.lua1222
1 files changed, 1222 insertions, 0 deletions
diff --git a/tex/context/base/publ-ini.lua b/tex/context/base/publ-ini.lua
new file mode 100644
index 000000000..a791f4726
--- /dev/null
+++ b/tex/context/base/publ-ini.lua
@@ -0,0 +1,1222 @@
+if not modules then modules = { } end modules ['publ-ini'] = {
+ version = 1.001,
+ comment = "this module part of publication support",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- use: for rest in gmatch(reference,"[^, ]+") do
+
+local next, rawget, type = next, rawget, type
+local match, gmatch, format, gsub = string.match, string.gmatch, string.format, string.gsub
+local concat, sort = table.concat, table.sort
+local utfsub = utf.sub
+local formatters = string.formatters
+local allocate = utilities.storage.allocate
+local settings_to_array = utilities.parsers.settings_to_array
+local sortedkeys, sortedhash = table.sortedkeys, table.sortedhash
+local lpegmatch = lpeg.match
+
+local report = logs.reporter("publications")
+local trace = false trackers.register("publications", function(v) trace = v end)
+
+local context = context
+
+local datasets = publications.datasets
+
+local variables = interfaces.variables
+
+local v_local = variables["local"]
+local v_global = variables["global"]
+
+local v_force = variables.force
+local v_standard = variables.standard
+local v_start = variables.start
+local v_none = variables.none
+local v_left = variables.left
+local v_right = variables.right
+local v_middle = variables.middle
+local v_inbetween = variables.inbetween
+
+local v_short = variables.short
+local v_cite = variables.cite
+local v_default = variables.default
+local v_reference = variables.default
+local v_dataset = variables.dataset
+
+local numbertochar = converters.characters
+
+local logsnewline = logs.newline
+local logspushtarget = logs.pushtarget
+local logspoptarget = logs.poptarget
+local csname_id = token.csname_id
+
+statistics.register("publications load time", function()
+ local publicationsstats = publications.statistics
+ local nofbytes = publicationsstats.nofbytes
+ if nofbytes > 0 then
+ return string.format("%s seconds, %s bytes, %s definitions, %s shortcuts",
+ statistics.elapsedtime(publications),nofbytes,publicationsstats.nofdefinitions,publicationsstats.nofshortcuts)
+ else
+ return nil
+ end
+end)
+
+luatex.registerstopactions(function()
+ logspushtarget("logfile")
+ logsnewline()
+ report("start used btx commands")
+ logsnewline()
+ local undefined = csname_id("undefined*crap")
+ for name, dataset in sortedhash(datasets) do
+ for command, n in sortedhash(dataset.commands) do
+ local c = csname_id(command)
+ if c and c ~= undefined then
+ report("%-20s %-20s % 5i %s",name,command,n,"known")
+ else
+ local u = csname_id(utf.upper(command))
+ if u and u ~= undefined then
+ report("%-20s %-20s % 5i %s",name,command,n,"KNOWN")
+ else
+ report("%-20s %-20s % 5i %s",name,command,n,"unknown")
+ end
+ end
+ end
+ end
+ logsnewline()
+ report("stop used btxcommands")
+ logsnewline()
+ logspoptarget()
+end)
+
+-- multipass, we need to sort because hashing is random per run and not per
+-- version (not the best changed feature of lua)
+
+local collected = allocate()
+local tobesaved = allocate()
+
+-- we use a a dedicated (and efficient as it know what it deals with) serializer,
+-- also because we need to ignore the 'details' field
+
+local function serialize(t)
+ local f_key_table = formatters[" [%q] = {"]
+ local f_key_string = formatters[" %s = %q,"]
+ local r = { "return {" }
+ local m = 1
+ for tag, entry in sortedhash(t) do
+ m = m + 1
+ r[m] = f_key_table(tag)
+ local s = sortedkeys(entry)
+ for i=1,#s do
+ local k = s[i]
+ -- if k ~= "details" then
+ m = m + 1
+ r[m] = f_key_string(k,entry[k])
+ -- end
+ end
+ m = m + 1
+ r[m] = " },"
+ end
+ r[m] = "}"
+ return concat(r,"\n")
+end
+
+local function finalizer()
+ local prefix = tex.jobname -- or environment.jobname
+ local setnames = sortedkeys(datasets)
+ for i=1,#setnames do
+ local name = setnames[i]
+ local dataset = datasets[name]
+ local userdata = dataset.userdata
+ local checksum = nil
+ local username = file.addsuffix(file.robustname(formatters["%s-btx-%s"](prefix,name)),"lua")
+ if userdata and next(userdata) then
+ if job.passes.first then
+ local newdata = serialize(userdata)
+ checksum = md5.HEX(newdata)
+ io.savedata(username,newdata)
+ end
+ else
+ os.remove(username)
+ username = nil
+ end
+ local loaded = dataset.loaded
+ local sources = dataset.sources
+ local used = { }
+ for i=1,#sources do
+ local source = sources[i]
+ if loaded[source.filename] ~= "previous" then -- or loaded[source.filename] == "current"
+ used[#used+1] = source
+ end
+ end
+ tobesaved[name] = {
+ usersource = {
+ filename = username,
+ checksum = checksum,
+ },
+ datasources = used,
+ }
+ end
+end
+
+local function initializer()
+ statistics.starttiming(publications)
+collected = publications.collected or collected -- for the moment as we load runtime
+ for name, state in next, collected do
+ local dataset = datasets[name]
+ local datasources = state.datasources
+ local usersource = state.usersource
+ if datasources then
+ for i=1,#datasources do
+ local filename = datasources[i].filename
+ publications.load(dataset,filename,"previous")
+ end
+ end
+ if usersource then
+ dataset.userdata = table.load(usersource.filename) or { }
+ end
+ end
+ statistics.stoptiming(publications)
+ function initializer() end -- will go, for now, runtime loaded
+end
+
+job.register('publications.collected',tobesaved,initializer,finalizer)
+
+if not publications.authors then
+ initializer() -- for now, runtime loaded
+end
+
+-- basic access
+
+local function getfield(dataset,tag,name)
+ local d = datasets[dataset].luadata[tag]
+ return d and d[name]
+end
+
+local function getdetail(dataset,tag,name)
+ local d = datasets[dataset].details[tag]
+ return d and d[name]
+end
+
+-- basic loading
+
+function commands.usebtxdataset(name,filename)
+ publications.load(datasets[name],filename,"current")
+end
+
+function commands.convertbtxdatasettoxml(name,nice)
+ publications.converttoxml(datasets[name],nice)
+end
+
+-- enhancing
+
+local splitauthorstring = publications.authors.splitstring
+
+local pagessplitter = lpeg.splitat(lpeg.P("-")^1)
+
+-- maybe not redo when already done
+
+function publications.enhance(dataset) -- for the moment split runs (maybe publications.enhancers)
+ statistics.starttiming(publications)
+ if type(dataset) == "string" then
+ dataset = datasets[dataset]
+ end
+ local luadata = dataset.luadata
+ local details = dataset.details
+ -- author, editor
+ for tag, entry in next, luadata do
+ local author = entry.author
+ local editor = entry.editor
+ details[tag] = {
+ author = author and splitauthorstring(author),
+ editor = editor and splitauthorstring(editor),
+ }
+ end
+ -- short
+ local shorts = { }
+ for tag, entry in next, luadata do
+ local author = details[tag].author
+ if author then
+ -- number depends on sort order
+ local t = { }
+ if #author == 0 then
+ -- what
+ else
+ local n = #author == 1 and 3 or 1
+ for i=1,#author do
+ local surnames = author[i].surnames
+ if not surnames or #surnames == 0 then
+ -- error
+ else
+ t[#t+1] = utfsub(surnames[1],1,n)
+ end
+ end
+ end
+ local year = tonumber(entry.year) or 0
+ local short = formatters["%t%02i"](t,math.mod(year,100))
+ local s = shorts[short]
+ if not s then
+ shorts[short] = tag
+ elseif type(s) == "string" then
+ shorts[short] = { s, tag }
+ else
+ s[#s+1] = tag
+ end
+ else
+ --
+ end
+ end
+ for short, tags in next, shorts do
+ if type(tags) == "table" then
+ table.sort(tags)
+ for i=1,#tags do
+ details[tags[i]].short = short .. numbertochar(i)
+ end
+ else
+ details[tags].short = short
+ end
+ end
+ -- pages
+ for tag, entry in next, luadata do
+ local pages = entry.pages
+ if pages then
+ local first, last = lpegmatch(pagessplitter,pages)
+ details[tag].pages = first and last and { first, last } or pages
+ end
+ end
+ statistics.stoptiming(publications)
+end
+
+function commands.addbtxentry(name,settings,content)
+ local dataset = datasets[name]
+ if dataset then
+ publications.addtexentry(dataset,settings,content)
+ end
+end
+
+function commands.setbtxdataset(name)
+ local dataset = rawget(datasets,name)
+ if dataset then
+ context(name)
+ else
+ report("unknown dataset %a",name)
+ end
+end
+
+function commands.setbtxentry(name,tag)
+ local dataset = rawget(datasets,name)
+ if dataset then
+ if dataset.luadata[tag] then
+ context(tag)
+ else
+ report("unknown tag %a in dataset %a",tag,name)
+ end
+ else
+ report("unknown dataset %a",name)
+ end
+end
+
+-- rendering of fields
+
+function commands.btxflush(name,tag,field)
+ local dataset = rawget(datasets,name)
+ if dataset then
+ local fields = dataset.luadata[tag]
+ if fields then
+ local value = fields[field]
+ if type(value) == "string" then
+ context(value)
+ return
+ end
+ local details = dataset.details[tag]
+ if details then
+ local value = details[field]
+ if type(value) == "string" then
+ context(value)
+ return
+ end
+ end
+ report("unknown field %a of tag %a in dataset %a",field,tag,name)
+ else
+ report("unknown tag %a in dataset %a",tag,name)
+ end
+ else
+ report("unknown dataset %a",name)
+ end
+end
+
+function commands.btxdetail(name,tag,field)
+ local dataset = rawget(datasets,name)
+ if dataset then
+ local details = dataset.details[tag]
+ if details then
+ local value = details[field]
+ if type(value) == "string" then
+ context(value)
+ else
+ report("unknown detail %a of tag %a in dataset %a",field,tag,name)
+ end
+ else
+ report("unknown tag %a in dataset %a",tag,name)
+ end
+ else
+ report("unknown dataset %a",name)
+ end
+end
+
+function commands.btxfield(name,tag,field)
+ local dataset = rawget(datasets,name)
+ if dataset then
+ local fields = dataset.luadata[tag]
+ if fields then
+ local value = fields[field]
+ if type(value) == "string" then
+ context(value)
+ else
+ report("unknown field %a of tag %a in dataset %a",field,tag,name)
+ end
+ else
+ report("unknown tag %a in dataset %a",tag,name)
+ end
+ else
+ report("unknown dataset %a",name)
+ end
+end
+
+-- testing: to be speed up with testcase
+
+function commands.btxdoifelse(name,tag,field)
+ local dataset = rawget(datasets,name)
+ if dataset then
+ local data = dataset.luadata[tag]
+ local value = data and data[field]
+ if value and value ~= "" then
+ context.firstoftwoarguments()
+ return
+ end
+ end
+ context.secondoftwoarguments()
+end
+
+function commands.btxdoif(name,tag,field)
+ local dataset = rawget(datasets,name)
+ if dataset then
+ local data = dataset.luadata[tag]
+ local value = data and data[field]
+ if value and value ~= "" then
+ context.firstofoneargument()
+ return
+ end
+ end
+ context.gobbleoneargument()
+end
+
+function commands.btxdoifnot(name,tag,field)
+ local dataset = rawget(datasets,name)
+ if dataset then
+ local data = dataset.luadata[tag]
+ local value = data and data[field]
+ if value and value ~= "" then
+ context.gobbleoneargument()
+ return
+ end
+ end
+ context.firstofoneargument()
+end
+
+-- -- alternative approach: keep data at the tex end
+
+function publications.listconcat(t)
+ local n = #t
+ if n > 0 then
+ context(t[1])
+ if n > 1 then
+ if n > 2 then
+ for i=2,n-1 do
+ context.btxlistparameter("sep")
+ context(t[i])
+ end
+ context.btxlistparameter("finalsep")
+ else
+ context.btxlistparameter("lastsep")
+ end
+ context(t[n])
+ end
+ end
+end
+
+function publications.citeconcat(t)
+ local n = #t
+ if n > 0 then
+ context(t[1])
+ if n > 1 then
+ if n > 2 then
+ for i=2,n-1 do
+ context.btxcitevariantparameter("sep")
+ context(t[i])
+ end
+ context.btxcitevariantparameter("finalsep")
+ else
+ context.btxcitevariantparameter("lastsep")
+ end
+ context(t[n])
+ end
+ end
+end
+
+function publications.singularorplural(singular,plural)
+ if lastconcatsize and lastconcatsize > 1 then
+ context(plural)
+ else
+ context(singular)
+ end
+end
+
+function commands.makebibauthorlist(settings)
+ if not settings then
+ return
+ end
+ local dataset = datasets[settings.dataset]
+ if not dataset or dataset == "" then
+ return
+ end
+ local tag = settings.tag
+ if not tag or tag == "" then
+ return
+ end
+ local asked = settings_to_array(tag)
+ if #asked == 0 then
+ return
+ end
+ local compress = settings.compress
+ local interaction = settings.interactionn == v_start
+ local limit = tonumber(settings.limit)
+ local found = { }
+ local hash = { }
+ local total = 0
+ local luadata = dataset.luadata
+ for i=1,#asked do
+ local tag = asked[i]
+ local data = luadata[tag]
+ if data then
+ local author = data.a or "Xxxxxxxxxx"
+ local year = data.y or "0000"
+ if not compress or not hash[author] then
+ local t = {
+ author = author,
+ name = name, -- first
+ year = { [year] = name },
+ }
+ total = total + 1
+ found[total] = t
+ hash[author] = t
+ else
+ hash[author].year[year] = name
+ end
+ end
+ end
+ for i=1,total do
+ local data = found[i]
+ local author = data.author
+ local year = table.keys(data.year)
+ table.sort(year)
+ if interaction then
+ for i=1,#year do
+ year[i] = string.formatters["\\bibmaybeinteractive{%s}{%s}"](data.year[year[i]],year[i])
+ end
+ end
+ context.setvalue("currentbibyear",concat(year,","))
+ if author == "" then
+ context.setvalue("currentbibauthor","")
+ else -- needs checking
+ local authors = settings_to_array(author) -- {{}{}},{{}{}}
+ local nofauthors = #authors
+ if nofauthors == 1 then
+ if interaction then
+ author = string.formatters["\\bibmaybeinteractive{%s}{%s}"](data.name,author)
+ end
+ context.setvalue("currentbibauthor",author)
+ else
+ limit = limit or nofauthors
+ if interaction then
+ for i=1,#authors do
+ authors[i] = string.formatters["\\bibmaybeinteractive{%s}{%s}"](data.name,authors[i])
+ end
+ end
+ if limit == 1 then
+ context.setvalue("currentbibauthor",authors[1] .. "\\bibalternative{otherstext}")
+ elseif limit == 2 and nofauthors == 2 then
+ context.setvalue("currentbibauthor",concat(authors,"\\bibalternative{andtext}"))
+ else
+ for i=1,limit-1 do
+ authors[i] = authors[i] .. "\\bibalternative{namesep}"
+ end
+ if limit < nofauthors then
+ authors[limit+1] = "\\bibalternative{otherstext}"
+ context.setvalue("currentbibauthor",concat(authors,"",1,limit+1))
+ else
+ authors[limit-1] = authors[limit-1] .. "\\bibalternative{andtext}"
+ context.setvalue("currentbibauthor",concat(authors))
+ end
+ end
+ end
+ end
+ -- the following use: currentbibauthor and currentbibyear
+ if i == 1 then
+ context.ixfirstcommand()
+ elseif i == total then
+ context.ixlastcommand()
+ else
+ context.ixsecondcommand()
+ end
+ end
+end
+
+local patterns = { "publ-imp-%s.mkiv", "publ-imp-%s.tex" }
+
+local function failure(name)
+ report("unknown library %a",name)
+end
+
+local function action(name,foundname)
+ context.input(foundname)
+end
+
+function commands.loadbtxdefinitionfile(name) -- a more specific name
+ commands.uselibrary {
+ name = gsub(name,"^publ%-",""),
+ patterns = patterns,
+ action = action,
+ failure = failure,
+ onlyonce = false,
+ }
+end
+
+-- lists:
+
+publications.lists = publications.lists or { }
+local lists = publications.lists
+
+local context = context
+local structures = structures
+
+local references = structures.references
+local sections = structures.sections
+
+-- per rendering
+
+local renderings = { } --- per dataset
+
+table.setmetatableindex(renderings,function(t,k)
+ local v = {
+ list = { },
+ done = { },
+ alldone = { },
+ used = { },
+ registered = { },
+ ordered = { },
+ shorts = { },
+ method = v_none,
+ currentindex = 0,
+ }
+ t[k] = v
+ return v
+end)
+
+-- why shorts vs tags: only for sorting
+
+function lists.register(dataset,tag,short)
+ local r = renderings[dataset]
+ if not short or short == "" then
+ short = tag
+ end
+ if trace then
+ report("registering publication entry %a with shortcut %a",tag,short)
+ end
+ local top = #r.registered + 1
+ -- do we really need these
+ r.registered[top] = tag
+ r.ordered [tag] = top
+ r.shorts [tag] = short
+end
+
+function lists.nofregistered(dataset)
+ return #renderings[dataset].registered
+end
+
+function lists.setmethod(dataset,method)
+ local r = renderings[dataset]
+ r.method = method or v_none
+ r.list = { }
+ r.done = { }
+end
+
+function lists.collectentries(specification)
+ local dataset = specification.btxdataset
+ if not dataset then
+ return
+ end
+ local rendering = renderings[dataset]
+ local method = rendering.method
+ if method == v_none then
+ return
+ end
+-- method=v_local --------------------
+ local result = structures.lists.filter(specification)
+ lists.result = result
+ local section = sections.currentid()
+ local list = rendering.list
+ local done = rendering.done
+ local alldone = rendering.alldone
+ if method == v_local then
+ for listindex=1,#result do
+ local r = result[listindex]
+ local u = r.userdata
+ if u and u.btxset == dataset then
+ local tag = u.btxref
+ if tag and done[tag] ~= section then
+ done[tag] = section
+ alldone[tag] = true
+ list[#list+1] = { tag, listindex }
+ end
+ end
+ end
+ elseif method == v_global then
+ for listindex=1,#result do
+ local r = result[listindex]
+ local u = r.userdata
+ if u and u.btxset == dataset then
+ local tag = u.btxref
+ if tag and not alldone[tag] and done[tag] ~= section then
+ done[tag] = section
+ alldone[tag] = true
+ list[#list+1] = { tag, listindex }
+ end
+ end
+ end
+ elseif method == v_force then
+ -- only for checking, can have duplicates, todo: collapse page numbers, although
+ -- we then also needs deferred writes
+ for listindex=1,#result do
+ local r = result[listindex]
+ local u = r.userdata
+ if u and u.btxset == dataset then
+ local tag = u.btxref
+ if tag then
+ list[#list+1] = { tag, listindex }
+ end
+ end
+ end
+ end
+end
+
+lists.sorters = {
+ [v_short] = function(dataset,rendering,list)
+ local shorts = rendering.shorts
+ return function(a,b)
+ local aa, bb = a and a[1], b and b[1]
+ if aa and bb then
+ aa, bb = shorts[aa], shorts[bb]
+ return aa and bb and aa < bb
+ end
+ return false
+ end
+ end,
+ [v_reference] = function(dataset,rendering,list)
+ return function(a,b)
+ local aa, bb = a and a[1], b and b[1]
+ if aa and bb then
+ return aa and bb and aa < bb
+ end
+ return false
+ end
+ end,
+ [v_dataset] = function(dataset,rendering,list)
+ return function(a,b)
+ local aa, bb = a and a[1], b and b[1]
+ if aa and bb then
+ aa, bb = list[aa].index or 0, list[bb].index or 0
+ return aa and bb and aa < bb
+ end
+ return false
+ end
+ end,
+ [v_default] = function(dataset,rendering,list) -- not really needed
+ local ordered = rendering.ordered
+ return function(a,b)
+ local aa, bb = a and a[1], b and b[1]
+ if aa and bb then
+ aa, bb = ordered[aa], ordered[bb]
+ return aa and bb and aa < bb
+ end
+ return false
+ end
+ end,
+}
+
+function lists.flushentries(dataset,sortvariant)
+ local rendering = renderings[dataset]
+ local list = rendering.list
+ local compare = lists.sorters[sortvariant] or lists.sorters[v_default]
+ compare = type(compare) == "function" and compare(dataset,rendering,list)
+ if compare then
+ sort(list,compare)
+ end
+ for i=1,#list do
+ context.setvalue("currentbtxindex",i)
+ context.btxhandlelistentry(list[i][1]) -- we can pass i here too ... more efficient to avoid the setvalue
+ end
+end
+
+function lists.fetchentries(dataset)
+ local list = renderings[dataset].list
+ for i=1,#list do
+ context.setvalue("currentbtxindex",i)
+ context.btxchecklistentry(list[i][1])
+ end
+end
+
+function lists.filterall(dataset)
+ local r = renderings[dataset]
+ local list = r.list
+ local registered = r.registered
+ for i=1,#registered do
+ list[i] = { registered[i], i }
+ end
+end
+
+function lists.registerplaced(dataset,tag)
+ renderings[dataset].used[tag] = true
+end
+
+function lists.doifalreadyplaced(dataset,tag)
+ commands.doifelse(renderings[dataset].used[tag])
+end
+
+-- we ask for <n>:tag but when we can't find it we go back
+-- to look for previous definitions, and when not found again
+-- we look forward
+
+local function compare(a,b)
+ local aa, bb = a and a[3], b and b[3]
+ return aa and bb and aa < bb
+end
+
+-- maybe hash subsets
+-- how efficient is this? old leftovers?
+
+-- rendering ?
+
+local f_reference = formatters["r:%s:%s:%s"] -- dataset, instance, tag
+local f_destination = formatters["d:%s:%s:%s"] -- dataset, instance, tag
+
+function lists.resolve(dataset,reference) -- maybe already feed it split
+ -- needs checking (the prefix in relation to components)
+ local subsets = nil
+ local block = tex.count.btxblock
+ local collected = references.collected
+ local prefix = nil -- todo: dataset ?
+ if prefix and prefix ~= "" then
+ subsets = { collected[prefix] or collected[""] }
+ else
+ local components = references.productdata.components
+ local subset = collected[""]
+ if subset then
+ subsets = { subset }
+ else
+ subsets = { }
+ end
+ for i=1,#components do
+ local subset = collected[components[i]]
+ if subset then
+ subsets[#subsets+1] = subset
+ end
+ end
+ end
+ if #subsets > 0 then
+ local result, nofresult, done = { }, 0, { }
+ for i=1,#subsets do
+ local subset = subsets[i]
+ for rest in gmatch(reference,"[^, ]+") do
+ local blk, tag, found = block, nil, nil
+ if block then
+-- tag = blk .. ":" .. rest
+ tag = dataset .. ":" .. blk .. ":" .. rest
+ found = subset[tag]
+ if not found then
+ for i=block-1,1,-1 do
+ tag = i .. ":" .. rest
+ found = subset[tag]
+ if found then
+ blk = i
+ break
+ end
+ end
+ end
+ end
+ if not found then
+ blk = "*"
+ tag = dataset .. ":" .. blk .. ":" .. rest
+ found = subset[tag]
+ end
+ if found then
+ local current = tonumber(found.entries and found.entries.text) -- tonumber needed
+ if current and not done[current] then
+ nofresult = nofresult + 1
+ result[nofresult] = { blk, rest, current }
+ done[current] = true
+ end
+ end
+ end
+ end
+ local first, last, firsti, lasti, firstr, lastr
+ local collected, nofcollected = { }, 0
+ for i=1,nofresult do
+ local r = result[i]
+ local current = r[3]
+ if not first then
+ first, last, firsti, lasti, firstr, lastr = current, current, i, i, r, r
+ elseif current == last + 1 then
+ last, lasti, lastr = current, i, r
+ else
+ if last > first + 1 then
+ nofcollected = nofcollected + 1
+ collected[nofcollected] = { firstr, lastr }
+ else
+ nofcollected = nofcollected + 1
+ collected[nofcollected] = firstr
+ if last > first then
+ nofcollected = nofcollected + 1
+ collected[nofcollected] = lastr
+ end
+ end
+ first, last, firsti, lasti, firstr, lastr = current, current, i, i, r, r
+ end
+ end
+ if first and last then
+ if last > first + 1 then
+ nofcollected = nofcollected + 1
+ collected[nofcollected] = { firstr, lastr }
+ else
+ nofcollected = nofcollected + 1
+ collected[nofcollected] = firstr
+ if last > first then
+ nofcollected = nofcollected + 1
+ collected[nofcollected] = lastr
+ end
+ end
+ end
+ if nofcollected > 0 then
+ for i=1,nofcollected do
+ local c = collected[i]
+ if i == nofcollected then
+ context.btxlistvariantparameter("lastpubsep")
+ elseif i > 1 then
+ context.btxlistvariantparameter("pubsep")
+ end
+ if #c == 3 then -- a range (3 is first or last)
+ context.btxdirectlink(f_reference(dataset,c[1],c[2]),c[3])
+ else
+ local f, l = c[2], c[2]
+ context.btxdirectlink(f_reference(dataset,f[1],f[2]),f[3])
+ context.endash() -- to do
+ context.btxdirectlink(f_reference(dataset,l[4],l[5]),l[6])
+ end
+ end
+ else
+ context("[btx error 1]")
+ end
+ else
+ context("[btx error 2]")
+ end
+end
+
+local done = { }
+
+function commands.btxreference(dataset,block,tag,data)
+ local ref = f_reference(dataset,block,tag)
+ if not done[ref] then
+ done[ref] = true
+ context.dodirectfullreference(ref,data)
+ end
+end
+
+local done = { }
+
+function commands.btxdestination(dataset,block,tag,data)
+ local ref = f_destination(dataset,block,tag)
+ if not done[ref] then
+ done[ref] = true
+ context.dodirectfullreference(ref,data)
+ end
+end
+
+commands.btxsetlistmethod = lists.setmethod
+commands.btxresolvelistreference = lists.resolve
+commands.btxregisterlistentry = lists.registerplaced
+commands.btxaddtolist = lists.addentry
+commands.btxcollectlistentries = lists.collectentries
+commands.btxfetchlistentries = lists.fetchentries
+commands.btxflushlistentries = lists.flushentries
+commands.btxdoifelselistentryplaced = lists.doifalreadyplaced
+
+local citevariants = { }
+publications.citevariants = citevariants
+
+-- helper
+
+local compare = sorters.comparers.basic -- (a,b)
+local strip = sorters.strip
+local splitter = sorters.splitters.utf
+
+local function sortedtags(dataset,list,sorttype)
+ local luadata = datasets[dataset].luadata
+ local valid = { }
+ for i=1,#list do
+ local tag = list[i]
+ local entry = luadata[tag]
+ if entry then
+ local key = entry[sorttype]
+ if key then
+ valid[#valid+1] = {
+ tag = tag,
+ split = splitter(strip(key))
+ }
+ else
+ end
+ end
+ end
+ if #valid == 0 or #valid ~= #list then
+ return list
+ else
+ table.sort(valid,sorters.comparers.basic)
+ for i=1,#valid do
+ valid[i] = valid[i].tag
+ end
+ return valid
+ end
+end
+
+-- todo: standard : current
+
+local splitter = lpeg.splitat("::")
+
+function commands.btxhandlecite(dataset,tag,mark,variant,sorttype,setup) -- variant for tracing
+ local prefix, rest = lpegmatch(splitter,tag)
+ if rest then
+ dataset = prefix
+ else
+ rest = tag
+ end
+ context.setvalue("currentbtxdataset",dataset)
+ local tags = settings_to_array(rest)
+ if #tags > 0 then
+ if sorttype and sorttype ~= "" then
+ tags = sortedtags(dataset,tags,sorttype)
+ end
+ context.btxcitevariantparameter(v_left)
+ for i=1,#tags do
+ local tag = tags[i]
+ context.setvalue("currentbtxtag",tag)
+ if i > 1 then
+ context.btxcitevariantparameter(v_middle)
+ end
+ if mark then
+ context.dobtxmarkcitation(dataset,tag)
+ end
+ context.formatted.directsetup(setup) -- cite can become alternative
+ end
+ context.btxcitevariantparameter(v_right)
+ else
+ -- error
+ end
+end
+
+function commands.btxhandlenocite(dataset,tag)
+ local prefix, rest = lpegmatch(splitter,tag)
+ if rest then
+ dataset = prefix
+ else
+ rest = tag
+ end
+ context.setvalue("currentbtxdataset",dataset)
+ local tags = settings_to_array(rest)
+ for i=1,#tags do
+ context.dobtxmarkcitation(dataset,tags[i])
+ end
+end
+
+function commands.btxcitevariant(dataset,block,tags,variant)
+ local action = citevariants[variant] or citevariants.default
+ if action then
+ action(dataset,tags,variant)
+ end
+end
+
+function citevariants.default(dataset,tags,variant)
+ local content = getfield(dataset,tags,variant)
+ if content then
+ context(content)
+ end
+end
+
+-- todo : sort
+-- todo : choose between publications or commands namespace
+-- todo : use details.author
+-- todo : sort details.author
+
+local function collectauthoryears(dataset,tags)
+ local luadata = datasets[dataset].luadata
+ local list = settings_to_array(tags)
+ local found = { }
+ local result = { }
+ local order = { }
+ for i=1,#list do
+ local tag = list[i]
+ local entry = luadata[tag]
+ if entry then
+ local year = entry.year
+ local author = entry.author
+ if author and year then
+ local a = found[author]
+ if not a then
+ a = { }
+ found[author] = a
+ order[#order+1] = author
+ end
+ local y = a[year]
+ if not y then
+ y = { }
+ a[year] = y
+ end
+ y[#y+1] = tag
+ end
+ end
+ end
+ -- found = { author = { year_1 = { e1, e2, e3 } } }
+ for i=1,#order do
+ local author = order[i]
+ local years = found[author]
+ local yrs = { }
+ for year, entries in next, years do
+ if subyears then
+ -- -- add letters to all entries of an author and if so shouldn't
+ -- -- we tag all years of an author as soon as we do this?
+ -- if #entries > 1 then
+ -- for i=1,#years do
+ -- local entry = years[i]
+ -- -- years[i] = year .. string.char(i + string.byte("0") - 1)
+ -- end
+ -- end
+ else
+ yrs[#yrs+1] = year
+ end
+ end
+ result[i] = { author = author, years = yrs }
+ end
+ return result, order
+end
+
+-- (name, name and name) .. how names? how sorted?
+-- todo: we loop at the tex end .. why not here
+-- \cite[{hh,afo},kvm]
+
+-- maybe we will move this tex anyway
+
+function citevariants.author(dataset,tags)
+ local result, order = collectauthoryears(dataset,tags,method,what) -- we can have a collectauthors
+ publications.citeconcat(order)
+end
+
+local function authorandyear(dataset,tags,formatter)
+ local result, order = collectauthoryears(dataset,tags,method,what) -- we can have a collectauthors
+ for i=1,#result do
+ local r = result[i]
+ order[i] = formatter(r.author,r.years) -- reuse order
+ end
+ publications.citeconcat(order)
+end
+
+function citevariants.authoryear(dataset,tags)
+ authorandyear(dataset,tags,formatters["%s (%, t)"])
+end
+
+function citevariants.authoryears(dataset,tags)
+ authorandyear(dataset,tags,formatters["%s, %, t"])
+end
+
+function citevariants.authornum(dataset,tags)
+ local result, order = collectauthoryears(dataset,tags,method,what) -- we can have a collectauthors
+ publications.citeconcat(order)
+ context.btxcitevariantparameter(v_inbetween)
+ lists.resolve(dataset,tags) -- left/right ?
+end
+
+function citevariants.short(dataset,tags)
+ local short = getdetail(dataset,tags,"short")
+ if short then
+ context(short)
+ end
+end
+
+function citevariants.page(dataset,tags)
+ local pages = getdetail(dataset,tags,"pages")
+ if not pages then
+ -- nothing
+ elseif type(pages) == "table" then
+ context(pages[1])
+ context.btxcitevariantparameter(v_inbetween)
+ context(pages[2])
+ else
+ context(pages)
+ end
+end
+
+function citevariants.num(dataset,tags)
+ lists.resolve(dataset,tags)
+end
+
+function citevariants.serial(dataset,tags) -- the traditional fieldname is "serial" and not "index"
+ local index = getfield(dataset,tags,"index")
+ if index then
+ context(index)
+ end
+end
+
+-- List variants
+
+local listvariants = { }
+publications.listvariants = listvariants
+
+-- function commands.btxhandlelist(dataset,block,tag,variant,setup)
+-- if sorttype and sorttype ~= "" then
+-- tags = sortedtags(dataset,tags,sorttype)
+-- end
+-- context.setvalue("currentbtxtag",tag)
+-- context.btxlistvariantparameter(v_left)
+-- context.formatted.directsetup(setup)
+-- context.btxlistvariantparameter(v_right)
+-- end
+
+function commands.btxlistvariant(dataset,block,tags,variant,listindex)
+ local action = listvariants[variant] or listvariants.default
+ if action then
+ action(dataset,block,tags,variant,tonumber(listindex) or 0)
+ end
+end
+
+function listvariants.default(dataset,block,tags,variant)
+ context("?")
+end
+
+function listvariants.num(dataset,block,tags,variant,listindex)
+ context.btxdirectlink(f_destination(dataset,block,tags),listindex) -- not okay yet
+end
+
+function listvariants.short(dataset,block,tags,variant,listindex)
+ local short = getdetail(dataset,tags,variant,variant)
+ if short then
+ context(short)
+ end
+end