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