1if not modules then modules = { } end modules ['publ-ini'] = {
2 version = 1.001,
3 comment = "this module part of publication support",
4 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5 copyright = "PRAGMA ADE / ConTeXt Development Team",
6 license = "see context related readme files"
7}
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24local next, rawget, type, tostring, tonumber = next, rawget, type, tostring, tonumber
25local match, find, gsub = string.match, string.find, string.gsub
26local concat, sort, tohash = table.concat, table.sort, table.tohash
27local mod = math.mod
28local formatters = string.formatters
29local allocate = utilities.storage.allocate
30local settings_to_array, settings_to_set = utilities.parsers.settings_to_array, utilities.parsers.settings_to_set
31local sortedkeys, sortedhash = table.sortedkeys, table.sortedhash
32local setmetatableindex = table.setmetatableindex
33local lpegmatch = lpeg.match
34local P, S, C, Ct, Cs, R, Carg = lpeg.P, lpeg.S, lpeg.C, lpeg.Ct, lpeg.Cs, lpeg.R, lpeg.Carg
35local upper = characters.upper
36
37local report = logs.reporter("publications")
38local report_cite = logs.reporter("publications","cite")
39local report_list = logs.reporter("publications","list")
40local report_suffix = logs.reporter("publications","suffix")
41
42local trace = false trackers.register("publications", function(v) trace = v end)
43local trace_cite = false trackers.register("publications.cite", function(v) trace_cite = v end)
44local trace_missing = false trackers.register("publications.cite.missing", function(v) trace_missing = v end)
45local trace_references = false trackers.register("publications.cite.references", function(v) trace_references = v end)
46local trace_details = false trackers.register("publications.details", function(v) trace_details = v end)
47local trace_suffixes = false trackers.register("publications.suffixes", function(v) trace_suffixes = v end)
48
49publications = publications or { }
50local datasets = publications.datasets
51local writers = publications.writers
52local casters = publications.casters
53local detailed = publications.detailed
54local enhancer = publications.enhancer
55local enhancers = publications.enhancers
56
57if not publications.btx then publications.btx = { } end
58
59local tracers = publications.tracers or { }
60publications.tracers = tracers
61
62local setmacro = interfaces.setmacro
63local setcounter = tex.setcounter
64local variables = interfaces.variables
65
66local v_local = variables["local"]
67local v_global = variables["global"]
68
69local v_force = variables.force
70local v_normal = variables.normal
71local v_reverse = variables.reverse
72local v_none = variables.none
73local v_yes = variables.yes
74local v_no = variables.no
75local v_all = variables.all
76local v_always = variables.always
77local v_text = variables.text
78local v_doublesided = variables.doublesided
79local v_default = variables.default
80local v_dataset = variables.dataset
81
82local conditionals = tex.conditionals
83
84local isdefined = tex.isdefined
85
86
87
88
89
90local manipulators = typesetters.manipulators
91local splitmanipulation = manipulators.splitspecification
92local applymanipulation = manipulators.applyspecification
93local manipulatormethods = manipulators.methods
94
95
96
97manipulatormethods.Word = converters.Word
98manipulatormethods.WORD = converters.WORD
99manipulatormethods.Words = converters.Words
100manipulatormethods.WORDS = converters.WORDS
101
102local context = context
103local commands = commands
104local implement = interfaces.implement
105
106local ctx_doifelse = commands.doifelse
107local ctx_doif = commands.doif
108local ctx_doifnot = commands.doifnot
109local ctx_gobbletwoarguments = context.gobbletwoarguments
110
111local ctx_btxhandlelistentry = context.btxhandlelistentry
112local ctx_btxhandlecombientry = context.btxhandlecombientry
113local ctx_btxchecklistentry = context.btxchecklistentry
114
115local ctx_btxsetdataset = context.btxsetdataset
116local ctx_btxsettag = context.btxsettag
117local ctx_btxsetnumber = context.btxsetnumber
118local ctx_btxsetlanguage = context.btxsetlanguage
119local ctx_btxsetcombis = context.btxsetcombis
120local ctx_btxsetcategory = context.btxsetcategory
121local ctx_btxsetfirst = context.btxsetfirst
122local ctx_btxsetsecond = context.btxsetsecond
123local ctx_btxsetsuffix = context.btxsetsuffix
124local ctx_btxsetinternal = context.btxsetinternal
125local ctx_btxsetlefttext = context.btxsetlefttext
126local ctx_btxsetrighttext = context.btxsetrighttext
127local ctx_btxsetbefore = context.btxsetbefore
128local ctx_btxsetafter = context.btxsetafter
129local ctx_btxsetbacklink = context.btxsetbacklink
130local ctx_btxsetfirstinternal = context.btxsetfirstinternal
131local ctx_btxsetlastinternal = context.btxsetlastinternal
132local ctx_btxsetauthorfield = context.btxsetauthorfield
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152local ctx_btxsetfirstpage = context.btxsetfirstpage
153local ctx_btxsetlastpage = context.btxsetlastpage
154
155local ctx_btxstartcite = context.btxstartcite
156local ctx_btxstopcite = context.btxstopcite
157local ctx_btxstartciteauthor = context.btxstartciteauthor
158local ctx_btxstopciteauthor = context.btxstopciteauthor
159local ctx_btxstartsubcite = context.btxstartsubcite
160local ctx_btxstopsubcite = context.btxstopsubcite
161local ctx_btxstartlistentry = context.btxstartlistentry
162local ctx_btxstoplistentry = context.btxstoplistentry
163local ctx_btxstartcombientry = context.btxstartcombientry
164local ctx_btxstopcombientry = context.btxstopcombientry
165
166local ctx_btxflushauthor = context.btxflushauthor
167
168local ctx_btxsetnoflistentries = context.btxsetnoflistentries
169local ctx_btxsetcurrentlistentry = context.btxsetcurrentlistentry
170local ctx_btxsetcurrentlistindex = context.btxsetcurrentlistindex
171
172local ctx_btxsetcount = context.btxsetcount
173local ctx_btxsetconcat = context.btxsetconcat
174
175local ctx_btxcitesetup = context.btxcitesetup
176local ctx_btxsubcitesetup = context.btxsubcitesetup
177local ctx_btxnumberingsetup = context.btxnumberingsetup
178local ctx_btxpagesetup = context.btxpagesetup
179local ctx_btxlistsetup = context.btxlistsetup
180
181local trialtypesetting = context.trialtypesetting
182
183languages.data = languages.data or { }
184local data = languages.data
185
186local specifications = publications.specifications
187local currentspecification = specifications[false]
188local ignoredfields = { }
189publications.currentspecification = currentspecification
190
191local function setspecification(name)
192 currentspecification = specifications[name]
193 if trace then
194 report("setting specification %a",type(name) == "string" and name or "anything")
195 end
196 publications.currentspecification = currentspecification
197end
198
199publications.setspecification = setspecification
200
201implement {
202 name = "btxsetspecification",
203 actions = setspecification,
204 arguments = "string",
205}
206
207local optionalspace = lpeg.patterns.whitespace^0
208local prefixsplitter = optionalspace * lpeg.splitat(optionalspace * P("::") * optionalspace)
209
210statistics.register("publications load time", function()
211 local publicationsstats = publications.statistics
212 local nofbytes = publicationsstats.nofbytes
213 if nofbytes > 0 then
214 return string.format("%s seconds, %s bytes, %s definitions, %s shortcuts",
215 statistics.elapsedtime(publications),
216 nofbytes,
217 publicationsstats.nofdefinitions or 0,
218 publicationsstats.nofshortcuts or 0
219 )
220 else
221 return nil
222 end
223end)
224
225logs.registerfinalactions(function()
226 local done = false
227 local unknown = false
228 for name, dataset in sortedhash(datasets) do
229 for command, n in sortedhash(dataset.commands) do
230 if not done then
231 logs.startfilelogging(report,"used btx commands")
232 done = true
233 end
234 if isdefined(command) then
235 report("%-20s %-20s % 5i %s",name,command,n,"known")
236 elseif isdefined(upper(command)) then
237 report("%-20s %-20s % 5i %s",name,command,n,"KNOWN")
238 else
239 report("%-20s %-20s % 5i %s",name,command,n,"unknown")
240 unknown = true
241 end
242 end
243 end
244 if done then
245 logs.stopfilelogging()
246 end
247 if unknown and logs.loggingerrors() then
248 logs.starterrorlogging(report,"unknown btx commands")
249 for name, dataset in sortedhash(datasets) do
250 for command, n in sortedhash(dataset.commands) do
251 if not isdefined(command) and not isdefined(upper(command)) then
252 report("%-20s %-20s % 5i %s",name,command,n,"unknown")
253 end
254 end
255 end
256 logs.stoperrorlogging()
257 end
258end)
259
260
261
262
263local collected = allocate()
264local tobesaved = allocate()
265
266do
267
268 local function serialize(t)
269 local f_key_table = formatters[" [%q] = {"]
270 local f_key_string = formatters[" %s = %q,"]
271 local r = { "return {" }
272 local m = 1
273 for tag, entry in sortedhash(t) do
274 m = m + 1
275 r[m] = f_key_table(tag)
276 local s = sortedkeys(entry)
277 for i=1,#s do
278 local k = s[i]
279 m = m + 1
280 r[m] = f_key_string(k,entry[k])
281 end
282 m = m + 1
283 r[m] = " },"
284 end
285 r[m] = "}"
286 return concat(r,"\n")
287 end
288
289 local function finalizer()
290 local prefix = tex.jobname
291 local setnames = sortedkeys(datasets)
292 for i=1,#setnames do
293 local name = setnames[i]
294 local dataset = datasets[name]
295 local userdata = dataset.userdata
296 local checksum = nil
297 local username = file.addsuffix(file.robustname(formatters["%s-btx-%s"](prefix,name)),"lua")
298 if userdata and next(userdata) then
299 if job.passes.first then
300 local newdata = serialize(userdata)
301 checksum = md5.HEX(newdata)
302 io.savedata(username,newdata)
303 end
304 else
305 os.remove(username)
306 username = nil
307 end
308 local loaded = dataset.loaded
309 local sources = dataset.sources
310 local used = { }
311 for i=1,#sources do
312 local source = sources[i]
313
314 if loaded[source.filename] ~= "previous" or loaded[source.filename] == "current" then
315 used[#used+1] = source
316 end
317 end
318 tobesaved[name] = {
319 usersource = {
320 filename = username,
321 checksum = checksum,
322 },
323 datasources = used,
324 }
325 end
326 end
327
328 local function initializer()
329 statistics.starttiming(publications)
330 for name, state in sortedhash(collected) do
331 local dataset = datasets[name]
332 local datasources = state.datasources
333 local usersource = state.usersource
334 if datasources then
335 for i=1,#datasources do
336 local filename = datasources[i].filename
337 publications.load {
338 dataset = dataset,
339 filename = filename,
340 kind = "previous"
341 }
342 end
343 end
344 if usersource then
345 dataset.userdata = table.load(usersource.filename) or { }
346 end
347 end
348 statistics.stoptiming(publications)
349 function initializer() end
350 end
351
352 job.register('publications.collected',tobesaved,initializer,finalizer)
353
354end
355
356
357
358
359local nofcitations = 0
360local usedentries = nil
361local citetolist = nil
362local listtocite = nil
363local listtolist = nil
364
365do
366
367 local initialize = nil
368
369 initialize = function(t)
370 usedentries = allocate { }
371 citetolist = allocate { }
372 listtocite = allocate { }
373 listtolist = allocate { }
374 local names = { }
375 local p_collect = (C(R("09")^1) * Carg(1) / function(s,entry) listtocite[tonumber(s)] = entry end + P(1))^0
376 local nofunique = 0
377 local nofreused = 0
378
379
380
381 local internals = structures.references.internals
382 for i, entry in sortedhash(internals) do
383 local metadata = entry.metadata
384 if metadata then
385 local kind = metadata.kind
386 if kind == "full" then
387
388 local userdata = entry.userdata
389 if userdata then
390 local tag = userdata.btxref
391 if tag then
392 local set = userdata.btxset or v_default
393 local s = usedentries[set]
394 if s then
395 local u = s[tag]
396 if u then
397 u[#u+1] = entry
398 else
399 s[tag] = { entry }
400 end
401 nofreused = nofreused + 1
402 else
403 usedentries[set] = { [tag] = { entry } }
404 nofunique = nofunique + 1
405 end
406
407 local int = tonumber(userdata.btxint)
408 if int then
409 listtocite[int] = entry
410 end
411 local detail = datasets[set].details[tag]
412
413 if detail then
414 local author = detail.author
415 if author then
416 for i=1,#author do
417 local a = author[i]
418 local s = a.surnames
419 if s then
420 local c = concat(s,"+")
421 local n = names[c]
422 if n then
423 n[#n+1] = a
424 break
425 else
426 names[c] = { a }
427 end
428 end
429 end
430 end
431 end
432 end
433 end
434 elseif kind == "btx" then
435
436 local userdata = entry.userdata
437 if userdata then
438 local int = tonumber(userdata.btxint)
439 if int then
440 citetolist[int] = entry
441 end
442 end
443 end
444 end
445 end
446 for k, v in sortedhash(names) do
447 local n = #v
448 if n > 1 then
449 local original = v[1].original
450 for i=2,n do
451 if original ~= v[i].original then
452 report("potential clash in name %a",k)
453 for i=1,n do
454 v[i].state = 1
455 end
456 break
457 end
458 end
459 end
460 end
461 if trace_details then
462 report("%s unique references, %s reused entries",nofunique,nofreused)
463 end
464 initialize = nil
465 end
466
467 usedentries = setmetatableindex(function(_,k) if initialize then initialize() end return usedentries[k] end)
468 citetolist = setmetatableindex(function(_,k) if initialize then initialize() end return citetolist [k] end)
469 listtocite = setmetatableindex(function(_,k) if initialize then initialize() end return listtocite [k] end)
470 listtolist = setmetatableindex(function(_,k) if initialize then initialize() end return listtolist [k] end)
471
472 function publications.usedentries()
473 if initialize then
474 initialize()
475 end
476 return usedentries
477 end
478
479end
480
481
482
483
484
485
486
487
488
489
490local findallused do
491
492 local reported = { }
493
494
495 findallused = function(dataset,reference,internal,forcethem)
496 local current = datasets[dataset]
497 local finder = publications.finder
498 local find = finder and finder(current,reference)
499 local tags = not find and settings_to_array(reference)
500 local todo = { }
501 local okay = { }
502 local allused = usedentries[dataset] or { }
503
504 local luadata = current.luadata
505 local details = current.details
506 local ordered = current.ordered
507 if allused then
508 local registered = { }
509 local function register(tag)
510 local entry = forcethem and luadata[tag]
511 if entry then
512 if registered[tag] then
513 if trace_cite then
514 report_cite("dataset: %s, tag: %s, state: %s",dataset,tag,"already cited (1)")
515 end
516 return
517 elseif trace_cite then
518 report_cite("dataset: %s, tag: %s, state: %s",dataset,tag,"okay")
519 end
520 okay[#okay+1] = entry
521
522 registered[tag] = true
523 return tag
524 end
525 entry = allused[tag]
526 if trace_cite then
527 report_cite("dataset: %s, tag: %s, used: % t",dataset,tag,table.sortedkeys(allused))
528 end
529 if not entry then
530 local parent = details[tag].parent
531 if parent then
532 entry = allused[parent]
533 end
534 if entry then
535 report("using reference of parent %a for %a",parent,tag)
536 tag = parent
537 elseif trace_cite then
538 report_cite("dataset: %s, tag: %s, state: %s",dataset,tag,"not used")
539 end
540 elseif trace_cite then
541 report_cite("dataset: %s, tag: %s, state: %s",dataset,tag,"used")
542 end
543 if registered[tag] then
544 if trace_cite then
545 report_cite("dataset: %s, tag: %s, state: %s",dataset,tag,"already cited (2)")
546 end
547 return
548 end
549 if entry then
550
551
552 if #entry == 1 then
553 entry = entry[1]
554 else
555
556 local done = false
557 if internal and internal > 0 then
558
559 for i=1,#entry do
560 local e = entry[i]
561 if e.references.internal > internal then
562 done = e
563 break
564 end
565 end
566 if not done then
567
568 for i=1,#entry do
569 local e = entry[i]
570 if e.references.internal < internal then
571 done = e
572 else
573 break
574 end
575 end
576 end
577 end
578 if done then
579 entry = done
580 else
581 entry = entry[1]
582 end
583 end
584 if not entry then
585 report_cite("dataset: %s, tag: %s, state: %s",dataset,tag,"no entry (1)")
586 elseif trace_cite then
587 report_cite("dataset: %s, tag: %s, state: %s",dataset,tag,"new entry")
588 end
589 okay[#okay+1] = entry
590 elseif not entry then
591 if trace_cite then
592 report_cite("dataset: %s, tag: %s, state: %s",dataset,tag,"no entry (2)")
593 end
594
595 end
596 todo[tag] = true
597 registered[tag] = true
598 return tag
599 end
600 if reference == "*" then
601 tags = { }
602 for i=1,#ordered do
603 local tag = ordered[i].tag
604 tag = register(tag)
605 tags[#tags+1] = tag
606 end
607 elseif find then
608 tags = { }
609 for i=1,#ordered do
610 local entry = ordered[i]
611 if find(entry) then
612 local tag = entry.tag
613 tag = register(tag)
614 tags[#tags+1] = tag
615 end
616 end
617 if #tags == 0 and not reported[reference] then
618 tags[1] = reference
619 reported[reference] = true
620 end
621 else
622 for i=1,#tags do
623 local tag = tags[i]
624 if luadata[tag] then
625 tag = register(tag)
626 tags[i] = tag
627 elseif not reported[tag] then
628 reported[tag] = true
629 report_cite("non-existent entry %a in %a",tag,dataset)
630 end
631 end
632 end
633 elseif find then
634 tags = { }
635 for i=1,#ordered do
636 local entry = ordered[i]
637 if find(entry) then
638 local tag = entry.tag
639 local parent = details[tag].parent
640 if parent then
641 tag = parent
642 end
643 tags[#tags+1] = tag
644 todo[tag] = true
645
646 end
647 end
648 if #tags == 0 and not reported[reference] then
649 tags[1] = reference
650 reported[reference] = true
651 end
652 else
653 for i=1,#tags do
654 local tag = tags[i]
655 local parent = details[tag].parent
656 if parent then
657 tag = parent
658 tags[i] = tag
659 end
660 local entry = luadata[tag]
661 if entry then
662 todo[tag] = true
663
664 elseif not reported[tag] then
665 reported[tag] = true
666 report_cite("non-existent entry %a in %a",tag,dataset)
667 end
668 end
669 end
670 return okay, todo, tags
671 end
672
673 local firstoftwoarguments = context.firstoftwoarguments
674 local secondoftwoarguments = context.secondoftwoarguments
675
676 implement {
677 name = "btxdoifelsematches",
678 arguments = "3 strings",
679 actions = function(dataset,tag,expression)
680 local find = publications.finder(dataset,expression)
681 local okay = false
682 if find then
683 local d = datasets[dataset]
684 if d then
685 local e = d.luadata[tag]
686 if e and find(e) then
687 firstoftwoarguments()
688 return
689 end
690 end
691 end
692 secondoftwoarguments()
693 end
694 }
695
696end
697
698local function unknowncite(reference)
699 ctx_btxsettag(reference)
700 if trace_details then
701 report("expanding %a cite setup %a","unknown","unknown")
702 end
703 ctx_btxcitesetup("unknown")
704end
705
706local concatstate = publications.concatstate
707
708
709
710
711
712
713local marked_todo = false
714local marked_dataset = false
715local marked_list = false
716local marked_method = false
717
718local function marknocite(dataset,tag,nofcitations,setup)
719 ctx_btxstartcite()
720 ctx_btxsetdataset(dataset)
721 ctx_btxsettag(tag)
722 ctx_btxsetbacklink(nofcitations)
723 if trace_details then
724 report("expanding cite setup %a",setup)
725 end
726 ctx_btxcitesetup(setup)
727 ctx_btxstopcite()
728end
729
730local function markcite(dataset,tag,flush)
731 if not marked_todo then
732 return 0
733 end
734 local citation = marked_todo[tag]
735 if not citation then
736 return 0
737 end
738 if citation == true then
739 nofcitations = nofcitations + 1
740 if trace_cite then
741 report_cite("mark, dataset: %s, tag: %s, number: %s, state: %s",dataset,tag,nofcitations,"cited")
742 end
743 if flush then
744 marknocite(dataset,tag,nofcitations,"nocite")
745 end
746 marked_todo[tag] = nofcitations
747 return nofcitations
748 else
749 return citation
750 end
751end
752
753local function btxflushmarked()
754 if marked_list and marked_todo then
755 for i=1,#marked_list do
756
757 local tag = marked_list[i]
758 local tbm = marked_todo[tag]
759 if tbm == true or not tbm then
760 nofcitations = nofcitations + 1
761 local setup = (tbm or marked_method == v_always) and "nocite" or "invalid"
762 marknocite(marked_dataset,tag,nofcitations,setup)
763 if trace_cite then
764 report_cite("mark, dataset: %s, tag: %s, number: %s, setup: %s",marked_dataset,tag,nofcitations,setup)
765 end
766 else
767
768 end
769 end
770 end
771 marked_todo = false
772 marked_dataset = false
773 marked_list = false
774 marked_method = false
775end
776
777implement { name = "btxflushmarked", actions = btxflushmarked }
778
779
780
781local function getfield(dataset,tag,name)
782 local d = datasets[dataset].luadata[tag]
783 return d and d[name]
784end
785
786local function getdetail(dataset,tag,name)
787 local d = datasets[dataset].details[tag]
788 return d and d[name]
789end
790
791local function getcasted(dataset,tag,field,specification)
792 local current = datasets[dataset]
793 if current then
794 local data = current.luadata[tag]
795 if data then
796 local category = data.category
797 if not specification then
798 specification = currentspecification
799 end
800 local catspec = specification.categories[category]
801 if not catspec then
802 return false
803 end
804 local fields = catspec.fields
805 if fields then
806 local sets = catspec.sets
807 if sets then
808 local set = sets[field]
809 if set then
810 for i=1,#set do
811 local field = set[i]
812 local value = fields[field] and data[field]
813 if value then
814 local kind = specification.types[field]
815 return detailed[kind][value], field, kind
816 end
817 end
818 end
819 end
820 local value = fields[field] and data[field]
821 if value then
822 local kind = specification.types[field]
823 return detailed[kind][value], field, kind
824 end
825 end
826 local data = current.details[tag]
827 if data then
828 local kind = specification.types[field]
829 return data[field], field, kind
830 end
831 end
832 end
833end
834
835local function getfaster(current,data,details,field,categories,types)
836 local category = data.category
837 local catspec = categories[category]
838 if not catspec then
839 return false
840 end
841 local fields = catspec.fields
842 if fields then
843 local sets = catspec.sets
844 if sets then
845 local set = sets[field]
846 if set then
847 for i=1,#set do
848 local field = set[i]
849 local value = fields[field] and data[field]
850 if value then
851 local kind = types[field]
852 return detailed[kind][value], field, kind
853 end
854 end
855 end
856 end
857 local value = fields[field] and data[field]
858 if value then
859 local kind = types[field]
860 return detailed[kind][value]
861 end
862 end
863 if details then
864 local kind = types[field]
865 return details[field]
866 end
867end
868
869local function getdirect(dataset,data,field,catspec)
870 local catspec = (catspec or currentspecification).categories[data.category]
871 if not catspec then
872 return false
873 end
874 local fields = catspec.fields
875 if fields then
876 local sets = catspec.sets
877 if sets then
878 local set = sets[field]
879 if set then
880 for i=1,#set do
881 local field = set[i]
882 local value = fields[field] and data[field]
883 if value then
884 return value
885 end
886 end
887 end
888 end
889 return fields[field] and data[field] or nil
890 end
891end
892
893local function getfuzzy(data,field,categories)
894 local catspec
895 if categories then
896 local category = data.category
897 if category then
898 catspec = categories[data.category]
899 end
900 end
901 if not field then
902 return
903 elseif not catspec then
904 return data[field]
905 end
906 local fields = catspec.fields
907 if fields then
908 local sets = catspec.sets
909 if sets then
910 local set = sets[field]
911 if set then
912 for i=1,#set do
913 local field = set[i]
914 local value = fields[field] and data[field]
915 if value then
916 return value
917 end
918 end
919 end
920 end
921 return fields[field] and data[field] or nil
922 end
923end
924
925publications.getfield = getfield
926publications.getdetail = getdetail
927publications.getcasted = getcasted
928publications.getfaster = getfaster
929publications.getdirect = getdirect
930publications.getfuzzy = getfuzzy
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969local inspectors = allocate()
970local nofmultiple = allocate()
971local firstandlast = allocate()
972
973publications.inspectors = inspectors
974inspectors.nofmultiple = nofmultiple
975inspectors.firstandlast = firstandlast
976
977function nofmultiple.author(d)
978 return type(d) == "table" and #d or 0
979end
980
981function publications.singularorplural(dataset,tag,name)
982 local data, field, kind = getcasted(dataset,tag,name)
983 if data then
984 local test = nofmultiple[kind]
985 if test then
986 local n = test(data)
987 return not n or n < 2
988 end
989 end
990 return true
991end
992
993function firstandlast.range(d)
994 if type(d) == "table" then
995 return d[1], d[2]
996 end
997end
998
999firstandlast.pagenumber = firstandlast.range
1000
1001function publications.oneorrange(dataset,tag,name)
1002 local data, field, kind = getcasted(dataset,tag,name)
1003 if data then
1004 local test = firstandlast[kind]
1005 if test then
1006 local first, last = test(data)
1007 return not (first and last)
1008 end
1009 end
1010 return nil
1011end
1012
1013function publications.firstofrange(dataset,tag,name)
1014 local data, field, kind = getcasted(dataset,tag,name)
1015 if data then
1016 local test = firstandlast[kind]
1017 if test then
1018 local first = test(data)
1019 if first then
1020 return first
1021 end
1022 end
1023 end
1024end
1025
1026function publications.lastofrange(dataset,tag,name)
1027 local data, field, kind = getcasted(dataset,tag,name)
1028 if data then
1029 local test = firstandlast[kind]
1030 if test then
1031 local first, last = test(data)
1032 if last then
1033 return last
1034 end
1035 end
1036 end
1037end
1038
1039implement {
1040 name = "btxsingularorplural",
1041 actions = { publications.singularorplural, ctx_doifelse },
1042 arguments = "3 strings"
1043}
1044
1045implement {
1046 name = "btxoneorrange",
1047 actions = { publications.oneorrange, function(b) if b == nil then ctx_gobbletwoarguments() else ctx_doifelse(b) end end },
1048 arguments = "3 strings"
1049}
1050
1051implement {
1052 name = "btxfirstofrange",
1053 actions = { publications.firstofrange, context },
1054 arguments = "3 strings"
1055}
1056
1057implement {
1058 name = "btxlastofrange",
1059 actions = { publications.lastofrange, context },
1060 arguments = "3 strings"
1061}
1062
1063
1064
1065function publications.usedataset(specification)
1066 specification.kind = "current"
1067 publications.load(specification)
1068end
1069
1070implement {
1071 name = "btxusedataset",
1072 actions = publications.usedataset,
1073 arguments = {
1074 {
1075 { "specification" },
1076 { "dataset" },
1077 { "filename" },
1078 }
1079 }
1080}
1081
1082implement {
1083 name = "convertbtxdatasettoxml",
1084 arguments = { "string", true },
1085 actions = publications.converttoxml
1086}
1087
1088
1089
1090do
1091
1092
1093
1094 local function shortsorter(a,b)
1095 local ay = a[2]
1096 local by = b[2]
1097 if ay ~= by then
1098 return ay < by
1099 end
1100 local ay = a[3]
1101 local by = b[3]
1102 if ay ~= by then
1103
1104 local an = tonumber(ay)
1105 local bn = tonumber(by)
1106 if an and bn then
1107 return an < bn
1108 else
1109 return ay < by
1110 end
1111 end
1112 return a[4] < b[4]
1113 end
1114
1115
1116
1117
1118
1119
1120 local f_short = formatters["%s%02i"]
1121
1122 function publications.enhancers.suffixes(dataset)
1123 if not dataset then
1124 return
1125 else
1126 report("analyzing previous publication run for %a",dataset.name)
1127 end
1128 dataset.suffixed = true
1129
1130 local used = usedentries[dataset.name]
1131 if not used then
1132 return
1133 end
1134 local luadata = dataset.luadata
1135 local details = dataset.details
1136 local ordered = dataset.ordered
1137 if not luadata or not details or not ordered then
1138 report("nothing to be analyzed in %a",dataset.name)
1139 return
1140 end
1141
1142 local kind = dataset.authorconversion or "name"
1143 local fields = { "author", "editor" }
1144 local shorts = { }
1145 local authors = { }
1146 local hasher = publications.authorhashers[kind]
1147 local shorter = publications.authorhashers.short
1148 for i=1,#ordered do
1149 local entry = ordered[i]
1150 if entry then
1151 local tag = entry.tag
1152 if tag then
1153 local use = used[tag]
1154 if use then
1155
1156
1157 local listentry = use[1]
1158 local userdata = listentry.userdata
1159 local btxspc = userdata and userdata.btxspc
1160 if btxspc then
1161
1162
1163 local done = false
1164 for i=1,#fields do
1165 local field = fields[i]
1166 local author = getcasted(dataset,tag,field,specifications[btxspc])
1167 local kind = type(author)
1168 if kind == "table" or kind == "string" then
1169 if u then
1170 u = listentry.entries.text
1171 else
1172 u = "0"
1173 end
1174 local year = tonumber(entry.year) or 9999
1175 local data = { tag, year, u, i }
1176
1177 local hash = hasher(author)
1178 local found = authors[hash]
1179 if not found then
1180 authors[hash] = { data }
1181 else
1182 found[#found+1] = data
1183 end
1184
1185 local hash = shorter(author)
1186 local short = f_short(hash,mod(year,100))
1187 local found = shorts[short]
1188 if not found then
1189 shorts[short] = { data }
1190 else
1191 found[#found+1] = data
1192 end
1193 done = true
1194 break
1195 end
1196 end
1197 if not done then
1198 report("unable to create short for %a, needs one of [%,t]",tag,fields)
1199 end
1200 else
1201
1202 end
1203 end
1204 end
1205 end
1206 end
1207 local function addsuffix(hashed,key,suffixkey)
1208 for hash, tags in sortedhash(hashed) do
1209 local n = #tags
1210 if n == 0 then
1211
1212 elseif n == 1 then
1213 local tagdata = tags[1]
1214 local tag = tagdata[1]
1215 local detail = details[tag]
1216 local entry = luadata[tag]
1217 local year = entry.year
1218 detail[key] = hash
1219 elseif n > 1 then
1220 sort(tags,shortsorter)
1221 local lastyear = nil
1222 local suffix = nil
1223 local previous = nil
1224 for i=1,n do
1225 local tagdata = tags[i]
1226 local tag = tagdata[1]
1227 local detail = details[tag]
1228 local entry = luadata[tag]
1229 local year = entry.year
1230 detail[key] = hash
1231 if not year or year ~= lastyear then
1232 lastyear = year
1233 suffix = 1
1234 else
1235 if previous and suffix == 1 then
1236 previous[suffixkey] = suffix
1237 end
1238 suffix = suffix + 1
1239 detail[suffixkey] = suffix
1240 end
1241 previous = detail
1242 end
1243 end
1244 if trace_suffixes then
1245 for i=1,n do
1246 local tag = tags[i][1]
1247 local year = luadata[tag].year
1248 local suffix = details[tag].suffix
1249 if suffix then
1250 report_suffix("%s: tag %a, hash %a, year %a, suffix %a",key,tag,hash,year or '',suffix or '')
1251 else
1252 report_suffix("%s: tag %a, hash %a, year %a",key,tag,hash,year or '')
1253 end
1254 end
1255 end
1256 end
1257 end
1258 addsuffix(shorts, "shorthash", "shortsuffix")
1259 addsuffix(authors,"authorhash","authorsuffix")
1260 end
1261
1262
1263
1264end
1265
1266implement {
1267 name = "btxaddentry",
1268 arguments = "3 strings",
1269 actions = function(name,settings,content)
1270 local dataset = datasets[name]
1271 if dataset then
1272 publications.addtexentry(dataset,settings,content)
1273 end
1274 end,
1275}
1276
1277function publications.checkeddataset(name,default)
1278 local dataset = rawget(datasets,name)
1279 if dataset then
1280 return name
1281 elseif default and default ~= "" then
1282 return default
1283 else
1284 report("unknown dataset %a, forcing %a",name,v_default)
1285 return v_default
1286 end
1287end
1288
1289implement {
1290 name = "btxsetdataset",
1291 arguments = "2 strings",
1292 actions = { publications.checkeddataset, context },
1293}
1294
1295implement {
1296 name = "btxsetentry",
1297 arguments = "2 strings",
1298 actions = function(name,tag)
1299 local dataset = rawget(datasets,name)
1300 if dataset then
1301 if dataset.luadata[tag] then
1302 context(tag)
1303 else
1304 report("unknown tag %a in dataset %a",tag,name)
1305 end
1306 else
1307 report("unknown dataset %a",name)
1308 end
1309 end,
1310}
1311
1312
1313
1314do
1315
1316 local typesetters = { }
1317 publications.typesetters = typesetters
1318
1319 local function defaulttypesetter(field,value,manipulator)
1320 if value and value ~= "" then
1321 value = tostring(value)
1322 context(manipulator and applymanipulation(manipulator,value) or value)
1323 end
1324 end
1325
1326 setmetatableindex(typesetters,function(t,k)
1327 local v = defaulttypesetter
1328 t[k] = v
1329 return v
1330 end)
1331
1332 function typesetters.string(field,value,manipulator)
1333 if value and value ~= "" then
1334 context(manipulator and applymanipulation(manipulator,value) or value)
1335 end
1336 end
1337
1338 function typesetters.author(field,value,manipulator)
1339 ctx_btxflushauthor(field)
1340 end
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355 local splitter = lpeg.splitat(":")
1356
1357 local function permitted(category,field)
1358 local catspec = currentspecification.categories[category]
1359 if not catspec then
1360 report("invalid category %a, %s",category,"no specification")
1361 return false
1362 end
1363 local fields = catspec.fields
1364 if not fields then
1365 report("invalid category %a, %s",category,"no fields")
1366 return false
1367 end
1368 if ignoredfields and ignoredfields[field] then
1369 return false
1370 end
1371 local virtualfields = currentspecification.virtualfields
1372 if virtualfields and virtualfields[field] then
1373 return true
1374 end
1375 local sets = catspec.sets
1376 if sets then
1377 local set = sets[field]
1378 if set then
1379 return set
1380 end
1381 end
1382 if fields[field] then
1383 return true
1384 end
1385 local f, l = lpegmatch(splitter,field)
1386 if f and l and fields[f] then
1387 return true
1388 end
1389 end
1390
1391 local function found(dataset,tag,field,valid,fields)
1392 if valid == true then
1393
1394 local okay = fields[field]
1395 if okay then
1396 return field, okay
1397 end
1398 local details = dataset.details[tag]
1399 local value = details[field]
1400 if value then
1401 return field, value
1402 end
1403 elseif valid then
1404
1405 for i=1,#valid do
1406 local field = valid[i]
1407 local value = fields[field]
1408 if value then
1409 return field, value
1410 end
1411 end
1412 local details = dataset.details[tag]
1413 for i=1,#valid do
1414 local value = details[field]
1415 if value then
1416 return field, value
1417 end
1418 end
1419 end
1420 end
1421
1422 local function get(dataset,tag,field,what,check,catspec)
1423 local current = rawget(datasets,dataset)
1424 if current then
1425 local data = current.luadata[tag]
1426 if data then
1427 local category = data.category
1428 local catspec = (catspec or currentspecification).categories[category]
1429 if not catspec then
1430 return false
1431 end
1432 local fields = catspec.fields
1433 if fields then
1434 local sets = catspec.sets
1435 if sets then
1436 local set = sets[field]
1437 if set then
1438 if check then
1439 for i=1,#set do
1440 local field = set[i]
1441 local kind = (not check or data[field]) and fields[field]
1442 if kind then
1443 return what and kind or field
1444 end
1445 end
1446 elseif what then
1447 local t = { }
1448 for i=1,#set do
1449 t[i] = fields[set[i]] or "unknown"
1450 end
1451 return concat(t,",")
1452 else
1453 return concat(set,",")
1454 end
1455 end
1456 end
1457 local kind = (not check or data[field]) and fields[field]
1458 if kind then
1459 return what and kind or field
1460 end
1461 end
1462 end
1463 end
1464 return ""
1465 end
1466
1467 publications.permitted = permitted
1468 publications.found = found
1469 publications.get = get
1470
1471 local function btxflush(name,tag,field)
1472 local dataset = rawget(datasets,name)
1473 if dataset then
1474 local fields = dataset.luadata[tag]
1475 if fields then
1476 local manipulator, field = splitmanipulation(field)
1477 local category = fields.category
1478 local valid = permitted(category,field)
1479 if valid then
1480 local name, value = found(dataset,tag,field,valid,fields)
1481 if value then
1482 typesetters[currentspecification.types[name]](field,value,manipulator)
1483 elseif trace_details then
1484 report("%s %s %a in category %a for tag %a in dataset %a","unknown","entry",field,category,tag,name)
1485 end
1486 elseif trace_details then
1487 report("%s %s %a in category %a for tag %a in dataset %a","invalid","entry",field,category,tag,name)
1488 end
1489 else
1490 report("unknown tag %a in dataset %a",tag,name)
1491 end
1492 else
1493 report("unknown dataset %a",name)
1494 end
1495 end
1496
1497 local function btxfield(name,tag,field)
1498 local dataset = rawget(datasets,name)
1499 if dataset then
1500 local fields = dataset.luadata[tag]
1501 if fields then
1502 local category = fields.category
1503 local manipulator, field = splitmanipulation(field)
1504 if permitted(category,field) then
1505 local value = fields[field]
1506 if value then
1507 typesetters[currentspecification.types[field]](field,value,manipulator)
1508 elseif trace_details then
1509 report("%s %s %a in category %a for tag %a in dataset %a","unknown","field",field,category,tag,name)
1510 end
1511 elseif trace_details then
1512 report("%s %s %a in category %a for tag %a in dataset %a","invalid","field",field,category,tag,name)
1513 end
1514 else
1515 report("unknown tag %a in dataset %a",tag,name)
1516 end
1517 else
1518 report("unknown dataset %a",name)
1519 end
1520 end
1521
1522 local function btxdetail(name,tag,field)
1523 local dataset = rawget(datasets,name)
1524 if dataset then
1525 local fields = dataset.luadata[tag]
1526 if fields then
1527 local details = dataset.details[tag]
1528 if details then
1529 local category = fields.category
1530 local manipulator, field = splitmanipulation(field)
1531 if permitted(category,field) then
1532 local value = details[field]
1533 if value then
1534 typesetters[currentspecification.types[field]](field,value,manipulator)
1535 elseif trace_details then
1536 report("%s %s %a in category %a for tag %a in dataset %a","unknown","detail",field,category,tag,name)
1537 end
1538 elseif trace_details then
1539 report("%s %s %a in category %a for tag %a in dataset %a","invalid","detail",field,category,tag,name)
1540 end
1541 else
1542 report("no details for tag %a in dataset %a",tag,name)
1543 end
1544 else
1545 report("unknown tag %a in dataset %a",tag,name)
1546 end
1547 else
1548 report("unknown dataset %a",name)
1549 end
1550 end
1551
1552 local function btxdirect(name,tag,field)
1553 local dataset = rawget(datasets,name)
1554 if dataset then
1555 local fields = dataset.luadata[tag]
1556 if fields then
1557 local manipulator, field = splitmanipulation(field)
1558 local value = fields[field]
1559 if value then
1560 context(typesetters.default(field,value,manipulator))
1561 elseif trace_details then
1562 report("field %a of tag %a in dataset %a has no value",field,tag,name)
1563 end
1564 else
1565 report("unknown tag %a in dataset %a",tag,name)
1566 end
1567 else
1568 report("unknown dataset %a",name)
1569 end
1570 end
1571
1572 local function okay(name,tag,field)
1573 local dataset = rawget(datasets,name)
1574 if dataset then
1575 local fields = dataset.luadata[tag]
1576 if fields then
1577 local category = fields.category
1578 local valid = permitted(category,field)
1579 if valid then
1580 local value, field = found(dataset,tag,field,valid,fields)
1581 return value and value ~= ""
1582 end
1583 end
1584 end
1585 end
1586
1587 publications.okay = okay
1588
1589 implement { name = "btxfield", actions = btxfield, arguments = "3 strings" }
1590 implement { name = "btxdetail", actions = btxdetail, arguments = "3 strings" }
1591 implement { name = "btxflush", actions = btxflush, arguments = "3 strings" }
1592 implement { name = "btxdirect", actions = btxdirect, arguments = "3 strings" }
1593
1594 implement { name = "btxfieldname", actions = { get, context }, arguments = { "string", "string", "string", false, false } }
1595 implement { name = "btxfieldtype", actions = { get, context }, arguments = { "string", "string", "string", true, false } }
1596 implement { name = "btxfoundname", actions = { get, context }, arguments = { "string", "string", "string", false, true } }
1597 implement { name = "btxfoundtype", actions = { get, context }, arguments = { "string", "string", "string", true, true } }
1598
1599 implement { name = "btxdoifelse", actions = { okay, ctx_doifelse }, arguments = "3 strings" }
1600 implement { name = "btxdoif", actions = { okay, ctx_doif }, arguments = "3 strings" }
1601 implement { name = "btxdoifnot", actions = { okay, ctx_doifnot }, arguments = "3 strings" }
1602
1603end
1604
1605
1606
1607function publications.singularorplural(singular,plural)
1608 if lastconcatsize and lastconcatsize > 1 then
1609 context(plural)
1610 else
1611 context(singular)
1612 end
1613end
1614
1615
1616
1617do
1618
1619 local patterns = {
1620 CONTEXTLMTXMODE > 0 and "symb-imp-%s.mklx" or "",
1621 CONTEXTLMTXMODE > 0 and "symb-imp-%s.mkxl" or "",
1622 "publ-imp-%s.mkvi",
1623 "publ-imp-%s.mkiv",
1624 "publ-imp-%s.tex",
1625 }
1626
1627 local function failure(name)
1628 report("unknown library %a",name)
1629 end
1630
1631 local function action(name,foundname)
1632 context.loadfoundpublicationfile(name,foundname)
1633 end
1634
1635 function publications.loaddefinitionfile(name)
1636 resolvers.uselibrary {
1637 name = gsub(name,"^publ%-",""),
1638 patterns = patterns,
1639 action = action,
1640 failure = failure,
1641 onlyonce = true,
1642 }
1643 end
1644
1645 local patterns = {
1646 "publ-imp-%s.lua",
1647 }
1648
1649 function publications.loadreplacementfile(name)
1650 resolvers.uselibrary {
1651 name = gsub(name,"^publ%-",""),
1652 patterns = patterns,
1653 action = publications.loaders.registercleaner,
1654 failure = failure,
1655 onlyonce = true,
1656 }
1657 end
1658
1659 implement { name = "btxloaddefinitionfile", actions = publications.loaddefinitionfile, arguments = "string" }
1660 implement { name = "btxloadreplacementfile", actions = publications.loadreplacementfile, arguments = "string" }
1661
1662end
1663
1664
1665
1666local renderings = { }
1667
1668do
1669
1670 publications.lists = publications.lists or { }
1671 local lists = publications.lists
1672
1673 local context = context
1674 local structures = structures
1675
1676 local references = structures.references
1677 local sections = structures.sections
1678
1679
1680
1681 setmetatableindex(renderings,function(t,k)
1682 local v = {
1683 list = { },
1684 done = { },
1685 alldone = { },
1686 used = { },
1687 registered = { },
1688 ordered = { },
1689 shorts = { },
1690 method = v_none,
1691 texts = setmetatableindex("table"),
1692 currentindex = 0,
1693 }
1694 t[k] = v
1695 return v
1696 end)
1697
1698
1699
1700 function lists.register(dataset,tag,short)
1701 local r = renderings[dataset]
1702 if not short or short == "" then
1703 short = tag
1704 end
1705 if trace then
1706 report("registering publication entry %a with shortcut %a",tag,short)
1707 end
1708 local top = #r.registered + 1
1709
1710 r.registered[top] = tag
1711 r.ordered [tag] = top
1712 r.shorts [tag] = short
1713 end
1714
1715 function lists.nofregistered(dataset)
1716 return #renderings[dataset].registered
1717 end
1718
1719 local function validkeyword(dataset,tag,keyword,specification)
1720 local kw = getcasted(dataset,tag,"keywords",specification)
1721 if kw then
1722 for i=1,#kw do
1723 if keyword[kw[i]] then
1724 return true
1725 end
1726 end
1727 end
1728 end
1729
1730 local function registerpage(pages,tag,result,listindex)
1731 local p = pages[tag]
1732 local r = result[listindex].references
1733 if p then
1734 local last = p[#p][2]
1735 local real = last.realpage
1736 if real ~= r.realpage then
1737 p[#p+1] = { listindex, r }
1738 end
1739 else
1740 pages[tag] = { { listindex, r } }
1741 end
1742 end
1743
1744
1745
1746 local methods = { }
1747 lists.methods = methods
1748
1749 methods[v_dataset] = function(dataset,rendering,keyword)
1750 local current = datasets[dataset]
1751 local luadata = current.luadata
1752 local list = rendering.list
1753 for tag, data in sortedhash(luadata) do
1754 if not keyword or validkeyword(dataset,tag,keyword) then
1755 local index = data.index or 0
1756 list[#list+1] = { tag, index, 0, false, index }
1757 end
1758 end
1759 end
1760
1761
1762
1763 methods[v_force] = function (dataset,rendering,keyword)
1764
1765
1766 local result = structures.lists.filter(rendering.specifications) or { }
1767 local list = rendering.list
1768 local current = datasets[dataset]
1769 local luadata = current.luadata
1770 for listindex=1,#result do
1771 local r = result[listindex]
1772 local u = r.userdata
1773 if u then
1774 local set = u.btxset or v_default
1775 if set == dataset then
1776 local tag = u.btxref
1777 if tag and (not keyword or validkeyword(dataset,tag,keyword)) then
1778 local data = luadata[tag]
1779 list[#list+1] = { tag, listindex, 0, u, data and data.index or 0 }
1780 end
1781 end
1782 end
1783 end
1784 lists.result = result
1785 end
1786
1787
1788
1789
1790 methods[v_local] = function(dataset,rendering,keyword)
1791 local result = structures.lists.filter(rendering.specifications) or { }
1792 local section = sections.currentid()
1793 local list = rendering.list
1794 local repeated = rendering.repeated == v_yes
1795 local r_done = rendering.done
1796 local r_alldone = rendering.alldone
1797 local done = repeated and { } or r_done
1798 local alldone = repeated and { } or r_alldone
1799 local doglobal = rendering.method == v_global
1800 local traced = { }
1801 local pages = { }
1802 local current = datasets[dataset]
1803 local luadata = current.luadata
1804
1805 rendering.result = result
1806
1807 for listindex=1,#result do
1808 local r = result[listindex]
1809 local u = r.userdata
1810 if u then
1811 local set = u.btxset or v_default
1812 if set == dataset then
1813
1814 local tag = u.btxref
1815 if not tag then
1816
1817 elseif done[tag] == section then
1818
1819 elseif doglobal and alldone[tag] then
1820
1821 elseif not keyword or validkeyword(dataset,tag,keyword) then
1822 if traced then
1823 local l = traced[tag]
1824 if l then
1825 l[#l+1] = u.btxint
1826 else
1827 local data = luadata[tag]
1828 local l = { tag, listindex, 0, u, data and data.index or 0 }
1829 list[#list+1] = l
1830 traced[tag] = l
1831 end
1832 else
1833 done[tag] = section
1834 alldone[tag] = true
1835 local data = luadata[tag]
1836 list[#list+1] = { tag, listindex, 0, u, data and data.index or 0 }
1837 end
1838 end
1839 if tag then
1840 registerpage(pages,tag,result,listindex)
1841 end
1842 end
1843 end
1844 end
1845 if traced then
1846 for tag in next, traced do
1847 done[tag] = section
1848 alldone[tag] = true
1849 end
1850 end
1851 lists.result = result
1852 structures.lists.result = result
1853 rendering.pages = pages
1854 end
1855
1856 methods[v_global] = methods[v_local]
1857
1858 function lists.collectentries(specification)
1859 local dataset = specification.dataset
1860 if not dataset then
1861 return
1862 end
1863 local rendering = renderings[dataset]
1864 if not rendering then
1865 return
1866 end
1867 local method = specification.method or v_none
1868 local ignored = specification.ignored or ""
1869 local filter = specification.filter or ""
1870 rendering.method = method
1871 rendering.ignored = ignored ~= "" and settings_to_set(ignored) or nil
1872 rendering.list = { }
1873 rendering.done = { }
1874 rendering.sorttype = specification.sorttype or v_default
1875 rendering.criterium = specification.criterium or v_none
1876 rendering.repeated = specification.repeated or v_no
1877 rendering.group = specification.group or ""
1878 rendering.specifications = specification
1879 local filtermethod = methods[method]
1880 if not filtermethod then
1881 report_list("invalid method %a",method or "")
1882 return
1883 end
1884 report_list("collecting entries using method %a and sort order %a",method,rendering.sorttype)
1885 lists.result = { }
1886 local keyword = specification.keyword
1887 if keyword and keyword ~= "" then
1888 keyword = settings_to_set(keyword)
1889 else
1890 keyword = nil
1891 end
1892 filtermethod(dataset,rendering,keyword)
1893 local list = rendering.list
1894 if list and filter ~= "" then
1895 local find = publications.finder(dataset,filter)
1896 if find then
1897 local luadata = datasets[dataset].luadata
1898 local matched = 0
1899 for i=1,#list do
1900 local found = list[i]
1901 local entry = luadata[found[1]]
1902 if find(entry) then
1903 matched = matched + 1
1904 list[matched] = found
1905 end
1906 end
1907 for i=#list,matched + 1,-1 do
1908 list[i] = nil
1909 end
1910 end
1911 end
1912 ctx_btxsetnoflistentries(list and #list or 0)
1913 end
1914
1915
1916
1917 local groups = setmetatableindex("number")
1918
1919 function lists.prepareentries(dataset)
1920 local rendering = renderings[dataset]
1921 local list = rendering.list
1922 local used = rendering.used
1923 local forceall = rendering.criterium == v_all
1924 local repeated = rendering.repeated == v_yes
1925 local sorttype = rendering.sorttype or v_default
1926 local group = rendering.group or ""
1927 local sorter = lists.sorters[sorttype]
1928 local current = datasets[dataset]
1929 local luadata = current.luadata
1930 local details = current.details
1931 local newlist = { }
1932 local lastreferencenumber = groups[group]
1933 for i=1,#list do
1934 local li = list[i]
1935 local tag = li[1]
1936 local entry = luadata[tag]
1937 if entry then
1938 if forceall or repeated or not used[tag] then
1939 newlist[#newlist+1] = li
1940
1941 if not repeated then
1942 used[tag] = true
1943 end
1944 end
1945 end
1946 end
1947 if type(sorter) == "function" then
1948 list = sorter(dataset,rendering,newlist,sorttype) or newlist
1949 else
1950 list = newlist
1951 end
1952 local newlist = { }
1953 local tagtolistindex = { }
1954 rendering.tagtolistindex = tagtolistindex
1955 for i=1,#list do
1956 local li = list[i]
1957 local tag = li[1]
1958 local entry = luadata[tag]
1959 if entry then
1960 local detail = details[tag]
1961 if not detail then
1962
1963 report("fatal error, missing details for tag %a in dataset %a (enhanced: %s)",tag,dataset,current.enhanced and "yes" or "no")
1964
1965
1966
1967
1968
1969 elseif detail.parent then
1970
1971 else
1972 local referencenumber = detail.referencenumber
1973 if not referencenumber then
1974 lastreferencenumber = lastreferencenumber + 1
1975 referencenumber = lastreferencenumber
1976 detail.referencenumber = lastreferencenumber
1977 end
1978 li[3] = referencenumber
1979 tagtolistindex[tag] = i
1980 newlist[#newlist+1] = li
1981 end
1982 end
1983 end
1984 groups[group] = lastreferencenumber
1985 rendering.list = newlist
1986 end
1987
1988 function lists.fetchentries(dataset)
1989 local rendering = renderings[dataset]
1990 local list = rendering.list
1991 if list then
1992 for i=1,#list do
1993 local li = list[i]
1994 ctx_btxsettag(li[1])
1995 ctx_btxsetnumber(li[3])
1996 ctx_btxchecklistentry()
1997 end
1998 end
1999 end
2000
2001
2002
2003
2004
2005 local function btxflushpages(dataset,tag)
2006
2007 local rendering = renderings[dataset]
2008 local pages = rendering.pages
2009 if not pages then
2010 return
2011 else
2012 pages = pages[tag]
2013 end
2014 if not pages then
2015 return
2016 end
2017 local nofpages = #pages
2018 if nofpages == 0 then
2019 return
2020 end
2021 local first_p = nil
2022 local first_r = nil
2023 local last_p = nil
2024 local last_r = nil
2025 local ranges = { }
2026 local nofdone = 0
2027 local function flush()
2028 if last_r and first_r ~= last_r then
2029 ranges[#ranges+1] = { first_p, last_p }
2030 else
2031 ranges[#ranges+1] = { first_p }
2032 end
2033 end
2034 for i=1,nofpages do
2035 local next_p = pages[i]
2036 local next_r = next_p[2].realpage
2037 if not first_r then
2038 first_p = next_p
2039 first_r = next_r
2040 elseif last_r + 1 == next_r then
2041
2042 elseif first_r then
2043 flush()
2044 first_p = next_p
2045 first_r = next_r
2046 end
2047 last_p = next_p
2048 last_r = next_r
2049 end
2050 if first_r then
2051 flush()
2052 end
2053 local nofranges = #ranges
2054 for i=1,nofranges do
2055 local r = ranges[i]
2056 ctx_btxsetconcat(concatstate(i,nofranges))
2057 local first = r[1]
2058 local last = r[2]
2059 ctx_btxsetfirstinternal(first[2].internal)
2060 ctx_btxsetfirstpage(first[1])
2061 if last then
2062 ctx_btxsetlastinternal(last[2].internal)
2063 ctx_btxsetlastpage(last[1])
2064 end
2065 if trace_details then
2066 report("expanding page setup")
2067 end
2068 ctx_btxpagesetup("")
2069 end
2070 end
2071
2072 implement {
2073 name = "btxflushpages",
2074 arguments = "2 strings",
2075 actions = btxflushpages,
2076 }
2077
2078 local function identical(a,b)
2079 local na = #a
2080 local nb = #b
2081 if na ~= nb then
2082 return false
2083 end
2084 if na > 0 then
2085 for i=1,na do
2086 if not identical(a[i],b[i]) then
2087 return false
2088 end
2089 end
2090 return true
2091 end
2092 local ha = a.hash
2093 local hb = b.hash
2094 if ha then
2095 return ha == hb
2096 end
2097 for k, v in next, a do
2098 if k == "original" or k == "snippets" then
2099
2100 elseif v ~= b[k] then
2101 return false
2102 end
2103 end
2104 return true
2105 end
2106
2107 function lists.sameasprevious(dataset,i,name,order,method)
2108 local rendering = renderings[dataset]
2109 local list = rendering.list
2110 local n = tonumber(i)
2111 if n and n > 1 and n <= #list then
2112 local luadata = datasets[dataset].luadata
2113 local p_index = list[n-1][1]
2114 local c_index = list[n ][1]
2115 local previous = getdirect(dataset,luadata[p_index],name)
2116 local current = getdirect(dataset,luadata[c_index],name)
2117
2118
2119
2120
2121
2122
2123 if order and order > 0 and (method == v_always or method == v_doublesided) then
2124 local clist = listtolist[order]
2125 local plist = listtolist[order-1]
2126 if clist and plist then
2127 local crealpage = clist.references.realpage
2128 local prealpage = plist.references.realpage
2129 if crealpage ~= prealpage then
2130 if method == v_always or not conditionals.layoutisdoublesided then
2131 if trace_details then
2132 report("previous %a, current %a, different page",previous,current)
2133 end
2134 return false
2135 elseif crealpage % 2 == 0 then
2136 if trace_details then
2137 report("previous %a, current %a, different page",previous,current)
2138 end
2139 return false
2140 end
2141 end
2142 end
2143 end
2144 local sameentry = false
2145 if current and current == previous then
2146 sameentry = true
2147 else
2148 local p_casted = getcasted(dataset,p_index,name)
2149 local c_casted = getcasted(dataset,c_index,name)
2150 if c_casted and c_casted == p_casted then
2151 sameentry = true
2152 elseif type(c_casted) == "table" and type(p_casted) == "table" then
2153 sameentry = identical(c_casted,p_casted)
2154 end
2155 end
2156 if trace_details then
2157 if sameentry then
2158 report("previous %a, current %a, same entry",previous,current)
2159 else
2160 report("previous %a, current %a, different entry",previous,current)
2161 end
2162 end
2163 return sameentry
2164 else
2165 return false
2166 end
2167 end
2168
2169 function lists.combiinlist(dataset,tag)
2170 local rendering = renderings[dataset]
2171
2172 local toindex = rendering.tagtolistindex
2173 return toindex and toindex[tag]
2174 end
2175
2176 function lists.flushcombi(dataset,tag)
2177 local rendering = renderings[dataset]
2178 local toindex = rendering.tagtolistindex
2179 local listindex = toindex and toindex[tag]
2180 if listindex then
2181 local list = rendering.list
2182 local li = list[listindex]
2183 if li then
2184 local data = datasets[dataset]
2185 local luadata = data.luadata
2186 local details = data.details
2187 local tag = li[1]
2188 local listindex = li[2]
2189 local n = li[3]
2190 local entry = luadata[tag]
2191 local detail = details[tag]
2192 ctx_btxstartcombientry()
2193 ctx_btxsetcurrentlistindex(listindex)
2194 ctx_btxsetcategory(entry.category or "unknown")
2195 ctx_btxsettag(tag)
2196 ctx_btxsetnumber(n)
2197 local language = entry.language
2198 if language then
2199 ctx_btxsetlanguage(language)
2200 end
2201 local authorsuffix = detail.authorsuffix
2202 if authorsuffix then
2203 ctx_btxsetsuffix(authorsuffix)
2204 end
2205 ctx_btxhandlecombientry()
2206 ctx_btxstopcombientry()
2207 end
2208 end
2209 end
2210
2211 function lists.flushtag(dataset,i)
2212 local li = renderings[dataset].list[i]
2213 ctx_btxsettag(li and li[1] or "")
2214 end
2215
2216 function lists.flushentry(dataset,i)
2217 local rendering = renderings[dataset]
2218 local list = rendering.list
2219 local li = list[i]
2220 if li then
2221 local data = datasets[dataset]
2222 local luadata = data.luadata
2223 local details = data.details
2224 local tag = li[1]
2225 local listindex = li[2]
2226 local n = li[3]
2227 local entry = luadata[tag]
2228 local detail = details[tag]
2229
2230 ctx_btxstartlistentry()
2231 ctx_btxsetcurrentlistentry(i)
2232 ctx_btxsetcurrentlistindex(listindex or 0)
2233 local children = detail.children
2234 local language = entry.language
2235 if children then
2236 ctx_btxsetcombis(concat(children,","))
2237 end
2238 ctx_btxsetcategory(entry.category or "unknown")
2239 ctx_btxsettag(tag)
2240 ctx_btxsetnumber(n)
2241
2242 local citation = citetolist[n]
2243 if citation then
2244 local references = citation.references
2245 if references then
2246 local internal = references.internal
2247 if internal and internal > 0 then
2248 ctx_btxsetinternal(internal)
2249 end
2250 end
2251 end
2252
2253 if language then
2254 ctx_btxsetlanguage(language)
2255 end
2256 local userdata = li[4]
2257 if userdata then
2258 local b = userdata.btxbtx
2259 local a = userdata.btxatx
2260 if b then
2261 ctx_btxsetbefore(b)
2262 end
2263 if a then
2264 ctx_btxsetafter(a)
2265 end
2266 local bl = userdata.btxint
2267 if bl and bl ~= "" then
2268 ctx_btxsetbacklink(bl)
2269 end
2270 end
2271 local authorsuffix = detail.authorsuffix
2272 if authorsuffix then
2273 ctx_btxsetsuffix(authorsuffix)
2274 end
2275 rendering.userdata = userdata
2276 ctx_btxhandlelistentry()
2277 ctx_btxstoplistentry()
2278
2279
2280
2281
2282
2283 end
2284 end
2285
2286 local function getuserdata(dataset,key)
2287 local rendering = renderings[dataset]
2288 if rendering then
2289 local userdata = rendering.userdata
2290 if userdata then
2291 local value = userdata[key]
2292 if value and value ~= "" then
2293 return value
2294 end
2295 end
2296 end
2297 end
2298
2299 lists.uservariable = getuserdata
2300
2301 function lists.filterall(dataset)
2302 local r = renderings[dataset]
2303 local list = r.list
2304 local registered = r.registered
2305 for i=1,#registered do
2306 list[i] = { registered[i], i, 0, false, false }
2307 end
2308 end
2309
2310 implement {
2311 name = "btxuservariable",
2312 arguments = "2 strings",
2313 actions = { getuserdata, context },
2314 }
2315
2316 implement {
2317 name = "btxdoifelseuservariable",
2318 arguments = "2 strings",
2319 actions = { getuserdata, ctx_doifelse },
2320 }
2321
2322
2323
2324
2325
2326
2327
2328 implement {
2329 name = "btxcollectlistentries",
2330 actions = lists.collectentries,
2331 arguments = {
2332 {
2333 { "names" },
2334 { "criterium" },
2335 { "reference" },
2336 { "method" },
2337 { "dataset" },
2338 { "keyword" },
2339 { "sorttype" },
2340 { "repeated" },
2341 { "ignored" },
2342 { "group" },
2343 { "filter" },
2344 }
2345 }
2346 }
2347
2348 implement {
2349 name = "btxpreparelistentries",
2350 arguments = "string",
2351 actions = lists.prepareentries,
2352 }
2353
2354 implement {
2355 name = "btxfetchlistentries",
2356 arguments = "string",
2357 actions = lists.fetchentries,
2358 }
2359
2360 implement {
2361 name = "btxflushlistentry",
2362 arguments = { "string", "integer" },
2363 actions = lists.flushentry,
2364 }
2365
2366 implement {
2367 name = "btxflushlisttag",
2368 arguments = { "string", "integer" },
2369 actions = lists.flushtag,
2370 }
2371
2372 implement {
2373 name = "btxflushlistcombi",
2374 arguments = "2 strings",
2375 actions = lists.flushcombi,
2376 }
2377
2378 implement {
2379 name = "btxdoifelsesameasprevious",
2380 actions = { lists.sameasprevious, ctx_doifelse },
2381 arguments = { "string", "integer", "string", "integer", "string" }
2382 }
2383
2384 implement {
2385 name = "btxdoifelsecombiinlist",
2386 arguments = "2 strings",
2387 actions = { lists.combiinlist, ctx_doifelse },
2388 }
2389
2390end
2391
2392do
2393
2394 local citevariants = { }
2395 publications.citevariants = citevariants
2396
2397 local function btxvalidcitevariant(dataset,variant)
2398 local citevariant = rawget(citevariants,variant)
2399 if citevariant then
2400 return citevariant
2401 end
2402 local s = datasets[dataset]
2403 if s then
2404 s = s.specifications
2405 end
2406 if s then
2407 for k, v in sortedhash(s) do
2408 s = k
2409 break
2410 end
2411 end
2412 if s then
2413 s = specifications[s]
2414 end
2415 if s then
2416 s = s.types
2417 end
2418 if s then
2419 variant = s[variant]
2420 if variant then
2421 citevariant = rawget(citevariants,variant)
2422 end
2423 if citevariant then
2424 return citevariant
2425 end
2426 end
2427 return citevariants.default
2428 end
2429
2430 local function btxhandlecite(specification)
2431 local dataset = specification.dataset or v_default
2432 local reference = specification.reference
2433 local variant = specification.variant
2434
2435 if not variant or variant == "" then
2436 variant = "default"
2437 end
2438 if not reference or reference == "" then
2439 return
2440 end
2441
2442 local data = datasets[dataset]
2443 if not data.suffixed then
2444 data.authorconversion = specification.authorconversion
2445 publications.enhancers.suffixes(data)
2446 end
2447
2448 specification.variant = variant
2449 specification.compress = specification.compress
2450 specification.markentry = specification.markentry ~= false
2451
2452 if specification.sorttype == v_yes then
2453 specification.sorttype = v_normal
2454 end
2455
2456 local prefix, rest = lpegmatch(prefixsplitter,reference)
2457 if prefix and rest then
2458 dataset = prefix
2459 specification.dataset = prefix
2460 specification.reference = rest
2461 end
2462
2463 if trace_cite then
2464 report_cite("inject, dataset: %s, tag: %s, variant: %s, compressed",
2465 specification.dataset or "-",
2466 specification.reference,
2467 specification.variant
2468 )
2469 end
2470
2471 ctx_btxsetdataset(dataset)
2472
2473 local citevariant = btxvalidcitevariant(dataset,variant)
2474
2475 citevariant(specification)
2476 end
2477
2478 local function btxhandlenocite(specification)
2479 if trialtypesetting() then
2480 return
2481 end
2482 local dataset = specification.dataset or v_default
2483 local reference = specification.reference
2484 if not reference or reference == "" then
2485 return
2486 end
2487
2488 local method = specification.method
2489 local internal = specification.internal or ""
2490
2491 local prefix, rest = lpegmatch(prefixsplitter,reference)
2492 if rest then
2493 dataset = prefix
2494 reference = rest
2495 end
2496
2497 if trace_cite then
2498 report_cite("mark, dataset: %s, tags: %s",dataset or "-",reference)
2499 end
2500
2501 local reference = publications.parenttag(dataset,reference)
2502
2503 local found, todo, list = findallused(dataset,reference,internal)
2504
2505 if todo then
2506 marked_todo = todo
2507 marked_dataset = dataset
2508 marked_list = list
2509 marked_method = method
2510 btxflushmarked()
2511 else
2512 marked_todo = false
2513 end
2514 end
2515
2516 implement {
2517 name = "btxhandlecite",
2518 actions = btxhandlecite,
2519 arguments = {
2520 {
2521 { "dataset" },
2522 { "reference" },
2523 { "method" },
2524 { "variant" },
2525 { "sorttype" },
2526 { "compress" },
2527 { "authorconversion" },
2528 { "author" },
2529 { "lefttext" },
2530 { "righttext" },
2531 { "before" },
2532 { "after" },
2533 }
2534 }
2535 }
2536
2537 implement {
2538 name = "btxhandlenocite",
2539 actions = btxhandlenocite,
2540 arguments = {
2541 {
2542 { "dataset" },
2543 { "reference" },
2544 { "method" },
2545 }
2546 }
2547 }
2548
2549
2550
2551 local keysorter = function(a,b)
2552 local ak = a.sortkey
2553 local bk = b.sortkey
2554 if ak == bk then
2555 local as = a.suffix
2556 local bs = b.suffix
2557 if as and bs then
2558 return (as or 0) < (bs or 0)
2559 else
2560 return false
2561 end
2562 else
2563 return ak < bk
2564 end
2565 end
2566
2567 local revsorter = function(a,b)
2568 return keysorter(b,a)
2569 end
2570
2571 local function compresslist(source,specification)
2572 if specification.sorttype == v_normal then
2573 sort(source,keysorter)
2574 elseif specification.sorttype == v_reverse then
2575 sort(source,revsorter)
2576 end
2577 if specification and specification.compress == v_yes and specification.numeric then
2578 local first, last, firstr, lastr
2579 local target, noftarget, tags = { }, 0, { }
2580 local oldvalue = nil
2581 local function flushrange()
2582 noftarget = noftarget + 1
2583 if last > first + 1 then
2584 target[noftarget] = {
2585 first = firstr,
2586 last = lastr,
2587 tags = tags,
2588 }
2589 else
2590 target[noftarget] = firstr
2591 if last > first then
2592 noftarget = noftarget + 1
2593 target[noftarget] = lastr
2594 end
2595 end
2596 tags = { }
2597 end
2598 for i=1,#source do
2599 local entry = source[i]
2600 local current = entry.sortkey
2601 if type(current) == "number" then
2602 if entry.suffix then
2603 if not first then
2604 first, last, firstr, lastr = current, current, entry, entry
2605 else
2606 flushrange()
2607 first, last, firstr, lastr = current, current, entry, entry
2608 end
2609 else
2610 if not first then
2611 first, last, firstr, lastr = current, current, entry, entry
2612 elseif current == last + 1 then
2613 last, lastr = current, entry
2614 else
2615 flushrange()
2616 first, last, firstr, lastr = current, current, entry, entry
2617 end
2618 end
2619 tags[#tags+1] = entry.tag
2620 end
2621 end
2622 if first and last then
2623 flushrange()
2624 end
2625 return target
2626 else
2627 local target, noftarget = { }, 0
2628 for i=1,#source do
2629 local entry = source[i]
2630 noftarget = noftarget + 1
2631 target[noftarget] = {
2632 first = entry,
2633 tags = { entry.tag },
2634 }
2635 end
2636 return target
2637 end
2638 end
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648 local numberonly = R("09")^1 / tonumber + P(1)^0
2649 local f_missing = formatters["<%s>"]
2650
2651
2652
2653
2654
2655 local function processcite(presets,specification)
2656
2657 if specification then
2658 setmetatableindex(specification,presets)
2659 else
2660 specification = presets
2661 end
2662
2663 local dataset = specification.dataset
2664 local reference = specification.reference
2665 local internal = specification.internal
2666 local setup = specification.variant
2667 local compress = specification.compress
2668 local sorttype = specification.sorttype
2669 local getter = specification.getter
2670 local setter = specification.setter
2671 local compressor = specification.compressor
2672 local method = specification.method
2673 local varfield = specification.varfield
2674
2675 local reference = publications.parenttag(dataset,reference)
2676
2677 local found, todo, list = findallused(dataset,reference,internal,method == v_text or method == v_always)
2678
2679 if not found or #found == 0 then
2680
2681 report("no entry %a found in dataset %a",reference,dataset)
2682 elseif not setup then
2683 report("invalid reference for %a",reference)
2684 else
2685 if trace_cite then
2686 report("processing reference %a",reference)
2687 end
2688 local source = { }
2689 local luadata = datasets[dataset].luadata
2690 for i=1,#found do
2691 local entry = found[i]
2692 local userdata = entry.userdata
2693 local references = entry.references
2694 local tag = userdata and userdata.btxref or entry.tag
2695 if tag then
2696 local ldata = luadata[tag]
2697 local data = {
2698 internal = references and references.internal,
2699 language = ldata.language,
2700 dataset = dataset,
2701 tag = tag,
2702 varfield = varfield,
2703
2704
2705 }
2706 setter(data,dataset,tag,entry)
2707 if type(data) == "table" then
2708 source[#source+1] = data
2709 else
2710 report("error in cite rendering %a",setup or "?")
2711 end
2712 end
2713 end
2714
2715 local lefttext = specification.lefttext
2716 local righttext = specification.righttext
2717 local before = specification.before
2718 local after = specification.after
2719
2720 if lefttext and lefttext ~= "" then lefttext = settings_to_array(lefttext) end
2721 if righttext and righttext ~= "" then righttext = settings_to_array(righttext) end
2722 if before and before ~= "" then before = settings_to_array(before) end
2723 if after and after ~= "" then after = settings_to_array(after) end
2724
2725 local function flush(i,n,entry,last)
2726 local tag = entry.tag
2727 local currentcitation = markcite(dataset,tag)
2728
2729 ctx_btxstartcite()
2730 ctx_btxsettag(tag)
2731 ctx_btxsetcategory(entry.category or "unknown")
2732
2733 local language = entry.language
2734 if language then
2735 ctx_btxsetlanguage(language)
2736 end
2737
2738 if lefttext then local text = lefttext [i] ; if text and text ~= "" then ctx_btxsetlefttext (text) end end
2739 if righttext then local text = righttext[i] ; if text and text ~= "" then ctx_btxsetrighttext(text) end end
2740 if before then local text = before [i] ; if text and text ~= "" then ctx_btxsetbefore (text) end end
2741 if after then local text = after [i] ; if text and text ~= "" then ctx_btxsetafter (text) end end
2742
2743 if method ~= v_text then
2744 ctx_btxsetbacklink(currentcitation)
2745 local bl = listtocite[currentcitation]
2746 if bl then
2747
2748 bl = bl.references.internal
2749 else
2750
2751 bl = entry.internal
2752 end
2753 ctx_btxsetinternal(bl and bl > 0 and bl or "")
2754 end
2755 local language = entry.language
2756 if language then
2757 ctx_btxsetlanguage(language)
2758 end
2759
2760
2761
2762
2763 if not getter(entry,last,nil,specification) then
2764 ctx_btxsetfirst("")
2765 end
2766 ctx_btxsetconcat(concatstate(i,n))
2767 if trace_details then
2768 report("expanding cite setup %a",setup)
2769 end
2770 ctx_btxcitesetup(setup)
2771 ctx_btxstopcite()
2772 end
2773 if sorttype == v_normal or sorttype == v_reverse then
2774 local target = (compressor or compresslist)(source,specification)
2775 local nofcollected = #target
2776 if nofcollected == 0 then
2777 local nofcollected = #source
2778 if nofcollected == 0 then
2779 unknowncite(reference)
2780 else
2781 for i=1,nofcollected do
2782 flush(i,nofcollected,source[i])
2783 end
2784 end
2785 else
2786 for i=1,nofcollected do
2787 local entry = target[i]
2788 local first = entry.first
2789 if first then
2790 flush(i,nofcollected,first,entry.last)
2791 else
2792 flush(i,nofcollected,entry)
2793 end
2794 end
2795 end
2796 else
2797 local nofcollected = #source
2798 if nofcollected == 0 then
2799 unknowncite(reference)
2800 else
2801 for i=1,nofcollected do
2802 flush(i,nofcollected,source[i])
2803 end
2804 end
2805 end
2806 end
2807 if trialtypesetting() then
2808 marked_todo = false
2809 elseif method ~= v_text then
2810 marked_todo = todo
2811 marked_dataset = dataset
2812 marked_list = list
2813 marked_method = method
2814 btxflushmarked()
2815 else
2816 marked_todo = false
2817 end
2818 end
2819
2820
2821
2822 local function simplegetter(first,last,field,specification)
2823 local value = first[field]
2824 if value then
2825 if type(value) == "string" then
2826 ctx_btxsetfirst(value)
2827 if last then
2828 ctx_btxsetsecond(last[field])
2829 end
2830 return true
2831 else
2832 report("missing data type definition for %a",field)
2833 end
2834 end
2835 end
2836
2837 local setters = setmetatableindex({},function(t,k)
2838 local v = function(data,dataset,tag,entry)
2839 local value = getcasted(dataset,tag,k)
2840 data.value = value
2841 data[k] = value
2842 data.sortkey = value
2843 data.sortfld = k
2844 end
2845 t[k] = v
2846 return v
2847 end)
2848
2849 local getters = setmetatableindex({},function(t,k)
2850 local v = function(first,last,_,specification)
2851 return simplegetter(first,last,k,specification)
2852 end
2853 t[k] = v
2854 return v
2855 end)
2856
2857 setmetatableindex(citevariants,function(t,k)
2858 local p = defaultvariant or "default"
2859 local v = rawget(t,p)
2860 report_cite("variant %a falls back on %a setter and getter with setup %a",k,p,k)
2861 t[k] = v
2862 return v
2863 end)
2864
2865 function citevariants.default(presets)
2866 local variant = presets.variant
2867 processcite(presets,{
2868 setup = variant,
2869 setter = setters[variant],
2870 getter = getters[variant],
2871 })
2872 end
2873
2874
2875
2876 do
2877
2878 local function setter(data,dataset,tag,entry)
2879 data.category = getfield(dataset,tag,"category")
2880 end
2881
2882 local function getter(first,last,_,specification)
2883 return simplegetter(first,last,"category",specification)
2884 end
2885
2886 function citevariants.category(presets)
2887 processcite(presets,{
2888 setter = setter,
2889 getter = getter,
2890 })
2891 end
2892
2893 end
2894
2895
2896
2897
2898 do
2899
2900 local function setter(data,dataset,tag,entry)
2901
2902 end
2903
2904 local function getter(first,last,_,specification)
2905 ctx_btxsetfirst(first.tag)
2906 end
2907
2908 function citevariants.entry(presets)
2909 processcite(presets,{
2910 compress = false,
2911 setter = setter,
2912 getter = getter,
2913 })
2914 end
2915
2916 end
2917
2918
2919
2920 do
2921
2922 local function setter(data,dataset,tag,entry)
2923 local short = getdetail(dataset,tag,"shorthash")
2924 local suffix = getdetail(dataset,tag,"shortsuffix")
2925 data.short = short
2926 data.sortkey = short
2927 data.suffix = suffix
2928 end
2929
2930 local function getter(first,last,_,specification)
2931 local short = first.short
2932 if short then
2933 local suffix = first.suffix
2934 ctx_btxsetfirst(short)
2935 if suffix then
2936 ctx_btxsetsuffix(suffix)
2937 end
2938 return true
2939 end
2940 end
2941
2942 function citevariants.short(presets)
2943 processcite(presets,{
2944 setter = setter,
2945 getter = getter,
2946 })
2947 end
2948
2949 end
2950
2951
2952
2953 do
2954
2955 local function setter(data,dataset,tag,entry)
2956 data.pages = getcasted(dataset,tag,"pages")
2957 end
2958
2959 local function getter(first,last,_,specification)
2960 local pages = first.pages
2961 if pages then
2962 if type(pages) == "table" then
2963 ctx_btxsetfirst(pages[1])
2964 ctx_btxsetsecond(pages[2])
2965 else
2966 ctx_btxsetfirst(pages)
2967 end
2968 return true
2969 end
2970 end
2971
2972 function citevariants.page(presets)
2973 processcite(presets,{
2974 setter = setter,
2975 getter = getter,
2976 })
2977 end
2978
2979 end
2980
2981
2982
2983 do
2984
2985 local function setter(data,dataset,tag,entry)
2986 local entries = entry.entries
2987 local text = entries and entries.text or "?"
2988 data.num = text
2989 data.sortkey = tonumber(text) or text
2990 end
2991
2992 local function getter(first,last,tag,specification)
2993 return simplegetter(first,last,"num",specification)
2994 end
2995
2996 function citevariants.num(presets)
2997 processcite(presets,{
2998 numeric = true,
2999 setter = setter,
3000 getter = getter,
3001 })
3002 end
3003
3004 citevariants.textnum = citevariants.num
3005
3006 end
3007
3008
3009
3010 do
3011
3012 local function setter(data,dataset,tag,entry)
3013 local year = getfield (dataset,tag,"year")
3014 local suffix = getdetail(dataset,tag,"authorsuffix")
3015 data.year = year
3016 data.suffix = suffix
3017 data.sortkey = tonumber(year) or 9999
3018 end
3019
3020 local function getter(first,last,_,specification)
3021 return simplegetter(first,last,"year",specification)
3022 end
3023
3024 function citevariants.year(presets)
3025 processcite(presets,{
3026 numeric = true,
3027 setter = setter,
3028 getter = getter,
3029 })
3030 end
3031
3032 end
3033
3034
3035
3036 do
3037
3038 local function setter(data,dataset,tag,entry)
3039 local index = getfield(dataset,tag,"index")
3040 data.index = index
3041 data.sortkey = index
3042 end
3043
3044 local function getter(first,last,_,specification)
3045 return simplegetter(first,last,"index",specification)
3046 end
3047
3048 function citevariants.index(presets)
3049 processcite(presets,{
3050 setter = setter,
3051 getter = getter,
3052 numeric = true,
3053 })
3054 end
3055
3056 end
3057
3058
3059
3060 do
3061
3062 local function setter(data,dataset,tag,entry)
3063 data.tag = tag
3064 data.sortkey = tag
3065 end
3066
3067 local function getter(first,last,_,specification)
3068 return simplegetter(first,last,"tag",specification)
3069 end
3070
3071 function citevariants.tag(presets)
3072 return processcite(presets,{
3073 setter = setter,
3074 getter = getter,
3075 })
3076 end
3077
3078 end
3079
3080
3081
3082 do
3083
3084 local function listof(list)
3085 local size = type(list) == "table" and #list or 0
3086 if size > 0 then
3087 return function()
3088 for i=1,size do
3089 ctx_btxsetfirst(list[i])
3090 ctx_btxsetconcat(concatstate(i,size))
3091 ctx_btxcitesetup("listelement")
3092 end
3093 return true
3094 end
3095 else
3096 return "?"
3097 end
3098 end
3099
3100 local function setter(data,dataset,tag,entry)
3101 data.keywords = getcasted(dataset,tag,"keywords")
3102 end
3103
3104 local function getter(first,last,_,specification)
3105 context(listof(first.keywords))
3106 end
3107
3108 function citevariants.keywords(presets)
3109 return processcite(presets,{
3110 variant = "keywords",
3111 setter = setter,
3112 getter = getter,
3113 })
3114 end
3115
3116 end
3117
3118
3119
3120 do
3121
3122
3123
3124 local keysorter = function(a,b)
3125 local ak = a.authorhash
3126 local bk = b.authorhash
3127 if ak == bk then
3128 local as = a.authorsuffix
3129 local bs = b.authorsuffix
3130 if as and bs then
3131 return (as or 0) < (bs or 0)
3132 else
3133 return false
3134 end
3135 elseif ak and bk then
3136 return ak < bk
3137 else
3138 return false
3139 end
3140 end
3141
3142 local revsorter = function(a,b)
3143 return keysorter(b,a)
3144 end
3145
3146 local function authorcompressor(found,specification)
3147
3148 if specification.sorttype == v_normal then
3149 sort(found,keysorter)
3150 elseif specification.sorttype == v_reverse then
3151 sort(found,revsorter)
3152 end
3153 local result = { }
3154 local entries = { }
3155 for i=1,#found do
3156 local entry = found[i]
3157 local author = entry.authorhash
3158 if author then
3159 local aentries = entries[author]
3160 if aentries then
3161 aentries[#aentries+1] = entry
3162 else
3163 entries[author] = { entry }
3164 end
3165 end
3166 end
3167
3168
3169 for i=1,#found do
3170 local entry = found[i]
3171 local author = entry.authorhash
3172 if author then
3173 local aentries = entries[author]
3174 if not aentries then
3175 result[#result+1] = entry
3176 elseif aentries == true then
3177
3178 else
3179 result[#result+1] = entry
3180 entry.entries = aentries
3181 entries[author] = true
3182 end
3183 end
3184 end
3185 return result
3186 end
3187
3188 local function authorconcat(target,key,setup)
3189 ctx_btxstartsubcite(setup)
3190 local nofcollected = #target
3191 if nofcollected == 0 then
3192 unknowncite(tag)
3193 else
3194 for i=1,nofcollected do
3195 local entry = target[i]
3196 local first = entry.first
3197 local tag = entry.tag
3198 local currentcitation = markcite(entry.dataset,tag)
3199 ctx_btxstartciteauthor()
3200 ctx_btxsettag(tag)
3201 ctx_btxsetbacklink(currentcitation)
3202 local bl = listtocite[currentcitation]
3203 ctx_btxsetinternal(bl and bl.references.internal or "")
3204 if first then
3205 ctx_btxsetfirst(first[key] or "")
3206 local suffix = entry.suffix
3207 local last = entry.last
3208 local value = last and last[key]
3209 if value then
3210 ctx_btxsetsecond(value)
3211 end
3212 if suffix then
3213 ctx_btxsetsuffix(suffix)
3214 end
3215 else
3216 local suffix = entry.suffix
3217 local value = entry[key] or ""
3218 ctx_btxsetfirst(value)
3219 if suffix then
3220 ctx_btxsetsuffix(suffix)
3221 end
3222 end
3223 ctx_btxsetconcat(concatstate(i,nofcollected))
3224 if trace_details then
3225 report("expanding %a cite setup %a","multiple author",setup)
3226 end
3227 ctx_btxsubcitesetup(setup)
3228 ctx_btxstopciteauthor()
3229 end
3230 end
3231 ctx_btxstopsubcite()
3232 end
3233
3234 local function authorsingle(entry,key,setup)
3235 ctx_btxstartsubcite(setup)
3236 ctx_btxstartciteauthor()
3237 local tag = entry.tag
3238 ctx_btxsettag(tag)
3239 ctx_btxsetfirst(entry[key] or "")
3240 if suffix then
3241 ctx_btxsetsuffix(entry.suffix)
3242 end
3243 if trace_details then
3244 report("expanding %a cite setup %a","single author",setup)
3245 end
3246 ctx_btxcitesetup(setup)
3247 ctx_btxstopciteauthor()
3248 ctx_btxstopsubcite()
3249 end
3250
3251 local partialinteractive = false
3252
3253 local currentbtxciteauthor = function()
3254 context.currentbtxciteauthorbyfield()
3255 return true
3256 end
3257
3258 local function authorgetter(first,last,key,specification)
3259 ctx_btxsetauthorfield(first.varfield or "author")
3260 if first.type == "author" then
3261 ctx_btxsetfirst(currentbtxciteauthor)
3262 else
3263 ctx_btxsetfirst(first.author)
3264 end
3265 local entries = first.entries
3266
3267
3268 if partialinteractive and not entries then
3269 entries = { first }
3270 end
3271 if entries then
3272
3273 local c = compresslist(entries,specification)
3274 local f = function() authorconcat(c,key,specification.setup or "author") return true end
3275 ctx_btxsetcount(#c)
3276 ctx_btxsetsecond(f)
3277 elseif first then
3278
3279 local f = function() authorsingle(first,key,specification.setup or "author") return true end
3280 ctx_btxsetcount(0)
3281 ctx_btxsetsecond(f)
3282 end
3283 return true
3284 end
3285
3286
3287
3288 local function setter(data,dataset,tag,entry)
3289 data.author, data.field, data.type = getcasted(dataset,tag,data.varfield or "author")
3290 data.sortkey = text and lpegmatch(numberonly,text)
3291 data.authorhash = getdetail(dataset,tag,"authorhash")
3292 end
3293
3294 local function getter(first,last,_,specification)
3295 ctx_btxsetauthorfield(specification.varfield or "author")
3296 if first.type == "author" then
3297 ctx_btxsetfirst(currentbtxciteauthor)
3298 else
3299 ctx_btxsetfirst(first.author)
3300 end
3301 return true
3302 end
3303
3304 function citevariants.author(presets)
3305 processcite(presets,{
3306 variant = "author",
3307 setup = "author",
3308 setter = setter,
3309 getter = getter,
3310 varfield = presets.variant or "author",
3311 compressor = authorcompressor,
3312 })
3313 end
3314
3315
3316
3317 local function setter(data,dataset,tag,entry)
3318 local entries = entry.entries
3319 local text = entries and entries.text or "?"
3320 data.author, data.field, data.type = getcasted(dataset,tag,"author")
3321 data.authorhash = getdetail(dataset,tag,"authorhash")
3322 data.num = text
3323 data.sortkey = text and lpegmatch(numberonly,text)
3324 end
3325
3326 local function getter(first,last,_,specification)
3327 authorgetter(first,last,"num",specification)
3328 return true
3329 end
3330
3331 function citevariants.authornum(presets)
3332 processcite(presets,{
3333 variant = "authornum",
3334 setup = "author:num",
3335 numeric = true,
3336 setter = setter,
3337 getter = getter,
3338 compressor = authorcompressor,
3339 })
3340 end
3341
3342
3343
3344 local function setter(data,dataset,tag,entry)
3345 data.author, data.field, data.type = getcasted(dataset,tag,"author")
3346 data.authorhash = getdetail(dataset,tag,"authorhash")
3347 local year = getfield (dataset,tag,"year")
3348 local suffix = getdetail(dataset,tag,"authorsuffix")
3349 data.year = year
3350 data.suffix = suffix
3351 data.sortkey = tonumber(year) or 9999
3352 end
3353
3354 local function getter(first,last,_,specification)
3355 authorgetter(first,last,"year",specification)
3356 return true
3357 end
3358
3359 function citevariants.authoryear(presets)
3360 processcite(presets,{
3361 variant = "authoryear",
3362 setup = "author:year",
3363 numeric = true,
3364 setter = setter,
3365 getter = getter,
3366 compressor = authorcompressor,
3367 })
3368 end
3369
3370 local function getter(first,last,_,specification)
3371 authorgetter(first,last,"year",specification)
3372 return true
3373 end
3374
3375 function citevariants.authoryears(presets)
3376 processcite(presets,{
3377 variant = "authoryears",
3378 setup = "author:years",
3379 numeric = true,
3380 setter = setter,
3381 getter = getter,
3382 compressor = authorcompressor,
3383 })
3384 end
3385
3386 end
3387
3388end
3389
3390
3391
3392do
3393
3394 local listvariants = { }
3395 publications.listvariants = listvariants
3396
3397 local function btxlistvariant(dataset,block,tag,variant,listindex)
3398 local action = listvariants[variant] or listvariants.default
3399 if action then
3400 listindex = tonumber(listindex)
3401 if listindex then
3402 action(dataset,block,tag,variant,listindex)
3403 end
3404 end
3405 end
3406
3407 implement {
3408 name = "btxlistvariant",
3409 arguments = "5 strings",
3410 actions = btxlistvariant,
3411 }
3412
3413 function listvariants.default(dataset,block,tag,variant)
3414 ctx_btxsetfirst("?")
3415 if trace_details then
3416 report("expanding %a list setup %a","default",variant)
3417 end
3418 ctx_btxnumberingsetup("default")
3419 end
3420
3421 function listvariants.num(dataset,block,tag,variant,listindex)
3422 ctx_btxsetfirst(listindex)
3423 if trace_details then
3424 report("expanding %a list setup %a","num",variant)
3425 end
3426 ctx_btxnumberingsetup(variant or "num")
3427 end
3428
3429
3430
3431 function listvariants.index(dataset,block,tag,variant,listindex)
3432 local index = getdetail(dataset,tag,"index")
3433 ctx_btxsetfirst(index or "?")
3434 if trace_details then
3435 report("expanding %a list setup %a","index",variant)
3436 end
3437 ctx_btxnumberingsetup(variant or "index")
3438 end
3439
3440 function listvariants.tag(dataset,block,tag,variant,listindex)
3441 ctx_btxsetfirst(tag)
3442 if trace_details then
3443 report("expanding %a list setup %a","tag",variant)
3444 end
3445 ctx_btxnumberingsetup(variant or "tag")
3446 end
3447
3448 function listvariants.short(dataset,block,tag,variant,listindex)
3449 local short = getdetail(dataset,tag,"shorthash")
3450 local suffix = getdetail(dataset,tag,"shortsuffix")
3451 if short then
3452 ctx_btxsetfirst(short)
3453 end
3454 if suffix then
3455 ctx_btxsetsuffix(suffix)
3456 end
3457 if trace_details then
3458 report("expanding %a list setup %a","short",variant)
3459 end
3460 ctx_btxnumberingsetup(variant or "short")
3461 end
3462
3463end
3464
3465
3466
3467do
3468
3469
3470
3471 local splitter = lpeg.tsplitat(":")
3472
3473 implement {
3474 name = "checkinterfacechain",
3475 arguments = "2 strings",
3476 actions = function(str,command)
3477 local chain = lpegmatch(splitter,str)
3478 if #chain > 0 then
3479 local command = context[command]
3480 local parent = ""
3481 local child = chain[1]
3482 command(child,parent)
3483 for i=2,#chain do
3484 parent = child
3485 child = child .. ":" .. chain[i]
3486 command(child,parent)
3487 end
3488 end
3489 end
3490 }
3491
3492end
3493
3494do
3495
3496 local btxstring = ""
3497
3498 implement {
3499 name = "btxcmdstring",
3500 actions = function() if btxstring ~= "" then context(btxstring) end end,
3501 }
3502
3503 function publications.prerollcmdstring(str)
3504 btxstring = str or ""
3505 tex.runlocal("t_btx_cmd")
3506 return nodes.toutf(tex.getbox("b_btx_cmd").list) or str
3507 end
3508
3509end
3510
3511do
3512
3513
3514
3515 interfaces.implement {
3516 name = "btxdoifelsecitedone",
3517 protected = true,
3518
3519
3520 arguments = "2 strings",
3521 actions = function(dataset,tag)
3522
3523 local list = structures.lists.tobesaved
3524 local done = false
3525 for i=1,#list do
3526 local l = list[i]
3527 local m = l.metadata
3528 if m and m.kind == "btx" then
3529 local u = l.userdata
3530 if u and u.btxref == tag then
3531 done = true
3532 break
3533 end
3534 end
3535 end
3536 ctx_doifelse(done)
3537 end
3538 }
3539
3540end
3541 |