1if not modules then modules = { } end modules ['publ-aut'] = {
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
9if not characters then
10 dofile(resolvers.findfile("char-def.lua"))
11 dofile(resolvers.findfile("char-ini.lua"))
12end
13
14local lpeg = lpeg
15
16local type, next, tostring, tonumber = type, next, tostring, tonumber
17local concat, sortedhash = table.concat, table.sortedhash
18local utfsub = utf.sub
19local find = string.find
20local formatters = string.formatters
21
22local P, S, C, V, Cs, Ct, Cc = lpeg.P, lpeg.S, lpeg.C, lpeg.V, lpeg.Cs, lpeg.Ct, lpeg.Cc
23
24local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns
25local settings_to_hash = utilities.parsers.settings_to_hash
26
27local context = context
28
29
30local implement = interfaces.implement
31
32local publications = publications
33
34local datasets = publications.datasets
35local getcasted = publications.getcasted
36
37local allocate = utilities.storage.allocate
38
39local chardata = characters.data
40
41local trace_hashing = false trackers.register("publications.authorhash", function(v) trace_hashing = v end)
42
43local expand_authors = false directives.register("publications.prerollauthor", function(v) expand_authors = v end)
44
45local report = logs.reporter("publications","authors")
46local report_cite = logs.reporter("publications","cite")
47
48local v_last = interfaces.variables.last
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66local space = lpegpatterns.whitespace
67local comma = P(",")
68local period = P(".") + P("{.}")
69local dash = P("-") + P("{-}")
70local firstcharacter = lpegpatterns.utf8byte
71local utf8character = lpegpatterns.utf8character
72local p_and = space^1 * (P("and") + P("&&") + P("++")) * space^1
73local p_comma = space^0 * comma * space^0
74local p_space = space^1
75local p_shortone = C((utf8character -dash-period)^1)
76local p_longone = C( utf8character) * (1-dash-period)^0
77
78local p_empty = P("{}")/"" * #(p_space^0 * (P(-1) + P(",")))
79
80local andsplitter = Ct { "start",
81 start = (Cs((V("inner") + (1-p_and))^1) + p_and)^1,
82 inner = P("{") * ((V("inner") + P(1-P("}")))^1) * P("}"),
83}
84
85local commasplitter = Ct { "start",
86 start = Cs(V("outer")) + (p_empty + Cs((V("inner") + (1-p_comma))^1) + p_comma)^1,
87 outer = (P("{")/"") * ((V("inner") + P(1-P("}")))^1) * ((P("}") * P(-1))/""),
88 inner = P("{") * ((V("inner") + P(1-P("}")))^1) * P("}"),
89}
90
91local spacesplitter = Ct { "start",
92 start = Cs(V("outer")) + (Cs((V("inner") + (1-p_space))^1) + p_space)^1,
93 outer = (P("{")/"") * ((V("inner") + P(1-P("}")))^1) * ((P("}") * P(-1))/""),
94 inner = P("{") * ((V("inner") + P(1-P("}")))^1) * P("}"),
95}
96
97local p_initial = p_shortone * period * dash^0
98 + p_longone * (period + dash + P(-1))
99local initialsplitter = p_initial * P(-1) + Ct((p_initial)^1)
100
101
102local optionsplitter = Ct("") * (C((1-space)^1) * space^0 * Cc(true) % rawset)^1
103
104local function is_upper(str)
105 local first = lpegmatch(firstcharacter,str)
106 local okay = chardata[first]
107 return okay and okay.category == "lu"
108end
109
110
111
112local cache = allocate()
113local nofhits = 0
114local nofused = 0
115
116publications.authorcache = cache
117
118local function makeinitials(firstnames)
119 if firstnames and #firstnames > 0 then
120 local initials = { }
121 for i=1,#firstnames do
122 initials[i] = lpegmatch(initialsplitter,firstnames[i])
123 end
124 return initials
125 end
126end
127
128local authormap = allocate()
129publications.authormap = authormap
130
131local prerollcmdstring = publications.prerollcmdstring
132
133local function splitauthor(author,justsplit)
134 local detail, remapped
135 if not justsplit then
136 detail = cache[author]
137 if detail then
138 return detail
139 end
140 remapped = authormap[author]
141 if remapped then
142 report("remapping %a to %a",author,remapped)
143 local detail = cache[remapped]
144 if detail then
145 cache[author] = detail
146 return detail
147 end
148 end
149 end
150 local author = remapped or author
151 local firstnames, vons, surnames, initials, juniors, options
152 if expand_authors and find(author,"\\btxcmd") then
153 author = prerollcmdstring(author)
154 end
155 local split = lpegmatch(commasplitter,author)
156 local n = #split
157 detail = {
158 original = author,
159 snippets = n,
160 }
161 if n == 1 then
162
163 local words = lpegmatch(spacesplitter,author)
164 local i = 1
165 local n = #words
166 firstnames = { }
167 vons = { }
168 surnames = { }
169 while i <= n do
170 local w = words[i]
171 if is_upper(w) then
172 firstnames[#firstnames+1], i = w, i + 1
173 else
174 break
175 end
176 end
177 while i <= n do
178 local w = words[i]
179 if is_upper(w) then
180 break
181 else
182 vons[#vons+1], i = w, i + 1
183 end
184 end
185 if i <= n then
186 while i <= n do
187 surnames[#surnames+1], i = words[i], i + 1
188 end
189 elseif #vons == 0 then
190 surnames[1] = firstnames[#firstnames]
191 firstnames[#firstnames] = nil
192 else
193
194 end
195 if #surnames == 0 then
196
197 firstnames = { }
198 vons = { }
199 surnames = { author }
200 else
201 initials = makeinitials(firstnames)
202 end
203 elseif n == 2 then
204
205
206 local words = lpegmatch(spacesplitter,split[1])
207 local i = 1
208 local n = #words
209 firstnames = { }
210 vons = { }
211 surnames = { }
212 while i <= n do
213 local w = words[i]
214 if is_upper(w) then
215 break
216 else
217 vons[#vons+1], i = w, i + 1
218 end
219 end
220 while i <= n do
221 surnames[#surnames+1] = words[i]
222 i = i + 1
223 end
224
225 local words = lpegmatch(spacesplitter,split[2])
226 local i = 1
227 local n = #words
228 while i <= n do
229 local w = words[i]
230 if is_upper(w) then
231 firstnames[#firstnames+1] = w
232 i = i + 1
233 else
234 break
235 end
236 end
237 while i <= n do
238 vons[#vons+1] = words[i]
239 i = i + 1
240 end
241 if surnames and firstnames and #surnames == 0 then
242
243 surnames[1] = firstnames[#firstnames]
244 firstnames[#firstnames] = nil
245 end
246 initials = makeinitials(firstnames)
247 elseif n == 3 then
248
249 surnames = lpegmatch(spacesplitter,split[1])
250 juniors = lpegmatch(spacesplitter,split[2])
251 firstnames = lpegmatch(spacesplitter,split[3])
252 initials = makeinitials(firstnames)
253 elseif n == 4 then
254
255 vons = lpegmatch(spacesplitter,split[1])
256 surnames = lpegmatch(spacesplitter,split[2])
257 juniors = lpegmatch(spacesplitter,split[3])
258 firstnames = lpegmatch(spacesplitter,split[4])
259 initials = makeinitials(firstnames)
260 elseif n >= 5 then
261
262
263 vons = lpegmatch(spacesplitter,split[1])
264 surnames = lpegmatch(spacesplitter,split[2])
265 juniors = lpegmatch(spacesplitter,split[3])
266 firstnames = lpegmatch(spacesplitter,split[4])
267 initials = lpegmatch(spacesplitter,split[5])
268 options = split[6]
269 if options then
270 options = lpegmatch(optionsplitter,options)
271 end
272 end
273 if firstnames and #firstnames > 0 then detail.firstnames = firstnames end
274 if vons and #vons > 0 then detail.vons = vons end
275 if surnames and #surnames > 0 then detail.surnames = surnames end
276 if initials and #initials > 0 then detail.initials = initials end
277 if juniors and #juniors > 0 then detail.juniors = juniors end
278 if options and next(options) then detail.options = options end
279 if not justsplit then
280 cache[author] = detail
281 nofhits = nofhits + 1
282 end
283 return detail
284end
285
286local function splitauthorstring(str)
287 if not str or str == "" then
288 return
289 end
290 nofused = nofused + 1
291
292 local remapped = authormap[str]
293 if remapped then
294 local detail = cache[remapped]
295 if detail then
296 cache[str] = detail
297 return { detail }
298 end
299 end
300
301 local authors = cache[str]
302 if authors then
303 return { authors }
304 end
305
306
307
308 local authors = lpegmatch(andsplitter,str) or { }
309 local nofauthors = #authors
310 for i=1,nofauthors do
311 authors[i] = splitauthor(authors[i])
312 end
313 if nofauthors > 1 and authors[nofauthors].original == "others" then
314
315 authors[nofauthors] = nil
316 authors.others = true
317 end
318 return authors
319end
320
321publications.splitoneauthor = splitauthor
322publications.splitauthor = splitauthorstring
323
324local function the_initials(initials,symbol,connector)
325 if not symbol then
326 symbol = "."
327 end
328 if not connector then
329 connector = "-"
330 end
331 local result = { }
332 local r = 0
333 for i=1,#initials do
334 local initial = initials[i]
335 if type(initial) == "table" then
336
337 local set = { }
338 local s = 0
339 for i=1,#initial do
340 if i > 1 then
341 s = s + 1 ; set[s] = connector
342 end
343 s = s + 1 ; set[s] = initial[i]
344 s = s + 1 ; set[s] = symbol
345 end
346 r = r + 1 ; result[r] = concat(set)
347 else
348
349 r = r + 1 ; result[r] = initial .. symbol
350 end
351 end
352 return result
353end
354
355local ctx_btxsetconcat = context.btxsetconcat
356local ctx_btxsetoverflow = context.btxsetoverflow
357local ctx_btxsetinitials = context.btxsetinitials
358local ctx_btxsetfirstnames = context.btxsetfirstnames
359local ctx_btxsetvons = context.btxsetvons
360local ctx_btxsetsurnames = context.btxsetsurnames
361local ctx_btxsetjuniors = context.btxsetjuniors
362local ctx_btxsetauthorvariant = context.btxsetauthorvariant
363
364local ctx_btxstartauthor = context.btxstartauthor
365local ctx_btxstopauthor = context.btxstopauthor
366
367local ctx_btxciteauthorsetup = context.btxciteauthorsetup
368local ctx_btxlistauthorsetup = context.btxlistauthorsetup
369
370local concatstate = publications.concatstate
371local f_invalid = formatters["<invalid %s: %s>"]
372
373local currentauthordata = nil
374local currentauthorsymbol = nil
375local currentauthorconnector = nil
376
377local manipulators = typesetters.manipulators
378local splitmanipulation = manipulators.splitspecification
379local applymanipulation = manipulators.applyspecification
380
381local function value(i,field)
382 if currentauthordata then
383 local entry = currentauthordata[i]
384 if entry then
385 local value = entry[field]
386 if value and #value > 0 then
387 return value
388 end
389 end
390 end
391end
392
393implement { name = "btxcurrentfirstnames", arguments = "integer", actions = function(i) local v = value(i,"firstnames") if v then context(concat(v," ")) end end }
394implement { name = "btxcurrentinitials", arguments = "integer", actions = function(i) local v = value(i,"initials") if v then context(concat(the_initials(v,currentauthorsymbol,currentauthorconnector))) end end }
395implement { name = "btxcurrentjuniors", arguments = "integer", actions = function(i) local v = value(i,"juniors") if v then context(concat(v," ")) end end }
396implement { name = "btxcurrentsurnames", arguments = "integer", actions = function(i) local v = value(i,"surnames") if v then context(concat(v," ")) end end }
397implement { name = "btxcurrentvons", arguments = "integer", actions = function(i) local v = value(i,"vons") if v then context(concat(v," ")) end end }
398
399local function btxauthorfield(i,field)
400 if currentauthordata then
401 local entry = currentauthordata[i]
402 if entry then
403 local manipulator, field = splitmanipulation(field)
404 local value = entry[field]
405 if not value or #value == 0 then
406
407 elseif manipulator then
408 for i=1,#value do
409 if i > 1 then
410 context(" ")
411 end
412 context(applymanipulation(manipulator,value) or value)
413 end
414 elseif field == "initials" then
415 context(concat(the_initials(value,currentauthorsymbol,currentauthorconnector)))
416 else
417 context(concat(value," "))
418 end
419 end
420 end
421end
422
423
424
425
426
427
428local function btxauthor(dataset,tag,field,settings)
429 local split, usedfield, kind = getcasted(dataset,tag,field)
430 if kind == "author" then
431 local max = split and #split or 0
432 if max == 0 then
433 return
434
435 end
436 local absmax = max
437 local etallimit = tonumber(settings.etallimit) or 1000
438 local etaldisplay = tonumber(settings.etaldisplay) or etallimit
439 local etaloption = settings_to_hash(settings.etaloption or "")
440 local etallast = etaloption[v_last]
441 local combiner = settings.combiner
442 local symbol = settings.symbol
443 local connector = settings.connector
444 local index = settings.index
445 if not combiner or combiner == "" then
446 combiner = "normal"
447 end
448 if not symbol then
449 symbol = "."
450 end
451 local ctx_btxsetup = settings.kind == "cite" and ctx_btxciteauthorsetup or ctx_btxlistauthorsetup
452 if max > etallimit and (etaldisplay+(etallast and 1 or 0)) < max then
453 max = etaldisplay
454 else
455 etallast = false
456 end
457 currentauthordata = split
458 currentauthorsymbol = symbol
459 currentauthorconnector = connector
460
461 local function oneauthor(i,last,justone)
462 local author = split[i]
463 if index then
464 ctx_btxstartauthor(i,1,0)
465 elseif last then
466 ctx_btxstartauthor(i,1,0)
467 ctx_btxsetconcat(0)
468 ctx_btxsetauthorvariant(combiner)
469 else
470 local state = author.state or 0
471 ctx_btxstartauthor(i,max,state)
472 ctx_btxsetconcat(concatstate(i,max))
473 ctx_btxsetauthorvariant(combiner)
474 end
475 local initials = author.initials
476 if initials and #initials > 0 then
477 ctx_btxsetinitials()
478 end
479 local firstnames = author.firstnames
480 if firstnames and #firstnames > 0 then
481 ctx_btxsetfirstnames()
482 end
483 local vons = author.vons
484 if vons and #vons > 0 then
485 ctx_btxsetvons()
486 end
487 local surnames = author.surnames
488 if surnames and #surnames > 0 then
489 ctx_btxsetsurnames()
490 end
491 local juniors = author.juniors
492 if juniors and #juniors > 0 then
493 ctx_btxsetjuniors()
494 end
495 if not index and i == max then
496 if split.others then
497 ctx_btxsetoverflow(1)
498 else
499 local overflow = #split - max
500 if overflow > 0 then
501 ctx_btxsetoverflow(overflow)
502 end
503 end
504 end
505 ctx_btxsetup(combiner)
506 ctx_btxstopauthor()
507 end
508
509 if index then
510 oneauthor(index)
511 elseif max == 1 then
512 oneauthor(1,false,true)
513 else
514 for i=1,max do
515 oneauthor(i)
516 end
517 if etallast then
518 oneauthor(absmax,true)
519 end
520 end
521
522 else
523 report("ignored field %a of tag %a, used field %a is no author",field,tag,usedfield)
524 end
525
526end
527
528implement {
529 name = "btxauthorfield",
530 actions = btxauthorfield,
531 arguments = { "integer", "string" }
532}
533
534implement {
535 name = "btxauthor",
536 actions = btxauthor,
537 arguments = {
538 "argument",
539 "argument",
540 "argument",
541 {
542 { "combiner" },
543 { "kind" },
544 { "etallimit" },
545 { "etaldisplay" },
546 { "etaloption" },
547 { "symbol" },
548 { "connector" },
549 }
550 }
551}
552
553local function components(snippet,short)
554 local vons = snippet.vons
555 local surnames = snippet.surnames
556 local initials = snippet.initials
557 local firstnames = not short and snippet.firstnames
558 local juniors = snippet.juniors
559 return
560 vons and #vons > 0 and concat(vons," ") or "",
561 surnames and #surnames > 0 and concat(surnames," ") or "",
562 initials and #initials > 0 and concat(the_initials(initials)," ") or "",
563 firstnames and #firstnames > 0 and concat(firstnames," ") or "",
564 juniors and #juniors > 0 and concat(juniors, " ") or ""
565end
566
567local collapsers = allocate { }
568
569publications.authorcollapsers = collapsers
570
571
572
573
574local function default(author)
575 local hash = author.hash
576 if hash then
577 return hash
578 end
579 local original = author.original
580 local vons = author.vons
581 local surnames = author.surnames
582 local initials = author.initials
583 local firstnames = author.firstnames
584 local juniors = author.juniors
585 local result = { }
586 local nofresult = 0
587 if vons and #vons > 0 then
588 for j=1,#vons do
589 nofresult = nofresult + 1
590 result[nofresult] = vons[j]
591 end
592 end
593 if surnames and #surnames > 0 then
594 for j=1,#surnames do
595 nofresult = nofresult + 1
596 result[nofresult] = surnames[j]
597 end
598 end
599 if initials and #initials > 0 then
600 initials = the_initials(initials)
601 for j=1,#initials do
602 nofresult = nofresult + 1
603 result[nofresult] = initials[j]
604 end
605 end
606 if firstnames and #firstnames > 0 then
607 for j=1,#firstnames do
608 nofresult = nofresult + 1
609 result[nofresult] = firstnames[j]
610 end
611 end
612 if juniors and #juniors > 0 then
613 for j=1,#juniors do
614 nofresult = nofresult + 1
615 result[nofresult] = juniors[j]
616 end
617 end
618 local hash = concat(result," ")
619 if trace_hashing then
620 report("hash: %s -> %s",original,hash)
621 end
622 author.hash = hash
623 return hash
624end
625
626local authorhashers = { }
627publications.authorhashers = authorhashers
628
629
630
631local function name(authors)
632 if type(authors) == "table" then
633 local n = #authors
634 if n == 0 then
635 return ""
636 end
637 local result = { }
638 local nofresult = 0
639 for i=1,n do
640 local author = authors[i]
641 local surnames = author.surnames
642 if surnames and #surnames > 0 then
643 for j=1,#surnames do
644 nofresult = nofresult + 1
645 result[nofresult] = surnames[j]
646 end
647 end
648 end
649 return concat(result," ")
650 else
651 return authors
652 end
653end
654
655table.setmetatableindex(authorhashers,function(t,k)
656 t[k] = name
657 return name
658end)
659
660authorhashers.normal = function(authors)
661 if type(authors) == "table" then
662 local n = #authors
663 if n == 0 then
664 return ""
665 end
666 local result = { }
667 local nofresult = 0
668 for i=1,n do
669 local author = authors[i]
670 local vons = author.vons
671 local surnames = author.surnames
672 local firstnames = author.firstnames
673 local juniors = author.juniors
674 if vons and #vons > 0 then
675 for j=1,#vons do
676 nofresult = nofresult + 1
677 result[nofresult] = vons[j]
678 end
679 end
680 if surnames and #surnames > 0 then
681 for j=1,#surnames do
682 nofresult = nofresult + 1
683 result[nofresult] = surnames[j]
684 end
685 end
686 if firstnames and #firstnames > 0 then
687 for j=1,#firstnames do
688 nofresult = nofresult + 1
689 result[nofresult] = firstnames[j]
690 end
691 end
692 if juniors and #juniors > 0 then
693 for j=1,#juniors do
694 nofresult = nofresult + 1
695 result[nofresult] = juniors[j]
696 end
697 end
698 end
699 return concat(result," ")
700 else
701 return authors
702 end
703end
704
705authorhashers.normalshort = function(authors)
706 if type(authors) == "table" then
707 local n = #authors
708 if n == 0 then
709 return ""
710 end
711 local result = { }
712 local nofresult = 0
713 for i=1,n do
714 local author = authors[i]
715 local vons = author.vons
716 local surnames = author.surnames
717 local initials = author.initials
718 local juniors = author.juniors
719 if vons and #vons > 0 then
720 for j=1,#vons do
721 nofresult = nofresult + 1
722 result[nofresult] = vons[j]
723 end
724 end
725 if surnames and #surnames > 0 then
726 for j=1,#surnames do
727 nofresult = nofresult + 1
728 result[nofresult] = surnames[j]
729 end
730 end
731 if initials and #initials > 0 then
732 initials = the_initials(initials)
733 for j=1,#initials do
734 nofresult = nofresult + 1
735 result[nofresult] = initials[j]
736 end
737 end
738 if juniors and #juniors > 0 then
739 for j=1,#juniors do
740 nofresult = nofresult + 1
741 result[nofresult] = juniors[j]
742 end
743 end
744 end
745 return concat(result," ")
746 else
747 return authors
748 end
749end
750
751local sequentialhash = function(authors)
752 if type(authors) == "table" then
753 local n = #authors
754 if n == 0 then
755 return ""
756 end
757 local result = { }
758 local nofresult = 0
759 for i=1,n do
760 local author = authors[i]
761 local vons = author.vons
762 local surnames = author.surnames
763 local firstnames = author.firstnames
764 local juniors = author.juniors
765 if firstnames and #firstnames > 0 then
766 for j=1,#firstnames do
767 nofresult = nofresult + 1
768 result[nofresult] = firstnames[j]
769 end
770 end
771 if vons and #vons > 0 then
772 for j=1,#vons do
773 nofresult = nofresult + 1
774 result[nofresult] = vons[j]
775 end
776 end
777 if surnames and #surnames > 0 then
778 for j=1,#surnames do
779 nofresult = nofresult + 1
780 result[nofresult] = surnames[j]
781 end
782 end
783 if juniors and #juniors > 0 then
784 for j=1,#juniors do
785 nofresult = nofresult + 1
786 result[nofresult] = juniors[j]
787 end
788 end
789 end
790 return concat(result," ")
791 else
792 return authors
793 end
794end
795
796local sequentialshorthash = function(authors)
797 if type(authors) == "table" then
798 local n = #authors
799 if n == 0 then
800 return ""
801 end
802 local result = { }
803 local nofresult = 0
804 for i=1,n do
805 local author = authors[i]
806 local vons = author.vons
807 local surnames = author.surnames
808 local initials = author.initials
809 local juniors = author.juniors
810 if initials and #initials > 0 then
811 initials = the_initials(initials)
812 for j=1,#initials do
813 nofresult = nofresult + 1
814 result[nofresult] = initials[j]
815 end
816 end
817 if vons and #vons > 0 then
818 for j=1,#vons do
819 nofresult = nofresult + 1
820 result[nofresult] = vons[j]
821 end
822 end
823 if surnames and #surnames > 0 then
824 for j=1,#surnames do
825 nofresult = nofresult + 1
826 result[nofresult] = surnames[j]
827 end
828 end
829 if juniors and #juniors > 0 then
830 for j=1,#juniors do
831 nofresult = nofresult + 1
832 result[nofresult] = juniors[j]
833 end
834 end
835 end
836 return concat(result," ")
837 else
838 return authors
839 end
840end
841
842authorhashers.sequential = sequentialhash
843authorhashers.sequentialshort = sequentialshorthash
844authorhashers.normalinverted = authorhashers.normal
845authorhashers.invertedshort = authorhashers.normalshort
846
847local p_clean = Cs ( (
848 P("\\btxcmd") / ""
849 + S("`~!@#$%^&*()_-+={}[]:;\"\'<>,.?/|\\") / ""
850 + lpeg.patterns.utf8character
851 )^1)
852
853
854
855authorhashers.short = function(authors)
856
857
858
859 if type(authors) == "table" then
860 local n = #authors
861 if n == 0 then
862 return "unk"
863 elseif n == 1 then
864 local surnames = authors[1].surnames
865 if not surnames or #surnames == 0 then
866 return "err"
867 else
868 local s = surnames[1]
869 local c = lpegmatch(p_clean,s)
870 if trace_hashing and s ~= c then
871 report_cite("name %a cleaned to %a for short construction",s,c)
872 end
873 return utfsub(c,1,3)
874 end
875 else
876 local t = { }
877 for i=1,n do
878 if i > 3 then
879 t[#t+1] = "+"
880 break
881 end
882 local surnames = authors[i].surnames
883 if not surnames or #surnames == 0 then
884 t[#t+1] = "?"
885 else
886 local s = surnames[1]
887 local c = lpegmatch(p_clean,s)
888 if s ~= c then
889 report_cite("name %a cleaned to %a for short construction",s,c)
890 end
891 t[#t+1] = utfsub(c,1,1)
892 end
893 end
894 return concat(t)
895 end
896 else
897 return utfsub(authors,1,3)
898 end
899end
900
901collapsers.default = default
902
903local function authorwriter(key,index)
904 if not key then
905 return ""
906 end
907 if type(key) == "string" then
908 return key
909 end
910 local n = #key
911 if n == 0 then
912 return ""
913 end
914 if index then
915 if not key[index] then
916 return ""
917 end
918 elseif n == 1 then
919 index = 1
920 end
921 if index then
922 local author = key[index]
923 local options = author.options
924 if options then
925 for option in next, options do
926 local collapse = collapsers[option]
927 if collapse then
928 return collapse(author)
929 end
930 end
931 end
932 local hash = default(author)
933
934
935
936 return hash
937 end
938 local t = { }
939 local s = 0
940 for i=1,n do
941 local author = key[i]
942 local options = author.options
943 s = s + 1
944 if options then
945 local done = false
946 for option in next, options do
947 local collapse = collapsers[option]
948 if collapse then
949 t[s] = collapse(author)
950 done = true
951 end
952 end
953 if not done then
954 t[s] = default(author)
955 end
956 else
957 t[s] = default(author)
958 end
959 end
960 local hash = concat(t," & ")
961
962
963
964 return hash
965end
966
967local function writer(key)
968 return authorwriter(key)
969end
970
971publications.writers .author = writer
972publications.casters .author = splitauthorstring
973publications.components.author = components
974
975
976
977
978
979
980
981
982
983
984
985
986publications.sortmethods.authoryear = {
987 sequence = {
988
989 { field = "author", default = "", unknown = "" },
990 { field = "year", default = "9998", unknown = "9999" },
991
992 { field = "month", default = "13", unknown = "14" },
993 { field = "day", default = "32", unknown = "33" },
994 { field = "journal", default = "", unknown = "" },
995 { field = "volume", default = "", unknown = "" },
996
997 { field = "pages", default = "", unknown = "" },
998 { field = "title", default = "", unknown = "" },
999 { field = "index", default = "", unknown = "" },
1000 },
1001}
1002
1003publications.sortmethods.authortitle = {
1004 sequence = {
1005 { field = "author", default = "", unknown = "" },
1006 { field = "title", default = "", unknown = "" },
1007 { field = "booktitle", default = "", unknown = "" },
1008 { field = "maintitle", default = "", unknown = "" },
1009 { field = "volume", default = "", unknown = "" },
1010 { field = "part", default = "", unknown = "" },
1011 { field = "date", default = "9998-13-32", unknown = "9999-14-33" },
1012 { field = "year", default = "9998", unknown = "9999" },
1013 { field = "month", default = "13", unknown = "14" },
1014 { field = "day", default = "32", unknown = "33" },
1015 { field = "index", default = "", unknown = "" },
1016 },
1017}
1018
1019implement {
1020 name = "btxremapauthor",
1021 arguments = "2 strings",
1022 actions = function(k,v)
1023 local a = { splitauthor(k,true) }
1024 local s1 = sequentialhash(a)
1025 local s2 = sequentialshorthash(a)
1026 if not authormap[k] then
1027 authormap[k] = v
1028 report("%a mapped onto %a",k,v)
1029 end
1030 if not authormap[s1] then
1031 authormap[s1] = v
1032 report("%a mapped onto %a, derived from %a",s1,v,k)
1033 end
1034 if not authormap[s2] then
1035 authormap[s2] = v
1036 report("%a mapped onto %a, derived from %a",s2,v,k)
1037 end
1038 end
1039}
1040
1041implement {
1042 name = "btxshowauthorremapping",
1043 actions = function(k,v)
1044 report("start author remapping")
1045 for k, v in sortedhash(authormap) do
1046 report(" %s => %s",k,v)
1047 end
1048 report("stop author remapping")
1049 end
1050}
1051 |